blob: 9a8446ce38f1bbc0ed84092a78d016bc9b446513 [file] [log] [blame]
// Copyright 2019 The Flutter Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
import 'dart:async';
import 'dart:typed_data';
import 'package:appengine/appengine.dart';
import 'package:gcloud/db.dart';
import 'package:meta/meta.dart';
import '../datastore/cocoon_config.dart';
import '../model/appengine/key_helper.dart';
import '../model/appengine/log_chunk.dart';
import '../model/appengine/task.dart';
import '../request_handling/api_request_handler.dart';
import '../request_handling/authentication.dart';
import '../request_handling/body.dart';
import '../request_handling/exceptions.dart';
import '../service/datastore.dart';
import '../service/stackdriver_logger.dart';
@immutable
class AppendLog extends ApiRequestHandler<Body> {
AppendLog(
Config config,
AuthenticationProvider authenticationProvider, {
@visibleForTesting
this.datastoreProvider = DatastoreService.defaultProvider,
StackdriverLoggerService stackdriverLogger,
@visibleForTesting Uint8List requestBodyValue,
}) : stackdriverLogger =
stackdriverLogger ?? StackdriverLoggerService(config: config),
super(
config: config,
authenticationProvider: authenticationProvider,
requestBodyValue: requestBodyValue);
final StackdriverLoggerService stackdriverLogger;
final DatastoreServiceProvider datastoreProvider;
static const String ownerKeyParam = 'ownerKey';
@override
Future<Body> post() async {
final DatastoreService datastore = datastoreProvider(config.db);
final String encodedOwnerKey = request.uri.queryParameters[ownerKeyParam];
if (encodedOwnerKey == null) {
throw const BadRequestException(
'Missing required query parameter: $ownerKeyParam');
}
final ClientContext clientContext = authContext.clientContext;
final KeyHelper keyHelper =
KeyHelper(applicationContext: clientContext.applicationContext);
final Key ownerKey = keyHelper.decode(encodedOwnerKey);
final Task task =
await datastore.lookupByValue<Model>(ownerKey, orElse: () {
throw const InternalServerError(
'Invalid owner key. Owner entity does not exist');
}) as Task;
final LogChunk logChunk = LogChunk(
ownerKey: ownerKey,
createTimestamp: DateTime.now().millisecondsSinceEpoch,
data: requestBody,
);
await datastore.insert(<LogChunk>[logChunk]);
await writeToStackdriver('${encodedOwnerKey}_${task.attempts}');
return Body.empty;
}
/// Write the log data from this request to Stackdriver under [logName].
///
/// [logName] must follow the format `[encodedOwnerKey]_[task.attempt]` so each
/// attempt for a task can be located.
///
/// This will write the log to the global path `projects/flutter-dashboard/logs/[logName]` on Google Cloud.
Future<void> writeToStackdriver(String logName) async {
final List<String> lines = String.fromCharCodes(requestBody).split('\n');
await stackdriverLogger.writeLines(logName, lines);
}
}