Home winston 모듈
Post
X

winston 모듈

winston에 대해 알아보자.


winston 모듈


winston은 로그를 효율적으로 관리할 수 있게 도와주는 모듈로 서버를 운영할 때 console.logconsole.error 를 대체하기 위한 모듈입니다.

쯕, Log를 파일에 저장해서 관리하기 위해 winston을 사용합니다.


winston-datily-rotate-file 모듈

winston-datily-rotate-file은 날짜별로 로그 파일을 생성하고 자동 삭제하는 등 파일 관리 모듈입니다.

서버가 종료되는 순간 쌓여있던 로그를 삭제하지않고 파일에 저장해서 관리합니다.


winston.format

format은 로그의 형식을 지정할 수 있습니다.

json, label, timestamp, printf, simple, combine 등의 다양한 형식으로 지정이 가능합니다.

combine은 여러 형식을 혼합해서 사용할 때 사용합니다.

  • timestamp : 날짜 형식 지정
  • label : 어플리케이션 이름 지정

로그 경로 & 출력 형식 설정

winston.format에서 필요한 프로퍼티들을 꺼내옵니다.

로그 파일을 저장할 경로를 설정해줍니다.

logs폴더를 만들어 설정해주겠습니다.

logFormat 변수에 로그 출력 포맷을 설정해줍니다.

timestamp [label] level: message

1
2
3
4
5
6
7
8
9
10
11
12
13
14
import winston from "winston";
import winstonDaily from "winston-daily-rotate-file";
let dirname = import.meta.dirname;

const { combine, timestamp, label, colorize, printf } = winston.format;

// 로그 파일 저장 경로
const logDir = `${dirname}/logs`;

// 로그 출력 포맷 정의 함수
const logFormat = printf(({ level, message, label, timestamp }) => {
  return `${timestamp} [${label}] ${level}: ${message}`;
  // 날짜 [라벨] 레벨 메세지
});

winston 로거 생성 (createLogger)

로그 기록(Logging)을 하는 메서드를 생성합니다.

winston.createLogger 메서드를 사용하며 format에 로그의 형식을 설정합니다.

보통 JSON 형식이나 combine을 통해 설정합니다.

1
2
3
4
5
6
7
8
9
const logger = winston.createLogger({
  // 로그 출력 형식 정의
  format: combine(
    timestamp({ format: "YYYY-MM-DD HH:MM:SS || " }),
    label({ label: "Project - 01" }), // 어플리케이션 이름
    logFormat // log 출력 포맷
    // format: combine() 에서 정의한 timestamp와 label 형식값이 logFormat에 들어가서 정의되게 된다. level이나 message는 콘솔에서 자동 정의
  ),
});

winston 로그 저장 방식 설정 (transports)

transports는 로그 저장 방식을 설정합니다.

winston-daily-rotate-file 모듈 에서 불러온 winstonDaily 생성자 함수를 사용해 어떤 레벨의 로그를 어떤 형식으로 저장할 지 등의 상세 설정이 가능합니다.

로그 레벨
error : 0, warn : 1, info : 2, http : 3, verbose : 4, debug : 5, silly : 6
숫자가 낮을 수록 위험도가 높습니다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
const logger = winston.createLogger({
  //* 로그 출력 형식 정의
  format: combine(),
  // ...

  transports: [
    // 레벨을 설정하면 해당 레벨보다 위험도가 높은 로그가 모두 출력됩니다.
    // info 레벨 로그
    new winstonDaily({
      level: "info", // info 레벨
      datePattern: "YYYY-MM-DD", // 파일 날짜 형식
      dirname: logDir, // 파일 경로
      filename: `%DATE%.log`, // 파일 이름
      maxFiles: 14, // 최근 14일치 저장
      zippedArchive: true, // gzip으로 압축할지 여부
    }),
    // error 레벨 로그
    new winstonDaily({
      level: "error",
      datePattern: "YYYY-MM-DD",
      dirname: logDir + "/error", // /logs/error 하위에 저장
      filename: `%DATE%.error.log`, // 에러 로그는 2022-12-28.error.log 형식으로 저장
      maxFiles: 14,
      zippedArchive: true,
    }),
  ],
});

winstonDaily 로깅 설정

  • frequency : 회전 빈도를 나타내는 문자열 (default : null)
    유효한 값은 ‘#m’ 또는 ‘#h’(예 : ‘5m’ 또는 ‘3h’)

  • datePattern : 회전에 사용할 moment.js 날짜 형식 을 나타내는 문자열 (default : ‘YYYY-MM-DD’)

  • zippedArchive : 아카이브된 로그 파일을 gzip으로 압축할지 여부 (default : ‘거짓’)

  • filename : 로그에 사용할 파일 이름 (default : ‘winston.log.%DATE%’)
    이 파일 이름은 파일 이름의 %DATE%를 포함할 수 있습니다.

  • dirname : 로그 파일을 저장할 디렉터리 이름 (기본: ‘.’)

  • stream : 사용자 지정 스트림에 직접 쓰고 회전 기능을 우회 (default : null)

  • maxSize : 회전할 파일의 최대 크기 (default : null)

  • maxFiles : 보관할 최대 로그 수 (default : null)

  • auditFile : 감사 파일의 이름을 나타내는 문자열 (default : ‘..json’)
    옵션 개체의 해시를 계산하여 생성된 기본 파일 이름을 재정의하는 데 사용할 수 있습니다.

  • utc : 파일 이름의 날짜에 UTC 시간 (default : false)

  • extension : 파일 이름에 추가할 파일 확장자 (default : ‘’)

  • createSymlink : 현재 활성 로그 파일에 대한 tailable symlink (default : false)

  • symlinkName : tailable symlink의 이름 (default : ‘current.log’)

  • options : 파일 스트림에 전달되어야 하는 추가 옵션 (default : { flags: ‘a’ })


winston 예외 로그 처리 (exceptionHandlers)

추가로 코드 에러 뿐만 아니라 try catch 에러도 잡기 위해 밑에 exceptionHandlers 를 설정해줍니다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
const logger = winston.createLogger({
  //_ 로그 출력 형식 정의
  format: combine(),
  // ...
  //_ 실제 로그를 어떻게 기록을 한 것인가 정의
  transports: [
    new winstonDaily({
      // ...
    }),
  ],

  //\* uncaughtException 발생시 파일 설정
  exceptionHandlers: [
    new winstonDaily({
      level: "error",
      datePattern: "YYYY-MM-DD",
      dirname: logDir + "/exception",
      filename: `%DATE%.exception.log`,
      maxFiles: 14,
      zippedArchive: true,
    }),
  ],
});

morgan과 winston 결합해서 사용하기

http 요청과 응답 로그를 남겨주는 morgan을 winston으로 관리해 해당 로그를 파일로 관리해보겠습니다.

먼저 morgan.js 파일에 사용할 morgan 커스텀을 정의합니다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
// morgan.js
import morgan from "morgan";
import logger from "../config/winston.js";

// 로그 작성을 위한 옵션.
const format =
  process.env.NODE_ENV === "production"
    ? "combined"
    : "[:date] :method :url :status :response-time ms";

// 로깅 스킵 여부
const skip = (_, res) => {
  if (process.env.NODE_ENV === "production") {
    return res.statusCode < 400; // 코드가 400 이상이면 로그 기록
  }
  return false;
};

// 로그 작성을 위한 Output stream옵션.
const stream = {
  write: (message) => {
    // logger.info(message);
    logger.info(
      message.replace(
        /[\u001b\u009b][[()#;?]*(?:[0-9]{1,4}(?:;[0-9]{0,4})*)?[0-9A-ORZcf-nqry=><]/g,
        ""
      )
    );
  },
};

const options = {
  skip,
  stream,
};

// 적용될 moran 미들웨어 형태
const morganMiddleware = morgan(format, options);

export default morganMiddleware;

이제 서버 파일인 app.jsmorgan.js를 불러와 미들웨어로 적용해줍니다.

1
2
3
4
5
6
//app.js
import morganMiddleware from "./src/middleware/morgan.js";

// ...

app.use(morganMiddleware);

참조

이 기사는 저작권자의 CC BY 4.0 라이센스를 따릅니다.