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
相关产品推荐
相关产品推荐

