如何配置Winston Logger使其输出调用文件的类名/文件名?
解决Winston日志显示调用文件/类名的问题
你不需要每次手动传参数(当然手动传是可行方案之一),还有更优雅的自动实现方式,下面分三种方案说明:
方案1:手动传入调用上下文(直接传参数)
这是最直接的方式,在调用Winston日志时,手动传入当前文件或类名作为上下文:
调用示例
// whatever.ts import logger from './logger.js'; logger.info('用户登录成功', { context: __filename.split('/').pop() }); // 或者如果是Nest类里,直接传类名 logger.info('用户登录成功', { context: WhateverService.name });
Winston配置调整
在Winston的格式化器中输出context字段:
// logger.js const winston = require('winston'); const logger = winston.createLogger({ format: winston.format.combine( winston.format.timestamp(), winston.format.printf(({ timestamp, level, message, context }) => { return `${timestamp} [${context || 'unknown'}] ${level}: ${message}`; }) ), transports: [new winston.transports.Console()], }); module.exports = logger;
方案2:自动获取调用文件(无需手动传参)
通过Node.js的调用栈信息,自动解析出日志调用方的文件名,不用每次手动传参数:
Winston配置修改
// logger.js const winston = require('winston'); // 自定义函数获取调用者文件名 const getCallerFile = () => { const err = new Error(); Error.captureStackTrace(err); // 跳过栈中当前logger文件的调用,定位到实际业务代码的调用行 const stackLine = err.stack.split('\n')[3]; if (!stackLine) return 'unknown'; // 解析出文件名(适配不同系统路径格式) const filePathMatch = stackLine.match(/\((.*?)\)/); if (!filePathMatch) return 'unknown'; return filePathMatch[1].split(/[\\/]/).pop(); }; const logger = winston.createLogger({ format: winston.format.combine( winston.format.timestamp(), winston.format.printf(({ timestamp, level, message }) => { const callerFile = getCallerFile(); return `${timestamp} [${callerFile}] ${level}: ${message}`; }) ), transports: [new winston.transports.Console()], }); module.exports = logger;
这样调用时直接写logger.info('日志内容'),就能自动显示调用方的文件名(比如whatever.ts)。
方案3:结合NestJS Logger体系(推荐)
既然你同时用NestJS,最好的方式是把Winston集成到Nest的Logger体系中,自动获取类名,无需额外配置:
实现自定义Winston Logger类
// winston-logger.service.ts import { Injectable, LoggerService } from '@nestjs/common'; import * as winston from 'winston'; @Injectable() export class WinstonLoggerService implements LoggerService { private readonly logger: winston.Logger; constructor() { this.logger = winston.createLogger({ level: process.env.NODE_ENV === 'production' ? 'info' : 'debug', format: winston.format.combine( winston.format.timestamp({ format: 'YYYY-MM-DD HH:mm:ss' }), winston.format.printf(({ timestamp, level, message, context }) => { return `${timestamp} [${context}] ${level.toUpperCase()}: ${message}`; }) ), transports: [new winston.transports.Console()], }); } log(message: string, context: string) { this.logger.info(message, { context }); } error(message: string, trace?: string, context?: string) { const logMessage = trace ? `${message}\nTrace: ${trace}` : message; this.logger.error(logMessage, { context }); } warn(message: string, context: string) { this.logger.warn(message, { context }); } debug(message: string, context: string) { this.logger.debug(message, { context }); } verbose(message: string, context: string) { this.logger.verbose(message, { context }); } }
在Nest中全局注册该Logger
// app.module.ts import { Module } from '@nestjs/common'; import { WinstonLoggerService } from './winston-logger.service'; import { APP_LOGGER } from '@nestjs/core'; @Module({ providers: [ { provide: APP_LOGGER, useClass: WinstonLoggerService, }, ], }) export class AppModule {}
业务类中使用
// whatever.service.ts import { Injectable, Logger } from '@nestjs/common'; @Injectable() export class WhateverService { private readonly logger = new Logger(WhateverService.name); execute() { this.logger.log('执行业务操作'); // 自动显示[WhateverService]作为上下文 } }
这种方式完全贴合Nest的开发习惯,同时利用Winston的强大日志功能,还能自动获取类名,不用手动传参。
内容的提问来源于stack exchange,提问作者Roy
相关产品推荐
相关产品推荐

