前言
在上一篇文章 Nest探索(七)Nest 中打印日志 中,我们探索了 Nest 中常用的打印日志的方法,包括 Logger 类、LoggerService 接口、通过继承 ConsoleLogger 以实现自定义的 logger ,以及实现依赖注入等。不过,在日常的开发工作中,为了更方便、更高效地管理日志,可以集成 Node.js 的 winston 包。
winston 是什么?正如官方介绍所说:"winston
is designed to be a simple and universal logging library with support for multiple transports." winston 是一个 Node.js 日志框架,目前有 22.1k star,支持设置多种 transports、format以及设置不同的日志级别等。
这里,我们将具体实践下。
新建 nest 项目
首先,新建一个 nest 项目。之前也操作了很多次了,没什么难度:
nest new nest-winston-demo-240505 -p pnpm
接着,在 src 目录下,我们添加一个 MyLogger.ts 文件,也就是自定义的日志打印器:
import { LoggerService, LogLevel } from '@nestjs/common';
export class MyLogger implements LoggerService {
log(message: string, context: string) {
console.log(`---log---[${context}]---`, message);
}
error(message: string, context: string) {
console.log(`---error---[${context}]---`, message);
}
warn(message: string, context: string) {
console.log(`---warn---[${context}]---`, message);
}
}
在 main.ts 文件中引入这个自定义的日志打印器:
import { MyLogger } from "./MyLogger";
async function bootstrap() {
const app = await NestFactory.create(AppModule);
app.useLogger(new MyLogger());
await app.listen(3000);
}
运行服务:
pnpm run start:dev
我们可以看到自定义 logger 生效了:
[Nest] 16084 - 2024/05/05 23:46:50 LOG [NestFactory] Starting Nest application...
[Nest] 16084 - 2024/05/05 23:46:50 LOG [InstanceLoader] AppModule dependencies initialized +25ms
---log---[RoutesResolver]--- AppController {/}:
---log---[RouterExplorer]--- Mapped {/, GET} route
---log---[NestApplication]--- Nest application successfully started
接着,在 AppController 里添加 logger:
import { Controller, Get, Logger } from '@nestjs/common';
import { AppService } from './app.service';
@Controller()
export class AppController {
constructor(private readonly appService: AppService) {}
private logger = new Logger()
@Get()
getHello(): string {
this.logger.log('hhh', AppController.name);
return this.appService.getHello();
}
}
浏览器访问 http://localhost:3000/ ,可以看到打印了
---log---[AppController]--- hhh
也就是说,自定义 logger 生效了。
集成 winston
在 Nest 应用中成功地引入自定义的 logger 后,我们接着集成 winston 日志框架,并进行相关的设置。
首先,安装 winston 包:
pnpm i winston
接着,在 MyLogger.ts 中引入 winston ,这里是使用 createLogger() 初始化 this.logger 属性,并替换原来的 console.log 为 this.logger.log() 等方法。
import { LoggerService, LogLevel } from '@nestjs/common';
import { createLogger, format, Logger, transports } from 'winston';
export class MyLogger implements LoggerService {
private logger: Logger;
constructor() {
this.logger = createLogger({
level: 'debug',
format: format.combine(format.colorize(), format.simple()),
transports: [new transports.Console()],
});
}
log(message: string, context: string) {
this.logger.log('info', `[${context}] ${message}`);
}
error(message: string, context: string) {
this.logger.log('error', `[${context}] ${message}`);
}
warn(message: string, context: string) {
this.logger.log('warn', `[${context}] ${message}`);
}
}
可以看到有新的打印,这里打印的日志就是 winston 的日志记录了。
[Nest] 15600 - 2024/05/05 23:52:12 LOG [NestFactory] Starting Nest application...
[Nest] 15600 - 2024/05/05 23:52:12 LOG [InstanceLoader] AppModule dependencies initialized +10ms
info: [RoutesResolver] AppController {/}:
info: [RouterExplorer] Mapped {/, GET} route
info: [NestApplication] Nest application successfully started
但是,我们不难发现日志的样式有点不规范,如何进行自定义和美化呢?
这里,引入了 dayjs 用于格式化输出日期,引入 chalk@4 用于打印自定义的颜色。
pnpm i dayjs chalk@4
相关配置如下,具体是,设置 transports 数组:
- 在
new transports.Console()
设置打印日志的格式,最终输出内容设置为${appStr} ${time} ${level} ${contextStr} ${message}
; - 在
new transports.File()
设置输出日志的格式,这里是通过format: format.combine(format.timestamp(), format.json()),
指定为 json 格式,并加上时间戳;
import { LoggerService, LogLevel } from '@nestjs/common';
import { createLogger, format, Logger, transports } from 'winston';
import * as chalk from 'chalk';
import * as dayjs from 'dayjs';
export class MyLogger implements LoggerService {
private logger: Logger;
constructor() {
this.logger = createLogger({
level: 'debug',
transports: [
// 设置打印日志的格式
new transports.Console({
format: format.combine(
format.colorize(),
format.printf(({ context, level, message, time }) => {
const appStr = chalk.green(`[NEST]`);
const contextStr = chalk.yellow(`[${context}]`);
return `${appStr} ${time} ${level} ${contextStr} ${message} `;
}),
),
}),
// 设置输出日志的格式
new transports.File({
// 指定为 json 格式,加上时间戳
format: format.combine(
format.timestamp(),
format.json()
),
filename: '111.log',
dirname: 'log'
})
],
});
}
log(message: string, context: string) {
const time = dayjs(Date.now()).format('YYYY-MM-DD HH:mm:ss');
this.logger.log('info', message, { context, time });
}
error(message: string, context: string) {
const time = dayjs(Date.now()).format('YYYY-MM-DD HH:mm:ss');
this.logger.log('info', message, { context, time });
}
warn(message: string, context: string) {
const time = dayjs(Date.now()).format('YYYY-MM-DD HH:mm:ss');
this.logger.log('info', message, { context, time });
}
}
最终,可以看到打印日志格式为:
[Nest] 11500 - 2024/05/05 23:53:07 LOG [NestFactory] Starting Nest application...
[Nest] 11500 - 2024/05/05 23:53:07 LOG [InstanceLoader] AppModule dependencies
initialized +22ms
[NEST] 2024-05-05 23:53:08 info [RoutesResolver] AppController {/}:
[NEST] 2024-05-05 23:53:08 info [RouterExplorer] Mapped {/, GET} route
[NEST] 2024-05-05 23:53:08 info [NestApplication] Nest application successfully started
并在 log 目录下生成了日志文件 111.log ,每行的内容均为 json 格式的文本。
至此,我们就完成了 nest 中日志框架 winston 的集成。
后记
总的来说,在日常的开发工作中,我们可以通过集成 Node.js 的 winston 日志框架,实现更方便、更高效地管理 Nest 应用中的日志的需求。
我们可以设置 winston 的多种 transports ,实现设置不同的打印日志的格式;或是设置输出日志的格式,指定输出内容为 json 格式、指定输出文件的目录以及文件格式等等。
此外,我们可以借助 dayjs 和 chalk,规范和美化输出日志的格式,实现相应的定制化开发的需求。