'Unable to inject winston's logger instance with NestJS

I'm using NestJS 7.0.7 and Winston 3.2.1 (with nest-winston 1.3.3).

I'm trying to integrate Winston into NestJS, but so far, I'm unable to inject a logger instance (to actually log anything) into any controller/service.

Since I would like to use Winston across the application AND during bootstrapping, I'm using the approach as the main Nest logger:

// main.ts
import { NestFactory } from "@nestjs/core";
import { WinstonModule } from "nest-winston";
import { format, transports } from "winston";
import { AppModule } from "./app.module";

async function bootstrap(): Promise<void> {
  const app = await NestFactory.create(AppModule, {
    logger: WinstonModule.createLogger({
      exitOnError: false,
      format: format.combine(format.colorize(), format.timestamp(), format.printf(msg => {
        return `${msg.timestamp} [${msg.level}] - ${msg.message}`;
      })),
      transports: [new transports.Console({ level: "debug" })], // alert > error > warning > notice > info > debug
    }),
  });
  app.use(helmet());
  await app.listen(process.env.PORT || 3_000);
}

bootstrap().then(() => {
  // ...
});

I'm not doing anything in regard to the logging in app.module.ts:

// app.module.ts
import { SomeController } from "@controller/some.controller";
import { Module } from "@nestjs/common";
import { SomeService } from "@service/some.service";

@Module({
  controllers: [SomeController],
  imports: [],
  providers: [SomeService],
})
export class AppModule {
  // ...
}
// some.controller.ts
import { Controller, Get, Inject, Param, ParseUUIDPipe, Post } from "@nestjs/common";
import { SomeService } from "@service/some.service";
import { WINSTON_MODULE_PROVIDER } from "nest-winston";
import { Logger } from "winston";

@Controller("/api/some-path")
export class SomeController {
  constructor(@Inject(WINSTON_MODULE_PROVIDER) private readonly logger: Logger, private readonly service: SomeService) {
    // ...
  }

  ...
}

The application tries to start but fails at some point:

2020-04-06T18:51:08.779Z [info] - Starting Nest application...
2020-04-06T18:51:08.787Z [error] - Nest can't resolve dependencies of the SomeController (?, SomeService). Please make sure that the argument winston at index [0] is available in the AppModule context.

Potential solutions:
- If winston is a provider, is it part of the current AppModule?
- If winston is exported from a separate @Module, is that module imported within AppModule?
  @Module({
    imports: [ /* the Module containing winston */ ]
  })


Solution 1:[1]

Try importing the WinstonModule in the root AppModule, as explained in the official docs: https://github.com/gremo/nest-winston:

import { Module } from '@nestjs/common';
import { WinstonModule } from 'nest-winston';
import * as winston from 'winston';

const logger: LoggerConfig = new LoggerConfig();    

@Module({
  imports: [WinstonModule.forRoot(logger.console())],
})
export class AppModule {}

It's probably a good idea to create some kind of factory/Logging-Config in order not to have to duplicate the logger options.

import winston, { format, transports } from "winston";

export class LoggerConfig {
  private readonly options: winston.LoggerOptions;

  constructor() {
    this.options = {
      exitOnError: false,
      format: format.combine(format.colorize(), format.timestamp(), format.printf(msg => {
        return `${msg.timestamp} [${msg.level}] - ${msg.message}`;
      })),
      transports: [new transports.Console({ level: "debug" })], // alert > error > warning > notice > info > debug
    };
  }

  public console(): object {
    return this.options;
  }
}

Solution 2:[2]

Implement Winston custom logger in NestJs project

Prerequisit:

npm install --save nest-winston winston winston-daily-rotate-file

import { NestFactory } from '@nestjs/core';
import { WinstonModule } from 'nest-winston';
import * as winston from 'winston';
import * as winstonDailyRotateFile from 'winston-daily-rotate-file';

import { AppModule } from './app.module';

const transports = {
  console: new winston.transports.Console({
    level: 'silly',
    format: winston.format.combine(
      winston.format.timestamp({
        format: 'YYYY-MM-DD HH:mm:ss',
      }),
      winston.format.colorize({
        colors: {
          info: 'blue',
          debug: 'yellow',
          error: 'red',
        },
      }),
      winston.format.printf((info) => {
        return `${info.timestamp} [${info.level}] [${
          info.context ? info.context : info.stack
        }] ${info.message}`;
      }),
      // winston.format.align(),
    ),
  }),
  combinedFile: new winstonDailyRotateFile({
    dirname: 'logs',
    filename: 'combined',
    extension: '.log',
    level: 'info',
  }),
  errorFile: new winstonDailyRotateFile({
    dirname: 'logs',
    filename: 'error',
    extension: '.log',
    level: 'error',
  }),
};

async function bootstrap() {
  const app = await NestFactory.create(AppModule);
  app.useLogger(
    WinstonModule.createLogger({
      format: winston.format.combine(
        winston.format.timestamp({
          format: 'YYYY-MM-DD HH:mm:ss',
        }),
        winston.format.errors({ stack: true }),
        winston.format.splat(),
        winston.format.json(),
      ),
      transports: [
        transports.console,
        transports.combinedFile,
        transports.errorFile,
      ],
    }),
  );
  await app.listen(4000);
}
bootstrap();

NestJs Custom Logger

NestJs Winston NPM Documentation

Note Log Levels, file names, dateformat you may edit as per your requirement. Follow officials documentations with more option.

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 x80486
Solution 2 Dipan Mandal