티스토리 뷰

728x90
반응형

Logging 

로그는 동작 과정을 남기고 에러의 원인을 추적할 수 있다.

로그 덕분에 에러의 원인을 파악할 수 있는 시간을 줄일 수 있다.

NestJS에서 제공하고 있는 내장로거는 파일, 데이터베이스 로 저장하는 기능을 제공하지 않는다.

이를 위해서는 커스텀 로거를 만들어야한다.

 

느려도 좋으니까, 다큐먼트 읽으면서 천천히 꼭꼭씹어먹으면서 이해하는 것을 포커스 뒀다.

 

[ 내장 Logger 활용하기 ]

// app.service.ts

import { Injectable, Logger } from '@nestjs/common';

@Injectable()
export class AppService {
  private readonly logger = new Logger(AppService.name);

  getHello(): string {
    this.logger.error('level: error');
    this.logger.warn('level: warn');
    this.logger.log('level:log');
    this.logger.verbose('level: verbose');
    this.logger.debug('level: debug');

    return 'hello world';
  }
}

 

 

insomnia로  localhost:3000/  호출해보니 아래와 같이 로그가 떴다 (색깔이쁘다 ㅎㅎ)


내장 Logger 비활성화 하기 

기본적으로 NestJS는 내장 로거를 활성화 한다.  그 이유는 npm run start 할 때 콘솔창에

[Nest] 10950  - 2022. 11. 07. 오후 10:55:21     LOG [NestApplication] Nest application blabla~~

형태의 로그들이 주루룩 나오기 때문이다.

 

- main.ts

// main.ts

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

 

[tips]

  • 프로덕션 환경에서는 debug 로그를 남기지 않는게 좋다.
    • debug 레벨에서는 민감한 정보가 포함할 수 있기 때문에
    • 로그파일 사이즈를 줄이기 위한 목적

 

  • 실행환경에 따라 로그 레벨 지정
// main.ts

async function bootstrap() {
  const app = await NestFactory.create(AppModule, {
    logger:
      process.env.NODE_EVN === 'production'
        ? ['error', 'warn', 'log']
        : ['error', 'warn', 'log', 'verbose', 'debug'],
  });

	...
    
  await app.listen(3000);
}
bootstrap();

custom logger 만들기 

- 인터페이스 생성

$ nest g itf loggers

 

- 로거 인터페이스 작성

// path: /src/loggers/loggers.interface.ts

import { LogLevel } from '@nestjs/common';

export interface LoggerService {
  log(message: any, ...optionalParams: any[]): any;
  error(message: any, ...optionalParams: any[]): any;
  warn(message: any, ...optionalParams: any[]): any;
  debug?(message: any, ...optionalParams: any[]): any;
  verbose?(message: any, ...optionalParams: any[]): any;
  setLogLevels?(levels: LogLevel[]): any;
}

 

- MyLogger 만들기

// path: /src/loggers/my-logger.ts

import { LoggerService, LogLevel } from '@nestjs/common';

export class MyLogger implements LoggerService {
  log(message: any, ...optionalParams: any[]) {
    console.log(message);
  }
  error(message: any, ...optionalParams: any[]) {
    console.log(message);
  }
  warn(message: any, ...optionalParams: any[]) {
    console.log(message);
  }
  debug?(message: any, ...optionalParams: any[]) {
    console.log(message);
  }
  verbose?(message: any, ...optionalParams: any[]) {
    console.log(message);
  }
}

 

- 커스텀 로거 MyLogger을 프로젝트에 적용시키기

// path: /src/main.ts

async function bootstrap() {

  // custom Logger 사용
  const app = await NestFactory.create(AppModule, { logger: new MyLogger() });
  ...

  await app.listen(3000);
}
bootstrap();

custom Logger을 주입시켜서 사용하기

매번 new 생성자를 부르지 않고, DI(Dependency Injection)으로 커스텀 로거를 적용시킬 수 있나요?

 

- loggers.module.ts 생성

$ nest g mo loggers

 

- custom logger(MyLogger)을 logger 모듈에 주입

// path: /src/loggers/loggers.module.ts

import { Module } from '@nestjs/common';
import { MyLogger } from './my-logger';

@Module({
  providers: [MyLogger],
  exports: [MyLogger],
})
export class LoggersModule {}

 

- app.module.ts 에 LoggerModule이 적용이 되었는지 체크

@Module({
  imports: [
	...,
    LoggersModule, // LoggerModule 체크!
  ],
  controllers: [AppController],
  providers: [AppService],
})
export class AppModule implements NestModule {
  configure(consumer: MiddlewareConsumer) {
    consumer.apply(LoggerMiddleware).forRoutes();
  }
}

 

- main.ts (전역으로 사용)

async function bootstrap() {
  const app = await NestFactory.create(AppModule);

  // custom Logger을 Dependency Injection 으로 사용
  app.useLogger(app.get(MyLogger));

  await app.listen(3000);
}
bootstrap();

 

npm run start 시켜봤다. (음... 내장로거보다 훨씬 구리군....... ^^;)


Winston Logger 적용시키기

 

why winston logger ?

  • NodeJS 에서 많이 사용되는 로깅 라이브러리
  • 분리된 로깅 프로세스 과정
  • 유연하고 확장 가능한 로깅 시스템
  • 화면에 출력된 로그를 파일이나 데이터베이스 저장하여 로그를 활용이 가능하다.
  • 이 로그들은 DataDog 와 같은 로그 분석/시각화 툴에 활용된다.

 

winston의 log level 은 7개로 되어있다. (NestJS 내장 logger는 5개)

log type level
error 0
warn 1
info 2
http 3
verbose 4
debug 5
silly 6

 

  • production 레벨에서는 debug와 silly 레벨의 로그는 제외시킨다.
  • 그외에 내부 데이터 파악이 필요할 때는 debug와 silly 레벨의 로그를 출력하도록 한다.

 

 

nest-winston 설치

$ npm i nest-winston winston

 

app.module.ts에 winstonModule 추가하기

// path: /src/app.module.ts

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

@Module({
  imports: [
    ...,
    WinstonModule.forRoot({
      transports: [
        new winston.transports.Console({
          level: process.env.NODE_ENV === 'production' ? 'info' : 'silly',
          format: winston.format.combine(
            winston.format.timestamp(),
            nestWinstonModuleUtilities.format.nestLike('MyApp', {
              prettyPrint: true,
              colors: true,
            }),
          ),
        }),
      ],
    }),
  ],
  controllers: [AppController],
  providers: [AppService],
})
export class AppModule implements NestModule {}

 

winston logger을 객체에 주입시켜서 적용하기

WINSTON_MODULE_PROVIDER 토큰으로 객체에 주입시켜 모든레벨의 로그를 남겨보기

- market.controller.ts

// path: /src/markets/markets.controller.ts

import { WINSTON_MODULE_PROVIDER } from 'nest-winston';
import { Logger as WinstonLogger } from 'winston';


@Controller('markets')
export class MarketsController {
  constructor(
    private readonly marketsService: MarketsService,
    @Inject(WINSTON_MODULE_PROVIDER) private readonly logger: WinstonLogger,
  ) {}

  @Post()
  create(@Body() createMarketDto: CreateMarketDto) {
    console.log(this.logger);

    this.logger.error('error', createMarketDto);
    this.logger.warn('warn', createMarketDto);
    this.logger.info('info', createMarketDto);
    this.logger.http('http', createMarketDto);
    this.logger.verbose('verbose', createMarketDto);
    this.logger.debug('debug', createMarketDto);
    this.logger.silly('silly', createMarketDto);
  }

  ...
}

 

- create-market.dto.ts

export class CreateMarketDto {
  marketId: number;
  marketName: string;
}

 

적용결과


Nest Logger를 Winston Logger 로 대체하기 

Nest가 시스템 로깅을 할 때 이 WinstonLogger 클래스를 이용할 수 있다.

Nest 시스템의 내장로거를 WinstonLogger로 대체시켜보자. Winston Logger 를 적용시킨 후 로그는  [MyApp] 태그가 붙어있다.

 

- main.ts

import { WINSTON_MODULE_NEST_PROVIDER } from 'nest-winston';

async function bootstrap() {

  const app = await NestFactory.create(AppModule);

  const { httpAdapter } = app.get(HttpAdapterHost);
  ...

  // WinstonLogger 전역 스코프에 적용
  app.useLogger(app.get(WINSTON_MODULE_NEST_PROVIDER));


  await app.listen(3000);
}
bootstrap();

 

- market.controller.ts

import { LoggerService } from '@nestjs/common';
import { WINSTON_MODULE_PROVIDER } from 'nest-winston';


@Controller('markets')
export class MarketsController {
  constructor(
    private readonly marketsService: MarketsService,
    
    // LoggerService 를 WINSTON_MODULE_NEST_PROVIDER 토큰으로 주입받는다.
    @Inject(WINSTON_MODULE_PROVIDER) private readonly logger: LoggerService,
  ) {}

  @Post()
  create(@Body() createMarketDto: CreateMarketDto) {
    this.printLoggerServiceLog(createMarketDto);
  }

  private printLoggerServiceLog(dto) {
    try {
      throw new InternalServerErrorException('test');
    } catch (e) {
      this.logger.error('ERROR: ' + JSON.stringify(dto), e.stack);
    } finally {
      this.logger.warn('WARN:' + JSON.stringify(dto));
      this.logger.log('log: ' + JSON.stringify(dto));
      this.logger.verbose('VERBOSE: ' + JSON.stringify(dto));
      this.logger.debug('DEBUG: ' + JSON.stringify(dto));
    }
  }

  ...
}


(최종) Bootstraping 단계에서도 Winston Logger 로 대체하기

Bootstraping: 프로젝트 시작할때까지도 winston 로거를 적용시키는 것이다.

부트스트래핑 까지 포함하여 Nest Logger 에서 Winston Logger로 대체하려면

모듈을 forRoot, forRootAsync  로 import 하지말고 Nest앱을 생성할 때 NestFactory.create 의 인자를 줘야한다.

NestFactory.create 함수에 로거를 직접생성한 인스턴스를 전달하도록 변경한다.

 

- main.ts

import {
  WINSTON_MODULE_NEST_PROVIDER,
  WinstonModule,
  utilities as nestWinstonModuleUtilities,
} from 'nest-winston';
import * as winston from 'winston';

async function bootstrap() {
  // logger 추가
  const app = await NestFactory.create(AppModule, {
    logger: WinstonModule.createLogger({
      transports: [
        new winston.transports.Console({
          level: process.env.NODE_ENV === 'production' ? 'info' : 'silly,',
          format: winston.format.combine(
            winston.format.timestamp(),
            nestWinstonModuleUtilities.format.nestLike('MyApp', {
              prettyPrint: true,
              colors: true,
            }),
          ),
        }),
      ],
    }),
  });


  ...
  // WinstonLogger 전역 스코프에 적용
  app.useLogger(app.get(WINSTON_MODULE_NEST_PROVIDER));
  await app.listen(3000);
}
bootstrap();

 

 

- market.module.ts

로그를 남길 모듈에 Logger 서비스를 프로바이더로 선언한다.

// path: /src/markets/markets.module.ts

import { Logger, Module } from '@nestjs/common';
import { MarketsService } from './markets.service';
import { MarketsController } from './markets.controller';


@Module({
  controllers: [MarketsController],
  providers: [
    MarketsService,
    Logger, // Logger 을 프로바이더로 선언
  ],
})
export class MarketsModule {}

 

- markets.controller.ts

@nestjs/common 패키지의 Logger 또는 LoggerService 주입하기

// path: src/markets/markets.controller.ts

import { Logger, LoggerService } from '@nestjs/common';

@Controller('markets')
export class MarketsController {
  constructor(
    private readonly marketsService: MarketsService,
    @Inject(Logger) private readonly logger: LoggerService,
  ) {}

  ...

}

 

이제는 npm run start 명령을 실행할때 모든 로그의 태그들이 [Nest] 에서 [MyApp] 으로 변경됐다.

[POST] localhost:3000/markets 호출할때 결과이다.

 


[참고]

 

Documentation | NestJS - A progressive Node.js framework

Nest is a framework for building efficient, scalable Node.js server-side applications. It uses progressive JavaScript, is built with TypeScript and combines elements of OOP (Object Oriented Progamming), FP (Functional Programming), and FRP (Functional Reac

docs.nestjs.com

 

[Nest.js] Winston 로거 적용하기

들어가며 많은 기업들이 Nest.js를 활용해서 서버를 구축하는 것을 알았습니다. 더 나은 개발자가 되기 위해서는 지금부터라도 Nest.js에 대해 기초부터 공부해야겠다고 생각했습니다. 왜 많은 기

overcome-the-limits.tistory.com

 

nest-winston

A Nest module wrapper for winston. Latest version: 1.8.0, last published: 19 days ago. Start using nest-winston in your project by running `npm i nest-winston`. There are 157 other projects in the npm registry using nest-winston.

www.npmjs.com

 

 

11.3.1 nest-winston 적용

nest-winston 라이브러리를 이용해서 우리가 만들고 있는 서비스에 로깅 기능을 구현하도록 하겠습니다. nest-winston은 세가지 방식으로 적용할 수 있습니다. 먼저 …

wikidocs.net

 

728x90
반응형
반응형
공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
링크
«   2024/05   »
1 2 3 4
5 6 7 8 9 10 11
12 13 14 15 16 17 18
19 20 21 22 23 24 25
26 27 28 29 30 31
글 보관함