https://www.npmjs.com/package/winston : logging library
https://www.npmjs.com/package/p-queue : promise callstack management
winston 은 로깅 라이브러리로
로그를 관리하고싶은데, 하드하게 관리하는건 귀찮을때 사용하기 편한 라이브러리다.
pino라는 로깅 라이브러리도 검토해봤지만, 기존 프로젝트에 큰 영향을 끼치지않고
독립적으로 쓰려고 하면 winston이 좋다.
winston의 기본 사용법은 어떻게 쓰는지는 쉽게 알 수 있으니 그부분에 description은 생략.
나는 추가로 winston-daily-rotate-file ( https://www.npmjs.com/package/winston-daily-rotate-file ) 라이브러리를 미들웨어로 사용해서, 매일 자동으로 새로운 파일로 생성되고, transport 생성으로 관리 하였다.
import winstonDaily from "winston-daily-rotate-file";
....
const log_file_max_size = 1048576 * 5; // 100MB
....
new winstonDaily({
level: "AnyLevelYouWant", // info, error, debug...
datePattern: "YYYYMMDD", // 20220310
dirname: path.join(__dirname, "../../logs/info"), // where is my log locate
filename: `altgap_%DATE%.log`, // filename rule
maxSize: log_file_max_size, // 파일당 최대 사이즈
maxFiles: 14, // 최대 파일 개수
}),
위와 같이 winston-daily-rotate-file 로 winston.transport 를 정의했다.
그런데 이렇게 해서 잘 사용하고있었는데,
프로세스가 빈번하게 crash & 재시작이 되는 문제가 발생했다.
원인은 해당 로깅 모듈을 여러 곳에 사용하고있었는데 그 중 bot 은 매 틱마다 ( 루프마다 ) 특정 이벤트를 호출하는 기능을 했다.
그러니, File I/O를 매 틱마다 실행하여 중첩되면서 app crash 가 계속 발생했던 것이다.
https://www.npmjs.com/package/p-queue
그래서 p-queue 라이브러리를 사용했다. p-queue 는 여러 promise 객체들을 queue에 넣어서 동시에 순차적으로 실행시켜주는 기능을 한다.
그렇게 p-queue 를 적용하면
import pqueue from "p-queue";
const queue = new pqueue({ concurrency: 4, intervalCap: 1, interval: 10 }); // 동시에 최대 4개 0.01초마다
....
const logger = winston.createLogger({
format: combine(
timestamp({
format: "YYYY-MM-DD HH:mm:ss",
}),
customFormat
),
levels: levels,
transports: transport,
});
const log = async (type: string, msg: string) => {
await queue.add(function () { // queue 에 promise 객체를 추가
return Promise.resolve(logger.log(type, msg)); // looger.log 호출
});
};
winston.ts
import pqueue from "p-queue";
import winston, { createLogger, format, transports } from "winston";
import winstonDaily from "winston-daily-rotate-file";
import path from "path";
const queue = new pqueue({ concurrency: 4, intervalCap: 1, interval: 10 });
const { combine, timestamp, printf, colorize } = format;
const customFormat = printf(info => `${info.timestamp} ${info.level}: ${info.message}`);
const log_file_max_size = 1048576 * 5; // 100MB
const levels = { trade: 0, error: 1, debug: 2, info: 3, warn: 4 };
const transport = [
new winston.transports.Console(),
new winstonDaily({
level: "info",
datePattern: "YYYYMMDD",
dirname: path.join(__dirname, "../../logs/info"),
filename: `altgap_%DATE%.log`,
maxSize: log_file_max_size,
maxFiles: 14,
}),
new winstonDaily({
level: "warn",
datePattern: "YYYYMMDD",
dirname: path.join(__dirname, "../../logs/warn"),
filename: `altgap_%DATE%.warn.log`,
maxSize: log_file_max_size,
maxFiles: 30,
}),
new winstonDaily({
level: "error",
datePattern: "YYYYMMDD",
dirname: path.join(__dirname, "../../logs/error"),
filename: `altgap_%DATE%.error.log`,
maxSize: log_file_max_size,
maxFiles: 50,
}),
new winstonDaily({
level: "debug",
datePattern: "MMDD",
dirname: path.join(__dirname, "../../logs/debug"),
filename: `altgap_%DATE%.debug.log`,
maxSize: log_file_max_size,
maxFiles: 100,
}),
new winstonDaily({
level: "trade",
datePattern: "YYYYMMDD",
dirname: path.join(__dirname, "../../logs/trade"),
filename: `altgap_%DATE%.trade.log`,
maxSize: log_file_max_size,
maxFiles: 50,
}),
];
const logger = winston.createLogger({
format: combine(
timestamp({
format: "YYYY-MM-DD HH:mm:ss",
}),
customFormat
),
levels: levels,
transports: transport,
});
const stream = {
write: message => {
logger.info(message);
},
};
const log = async (type: string, msg: string) => {
await queue.add(function () {
return Promise.resolve(logger.log(type, msg));
});
};
export { log, stream };
끝 👋