You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在NestJS + GraphQL中记录堆栈跟踪到应用日志

Apollo的debug配置仅控制接口响应中是否返回堆栈跟踪,不会主动将错误信息写入应用日志,不需要为了打日志在生产环境开启该配置(会导致堆栈泄露给前端),以下是两种可直接落地的实现方式:

实现方案
  • 方案1:自定义全局GraphQL异常过滤器(优先推荐,适配NestJS原生生命周期)
    该方案和NestJS内置的日志、异常处理体系完全打通,侵入性最低,代码示例:

    import { Catch, ArgumentsHost, Logger } from '@nestjs/common';
    import { GqlExceptionFilter, GqlArgumentsHost } from '@nestjs/graphql';
    import { ApolloError } from 'apollo-server-errors';
    
    @Catch()
    export class GqlErrorLoggerFilter implements GqlExceptionFilter {
      private readonly logger = new Logger(GqlErrorLoggerFilter.name);
    
      catch(exception: Error, host: ArgumentsHost) {
        const gqlHost = GqlArgumentsHost.create(host);
        const operationInfo = gqlHost.getInfo();
        // 提取原始错误的完整堆栈
        const realStack = exception.stack || (exception as ApolloError).originalError?.stack;
        const operationName = operationInfo?.fieldName || 'unknown_operation';
    
        // 写入应用日志,可替换为项目实际使用的日志库方法
        this.logger.error(
          `GraphQL操作[${operationName}]执行异常: ${exception.message}`,
          realStack
        );
    
        // 生产环境返回脱敏错误,不透传堆栈给前端
        return new ApolloError(
          process.env.NODE_ENV === 'production' ? '服务内部错误' : exception.message,
          (exception as ApolloError).extensions?.code || 'INTERNAL_SERVER_ERROR',
          process.env.NODE_ENV === 'production' ? {} : { stack: realStack }
        );
      }
    }
    

    注册过滤器,在根模块的providers数组中添加配置即可全局生效:

    import { APP_FILTER } from '@nestjs/core';
    // 其余模块配置省略
    {
      provide: APP_FILTER,
      useClass: GqlErrorLoggerFilter,
    }
    
  • 方案2:配置Apollo Server错误拦截插件(覆盖全链路错误场景)
    如果需要捕获GraphQL语法解析、参数校验等Nest过滤器触达不到的Apollo内部错误,可以直接在GraphQL模块初始化时传入自定义插件:

    GraphQLModule.forRoot({
      // 其余基础配置省略
      debug: process.env.NODE_ENV !== 'production',
      plugins: [
        {
          async requestDidStart() {
            return {
              async didEncounterErrors(requestContext) {
                const logger = new Logger('ApolloServer');
                const operationName = requestContext.operation?.name?.value || 'unknown_operation';
                requestContext.errors.forEach(err => {
                  const realStack = err.originalError?.stack || err.stack;
                  logger.error(
                    `GraphQL链路[${operationName}]报错: ${err.message}`,
                    realStack
                  );
                });
              }
            };
          }
        }
      ]
    })
    
注意事项
  • 提取堆栈时不要直接读取Apollo包装后外层错误的stack属性,业务代码抛出的原始错误堆栈会挂载在error.originalError.stack路径下,直接读取外层stack拿到的只是Apollo内部包装逻辑的调用栈,没有问题排查价值。
  • 两种方案二选一即可,同时使用会导致重复打印日志。
  • 生产环境务必将debug配置设为false,全量堆栈仅输出到服务端日志,前端响应只返回通用错误提示,避免敏感代码路径、内部结构信息泄露。

内容的提问来源于stack exchange,提问作者hikky36

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.29 16:01:08