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

Nest.js中JSON循环结构序列化报错,如何获取完整堆栈追踪?

解决NestJS生产环境循环引用JSON序列化崩溃及定位错误来源问题

问题原因

你的生产环境崩溃是因为NestJS默认的ConsoleLogger在处理包含循环引用的错误对象时,尝试用JSON.stringify序列化失败。从堆栈日志来看,错误发生在全局异常过滤器ExceptionsHandler处理未知错误时——它调用logger.error并传入了带循环引用的对象(比如ClientRequest/Socket这类包含自身引用的对象)。你的自定义代码只是可能触发了原始错误,但崩溃点实际在框架的日志处理逻辑里。

解决方法

1. 自定义全局异常过滤器,捕获原始错误堆栈

通过自定义异常过滤器,先记录完整的原始错误堆栈,再处理响应,避免框架默认日志处理触发序列化错误:

import { ExceptionFilter, Catch, ArgumentsHost, HttpException, HttpStatus } from '@nestjs/common';
import { Logger } from '@nestjs/common';

@Catch()
export class AllExceptionsFilter implements ExceptionFilter {
  private readonly logger = new Logger(AllExceptionsFilter.name);

  catch(exception: unknown, host: ArgumentsHost) {
    const ctx = host.switchToHttp();
    const response = ctx.getResponse();
    const request = ctx.getRequest();

    // 记录原始错误的完整堆栈信息
    if (exception instanceof Error) {
      this.logger.error(`原始错误详情:\n${exception.stack}`);
    }

    const status = exception instanceof HttpException
      ? exception.getStatus()
      : HttpStatus.INTERNAL_SERVER_ERROR;

    response.status(status).json({
      statusCode: status,
      timestamp: new Date().toISOString(),
      path: request.url,
    });
  }
}

在main.ts中注册全局过滤器:

import { NestFactory } from '@nestjs/core';
import { AppModule } from './app.module';
import { AllExceptionsFilter } from './filters/all-exceptions.filter';

async function bootstrap() {
  const app = await NestFactory.create(AppModule);
  app.useGlobalFilters(new AllExceptionsFilter());
  await app.listen(3000);
}
bootstrap();

2. 自定义Logger,处理循环引用的序列化

替换默认的ConsoleLogger,使用支持循环引用的序列化库(如flatted)或者实现容错序列化逻辑:

import { ConsoleLogger } from '@nestjs/common';
import flatted from 'flatted';

export class SafeConsoleLogger extends ConsoleLogger {
  protected stringifyMessage(message: any): string {
    if (typeof message === 'object' && message !== null) {
      try {
        // 用flatted处理循环引用的对象序列化
        return flatted.stringify(message, (key, value) => 
          typeof value === 'bigint' ? value.toString() : value, 2);
      } catch (e) {
        // 序列化失败时回退到toString,避免崩溃
        return message.toString();
      }
    }
    return super.stringifyMessage(message);
  }
}

在main.ts中替换默认Logger:

async function bootstrap() {
  const app = await NestFactory.create(AppModule, {
    logger: new SafeConsoleLogger(),
  });
  await app.listen(3000);
}

3. 临时排查技巧

  • 修改你怀疑的代码,直接记录错误对象而非toString:把this.logger.error(error.toString())改成console.error('触发错误:', error),console.error可以安全处理循环引用对象,不会抛出错误,同时能打印完整的错误信息。
  • 临时在生产环境开启NODE_ENV=development(注意性能影响),可能更容易复现问题。

为什么你的怀疑代码看起来不相关?

崩溃不是来自你调用logger.error的地方,而是框架的全局异常过滤器在处理其他未知错误时,传入了带循环引用的错误对象给默认Logger,导致序列化失败。你的代码只是可能触发了原始错误,但真正的崩溃点在框架日志处理逻辑中。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.01 04:05:53