Node.js + express 기반으로 서버를 구축할 일이 생겨서 logging library인 winston을 적용해보았습니다.
서버에 로그를 남기는 것은 몇 번을 강조해도 부족하지 않을 만큼 굉장히 중요합니다!!!
npm(혹은 yarn)으로 winston과 winston-daily-rotate-file을 설치합니다.
npm install winston winston-daily-rotate-file
winston-daily-rotate-file은 로그 파일을 관리해주는 모듈입니다.
기본적으로 하루 단위로 새 로그 파일을 생성해주고, 로그 파일의 최대 크기와 최대 저장 파일 개수 등을 설정할 수 있습니다.
./config/winston.js
파일을 생성하고 아래와 같이 설정합니다.
import winston from 'winston';
import winstonDaily from 'winston-daily-rotate-file';
const logDir = 'logs'; // logs 디렉토리 하위에 로그 파일 저장
const { combine, timestamp, printf } = winston.format;
// Define log format
const logFormat = printf(info => {
return `${info.timestamp} ${info.level}: ${info.message}`;
});
/*
* Log Level
* error: 0, warn: 1, info: 2, http: 3, verbose: 4, debug: 5, silly: 6
*/
const logger = winston.createLogger({
format: combine(
timestamp({
format: 'YYYY-MM-DD HH:mm:ss',
}),
logFormat,
),
transports: [
// info 레벨 로그를 저장할 파일 설정
new winstonDaily({
level: 'info',
datePattern: 'YYYY-MM-DD',
dirname: logDir,
filename: `%DATE%.log`,
maxFiles: 30, // 30일치 로그 파일 저장
zippedArchive: true,
}),
// error 레벨 로그를 저장할 파일 설정
new winstonDaily({
level: 'error',
datePattern: 'YYYY-MM-DD',
dirname: logDir + '/error', // error.log 파일은 /logs/error 하위에 저장
filename: `%DATE%.error.log`,
maxFiles: 30,
zippedArchive: true,
}),
],
});
// Production 환경이 아닌 경우(dev 등)
if (process.env.NODE_ENV !== 'production') {
logger.add(new winston.transports.Console({
format: winston.format.combine(
winston.format.colorize(), // 색깔 넣어서 출력
winston.format.simple(), // `${info.level}: ${info.message} JSON.stringify({ ...rest })` 포맷으로 출력
)
}));
}
export { logger };
logFormat
에 로그를 출력할 포맷을 정의하고, createLogger
를 통해 logger를 생성합니다.
winston의 로그 레벨은 다음과 같고, 숫자가 낮을 수록 priority가 높습니다.
level
을 설정하면 해당 레벨 이상의 priority를 가지는(즉, 숫자가 같거나 낮은) 로그를 출력하게 됩니다.
error: 0, warn: 1, info: 2, http: 3, verbose: 4, debug: 5, silly: 6
저는 info 레벨 이상(info, warn, error)인 로그를 {date}.log
파일에 저장하고, 에러 로그는 따로 /error/{date}.error.log
파일로 저장하기 위해 transports
에 두 개의 winstonDaily
인스턴스를 설정했습니다.
winston.js
에서 생성, export한 logger를 import해서 로그를 남길 수 있습니다.
index.ts
에서 logger를 import해서 로그를 남겨 보겠습니다.
http://localhost:3000
의 /
path에 접근하면 info 로그를, /error
에 접근하면 error 로그를 남기는 예시 프로그램을 생성했습니다.
import express from 'express';
import { logger } from './config/winston';
const app = express().Application;
app.listen(3000, () => {
logger.info('Server listening on port 3000');
});
app.get('/', (req, res) => {
logger.info('GET /');
res.sendStatus(200);
});
app.get('/error', (req, res) => {
logger.error('Error message');
res.sendStatus(500);
});
서버를 시작한 뒤 /
, /error
에 차례로 접근해 보겠습니다.
Console에 출력된 로그는 다음과 같습니다.
winston.js
의 Console 블럭에 설정한 것처럼 ${info.level}: ${info.message} JSON.stringify({ ...rest })
의 포맷으로 출력되는 것을 볼 수 있습니다.
이번에는 생성된 로그 파일을 살펴봅시다.
logs 디렉토리와 그 하위에 error 디렉토리가 생성되고, 로그 파일들이 생성되었습니다.
logs 디렉토리에 생성되는 {date}.log
파일은 info 레벨의 로그가 출력되도록 설정했었습니다.
생성된 로그 파일은 다음과 같습니다.
2020-06-25 00:35:17 info: Server listening on port 3000
2020-06-25 00:35:22 info: GET /
2020-06-25 00:35:30 error: Error message
winston.js
에서 logFormat
에 정의한 포맷 그대로 출력되는 것을 볼 수 있습니다.
info 레벨 로그, 그리고 info보다 priority가 높은 error 레벨의 로그까지 모두 저장됩니다.
logs 하위의 error 디렉토리에 생성되는 {date}.error.log
파일에는 error 레벨의 로그만 남도록 설정했습니다.
이렇게 하면 서버에 문제가 발생했을 때 에러 로그를 파악하기가 더 쉬워집니다.
생성된 파일을 확인해보면 info 로그까지 남았던 {date}.log
와 달리 error 로그만 저장되어 있는 것을 볼 수 있습니다.
2020-06-25 00:35:30 error: Error message
서버를 운영할 때 로깅은 굉장히 중요한 요소입니다. 로그를 남기지 않는다면 이슈가 발생했을 때 어느 부분을 뜯어 봐야 하는지조차 감이 오지 않는 경우가 생기지만, 로그를 남기면 어느 부분에서 어떤 문제가 생겼는지 파악하는 데 큰 도움이 됩니다. 항상 로그를 생활화합시다!
더 많은 옵션은 Document를 참고하시기 바랍니다.