Search code examples
nestjsapollo-server

How to log stacktrace on NestJS + GraphQL?


I'm trying out NestJS + GraphQL using Apollo underneath. When I set the Apollo 'debug' option to be true, I can see the stacktrace in the response but I cannot find a way to log this stacktrace in our application logs.

I would like to have it in the log to troubleshoot issues in production. Is there a way to do this?


Solution

  • Here's the ApolloServerPlugin I use

    import { Plugin } from '@nestjs/apollo';
    import { Logger } from '@nestjs/common';
    import {
      ApolloServerPlugin,
      GraphQLRequestListener,
    } from 'apollo-server-plugin-base';
    import {
      BaseContext,
      GraphQLRequestContext,
      GraphQLRequestContextWillSendResponse,
    } from 'apollo-server-types';
    import * as util from 'util';
    
    @Plugin()
    export class LoggingPlugin implements ApolloServerPlugin {
      constructor(private readonly logger: Logger) {}
    
      async requestDidStart(
        requestContext: GraphQLRequestContext,
      ): Promise<GraphQLRequestListener> {
        const thatLogger = this.logger;
        if (requestContext.request.operationName !== 'IntrospectionQuery') {
          thatLogger.log(
            `request query: ${requestContext.request.query || 'undefined'}`,
          );
        }
        return {
          async willSendResponse(
            requestContextWillSendResponse: GraphQLRequestContextWillSendResponse<BaseContext>,
          ): Promise<void> {
            if (
              requestContextWillSendResponse.request.operationName !==
              'IntrospectionQuery'
            ) {
              if (!requestContextWillSendResponse.errors) {
                thatLogger.log(`response without any errors`);
              } else {
                const errors = requestContextWillSendResponse.errors.concat();
                const responseErrors =
                  requestContextWillSendResponse.response.errors?.concat();
                if (errors && responseErrors) {
                  for (let i = 0; i < errors.length; i++) {
                    const result = {
                      ...responseErrors[i],
                      stack: errors[i].stack,
                    };
                    if (result.extensions) {
                      delete result.extensions.exception;
                    }
                    if (
                      result.extensions &&
                      result.extensions.code !== 'INTERNAL_SERVER_ERROR'
                    ) {
                      thatLogger.warn(
                        `response with errors: ${util.inspect(result, {
                          depth: 4,
                        })}`,
                      );
                    } else {
                      thatLogger.error(
                        `response with errors: ${util.inspect(result, {
                          depth: 4,
                        })}`,
                      );
                    }
                  }
                }
              }
            }
          },
        };
      }
    }