UNPKG

@lucidcms/core

Version:

The core of the Lucid CMS. It's responsible for spinning up the API and serving the CMS.

1 lines 8.46 kB
{"version":3,"file":"index.mjs","names":[],"sources":["../../../src/libs/logger/index.ts"],"sourcesContent":["import { tryGetContext } from \"hono/context-storage\";\nimport type { LucidHonoGeneric } from \"../../types/hono.js\";\nimport createConsoleTransport from \"./console-transport/index.js\";\nimport type {\n\tLogEntry,\n\tLogEntryLevel,\n\tLogInput,\n\tLogLevel,\n\tLogTransport,\n\tLucidLogger,\n} from \"./types.js\";\n\nconst LOG_LEVELS: Record<LogLevel, number> = {\n\tsilent: -1,\n\terror: 0,\n\twarn: 1,\n\tinfo: 2,\n\tdebug: 3,\n} as const;\n\ntype LoggerState = {\n\tdestroyed: boolean;\n\tlevel: LogLevel;\n\ttransport: LogTransport;\n};\n\nlet isBuffering = false;\nlet logBuffer: LogEntry[] = [];\n\n/**\n * Reports provider failures without allowing logging to interrupt application work.\n */\nconst reportTransportError = (\n\toperation: \"write\" | \"flush\" | \"destroy\",\n\terror: unknown,\n) => {\n\ttry {\n\t\tconsole.error(`Lucid logger transport failed to ${operation}`, error);\n\t} catch {\n\t\t// Logging must never break application work.\n\t}\n};\n\n/**\n * Checks an entry against the configured process-level log threshold.\n */\nconst shouldLog = (\n\tcurrentLevel: LogLevel,\n\tmessageLevel: LogEntryLevel,\n): boolean => {\n\treturn LOG_LEVELS[messageLevel] <= LOG_LEVELS[currentLevel];\n};\n\n/**\n * Reads the request ID from Hono's concurrency-safe async context when available.\n */\nconst getActiveRequestId = () => {\n\treturn tryGetContext<LucidHonoGeneric>()?.get(\"requestId\");\n};\n\n/**\n * Writes an entry while containing synchronous and asynchronous transport failures.\n */\nconst safelyWrite = (state: LoggerState, entry: LogEntry) => {\n\tif (state.destroyed) return;\n\n\ttry {\n\t\tconst result = state.transport.write(entry) as unknown;\n\t\tif (\n\t\t\ttypeof result === \"object\" &&\n\t\t\tresult !== null &&\n\t\t\t\"then\" in result &&\n\t\t\ttypeof result.then === \"function\"\n\t\t) {\n\t\t\tvoid Promise.resolve(result).catch((error) => {\n\t\t\t\treportTransportError(\"write\", error);\n\t\t\t});\n\t\t}\n\t} catch (error) {\n\t\treportTransportError(\"write\", error);\n\t}\n};\n\n/**\n * Runs an optional transport lifecycle hook without leaking provider failures.\n */\nconst safelyRunTransportLifecycle = async (\n\tstate: LoggerState,\n\toperation: \"flush\" | \"destroy\",\n) => {\n\tif (state.destroyed) return;\n\n\tconst fn = state.transport[operation];\n\tif (!fn) return;\n\n\ttry {\n\t\tawait fn();\n\t} catch (error) {\n\t\treportTransportError(operation, error);\n\t}\n};\n\n/**\n * Binds logger methods to one state so replaced transports remain isolated.\n */\nconst createLogger = (state: LoggerState): LucidLogger => ({\n\tget level() {\n\t\treturn state.level;\n\t},\n\terror: (log: LogInput) => {\n\t\twriteLog(state, \"error\", log);\n\t},\n\twarn: (log: LogInput) => {\n\t\twriteLog(state, \"warn\", log);\n\t},\n\tinfo: (log: LogInput) => {\n\t\twriteLog(state, \"info\", log);\n\t},\n\tdebug: (log: LogInput) => {\n\t\twriteLog(state, \"debug\", log);\n\t},\n\tflush: () => safelyRunTransportLifecycle(state, \"flush\"),\n});\n\n/**\n * Creates the default process logger backed by the human-readable console transport.\n */\nconst createDefaultState = (): LoggerState => ({\n\tdestroyed: false,\n\tlevel: \"info\",\n\ttransport: createConsoleTransport(),\n});\n\nlet loggerState = createDefaultState();\nlet logger = createLogger(loggerState);\n\n/**\n * Creates the structured entry before buffering or delivering it.\n */\nconst writeLog = (state: LoggerState, level: LogEntryLevel, log: LogInput) => {\n\tif (!isBuffering && !shouldLog(state.level, level)) return;\n\n\tconst requestId = log.requestId ?? getActiveRequestId();\n\tconst entry: LogEntry = {\n\t\t...log,\n\t\t...(requestId ? { requestId } : {}),\n\t\tlevel,\n\t\ttimestamp: new Date().toISOString(),\n\t};\n\n\tif (isBuffering) {\n\t\tlogBuffer.push(entry);\n\t\treturn;\n\t}\n\n\tsafelyWrite(state, entry);\n};\n\n/**\n * Flushes pending work before releasing a transport and marks it closed.\n */\nconst closeLoggerTransport = async (state: LoggerState) => {\n\tif (state.destroyed) return;\n\n\tawait safelyRunTransportLifecycle(state, \"flush\");\n\tawait safelyRunTransportLifecycle(state, \"destroy\");\n\tstate.destroyed = true;\n};\n\n/**\n * Replaces the active process logger after cleanly closing its previous transport.\n */\nexport const initializeLogger = async (props?: {\n\ttransport?: LogTransport;\n\tlevel?: LogLevel;\n}) => {\n\tawait closeLoggerTransport(loggerState);\n\n\tconst level = props?.level ?? \"info\";\n\tloggerState = {\n\t\tdestroyed: false,\n\t\tlevel,\n\t\ttransport:\n\t\t\tprops?.transport ??\n\t\t\tcreateConsoleTransport({\n\t\t\t\tverbose: level === \"debug\",\n\t\t\t}),\n\t};\n\tlogger = createLogger(loggerState);\n\n\treturn logger;\n};\n\n/**\n * Buffers internal logs so they do not interrupt the CLI progress UI.\n */\nexport const startLoggerBuffering = () => {\n\tisBuffering = true;\n};\n\n/**\n * Drains CLI-buffered entries through the current transport and flushes it.\n */\nexport const stopLoggerBuffering = async () => {\n\tisBuffering = false;\n\tconst bufferedEntries = logBuffer;\n\tlogBuffer = [];\n\n\tif (loggerState.destroyed) {\n\t\tloggerState = createDefaultState();\n\t\tlogger = createLogger(loggerState);\n\t}\n\n\tfor (const entry of bufferedEntries) {\n\t\tif (shouldLog(loggerState.level, entry.level)) {\n\t\t\tsafelyWrite(loggerState, entry);\n\t\t}\n\t}\n\n\tawait safelyRunTransportLifecycle(loggerState, \"flush\");\n};\n\n/**\n * Flushes and closes the process transport while preserving CLI reload buffers.\n */\nexport const destroyLogger = async () => {\n\tawait closeLoggerTransport(loggerState);\n};\n\n/**\n * Returns the active process logger used by the stable public proxy.\n */\nexport const getLogger = (): LucidLogger => logger;\n\nconst loggerProxy: LucidLogger = {\n\tget level() {\n\t\treturn getLogger().level;\n\t},\n\terror: (log: LogInput) => getLogger().error(log),\n\twarn: (log: LogInput) => getLogger().warn(log),\n\tinfo: (log: LogInput) => getLogger().info(log),\n\tdebug: (log: LogInput) => getLogger().debug(log),\n\tflush: () => getLogger().flush(),\n};\n\nexport default loggerProxy;\n"],"mappings":"kGAYA,MAAM,EAAuC,CAC5C,OAAQ,GACR,MAAO,EACP,KAAM,EACN,KAAM,EACN,MAAO,CACR,EAQA,IAAI,EAAc,GACd,EAAwB,CAAC,EAK7B,MAAM,GACL,EACA,IACI,CACJ,GAAI,CACH,QAAQ,MAAM,oCAAoC,IAAa,CAAK,CACrE,MAAQ,CAER,CACD,EAKM,GACL,EACA,IAEO,EAAW,IAAiB,EAAW,GAMzC,MACE,EAAgC,CAAC,EAAE,IAAI,WAAW,EAMpD,GAAe,EAAoB,IAAoB,CACxD,MAAM,UAEV,GAAI,CACH,IAAM,EAAS,EAAM,UAAU,MAAM,CAAK,EAEzC,OAAO,GAAW,UAClB,GACA,SAAU,GACV,OAAO,EAAO,MAAS,YAEvB,QAAa,QAAQ,CAAM,CAAC,CAAC,MAAO,GAAU,CAC7C,EAAqB,QAAS,CAAK,CACpC,CAAC,CAEH,OAAS,EAAO,CACf,EAAqB,QAAS,CAAK,CACpC,CACD,EAKM,EAA8B,MACnC,EACA,IACI,CACJ,GAAI,EAAM,UAAW,OAErB,IAAM,EAAK,EAAM,UAAU,GACtB,KAEL,GAAI,CACH,MAAM,EAAG,CACV,OAAS,EAAO,CACf,EAAqB,EAAW,CAAK,CACtC,CACD,EAKM,EAAgB,IAAqC,CAC1D,IAAI,OAAQ,CACX,OAAO,EAAM,KACd,EACA,MAAQ,GAAkB,CACzB,EAAS,EAAO,QAAS,CAAG,CAC7B,EACA,KAAO,GAAkB,CACxB,EAAS,EAAO,OAAQ,CAAG,CAC5B,EACA,KAAO,GAAkB,CACxB,EAAS,EAAO,OAAQ,CAAG,CAC5B,EACA,MAAQ,GAAkB,CACzB,EAAS,EAAO,QAAS,CAAG,CAC7B,EACA,UAAa,EAA4B,EAAO,OAAO,CACxD,GAKM,OAAyC,CAC9C,UAAW,GACX,MAAO,OACP,UAAW,EAAuB,CACnC,GAEA,IAAI,EAAc,EAAmB,EACjC,EAAS,EAAa,CAAW,EAKrC,MAAM,GAAY,EAAoB,EAAsB,IAAkB,CAC7E,GAAI,CAAC,GAAe,CAAC,EAAU,EAAM,MAAO,CAAK,EAAG,OAEpD,IAAM,EAAY,EAAI,WAAa,EAAmB,EAChD,EAAkB,CACvB,GAAG,EACH,GAAI,EAAY,CAAE,WAAU,EAAI,CAAC,EACjC,QACA,UAAW,IAAI,KAAK,CAAA,CAAE,YAAY,CACnC,EAEA,GAAI,EAAa,CAChB,EAAU,KAAK,CAAK,EACpB,MACD,CAEA,EAAY,EAAO,CAAK,CACzB,EAKM,EAAuB,KAAO,IAAuB,CACtD,AAIJ,EAAM,aAFN,MAAM,EAA4B,EAAO,OAAO,EAChD,MAAM,EAA4B,EAAO,SAAS,EAChC,GACnB,EAKa,EAAmB,KAAO,IAGjC,CACL,MAAM,EAAqB,CAAW,EAEtC,IAAM,EAAQ,GAAO,OAAS,OAY9B,MAXA,GAAc,CACb,UAAW,GACX,QACA,UACC,GAAO,WACP,EAAuB,CACtB,QAAS,IAAU,OACpB,CAAC,CACH,EACA,EAAS,EAAa,CAAW,EAE1B,CACR,EAKa,MAA6B,CACzC,EAAc,EACf,EAKa,EAAsB,SAAY,CAC9C,EAAc,GACd,IAAM,EAAkB,EACxB,EAAY,CAAC,EAET,EAAY,YACf,EAAc,EAAmB,EACjC,EAAS,EAAa,CAAW,GAGlC,IAAK,IAAM,KAAS,EACf,EAAU,EAAY,MAAO,EAAM,KAAK,GAC3C,EAAY,EAAa,CAAK,EAIhC,MAAM,EAA4B,EAAa,OAAO,CACvD,EAKa,EAAgB,SAAY,CACxC,MAAM,EAAqB,CAAW,CACvC,EAKa,MAA+B,EAEtC,EAA2B,CAChC,IAAI,OAAQ,CACX,OAAO,EAAU,CAAC,CAAC,KACpB,EACA,MAAQ,GAAkB,EAAU,CAAC,CAAC,MAAM,CAAG,EAC/C,KAAO,GAAkB,EAAU,CAAC,CAAC,KAAK,CAAG,EAC7C,KAAO,GAAkB,EAAU,CAAC,CAAC,KAAK,CAAG,EAC7C,MAAQ,GAAkB,EAAU,CAAC,CAAC,MAAM,CAAG,EAC/C,UAAa,EAAU,CAAC,CAAC,MAAM,CAChC"}