日志记录
内置 Logger 可替换为 Pino 等实现。
日志是监控和调试应用的重要手段。NestJS 内置了轻量级日志器,同时也支持与 pino、winston 等专业日志库集成,满足生产环境需求。
一、内置 Logger
NestJS 在 @nestjs/common 中提供了 Logger 类,支持多种日志级别,开箱即用。
1. 基本使用
import { Logger } from "@nestjs/common";
@Injectable()
export class AppService {
private readonly logger = new Logger(AppService.name);
getHello(): string {
this.logger.log("Returning hello");
this.logger.debug("Debug info");
this.logger.warn("Warning message");
this.logger.error("Error occurred", "stack trace");
this.logger.verbose("Verbose output");
return "Hello World!";
}
}
2. 日志级别
| 方法 | 级别 | 说明 |
|---|---|---|
log() | log | 一般信息 |
error() | error | 错误信息,可传堆栈 |
warn() | warn | 警告信息 |
debug() | debug | 调试信息 |
verbose() | verbose | 详细输出 |
可通过设置 LOG_LEVELS 环境变量或启动参数控制哪些级别输出:
# 只输出 error 和 warn
LOG_LEVELS=error,warn node dist/main
3. 全局替换日志器
你可以创建自定义日志器替换内置的 Logger。例如,将日志输出到文件:
// main.ts
import { Logger } from "@nestjs/common";
class MyLogger extends Logger {
error(message: any, trace?: string, context?: string) {
// 自定义逻辑(保存到文件、发送到外部服务)
super.error(message, trace, context);
}
}
async function bootstrap() {
const app = await NestFactory.create(AppModule, {
logger: new MyLogger(),
});
await app.listen(3000);
}
二、自定义日志器:实现 LoggerService 接口
如果你需要完全控制日志行为,可以实现 LoggerService 接口(来自 @nestjs/common)。
import { LoggerService } from "@nestjs/common";
export class MyCustomLogger implements LoggerService {
log(message: any, ...optionalParams: any[]) {
/* 实现 */
}
error(message: any, ...optionalParams: any[]) {
/* 实现 */
}
warn(message: any, ...optionalParams: any[]) {
/* 实现 */
}
debug?(message: any, ...optionalParams: any[]) {
/* 实现 */
}
verbose?(message: any, ...optionalParams: any[]) {
/* 实现 */
}
setLogLevels?(levels: string[]) {
/* 可选 */
}
}
然后在创建应用时传入:
app.useLogger(new MyCustomLogger());
三、生产级日志:pino 与 winston
1. nestjs-pino(推荐用于高性能)
pino 是极快的 JSON 日志库,nestjs-pino 提供 NestJS 集成(注意包名是 nestjs-pino,不是 nest-pino)。
安装:
npm install nestjs-pino pino-http
在模块中导入:
// app.module.ts
import { LoggerModule } from "nestjs-pino";
@Module({
imports: [
LoggerModule.forRoot({
pinoHttp: {
level: process.env.LOG_LEVEL || "info",
transport:
process.env.NODE_ENV !== "production"
? { target: "pino-pretty" } // 开发友好格式
: undefined,
},
}),
],
})
export class AppModule {}
在服务中注入 Pino 日志器:
import { PinoLogger, InjectPinoLogger } from "nestjs-pino";
@Injectable()
export class MyService {
constructor(@InjectPinoLogger(MyService.name) private logger: PinoLogger) {}
doSomething() {
this.logger.info({ data: 123 }, "Info log");
}
}
2. nest-winston
winston 功能丰富,支持多运输渠道(控制台、文件、远程服务)。
安装:
npm install nest-winston winston
配置:
import { WinstonModule } from "nest-winston";
import * as winston from "winston";
@Module({
imports: [
WinstonModule.forRoot({
transports: [
new winston.transports.Console({
format: winston.format.simple(),
}),
new winston.transports.File({ filename: "logs/error.log", level: "error" }),
],
}),
],
})
export class AppModule {}
在服务中注入(nest-winston 没有 InjectLogger 装饰器,需通过 WINSTON_MODULE_PROVIDER 令牌注入):
import { Inject, Injectable } from "@nestjs/common";
import { WINSTON_MODULE_PROVIDER } from "nest-winston";
import { Logger } from "winston";
@Injectable()
export class MyService {
constructor(@Inject(WINSTON_MODULE_PROVIDER) private logger: Logger) {}
test() {
this.logger.info("message");
}
}
四、请求日志中间件
记录每个 HTTP 请求的详细信息(方法、URL、参数、响应状态、耗时等)对问题排查很有帮助。可以用中间件实现。
// logger.middleware.ts
import { Injectable, NestMiddleware, Logger } from "@nestjs/common";
import { Request, Response, NextFunction } from "express";
@Injectable()
export class RequestLoggerMiddleware implements NestMiddleware {
private logger = new Logger("HTTP");
use(req: Request, res: Response, next: NextFunction) {
const { method, originalUrl, ip, body, query } = req;
const userAgent = req.get("user-agent") || "";
const start = Date.now();
res.on("finish", () => {
const duration = Date.now() - start;
this.logger.log(
`${method} ${originalUrl} ${res.statusCode} ${duration}ms - ${userAgent} ${ip}`,
);
// 可选:记录 body、query(注意敏感信息脱敏)
});
next();
}
}
注册中间件(例如在 AppModule 中全局使用):
export class AppModule implements NestModule {
configure(consumer: MiddlewareConsumer) {
consumer.apply(RequestLoggerMiddleware).forRoutes("*");
}
}
对于更高级的请求追踪(如关联请求 ID),可以使用 cls-hooked 或 nestjs-cls。
五、日志最佳实践
- 分级输出:开发环境使用
debug/verbose,生产环境使用info/warn/error。 - 结构化日志:使用 JSON 格式(pino/winston 默认),便于日志聚合工具分析。
- 敏感信息脱敏:在记录请求体或参数时,滤除密码、Token 等。
- 请求 ID 关联:为每个请求生成唯一 ID,贯穿所有日志,方便定位链路。
- 异步日志:使用非阻塞日志库(pino 自带低开销),避免日志影响性能。
- 集中收集:生产环境将日志输出到 stdout/stderr,由容器或进程管理器(如 PM2)转发到 ELK、Splunk 等。
总结
| 需求 | 推荐方案 |
|---|---|
| 简单调试 | 内置 Logger,轻量且够用 |
| 高性能生产 | nestjs-pino(JSON 日志、低开销) |
| 丰富运输渠道 | nest-winston(文件、远程、多格式) |
| 请求日志 | 自定义中间件记录入参、耗时、状态码 |
| 多级别控制 | 环境变量 LOG_LEVELS 或日志库的 level 配置 |
根据项目规模和性能要求,选择合适的日志方案,并在开发初期就引入规范的日志记录,便于后期维护和故障排查。
参考文献
以下链接在编写时均可正常访问:
| 资料 | 说明 |
|---|---|
| NestJS 文档 | 官方 |
| Logger | 本章主题 |
| Request lifecycle | 执行顺序 |
相关文章
认证与授权
认证(Authentication):确认「你是谁」(如 JWT、Session)。 - 授权(Authorization):确认「你能做什么」(如 RBAC、策略检查)。
缓存
@nestjs/cache-manager 统一缓存 API,存储实现可插拔。
提供者与服务
Service 是最常见的 Provider,封装业务逻辑。见 module。
守卫 (Guards) 与授权
守卫决定是否放行请求,常用于认证与授权。见 auth。
配置管理(Config 模块)
@nestjs/config 加载 .env 并提供 ConfigService。见 工程化 env。
数据库集成(以 TypeORM 为例)
Nest 通过 @nestjs/typeorm 等包集成 ORM;生产环境用 migration,慎用 synchronize。亦可选用 Prisma、MikroORM 等(见 官方 Database)。
Series
new
13 / 19