본문 바로가기

Node.js/node 활용하기

node - winston node.js 로그 관리하기 - 기본 명령어, 적용하기

반응형

개발을 하다보면 새삼 로그의 중요성을 느낄 때가 많다.

어느 시점에 에러가 났는지 그에 따라서 어느시점에 복구가 되었는지 로그를 보고 대응이 가능하며 

에러 시점을 파악하기 위해 터미널을 쫙 열어놓고 tail -f (해당 파일의 끝부분을 계속해서 읽어오는 명령어)만 주구장창 보는일도 많았다. (이렇게 로그를 계속 보며 테스트 하며  왜 버그가 일어날까 하고 일주일동안 고통받던적도 있다. )

 

그에 따라서 Log는 에러를 파악할 수 있는 열쇠기 때문에 꼭 로그 시스템을 구축해서 시스템을 운영하는것이 필수적이다. 

 

Node.js에서도 log를 효율적으로 관리할 수 있게 도와주는 모듈인 winston.js가 존재한다. 

 

https://github.com/winstonjs/winston

 

winstonjs/winston

A logger for just about everything. Contribute to winstonjs/winston development by creating an account on GitHub.

github.com

 

 

log level

일단 기본적으로 log level은 크게 7가지로 나눌 수 있다. 

const levels = { 

  error: 0,

  warn: 1,

  info: 2,

  http: 3,

  verbose: 4,

  debug: 5,

  silly: 6

};

주로 로깅을 진행하게 되면 error, info를 로그에 남겨서 사용하게 된다. 

 

winston.createLogger를 활용해서 loggin system을 구축 할 수 있다. 

const logger = winston.createLogger({
  transports: [
    new winston.transports.Console(),
    new winston.transports.File({ filename: 'combined.log' })
  ]
});

 

winston 기본 값 

name default   내용 
level 'info' info.level이 수준 이하인 경우에만 기록
levels winston.config.npm.levels 로그 우선 순위를 나타내는 level (및 색상)
format winston.format.json 메시지에 대한 기본 형식을 지정한다 (날짜, level, message 등) 
transports []  메시지에 대한 로깅 형식을 종합해서 나타 낼 수 있다. 
exitOnError true false인 경우 처리된 예외가 발생하지 않는다
silent false true인 경우  모든 로그가 표시되지 않습니다.

 

 

로깅 작성해보기 

 

npm init
npm i express winston

 

해당 winston.js 에서 제공해주는 로깅 샘플이 존재한다 .

 

샘플을 기반으로 logger.js를 작성한다 

logger.js

const winston = require('winston');

const logger = winston.createLogger({
  level: 'info',
  format: winston.format.json(),
  defaultMeta: { service: 'user-service' },
  transports: [
    //
    // - Write all logs with level `error` and below to `error.log`
    // - Write all logs with level `info` and below to `combined.log`
    // error 레벨을 error.log라는 파일에, info레벨을 combined.log에 저장 
    new winston.transports.File({ filename: 'error.log', level: 'error' }),
    new winston.transports.File({ filename: 'combined.log' }),
  ],
});

//
// If we're not in production then log to the `console` with the format:
// node의 Console.log 도 또한 format에 저장한다
// 위와같이 console을 로그에 저장하는 경우는 dev 서버에서 진행하기 때문에 
// console 또한 로그에 남길 수 있다.
// `${info.level}: ${info.message} JSON.stringify({ ...rest }) `
//
if (process.env.NODE_ENV !== 'production') {
  logger.add(new winston.transports.Console({
    format: winston.format.simple(),
  }));
}
module.exports = {logger};

 

 

 

위와같이 파일을작성하고 로그를 작성해보겠다. 

 

index.js 

const express =require('express');
const morgan = require('morgan')
const PORT = 8080;
const app = express();
const {logger}= require('./log');
app.use(express.json());
app.use(express.urlencoded({extended:false}));

app.get("/", (req,res) => {
    logger.info("test")
    logger.error("test")
    return res.json({hello: "world"});
})

app.listen(PORT, () => console.log(`this server listening on ${PORT}`))

해당 내용으로 express를 작성하고 서버를 기동한다. 

 

그후 GET 요청으로 해당 localhost에 보내게 되면 log가 찍히게 될 것이다. 

 

log 파일이 생성되고
log 내용이 잘 작성되어 있다. 

 

이렇게 1차적으로 로그 파일 을 작성 해 보았다. 

 

log file 관리하기

log 작성까지는 순조롭게 완료가 되었다. 하지만 실제로 서비스를 운영하다보면 로그 파일은 계속해서 쌓이기 시작하고 

그에 따라서 엄청나게 용량을 많이 차지하게 된다. 따라서 이에 따라 로그 파일에 대한 관리가 필요해지게 된다.

 

이에따른 방법 또한 winston-daily-rotate-file module이 해당 부분을 해결해 준다. 

 

https://www.npmjs.com/package/winston-daily-rotate-file

 

winston-daily-rotate-file

A transport for winston which logs to a rotating file each day.

www.npmjs.com

 

해당 모듈은 

  • 오래된 로그 파일 관리 (지우거나 압축하는 형식으로 변경)
  • 해당 로그 파일에 대한 최대 용량값, 최대 파일 수 제한

등 효과적으로 로그파일을 관리 할 수 있게 도와준다.

 

 

 

해당 로그가 제공해주는 시스템은 다음과 같다. 

  • frequency: 회전 빈도를 나타내는 문자열입니다. 이는 특정 시간에 발생하는 회전과 달리 시간이 지정된 회전을 원하는 경우에 유용합니다. 유효한 값은 '#m' 또는 '#h'(예: '5m' 또는 '3h')입니다. 이 null을 남겨두는 datePattern것은 회전 시간 에 의존합니다 . (기본값: null)
  • datePattern: 회전에 사용할 moment.js 날짜 형식  나타내는 문자열 입니다. 이 문자열에 사용된 메타 문자는 파일 회전 빈도를 나타냅니다. 예를 들어, datePattern이 단순히 'HH'인 경우 매일 선택하여 추가되는 24개의 로그 파일로 끝납니다. (기본값: 'YYYY-MM-DD')
  • zippedArchive: 아카이브된 로그 파일을 gzip으로 압축할지 여부를 정의하는 부울입니다. (기본값: '거짓')
  • filename: 로그에 사용할 파일 이름입니다. 이 파일 이름은 파일 이름의 %DATE%해당 지점에 서식이 지정된 datePattern을 포함하는 자리 표시자를 포함할 수 있습니다 . (기본값: 'winston.log.%DATE%')
  • dirname: 로그 파일을 저장할 디렉터리 이름입니다. (기본: '.')
  • 스트림: 사용자 지정 스트림에 직접 쓰고 회전 기능을 우회합니다. (기본값: null)
  • maxSize: 회전할 파일의 최대 크기입니다. 바이트 수 또는 kb, mb 및 GB 단위가 될 수 있습니다. 단위를 사용하는 경우 접미사로 'k', 'm' 또는 'g'를 추가합니다. 단위는 숫자를 직접 따라야 합니다. (기본값: null)
  • maxFiles: 보관할 최대 로그 수입니다. 설정하지 않으면 로그가 제거되지 않습니다. 이는 파일 수 또는 일 수일 수 있습니다. 일을 사용하는 경우 접미사로 'd'를 추가합니다. (기본값: null)
  • options: 파일 스트림에 전달되어야 하는 추가 옵션을 나타내는 https://nodejs.org/api/fs.html#fs_fs_createwritestream_path_options 와 유사한 객체 . (기본값: { flags: 'a' })
  • auditFile : 감사 파일의 이름을 나타내는 문자열. 옵션 개체의 해시를 계산하여 생성된 기본 파일 이름을 재정의하는 데 사용할 수 있습니다. (기본값: '..json')
  • utc : 파일 이름의 날짜에 UTC 시간을 사용합니다. (기본값: 거짓)
  • extension : 파일 이름에 추가할 파일 확장자. (기본: '')
  • createSymlink : 현재 활성 로그 파일에 대한 tailable symlink를 만듭니다. (기본값: 거짓)
  • symlinkName : tailable symlink의 이름입니다. (기본값: 'current.log')

 

 

시스템에 적용하기

winston-daily-rotate-file 과 morgan을 다운받아 준다. 

 

morgan은 http request에 대한 logging 을 남겨주는 Middlware 이다 

 

 

npm i winston-daily-rotate-file morgan

 

morgan은 아래와 같은 형식으로 로그가 찍히게 된다. 

2021-07-11 10:50:01 info: ::1 - - [11/Jul/2021:01:50:01 +0000] "GET / HTTP/1.1" 304 - "-" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36"

 

 

위 내용을 기반으로 logger.js 파일을 다시 작성해 보았다. 

const winston = require('winston');
require('winston-daily-rotate-file');
const { combine, timestamp, printf } = winston.format;

const customFormat = printf(info => {
    return `${info.timestamp} ${info.level}: ${info.message}`;
});

const logger = winston.createLogger({
    format: combine(
        timestamp({
            format: 'YYYY-MM-DD HH:mm:ss',
        }),
        customFormat,
    ),
    // 로그에 대한 형식을 정 할 수 있다.
    transports: [
        // new transports.Console(),
// 날짜별로 파일 관리할 떄 사용가능하다 
        new winston.transports.DailyRotateFile({
            level: 'info',
            datePattern: 'YYYY-MM-DD',
            dirname: './logs',
            filename: `server_%DATE%.log`,
            maxSize: '20m',
            maxFiles: '7d',
            // maxFiles에서 벗어나서 지워질 파일을 압축파일로 변경할 것인가?
            zippedArchive: true,
        }),
        new winston.transports.DailyRotateFile({
            level: 'error',
            datePattern: 'YYYY-MM-DD',
            dirname: './logs',
            filename: `server_%DATE%.error.log`,
            maxSize: '20m',
            maxFiles: '7d',
            zippedArchive: true,
        }),
    ],
});

// production 모드가 아닐 경우
// 기본 console.log 도 log파일에 남길 수 있다. 
if (process.env.NODE_ENV !== 'production') {
    logger.add(
      new winston.transports.Console({
        format: winston.format.combine(winston.format.colorize(), winston.format.simple()),
      }),
    );
  }

//해당 stream은 morgan과도 연동 할 수 있다. 
const stream = {
    write: message => {
      logger.info(message)
    }
}

module.exports = { logger, stream };

 

 

위의 내용에서  stream 을 활용하였는데 해당 stream을 활용해서 morgan과 연동해서 로깅을 남길 수 도 있다.

 

 

 

index.js

const express =require('express');

const {logger, stream}  =require('./logger.js')
const morgan = require('morgan')
const PORT = 8080;
const app = express();
app.use(express.json());
app.use(express.urlencoded({extended:false}));
app.use(morgan('combined', {stream}))


app.get("/", (req,res) => {
    logger.info("test")
    logger.error("test")
    return res.json({hello: "world"});
})

app.listen(PORT, () => console.log(`this server listening on ${PORT}`))

 

morgan과 logger를 활용해서 다시 로그를 남겨보았다.

POSTMAN 요청 보내기 

 

 

위와 같이 로그 파일이 잘 기록 되었으며 로그 파일 관리 또한 잘 되는 것을 확인 할 수 있다. 

반응형