0

I'm trying to inject nestwinston globally in my nestjs app and also to keep a beautiful format in my console/terminal and in Grafana, and most important to display custom objects where needed, but using a beautiful format, not simply JSON.

My problem is that the nestwinston changes its behavior when is injected globally.

I followed https://github.com/gremo/nest-winston#replacing-the-nest-logger-also-for-bootstrapping, but when I'm running the next code sample, my error log's object is not displayed in log whatever format type I use (tried all of them and the only option was to format the log as simple JSON, which looks awful in Grafana):

import { Injectable, Logger } from '@nestjs/common';
...
  constructor(
    private readonly logger: Logger,
  ) {
    this.logger.log("my message", {myValue: true});
  }

result:

[NestWinston] Info [Bootstrapper] my message - {}

see that {myValue: true} is not displayed in log... even it displays an empty object which I really don't know what it is...

johnykes
  • 1,663
  • 2
  • 9
  • 25

1 Answers1

1

I achived that by creating a custom LoggerService in which I use the nestwinston alongside with a custom logger formatter, then replacing the nestjs default logger with this service, so it will be used everywhere in my project.

LoggerService:

import { LoggerService as LS } from '@nestjs/common';
import * as winston from 'winston';
const { combine, timestamp, printf } = winston.format;
import * as Transport from 'winston-transport';
import {
  utilities as nestWinstonModuleUtilities,
  WinstonModule,
} from 'nest-winston';

export class LoggerService implements LS {
  private logger: LS;

  constructor() {
    this.logger = WinstonModule.createLogger({
      transports: this.logTransports(),
    });
  }

  log(message: any, fields?: any) {
    this.logger.log(this.toPrettyJson(message, fields));
  }
  error(message: any, fields?: any) {
    this.logger.error(this.toPrettyJson(message, fields));
  }
  warn(message: any, fields?: any) {
    this.logger.warn(this.toPrettyJson(message, fields));
  }
  debug(message: any, fields?: any) {
    this.logger.debug(this.toPrettyJson(message, fields));
  }
  verbose(message: any, fields?: any) {
    this.logger.verbose(this.toPrettyJson(message, fields));
  }

  private toPrettyJson(message: any, fields?: any) {
    let log = {};
    if (typeof message === 'string') {
      log['message'] = message;
    } else if (typeof message === 'object') {
      for (const [key, value] of Object.entries(message)) {
        log[key] = value;
      }
    }
    if (fields) {
      if (typeof fields === 'object') {
        for (const [key, value] of Object.entries(fields)) {
          log[key] = value;
        }
      } else if (typeof fields === 'string') {
        log['context'] = fields;
      }
    }
    return log;
  }

  private logTransports = () => {
    const format = combine(
      timestamp(),
      nestWinstonModuleUtilities.format.nestLike(),
    );

    const logTransports: Transport[] = [
      new winston.transports.Console({
        format: format,
      }),
    ];

    return logTransports;
  };
}

main.ts:

...
  const app = await NestFactory.create(AppModule, {
    logger: new LoggerService(),
  });
...

then in any module you want to use the new Logger, add it to providers:

import { Logger } from '@nestjs/common';
...
providers: [Logger...]
...

and then to any module's service:

import { Injectable, Logger } from '@nestjs/common';
...
  constructor(
    private readonly logger: Logger,
  ) {
    // method 1
    this.logger.log("my message", {myValue: true});

    // method 2
    this.logger.log({message: "my message", myValue: true});
  }

=>

[NestWinston] Info [Bootstrapper] my message - {"myValue": true}
johnykes
  • 1,663
  • 2
  • 9
  • 25