当然!我们来探讨一个虽然基础但却至关重要的主题:日志 (Logger)。这就像是为你的应用安装一个全方位的“黑匣子”和“飞行记录仪”,在应用平稳运行时它默默无闻,但在出现问题时,它就是你排查故障、还原现场的唯一线索。
为你的应用安装“黑匣子”:NestJS 日志系统完全指南
想象你的 NestJS 应用是一架正在执行跨洋飞行的精密客机。
- 在万米高空平稳飞行时(正常运行),一切安好。
- 但如果突然遇到一阵强烈的气流(一个意外的错误),或者某个引擎的指示灯闪烁(性能瓶颈),飞行员(你,开发者)需要立刻知道:发生了什么?在哪里发生的?当时的情况是怎样的?
没有日志系统,就如同驾驶一架仪表盘全黑的飞机,你对飞机的内部状态一无所知,一旦出现问题便束手无策。
日志 (Logging) 就是记录应用在运行期间发生的关键事件的过程。一个好的日志系统应该能:
- 分级记录:区分普通信息(
log
)、调试细节(debug
)、警告(warn
)和严重错误(error
)。 - 提供上下文:清晰地标明日志信息来自哪个模块(
[UsersService]
),以及发生的时间。 - 结构化输出:最好能输出为 JSON 格式,方便机器进行解析、搜索和分析。
- 可定制化:允许你轻松地替换底层的日志库(比如换成更专业的
Pino
或Winston
),并将日志输出到文件、远程日志服务等。
NestJS 提供了一个内置的、轻量级的日志系统,并设计了一套非常优雅的机制,让你可以在此基础上进行扩展和定制。
1. 基础用法:开箱即用的 Logger
服务
NestJS 内置了一个 Logger
类,你可以直接在任何服务或控制器中使用它,无需任何额外配置。
src/cats/cats.service.ts
```typescript import { Injectable, Logger } from '@nestjs/common';
@Injectable() export class CatsService { // 1. 在类的属性中创建一个 Logger 实例 // 传入的上下文('CatsService')会自动显示在日志输出中 private readonly logger = new Logger(CatsService.name);
create(catDto: any) { // 2. 使用不同的方法记录不同级别的日志 this.logger.log('开始执行创建猫猫的操作...'); this.logger.debug(收到的数据是: ${JSON.stringify(catDto)}
, 'create method'); // 可以传入第二个参数作为更详细的上下文
if (!catDto.name) {
this.logger.warn('警告:创建的猫猫没有名字!');
}
// ... 执行创建逻辑 ...
this.logger.log('猫猫创建成功!');
} }
当你调用 `create()` 方法时,你的控制台会输出类似这样的彩色日志:
```bash
[Nest] 12345 - 07/16/2025, 5:00:00 PM LOG [CatsService] 开始执行创建猫猫的操作...
[Nest] 12345 - 07/16/2025, 5:00:00 PM DEBUG [CatsService] 收到的数据是: {"age":5} - create method
[Nest] 12345 - 07/16/2025, 5:00:00 PM WARN [CatsService] 警告:创建的猫猫没有名字!
[Nest] 12345 - 07/16/2025, 5:00:00 PM LOG [CatsService] 猫猫创建成功!
这种方式非常简单直观,对于开发和小项目来说已经足够了。
2. 更换“飞行记录仪”:自定义你的日志系统
内置的 Logger
只是一个简单的控制台输出工具。在生产环境中,我们通常有更复杂的需求:
- 将日志写入文件以便长期保存。
- 将日志输出为 JSON 格式,方便日志收集系统(如 ELK Stack, Datadog)进行处理。
- 根据日志级别(
error
级别的日志要发邮件告警)执行不同操作。
NestJS 允许你完全替换掉它的默认日志实现。你可以通过在 main.ts
中将一个自定义的 Logger 实例传递给 NestJS 应用来实现。
如何自定义? 你需要创建一个实现了 LoggerService
接口的类。
实战:使用强大的 Winston
库来接管日志
Winston 是 Node.js 生态中最流行、功能最强大的日志库之一。
第一步:安装依赖
npm install winston nest-winston
````nest-winston` 是一个社区提供的辅助库,让集成过程更简单。
**第二步:创建我们的自定义 Logger**
**`src/logger/winston.logger.ts`**
```typescript
import { transports, format, createLogger } from 'winston';
import { WinstonModule } from 'nest-winston';
// 创建一个 Winston Logger 实例
const winstonLogger = createLogger({
transports: [
// 1. 在控制台输出
new transports.Console({
format: format.combine(
format.timestamp(),
format.ms(),
format.colorize({ all: true }), // 为日志级别添加颜色
format.printf(info => `${info.timestamp} [${info.level}] ${info.context}: ${info.message}`),
),
}),
// 2. 将错误日志写入文件
new transports.File({
filename: 'logs/error.log',
level: 'error',
format: format.combine(format.timestamp(), format.json()),
}),
// 3. 将所有日志写入另一个文件
new transports.File({
filename: 'logs/combined.log',
format: format.combine(format.timestamp(), format.json()),
}),
],
});
// 使用 nest-winston 的辅助函数创建一个 NestJS Logger
export const logger = WinstonModule.createLogger({
instance: winstonLogger,
});
这里我们配置了三个“输出目的地”(Transports):一个彩色的控制台输出,一个只记录错误的 error.log
文件,以及一个记录所有信息的 combined.log
文件。
第三步:在 main.ts
中“注入”我们的新 Logger
// main.ts
import { NestFactory } from '@nestjs/core';
import { AppModule } from './app.module';
import { logger } from './logger/winston.logger'; // 导入我们的自定义 logger
async function bootstrap() {
const app = await NestFactory.create(AppModule, {
// 告诉 NestJS 使用我们的 Winston 实例作为默认 Logger
logger: logger,
});
await app.listen(3000);
}
bootstrap();
现在,当你启动应用并再次调用 CatsService
的 create()
方法时,你会发现:
- 控制台的日志格式变成了我们用 Winston 定义的新格式。
- 项目根目录下会生成一个
logs
文件夹。 logs/combined.log
文件里会包含所有级别的日志(JSON 格式)。- 如果你触发了
warn
或error
级别的日志,logs/error.log
文件里也会记录下来。
我们成功地、无缝地更换了整个应用的“飞行记录仪”核心!
3. 控制日志的“音量”
在生产环境中,为了性能,我们通常不希望打印大量的 debug
信息。你可以在 main.ts
中设置日志级别。
// main.ts
async function bootstrap() {
const app = await NestFactory.create(AppModule, {
// 只显示 'log', 'warn', 'error' 级别的日志
logger: ['log', 'warn', 'error'],
});
// ...
}
这样设置后,所有通过 logger.debug()
或 logger.verbose()
打印的日志都将被忽略。
总结
日志是保证应用可维护性和可观测性的生命线。
当你... | 你应该... | 核心思想 |
---|---|---|
在开发中或进行快速原型设计时 | 直接使用内置的 new Logger('Context') | 简单、开箱即用。快速地在代码中添加上下文感知的日志输出。 |
构建生产级应用时 | 创建一个自定义 Logger (实现 LoggerService 接口) | 强大、可定制。允许你集成专业的日志库(如 Winston, Pino),实现日志分级、文件输出、JSON 格式化等高级功能。 |
需要替换整个应用的日志系统时 | 在 NestFactory.create() 的第二个参数中传入 logger 实例 | 全局替换。以一种非侵入式的方式,将整个框架和你的业务日志都交由新的日志系统管理。 |
不要等到飞机出事了才想起“黑匣子”的重要性。从项目一开始就养成良好的日志记录习惯,使用带有明确上下文和级别的日志,这会在未来的调试和维护工作中为你节省无数个小时。