Ayesha
Ayesha

Reputation: 11

Unable to use Winston Logger globally in all modules - NestJS

I'm new to NestJS and don't understand it completely. Any help is appreciated.

I'm trying to configure Winston logger globally so I can use it in all modules without the need to import it for every module.

// main.ts

import { WinstonModule } from 'nest-winston';
import * as winston from 'winston';

async function bootstrap() {
  process.env.APP_ENV !== 'test' && process.env.APP_ENV !== 'local' && require('newrelic');

  const app = await NestFactory.create(AppModule, {
    logger: WinstonModule.createLogger({
      transports: [
        new winston.transports.Console({
          format: winston.format.combine(winston.format.timestamp(), winston.format.ms()),
        }),
      ],
    }),
  });
}

bootstrap();
// app.module.ts

import { LoggerConfig } from './config/logger.config';
import { WinstonModule } from 'nest-winston';

const logger: LoggerConfig = new LoggerConfig();

@Module({
  imports: [AppConfigModule, HealthModule, CouponModule, WinstonModule.forRoot(logger.console())],
  controllers: [],
  providers: [],
})
export class AppModule {}


// coupon.controller.ts

@Controller(BASE_ROUTE)
export class CouponController {
  constructor(private couponService: CouponService, private readonly logger: Logger) {}

Error: Nest can't resolve dependencies of the CouponController (CouponService, ?). Please make sure that the argument Logger at index [1] is available in the CouponModule context. Potential solutions:

  • If Logger is a provider, is it part of the current CouponModule?
  • If Logger is exported from a separate @Module, is that module imported within CouponModule?

It works if I import Logger in coupon.module.ts, but I don't want to import it in all modules separately. Can anyone please tell what am I missing?

Thank you.

Upvotes: 1

Views: 4597

Answers (3)

Chandni
Chandni

Reputation: 11

To implement Winston logger globally which catches nestjs default error exception and write out in error log file then you can follow the below steps:

Step 1: Install Dependencies

npm install winston nest-winston

Step 2: Create a Logging Service

Create a logging service that will handle the logging logic. Create a file named logger.service.ts and add the following code:

import { Injectable } from '@nestjs/common';
import { createLogger, format, transports } from 'winston';

@Injectable()
export class LoggerService {
  private logger;

  constructor() {
    const logFormat = format.printf(({ level, message, timestamp }) => {
      return `${timestamp} [${level.toUpperCase()}] ${message}`;
    });

    this.logger = createLogger({
      format: format.combine(
        format.timestamp(),
        logFormat,
      ),
      transports: [
        new transports.Console(),
        new transports.File({
          filename: 'logs/error.log',
          level: 'error',
        }),
      ],
    });
  }

  logError(error: Error): void {
    this.logger.error(error.message);
  }
}

Step 3: Create a Global Exception Filter

Create a global exception filter that will catch and log exceptions. Create a file named global-exception.filter.ts and add the following code:

import { Catch, ArgumentsHost } from '@nestjs/common';
import { BaseExceptionFilter } from '@nestjs/core';
import { LoggerService } from './logger';

@Catch()
export class GlobalExceptionFilter extends BaseExceptionFilter {
  constructor(private readonly loggerService: LoggerService) {
    super();
  }

  catch(exception: any, host: ArgumentsHost) {
    this.loggerService.logError(exception);
    super.catch(exception, host);
  }
}

Step 4: Register the Logger and Exception Filter

In your NestJS application's main module, register the LoggerService and GlobalExceptionFilter as global providers. Open the app.module.ts file and update it as follows:

import { Module } from '@nestjs/common';
import { APP_FILTER } from '@nestjs/core';
import { LoggerService } from './logger.service';
import { GlobalExceptionFilter } from './global-exception.filter';

@Module({
  providers: [
    LoggerService,
    {
      provide: APP_FILTER,
      useClass: GlobalExceptionFilter,
    },
  ],
})
export class AppModule {}

Step 5: Usage

Now, whenever an exception occurs in your NestJS application, it will be caught by the GlobalExceptionFilter and logged using the LoggerService. You can inject the LoggerService into any module or service where you want to log errors. For example:

import { Injectable } from '@nestjs/common';
import { LoggerService } from './logger.service';

@Injectable()
export class MyService {
  constructor(private readonly loggerService: LoggerService) {}

  someMethod() {
    try {
      // Code that may throw an error
    } catch (error) {
      this.loggerService.logError(error);
    }
  }
}

The logger will log errors with their messages and timestamps to the console, as well as to log files. Error logs will be stored in the logs/error.log file. This logger will also catch nestjs thrown exceptions and will store them in the logs/error.log file.

Upvotes: 1

Lucas
Lucas

Reputation: 661

Check your Logger import on your controller, you might be accidentally using Nest's native Logger. As a side note, my suggestion depends wether you just want the application to automatically log using Winston or if you need to use Winston's methods.

If you just want to use Winston as the default logger and won't use it's methods (info, error, warn) to manually log stuff, then you just need it on your main.ts and app.module.ts.

If you need to manually log stuff, my suggestion is for you to create a new service for your custom logger and use it on your app.module as a provider, just like so:

// Logger.provider.ts
@Injectable()
export class LoggerService implements LoggerInterface {
  constructor(
    @Inject(WINSTON_MODULE_PROVIDER) private readonly logger: Logger,
  ) {}

  warn(message): void {
    this.logger.warn(message);
  }

  error(message): void {
    this.logger.error(message);
  }

  debug(message): void {
    this.logger.debug(message);
  }

  info(message): void {
    this.logger.info(message);
  }
}

That second option is my current personal use case, so if you need, I may share a repo with you.

Also, take a read at global modules: https://docs.nestjs.com/modules#global-modules

Upvotes: 1

Mustapha Afkir
Mustapha Afkir

Reputation: 64

In the controller you have to inject the winston module provider

@Controller('cats')
export class CatsController {
  constructor(@Inject(WINSTON_MODULE_PROVIDER) private readonly logger: Logger) { }
}

See documentation: https://github.com/gremo/nest-winston#quick-start

Upvotes: 0

Related Questions