/** Appends timestamped lines to the instance's log, keeping the last few files. */ import { appendFileSync, existsSync, mkdirSync, renameSync, rmSync, statSync } from 'node:fs'; import { dirname } from 'node:path'; const MAX_BYTES = 10 * 1024 * 1024; const KEEP = 5; /** Rotation keeps a multi-hour broadcast's log bounded: worker.log, worker.1.log, … */ function rotate(path: string) { if (!existsSync(path) || statSync(path).size < MAX_BYTES) return; const numbered = (n: number) => path.replace(/\.log$/, `.${n}.log`); rmSync(numbered(KEEP), { force: true }); for (let n = KEEP - 1; n >= 1; n--) if (existsSync(numbered(n))) renameSync(numbered(n), numbered(n + 1)); renameSync(path, numbered(1)); } export function fileLogger(path: string, alsoConsole = false) { mkdirSync(dirname(path), { recursive: true }); let lines = 0; return (...parts: unknown[]) => { const text = parts .map((part) => part instanceof Error ? part.message : typeof part === 'string' ? part : JSON.stringify(part) ) .join(' '); const line = `${new Date().toISOString()} ${text}\n`; if (alsoConsole) process.stdout.write(line); try { if (++lines % 200 === 0) rotate(path); appendFileSync(path, line); } catch { /* a full or locked disk must not stop the broadcast */ } }; }