윈스턴과 인터셉터를 이용해서 로깅 할 방식이 있는지 물어봤다.
Winston과 NestJS Interceptor를 함께 사용하여 로깅하는 코드를 작성해보겠습니다. 먼저, Winston 라이브러리를 설치해야 합니다:
npm install winston
다음으로, Winston의 로거 인스턴스를 설정하고 NestJS Interceptor를 사용하여 로깅하겠습니다.
Winston 설정:
먼저, Winston 로거를 설정합니다. 보통 애플리케이션의 진입점 (예: main.ts
)에서 Winston을 설정합니다.
import { Logger } from '@nestjs/common';
import { WinstonModule } from 'nest-winston';
import * as winston from 'winston';
const winstonLogger = WinstonModule.createLogger({
level: 'info', // Log level
format: winston.format.combine(
winston.format.colorize(),
winston.format.timestamp(),
winston.format.printf(({ timestamp, level, message }) => {
return `${timestamp} [${level}] ${message}`;
}),
),
transports: [
new winston.transports.Console(),
new winston.transports.File({ filename: 'error.log', level: 'error' }),
new winston.transports.File({ filename: 'combined.log' }),
],
});
async function bootstrap() {
const app = await NestFactory.create(AppModule);
app.useLogger(winstonLogger);
//커스텀한 윈스턴 로거를 Logger로 사용하도록 등록하는 코드
// Nest 내장 로거를 winston logger로 변환
await app.listen(3000);
}
bootstrap();
NestJS Interceptor 생성:
다음으로, NestJS Interceptor를 생성하고 Winston을 사용하여 로깅하겠습니다.
import { Injectable, NestInterceptor, ExecutionContext, CallHandler } from '@nestjs/common';
import { Observable } from 'rxjs';
import { tap } from 'rxjs/operators';
import { Logger } from 'winston';
@Injectable()
export class LoggingInterceptor implements NestInterceptor {
private logger: Logger = new Logger('Request');
//등록해둔 윈스턴 로거 이용
intercept(context: ExecutionContext, next: CallHandler): Observable<any> {
const req = context.switchToHttp().getRequest();
const { method, url } = req;
const now = Date.now();
return next.handle().pipe(
tap(() => {
const responseTime = Date.now() - now;
this.logger.log(`${method} ${url} ${responseTime}ms`);
}),
);
}
}
Interceptor를 라우터 핸들러에 적용:
라우터 핸들러 클래스에서 Interceptor를 사용하려면 @UseInterceptors()
데코레이터를 사용합니다.
import { Controller, Get, UseInterceptors } from '@nestjs/common';
import { LoggingInterceptor } from './logging.interceptor';
@UseInterceptors(LoggingInterceptor)
// 인터셉터를 사용하고자 하면 매번 데코레이터를 적어주어야 한다. (전역 등록을 안 했기 때문에)
@Controller('cats')
export class CatsController {
@Get()
findAll() {
return 'This action returns all cats';
}
}
이제 위의 코드를 사용하면 모든 라우터 핸들러의 요청과 응답이 Winston 로깅으로 기록됩니다. 요청과 응답의 URL, HTTP 메서드 및 응답 시간이 로그에 포함됩니다.
logging 디렉토리 생성
winston logger로 커스텀 로거 작성
winston logger를 사용하는 loggingInterceptor 작성
@Injectable()
export class LoggingInterceptor implements NestInterceptor {
intercept(context: ExecutionContext, next: CallHandler): Observable<any> {
const req = context.switchToHttp().getRequest();
const { method, url, body } = req;
winstonLogger.log(
`Request to ${method} ${url}\\nbody: ${JSON.stringify(body)}`
);
return next
.handle()
.pipe(
tap((data) =>
winstonLogger.log(
`Response from ${method} ${url}\\nresponse: ${JSON.stringify(data)}`
)
)
);
}
}
main.ts
: LoggingInterceptor를 전역 인터셉터로 등록
@UseInterceptor()
데코레이터를 작성해줘야 한다.async function bootstrap() {
const app = await NestFactory.create(AppModule);
**app.useGlobalInterceptors(new LoggingInterceptor());**
await app.listen(3000);
}
handle()
이 각 라우터 핸들러인 것 같은데tap()
까지 가지 않고 곧 바로 handle()
에서 에러가 반환되어 버림