winston : 로그 파일 및 로그 레벨 관리 모듈winston-dayily-rotate-file : 매일 날짜 별로 로그 파일 생성 및 관리 모듈 ( 시간이 지나면 자동으로 삭제 & 압축 관리 )morgan : request 요청 로깅 미들웨어
winston으로 로그 레벨, 타임라인을 관리하고morgan으로 요청/응답 정보들을 로깅을 할 수 있게 해서Express로그를 관리하고 위에서 로깅한 정보들을 날짜 별로 로그 파일을 생성해주고 일정 시간이 지나면 삭제 및 압축을 해주는 기능을 사용하기 위해winston-dayily-rotate-file을 사용했습니다!
: 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은 createLogger를 통해 자신만의 고유한 logger를 만들 수 있습니다.
winston은 기본 로깅 수준 설정은 RFC5424에서 지정한 심각도 순서를 따릅니다.
const levels = {
  error: 0,
  warn: 1,
  info: 2,
  http: 3,
  verbose: 4,
  debug: 5,
  silly: 6,
};
즉 따로 설정하지 않으면 위의 수준을 사용하는건데 따로 설정할 필요가 없어 저대로 사용하였습니다.
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은 코드 중 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 : 압축여부입니다.
오류가 나타나야 하는 부분에 직접 로그를 작성하는 방법은 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 환경이 아닌 경우(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 };