[원본]에서 보시는게 더 깔끔합니다.
www.notion.so/logger-5955a78345c44fb4886c88c00000bba8
[개발환경]
-
운영체제: 윈도우 10 Pro 64비트
-
빌드버전: 1903
-
CPU: Intel(R) Core(TM) i7-7700
-
램: 32GB
-
Node.js 버전: v12.18.2
-
TypeScript 버전: 3.9.5
-
winston 버전: 3.3.3
[tsconfig.json]
{
"compilerOptions": {
"target": "ES2019",
"module": "commonjs",
"noImplicitAny": false,
"skipLibCheck": true,
"forceConsistentCasingInFileNames": true
}
}
-
이 중에서 특히 "noImplicitAny"는 false로 설정해야 함
[logger.ts]
import moment from 'moment';
import winston from 'winston';
import path from 'path';
import mkdirp from 'mkdirp';
import fs from 'fs';
const enum log_level_e {
LogLevelDebug = 'debug',
LogLevelInfo = 'info',
LogLevelError = 'error'
};
class logger_base {
public static readonly kLogLevel: string = log_level_e.LogLevelDebug;
public static readonly kMaxFileSize: number = 1024 * 1024 * 100; //100MB
public static readonly kNumMaxFiles: number = 100; //로그파일 최대 100개
public static readonly kFilename: string = 'mam_server.log';
public static readonly kMaxFilenameLength: number = 20;
//INFO: 로그 저장 폴더
public static readonly kLogPath: string = './logs';
//INFO: 프로젝트 최상위 폴더
public static readonly kProjRootPath: string = path.join(__dirname, '..');
//INFO: 파일이름만 출력할 경우 false, 경로까지 출력할 경우 true
public static readonly kUseRelativePath: boolean = false;
private readonly writer: winston.Logger;
constructor(){
this.makeLoggerFolder();
this.writer = this.getLogger();
}
private makeLoggerFolder(){
try{
mkdirp.sync(logger_base.kLogPath);
}
catch(ex){
console.error(`Create logger path FAILED; ${ex.message}`);
return;
}
console.info(`Create logger folder SUCCESS`);
}
private getTimeStampFormat(): string {
return moment().format('YYYY-MM-DD HH:mm:ss.SSS ZZ').trim();
}
private getLogger(){
if(this.writer !== undefined){
return this.writer;
}
return winston.createLogger({
transports: [
new winston.transports.File({
filename: path.join(logger_base.kLogPath, './mam_server.log'),
level: logger_base.kLogLevel,
maxsize: logger_base.kMaxFileSize,
maxFiles: logger_base.kNumMaxFiles,
format: winston.format.printf(info => `${this.getTimeStampFormat()} [${info.level.toUpperCase()}] ${info.message}`),
tailable: true //INFO: 최신 로그 파일의 이름이 항상 동일하게 유지됨 (직전 로그 파일은 가장 높은 번호의 파일)
}),
new winston.transports.Console({
level: logger_base.kLogLevel,
format: winston.format.printf(info => `${this.getTimeStampFormat()} [${info.level.toUpperCase()}] ${info.message}`)
}),
]
});
}
private createFinalMessage(message: string){
let stackInfo = this.getStackInfo(1);
let filenameInfo: string = (logger_base.kUseRelativePath ? stackInfo?.relativePath : stackInfo?.file) as string;
let finalMessage: string = `[${filenameInfo}:${stackInfo?.line}] ${message}`;
return finalMessage;
}
public info(...args: any[]){
this.writer.info(this.createFinalMessage(this.getLogString(args)));
}
public warn(...args: any[]){
this.writer.warn(this.createFinalMessage(this.getLogString(args)));
}
public error(...args: any[]){
this.writer.error(this.createFinalMessage(this.getLogString(args)));
}
public debug(...args: any[]){
this.writer.debug(this.createFinalMessage(this.getLogString(args)));
}
private getLogString(args: any[]){
let resultStr: string = '';
for(let i=1;i<args.length;i++){
//INFO: 객체 타입
if(typeof(args[i]) === 'object'){
resultStr += JSON.stringify(args[i]) + '\t';
}
else {
resultStr += args[i] + '\t';
}
}
return args[0] + '\t' + resultStr;
}
/**
* Parses and returns info about the call stack at the given index.
*/
private getStackInfo (stackIndex: number) {
// get call stack, and analyze it
// get all file, method, and line numbers
let stacklist = (new Error(undefined)).stack?.split('\n').slice(3);
// stack trace format:
// http://code.google.com/p/v8/wiki/JavaScriptStackTraceApi
// do not remove the regex expresses to outside of this method (due to a BUG in node.js)
let stackReg = /at\s+(.*)\s+\((.*):(\d*):(\d*)\)/gi
let stackReg2 = /at\s+()(.*):(\d*):(\d*)/gi
let s = stacklist?.[stackIndex] || stacklist?.[0];
if(s === undefined){
throw new Error();
}
s = s.toString();
let sp = stackReg.exec(s) || stackReg2.exec(s)
if (sp && sp.length === 5) {
return {
method: sp[1],
relativePath: path.relative(logger_base.kProjRootPath, sp[2]),
line: sp[3],
pos: sp[4],
file: path.basename(sp[2]),
stack: stacklist?.join('\n')
}
}
}
}
const loggerBase: logger_base = new logger_base();
export default loggerBase;
[index.ts]
import logger from './logger';
logger.info('This is info log');
logger.error('This is error log');
logger.debug('This is debug log');
logger.warn('This is warn log');
[output]
2020-07-02 18:58:23.579 +0900 [INFO] [index.ts:3] This is info log
2020-07-02 18:58:23.583 +0900 [ERROR] [index.ts:4] This is error log
2020-07-02 18:58:23.584 +0900 [DEBUG] [index.ts:5] This is debug log
2020-07-02 18:58:23.598 +0900 [WARN] [index.ts:6] This is warn log
'컴퓨터 공학 > JavaScript' 카테고리의 다른 글
값에 따른 isNaN() 결과 정리 (0) | 2020.07.11 |
---|---|
데이터 타입에 따른 Boolean 형변환 결과 정리 (0) | 2020.07.11 |
Node.js 14 추가 기능 (0) | 2020.06.26 |
웹 페이지의 DOM을 크롬 확장 앱에서 제어하는 소스 예제 (0) | 2020.01.11 |
타입스크립트 기반 간단한 TCP 서버/클라이언트 개발 (0) | 2019.12.29 |