在前端开发过程中,通常会使用到控制台打印的方式对程序进行调试,以便记录错误以及定位问题。在企业级开发中我们需要在业务系统中集成日志模块,来及时发现问题产生的时间以及原因,这里将从两个方面来介绍Nest中的日志系统。
- 常见日志以及记录方式
- 第三方日志方案:pino(体积小、简单易用)、winston(官方推荐方案、日志信息更详细)
日志分类
按类别分类
在第三方的日志系统中,通常都会实现下列方法(名称可能有所不同),通过以下方法来输出不同等级的日志。
- Log:通用日志,按需进行打印,用于日常开发调试
- Warning:警告日志,比如某些包和库版本过低即将淘汰
- Error:
- Debug:调试日志
- Verbose:详细日志,包括所有的操作和详细信息
按功能分类
- 错误日志:方便开发者定位问题,给予用户友好的提示
- 调试日志:方便开发
- 请求日志:记录用户的敏感行为
nest内置日志模块--Logger
一般使用
首先介绍下nest官方内置的日志模块--Logger,我们可以在main.ts中设置日志的监控等级,为logger属性传入一个数组,在数组中填充想要开启的日志等级即可。由于nest中集成了ts,我们可以点到logger中来找到数组更多的可填充值即可用日志等级。同时,我们可以通过new创建的Logger实例对象来调用warn、verbose等方法手动输出日志信息。
import { NestFactory } from '@nestjs/core';
import { AppModule } from './app.module';
import { Logger } from '@nestjs/common';
async function bootstrap() {
const logger = new Logger();
const app = await NestFactory.create(AppModule, {
logger: ['error', 'warn'],
});
await app.listen(3000);
logger.warn('应用已启动,端口:3000');
}
bootstrap();
下方控制台输出中黄色高亮的信息即代码第11行输出的信息。
在某一指定模块中使用
当在某一指定模块中使用Logger时,我们可以在模块的controller中实例化Logger。以user模块为例,与上一步不同的是这里我们需要额外为Logger构造器传入参数:UserController.name。之后,在构造器中通过this调用并输出相关日志信息时,我们可以看到在[ ]中显示出日志来自哪一模块,方便后续问题的定位。
import { Controller, Get, Logger } from '@nestjs/common';
@Controller('user')
export class UserController {
private logger = new Logger(UserController.name);
constructor() {
this.logger.log('user模块实例化完成');
}
@Get()
getUser(): any {
return ' ';
}
}
第三方日志模块--Pino
介绍完Logger之后,再来介绍一种在nodejs项目中常用的轻量级日志模块-pino。在pino的npm介绍中,我们可以看到通过pino完成的日志输出效率相较于其他日志模块来说更快。这里我们需要完成两步操作即可开始使用:
1.安装pino:npm install nestjs-pino
2.在模块中引入依赖并在模块中注册(以演示中的user模块为例)
在user模块中注册pino:
import { Module } from '@nestjs/common';
import { UserController } from './user.controller';
import { LoggerModule } from 'nestjs-pino';
@Module({
imports: [LoggerModule.forRoot()],
controllers: [UserController]
})
export class UserModule {
}
在controller中通过依赖注入的方式实例化Logger(这里要注意Logger是nestjs-pino包里面的):
import { Controller, Get } from '@nestjs/common';
import { Logger } from 'nestjs-pino';
@Controller('user')
export class UserController {
constructor(
private logger: Logger,
) {
this.logger.log('user模块实例化完成');
}
@Get('getuser')
getUser(): any {
return ' ';
}
}
可以看到当发送请求后,控制台输出了日志信息,包括请求的方法、url、参数等信息。
可以看到,pino原始输出的日志信息虽然详细,但是并没有格式化,导致日志的可读性较差。接下来我们可以使用pino-pretty来格式化输出信息。
首先,需要通过 npm i pino-pretty 安装依赖,之后在引入pino模块的module文件中配置中间件:
import { Module } from '@nestjs/common';
import { UserController } from './user.controller';
import { LoggerModule } from 'nestjs-pino';
@Module({
imports: [LoggerModule.forRoot({
pinoHttp: {
transport: {
target: 'pino-pretty',
options: {
colorize: true,
}
}
}
})],
controllers: [UserController]
})
export class UserModule {
}
之后,我们可以看到日志信息字体变成彩色,并且具有格式化的效果。
第三方日志模块--winston
winston是一个集成度较高的日志模块,拥有比pino更多的定制化功能以及更详细的日志信息输出。接下来介绍使用步骤:
- 安装依赖
npm i nest-winston winston - 在main.ts中配置winston,替换掉nest官方内置的Logger系统
import { NestFactory } from '@nestjs/core';
import { AppModule } from './app.module';
import { createLogger } from 'winston';
import * as winston from 'winston';
import { WinstonModule, utilities } from 'nest-winston';
async function bootstrap() {
// 1.创建Logger实例
const instance = createLogger({
// 2.配置输出
transports: [
new winston.transports.Console({
format: winston.format.combine(
winston.format.timestamp(),
utilities.format.nestLike(),
),
}),
],
});
const app = await NestFactory.create(AppModule, {
// 3.将第一步的instance传入
logger: WinstonModule.createLogger({
instance,
}),
});
await app.listen(3000);
}
bootstrap();
-
其他模块怎么使用winston?这里有两种方式:
①.在app模块中将Logger模块暴露,以供其他模块引入使用,我们需要在其他需要使用到winston的模块module文件中手动imports 。
②.将app模块设置为全局模块,这样就不用在每个模块中分别import引入了,全局模块可以完成对provider数组中的模块方法的注册
import { Global, Logger, Module } from '@nestjs/common';
import { AppController } from './app.controller';
import { AppService } from './app.service';
import { UserModule } from './user/user.module';
// 方式②: 将module声明为全局模块
@Global()
@Module({
imports: [UserModule],
controllers: [AppController],
providers: [AppService],
// 方式①: 暴露模块供其他模块引入,其他模块需要手动imports
exports: [Logger],
imports: [],
})
export class AppModule {}
- 在想要使用winston日志输出的模块,通过依赖注入的方式获取logger对象,并完成日志输出
import { Controller, Get } from '@nestjs/common';
import { Logger } from '@nestjs/common';
@Controller('user')
export class UserController {
constructor(
private logger: Logger,
) {
this.logger.log('user finish');
}
@Get('getuser')
getUser(): any {
return ' ';
}
}
这里我们需要注意的是,winston官方npm仓库下方的配置方法有误,并不能完成logger的依赖注入,可按照本文的方法进行配置。更多使用方法以及配置项请参考文档。