WEBKT

NestJS 日志进阶:winston-daily-rotate-file 多环境配置与实践指南

72 0 0 0

前言:为什么日志管理如此重要?

一、 基础配置:让日志先跑起来

1.1 创建日志服务

1.2 在 AppModule 中引入

1.3 LoggerModule

1.4 使用日志服务

二、 进阶配置:多环境日志管理

2.1 创建配置文件

2.2 加载配置

2.3 在 AppModule 中引入 ConfigModule

三、 实践技巧:让日志更实用

3.1 自定义日志格式

3.2 使用日志上下文

3.3 异步日志

3.4 日志监控和告警

四、 总结与思考

常见问题解答(FAQ)

前言:为什么日志管理如此重要?

“哥们,你这代码又崩了?”

“啊?不能吧,我本地跑得好好的!”

“你自己看日志去!”

相信不少开发者都经历过类似的“灵魂拷问”。在软件开发的世界里,日志就像飞机的“黑匣子”,记录着应用程序运行过程中的每一个细节。无论是调试代码、排查线上问题,还是分析用户行为,日志都扮演着至关重要的角色。一个好的日志系统,能让你在问题发生时迅速定位,减少“背锅”的几率。

对于 Node.js 开发者来说,Winston 是一款非常流行的日志库,而 winston-daily-rotate-file 则是 Winston 的一个强大扩展,它能够按照日期自动分割日志文件,避免单个日志文件过大,方便日志的管理和归档。更重要的是,我们可以根据不同的环境(开发、测试、生产)配置不同的日志级别和输出格式,这对于构建健壮、可维护的应用程序至关重要。

今天,咱们就来聊聊如何在 NestJS 项目中玩转 winston-daily-rotate-file,打造一个既实用又高效的日志系统。

一、 基础配置:让日志先跑起来

在开始之前,我们需要先安装必要的依赖:

npm install winston winston-daily-rotate-file @nestjs/config

@nestjs/config 是 NestJS 官方提供的配置模块,它可以帮助我们更方便地管理不同环境的配置。

1.1 创建日志服务

首先,我们创建一个 logger.service.ts 文件,用于封装 Winston 的相关配置:

// src/logger/logger.service.ts
import { Injectable, LoggerService } from '@nestjs/common';
import * as winston from 'winston';
import 'winston-daily-rotate-file';
@Injectable()
export class Logger implements LoggerService {
private logger: winston.Logger;
constructor() {
this.logger = winston.createLogger({
level: 'info', // 默认日志级别
format: winston.format.combine(
winston.format.timestamp(),
winston.format.json()
),
transports: [
new winston.transports.Console(), // 输出到控制台
new winston.transports.DailyRotateFile({
filename: 'logs/application-%DATE%.log', // 日志文件名
datePattern: 'YYYY-MM-DD',
zippedArchive: true, // 是否压缩
maxSize: '20m', // 最大文件大小
maxFiles: '14d', // 最多保留天数
}),
],
});
}
log(message: string, context?: string) {
this.logger.log('info', message, { context });
}
error(message: string, trace?: string, context?: string) {
this.logger.error(message, { trace, context });
}
warn(message: string, context?: string) {
this.logger.warn(message, { context });
}
debug(message: string, context?: string) {
this.logger.debug(message, { context });
}
verbose(message: string, context?: string) {
this.logger.verbose(message, { context });
}
}

1.2 在 AppModule 中引入

接下来,我们需要在 app.module.ts 中引入 LoggerModule

// src/app.module.ts
import { Module } from '@nestjs/common';
import { AppController } from './app.controller';
import { AppService } from './app.service';
import { LoggerModule } from './logger/logger.module'; // 引入 LoggerModule
@Module({
imports: [LoggerModule], // 导入 LoggerModule
controllers: [AppController],
providers: [AppService],
})
export class AppModule {}

1.3 LoggerModule

// src/logger/logger.module.ts
import { Module } from '@nestjs/common';
import { Logger } from './logger.service';
@Module({
providers: [Logger],
exports: [Logger], // 导出 Logger,以便其他模块使用
})
export class LoggerModule {}

1.4 使用日志服务

现在,我们就可以在任何需要的地方注入 Logger 服务,并使用它来记录日志了:

// src/app.service.ts
import { Injectable } from '@nestjs/common';
import { Logger } from './logger/logger.service'; // 导入 Logger
@Injectable()
export class AppService {
constructor(private readonly logger: Logger) {}
getHello(): string {
this.logger.log('访问了首页', AppService.name); // 记录日志
return 'Hello World!';
}
}

二、 进阶配置:多环境日志管理

在实际开发中,我们通常需要根据不同的环境(开发、测试、生产)配置不同的日志级别和输出格式。例如,在开发环境中,我们希望看到详细的调试信息,而在生产环境中,我们只需要记录错误和警告信息。

2.1 创建配置文件

首先,我们在 src 目录下创建一个 config 文件夹,并在其中创建三个配置文件:development.yamltest.yamlproduction.yaml

# src/config/development.yaml
logger:
level: debug
format: simple
# src/config/test.yaml
logger:
level: info
format: json
# src/config/production.yaml
logger:
level: warn
format: json

2.2 加载配置

接下来,我们需要修改 logger.service.ts,使用 @nestjs/config 加载配置:

// src/logger/logger.service.ts
import { Injectable, LoggerService } from '@nestjs/common';
import { ConfigService } from '@nestjs/config'; // 导入 ConfigService
import * as winston from 'winston';
import 'winston-daily-rotate-file';
@Injectable()
export class Logger implements LoggerService {
private logger: winston.Logger;
constructor(private configService: ConfigService) {
const loggerConfig = this.configService.get('logger');
const level = loggerConfig.level || 'info';
const format = loggerConfig.format === 'json' ? winston.format.json() : winston.format.simple();
this.logger = winston.createLogger({
level: level,
format: winston.format.combine(
winston.format.timestamp(),
format
),
transports: [
new winston.transports.Console(),
new winston.transports.DailyRotateFile({
filename: 'logs/application-%DATE%.log',
datePattern: 'YYYY-MM-DD',
zippedArchive: true,
maxSize: '20m',
maxFiles: '14d',
}),
],
});
}
log(message: string, context?: string) {
this.logger.log('info', message, { context });
}
error(message: string, trace?: string, context?: string) {
this.logger.error(message, { trace, context });
}
warn(message: string, context?: string) {
this.logger.warn(message, { context });
}
debug(message: string, context?: string) {
this.logger.debug(message, { context });
}
verbose(message: string, context?: string) {
this.logger.verbose(message, { context });
}
}

2.3 在 AppModule 中引入 ConfigModule

最后,我们需要在 app.module.ts 中引入 ConfigModule,并配置配置文件的路径:

// src/app.module.ts
import { Module } from '@nestjs/common';
import { ConfigModule } from '@nestjs/config'; // 导入 ConfigModule
import { AppController } from './app.controller';
import { AppService } from './app.service';
import { LoggerModule } from './logger/logger.module';
@Module({
imports: [
ConfigModule.forRoot({
envFilePath: [`src/config/.${process.env.NODE_ENV}.yaml`], // 配置文件路径
isGlobal: true,
}),
LoggerModule,
],
controllers: [AppController],
providers: [AppService],
})
export class AppModule {}

现在,我们就可以通过设置 NODE_ENV 环境变量来切换不同的日志配置了。例如,在开发环境中,我们可以设置 NODE_ENV=development,在生产环境中,我们可以设置 NODE_ENV=production

三、 实践技巧:让日志更实用

3.1 自定义日志格式

Winston 提供了多种内置的日志格式,例如 jsonsimpleprintf 等。我们也可以通过 winston.format.combine 将多个格式组合起来使用。此外,我们还可以自定义日志格式,以满足特定的需求。

例如,我们可以创建一个自定义格式,将日志级别、时间戳、上下文和消息格式化为更易读的形式:

// src/logger/my-logger-format.ts
import { format } from 'winston';
const myFormat = format.printf(({ level, message, timestamp, context }) => {
return `${timestamp} [${context}] ${level}: ${message}`;
});
export default myFormat;

然后在logger.service.ts引入:

// src/logger/logger.service.ts
// ...其他代码
format: winston.format.combine(
winston.format.timestamp(),
myFormat // 使用自定义格式
),
// ...其他代码

3.2 使用日志上下文

在记录日志时,我们可以通过 context 参数提供额外的上下文信息。这对于区分不同模块、不同请求的日志非常有帮助。例如:

this.logger.log('用户登录成功', 'AuthService');
this.logger.error('创建订单失败', 'OrderService', err.stack);

3.3 异步日志

如果你的应用程序需要处理大量的日志,同步写入日志文件可能会成为性能瓶颈。为了解决这个问题,我们可以使用异步日志。

Winston 默认情况下是同步写入日志的。要实现异步日志,我们可以使用 winston-transport 提供的 TransportStream
具体实现稍微复杂,这里只提供思路,感兴趣的同学可以自行研究。

3.4 日志监控和告警

对于生产环境的应用程序,我们需要对日志进行监控和告警。当出现错误或异常时,我们需要及时收到通知,以便快速响应。

我们可以使用一些第三方工具来实现日志监控和告警,例如 ELK Stack(Elasticsearch, Logstash, Kibana)、Sentry、Graylog 等。

四、 总结与思考

日志管理是软件开发中不可或缺的一部分。一个好的日志系统,可以帮助我们更快地定位问题,提高开发效率,降低维护成本。

在本文中,我们介绍了如何在 NestJS 项目中使用 winston-daily-rotate-file 实现日志的自动分割和多环境配置。我们还讨论了一些实践技巧,例如自定义日志格式、使用日志上下文、异步日志以及日志监控和告警。

当然,日志管理不仅仅是技术问题,更是一种思维方式。我们需要养成良好的日志记录习惯,思考哪些信息需要记录,如何记录,如何利用日志来改进我们的应用程序。

希望本文能够帮助你更好地理解和应用 NestJS 中的日志管理。如果你有任何问题或建议,欢迎在评论区留言。

常见问题解答(FAQ)

Q:winston-daily-rotate-file 会自动删除过期的日志文件吗?

A:是的,winston-daily-rotate-file 会根据你配置的 maxFiles 选项自动删除过期的日志文件。

Q:我可以在运行时动态修改日志级别吗?

A:可以,但是不推荐这样做。动态修改日志级别可能会导致日志记录不一致,增加排查问题的难度。如果确实需要动态调整日志级别,建议使用专门的日志管理工具或平台。

Q:除了 winston-daily-rotate-file,还有其他推荐的 Winston 传输器吗?

A:除了 winston-daily-rotate-file,还有一些常用的 Winston 传输器,例如:

  • winston-console: 将日志输出到控制台。
  • winston-file: 将日志写入到单个文件。
  • winston-syslog: 将日志发送到 Syslog 服务器。
  • winston-cloudwatch: 将日志发送到 AWS CloudWatch。

你可以根据自己的需求选择合适的传输器。

Q:如何处理敏感信息,避免记录到日志中?

A:在记录日志时,一定要注意避免将敏感信息(例如密码、密钥、身份证号等)记录到日志中。可以使用脱敏处理或者占位符代替。也可以使用专门的日志安全工具来检测和过滤敏感信息。

技术老司机 NestJSwinston日志管理

评论点评

打赏赞助
sponsor

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

分享

QRcode

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