question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

Custom Logger service not working as expected

See original GitHub issue

I’m submitting a…


[ ] Regression 
[X] Bug report
[ ] Feature request
[ ] Documentation issue or request
[ ] Support request => Please do not submit support request here, instead post your question on Stack Overflow.

Current behavior

  • It doesn’t print on console the message you send to the custom log function.
  • The logger is always calling the log function inside the custom logger class. Also it doesn’t print the message.

Expected behavior

Use the custom logger.

Minimal reproduction of the problem with instructions

Implement the following custom logger inside a project (I am using the cqrs project as an example):

logger.module.ts


import * as winston from 'winston'
import {Component, Logger, LoggerService} from '@nestjs/common';
import {DateTime} from 'luxon'

@Component()
export class MyLogger extends winston.Logger implements LoggerService {
    private winston

    constructor() {
        super()
        this.winston = new winston.Logger({
            transports: [
                new winston.transports.Console({
                    json: true,
                    colorize: true,
                    label: 'GameService',
                    timestamp: () => DateTime.local().toString(),
                    formatter: options => `${options.timestamp()} [${options.level.toUpperCase()}] ${options.label} - ${options.message}`,
                }),
            ],
        })
    }

    error(message: string, trace: string) {
        this.winston.log({
            level: 'error',
            message
        })
    }

    log(message: string) {
        this.winston.log({
            level: 'warn',
            message,
        })
    }

    warn(message: string) {
        this.winston.log({level: 'warn', message})
    }

    info(message: string) {
        this.winston.log({
            level: 'info',
            message
        })
    }
}

Then, in the heroes.module.ts import it:


import { CommandBus, EventBus, CQRSModule } from '@nestjs/cqrs'
import { ModuleRef } from '@nestjs/core'
import { HeroesGameController } from './heroes.controller';
import {Module, OnModuleInit} from '@nestjs/common';
import {HeroesGameService} from './heroes.service';
import {HeroesGameSagas} from './sagas/heroes.sagas';
import {CommandHandlers} from './commands/handlers';
import {EventHandlers} from './events/handlers';
import {HeroRepository} from './repository/hero.repository';
import {MyLogger} from './middlewares/logger/logger.module';

@Module({
    modules: [CQRSModule],
    controllers: [HeroesGameController],
    components: [
        HeroesGameService,
        HeroesGameSagas,
        ...CommandHandlers,
        ...EventHandlers,
        HeroRepository,
        MyLogger,
    ],
    exports: [MyLogger],
})
export class HeroesGameModule implements OnModuleInit {
    constructor(
        private readonly moduleRef: ModuleRef,
        private readonly command$: CommandBus,
        private readonly event$: EventBus,
        private readonly heroesGameSagas: HeroesGameSagas,
        private logger: MyLogger,
    ) {}

    onModuleInit() {
        this.command$.setModuleRef(this.moduleRef)
        this.event$.setModuleRef(this.moduleRef)

        this.event$.register(EventHandlers)
        this.command$.register(CommandHandlers)
        this.event$.combineSagas([this.heroesGameSagas.dragonKilled])
    }
}

I have tried adding it in the bootstrap function in main.ts


import { NestFactory } from '@nestjs/core';
import { ApplicationModule } from './app.module';
import {MyLogger} from './cqrs/modules/heroes/middlewares/logger/logger.module';

async function bootstrap() {
	const app = await NestFactory.create(ApplicationModule, {
		logger: new MyLogger(),
	})
	await app.listen(3000);
}
bootstrap();

What is the motivation / use case for changing the behavior?

We need to use our custom injectable logger.

Environment


Nest version: 4.6.5

 
For Tooling issues:
- Node version: v8.10.0
- Platform:  Linux xUbuntu 64bit 17.10

Another things

Related from: https://github.com/nestjs/nest/issues/247, https://github.com/nestjs/nest/issues/435

I think I am doing something wrong, so if you don’t mind writing a little example please.

Thank you so much.

Issue Analytics

  • State:closed
  • Created 6 years ago
  • Reactions:2
  • Comments:12 (5 by maintainers)

github_iconTop GitHub Comments

31reactions
shekohexcommented, Mar 19, 2018

Hmm, in my Daily projects I used to use my own logger, which i’d love to share it it’s fairly simple, and easy to use.

// logger.service.ts

import * as winston from 'winston';
import * as chalk from 'chalk';
import * as PrettyError from 'pretty-error'; // it's really handy to make your life easier
import { Env } from '@shared/utils'; // using typescript paths, you know !
import { LoggerInstance, LoggerOptions, Transport } from 'winston';

export class LoggerService {
  private readonly logger: LoggerInstance;
  private readonly prettyError = new PrettyError();
  public static loggerOptions: LoggerOptions = {
    transports: [
      new winston.transports.File({
        filename: Env('LOG_FILE', 'app.dev.log'), // i will explain this later
        json: true,
        prettyPrint: true,
        timestamp: true,
      }),
    ],
  };
  constructor(private context: string, transport?) {
    this.logger = (winston as any).createLogger(LoggerService.loggerOptions);
    this.prettyError.skipNodeFiles();
    this.prettyError.skipPackage('express', '@nestjs/common', '@nestjs/core');
  }
  get Logger(): LoggerInstance {
    return this.logger; // idk why i have this in my code !
  }
  static configGlobal(options?: LoggerOptions) {
    this.loggerOptions = options; 
  }
  log(message: string): void {
    const currentDate = new Date();
    this.logger.info(message, {
      timestamp: currentDate.toISOString(),
      context: this.context,
    });
    this.formatedLog('info', message);
  }
  error(message: string, trace?: any): void {
    const currentDate = new Date();
    // i think the trace should be JSON Stringified 
    this.logger.error(`${message} -> (${trace || 'trace not provided !'})`, {
      timestamp: currentDate.toISOString(),
      context: this.context,
    });
    this.formatedLog('error', message, trace);
  }
  warn(message: string): void {
    const currentDate = new Date();
    this.logger.warn(message, {
      timestamp: currentDate.toISOString(),
      context: this.context,
    });
    this.formatedLog('warn', message);
  }
  overrideOptions(options: LoggerOptions) {
    this.logger.configure(options);
  }
  // this method just for printing a cool log in your terminal , using chalk
  private formatedLog(level: string, message: string, error?): void {
    let result = '';
    const color = chalk.default;
    const currentDate = new Date();
    const time = `${currentDate.getHours()}:${currentDate.getMinutes()}:${currentDate.getSeconds()}`;

    switch (level) {
      case 'info':
        result = `[${color.blue('INFO')}] ${color.dim.yellow.bold.underline(time)} [${color.green(
          this.context,
        )}] ${message}`;
        break;
      case 'error':
        result = `[${color.red('ERR')}] ${color.dim.yellow.bold.underline(time)} [${color.green(
          this.context,
        )}] ${message}`;
        if (error && Env('NODE_ENV') === 'dev') this.prettyError.render(error, true);
        break;
      case 'warn':
        result = `[${color.yellow('WARN')}] ${color.dim.yellow.bold.underline(time)} [${color.green(
          this.context,
        )}] ${message}`;
        break;
      default:
        break;
    }
    console.log(result);
  }
}

I used to have a utils folder in my shared module, it helps me alot in my projects.

// env.util.ts
import { config } from 'dotenv';
import { isNil } from './shared.util';
declare const process: { // nevermind 
  env: {
    [key: string]: string;
  };
};
config(); // load .env file
// Get an env value by key
export const Env = (key: string, fallback: any = null) => {
  return isNil(process.env[key]) ? fallback : process.env[key];
};

in your bootstrap function

// main.ts
import { LoggerService } from '@shared/services';

const app = await NestFactory.create(AppModule, {
    logger: new LoggerService('Main'), // Main here is just the context
  });

and finally you can use it everywhere.

...
import { LoggerService } from '@shared/services';
@Controller('auth')
export class AuthController {
  private readonly logger: LoggerService = new LoggerService(AuthController.name);
  ...
  @Post('login')
  async login(@Body('mobileNumber') mobileNumber: string): Promise<any> {
    this.logger.log('logging in...');
    return await this.authService.loginByMobileNumber(mobileNumber);
  }
...
}

Here is how it looks like 😃

image

Anyway, it’s easy to implement your own. Hope this helped.

3reactions
etcommented, May 22, 2018

Does anyone have an example of doing the above but with a decorator for dependency injection into the constructor?

Read more comments on GitHub >

github_iconTop Results From Across the Web

java.util.logging custom formatter not working as expected
I am using Java 6. I have created a custom formatter which creates only the time and the message, but it always prints...
Read more >
Logger | NestJS - A progressive Node.js framework
You can provide a custom logger implementation to be used by Nest for system logging by setting the value of the logger property...
Read more >
Systemd/rsyslog logging not working as expected
It seems that part of the issue is described in bug #1861881. I was able to get a working log file while excluding...
Read more >
Logging in K2 - Nintex help documentation
Check this log file to see if the expected data was returned to the SmartObject, in which case the problem is likely on...
Read more >
Troubleshoot pushing log data to CloudWatch - Amazon AWS
Even if you aren't using IMDSv2, it's a best practice to use the newer unified CloudWatch agent instead of the logs agent. Fingerprinting...
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found