cloudLoggingMiddleware function

Middleware cloudLoggingMiddleware(
  1. String projectId
)

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