(level: LogLevel, scope: string, message: string, data?: unknown)
| 52 | } |
| 53 | |
| 54 | private write(level: LogLevel, scope: string, message: string, data?: unknown): void { |
| 55 | if (LEVEL_ORDER[level] < LEVEL_ORDER[this.threshold]) return |
| 56 | |
| 57 | let line = `[${new Date().toISOString()}] [${level}] [${scope}] ${message}` |
| 58 | |
| 59 | try { |
| 60 | const tail = formatData(data) |
| 61 | if (tail) line += `\n${tail}` |
| 62 | line += '\n' |
| 63 | ensureDir(path.dirname(this.logFile)) |
| 64 | this.rotateIfNeeded() |
| 65 | fs.appendFileSync(this.logFile, line, 'utf-8') |
| 66 | } catch (err) { |
| 67 | // Logging must never break the app; surface to console only. |
| 68 | console.error('[AppLogger] Failed to write log:', err) |
| 69 | } |
| 70 | } |
| 71 | |
| 72 | // ponytail: rename-based rotation, atomic, no rewrite, ~20MB total ceiling |
| 73 | private rotateIfNeeded(): void { |
no test coverage detected