윈스턴을 사용한, Node.js에서 로그(log) 관리하기

Minyeong Jeong·2022년 10월 1일
0
post-thumbnail

체계적으로 로그를 관리하면,
로그 시스템 개발 혹은 시스템의 운영을 효과적으로 할 수 있습니다.
예기치 못한 오류로 시스템이 다운되는 상황에서 유연하게 대처할 수 있습니다.

이번 포스팅에서는 Node.js에서 윈스턴 모듈을 사용해,
체계적으로, 로그 레벨과 파일로 로그를 관리하는 방법에 대해 알아보겠습니다.

윈스턴(winston)과, 윈스턴 데일리 로테이트 파일(winston-daily-rotate-file)을 사용하면,
아래 스크린숏과 같이, 로그 기록을 파일로 관리할 수 있습니다.

파일에 기록된 로그 입니다.

➖ 윈스턴과 윈스턴 데일리 로테이트 파일이란?

윈스턴은 로그를 파일에 저장해 관리할 수 있게 도와주는 모듈입니다.

윈스턴 데일리 로테이트 파일은 윈스턴이 생성할 로그 파일을
일자별로 생성해, 관리할 수 있게 도움을 주는 모듈입니다.

윈스턴을 사용하는 이유

console 객체의 메서드로, 서버의 상황을 파악할 수 있습니다.
하지만, 서버가 종료되는 순간 쌓여있던 로그들은 사라져버립니다.

윈스턴은 로그를 파일로 관리해, 위 상황을 타계할 수 있습니다.

🗒️ 윈스턴 사용 방법

  1. 윈스턴 모듈을 설치합니다.
    터미널에 npm i winston install, npm i winston-daliy-rotate-file을 입력해,
    윈스턴과, 윈스턴 데일리 로테이트 파일을 설치합니다.

  2. 사용할 모듈을 파일에 가져오고, 저장할 디렉토리를 설정합니다.

    const winston = require("winston");
    const winstonDaily = require("winston-daily-rotate-file"); 
    const path = require("path"); // node의 내장 모듈 path를 사용하면, 저장할 디렉토리를 알 수 있습니다.
    const appRoot = require("app-root-path"); // **app-root-path** 모듈을 사용하면 app의 root path에 쉽게 접근할 수 있습니다.
    																					// 내장 모듈이 아니기 때문에 npm i app-root-path를 통해, 모듈을 설치해야합니다.
    const { createLogger } = require("winston"); 
    const process = require("process"); // node의 내장 모듈 process를 사용하면, 터미널에서도 로그를 확인할 수 있습니다.
    
    const logDir = `${appRoot}/logs`; // 디렉토리 'logs'의 하위 파일로 저장됩니다.
  3. 기록될 로그의 네이밍 룰을 설정합니다.

    
    const { combine, timestamp, label, printf } = winston.format;
    
    // 로그 네이밍 설정
    const logFormat = printf(({ level, message, label, timestamp }) => {
    // label은 시스템 명입니다.
    // timestamp는 생성일과 시간입니다. 
    // level은 로그의 형식입니다.
    // message는 로그에 남겨진 메시지입니다.
    
      return `${timestamp} [${label}] ${level}: ${message}`; 
    });
  4. 윈스턴 로거(logger)를 생성합니다. createLogger

    
    // logger 생성
    const logger = createLogger({
      format: combine(label({ label: "NODE_PROJECT" }), timestamp(), logFormat),
    	// format은 로그의 다양한 형식을 지정합니다.  
      // 그중 combine은 여러 형식을 혼합해서 사용할 때 사용됩니다.
    	// label의 값은 일반적으로 시스템 명으로 지정합니다.
    
    	// transports에는, 다양한 형식에 맞는 로그 메시지 정의합니다.
      transports: [
        new winstonDaily({
          level: "info", 
          datePattern: "YYYY-MM-DD", 
          dirname: logDir, // dirname은 저장할 디렉토리입니다.
          filename: "%DATE%.log", // %DATE%를 하면, 자동적으로 파일명이 생성됩니다.
          maxSize: "20m", // maxSize는 저장할 로그 파일의 최대 크기입니다. 
    											// 최대 크기를 초과하면 앞에 기록된 메시지가 삭제됩니다.
    											// 굳이 정의를 하지 않아도 괜찮습니다.
          maxFiles: "30d", // maxFiles에 파일 보관 기한입니다. 기한을 넘어가면, 로그 파일이 삭제됩니다.
        }),
        new winstonDaily({
          level: "error", 
          datePattern: "YYYY-MM-DD",
          dirname: logDir, 
          filename: "%DATE%.error.log",
          maxSize: "20m",
          maxFiles: "30d", 
        }),
      ],
    });
  1. 개발환경을 설정합니다.
    prod가 아닌 개발 환경에서, 터미널에서도 로그를 확인할 수 있습니다.
     // 개발 환경에서 터미널에서 출력
     if (process.env.NODE_ENV != "prod") {
       logger.add(
         new winston.transports.Console({
           format: winston.format.combine(
             winston.format.colorize(), // colorize는 로그의 색상입니다.
             winston.format.simple() 
           ),
         })
       );
     }
     
     module.exports = logger;
  2. 테스트 파일을 생성해 logger를 가져와,
    logger 형식에 따라 보여줄 메시지를 정의합니다.
     const logger = require("./winston/logger");
     
     logger.info("인포(info) 입니다.");
     logger.error("에러(error) 입니다.");
  1. 터미널에 테스트 파일을 구동해봅니다.

    node test.js를 터미널에 입력하면,
    아래와 같이 로그를 터미널에서도 확인할 수 있습니다.

    모든 코드를 합쳐서 구성하면 다음과 같습니다.

    const winston = require("winston");
    const winstonDaily = require("winston-daily-rotate-file"); 
    const path = require("path"); // node의 내장 모듈 path를 사용하면, 저장할 디렉토리를 알 수 있습니다.
    const appRoot = require("app-root-path"); // 모듈은 app의 root path에 쉽게 접근을 가능하게 해줍니다.
    // 내장 모듈이 아니기 때문에 npm i app-root-path를 통해, 모듈을 설치해야합니다.
    const { createLogger } = require("winston"); // 
    const process = require("process"); // node의 내장 모듈 process를 사용하면, 터미널에서도 로그를 확인할 수 있습니다.
    
    const logDir = `${appRoot}/logs`; // 디렉토리 'logs'의 하위 파일로 저장됩니다.
    
    const { combine, timestamp, label, printf } = winston.format;
    
    // 로그 네이밍 설정
    const logFormat = printf(({ level, message, label, timestamp }) => {
    // label은 시스템 명입니다.
    // timestamp는 생성일과 시간입니다. 
    // level은 로그의 형식입니다.
    // message는 로그에 남겨진 메시지입니다.
    
      return `${timestamp} [${label}] ${level}: ${message}`; 
    });
    
    // logger 생성
    const logger = createLogger({
      format: combine(label({ label: "NODE_PROJECT" }), timestamp(), logFormat),
    	// format은 로그의 다양한 형식을 지정합니다.  
      // 그중 combine은 여러 형식을 혼합해서 사용할 때 활용됩니다.
    	// label의 값은 일반적으로 시스템 명으로 지정합니다.
    
    	// transports에는, 다양한 형식에 맞는 로그 메시지 정의합니다.
      transports: [
        new winstonDaily({
          level: "info", 
          datePattern: "YYYY-MM-DD", 
          dirname: logDir, // dirname은 저장할 디렉토리입니다.
          filename: "%DATE%.log", // %DATE%를 하면, 자동적으로 파일명이 생성됩니다.
          maxSize: "20m", // maxSize는 저장할 로그 파일의 최대 크기입니다. 
    											// 최대 크기를 초과하면 앞에 기록된 메시지가 삭제됩니다.
    											// 굳이 정의를 하지 않아도 괜찮습니다.
          maxFiles: "30d", // maxFiles에 파일 보관 기한입니다. 기한을 넘어가면, 로그 파일이 삭제됩니다.
        }),
        new winstonDaily({
          level: "error", 
          datePattern: "YYYY-MM-DD",
          dirname: logDir, 
          filename: "%DATE%.error.log",
          maxSize: "20m",
          maxFiles: "30d", 
        }),
      ],
    });
    
    // 개발 환경에서 터미널에서 출력
    if (process.env.NODE_ENV != "prod") {
      logger.add(
        new winston.transports.Console({
          format: winston.format.combine(
            winston.format.colorize(), // colorize는 로그의 색상입니다.
            winston.format.simple() 
          ),
        })
      );
    }
    
    module.exports = logger;

참고 및 추천

이 글은 윈스턴의 모든 기능을 설명하고 있지 않습니다.
윈스턴이 더 궁금하시다면, 아래 잘 정리된 블로그 글을 읽어보시는 걸 추천드리겠습니다.
인파: NODE-📚-Winston-모듈-사용법-서버-로그-관리

참고 인파: NODE-📚-Winston-모듈-사용법-서버-로그-관리

0개의 댓글