[nestjs] log๋ฅผ ๋จ๊ฒจ๋ณด์ - winston
nestjs๋ก ์ฌ์ด๋ ํ๋ก์ ํธ๋ฅผ ๊ฐ๋ฐํ๋ฉด์ winston์ ์ด์ฉํด logging์ ์ ์ฉํ ๊ฒฝํ์ ๊ธฐ๋กํฉ๋๋ค.
๋ค์ ์์๋ก ๊ธฐ๋กํฉ๋๋ค.
- http-logger-middleware ์ ์ฉ (morgan X)
- nest-winston + winston-daily-rotate-file
log๋ฅผ ๋จ๊ฒจ์ผํ๋ ์ด์
log๋ฅผ ๋จ๊ฒจ์ผํ๋ ์ด์ ๋ ๋ค์ํ๊ฒ ์ง๋ง ๋ด๊ฐ ํด๋น ์ฌ์ด๋ ํ๋ก์ ํธ์์ log๋ฅผ ๋จ๊ฒจ์ผ๊ฒ ๋ค๊ณ ์๊ฐํ ์ด์ ๋ ๋ค์๊ณผ ๊ฐ๋ค. ๋ณดํต ํผ๋๋ฐฑ๊ณผ ๊ด๋ จ๋ ๋ด์ฉ์ด๋ค.
- ์์ ๋ ์๋น์ค๋ฅผ ์ํด log๋ฅผ ํตํ ์๋น์ค ์ํ ๋ฐ ๋ก์ง์ ์ถ์
- error ๋ฐ์ ์์ error ์ํฉ ์ถ์
- file ํํ๋ก log๋ฅผ ๋จ๊ฒจ ์ ๊ณผ์ ์ ์ข ๋ ์ ์ฐํ๊ฒ ๋ง๋ค๊ธฐ
- ๋ก๊ทธ๋ฅผ ํ์ฉํ ๋ชจ๋ํฐ๋ง ํ๊ฒฝ ๊ตฌ์ถ
http-logger-middleware ์ ์ฉํ๊ธฐ
๋จผ์ , ๊ธฐ์กด nodejs ํ๋ก์ ํธ์์ ์ฌ์ฉํ๋ morgan module์ฒ๋ผ http ์์ฒญ ์ ๋ณด๋ฅผ logging ํด์ฃผ๋ ๊ธฐ๋กํ๋ ๊ฒ์ด ํ์ํ๋ค.
๊ทธ๋์ ๊ฐ์ฅ ๋จผ์ ๋ ์ค๋ฅธ morgan์ nest-morgan์ ์ด์ฉํด์ ์ ์ฉํด๋ณด์์ผ๋ ํด๋น package๋ deprecated ๋์ด์์๊ณ , ์ถ๋ ฅ๋๋ ๋ด์ฉ๋ ๋ด๊ฐ ์ํ๋ ํ์์ ์๋์๋ค.
๊ฒฐ๊ตญ ๊ฐ๋จํ๊ฒ http-logger-middeleware๋ ์ง์ ๊ตฌํํ๊ธฐ๋ก ํ๋ค.
๊ตฌํํ http-logger middleware๋ ๋ค์๊ณผ ๊ฐ๋ค. ๊ธฐ์กด์ ์ฌ์ฉํ๋ morgan์ ํํ์ ๋น์ทํ ํํ์ด๋ค.
Youtube http-logger๋ฅผ ๋ง๋๋ ์์์ด ์์ด ์ฐธ๊ณ ํ๋ค.
// http-logger.middleware.ts
import { Injectable, Logger, NestMiddleware } from '@nestjs/common';
import { NextFunction, Request, Response } from 'express';
@Injectable()
export class HttpLoggerMiddleware implements NestMiddleware {
private logger = new Logger('HTTP');
use(request: Request, response: Response, next: NextFunction) {
const { ip, method, originalUrl } = request;
const userAgent = request.get('user-agent') || '';
response.on('finish', () => { // (1)
const { statusCode } = response;
const contentLength = response.get('content-length');
this.logger.log(
`${method} ${originalUrl} ${statusCode} ${contentLength} - ${userAgent} ${ip}`,
);
});
next();
}
}
- (1) response์ finish eventListener : node์์ http response ์ ์ก ์์ emit ํ๋ finish ์ด๋ฒคํธ๋ฅผ catch ํ๋ค node.js event-finish
์ middleware๋ฅผ ๊ตฌํํ๊ณ , ์๋์ ๊ฐ์ด ์ต์์ ๋ชจ๋์ธ AppModule
์ HttpLoggerMiddleware๋ฅผ ์ ์ฉํ๋ค.
// app.module.ts
@Module({
imports: [
TypeOrmModule.forRoot(typeORMConfig),
AuthModule,
WorkbookModule,
UserModule,
CommonModule,
],
})
export class AppModule implements NestModule {
configure(consumer: MiddlewareConsumer) {
consumer.apply(HttpLoggerMiddleware).forRoutes('*');
}
}
์ดํ API๋ฅผ ํ ์คํธํด๋ณธ ๊ฒฐ๊ณผ ๋ค์๊ณผ ๊ฐ์ด ์ถ๋ ฅ๋๋ ๊ฒ์ ํ์ธํ๋ค.
file ํํ๋ก log๋ฅผ ๊ธฐ๋กํ์
๋ณดํต ์๋ฒ ๋ฐฐํฌ ์์ nohub์ ์ด์ฉํด ์ถ๋ ฅ ๋ก๊ทธ๊ฐ ๊ธฐ๋ก๋๊ธดํ์ง๋ง, ์ด๋ ๊ด๋ฆฌ๊ฐ ์ด๋ ต๊ณ ๊ฐ์ธ์ ์ผ๋ก ๋ก๊ทธ๋ฅผ ๋ฐ๋ก ํ์ผ๋ก ์ ๋ฆฌํด์ ์ ์ฅํ๋ ๊ฒ์ด ํ์ํ๋ค๊ณ ํ๋จํ๋ค. ๊ทธ๋์ file ํํ๋ก log๋ฅผ ๋ฐ๋ก ์ ์ฅํ๊ธฐ๋ก ํ๋ค.
node์์ ์ต์ํ winston module์ ์ฌ์ฉํ๊ณ ์ถ์๋๋ฐ, nest-winston ๋ชจ๋์ ๋ฐ๊ฒฌํด์ ๋น ๋ฅด๊ฒ ์ ์ฉํด๋ณผ ์ ์์๋ค.
๋ํ ๋ ์ง๋ณ๋ก ํ์ผ์ ์ ์ฅํ๊ธฐ ์ํด winston-daily-rotate-file module๋ ์ถ๊ฐ๋ก ์ด์ฉํ๋ค
npm install nest-winston winston
npm install winston-daily-rotate-file
// logger-config.ts
import {
utilities as nestWinstonModuleUtilities,
WinstonModule,
} from 'nest-winston';
import { format, Logform, transports } from 'winston';
import 'winston-daily-rotate-file';
export class LoggerConfig {
static createApplicationLogger() {
return WinstonModule.createLogger({
format: format.combine(
format.timestamp(),
nestWinstonModuleUtilities.format.nestLike('FLIP'), // (1)
),
transports: [
new transports.Console({}),
new transports.DailyRotateFile({ // (2)
format: this.logFileFormat(),
filename: 'application-%DATE%.log', // (3)
dirname: 'logs',
datePattern: 'YYYY-MM-DD-HH',
zippedArchive: true,
maxSize: '20m',
maxFiles: '14d',
}),
new transports.DailyRotateFile({
format: this.logFileFormat(),
level: 'error',
filename: 'error-%DATE%.log',
dirname: 'logs',
datePattern: 'YYYY-MM-DD',
zippedArchive: true,
maxSize: '20m',
maxFiles: '14d',
}),
],
});
}
private static logFileFormat(): Logform.Format {
return format.combine(
format.timestamp({ format: 'YYYY-MM-DD HH:mm:ss' }),
format.printf((info) => JSON.stringify(info)), // (4)
);
}
- (1) nest-winston ๋ชจ๋์ ๊ธฐ์กด nest log์ ๋น์ทํ๊ฒ ์ถ๋ ฅ ํฌ๋ฉง์ ์ ํด์ฃผ๋ ์ ํธ์ด ์์ด ์ฌ์ฉํ๋ค. FLIP์ ์ฌ์ด๋ ํ๋ก์ ํธ ์ด๋ฆ์ด๋ค. ์๋ฌด๊ฑฐ๋ ์ ๋ ฅํด๋ ๋๋ค.
- (2)
winston-daily-rotate-file
์DailyRotateFile()
์ ์ด์ฉํด ํ์ผ์ ์๊ฐ์ ๋ฐ๋ผ ๋ฐ๊พธ๊ฒ ํ๋ค. - (3) %DATE%์๋ ์๊ฐ ํฌ๋งท(datePattern)๊ฐ์ด ์ ๋ ฅ๋๋ค. ex) application-2022-02-21.log
- (4) ํด๋น ํฌ๋ฉง์ logFile์ ์ ์ฅ๋ ํฌ๋ฉง์ด๋ค ์์ ์ด ์ํ๋ ํ์์ ์ ํํ์.
์ดํ main.ts bootstrap()์์ ๊ธฐ๋ณธ logger๋ฅผ ๋ค์๊ณผ ๊ฐ์ด ๋ณ๊ฒฝํด์ฃผ์๋ค.
// main.ts
import { LoggerConfig } from './common/config/logger-config';
async function bootstrap() {
const appOptions: NestApplicationOptions = {
logger: LoggerConfig.createApplicationLogger(),
};
const app = await NestFactory.create(AppModule, appOptions);
// ...
}
๋ง๋ฌด๋ฆฌ
๋ค์์ ์ ์ฝ๋๋ฅผ ํ
์คํธํ ๊ฒฐ๊ณผ์ด๋ค. (http-logger, log files)
์ํ๋๋๋ก ์ ์๋ํ๊ณ , ์ดํ์ ๋ชจ๋ํฐ๋ง, ๋ก๊ทธ ๋ถ์ ๋ฑ์ ํ์ต์ ์ด์ด๊ฐ ์๊ฐ์ด๋ค
log files