티스토리 뷰
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] 으로 변경됐다.
[참고]
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
'Backend > 꾸준히 TIL' 카테고리의 다른 글
[NestJS + MongoDB ] NestJS 프레임워크에 MongoDB 연결해보자 (0) | 2022.11.12 |
---|---|
[NestJS] Repository Pattern 적용시키기 (0) | 2022.11.11 |
[NestJS] Custom 에러 만들기 와 Exception Filter (0) | 2022.11.08 |
[NestJS+TypeORM] Pagination 처리 (0) | 2022.11.07 |
[Jest+NestJS] Jest 테스트 환경세팅 (0) | 2022.11.05 |
- Total
- Today
- Yesterday
- 참고
- MySQL
- 개발용어
- 미완
- Jekyll
- node.js
- TypeScript
- 한달독서
- TDD
- nestjs jest
- 습관개선
- 스마트폰중독
- Mongoose
- 바이트디그리
- IT용어
- OS
- RDBMS
- gem
- vscode
- 클린아키텍쳐
- git
- jest
- Nest.js
- 디지털디톡스
- 갓생살자
- typeORM
- nestjs
- 한달어스
- 나도 할 수 있다
- MongoDB
일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
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 |