文章目录
前言
我们来系统性分析 NestJS 统一异常处理 + 日志追踪链路设计:
✅ 一、统一异常处理 ExceptionFilter
核心目标:
- 所有异常结构统一返回格式
{ code, msg, data }
- 错误信息脱敏处理(不泄露 stack trace 给前端)
- 含有 traceId,便于日志串联、故障排查
- 支持国际化、错误码映射表
🚀 标准实现结构:
common/filters/http-exception.filter.ts
import {
ArgumentsHost,
Catch,
ExceptionFilter,
HttpException,
HttpStatus,
} from '@nestjs/common';
import { Request, Response } from 'express';
@Catch()
export class AllExceptionsFilter implements ExceptionFilter {
catch(exception: unknown, host: ArgumentsHost) {
const ctx = host.switchToHttp();
const response = ctx.getResponse<Response>();
const request = ctx.getRequest<Request>();
const traceId = request.headers['x-trace-id'] || ''; // 日志链路追踪用
let code = -1;
let msg = 'Internal server error';
let status = HttpStatus.INTERNAL_SERVER_ERROR;
if (exception instanceof HttpException) {
status = exception.getStatus();
const res = exception.getResponse();
if (typeof res === 'string') {
msg = res;
} else if (typeof res === 'object') {
msg = (res as any).message || msg;
code = (res as any).code || status;
}
}
response.status(status).json({
code,
msg,
data: null,
traceId,
});
}
}
注册为全局过滤器:
// main.ts
app.useGlobalFilters(new AllExceptionsFilter());
✅ 二、日志追踪 LoggerMiddleware + TraceId
目的:
- 自动注入 traceId,贯穿中间件 → 控制器 →服务 → 日志输出
- 每个请求唯一标识
- 支持链路追踪系统(如 ELK、SkyWalking、Jaeger)
- skywalking
- ELK
实现步骤:
common/middleware/logger.middleware.ts
import { Injectable, NestMiddleware } from '@nestjs/common';
import { Request, Response, NextFunction } from 'express';
import { v4 as uuid } from 'uuid';
@Injectable()
export class LoggerMiddleware implements NestMiddleware {
use(req: Request, res: Response, next: NextFunction) {
const traceId = req.headers['x-trace-id'] || uuid();
req.headers['x-trace-id'] = traceId;
(req as any).traceId = traceId;
console.log(`[Request] ${traceId} ${req.method} ${req.originalUrl}`);
res.on('finish', () => {
console.log(`[Response] ${traceId} ${res.statusCode}`);
});
next();
}
}
使用中间件:
// app.module.ts
export class AppModule implements NestModule {
configure(consumer: MiddlewareConsumer) {
consumer.apply(LoggerMiddleware).forRoutes('*');
}
}
如何在服务/拦截器中访问 traceId?
// controller / service
@Get()
handle(@Req() req: Request) {
const traceId = (req as any).traceId;
}
✅ 三、标准响应结构示例
{
"code": 404,
"msg": "Resource not found",
"data": null,
"traceId": "c84e9e6b-2df0-4e83-87d2-0db7cc319bd4"
}
🧠 总结:落地关键点
目标 | 实现 |
---|---|
❗ 异常统一结构 | 全局 @Catch() Filter + HttpException 规范 |
🔗 Trace ID | 中间件生成 UUID,挂载 req.headers / req.traceId |
🧾 日志打点 | 在 controller / service / errorFilter / middleware 处打印 |
📘 错误码管理 | code => message 映射表,推荐配置表 + i18n |
🔍 安全性 | 不暴露栈信息、脱敏字段过滤 |
Winston
和 Pino
在 NestJS 项目中, Winston
和 Pino
之间二选一,以下是从实战角度对比推荐:
✅ 快速结论:
需求类型 | 推荐选项 | 理由 |
---|---|---|
🚀 极致性能(百万级日志) | Pino | 性能极高,写入速度领先 |
📦 功能丰富 + 插件生态 | Winston | 插件多,生态成熟,结构更灵活 |
🧩 与 NestJS 生态融合 | Winston 更主流(但 Pino 也支持) | |
📊 日志格式自定义 | Winston 更直观 | |
🧪 开发中调试体验 | Winston 可读性好、格式清晰 |
📊 Winston 优势(适合大部分企业项目)
- ✅ 成熟稳定、社区庞大
- ✅ 支持多个
transport
(console/file/http/syslog) - ✅ 配置灵活,适合嵌入
traceId
、userId
等 - ✅ Nest 官方文档示例使用 Winston(默认推荐)
示例集成:
pnpm add winston nest-winston
// main.ts
import { WinstonModule } from 'nest-winston';
import * as winston from 'winston';
const app = await NestFactory.create(AppModule, {
logger: WinstonModule.createLogger({
transports: [
new winston.transports.Console({
format: winston.format.combine(
winston.format.timestamp(),
winston.format.simple()
),
}),
],
}),
});
⚡ Pino 优势(极致性能 + Cloud Native)
- 🚀 极快(是 Winston 的 5~10 倍写入速度)
- ✅ 默认结构化 JSON 输出,适合对接 ELK、Datadog、Grafana Loki 等
- ✅ 支持异步写入、流式传输
- ❗ 但:Nest 中整合复杂度略高,需要
nestjs-pino
支持
示例:
pnpm add pino pino-pretty nestjs-pino
import { LoggerModule } from 'nestjs-pino';
@Module({
imports: [
LoggerModule.forRoot({
pinoHttp: {
transport: {
target: 'pino-pretty',
options: { colorize: true },
},
},
}),
],
})
🧠 推荐策略(大厂项目实战):
场景 | 推荐使用 |
---|---|
🚀 API 网关、高并发日志收集 | ✅ Pino |
🧾 通用后管系统、业务系统 | ✅ Winston |
🌐 多语言服务,统一日志格式 | ✅ Pino (配合 JSON 格式日志) |
🧪 本地开发调试优先 | ✅ Winston |
🔚 总结
对比项 | Winston | Pino |
---|---|---|
性能 | ⭐⭐⭐ | ⭐⭐⭐⭐⭐(超快) |
格式配置 | 灵活(支持组合 Format) | JSON + 简洁配置 |
插件生态 | 丰富(file, http, syslog 等) | 偏轻量(适合定向场景) |
Nest 集成 | 默认推荐 nest-winston | 需引入 nestjs-pino |
上手体验 | 非常易用 | 更偏 Cloud Native / DevOps 场景 |
🔧 如果你已经在用 traceId + Filter + Middleware
架构,我推荐:
✅ 用
Winston
做日志收集 + 多 transport 输出(控制台 + 文件 + Graylog)
若未来要对接 Prometheus、ELK、Datadog、Loki,那可以再考虑引入 Pino
。