如何在NestJS中配置多个Winston日志器实现指定传输端输出?
问题原因分析
- 方法签名与Nest内置
LoggerService接口不匹配:你自定义的log、error方法参数顺序和Nest要求的不一致,导致你传入的配置对象被识别为上下文参数,无法正确判断输出目标。 - 缺失日志级别兜底配置:Winston实例未显式配置输出级别,默认仅输出
info及以上级别的日志,如果传入的自定义level低于该等级会被直接过滤。 error方法逻辑缺失trace参数处理:现有实现中完全丢弃了trace参数,不符合日志规范的同时可能触发参数校验异常。- 未补全
LoggerService要求的所有方法:Nest日志服务要求实现warn、debug、verbose等方法,缺失会导致部分场景下调用异常。
修复方案
1. 修正CustomLogger实现
import { Injectable, LoggerService } from '@nestjs/common'; import winston from 'winston'; import { WinstonModule } from 'nest-winston'; @Injectable() export class CustomLogger implements LoggerService { // 显式配置日志输出级别,可根据需求调整 private readonly fileLogger = WinstonModule.createLogger({ level: 'debug', // 允许输出debug及以上级别的日志 transports: [ new winston.transports.File({ filename: "combined.log" }) ] }); private readonly consoleLogger = WinstonModule.createLogger({ level: 'debug', transports: [ new winston.transports.Console(), ] }); // 适配Nest LoggerService的标准参数签名:message, context?, ...rest log(message: any, context?: string, ...optionalParams: any[]) { // 提取配置对象:如果第二个参数是对象则为配置,否则取第三个参数 const options = typeof context === 'object' ? context : optionalParams[0] || {}; const level = options.level || 'info'; if(options.logger === "file"){ this.fileLogger.log(level, message); }else{ this.consoleLogger.log(level, message); } } error(message: any, trace?: string, context?: string, ...optionalParams: any[]) { const options = typeof context === 'object' ? context : optionalParams[0] || {}; const level = options.level || 'error'; if(options.logger === "file"){ this.fileLogger.error(level, message, trace); }else{ this.consoleLogger.error(level, message, trace); } } // 补全LoggerService要求的剩余方法 warn(message: any, context?: string, ...optionalParams: any[]) { const options = typeof context === 'object' ? context : optionalParams[0] || {}; const level = options.level || 'warn'; if(options.logger === "file"){ this.fileLogger.warn(level, message); }else{ this.consoleLogger.warn(level, message); } } debug(message: any, context?: string, ...optionalParams: any[]) { const options = typeof context === 'object' ? context : optionalParams[0] || {}; const level = options.level || 'debug'; if(options.logger === "file"){ this.fileLogger.debug(level, message); }else{ this.consoleLogger.debug(level, message); } } verbose(message: any, context?: string, ...optionalParams: any[]) { const options = typeof context === 'object' ? context : optionalParams[0] || {}; const level = options.level || 'verbose'; if(options.logger === "file"){ this.fileLogger.verbose(level, message); }else{ this.consoleLogger.verbose(level, message); } } }
2. 注册CustomLogger
在对应业务模块中注册服务,跨模块使用需要导出:
import { Module } from '@nestjs/common'; import { CustomLogger } from './custom-logger.service'; import { TestingService } from './testing.service'; @Module({ providers: [CustomLogger, TestingService], exports: [CustomLogger] }) export class LoggerModule {}
如果要替换Nest全局日志服务,在main.ts中配置:
import { NestFactory } from '@nestjs/core'; import { AppModule } from './app.module'; import { CustomLogger } from './custom-logger.service'; async function bootstrap() { const app = await NestFactory.create(AppModule, { logger: new CustomLogger() }); await app.listen(3000); } bootstrap();
3. 调用逻辑
原有调用方式无需修改即可正常运行,指定logger: "file"的日志仅写入文件,未指定的默认输出到控制台:
@Injectable() export class TestingService { constructor( private logger: CustomLogger ) { } async createLog(message: string, level: string): Promise<void> { this.logger.log(message, { logger: "file", level }); } }
内容的提问来源于stack exchange,提问作者Muhammad Zeeshan
相关产品推荐
相关产品推荐

