8a8c10248c
Local-first photo organizer that auto-sorts images by face recognition and EXIF capture date. - Electron app with 3-process split: Main (Node) / UI Renderer (React) / hidden Inference Renderer (face-api + WebGL) - Core pipeline: scan -> face match + EXIF -> path build -> atomic move/copy - Move = copy -> verify -> delete; auto-rename on filename collision - 1st-registered profile = move, others = copy; unmatched -> [미정]/YYYY/MM - EXIF date with mtime fallback - Vitest unit tests (pathBuilder / fileOps / concurrency) all green - electron-builder config for Windows (nsis) + macOS (dmg) - Docs: PRD / DECISIONS / ARCHITECTURE Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
65 lines
1.7 KiB
TypeScript
65 lines
1.7 KiB
TypeScript
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<void> {
|
|
this.logFile = path
|
|
await mkdir(dirname(path), { recursive: true })
|
|
}
|
|
|
|
private async write(level: Level, msg: string, meta?: unknown): Promise<void> {
|
|
// 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()
|