You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何配置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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.28 09:35:36