如何在NestJS的非类函数库中使用Winston Logger(基于nest-winston)
Hey Martin, let's break down solutions for both of your NestJS + Winston issues step by step:
1. Fixing the "missing getTimestamp function" error when setting Winston as NestJS's default logger
The root issue here is that NestJS's Logger interface expects a getTimestamp() method, but the raw Winston logger instance doesn't implement this. The nest-winston package provides a wrapper class (WinstonLogger) that bridges this gap—here's how to set it up correctly:
Step 1: Configure WinstonModule with timestamp formatting
First, ensure your Winston config includes timestamp formatting (this feeds into the getTimestamp() method the wrapper uses):
// src/app.module.ts import { Module } from '@nestjs/common'; import { WinstonModule, WinstonLogger } from 'nest-winston'; import * as winston from 'winston'; @Module({ imports: [ WinstonModule.forRoot({ transports: [new winston.transports.Console()], format: winston.format.combine( winston.format.timestamp({ format: 'YYYY-MM-DD HH:mm:ss' }), winston.format.printf(({ timestamp, level, message }) => { return `${timestamp} [${level}]: ${message}`; }), ), }), ], // Replace Nest's default Logger with WinstonLogger providers: [ { provide: 'Logger', useClass: WinstonLogger, }, ], }) export class AppModule {}
Step 2: Use the wrapped logger in your bootstrap
In your main.ts, explicitly use the WinstonLogger when creating the Nest app:
// src/main.ts import { NestFactory } from '@nestjs/core'; import { AppModule } from './app.module'; import { WinstonLogger } from 'nest-winston'; async function bootstrap() { const app = await NestFactory.create(AppModule, { logger: app.get(WinstonLogger), }); await app.listen(3000); } bootstrap();
This ensures the default logger implements all required NestJS interface methods, including getTimestamp().
2. Accessing the main Winston logger in functional TypeScript libraries
Since you're avoiding OOP and dependency injection, the cleanest approach is to expose the configured Winston instance as a globally accessible variable (while keeping it tied to Nest's container to ensure consistency):
Step 1: Create a logger utility file
Create a file to hold the global logger reference and a setter function:
// src/utils/global-logger.ts import * as winston from 'winston'; // Declare a mutable variable to hold the logger instance export let globalWinstonLogger: winston.Logger; // Setter function to initialize the logger from Nest's container export const setGlobalWinstonLogger = (logger: winston.Logger) => { globalWinstonLogger = logger; };
Step 2: Initialize the global logger in bootstrap
In your main.ts, fetch the Winston instance from Nest's container and pass it to the setter:
// src/main.ts import { NestFactory } from '@nestjs/core'; import { AppModule } from './app.module'; import { setGlobalWinstonLogger } from './utils/global-logger'; import * as winston from 'winston'; async function bootstrap() { const app = await NestFactory.create(AppModule); // Fetch the Winston instance registered by WinstonModule const winstonLogger = app.get<winston.Logger>('winston'); setGlobalWinstonLogger(winstonLogger); await app.listen(3000); } bootstrap();
Step 3: Use the logger in your functional libraries
Now you can import and use the logger directly in any function:
// src/utils/your-functional-library.ts import { globalWinstonLogger } from './global-logger'; export const processData = (data: any) => { globalWinstonLogger.info('Processing data started', { dataLength: data.length }); // Your function logic here globalWinstonLogger.debug('Data processing completed'); };
Note on global variables
While global variables aren't ideal in all cases, this is a pragmatic solution for functional codebases where dependency injection isn't feasible. It ensures you're using the same configured Winston instance across your entire app, avoiding duplicate logger setups.
内容的提问来源于stack exchange,提问作者Martin Thompson

