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, {
bool Function(Frame)? predicate,
}) {
// Collect and format error information as described here
// https://cloud.google.com/functions/docs/monitoring/logging#writing_structured_logs
final chain = stackTrace == null
? Chain.current()
: Chain.forTrace(stackTrace).foldFrames(
predicate ??
(f) =>
f.package == 'functions_framework' ||
f.package == 'shelf',
terse: true,
);
final stackFrame = _frameFromChain(chain);
return _createLogEntry(
traceValue,
'$error\n$chain'.trim(),
logSeverity,
stackFrame: stackFrame,
);
}
final completer = Completer<Response>.sync();
final currentZone = Zone.current;
Zone.current
.fork(
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,
predicate: (f) => f.package == 'shelf',
)
: 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
? _fromBadRequestException(error)
: 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 {
_requestExpando[request] = _CloudLogger(currentZone, traceValue);
final response = await innerHandler(request);
if (!completer.isCompleted) {
completer.complete(response);
}
},
);
return completer.future;
};
return hostedLoggingMiddleware;
}