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