[Node.js] Logging 라이브러리 winston 적용하기

ash·2020년 6월 24일
26

Node.js + express 기반으로 서버를 구축할 일이 생겨서 logging library인 winston을 적용해보았습니다.

서버에 로그를 남기는 것은 몇 번을 강조해도 부족하지 않을 만큼 굉장히 중요합니다!!!

설치

npm(혹은 yarn)으로 winston과 winston-daily-rotate-file을 설치합니다.

npm install winston winston-daily-rotate-file

winston-daily-rotate-file은 로그 파일을 관리해주는 모듈입니다.
기본적으로 하루 단위로 새 로그 파일을 생성해주고, 로그 파일의 최대 크기와 최대 저장 파일 개수 등을 설정할 수 있습니다.

사용하기

winston logger 설정

./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를 참고하시기 바랍니다.

profile
Junior Frontend Developer

0개의 댓글