const util = require("util"); const appLogger = require("../utils/logger"); function RRLogger(socket, baseCtx = {}) { const levels = new Set(["error", "warn", "info", "http", "verbose", "debug", "silly"]); const safeString = (v) => { if (v instanceof Error) return v.message; if (typeof v === "string") return v; try { return JSON.stringify(v); } catch { return util.inspect(v, { depth: 2, maxArrayLength: 50 }); } }; return function log(levelOrMsg, msgOrCtx, ctx) { let level = "info"; let message = undefined; let meta = {}; if (typeof levelOrMsg === "string" && levels.has(levelOrMsg)) { level = levelOrMsg; message = msgOrCtx; meta = ctx || {}; } else { message = levelOrMsg; meta = msgOrCtx || {}; } // Prepare console line + metadata const emitError = message instanceof Error; if (emitError) { meta.err = { name: message.name, message: message.message, stack: message.stack }; message = message.message; if (level === "info") level = "error"; } const messageString = safeString(message); const line = `[RR] ${new Date().toISOString()} [${String(level).toUpperCase()}] ${messageString}`; const loggerFn = appLogger?.logger?.[level] || appLogger?.logger?.info || ((...args) => console.log(...args)); loggerFn(line, { ...baseCtx, ...meta }); // Always emit a STRING for `message` to sockets to avoid React crashes // If the original message was an object, include it in `details` const details = message && typeof message !== "string" && !emitError ? message : undefined; try { socket?.emit?.("rr-log-event", { level, message: messageString, // <-- normalized string for UI ctx: { ...baseCtx, ...meta }, ...(details ? { details } : {}), ts: Date.now() }); } catch { /* ignore socket emission errors */ } }; } module.exports = RRLogger;