Skip to content

로그 남기고 관리하기

padawanR0k edited this page Jun 10, 2021 · 1 revision

로그를 남겨야하는 이유

  • 서버를 운영하다보면 다양한 이슈와 오류를 마주침. 로그없이 그런 상황을 대처하는것은 거의 불가능함
  • 로그를 남겨 언제, 어디서, 왜 이슈가 발생했는지를 더 빨리, 쉽게 추론할 수 있기 때문에 로그를 남김

라이브러리

  • winston
    • 로그를 중요도에 따라 레벨별로 남길 수 있으며 로그 메세지도 커스텀가능
    • 기존에 사용되던 tracer보다 더 문서가 잘되있고 정보도 많아 교체
  • winston-daily-rotate-file
    • A transport for winston which logs to a rotating file. Logs can be rotated based on a date, size limit, and old logs can be removed based on count or elapsed days.
    • 로그파일을 관리해주는 모듈로서 내가 정해놓은 기준에 따라 저장되는 파일의 최대 갯수등을 설정할 수 있다.

사용

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 };
  • 로컬에서 개발시에 콘솔을 찍을 경우 색상을 같이 찍어주도록 설정한다.

적용하기

import winston from 'winston';
import winstonDaily from 'winston-daily-rotate-file';
const { printf, timestamp, combine } = winston.format;

export class MyLogger {
	// Define log format
	private logDir = 'logs';
	private logFormat = printf((info) => {
		return `${info.timestamp} ${info.level}: ${info.message}`;
	});
	private logger: winston.Logger;

	constructor() {
		this.logger = winston.createLogger({
			format: combine(
				timestamp({
					format: 'YYYY-MM-DD HH:mm:ss'
				}),
				this.logFormat
			),
			transports: [
				// info 레벨 로그를 저장할 파일 설정
				new winstonDaily({
					level: 'info',
					datePattern: 'YYYY-MM-DD',
					dirname: this.logDir + '/info',
					filename: `%DATE%.log`,
					maxFiles: 7, // 30일치 로그 파일 저장
					zippedArchive: true
				}),
				// error 레벨 로그를 저장할 파일 설정
				new winstonDaily({
					level: 'error',
					datePattern: 'YYYY-MM-DD',
					dirname: this.logDir + '/error', // error.log 파일은 /logs/error 하위에 저장
					filename: `%DATE%.error.log`,
					maxFiles: 7,
					zippedArchive: true
				}),
				// error 레벨 로그를 저장할 파일 설정
				new winstonDaily({
					level: 'warn',
					datePattern: 'YYYY-MM-DD',
					dirname: this.logDir + '/warn',
					filename: `%DATE%.warn.log`,
					maxFiles: 7,
					zippedArchive: true
				}),
				new winstonDaily({
					level: 'notice',
					datePattern: 'YYYY-MM-DD',
					dirname: this.logDir + '/notice',
					filename: `%DATE%.notice.log`,
					maxFiles: 7,
					zippedArchive: true
				}),
				new winstonDaily({
					level: 'debug',
					datePattern: 'YYYY-MM-DD',
					dirname: this.logDir + '/debug',
					filename: `%DATE%.debug.log`,
					maxFiles: 7,
					zippedArchive: true
				}),
			]
		});

    // Production 환경이 아닌 경우(dev 등)
    if (process.env.NODE_ENV !== 'production') {
      this.logger.add(new winston.transports.Console({
        format: winston.format.combine(
          winston.format.colorize(),  // 색깔 넣어서 출력
          winston.format.simple(),  // `${info.level}: ${info.message} JSON.stringify({ ...rest })` 포맷으로 출력
        )
      }));
    }
	}


	notice(...trace: any[]) {
		this.logger.notice(trace);
	}
	error(...trace: any[]) {
		this.logger.error(trace);
	}
	warn(...trace: any[]) {
		this.logger.warn(trace);
	}
	debug(...trace: any[]) {
		this.logger.debug(trace);
	}
	info(...trace: any[]) {
		this.logger.info(trace);
	}
}
  • 재사용 쉽게 클래스로 만들어주었다.
  • 각 로그레벨에 해당하는 파일을 따로 관리하도록 하였다. 로그를 활용하려는 곳에서는 인스턴스를 생성하고 메소드를 호출하기만 하면된다.

참고