const logger = require("../utils/logger"); /** * Convert an Error object to a plain object for serialization. * @param err * @returns {{[p: string]: unknown, name: *, message: *, stack: *}|null} */ const toPlainError = (err) => { if (!err) return null; return { name: err.name, message: err.message, stack: err.stack, ...Object.fromEntries(Object.entries(err).filter(([k]) => !["stack"].includes(k))) }; }; /** * Safely serialize meta information for logging. * @param meta * @returns {{note: string}|any|{[p: string]: *, name: *, message: *, stack: *}|null} */ const safeMeta = (meta) => { try { if (meta instanceof Error) return toPlainError(meta); if (meta && typeof meta === "object") { // JSON-safe clone w/ BigInt -> string return JSON.parse(JSON.stringify(meta, (_k, v) => (typeof v === "bigint" ? v.toString() : v))); } return meta ?? null; } catch { return { note: "meta not serializable" }; } }; /** * Create and emit a Reynolds log event. * @param socket * @param level * @param message * @param meta * @returns {{timestamp: number, level: string, message: string|string, meta: {note: string}|*|{[p: string]: *, name: *, message: *, stack: *}|null}} * @constructor */ const CreateRRLogEvent = (socket, level = "INFO", message = "", meta = null) => { const ts = Date.now(); const lvl = String(level || "INFO").toUpperCase(); const normLevel = lvl.toLowerCase(); const msg = typeof message === "string" ? message : (message?.toString?.() ?? JSON.stringify(message)); const payload = { timestamp: ts, level: lvl, message: msg, meta: safeMeta(meta) }; // Central logger (Winston + CloudWatch + S3) try { // user = "RR", record = null, meta = payload.meta logger.log(`[RR] ${msg}`, normLevel, "RR", null, payload.meta); } catch { // Fallback console console.log(`[RR] ${new Date(ts).toISOString()} | ${lvl} | ${msg}`, payload.meta); } // Socket try { socket?.emit?.("rr-log-event", payload); } catch { // ignore socket failures } return payload; }; module.exports = CreateRRLogEvent;