UNPKG

@paroicms/internal-server-lib

Version:

Common utilitaries for the paroicms server.

155 lines 5.55 kB
import { promiseToHandle } from "@paroi/async-lib"; import { EventEmitter } from "node:events"; import { pino } from "pino"; const levels = ["error", "warn", "info", "stats", "debug", "trace"]; const levelIndexes = new Map(levels.map((l, index) => [l, index])); export function createPlatformLog({ file, level, transport, reportMessage, }) { const destination = file ? pino.destination({ dest: file, sync: false, }) : undefined; const emitter = new EventEmitter(); const readyState = destination ? createReadyState(destination) : { ready: true, untilReady: Promise.resolve() }; let waitingMessages; function makeLogFn(level, logger) { return (...messages) => { const textMessage = messagesToString(messages); if (readyState.ready) logger[level](textMessage); else { if (!waitingMessages) { waitingMessages = []; console.warn("There is something to log before the logger is ready"); void readyState.untilReady.then(() => { if (waitingMessages) { if (file) { waitingMessages.forEach((wMessages) => logger[level](messagesToString(wMessages))); } waitingMessages = undefined; } }); } if (level in console) console[level](...messages); else console.log(...messages); messages.unshift("[DELAYED]"); waitingMessages.push(messages); } try { emitAppLoggerEvent({ level, textMessage, originalMessages: messages, }, emitter); } catch (error) { if (readyState.ready) logger.error(messagesToString(["Error in app log listener:", error])); else console.error("Error in app log listener:", error); } }; } const mainLogger = pino({ level, customLevels: { stats: 25, }, transport, }, destination); if (reportMessage) { console.log(`Application log with level '${level}' is in: ${file ?? "stdout"}.`); } function createChildLog(properties) { const child = mainLogger.child(properties); return { error: makeLogFn("error", child), warn: makeLogFn("warn", child), info: makeLogFn("info", child), stats: makeLogFn("stats", child), debug: makeLogFn("debug", child), trace: makeLogFn("trace", child), }; } return { error: makeLogFn("error", mainLogger), warn: makeLogFn("warn", mainLogger), info: makeLogFn("info", mainLogger), stats: makeLogFn("stats", mainLogger), debug: makeLogFn("debug", mainLogger), trace: makeLogFn("trace", mainLogger), on: (level, listener) => emitter.on(`on-${level}`, listener), untilReady: readyState.untilReady, flushSync() { if (!destination) return; if (readyState.ready) destination.flushSync(); else console.warn("Flush is called before the logger is ready."); }, createChildLog, }; } function messagesToString(messages) { return messages.map((msg) => messageToString(msg)).join(" "); } function messageToString(msg, parents = []) { if (parents.includes(msg)) return "[recursive-ref]"; if (parents.length > 5) return "[too-deep]"; switch (typeof msg) { case "string": return msg; case "number": case "bigint": case "boolean": case "undefined": case "symbol": return String(msg); case "function": return `[function ${msg.name}]`; case "object": if (msg === null) return "null"; if (Array.isArray(msg)) return `[${msg.map((child) => messageToString(child, [...parents, msg])).join(",")}]`; if (msg instanceof Error) return msg.stack ?? `Error: ${msg.message ?? "-no-message-"}`; return `{${Object.entries(msg) .map(([key, child]) => `${key}: ${messageToString(child, [...parents, msg])}`) .join(",")}}`; default: return `Unexpected message type: ${typeof msg}`; } } function emitAppLoggerEvent(ev, emitter) { let index = levelIndexes.get(ev.level); if (index === undefined) throw new Error(`Invalid level '${ev.level}'`); do { emitter.emit(`on-${levels[index]}`, ev); } while (++index < levels.length); } function createReadyState(destination) { const { promise: untilReady, resolve, reject } = promiseToHandle(); const readyState = { ready: false, untilReady, }; destination.on("error", reject); destination.on("ready", () => { readyState.ready = true; destination.off("error", reject); destination.on("error", (error) => console.error("[Error in Pino]", error)); resolve(); }); return readyState; } //# sourceMappingURL=pino-logger.js.map