const fs = require('fs') const path = require('path') const logsDir = path.join(process.cwd(), 'logs') if (!fs.existsSync(logsDir)) fs.mkdirSync(logsDir, { recursive: true }) const logFile = path.join(logsDir, 'app.log') if (!fs.existsSync(logFile)) fs.writeFileSync(logFile, '') const channelFileCache = new Map() let pendingWrites = new Map() let flushScheduled = false const ts = () => new Date().toISOString() const ensureChannel = channel => { const name = String(channel || '').trim() if (!name) return null if (channelFileCache.has(name)) return channelFileCache.get(name) const file = path.join(logsDir, `${name}.log`) if (!fs.existsSync(file)) fs.writeFileSync(file, '') channelFileCache.set(name, file) return file } const flushPendingWrites = () => { flushScheduled = false if (pendingWrites.size === 0) return const batch = pendingWrites pendingWrites = new Map() batch.forEach((content, file) => { fs.appendFile(file, content, () => {}) }) if (pendingWrites.size > 0) scheduleFlush() } const scheduleFlush = () => { if (flushScheduled) return flushScheduled = true setImmediate(flushPendingWrites) } const enqueueWrite = (file, line) => { const prev = pendingWrites.get(file) || '' pendingWrites.set(file, prev + line) scheduleFlush() } const log = (msg, channel) => { const line = `${ts()} ${String(msg)}\n` enqueueWrite(logFile, line) const chFile = ensureChannel(channel) if (chFile) enqueueWrite(chFile, line) } const logJSON = (label, obj, channel) => { let s = '' try { s = JSON.stringify(obj) } catch { s = String(obj) } log(`${label} ${s}`, channel) } const getLogs = (limit = 2000, channel) => { let file = logFile const chFile = ensureChannel(channel) if (chFile) file = chFile let content = '' try { const stat = fs.statSync(file) const maxReadBytes = Math.min(stat.size, 512 * 1024) const start = Math.max(0, stat.size - maxReadBytes) const fd = fs.openSync(file, 'r') const buf = Buffer.alloc(maxReadBytes) const bytesRead = fs.readSync(fd, buf, 0, maxReadBytes, start) fs.closeSync(fd) content = buf.subarray(0, bytesRead).toString('utf-8') } catch {} const lines = content.split(/\r?\n/).filter(Boolean) const tail = lines.slice(Math.max(0, lines.length - limit)) return tail } const clearLogs = channel => { const chFile = ensureChannel(channel) if (chFile) fs.truncateSync(chFile, 0) else fs.truncateSync(logFile, 0) } module.exports = { log, logJSON, getLogs, clearLogs }