NestJS 日志进阶:Winston & winston-daily-rotate-file 打造可扩展日志模块
NestJS 日志进阶:Winston & winston-daily-rotate-file 打造可扩展日志模块
为什么选择 Winston?
动手实践:搭建基础日志模块
进阶:自定义传输器
进阶:日志过滤器
进阶:错误追踪集成
总结
NestJS 日志进阶:Winston & winston-daily-rotate-file 打造可扩展日志模块
嗨,各位高级开发者们!相信你们在构建 NestJS 应用时,一定深知日志系统的重要性。一个好的日志系统不仅能帮你快速定位问题,还能让你洞察应用的运行状态。今天,咱们就来聊聊如何在 NestJS 中使用 winston
和 winston-daily-rotate-file
打造一个可扩展的日志模块,让你的日志系统更上一层楼。
为什么选择 Winston?
在 Node.js 的世界里,winston
绝对是日志库中的佼佼者。它灵活、强大,拥有丰富的特性,能满足各种复杂的日志需求。而且,winston
的社区非常活跃,这意味着你能找到大量的资源和帮助。
winston-daily-rotate-file
是 winston
的一个传输器(Transport),它能按日期自动分割日志文件,避免单个日志文件过大,方便日志管理和归档。
动手实践:搭建基础日志模块
- 安装依赖
首先,咱们需要安装 winston
和 winston-daily-rotate-file
:
npm install winston winston-daily-rotate-file
- 创建日志模块
在 NestJS 中,咱们通常会创建一个独立的模块来管理日志。创建一个 logger.module.ts
文件:
import { Module } from '@nestjs/common'; import { LoggerService } from './logger.service'; @Module({ providers: [LoggerService], exports: [LoggerService], }) export class LoggerModule {}
- 创建日志服务
接下来,创建一个 logger.service.ts
文件,这是咱们日志模块的核心:
import { Injectable, LoggerService as NestLoggerService } from '@nestjs/common'; import * as winston from 'winston'; import 'winston-daily-rotate-file'; @Injectable() export class LoggerService implements NestLoggerService { private readonly logger: winston.Logger; constructor() { this.logger = winston.createLogger({ level: 'info', // 设置日志级别 format: winston.format.combine( winston.format.timestamp(), // 添加时间戳 winston.format.json() // 使用 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.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 }); } }
在这个服务中,咱们:
- 实现了 NestJS 的
LoggerService
接口,这样就能在 NestJS 的其他地方注入并使用咱们的日志服务了。 - 使用
winston.createLogger
创建了一个winston
实例。 - 设置了日志级别为
info
,这意味着info
、warn
、error
级别的日志都会被记录。 - 使用了
winston.format.combine
组合了多个格式化器:
*winston.format.timestamp
添加时间戳。
*winston.format.json
将日志格式化为 JSON。 - 配置了两个传输器:
*winston.transports.Console
将日志输出到控制台。
*winston.transports.DailyRotateFile
将日志按日期分割保存到文件中。 - 定义了
log
、error
、warn
、debug
、verbose
方法,分别对应不同的日志级别。
- 使用日志服务
现在,咱们可以在其他模块中使用这个日志服务了。例如,在 app.controller.ts
中:
import { Controller, Get } from '@nestjs/common'; import { LoggerService } from './logger/logger.service'; @Controller() export class AppController { constructor(private readonly logger: LoggerService) {} @Get() getHello(): string { this.logger.log('访问了首页', AppController.name); return 'Hello World!'; } }
这样,每次访问首页,咱们的日志系统就会记录一条 info
级别的日志。
进阶:自定义传输器
winston
的强大之处在于它的可扩展性。你可以自定义传输器,将日志发送到任何你想发送的地方,比如数据库、消息队列、第三方日志服务等。
下面是一个简单的自定义传输器示例,它将日志输出到一个名为 custom.log
的文件中:
import * as Transport from 'winston-transport'; import * as fs from 'fs'; export class CustomTransport extends Transport { constructor(opts?) { super(opts); } log(info: any, callback: () => void) { setImmediate(() => { this.emit('logged', info); }); fs.appendFile('custom.log', JSON.stringify(info) + '\n', (err) => { if (err) { console.error('写入日志文件失败:', err); } }); callback(); } }
然后,在logger.service.ts
中添加这个自定义的transport:
// ... 其他代码 constructor() { this.logger = winston.createLogger({ // ... 其他配置 transports: [ new winston.transports.Console(), new winston.transports.DailyRotateFile({ // ... DailyRotateFile 配置 }), new CustomTransport(), // 添加自定义传输器 ], }); } // ... 其他代码
进阶:日志过滤器
有时候,你可能只想记录特定类型的日志。这时,你可以使用日志过滤器。
winston
本身并没有提供专门的过滤器功能,但你可以通过自定义格式化器来实现。
下面是一个简单的日志过滤器示例,它只记录包含特定上下文的日志:
import { format } from 'winston'; const contextFilter = format((info, opts) => { if (info.context === opts.context) { return info; } return false; // 过滤掉不符合条件的日志 });
然后,在 logger.service.ts
中使用这个过滤器:
// ... 其他代码 this.logger = winston.createLogger({ // ... 其他配置 format: winston.format.combine( contextFilter({ context: 'AppController' }), // 只记录 context 为 AppController 的日志 winston.format.timestamp(), winston.format.json() ), // ... 其他配置 }); // ... 其他代码
进阶:错误追踪集成
当应用发生错误时,你可能希望将错误信息发送到错误追踪服务,比如 Sentry、Bugsnag 等。
以 Sentry 为例,你可以这样做:
- 安装 Sentry SDK
npm install @sentry/node @sentry/tracing
- 初始化 Sentry
在你的应用入口文件(通常是 main.ts
)中初始化 Sentry:
import * as Sentry from '@sentry/node'; import * as Tracing from '@sentry/tracing'; Sentry.init({ dsn: 'YOUR_SENTRY_DSN', // 替换为你的 Sentry DSN integrations: [ new Sentry.Integrations.Http({ tracing: true }), new Tracing.Integrations.Express({ app }), // 如果你使用了 Express ], tracesSampleRate: 1.0, // 采样率 });
- 在日志服务中捕获错误
在 logger.service.ts
中,修改 error
方法:
error(message: string, trace?: string, context?: string) { this.logger.error(message, { trace, context }); Sentry.captureException(new Error(message), { // 捕获错误并发送到 Sentry contexts:{ trace: trace, context: context } }); }
现在当程序发生错误,sentry也能同时收到错误信息,方便追踪和管理
总结
通过本文,相信你已经掌握了如何在 NestJS 中使用 winston
和 winston-daily-rotate-file
打造一个可扩展的日志模块。记住,日志系统是应用的重要组成部分,一个好的日志系统能让你事半功倍。希望这些内容对你有所帮助,让你的 NestJS 应用更上一层楼!
如果你有任何问题或想法,欢迎在评论区留言,咱们一起交流学习!