[ Node ] Express 에서 Morgan과 Winston 으로 Logging 관리하기

Shin·2022년 4월 25일

Node

목록 보기
2/4
post-thumbnail

로그 정리에 사용하는 모듈은 총 3가지 입니다.

  • winston : 로그 파일 및 로그 레벨 관리 모듈
  • winston-dayily-rotate-file : 매일 날짜 별로 로그 파일 생성 및 관리 모듈 ( 시간이 지나면 자동으로 삭제 & 압축 관리 )
  • morgan : request 요청 로깅 미들웨어

winston으로 로그 레벨, 타임라인을 관리하고 morgan으로 요청/응답 정보들을 로깅을 할 수 있게 해서 Express 로그를 관리하고 위에서 로깅한 정보들을 날짜 별로 로그 파일을 생성해주고 일정 시간이 지나면 삭제 및 압축을 해주는 기능을 사용하기 위해 winston-dayily-rotate-file 을 사용했습니다!


Morgan 사용법

모건 주소

: Morgan 사용법은 정말 간단합니다 기본적으로 사용할 때는

margan("출력타입");

위 처럼 작성하면 끝 입니다! 이때 출력 타입들의 종류는 커스터마이징 하거나 기본적으로 제공해주는 Format 중에 선택하면 됩니다.

여러가지 Format 중 주로 사용하는 부분만 정리하면 아래와 같습니다.

  • :http-version : 요청한 HTTP 버전
  • :method : 요청한 HTTP method
  • :status : 응답의 상태 코드 입니다.

    📌 status 주의 사항

    클라이언트에 응답을 보내기 전에 요청/응답 주기가 완료되면(예: 클라이언트가 요청을 중단하여 TCP 소켓이 조기에 닫히는 상황) 상태가 비어 있게 됩니다.
    ( "-"가 로그에 표시됨).

  • :url : 요청한 URL
  • :response-time : morgan으로 들어오는 요청과 응답 헤더가 작성된 시간 사이의 시간(응답시간)
  • :res[content-length] : 응답 길이

저는 아래와 같은 형식을 주로 사용합니다.
:method :status :url :response-time ms

코드에서는

// app.js 파일
app.use(
  morgan(":method :status :url :response-time ms", { stream: logger.stream })
);

위처럼 morgan을 통해 dev format으로 생성 된 log를 winston에 보내주는 코드 입니다.


Winston 사용법

윈스턴 주소

winston은 createLogger를 통해 자신만의 고유한 logger를 만들 수 있습니다.
winston은 기본 로깅 수준 설정은 RFC5424에서 지정한 심각도 순서를 따릅니다.

const levels = {
  error: 0,
  warn: 1,
  info: 2,
  http: 3,
  verbose: 4,
  debug: 5,
  silly: 6,
};

즉 따로 설정하지 않으면 위의 수준을 사용하는건데 따로 설정할 필요가 없어 저대로 사용하였습니다.

로그 format 설정

const logFormat = printf((info) => {
  return `${info.timestamp} ${info.level}: ${info.message}`;
});

const logger = winston.createLogger({
  format: combine(
    timestamp({
      format: "YYYY-MM-DD HH:mm:ss",
    }),
    logFormat
  ),

logger.stream = {
  // morgan wiston 설정
  write: (message) => {
    logger.info(message);
  },
};

winston createLogger의 설정은 위와 같게 되어있습니다.
하나씩 풀면 logFormat을 통해 로그 형식을 맞춘 후,

createLogger의 format: combine을 통해 여러 형식을 하나의 format으로 합쳐줍니다.
여기서 timestamp 즉 수신한 시간과 logFormat으로 만든 사용자 지정 로그 format을 combine으로 하나의 format으로 합쳐줍니다.

이때 logger.stream을 통해 logger의 info에 받은 message값을 넣어주게 됩니다.

그 후, app.js파일의 { stream: logger.stream } 을 통해 morgan을 통해 나온 로그를 넣어줍니다.

app.use(
  morgan(":method :status :url :response-time ms", { stream: logger.stream })
);

winston-dayily-rotate-file 설명

winston-dayily-rotate-file 주소

winston-dayily-rotate-file은 코드 중 transport부분을 담당하고 있습니다.
new winstonDaily 를 통해 하나의 로그 파일의 포맷과 폴더 형식들을 정해줄 수 있다.

const logDir = "logs";

 new winstonDaily({
      level: "info",
      datePattern: "YYYY-MM-DD",
      dirname: logDir,
      filename: `%DATE%.log`, // file 이름 날짜로 저장
      maxFiles: 30, // 30일치 로그 파일 저장
      zippedArchive: true,
    }),

위 코드의 값들에 대해 하나씩 알아보자면

  • level : 저장될 로그의 레벨
  • datePattern : 날짜형식
  • dirname: 로그가 저장 될 폴더의 이름
  • filename: 로그 파일 이름 형식
  • maxFiles : 로그 파일이 저장되는 기간
  • zippedArchive : 압축여부

입니다.


Winston 로그 사용법

오류가 나타나야 하는 부분에 직접 로그를 작성하는 방법은 logger를 import 해온 후 오류를 작성할 부분에

import { logger } from "../utils/winstonLogger.js";

logger.info("정보를 나타내는 로그")
logger.warn("위험성을 나타내는 로그")
logger.error("에러를 나타내는 로그")

위 처럼 3단계로 나눠져있는 부분을 필요에 맞게 작성하시면 됩니다.
logger.warn 과 logger.error로 작성된 것은 error폴더와 warn 폴더에 따로 저장이 됩니다.

이때 error는 warn에도 같이 저장되고 info에도 저장되며
warn은 info에도 같이 저장이 되고
info는 혼자만 저장 됩니다.


production 환경 일때의 콘솔창 로그

// Production 환경이 아닌 경우(dev 등) 배포 환경에서는 
// 최대한 자원을 안잡아 먹는 로그를 출력해야함
if (process.env.NODE_ENV !== "production") {
  logger.add(
    new winston.transports.Console({
      format: combine(
        colorize({ all: true }), // console 에 출력할 로그 컬러 설정 적용함
        logFormat // log format 적용
      ),
    })
  );
}

위 코드는 배포 환경에서는 console에 로그를 출력하지 않고, 그 외의 환경에서만 console에 로그를 출력하도록 설정한 것 입니다!


전체코드

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

const logDir = "logs"; // logs 디렉토리 하위에 로그 파일 저장

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`, // file 이름 날짜로 저장
      maxFiles: 30, // 30일치 로그 파일 저장
      zippedArchive: true,
    }),
    // warn 레벨 로그를 저장할 파일 설정
    new winstonDaily({
      level: "warn",
      datePattern: "YYYY-MM-DD",
      dirname: logDir + "/warn",
      filename: `%DATE%.warn.log`, // file 이름 날짜로 저장
      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,
    }),
  ],
});

logger.stream = {
  // morgan wiston 설정
  write: (message) => {
    logger.info(message);
  },
};

// Production 환경이 아닌 경우(dev 등) 배포 환경에서는 
// 최대한 자원을 안잡아 먹는 로그를 출력해야함
if (process.env.NODE_ENV !== "production") {
  logger.add(
    new winston.transports.Console({
      format: combine(
        colorize({ all: true }), // console 에 출력할 로그 컬러 설정 적용함
        logFormat // log format 적용
      ),
    })
  );
}

export { logger };
profile
누군가의 선택지가 될 수 있는 사람이 되자

0개의 댓글