cloudLoggingMiddleware function
Return Middleware
that logs errors using Google Cloud structured logs and
returns the correct response.
Implementation
Middleware cloudLoggingMiddleware(String projectId) {
Handler hostedLoggingMiddleware(Handler innerHandler) => (request) async {
// Add log correlation to nest all log messages beneath request log in
// Log Viewer.
String? traceValue;
final traceHeader = request.headers[cloudTraceContextHeader];
if (traceHeader != null) {
traceValue =
'projects/$projectId/traces/${traceHeader.split('/')[0]}';
}
String createErrorLogEntry(
Object error,
StackTrace? stackTrace,
LogSeverity logSeverity,
) {
// Collect and format error information as described here
// https://cloud.google.com/functions/docs/monitoring/logging#writing_structured_logs
final chain = _fromStackTrace(stackTrace);
final stackFrame = chain.traces.firstOrNull?.frames.firstOrNull;
return _createLogEntry(
traceValue,
'$error\n$chain'.trim(),
logSeverity,
stackFrame: stackFrame,
);
}
final completer = Completer<Response>.sync();
final currentZone = Zone.current;
Zone.current.fork(
zoneValues: {_loggerKey: _CloudLogger(currentZone, traceValue)},
specification: ZoneSpecification(
handleUncaughtError: (self, parent, zone, error, stackTrace) {
if (error is HijackException) {
completer.completeError(error, stackTrace);
}
final logContentString = error is BadRequestException
? createErrorLogEntry(
'Bad request. ${error.message}',
error.innerStack ?? stackTrace,
LogSeverity.warning,
)
: createErrorLogEntry(
error,
stackTrace,
LogSeverity.error,
);
// Serialize to a JSON string and output.
parent.print(self, logContentString);
if (completer.isCompleted) {
return;
}
final response = error is BadRequestException
? _responseFromBadRequest(error, stackTrace)
: Response.internalServerError();
completer.complete(response);
},
print: (self, parent, zone, line) {
final logContent = _createLogEntry(
traceValue,
line,
LogSeverity.info,
);
// Serialize to a JSON string and output to parent zone.
parent.print(self, logContent);
},
),
).runGuarded(
() async {
final response = await innerHandler(request);
if (!completer.isCompleted) {
completer.complete(response);
}
},
);
return completer.future;
};
return hostedLoggingMiddleware;
}