cloudLoggingMiddleware function

Middleware cloudLoggingMiddleware(
  1. String projectId
)

Return Middleware that logs errors using Google Cloud structured logs and returns the correct response.

projectId is the Google Cloud Project ID used for trace correlation.

Log messages of type Map are logged as structured logs (jsonPayload); all other logs messages are logged as text logs (textPayload).

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 createErrorLogEntryFromRequest(
      Object error,
      StackTrace? stackTrace,
      LogSeverity logSeverity,
    ) => structuredLogEntry(
      '$error'.trim(),
      logSeverity,
      traceId: traceValue,
      stackTrace: stackTrace,
    );

    final completer = Completer<Response>.sync();

    final currentZone = Zone.current;

    Zone.current
        .fork(
          zoneValues: {
            _loggerKey: _CloudLogger(zone: currentZone, traceId: traceValue),
          },
          specification: ZoneSpecification(
            handleUncaughtError: (self, parent, zone, error, stackTrace) {
              if (error is HijackException) {
                completer.completeError(error, stackTrace);
              }

              final logContentString = error is BadRequestException
                  ? createErrorLogEntryFromRequest(
                      'Bad request. ${error.message}',
                      error.innerStack ?? stackTrace,
                      LogSeverity.warning,
                    )
                  : createErrorLogEntryFromRequest(
                      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 = structuredLogEntry(
                line,
                LogSeverity.info,
                traceId: traceValue,
              );

              // 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;
}