This commit is contained in:
2025-08-30 20:21:14 +09:00
parent d34f6b803f
commit d77f0542f5
8 changed files with 775 additions and 0 deletions

180
logger.js Normal file
View File

@@ -0,0 +1,180 @@
import fs from 'fs';
import path from 'path';
// 로그 디렉토리 생성
const logDir = './logs';
if (!fs.existsSync(logDir)) {
fs.mkdirSync(logDir, { recursive: true });
}
// 로그 레벨 정의
const logLevels = {
DEBUG: 0,
INFO: 1,
WARN: 2,
ERROR: 3,
CRITICAL: 4
};
const logLevelNames = Object.keys(logLevels);
const currentLogLevel = logLevels.info; // 설정에 따라 변경 가능
// 타임스탬프 생성
const getTimestamp = () => new Date().toISOString();
// 호출자 정보 추출 (ESM 호환)
const getCallerInfo = () => {
const err = new Error();
const stack = err.stack.split('\n');
// 스택에서 호출자 위치 찾기
let callerIndex = 2; // 기본값 (0: Error, 1: getCallerInfo, 2: logger function)
// 'at '로 시작하는 라인 찾기
for (let i = 2; i < stack.length; i++) {
if (stack[i].includes('at ')) {
callerIndex = i;
break;
}
}
if (stack.length > callerIndex) {
const callerLine = stack[callerIndex].trim();
// 파일 경로 추출 (Windows와 Unix 경로 모두 처리)
const fileMatch = callerLine.match(/\((.*?):\d+:\d+\)/) ||
callerLine.match(/(.*?):\d+:\d+$/);
if (fileMatch && fileMatch[1]) {
const filePath = fileMatch[1];
const fileName = path.basename(filePath);
return fileName;
}
}
return 'unknown';
};
// 로그 포맷팅
const formatLog = (level, message, metadata = {}) => {
const timestamp = getTimestamp();
const callerInfo = getCallerInfo();
const levelName = logLevelNames[level];
let logMessage = `[${levelName}] ${timestamp} [${callerInfo}] ${message}`;
if (Object.keys(metadata).length > 0) {
try {
logMessage += `\n Meta: ${JSON.stringify(metadata, null, 2)}`;
} catch (e) {
logMessage += `\n Meta: [Circular or invalid object]`;
}
}
return logMessage;
};
// 로그 파일 쓰기
const writeLog = (logMessage, level) => {
if (level < currentLogLevel) return;
// 콘솔 출력
if (level >= logLevels.ERROR) {
console.error(logMessage);
} else {
console.log(logMessage);
}
// 파일 로깅
try {
const date = new Date().toISOString().split('T')[0];
const logFile = path.join(logDir, `trading-${date}.log`);
fs.appendFileSync(logFile, logMessage + '\n');
} catch (e) {
// 파일 로깅 실패 시 콘솔에 경고
console.error(`[LOGGER] Failed to write log file: ${e.message}`);
}
};
// 로거 인스턴스 생성
const createLogger = () => {
return {
debug: (message, metadata = {}) => {
const logMessage = formatLog(logLevels.DEBUG, message, metadata);
writeLog(logMessage, logLevels.DEBUG);
},
info: (message, metadata = {}) => {
const logMessage = formatLog(logLevels.INFO, message, metadata);
writeLog(logMessage, logLevels.INFO);
},
warn: (message, metadata = {}) => {
const logMessage = formatLog(logLevels.WARN, message, metadata);
writeLog(logMessage, logLevels.WARN);
},
error: (message, error = null, metadata = {}) => {
const errorMetadata = { ...metadata };
if (error) {
errorMetadata.error = {
message: error.message || 'Unknown error',
stack: error.stack ?
error.stack.split('\n').slice(0, 5).join('\n') :
'No stack trace'
};
}
const logMessage = formatLog(logLevels.ERROR, message, errorMetadata);
writeLog(logMessage, logLevels.ERROR);
},
critical: (message, error = null, metadata = {}) => {
const errorMetadata = { ...metadata };
if (error) {
errorMetadata.error = {
message: error.message || 'Unknown critical error',
stack: error.stack || 'No stack trace'
};
}
const logMessage = formatLog(logLevels.CRITICAL, message, errorMetadata);
writeLog(logMessage, logLevels.CRITICAL);
},
logPosition: (position, message) => {
if (!position) return;
const metadata = {
position: {
id: position.id,
side: position.side,
entry: position.entry,
qty: position.qty,
sl: position.sl,
tp: position.tp,
openTime: position.openTime ?
new Date(position.openTime).toISOString() : 'N/A'
}
};
if (position.closed) {
metadata.position.exitPrice = position.exitPrice;
metadata.position.exitReason = position.exitReason;
metadata.position.pnl = position.pnl;
metadata.position.closeTime = position.closeTime ?
new Date(position.closeTime).toISOString() : 'N/A';
}
this.info(message, metadata);
}
};
};
// 싱글톤 인스턴스 생성
const logger = createLogger();
// 모듈 내보내기
export default logger;