'use strict' /** * Copyright (c) 2017~2024, OBCon Inc. * All rights reserved. */ /** * @file * @copyright 2017~2024, OBCon Inc. * @author gye hyun james kim [pnuskgh@gmail.com] */ //--- https://github.com/winstonjs/winston //--- Logging Level : error: 0, warn: 1, info: 2, verbose: 3, debug: 4, silly: 5 // logger.error('~'); // logger.warn('~'); // logger.info('~'); // logger.debug('~'); //--- 로그 폴더 생성 const fs = require('fs'); const path = require('path'); //--- https://www.npmjs.com/package/winston const winston = require('winston'); //--- 로거 모듈 const winstonDaily = require('winston-daily-rotate-file'); //--- 날자별로 로거 파일 생성 //--- To-Do : 어떤 프로그램의 몇번째 라인에서 메시지를 표시하였는지를 로그에 표시 한다. //--- To-Do : log > Elastic Search > Kibana //--- 로그 포맷 //--- {level}: {timestamp} : {message} //--- {level}: {timestamp} : {filename} : {message} //--- 일반 로그 포맷 //--- {level}: {timestamp} : {filename} : {deviceIdx} : {message} //--- 특정 Device의 로그 포맷 class Logger { constructor() { this.logFolder = utils.getPath(config.logger.folder); this._initFolder(); this.useJson = config.logger.useJson; this.useConsole = config.logger.useConsole this.level = config.logger.level; //--- cluster와 pm2 환경에서 실행되는 instance의 수만큼 1씩 증가하며 로그 파일이 생성 된다. (0: Master in cluster, 1, 2, ...) this.filename = config.logger.filename.replace('%worker%', appl.worker.id); if (appl.type.endsWith('_batch')) { this.filename = this.filename.replace('\.log', '_batch.log'); } this.maxSize = config.logger.maxSize; this.maxFiles = config.logger.maxFiles; } _initFolder() { //--- 로그 폴더가 없는 경우 로그 폴더를 생성 한다. if (!fs.existsSync(this.logFolder)) { fs.mkdirSync(this.logFolder); } } getFormat() { const format = `YYYY-MM-DD HH:mm:ss.SSS : ${appl.worker.id}`; const formats = [ winston.format.timestamp({ format: format }) ]; if (this.useJson) { formats.push(winston.format.json()); } else { if (config.logger.showFileinfo == false) { formats.push(winston.format(function(info, opts) { info.message = info.timestamp + ' : ' + info.message; delete info.timestamp; return info; })({})); } else { formats.push(winston.format(function(info, opts) { let fileName = null; let lineNumber = null; let columnNumber = null; let tmpStackArray = new Error().stack.split('\n'); if (10 < tmpStackArray.length) { let tmpStr = tmpStackArray[10]; if ((-1 < tmpStr.indexOf('(')) && (-1 < tmpStr.indexOf(')'))) { //--- " at Object. (/work/appl/obcon/modules/modules.js:62:8)" tmpStr = tmpStr.substring(tmpStr.indexOf('(') + 1, tmpStr.indexOf(')')); } else { //--- " at /work/appl/obcon/modules/http_app.js:244:20" tmpStr = tmpStr.replace(/ at /, ''); } tmpStr = tmpStr.replace('file:///', ''); const tmpArray = tmpStr.split(':'); if (tmpArray.length == 3) { fileName = tmpArray[0]; lineNumber = tmpArray[1]; columnNumber = tmpArray[2]; } else if (tmpArray.length == 4) { fileName = tmpArray[0] + ':' + tmpArray[1]; lineNumber = tmpArray[2]; columnNumber = tmpArray[3]; } fileName = fileName.replace(/\\/g, '/').replace(appl.root, ''); } if (fileName == null) { info.message = info.timestamp + ' : : ' + info.message; } else { info.message = info.timestamp + ` : ${fileName} (${lineNumber}, ${columnNumber}) : ` + info.message; } delete info.timestamp; return info; })({})); } formats.push(winston.format.simple()); } return winston.format.combine(...formats); } getTransports() { const transports = [ new winstonDaily({ level: this.level, filename: path.join(this.logFolder, this.filename), datePattern: "YYYYMMDD", zippedArchive: false, maxSize: this.maxSize, maxFiles: this.maxFiles, handleExceptions: true }) ]; if (this.useConsole) { transports.push( new winston.transports.Console({ level: this.level, // colorize: true, handleExceptions: true }) ); } return transports; } createLogger() { const log = winston.createLogger({ format: this.getFormat(), transports: this.getTransports() }); log.setLoggerLevel = function(level) { log._readableState.pipes.forEach(pipe => { pipe.level = level; }); }; log.setLevel = async function(level, workerId = -1) { try { log.setLoggerLevel(level); // await ipc.requestOnly('services', 'setLoggerLevel', workerId, {}, { level: level }); } catch(ex) { console.error(ex); } }; return log; } } module.exports = Logger;