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

Shin·2022년 4월 25일
1

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으로 생성 된 logwinston에 보내주는 코드 입니다.


Winston 사용법

윈스턴 주소

winstoncreateLogger를 통해 자신만의 고유한 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을 통해 로그 형식을 맞춘 후,

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

이때 logger.stream을 통해 loggerinfo에 받은 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은 코드 중 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 로그 사용법

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

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

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

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

이때 errorwarn에도 같이 저장되고 info에도 저장되며
warninfo에도 같이 저장이 되고
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개의 댓글