WEBKT

NestJS 进阶:用 Winston 和日志轮转,告别无限膨胀的日志文件!

35 0 0 0

1. 为什么需要日志轮转?

2. Winston 和 winston-daily-rotate-file 简介

3. 在 NestJS 中配置 Winston 和日志轮转

3.1 安装依赖

3.2 创建日志模块

3.3 使用日志

3.4 运行和测试

4. 进阶配置和优化

4.1 日志级别

4.2 日志格式

4.3 异常处理

4.4 异步日志

4.5 日志清理策略

5. 总结

6. 常见问题解答

7. 更多资源

嘿,老铁们!我是老码农,今天咱们聊聊在 NestJS 项目里,如何优雅地处理日志,避免日志文件越滚越大,最后把硬盘都塞满的情况。特别是对于那些高并发、需要大量日志输出的项目,一个好的日志方案至关重要。咱们用 Winston 和 winston-daily-rotate-file 这对黄金搭档,轻松实现日志轮转,让你的项目更健康,运维更省心。

1. 为什么需要日志轮转?

首先,咱们得明白为啥要搞日志轮转。试想一下,你的 NestJS 项目每天产生几百万甚至上千万条日志,如果都写到一个文件里,会发生什么?

  • 文件越来越大: 很快,你的日志文件就会变得巨大无比,打开、搜索、备份都会变得非常缓慢。这不仅影响开发效率,也给运维带来了挑战。
  • 磁盘空间耗尽: 如果没有限制,日志文件可能会吃掉你服务器的所有磁盘空间,导致系统崩溃。
  • 难以查找问题: 巨量的日志信息会让定位问题变得非常困难,你需要在海量数据中找到关键的错误信息,简直是海底捞针。

所以,我们需要一种机制,定期地创建新的日志文件,并将旧的日志文件进行归档或删除,这就是日志轮转。这样,我们可以控制单个日志文件的大小,保留一定时间范围内的日志,方便排查问题,也避免了磁盘空间被耗尽的风险。

2. Winston 和 winston-daily-rotate-file 简介

  • Winston: Winston 是一个功能强大、灵活的 Node.js 日志库。它支持多种日志级别(如 debug, info, warn, error),可以方便地将日志输出到控制台、文件、数据库等多种目标。Winston 的核心是 transports,你可以配置不同的 transports 来定义日志的输出方式。
  • winston-daily-rotate-file 这个包是 Winston 的一个 transport,专门用来实现日志轮转。它可以按照日期、大小等规则自动创建新的日志文件,并将旧的日志文件进行归档或删除。这样,我们就可以轻松地控制日志文件的大小和数量。

3. 在 NestJS 中配置 Winston 和日志轮转

下面,咱们一步一步来,看看如何在 NestJS 项目中使用 Winston 和 winston-daily-rotate-file

3.1 安装依赖

首先,你需要安装 Winston 和 winston-daily-rotate-file

npm install winston winston-daily-rotate-file --save

3.2 创建日志模块

接下来,咱们创建一个 NestJS 模块来管理 Winston 的配置。这可以让你更方便地在整个项目中引用和使用日志。

// logger.module.ts
import { Module } from '@nestjs/common';
import { WinstonModule } from 'nest-winston';
import * as winston from 'winston';
import * as DailyRotateFile from 'winston-daily-rotate-file';
import { utilities as nestWinstonModuleUtilities } from 'nest-winston';
@Module({
imports: [
WinstonModule.forRoot({
transports: [
// 每天创建一个新的日志文件
new DailyRotateFile({
dirname: 'logs', // 日志文件存放的目录
filename: '%DATE%.log', // 日志文件名,%DATE% 会被替换为日期
datePattern: 'YYYY-MM-DD', // 日期格式
zippedArchive: true, // 是否压缩旧的日志文件
maxSize: '20m', // 单个日志文件的最大大小,单位是 MB
maxFiles: '14d', // 保留的日志文件数量,这里是 14 天
format: winston.format.combine(
winston.format.timestamp(), // 添加时间戳
winston.format.printf(({ timestamp, level, message }) => {
return `${timestamp} [${level.toUpperCase()}] ${message}`;
}),
),
}),
// 在控制台输出日志
new winston.transports.Console({
format: winston.format.combine(
winston.format.colorize(), // 颜色高亮
winston.format.timestamp(), // 添加时间戳
nestWinstonModuleUtilities.format.nestLike('MyApp', { // 美化 NestJS 的日志格式
prettyPrint: true,
}),
),
}),
],
// 异常处理
exceptionHandlers: [
new DailyRotateFile({
dirname: 'logs', // 异常日志文件存放的目录
filename: 'exceptions-%DATE%.log', // 异常日志文件名
datePattern: 'YYYY-MM-DD', // 日期格式
zippedArchive: true, // 是否压缩旧的异常日志文件
maxSize: '20m', // 单个异常日志文件的最大大小
maxFiles: '14d', // 保留的异常日志文件数量
format: winston.format.combine(
winston.format.timestamp(), // 添加时间戳
winston.format.printf(({ timestamp, level, message }) => {
return `${timestamp} [${level.toUpperCase()}] ${message}`;
}),
),
}),
],
}),
],
exports: [WinstonModule],
})
export class LoggerModule {}

在这个模块中,我们做了以下几件事:

  • 导入必要的模块: WinstonModule, winston, DailyRotateFile
  • 配置 forRoot 这是 Winston 模块的核心,我们在这里配置了 transportsexceptionHandlers
    • transports 定义了日志的输出方式。我们配置了两个 transports
      • DailyRotateFile:将日志写入文件,并按照日期进行轮转。dirname 指定日志文件的存放目录,filename 指定日志文件名,datePattern 指定日期格式,zippedArchive 表示是否压缩旧的日志文件,maxSize 指定单个日志文件的最大大小,maxFiles 指定保留的日志文件数量。
      • Console:将日志输出到控制台,方便开发调试。我们使用了 colorize 来给日志添加颜色,timestamp 添加时间戳,nestLike 美化 NestJS 的日志格式。
    • exceptionHandlers 定义了异常日志的输出方式。我们配置了一个 DailyRotateFile,将异常信息写入单独的日志文件,方便排查问题。
  • exports: [WinstonModule] 将 WinstonModule 导出,这样其他模块就可以通过 WinstonModule.forFeature() 来使用 Winston 了。

3.3 使用日志

现在,我们可以在任何需要使用日志的地方注入 LoggerService 并使用它。

// app.module.ts
import { Module } from '@nestjs/common';
import { AppController } from './app.controller';
import { AppService } from './app.service';
import { LoggerModule } from './logger.module';
@Module({
imports: [LoggerModule],
controllers: [AppController],
providers: [AppService],
})
export class AppModule {}
// app.controller.ts
import { Controller, Get, Inject } from '@nestjs/common';
import { AppService } from './app.service';
import { Logger } from 'winston';
import { WINSTON_MODULE_PROVIDER } from 'nest-winston';
@Controller()
export class AppController {
constructor(
private readonly appService: AppService,
@Inject(WINSTON_MODULE_PROVIDER) private readonly logger: Logger,
) {}
@Get()
getHello(): string {
this.logger.debug('This is a debug message.');
this.logger.info('This is an info message.');
this.logger.warn('This is a warn message.');
this.logger.error('This is an error message.');
return this.appService.getHello();
}
}

在这个例子中,我们:

  • 引入 LoggerModuleAppModule 中引入我们创建的日志模块。
  • 注入 WINSTON_MODULE_PROVIDERAppController 中,我们使用 @Inject(WINSTON_MODULE_PROVIDER) 注入了 Winston 的 Logger 实例。WINSTON_MODULE_PROVIDER 是 NestJS 提供的用于访问 Winston 实例的令牌。
  • 使用日志: 我们可以通过 this.logger 来使用 Winston 的各种日志级别,例如 debug, info, warn, error

3.4 运行和测试

启动你的 NestJS 项目,访问你的 API 接口(例如 http://localhost:3000),你会在控制台看到日志输出,同时在 logs 目录下也会生成日志文件。你可以观察日志文件的大小和数量,看看是否符合你的预期。如果一切正常,那么恭喜你,你已经成功配置了 Winston 和日志轮转!

4. 进阶配置和优化

上面的例子是一个基本的配置,你可以根据自己的需求进行更高级的配置和优化。

4.1 日志级别

Winston 支持多种日志级别,从低到高分别是:silly, debug, verbose, info, warn, error。你可以根据不同的环境(如开发、测试、生产)设置不同的日志级别,来控制输出的日志信息量。

// logger.module.ts
import { Module } from '@nestjs/common';
import { WinstonModule } from 'nest-winston';
import * as winston from 'winston';
import * as DailyRotateFile from 'winston-daily-rotate-file';
import { utilities as nestWinstonModuleUtilities } from 'nest-winston';
@Module({
imports: [
WinstonModule.forRoot({
level: process.env.NODE_ENV === 'production' ? 'info' : 'debug', // 根据环境设置日志级别
transports: [
// ... (其他配置)
],
}),
],
exports: [WinstonModule],
})
export class LoggerModule {}

在这个例子中,我们根据 NODE_ENV 环境变量来设置日志级别。在生产环境中,我们只输出 info 和更高级别的日志,而在开发环境中,我们输出 debug 和更低级别的日志。

4.2 日志格式

Winston 提供了丰富的日志格式化选项,你可以自定义日志的输出格式,例如添加时间戳、日志级别、文件名、行号等信息。

// logger.module.ts
import { Module } from '@nestjs/common';
import { WinstonModule } from 'nest-winston';
import * as winston from 'winston';
import * as DailyRotateFile from 'winston-daily-rotate-file';
import { utilities as nestWinstonModuleUtilities } from 'nest-winston';
@Module({
imports: [
WinstonModule.forRoot({
transports: [
new DailyRotateFile({
// ... (其他配置)
format: winston.format.combine(
winston.format.timestamp({
format: 'YYYY-MM-DD HH:mm:ss',
}), // 自定义时间戳格式
winston.format.printf(({ timestamp, level, message, stack }) => {
// 包含堆栈信息
return `${timestamp} [${level.toUpperCase()}] ${message} ${stack ? '\n${stack}' : ''}`;
}),
),
}),
// ... (其他配置)
],
}),
],
exports: [WinstonModule],
})
export class LoggerModule {}

在这个例子中,我们自定义了时间戳的格式,并在日志中包含了堆栈信息,方便排查错误。

4.3 异常处理

除了普通的日志,你还可以配置 Winston 来处理未捕获的异常,将异常信息写入单独的日志文件,方便定位问题。

// logger.module.ts
import { Module } from '@nestjs/common';
import { WinstonModule } from 'nest-winston';
import * as winston from 'winston';
import * as DailyRotateFile from 'winston-daily-rotate-file';
import { utilities as nestWinstonModuleUtilities } from 'nest-winston';
@Module({
imports: [
WinstonModule.forRoot({
// ... (其他配置)
exceptionHandlers: [
new DailyRotateFile({
dirname: 'logs', // 异常日志文件存放的目录
filename: 'exceptions-%DATE%.log', // 异常日志文件名
datePattern: 'YYYY-MM-DD', // 日期格式
zippedArchive: true, // 是否压缩旧的异常日志文件
maxSize: '20m', // 单个异常日志文件的最大大小
maxFiles: '14d', // 保留的异常日志文件数量
format: winston.format.combine(
winston.format.timestamp(),
winston.format.printf(({ timestamp, level, message, stack }) => {
return `${timestamp} [${level.toUpperCase()}] ${message} ${stack ? '\n${stack}' : ''}`;
}),
),
}),
],
}),
],
exports: [WinstonModule],
})
export class LoggerModule {}

在这个例子中,我们配置了 exceptionHandlers,将未捕获的异常信息写入 exceptions-%DATE%.log 文件。

4.4 异步日志

在某些情况下,同步写入日志可能会影响应用程序的性能。为了避免这种情况,你可以使用异步日志。

// logger.module.ts
import { Module } from '@nestjs/common';
import { WinstonModule } from 'nest-winston';
import * as winston from 'winston';
import * as DailyRotateFile from 'winston-daily-rotate-file';
import { utilities as nestWinstonModuleUtilities } from 'nest-winston';
@Module({
imports: [
WinstonModule.forRoot({
transports: [
new DailyRotateFile({
// ... (其他配置)
options: {
// 使用异步写入
maxsize: '20m',
maxFiles: '14d',
zippedArchive: true,
// 异步写入配置
queue: true,
flushInterval: 2000,
},
}),
],
}),
],
exports: [WinstonModule],
})
export class LoggerModule {}

在这个例子中,我们在 DailyRotateFileoptions 中配置了 queue: trueflushInterval,表示使用异步写入。flushInterval 指定了日志写入的间隔时间。

4.5 日志清理策略

除了控制日志文件的大小和数量,你还可以设置更精细的日志清理策略,例如:

  • 定期清理: 使用定时任务,定期扫描日志目录,删除过期的日志文件。
  • 基于磁盘空间: 监控磁盘空间,当磁盘空间不足时,自动删除旧的日志文件。

5. 总结

通过使用 Winston 和 winston-daily-rotate-file,我们可以轻松地在 NestJS 项目中实现日志轮转,有效地控制日志文件的大小和数量,避免磁盘空间耗尽的风险,提高开发和运维效率。希望这篇文章对你有所帮助!

6. 常见问题解答

  • Q:为什么我的日志文件没有轮转?
    • A:请检查你的配置是否正确,特别是 dirname, filename, datePattern, maxSize, maxFiles 这些参数。确保你的项目有写入日志文件的权限,并且你的服务器时间是正确的。
  • Q:如何查看压缩后的日志文件?
    • A:你可以使用 gzip 命令解压缩 .gz 文件,或者使用支持 gzip 的文本编辑器(如 VS Code)打开压缩后的日志文件。
  • Q:日志轮转会影响性能吗?
    • A:如果配置不当,日志轮转可能会影响性能。例如,频繁地创建和删除文件,或者写入大量日志时,可能会导致性能下降。你可以通过调整 maxSize, maxFiles 等参数,以及使用异步日志来优化性能。
  • Q:除了 Winston,还有其他日志库吗?
    • A:是的,还有很多其他的 Node.js 日志库,例如 pino, bunyan, log4js 等。它们各有优缺点,你可以根据自己的需求选择合适的日志库。

7. 更多资源

好了,今天就到这里了。如果你还有其他问题,欢迎在评论区留言,我会尽力解答。咱们下次再见!

老码农 NestJS日志Winston日志轮转

评论点评

打赏赞助
sponsor

感谢您的支持让我们更好的前行

分享

QRcode

https://www.webkt.com/article/7874