@paroicms/internal-server-lib
Version:
Common utilitaries for the paroicms server.
155 lines • 5.55 kB
JavaScript
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