目标
项目上线后,日志如果还依赖console的方式显示在控制台的话,无法方便查看。需要将日志存储到/logs文件夹中,根据访问时间进行查询
实现
使用winston来配置日志格式并存储
安装依赖
npm i winston nest-winston winston-daily-rotate-file -S
main.ts配置日志
// ...
import * as winston from 'winston';
import { createLogger } from 'winston';
import { utilities, WinstonModule } from 'nest-winston';
import 'winston-daily-rotate-file'; // 用于存储日志到文件
async function bootstrap() {
// 日志配置
const instance = createLogger({
// 日志选项
transports: [
new winston.transports.Console({
level: 'info',
// 字符串拼接
format: winston.format.combine(
winston.format.timestamp(),
utilities.format.nestLike(),
),
}),
// warn、error日志存储到/logs/application-日期.log文件中
new winston.transports.DailyRotateFile({
level: 'warn',
dirname: 'logs',
filename: 'application-%DATE%.log',
datePattern: 'YYYY-MM-DD-HH',
zippedArchive: true,
maxSize: '20m',
maxFiles: '14d',
format: winston.format.combine(
winston.format.timestamp(),
winston.format.simple(),
),
}),
// info日志存储到/logs/info-日期.log文件中
new winston.transports.DailyRotateFile({
level: 'info',
dirname: 'logs',
filename: 'info-%DATE%.log',
datePattern: 'YYYY-MM-DD-HH',
zippedArchive: true,
// 文件大小
maxSize: '20m',
// 最多14 天
maxFiles: '14d',
format: winston.format.combine(
winston.format.timestamp(),
winston.format.simple(),
),
}),
],
});
const app = await NestFactory.create(AppModule, {
logger: WinstonModule.createLogger({
instance,
}),
});
await app.listen(3000);
}
bootstrap();
业务代码中添加logger打印
import { Controller, Get, Logger } from '@nestjs/common';
@Controller()
export class AppController {
constructor(private readonly logger: Logger) {}
@Get()
index(): string {
this.logger.log('log 类型日志');
this.logger.warn('warn 类型日志');
this.logger.error('error 类型日志');
return 'Hello world'
}
}
访问首页,查看终端和/logs文件夹
终端打印了日志内容
info-日期.log 文件中打印了所有日志
application-日期.log 文件中打印了warn、error级别日志
部署
// 打包
npm run build
// 使用pm2部署代码
pm2 start dist/main.js
部署后访问,可以看到/logs文件内容更新
全局接口出入参日志
在全局守卫添加logger,全局守卫配置详见另一篇博客:nestjs守卫/全局守卫校验jwt-CSDN博客
import { Injectable, NestInterceptor, ExecutionContext, HttpException, HttpStatus, Logger } from '@nestjs/common';
import { Observable } from 'rxjs';
import { map } from 'rxjs/operators';
@Injectable()
export class GlobalGuard implements NestInterceptor {
constructor(private logger: Logger) {}
intercept(context: ExecutionContext, next): Observable<any> {
// 在这里执行全局守卫逻辑
const request = context.switchToHttp().getRequest();
// ...
// 不需要校验的接口、校验通过的接口直接放行
return next.handle().pipe(
// 打印日志
map((data)=> {
this.logger.log(`
url:${url}
参数:${JSON.stringify(request.method == 'POST' ? request.body : request.query)}
响应:${JSON.stringify(data)}
`);
return data;
})
);
}
}
查看日志