Files
photoai/src/main/logger.ts
T
koriweb 8a8c10248c Initial commit: AI Photo Organizer (Electron + face-api)
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>
2026-06-01 13:36:40 +09:00

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()