- 개발 도중 이슈가 발생했을 때, 이슈 증상만으로 원인을 파악하기에는 시간과 노력이 많이 들고, 코드를 다시 역추적하면서 이해하는 데에 어려움이 따른다.
- 이슈가 발생한 지점과 콜 스택이 함께 제공된다면 빨리 해결이 가능할 것이다.
- 또 어떠한 기능이 많이 사용되는지에 대한 내용과 같이 유저의 사용 패턴을 분석하는데에도 로그log를 활용할 수 있다.
- 예시로 서비스를 실행하면 서버 콘솔에는 아래 그림과 같은 로그가 출력된다.
- 이미 각 컴포넌트에서는 내장 로거를 이용하여 로그를 출력하고 있다.
- 내장 Logger 클래스는 @nest/common 패키지로 제공된다.
- 로깅 옵션을 조절하면 다음과 같이 로깅 시스템의 동작을 제어할 수 있다.
1. 로깅 비활성화
2. 로그 레벨 지정: log,error,warn,debug,verbose
3. 로거의 타이미 스탬프 재정의 ex) 날짜를 ISO8601 형식으로 변경
4. 기본 로거를 재정의(오버라이딩)
5. 기본 로거를 확장해서 커스텀 로거를 작성
6. 의존성 주입을 통해 손쉽게 로거를 주입하거나 테스트 모듈로 제공
로그 레벨 지정
- 일반적으로 프로덕션 환경에선 debug 로그가 남지 않도록 하는게 좋다.
- 디버그 로그는 테스트 과정에서 디버깅용으로 객체가 가지고 있는 세부 데이터 까지 남기는 경우가 많아 사용자의 민감한 내용까지 포함할 가능성이 있다.
- 다음과 같이 실행 환경에 따라 로그 레벨을 지정하는 경우가 보통.
const app = await NestFactory.create(AppModule, {
logger:
process.env.NODE_ENV === 'production'
? ['error', 'warn', 'log']
: ['error', 'warn', 'verbose', 'debug'],
});
커스텀 로거
- 로그 분석을 위해서는 어떤 형태든 로그를 저장해 두고 검색을 할 수 있어야 한다.
- 하지만 내장 로거는 파일이나 데이터베이스로 저장하는 기능을 제공하지 않는다.
- 이를 위해 커스텀 로거를 만들어야 한다.
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 객체를 생성해서 로그를 출력해 보면 조금 밋밋하게 텍스트만 출력될 겻이다.
- 내장 로거와 같이 프로세스 ID, 로깅 시간, 로그 레벨, 콘텍스트 이름 등을 함께 출력하려면 직접 각 함수 내에세 출력메시지를 구성해야 한다.
import { ConsoleLogger, LoggerService, LogLevel } from '@nestjs/common';
export class MyLogger extends ConsoleLogger {
error(message: any, stack?: string, context?: string) {
// eslint-disable-next-line prefer-rest-params
super.error.apply(this, arguments);
this.doSomething();
}
// eslint-disable-next-line @typescript-eslint/member-ordering
private doSomething() {
// 여기에 로깅에 관련된 부가 로직을 추가한다.
// DB에 저장하기 등등
return;
}
}
커스텀 로거 주입해서 사용하기
- 로거를 모듈로 만들면 생성자에서 주입받을 수 있다.
- 먼저 LoggerModule을 만들고, AppModule에 가져온다.
// src/logger/logger.module.ts
import { Module } from '@nestjs/common';
import { MyLogger } from 'src/logger/MyLogger';
@Module({
providers: [MyLogger],
exports: [MyLogger],
})
export class LoggerModule {}
// src/app.module.ts
@Module({
imports: [
...
LoggerModule,
...
],
...
})
export class AppModule implements NestModule { ... }
- 이제 MyLogger 프로바이더를 주입받아 사용한다.
import { Injectable } from '@nestjs/common';
import { MyLogger } from './logger/MyLogger';
@Injectable()
export class AppService {
constructor(private myLogger: MyLogger) {}
getHello(): string {
this.myLogger.error('level: error');
this.myLogger.warn('level: warn');
this.myLogger.log('level: log');
this.myLogger.verbose('level: verbose');
this.myLogger.debug('level: debug');
return 'Hello World';
}
}
커스텀 로거 전역으로 사용하기
- 커스텀 로거를 전역으로 사용하려면 main.ts에 지정해 주어야 한다.
- 이렇게 하면 서비스 브트스트래핑 과정에서도 커스텀 로거가 사용된다.
// src/main.ts
async function bootstrap() {
const app = await NestFactory.create(AppModule);
...
app.use(app.get(MyLogger));
await app.listen(3000);
}
bootstrap();
외부 로거 사용하기
- 상용프로그램에서는 위와 같은 커스텀 로거를 매우 정교하게 다듬어 사용해야 한다.
- 하지만 node.js에서는 이미 훌륭한 로깅 라이브러리인 winston이 있으며, 나아가 winston을 Nest 모듈로 만들어 놓은 nest-winston 패키지가 존재한다.
- @nestjs/common 패키지에서 제공하는 Logger 클래스를 이용하여 로깅을 구현하는 것도 가능하지만, 서비스 상용 수준으로 운영하기 위해서는 로그를 콘솔에만 출력하는게 아니라 파일에 저장을 하거나, 중요한 로그는 DB에 저장을 해서 쉽게 검색할 수 있도록 해야 한다.
- 먼저 nest-winston 라이브러리를 설치해 보자.
// yarn
yarn add nest-winston
// npm
npm i nest-winston winston
- AppModule에 WinstonModule을 임포트한다.
...
import {
utilities as nestWinstonModuleUtilities,
WinstonModule,
} from 'nest-winston';
import * as winston from 'winston';
@Module({
imports: [
...
WinstonModule.forRoot({
transports: [ // transport 옵션을 설정
new winston.transports.Console({
// 로그 레벨을 개발 환경에 따라 다르도록 지정
level: process.env.NODE_ENV === 'production' ? 'info' : 'silly',
format: winston.format.combine(
winston.format.timestamp(), // 로그를 남긴 시각을 함께 표시하도록 한다.
// MyApp 어디서 로그를 남겼는지 구분하기 위함
nestWinstonModuleUtilities.format.nestLike('MyApp', {
prettyPrint: true, // 로그를 읽기 쉽도록 하는 옵션.
}),
),
}),
],
}),
'Back-End > Nest.js' 카테고리의 다른 글
인터셉터 (0) | 2023.02.22 |
---|---|
예외 처리 (0) | 2023.02.13 |
파이프와 유효성 검사 (0) | 2023.02.01 |
동적 모듈을 활용한 환경 변수 구성 (0) | 2023.01.25 |
멋사 2주차 JS decorator, MVC 패턴, Nest.js 찍먹 (0) | 2023.01.11 |