본문 바로가기

Nodejs

Nodejs 로그 라이브러리 (log4js or winston) 적용

반응형

log4js

스프링을 하다가 Nodejs로 넘어온뒤 가장 낯익은 로그 라이브러리는 log4js 였다.

log4js 의 장점은 설정 옵션이 간단한 편? 이라고 생각한다.

 

일단 라이브러리부터 설치한다.

## npm
$ npm i --save log4js

## yarn
$ yarn add log4js

이 후, log4js 설정파일을 만든다. (파일로 안만들고 바로 넣어도된다..)

## ./log4js.json

{
  "appenders": {
    "app": {
      "type": "console"
    },
    "infoFile": {
      "type": "file",
      "filename": "./log/info.log",
      "maxLogSize": 524288,
      "numBackups": 3,
      "compress": true
    },
    "info": {
      "type": "logLevelFilter",
      "level": "INFO",
      "appender": "infoFile"
    },
    "errorFile": {
      "type": "file",
      "filename": "./log/errors.log",
      "maxLogSize": 524288,
      "numBackups": 3,
      "compress": true
    },
    "errors": {
      "type": "logLevelFilter",
      "level": "ERROR",
      "appender": "errorFile"
    }
  },
  "categories": {
    "default": {
      "appenders": [
        "app", "errors", "info"
      ],
      "level": "info"
    }
  }
}

appenders : log4js 의 액션을 담당 ( 각 종류 별로 파일저장, 콘솔출력 등을 정의할 수 있다.)

categories : log4js.getLogger() 함수의 매개변수로 지정한 카테고리로 원하는 로그액션을 처리할 수 있다.

   default : 기본설정으로 여기서는 app(콘솔출력), errors(에러로그파일저장), info(모든로그파일저장) 를 appenders에                  추가했다.

 

 

설정을 정의하고 (나는 내가 출력시키는 모든 로그에 대해서 콘솔창에 출력 및 info 로그와 error 로그를 파일로 쌓도록 설정하였다.) nodejs 가 시작할 때, log4js 의 configure 함수를 통해 설정을 완료하면 된다.

const path = require('path');
const log4js = require('log4js');
log4js.configure(path.join(__dirname, 'log4js.json')); // log4js 설정

이 후, 로그를 원하는 위치에서 import 이후 사용하면된다.

const logger = require('log4js').getLogger('logTest');

logger.info('info test');
logger.error('error test');

 

콘솔 로그에도 찍히고, 파일로도 정상적으로 쌓이는것을 확인할 수 있다.

위에서 보는 바와 같이 log4js 의 장점은 log4js 패키지를 가져와서 설정을 했기 때문에 한번 설정해놓으면 어디서든지 log4js 의 패키지를 가져오면 같은 설정이라는 점이다. 

 

 

winston

winston 은 사용자가 많다. 그만큼 인터넷에 정보도 많고, 관련 라이브러리도 많은 것 같다.

 

라이브러리를 설치한다.

## npm
$ npm i --save winston
$ npm i --save winston-daily-rotate-file

## yarn
$ yarn add winston
$ yarn add winston-daily-rotate-file

winston 의 로그 설정을 한다.

/**
* winston.js
*/

const winston = require('winston');
const winstonDaily = require('winston-daily-rotate-file'); // 날짜별로 로그 저장

const logDir = 'logs';  // logs 디렉토리 하위에 로그 파일 저장
const { combine, timestamp, printf, label } = winston.format;

const colorize = winston.format.colorize(); // 로그레벨별로 색상 정의

// 기본설정을 사용하면 로그레벨만 색상이 적용되어 출력되는 로그를 재정의하였다.
// Define log format
const logFormat = printf(({ level, message, label, timestamp }) => {
    return `${colorize.colorize(level, `[${timestamp}] [${level.toUpperCase()}] ${label ?? 'default'}:`)} ${message}`;
});

/**
 * winston 로그 사용을 위한 함수
 * @param path{string}: label
 * @returns {Logger}
 */
const getLogger = (path) => {
    /**
     * Log Level
     * error: 0, warn: 1, info: 2, http: 3, verbose: 4, debug: 5, silly: 6
     */
    const logger = winston.createLogger({
        format: combine(
            label({ label: path }), // 로그 출력 시 라벨링 설정
            timestamp({
                format: 'YYYY-MM-DD HH:mm:ss',
            }),
            logFormat,
        ),
        transports: [
            // info 레벨 로그를 저장할 파일 설정
            new winstonDaily({
                level: 'info',
                datePattern: 'YYYY-MM-DD',
                dirname: logDir,
                filename: `%DATE%.log`,
                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,
            }),
        ],
    });

    /**
     * http log
     */
    const httpLogger = winston.createLogger({
        format: combine(
            label({ label: 'http' }),
            timestamp({
                format: 'YYYY-MM-DD HH:mm:ss',
            }),
            logFormat,
        ),
        transports: [
            // info 레벨 로그를 저장할 파일 설정
            new winstonDaily({
                level: 'info',
                datePattern: 'YYYY-MM-DD',
                dirname: logDir,
                filename: `%DATE%.http.log`,
                maxFiles: 30,  // 30일치 로그 파일 저장
                zippedArchive: true,
            })
        ],
    });

    logger.stream = {
        write: (message, encoding) => {
            httpLogger.info(message);
        }
    };

    // Production 환경이 아닌 경우(dev 등) - Console 로그 출력
    if (process.env.NODE_ENV !== 'production') {
        logger.add(new winston.transports.Console({
            // handleExceptions: true,
            // json: false,
            format: combine(
                label({ label: path }),
                timestamp(),
                logFormat,
                // `${info.level}: ${info.message} JSON.stringify({ ...rest })` 포맷으로 출력
                // winston.format.simple(),  
            )
        }));
    }

    return logger;
};

module.exports = getLogger;

이 후, 로그를 원하는 위치에서 import 이후 사용하면된다.

const logger = require('./winston')('server');

logger.info('winston info log test');
logger.error('winston error log test');

timestamp + log level + label + message

위와 같이 정상적으로 로그가 출력되고, 파일로 저장되는 것을 확인할 수 있다.

 

+ morgan ( with winston )

morgan 은 http request 에 대한 로그를 기록하는 미들웨어이다. 

winston 을 사용하고 있다면, 기존 로거를 사용해도 되지만 나는 새로 만들었다. ( 위 글 winston 예제를 참고하신 거라면 이미 추가 되어 있을 것이다.)

    // winston.js
    
    /**
     * http log
     */
    const httpLogger = winston.createLogger({
        format: combine(
            label({ label: 'http' }),
            timestamp({
                format: 'YYYY-MM-DD HH:mm:ss',
            }),
            logFormat,
        ),
        transports: [
            // info 레벨 로그를 저장할 파일 설정
            new winstonDaily({
                level: 'info',
                datePattern: 'YYYY-MM-DD',
                dirname: logDir,
                filename: `%DATE%.http.log`,
                maxFiles: 30,  // 30일치 로그 파일 저장
                zippedArchive: true,
            })
        ],
    });

    logger.stream = {
        write: (message, encoding) => {
            httpLogger.info(message);
        }
    };

이 후, express 를 셋팅하는 부분에서 

// app.js

const express = require('express');
const cors = require('cors');
const morgan = require('morgan');
const { stream } = require('./winston')();

const app = express();

app.use(cors());
app.use(express.json());
app.use(express.urlencoded({ extended: true }));
app.use(
    // 환경에 따라 다른 인자 적용 - 운영 : combined , 개발 : dev
    // (dev, short, common, combined)
    morgan(process.env.NODE_ENV !== "production" ? "dev" : "combined", {
    	// 400 미만의 http code일 때는 스킵
        skip: (req, res) => { return res.statusCode < 400 },
        // 400 이상일 때 로그 출력
        stream
    })
);

...........생략................

2021-04-18.http.log

  [dev]

[2021-04-18 11:21:47] [INFO] http: GET /api/v1/test/test1 404 2.554 ms - -

  [combined]

[2021-04-18 11:20:06] [INFO] http: ::1 - - [18/Apr/2021:02:20:06 +0000] "GET /api/v1/test/test1 HTTP/1.1" 404 - "-"
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/90.0.4430.72 Safari/537.36"

위와 같이 파일로 저장되는 것을 확인 할 수 있다.

 

각 로그 라이브러리들은 장단점이 있을 수 있기 때문에 본인 입맛에 맞는 라이브러리를 사용하면 될 듯 하다.

 

참고소스

https://github.com/sbjang123456/nodejs-sequelize-pm2.git

 

sbjang123456/nodejs-sequelize-pm2

pm2 배포 환경 설정. Contribute to sbjang123456/nodejs-sequelize-pm2 development by creating an account on GitHub.

github.com

 

반응형