Files
bodyshop/server/rr/rr-logger-event.js

76 lines
2.0 KiB
JavaScript

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 msg = typeof message === "string" ? message : (message?.toString?.() ?? JSON.stringify(message));
const payload = {
timestamp: ts,
level: lvl,
message: msg,
meta: safeMeta(meta)
};
// Console
try {
const fn = logger?.logger?.[lvl.toLowerCase()] ?? logger?.logger?.info ?? console.log;
fn(`[RR] ${new Date(ts).toISOString()} | ${lvl} | ${msg}`, payload.meta);
} catch {
// ignore console failures
}
// Socket
try {
socket?.emit?.("rr-log-event", payload);
} catch {
// ignore socket failures
}
return payload;
};
module.exports = CreateRRLogEvent;