工具版本
- nodejs:20.9.0
- npm:10.1.0
- nestjs:10.0.0
安装相关包
pnpm i winston nest-winston winston-daily-rotate-file
配置日志
日志配置会用到环境变量,具体怎么请参考从零开始搭建nestjs项目-1-环境配置 - 掘金 (juejin.cn)
新建日志的配置文件(/src/config/log.config.ts):
import { WinstonModuleOptions } from "nest-winston"
import { transports, format } from "winston";
const env = process.env.NODE_ENV || 'development'
import "winston-daily-rotate-file";
export const winstonModuleOptions: WinstonModuleOptions = {
transports: (() => {
//开发环境下输出到控制台
if (env === 'development') {
return [new transports.Console()]
}
if (env === 'production') {
return [
// 记录错误日志
new transports.DailyRotateFile({
level: "error",
dirname: `logs/errors`,
filename: `%DATE%-error.log`,
datePattern: "YYYY-MM-DD",
maxSize: "20m"
}),
new transports.DailyRotateFile({
dirname: `logs/logs`,
filename: `%DATE%-combined.log`,
datePattern: "YYYY-MM-DD",
maxSize: "20m",
format: format.combine(
format((info) => {
if (info.level === "error") {
return false; // 过滤掉'error'级别的日志
}
return info;
})()
)
})
]
}
return []
})()
}
app.module.ts中
import { Module } from '@nestjs/common';
import { AppController } from './app.controller';
import { AppService } from './app.service';
import { ConfigModule } from '@nestjs/config'
import { WinstonModule } from 'nest-winston';
import { winstonModuleOptions } from './config/log.config';
@Module({
imports: [
// 环境变量配置
ConfigModule.forRoot({
isGlobal: true,
// 指定存储环境变量的文件, 靠前的文件拥有较高的优先级
envFilePath: [`.env.${process.env.NODE_ENV || 'development'}`, '.env'],
}),
// 日志
WinstonModule.forRoot(winstonModuleOptions),
],
controllers: [AppController],
providers: [AppService],
})
export class AppModule { }
使用
在服务中注入即可:@Inject(WINSTON_MODULE_PROVIDER) private readonly logger: Logger
示例:
import { Inject, Injectable } from '@nestjs/common';
import { WINSTON_MODULE_PROVIDER } from 'nest-winston';
import { Logger } from 'winston';
@Injectable()
export class AppService {
constructor(
@Inject(WINSTON_MODULE_PROVIDER) private readonly logger: Logger
) {
this.logger.error('demo')
}
}
利用nest的全局异常处理机制记录日志
异常过滤器如何使用请参考异常过滤器 | NestJS 中文网 (nodejs.cn)
新建过滤器
nest g f filters/all-exception
我这里只记录了http的异常日志
import { ArgumentsHost, BadRequestException, Catch, ExceptionFilter, HttpException, Inject } from '@nestjs/common';
import { WINSTON_MODULE_PROVIDER } from 'nest-winston';
import { Logger } from 'winston';
import * as requestIp from 'request-ip'
@Catch()
export class AllExceptionFilter implements ExceptionFilter {
constructor(
@Inject(WINSTON_MODULE_PROVIDER) private readonly logger: Logger
) { }
catch(exception: any, host: ArgumentsHost) {
const ctx = host.switchToHttp();
const response = ctx.getResponse();
const request = ctx.getRequest();
// 如果是http异常
if (exception instanceof HttpException) {
const status = exception.getStatus();
const results = exception.getResponse() as any;
const code = results.statusCode;
// 返回的对象
const jsonData = {
code: code,
message: results.message,
data: null
};
// 参数校验错误,默认都是BadRequestException
const isArrayMessage = Array.isArray(results.message);
const isValidationError =
isArrayMessage && typeof results.message[0] === "string" && results.message[0].includes("⓿");
if (exception instanceof BadRequestException && isValidationError) {
const message: Array<{ field: string; message: Array<string> }> = [];
results.message.forEach((item) => {
const [key, val] = item.split("⓿") as [string, string];
const findData = message.find((item) => item.field === key);
if (findData) {
findData.message.push(val);
} else {
message.push({ field: key, message: [val] });
}
});
jsonData.message = message;
}
// // 记录日志
const { method, originalUrl, body, query, params } = request;
this.logger.error("HttpException", {
res: {
code,
status,
message: jsonData.message
},
req: {
method,
url: originalUrl,
body,
query,
params,
ip: requestIp.getClientIp(request),
timestamp: new Date().toISOString()
}
});
return response.status(status).json(jsonData);
}
return response.status(500).json({ code: 500, msg: '服务器内部异常',success:false });
}
}
在app.module.ts中配置过滤器,使全局使用
import { Module } from '@nestjs/common';
import { AppController } from './app.controller';
import { AppService } from './app.service';
import { ConfigModule } from '@nestjs/config'
import { WinstonModule } from 'nest-winston';
import { winstonModuleOptions } from './config/log.config';
import { APP_FILTER } from '@nestjs/core';
import { AllExceptionFilter } from './filters/all-exception/all-exception.filter';
@Module({
//...省略的代码
providers: [
AppService,
{
provide: APP_FILTER,
useClass: AllExceptionFilter
},
],
})
export class AppModule { }
结束