'use strict'; const fs = require('fs'); const path = require('path'); const MAX_LOG_LINES = 500; const LOG_FILE = path.join(__dirname, 'runtime.log'); class Logger { constructor() { this._logs = []; this._listeners = new Set(); this._origLog = console.log; this._origError = console.error; this._origWarn = console.warn; this._capturing = false; } start() { if (this._capturing) return; this._capturing = true; const self = this; console.log = function() { const msg = Array.from(arguments).map(a => typeof a === 'object' ? JSON.stringify(a) : String(a)).join(' '); self._append('log', msg); self._origLog.apply(console, arguments); }; console.error = function() { const msg = Array.from(arguments).map(a => typeof a === 'object' ? JSON.stringify(a) : String(a)).join(' '); self._append('error', msg); self._origError.apply(console, arguments); }; console.warn = function() { const msg = Array.from(arguments).map(a => typeof a === 'object' ? JSON.stringify(a) : String(a)).join(' '); self._append('warn', msg); self._origWarn.apply(console, arguments); }; } _append(level, message) { const entry = { time: new Date().toISOString(), level, message: message.slice(0, 2000), }; this._logs.push(entry); if (this._logs.length > MAX_LOG_LINES) this._logs.shift(); // Write to file try { const line = `[${entry.time}] [${level.toUpperCase()}] ${entry.message}\n`; fs.appendFileSync(LOG_FILE, line); } catch (_) {} // Notify SSE listeners for (const cb of this._listeners) { try { cb(entry); } catch (_) {} } } getLogs(count = 100) { return this._logs.slice(-count); } onLog(cb) { this._listeners.add(cb); return () => this._listeners.delete(cb); } stop() { console.log = this._origLog; console.error = this._origError; console.warn = this._origWarn; this._capturing = false; this._listeners.clear(); } } module.exports = { Logger };