当前位置: 首页 > news >正文

nestjs日志(nest-winston)

1、安装包

pnpm i winston nest-winston winston-daily-rotate-file

2、配置service

import * as winston from 'winston';
import { WinstonModule } from 'nest-winston';
import { Console } from 'winston/lib/winston/transports';
import 'winston-daily-rotate-file';
import { LoggerService } from '@nestjs/common';
import { ConfigService } from '@nestjs/config';export class WinstonService implements LoggerService {private logger: LoggerService;constructor(private readonly configService: ConfigService) {this.logger = this.setupLogger();}setupLogger() {const consoleTransports = new Console({level: 'info',format: winston.format.combine(winston.format.timestamp({format: 'YYYY-MM-DD HH:mm:ss',}),winston.format.json()),});const otherTransports =this.configService.get("env") == 'production'? [createDailyRotateTransport('info', 'winston-info'),createDailyRotateTransport('warn', 'winston-warn'),createDailyRotateTransport('error', 'winston-error'),]: [];return WinstonModule.createLogger({transports: [consoleTransports, ...otherTransports],})}log(message: any, context?: any) {this.logger.log(message, context);}error(message: any, context?: any) {this.logger.error(message, context);}warn(message: any, context?: any) {this.logger.warn(message, context);}
}
function createDailyRotateTransport(level: string, filename: string) {return new winston.transports.DailyRotateFile({level,  //日志级别dirname: 'logs', //日志文件夹filename: `${filename}-%DATE%.log`, //日志名称,占位符 %DATE% 取值为 datePattern 值datePattern: 'YYYY-MM-DD', //日志轮换的频率,此处表示每天。其他值还有:YYYY-MM、YYYY-MM-DD-HH、YYYY-MM-DD-HH-mmzippedArchive: true, //是否通过压缩的方式归档被轮换的日志文件maxSize: '20m', // 设置日志文件的最大大小,m 表示 mb 。maxFiles: '14d', // 保留日志文件的最大天数,此处表示自动删除超过 14 天的日志文件format: winston.format.combine(winston.format.timestamp({format: 'YYYY-MM-DD HH:mm:ss',}),winston.format.json(),),});
}

3、全局注入

// 引入
import { WinstonService } from 'src/utils/logger/winston-service';
// 注册。注入configService 是为了内部区分环境,方便测试环境不要有那么多的日志输出
providers: [{provide: WinstonService,inject: [ConfigService],useFactory: (configService: ConfigService) => {return new WinstonService(configService);}}
]
// 导出
exports: [WinstonService
],

完整实例如下:(包括了全局注入异常筛选器、全局拦截器、中间键)

import { Global, Module } from '@nestjs/common';
import { ConfigModule, ConfigService } from '@nestjs/config';
import { TypeOrmModule, TypeOrmModuleOptions } from '@nestjs/typeorm';
import configuration from '../../config/index';
import { JwtModule } from '@nestjs/jwt';
import { APP_FILTER, APP_GUARD, APP_INTERCEPTOR } from '@nestjs/core';
import { JwtGuard } from 'src/utils/jwt/jwt-guard';
import { JwtStrategy } from 'src/utils/jwt/jwt-strategy';
import { WinstonService } from 'src/utils/logger/winston-service';
import { CatchLoggerFilter } from 'src/utils/logger/catch-logger-filter';
import { ResponseLoggerInterceptor } from 'src/utils/logger/response-logger-interceptor';
import { RedisModule } from '@nestjs-modules/ioredis';
import { RequirePermissionGuard } from 'src/utils/premission/require-premission.guard';@Global()
@Module({imports: [ConfigModule.forRoot({isGlobal: true,load: [configuration],}),TypeOrmModule.forRootAsync({inject: [ConfigService],useFactory: (configService: ConfigService) => {return {type: 'mysql',...configService.get('db.mysql'),timezone: '+08:00',// logger: 'advanced-console',entities: [__dirname + '/../**/*.entity.{js,ts}'],} as TypeOrmModuleOptions;},}),RedisModule.forRootAsync({inject: [ConfigService],useFactory: (configService: ConfigService) => {return {type:"single",url: configService.get('redis.url'),};},}),JwtModule.registerAsync({inject: [ConfigService],global: true,useFactory: (configService: ConfigService) => {return {secret: configService.get('jwt.secretkey'),// signOptions: { expiresIn: configService.get('jwt.expiresin') },};},})],providers: [JwtStrategy,{provide: APP_GUARD,useFactory: (configService: ConfigService) => {return new JwtGuard(configService);},inject: [ConfigService],},{provide: APP_GUARD,useClass: RequirePermissionGuard},{provide: WinstonService,inject: [ConfigService],useFactory: (configService: ConfigService) => {return new WinstonService(configService);}},{provide: APP_FILTER,useClass: CatchLoggerFilter},{provide: APP_INTERCEPTOR,useClass: ResponseLoggerInterceptor}],exports: [WinstonService],
})
export class ShareModule { }

注:全局中间键是在AppModule中进行配置的,以上代码并没有,具体代码如下:

import { MiddlewareConsumer, Module, NestModule } from '@nestjs/common';
import { SystemModule } from './admin/system/system.module';
import { ShareModule } from './module/share.module';
import { RequestLoggerMiddleware } from './utils/logger/request-logger-middleware';
@Module({imports: [ShareModule,SystemModule,],controllers: [],providers: [],
})
export class AppModule implements NestModule {configure(consumer: MiddlewareConsumer) {// 所有路由都是用此中间键consumer.apply(RequestLoggerMiddleware).forRoutes('*');}
}

4、使用:

1):中间键中使用(记录前端请求参数)

import { Injectable, NestMiddleware } from "@nestjs/common";
import { Request, Response, NextFunction } from "express";
import { WinstonService } from "./winston-service";
import { ConfigService } from "@nestjs/config";@Injectable()
export class RequestLoggerMiddleware implements NestMiddleware {constructor(private readonly winstonService: WinstonService,private readonly configService: ConfigService) {}use(req: Request, res: Response, next: NextFunction) {if (this.configService.get("env") != "development") {this.winstonService.log('winstonRoute', {url: req.originalUrl,query: req.query,body: req.body,method: req.method,ip: req.ip,token: req.headers.authorization})}next();}
}

2):拦截器中使用(记录响应数据)


import { Injectable, NestInterceptor, ExecutionContext, CallHandler } from '@nestjs/common';
import { Observable } from 'rxjs';
import { map } from 'rxjs/operators';
import { WinstonService } from './winston-service';
import { ConfigService } from '@nestjs/config';
import { instanceToPlain } from 'class-transformer';export interface Response<T> {result: T;
}/*** 全局拦截器,记录响应数据*/
@Injectable()
export class ResponseLoggerInterceptor<T> implements NestInterceptor<T, Response<T>> {constructor(private readonly winstonService: WinstonService,private readonly configService: ConfigService) {}intercept(context: ExecutionContext, next: CallHandler): Observable<Response<T>> {const now = Date.now();return next.handle().pipe(map(data => {const elapsed = Date.now() - now;const res = context.switchToHttp().getResponse();const request = context.switchToHttp().getRequest();if (this.configService.get("env") != "development") {this.winstonService.log('winstonResponse', {response: data,url: request.url,method: request.method,query: request.query,body: request.body,responseTime: elapsed,user: request.user})}if (data && typeof data == 'object') {// 统一过滤掉不能给前端展示的数据。比如密码。实体类中需要加 @Exclude({ toPlainOnly: true }) // 输出屏蔽密码return instanceToPlain(data);}return data}));}
}

3):异常筛选器(记录所有的错误)

import { ExceptionFilter, Catch, ArgumentsHost, HttpException } from '@nestjs/common';
import dayjs from 'dayjs';
import { Request, Response } from 'express';
import { WinstonService } from "./winston-service";
/*** 捕获所有异常*/
@Catch()
export class CatchLoggerFilter implements ExceptionFilter {constructor(private readonly winstonService: WinstonService) {}catch(exception: any, host: ArgumentsHost) {const ctx = host.switchToHttp();const response = ctx.getResponse<Response>();const request = ctx.getRequest<Request>();// console.log(exception.getResponse())this.winstonService.error('winstonCatchEverythingFilter', {url: request.url,method: request.method,query: request.query,body: request.body,exception: exception.message || '服务器异常',user: request.user})response.status(200).json({code: exception.status || 500,data: null,path: request.url,timestamp: dayjs(new Date()).format('YYYY-MM-DD HH:mm:ss'),message: exception.message || '服务器异常',});}
}

到此,全局的异常、响应、请求日志就全部都记录好啦

http://www.dtcms.com/a/353650.html

相关文章:

  • pyecharts可视化图表-tree:从入门到精通
  • Linux 系统调优与CPU-IO-网络内核参数调优
  • Task04: CAMEL框架中的多智能体系统(课程第三章剩余章节)
  • 大模型安全概述、LlamaFirewall
  • ESP8266:Arduino学习
  • 前端性能优化:从指标监控到全链路落地(2024最新实战指南)
  • 短视频矩阵管理软件推荐——小麦矩阵系统深度解析
  • 关于两视图相机几何关系
  • DevExpress WPF中文教程:如何将WPF数据网格绑定到本地集合?
  • 软件定义汽车(SDV)调试——如何做到 适配软件定义汽车(SDV)?(下)
  • vue新能源汽车销售平台的设计与实现(代码+数据库+LW)
  • 【Vue2✨】 Vue2 入门之旅(二):模板语法
  • Python异步编程:从理论到实战的完整指南
  • Qt---项目架构解读
  • BiLSTM-Attention分类预测+SHAP分析+特征依赖图!深度学习可解释分析,Matlab代码实现
  • 【GaussDB】深度解析:创建存储过程卡死且无法Kill会话的疑难排查
  • codeforces(1045)(div2)D. Sliding Tree
  • 装饰器模式(C++python)
  • 第十四章 Leaflet-Ant-Path 实现西气东输管线动态流向可视化
  • 源代码接入 1688 接口的详细指南
  • 【生产事故处理--kafka日志策略保留】
  • antv x6实现封装拖拽流程图配置(适用于工单流程、审批流程应用场景)
  • 使用Stone 3D快速制作第一人称视角在线小游戏
  • STM32八大模式
  • Yapi接口文档导出测试用例至Excel中
  • ProfiNet 转 Ethernet/IP西门子 S7-400 及罗克韦尔 PLC 于原油蒸馏的集成应用
  • 插入排序讲解
  • D‘RespNeT无人机图像分割数据集与YOLOv8-DRN模型,实时识别入口与障碍,助力灾后救援
  • WebConfig的登录与放行
  • 【C语言16天强化训练】从基础入门到进阶:Day 12