import { appendFile, mkdir } from 'node:fs/promises' import { dirname } from 'node:path' type Level = 'INFO' | 'WARN' | 'ERROR' /** * 구조적 로거. 콘솔 + (옵션) 파일에 기록. * 잡 실행 시 setLogFile()로 출력 루트 하위 로그 파일 경로를 지정한다. */ class Logger { private logFile: string | null = null private buffer: string[] = [] async setLogFile(path: string): Promise { this.logFile = path await mkdir(dirname(path), { recursive: true }) } private async write(level: Level, msg: string, meta?: unknown): Promise { // new Date() 사용 (Main 프로세스는 일반 Node — 제약 없음) const ts = new Date().toISOString() const metaStr = meta === undefined ? '' : ` ${safeJson(meta)}` const line = `[${ts}] [${level}] ${msg}${metaStr}` if (level === 'ERROR') console.error(line) else if (level === 'WARN') console.warn(line) else console.log(line) this.buffer.push(line) if (this.logFile) { try { await appendFile(this.logFile, line + '\n', 'utf-8') } catch { // 로그 파일 기록 실패는 치명적이지 않음 — 콘솔 출력은 이미 됨 } } } info(msg: string, meta?: unknown) { return this.write('INFO', msg, meta) } warn(msg: string, meta?: unknown) { return this.write('WARN', msg, meta) } error(msg: string, meta?: unknown) { return this.write('ERROR', msg, meta) } /** 잡 종료 시 버퍼 비우기 */ reset(): void { this.buffer = [] this.logFile = null } } function safeJson(v: unknown): string { try { return JSON.stringify(v) } catch { return String(v) } } export const logger = new Logger()