UNPKG

@catbee/utils

Version:

A modular, production-grade utility toolkit for Node.js and TypeScript, designed for robust, scalable applications (including Express-based services). All utilities are tree-shakable and can be imported independently.

101 lines 3.74 kB
import pino, { stdTimeFunctions } from "pino"; import { Config } from "../config"; import { ContextStore, StoreKeys } from "./context-store.utils"; /** * Symbol used to store the root logger in the Node.js global object. */ const GLOBAL_LOGGER_KEY = Symbol.for("logger"); /** * Use an object compatible with either modern or legacy global scopes. */ export const _globalThis = typeof globalThis === "object" ? globalThis : global; const _global = _globalThis; /** * Initializes the global root logger according to app configuration. * * - Sets log name, level, timestamp, and redaction for sensitive fields. * - Uses singleton in global symbol registry. */ function setupLogger() { const logParams = { name: Config.Logger.name, level: Config.Logger.level, redact: { paths: ["req.authorization", "url"], censor(value, path) { if (path[0] === "url") { return value.replace(/access_token=[a-zA-Z0-9_-]*/, "access_token=***"); } else if (path[1] === "authorization") { return value.replace(/\s+(\S+)$/, " ***"); } return "***"; }, }, }; if (Config.Logger.isoTimestamp) { logParams.timestamp = stdTimeFunctions.isoTime; } _global[GLOBAL_LOGGER_KEY] = pino(logParams); _global[GLOBAL_LOGGER_KEY].debug("Logger initialized"); } /** * Retrieves the current logger instance: * - Returns a request-scoped logger from AsyncLocalStorage if available * - Falls back to the global (singleton) logger * - Initializes the global logger if not created yet * * @returns {Logger} The logger instance (request-bound or global root logger) */ export function getLogger() { const logger = ContextStore.get(StoreKeys.LOGGER); if (logger) return logger; if (!_global[GLOBAL_LOGGER_KEY]) { setupLogger(); } return _global[GLOBAL_LOGGER_KEY]; } /** * Creates a child logger with additional context. * * @param {Record<string, any>} bindings - Properties to attach to all log records * @param {Logger} [parentLogger] - Parent logger (defaults to current context logger or global) * @returns {Logger} Child logger with merged context */ export function createChildLogger(bindings, parentLogger) { const logger = parentLogger || getLogger(); return logger.child(bindings); } /** * Creates a request-scoped logger with request ID and stores it in context * * @param {string} requestId - Unique request identifier * @param {object} [additionalContext] - Additional context to include in logs * @returns {Logger} Request-scoped logger instance */ export function createRequestLogger(requestId, additionalContext = {}) { const logger = createChildLogger(Object.assign({ requestId }, additionalContext)); try { ContextStore.set(StoreKeys.LOGGER, logger); } catch (_a) { // Context not initialized, can't store logger logger.debug("Failed to store logger in context - AsyncLocalStorage not initialized"); } return logger; } /** * Utility to safely log errors with proper stack trace extraction * * @param {Error|unknown} error - Error object to log * @param {string} [message] - Optional message to include * @param {Record<string, any>} [context] - Additional context properties */ export function logError(error, message, context) { const logger = getLogger(); const errObj = error instanceof Error ? error : new Error(String(error)); const logContext = Object.assign(Object.assign({}, context), { err: errObj }); logger.error(logContext, message || errObj.message); } //# sourceMappingURL=logger.utils.js.map