winston, winston-daily-rotate-file 로 로그 관리하기(node.js)

뭐하시는 분이세요?·2022년 3월 31일
0

서비스를 개발할때는 로그를 남기는것이 필수다.
로그를 남기지 않으면 운영중인 서비스에서 문제가 생겼을때, 원인을 찾고 해결하기가 매우 어려워진다.

나는 프론트엔드 개발자이지만, 아무도 로그를 고려하지 않는다...
그래서 내가 직접한다.. 그 과정에서 알게된 것들을 기록해두었다.

준비

winston, winston-daily-rotate-file 패키지 설치

npm install winston winston-daily-rotate-file

Logger 설정 파일 작성

  1. winston.js 파일을 생성한다. (이름은 개발자 마음대로..^^)
  2. winston.createLogger() 에 로그 설정들을 작성해서 넘겨준다.
    • format : 타임스탬프나 로그 포맷을 결정할 수 있다.
    • transports : logging targets를 설정할 수 있다.
      여기서 winston.transports.Console 로 똑같은 로그를 콘솔에도 찍어줄 수 있다.
const winston = require('winston');
const winstonDaily = require('winston-daily-rotate-file');
const { combine, timestamp, printf, colorize  } = winston.format;

const logDir = 'logs';  // 'logs' 디렉토리 

// 로거 생성
const logger = winston.createLogger({
  format: combine(
    timestamp({format: 'YYYY-MM-DD HH:mm:ss'}), // 로그 파일에 시간이 이런 형태로 기록됨
    printf(info => { // 데이터를 어떤 형태로 받을지 직접 커스텀 할 수 있음
			return `${info.timestamp} ${info.level}: ${info.message}`
    }),
  ),

  transports: [
		// 로그 파일에 찍힐 내용을 콘솔에도 찍어줌
    new winston.transports.Console({ 
      format: winston.format.combine(
        winston.format.splat(), 
        winston.format.colorize(), 
        // winston.format.simple() // `${info.level}: ${info.message} JSON.stringify({ ...rest })` 포맷
			)
    }),

    new winstonDaily({
      level: 'info', // info 레벨 로그를 저장할 파일 설정
      datePattern: 'YYYY-MM-DD',
      zippedArchive: true, 
      filename: `%DATE%.log`, // %DATE% = 위에서 설정한 datePattern 이 들어감
      dirname: logDir,
      maxFiles: 30,  // 30일치 로그 파일 저장
    }),
    
    new winstonDaily({
      level: 'error', // error 레벨 로그를 저장할 파일 설정
      datePattern: 'YYYY-MM-DD',
      zippedArchive: true,
      filename: `%DATE%.error.log`,
      dirname: logDir + '/error',  // error.log 파일은 /logs/error 하위에 저장 
      maxFiles: 30,
    }),
  ],
});

module.exports = { logger };


만약 개발 환경에서만 콘솔에 로그 내용을 찍어주고 싶다면..

환경변수에 따라 분기처리를 해주고,
logger.add(new Winston.transports.Console ... 로 새로운 로거를 추가해주면 된다.

if (process.env.NODE_ENV !== 'PROD') {
  logger.add(new winston.transports.Console({
    format: winston.format.combine(
      winston.format.colorize(),  // 색깔 넣어서 출력
      winston.format.simple(),  // `${info.level}: ${info.message} JSON.stringify({ ...rest })` 포맷으로 출력됨
    )
  }));
}


만약 내가 로그 포맷을 커스텀 했다면?

format 옵션의 값으로 winston.format.simple() 을 넘겨주면, 로그가 ${info.level}: ${info.message} JSON.stringify({ ...rest }) 포맷으로 출력 되기 때문에 따로 값을 설정하지 않는 게 좋겠다...!!



로그 남기기

log( ) 또는 각 레벨 함수로 로그를 남길 수 있다.

해당 함수에는 message를 인자로 넘겨줄 수 있는데, 이 인자는 info객체의 message로 들어간다. createLogger 참고,,

logger.info(message) 또는 logger.log("info", message)
logger.warn(message) 또는 logger.warn("info", message)
logger.error(message) 또는 logger.error("info", message)
...

이 함수를 로그를 남기고자 하는 액션이 작성된 곳에 추가해준다.



실제 적용 예시

아래 예시는 로그인이 성공하는 경우에 로그를 남긴다.

  • 요청 보낸 사용자의 IP주소를 알아내기 위해서 request-ip 라는 패키지 사용
  • logger.info() 에 인자로 객체를 넘겨주어, 유저아이디/타입(로그인)/아이피주소 를 같이 넘겨주었다.
const commonModel = require("../../models/Common/commonModel");
const { logger } = require("../../winston");
const requestIP = require('request-ip');

const commonController = {
  login : async (req, res) => {
    const response = await commonModel.login(req.body);

    if (response.ErrorCode === "OK") {
      const userIP = requestIP.getClientIp(req);
      logger.info( {id:req.body.id, ip:userIP, type:"로그인"} );
    }
    return res.json(response);
  },
}

module.exports = commonController;

그리고 YYYY-MM-DD hh:mm:ss info [로그인] 사용자id (IP주소) 이런 형태로 로그를 남기고 싶어서 아래와 같이 작성 했다.

...
format: combine(
    timestamp({format: 'YYYY-MM-DD HH:mm:ss'}),
    printf(info => { 
      return `${info.timestamp} ${info.level}: [${info.message.type}] ${info.message.id} (${info.message.ip})`
    }),
  ),
...


내가 만든 서비스에도 많은 유저가 생겼으면 좋겠다,, 😂



profile
이것저것 개발하지만 프론트엔드 개발이 제일 좋아요 👩🏻‍💻

0개의 댓글