@logtape/logtape 1.2.0-dev.344 → 1.2.0-dev.354
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/deno.json +1 -1
- package/dist/config.cjs +3 -2
- package/dist/config.d.cts.map +1 -1
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +3 -2
- package/dist/config.js.map +1 -1
- package/dist/formatter.cjs +3 -3
- package/dist/formatter.d.cts +28 -3
- package/dist/formatter.d.cts.map +1 -1
- package/dist/formatter.d.ts +28 -3
- package/dist/formatter.d.ts.map +1 -1
- package/dist/formatter.js +3 -3
- package/dist/formatter.js.map +1 -1
- package/package.json +1 -1
- package/src/config.ts +11 -4
- package/src/formatter.test.ts +37 -0
- package/src/formatter.ts +34 -6
package/deno.json
CHANGED
package/dist/config.cjs
CHANGED
|
@@ -156,9 +156,10 @@ function configureInternal(config, allowAsync) {
|
|
|
156
156
|
else throw new ConfigError("Async disposables cannot be used with configureSync().");
|
|
157
157
|
if (Symbol.dispose in filter) disposables.add(filter);
|
|
158
158
|
}
|
|
159
|
-
if ("process" in globalThis && !("Deno" in globalThis)) {
|
|
159
|
+
if (typeof globalThis.EdgeRuntime !== "string" && "process" in globalThis && !("Deno" in globalThis)) {
|
|
160
160
|
const proc = globalThis.process;
|
|
161
|
-
|
|
161
|
+
const onMethod = proc?.["on"];
|
|
162
|
+
if (typeof onMethod === "function") onMethod.call(proc, "exit", allowAsync ? dispose : disposeSync);
|
|
162
163
|
} else addEventListener("unload", allowAsync ? dispose : disposeSync);
|
|
163
164
|
const meta = require_logger.LoggerImpl.getLogger(["logtape", "meta"]);
|
|
164
165
|
if (!metaConfigured) meta.sinks.push(require_sink.getConsoleSink());
|
package/dist/config.d.cts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"config.d.cts","names":[],"sources":["../src/config.ts"],"sourcesContent":[],"mappings":";;;;;;;;;AASA;AAAuB,UAAN,MAAM,CAAA,gBAAA,MAAA,EAAA,kBAAA,MAAA,CAAA,CAAA;EAAA;;;;EAUK,KAAE,EALrB,MAKqB,CALd,OAKc,EALL,IAKK,CAAA;EAAU;;;;EAKjB,OAMqB,CAAA,EAXhC,MAWgC,CAXzB,SAWyB,EAXd,UAWc,CAAA;EAAM;AAAP;AAW3C;EAA6B,OAAA,EAjBlB,YAiBkB,CAjBL,OAiBK,EAjBI,SAiBJ,CAAA,EAAA;EAAA;;;AAoCL;EA6EF,mBAAS,CAAA,EA5HP,mBA4HO,CA5Ha,MA4Hb,CAAA,MAAA,EAAA,OAAA,CAAA,CAAA;EAAA;;;EAGI,KAAzB,CAAA,EAAA,OAAA;;AAAoC;AAgD9C;;AACiB,UArKA,YAqKA,CAAA,gBAAA,MAAA,EAAA,kBAAA,MAAA,CAAA,CAAA;EAAO;;AAAR;
|
|
1
|
+
{"version":3,"file":"config.d.cts","names":[],"sources":["../src/config.ts"],"sourcesContent":[],"mappings":";;;;;;;;;AASA;AAAuB,UAAN,MAAM,CAAA,gBAAA,MAAA,EAAA,kBAAA,MAAA,CAAA,CAAA;EAAA;;;;EAUK,KAAE,EALrB,MAKqB,CALd,OAKc,EALL,IAKK,CAAA;EAAU;;;;EAKjB,OAMqB,CAAA,EAXhC,MAWgC,CAXzB,SAWyB,EAXd,UAWc,CAAA;EAAM;AAAP;AAW3C;EAA6B,OAAA,EAjBlB,YAiBkB,CAjBL,OAiBK,EAjBI,SAiBJ,CAAA,EAAA;EAAA;;;AAoCL;EA6EF,mBAAS,CAAA,EA5HP,mBA4HO,CA5Ha,MA4Hb,CAAA,MAAA,EAAA,OAAA,CAAA,CAAA;EAAA;;;EAGI,KAAzB,CAAA,EAAA,OAAA;;AAAoC;AAgD9C;;AACiB,UArKA,YAqKA,CAAA,gBAAA,MAAA,EAAA,kBAAA,MAAA,CAAA,CAAA;EAAO;;AAAR;AAyIhB;EAOsB,QAAK,EAAA,MAAA,GAAI,MAAA,EAAO;EAUtB;AAgBhB;AAeA;EAQa,KAAA,CAAA,EAzVH,OAyVe,EAAA;;;;;;;;;;;;;;YAzUb;;;;;;gBAOI;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;iBA6EM,oEAGZ,OAAO,SAAS,aAAa;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;iBAgDvB,wEACN,OAAO,SAAS;;;;;iBAyIV,SAAA,CAAA,GAAa;;;;iBAOP,KAAA,CAAA,GAAS;;;;;;iBAUf,SAAA,CAAA;;;;iBAgBM,OAAA,CAAA,GAAW;;;;;;iBAejB,WAAA,CAAA;;;;cAQH,WAAA,SAAoB,KAAK"}
|
package/dist/config.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"config.d.ts","names":[],"sources":["../src/config.ts"],"sourcesContent":[],"mappings":";;;;;;;;;AASA;AAAuB,UAAN,MAAM,CAAA,gBAAA,MAAA,EAAA,kBAAA,MAAA,CAAA,CAAA;EAAA;;;;EAUK,KAAE,EALrB,MAKqB,CALd,OAKc,EALL,IAKK,CAAA;EAAU;;;;EAKjB,OAMqB,CAAA,EAXhC,MAWgC,CAXzB,SAWyB,EAXd,UAWc,CAAA;EAAM;AAAP;AAW3C;EAA6B,OAAA,EAjBlB,YAiBkB,CAjBL,OAiBK,EAjBI,SAiBJ,CAAA,EAAA;EAAA;;;AAoCL;EA6EF,mBAAS,CAAA,EA5HP,mBA4HO,CA5Ha,MA4Hb,CAAA,MAAA,EAAA,OAAA,CAAA,CAAA;EAAA;;;EAGI,KAAzB,CAAA,EAAA,OAAA;;AAAoC;AAgD9C;;AACiB,UArKA,YAqKA,CAAA,gBAAA,MAAA,EAAA,kBAAA,MAAA,CAAA,CAAA;EAAO;;AAAR;
|
|
1
|
+
{"version":3,"file":"config.d.ts","names":[],"sources":["../src/config.ts"],"sourcesContent":[],"mappings":";;;;;;;;;AASA;AAAuB,UAAN,MAAM,CAAA,gBAAA,MAAA,EAAA,kBAAA,MAAA,CAAA,CAAA;EAAA;;;;EAUK,KAAE,EALrB,MAKqB,CALd,OAKc,EALL,IAKK,CAAA;EAAU;;;;EAKjB,OAMqB,CAAA,EAXhC,MAWgC,CAXzB,SAWyB,EAXd,UAWc,CAAA;EAAM;AAAP;AAW3C;EAA6B,OAAA,EAjBlB,YAiBkB,CAjBL,OAiBK,EAjBI,SAiBJ,CAAA,EAAA;EAAA;;;AAoCL;EA6EF,mBAAS,CAAA,EA5HP,mBA4HO,CA5Ha,MA4Hb,CAAA,MAAA,EAAA,OAAA,CAAA,CAAA;EAAA;;;EAGI,KAAzB,CAAA,EAAA,OAAA;;AAAoC;AAgD9C;;AACiB,UArKA,YAqKA,CAAA,gBAAA,MAAA,EAAA,kBAAA,MAAA,CAAA,CAAA;EAAO;;AAAR;AAyIhB;EAOsB,QAAK,EAAA,MAAA,GAAI,MAAA,EAAO;EAUtB;AAgBhB;AAeA;EAQa,KAAA,CAAA,EAzVH,OAyVe,EAAA;;;;;;;;;;;;;;YAzUb;;;;;;gBAOI;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;iBA6EM,oEAGZ,OAAO,SAAS,aAAa;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;iBAgDvB,wEACN,OAAO,SAAS;;;;;iBAyIV,SAAA,CAAA,GAAa;;;;iBAOP,KAAA,CAAA,GAAS;;;;;;iBAUf,SAAA,CAAA;;;;iBAgBM,OAAA,CAAA,GAAW;;;;;;iBAejB,WAAA,CAAA;;;;cAQH,WAAA,SAAoB,KAAK"}
|
package/dist/config.js
CHANGED
|
@@ -156,9 +156,10 @@ function configureInternal(config, allowAsync) {
|
|
|
156
156
|
else throw new ConfigError("Async disposables cannot be used with configureSync().");
|
|
157
157
|
if (Symbol.dispose in filter) disposables.add(filter);
|
|
158
158
|
}
|
|
159
|
-
if ("process" in globalThis && !("Deno" in globalThis)) {
|
|
159
|
+
if (typeof globalThis.EdgeRuntime !== "string" && "process" in globalThis && !("Deno" in globalThis)) {
|
|
160
160
|
const proc = globalThis.process;
|
|
161
|
-
|
|
161
|
+
const onMethod = proc?.["on"];
|
|
162
|
+
if (typeof onMethod === "function") onMethod.call(proc, "exit", allowAsync ? dispose : disposeSync);
|
|
162
163
|
} else addEventListener("unload", allowAsync ? dispose : disposeSync);
|
|
163
164
|
const meta = LoggerImpl.getLogger(["logtape", "meta"]);
|
|
164
165
|
if (!metaConfigured) meta.sinks.push(getConsoleSink());
|
package/dist/config.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"config.js","names":["currentConfig: Config<string, string> | null","strongRefs: Set<LoggerImpl>","disposables: Set<Disposable>","asyncDisposables: Set<AsyncDisposable>","cfg: LoggerConfig<TSinkId, TFilterId>","config: Config<TSinkId, TFilterId>","allowAsync: boolean","promises: PromiseLike<void>[]","message: string"],"sources":["../src/config.ts"],"sourcesContent":["import type { ContextLocalStorage } from \"./context.ts\";\nimport { type FilterLike, toFilter } from \"./filter.ts\";\nimport type { LogLevel } from \"./level.ts\";\nimport { LoggerImpl } from \"./logger.ts\";\nimport { getConsoleSink, type Sink } from \"./sink.ts\";\n\n/**\n * A configuration for the loggers.\n */\nexport interface Config<TSinkId extends string, TFilterId extends string> {\n /**\n * The sinks to use. The keys are the sink identifiers, and the values are\n * {@link Sink}s.\n */\n sinks: Record<TSinkId, Sink>;\n /**\n * The filters to use. The keys are the filter identifiers, and the values\n * are either {@link Filter}s or {@link LogLevel}s.\n */\n filters?: Record<TFilterId, FilterLike>;\n\n /**\n * The loggers to configure.\n */\n loggers: LoggerConfig<TSinkId, TFilterId>[];\n\n /**\n * The context-local storage to use for implicit contexts.\n * @since 0.7.0\n */\n contextLocalStorage?: ContextLocalStorage<Record<string, unknown>>;\n\n /**\n * Whether to reset the configuration before applying this one.\n */\n reset?: boolean;\n}\n\n/**\n * A logger configuration.\n */\nexport interface LoggerConfig<\n TSinkId extends string,\n TFilterId extends string,\n> {\n /**\n * The category of the logger. If a string, it is equivalent to an array\n * with one element.\n */\n category: string | string[];\n\n /**\n * The sink identifiers to use.\n */\n sinks?: TSinkId[];\n\n /**\n * Whether to inherit the parent's sinks. If `inherit`, the parent's sinks\n * are used along with the specified sinks. If `override`, the parent's\n * sinks are not used, and only the specified sinks are used.\n *\n * The default is `inherit`.\n * @default `\"inherit\"\n * @since 0.6.0\n */\n parentSinks?: \"inherit\" | \"override\";\n\n /**\n * The filter identifiers to use.\n */\n filters?: TFilterId[];\n\n /**\n * The lowest log level to accept. If `null`, the logger will reject all\n * records.\n * @since 0.8.0\n */\n lowestLevel?: LogLevel | null;\n}\n\n/**\n * The current configuration, if any. Otherwise, `null`.\n */\nlet currentConfig: Config<string, string> | null = null;\n\n/**\n * Strong references to the loggers.\n * This is to prevent the loggers from being garbage collected so that their\n * sinks and filters are not removed.\n */\nconst strongRefs: Set<LoggerImpl> = new Set();\n\n/**\n * Disposables to dispose when resetting the configuration.\n */\nconst disposables: Set<Disposable> = new Set();\n\n/**\n * Async disposables to dispose when resetting the configuration.\n */\nconst asyncDisposables: Set<AsyncDisposable> = new Set();\n\n/**\n * Check if a config is for the meta logger.\n */\nfunction isLoggerConfigMeta<TSinkId extends string, TFilterId extends string>(\n cfg: LoggerConfig<TSinkId, TFilterId>,\n): boolean {\n return cfg.category.length === 0 ||\n (cfg.category.length === 1 && cfg.category[0] === \"logtape\") ||\n (cfg.category.length === 2 &&\n cfg.category[0] === \"logtape\" &&\n cfg.category[1] === \"meta\");\n}\n\n/**\n * Configure the loggers with the specified configuration.\n *\n * Note that if the given sinks or filters are disposable, they will be\n * disposed when the configuration is reset, or when the process exits.\n *\n * @example\n * ```typescript\n * await configure({\n * sinks: {\n * console: getConsoleSink(),\n * },\n * filters: {\n * slow: (log) =>\n * \"duration\" in log.properties &&\n * log.properties.duration as number > 1000,\n * },\n * loggers: [\n * {\n * category: \"my-app\",\n * sinks: [\"console\"],\n * lowestLevel: \"info\",\n * },\n * {\n * category: [\"my-app\", \"sql\"],\n * filters: [\"slow\"],\n * lowestLevel: \"debug\",\n * },\n * {\n * category: \"logtape\",\n * sinks: [\"console\"],\n * lowestLevel: \"error\",\n * },\n * ],\n * });\n * ```\n *\n * @param config The configuration.\n */\nexport async function configure<\n TSinkId extends string,\n TFilterId extends string,\n>(config: Config<TSinkId, TFilterId>): Promise<void> {\n if (currentConfig != null && !config.reset) {\n throw new ConfigError(\n \"Already configured; if you want to reset, turn on the reset flag.\",\n );\n }\n await reset();\n try {\n configureInternal(config, true);\n } catch (e) {\n if (e instanceof ConfigError) await reset();\n throw e;\n }\n}\n\n/**\n * Configure sync loggers with the specified configuration.\n *\n * Note that if the given sinks or filters are disposable, they will be\n * disposed when the configuration is reset, or when the process exits.\n *\n * Also note that passing async sinks or filters will throw. If\n * necessary use {@link resetSync} or {@link disposeSync}.\n *\n * @example\n * ```typescript\n * configureSync({\n * sinks: {\n * console: getConsoleSink(),\n * },\n * loggers: [\n * {\n * category: \"my-app\",\n * sinks: [\"console\"],\n * lowestLevel: \"info\",\n * },\n * {\n * category: \"logtape\",\n * sinks: [\"console\"],\n * lowestLevel: \"error\",\n * },\n * ],\n * });\n * ```\n *\n * @param config The configuration.\n * @since 0.9.0\n */\nexport function configureSync<TSinkId extends string, TFilterId extends string>(\n config: Config<TSinkId, TFilterId>,\n): void {\n if (currentConfig != null && !config.reset) {\n throw new ConfigError(\n \"Already configured; if you want to reset, turn on the reset flag.\",\n );\n }\n if (asyncDisposables.size > 0) {\n throw new ConfigError(\n \"Previously configured async disposables are still active. \" +\n \"Use configure() instead or explicitly dispose them using dispose().\",\n );\n }\n resetSync();\n try {\n configureInternal(config, false);\n } catch (e) {\n if (e instanceof ConfigError) resetSync();\n throw e;\n }\n}\n\nfunction configureInternal<\n TSinkId extends string,\n TFilterId extends string,\n>(config: Config<TSinkId, TFilterId>, allowAsync: boolean): void {\n currentConfig = config;\n\n let metaConfigured = false;\n const configuredCategories = new Set<string>();\n\n for (const cfg of config.loggers) {\n if (isLoggerConfigMeta(cfg)) {\n metaConfigured = true;\n }\n\n // Check for duplicate logger categories\n const categoryKey = Array.isArray(cfg.category)\n ? JSON.stringify(cfg.category)\n : JSON.stringify([cfg.category]);\n if (configuredCategories.has(categoryKey)) {\n throw new ConfigError(\n `Duplicate logger configuration for category: ${categoryKey}. ` +\n `Each category can only be configured once.`,\n );\n }\n configuredCategories.add(categoryKey);\n\n const logger = LoggerImpl.getLogger(cfg.category);\n for (const sinkId of cfg.sinks ?? []) {\n const sink = config.sinks[sinkId];\n if (!sink) {\n throw new ConfigError(`Sink not found: ${sinkId}.`);\n }\n logger.sinks.push(sink);\n }\n logger.parentSinks = cfg.parentSinks ?? \"inherit\";\n if (cfg.lowestLevel !== undefined) {\n logger.lowestLevel = cfg.lowestLevel;\n }\n for (const filterId of cfg.filters ?? []) {\n const filter = config.filters?.[filterId];\n if (filter === undefined) {\n throw new ConfigError(`Filter not found: ${filterId}.`);\n }\n logger.filters.push(toFilter(filter));\n }\n strongRefs.add(logger);\n }\n\n LoggerImpl.getLogger().contextLocalStorage = config.contextLocalStorage;\n\n for (const sink of Object.values<Sink>(config.sinks)) {\n if (Symbol.asyncDispose in sink) {\n if (allowAsync) asyncDisposables.add(sink as AsyncDisposable);\n else {\n throw new ConfigError(\n \"Async disposables cannot be used with configureSync().\",\n );\n }\n }\n if (Symbol.dispose in sink) disposables.add(sink as Disposable);\n }\n\n for (const filter of Object.values<FilterLike>(config.filters ?? {})) {\n if (filter == null || typeof filter === \"string\") continue;\n if (Symbol.asyncDispose in filter) {\n if (allowAsync) asyncDisposables.add(filter as AsyncDisposable);\n else {\n throw new ConfigError(\n \"Async disposables cannot be used with configureSync().\",\n );\n }\n }\n if (Symbol.dispose in filter) disposables.add(filter as Disposable);\n }\n\n if (\"process\" in globalThis && !(\"Deno\" in globalThis)) {\n // deno-lint-ignore no-explicit-any\n const proc = (globalThis as any).process;\n if (proc?.on) {\n proc.on(\"exit\", allowAsync ? dispose : disposeSync);\n }\n } else {\n // @ts-ignore: It's fine to addEventListener() on the browser/Deno\n addEventListener(\"unload\", allowAsync ? dispose : disposeSync);\n }\n const meta = LoggerImpl.getLogger([\"logtape\", \"meta\"]);\n if (!metaConfigured) {\n meta.sinks.push(getConsoleSink());\n }\n\n meta.info(\n \"LogTape loggers are configured. Note that LogTape itself uses the meta \" +\n \"logger, which has category {metaLoggerCategory}. The meta logger \" +\n \"purposes to log internal errors such as sink exceptions. If you \" +\n \"are seeing this message, the meta logger is automatically configured. \" +\n \"It's recommended to configure the meta logger with a separate sink \" +\n \"so that you can easily notice if logging itself fails or is \" +\n \"misconfigured. To turn off this message, configure the meta logger \" +\n \"with higher log levels than {dismissLevel}. See also \" +\n \"<https://logtape.org/manual/categories#meta-logger>.\",\n { metaLoggerCategory: [\"logtape\", \"meta\"], dismissLevel: \"info\" },\n );\n}\n\n/**\n * Get the current configuration, if any. Otherwise, `null`.\n * @returns The current configuration, if any. Otherwise, `null`.\n */\nexport function getConfig(): Config<string, string> | null {\n return currentConfig;\n}\n\n/**\n * Reset the configuration. Mostly for testing purposes.\n */\nexport async function reset(): Promise<void> {\n await dispose();\n resetInternal();\n}\n\n/**\n * Reset the configuration. Mostly for testing purposes. Will not clear async\n * sinks, only use with sync sinks. Use {@link reset} if you have async sinks.\n * @since 0.9.0\n */\nexport function resetSync(): void {\n disposeSync();\n resetInternal();\n}\n\nfunction resetInternal(): void {\n const rootLogger = LoggerImpl.getLogger([]);\n rootLogger.resetDescendants();\n delete rootLogger.contextLocalStorage;\n strongRefs.clear();\n currentConfig = null;\n}\n\n/**\n * Dispose of the disposables.\n */\nexport async function dispose(): Promise<void> {\n disposeSync();\n const promises: PromiseLike<void>[] = [];\n for (const disposable of asyncDisposables) {\n promises.push(disposable[Symbol.asyncDispose]());\n asyncDisposables.delete(disposable);\n }\n await Promise.all(promises);\n}\n\n/**\n * Dispose of the sync disposables. Async disposables will be untouched,\n * use {@link dispose} if you have async sinks.\n * @since 0.9.0\n */\nexport function disposeSync(): void {\n for (const disposable of disposables) disposable[Symbol.dispose]();\n disposables.clear();\n}\n\n/**\n * A configuration error.\n */\nexport class ConfigError extends Error {\n /**\n * Constructs a new configuration error.\n * @param message The error message.\n */\n constructor(message: string) {\n super(message);\n this.name = \"ConfigureError\";\n }\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"config.js","names":["currentConfig: Config<string, string> | null","strongRefs: Set<LoggerImpl>","disposables: Set<Disposable>","asyncDisposables: Set<AsyncDisposable>","cfg: LoggerConfig<TSinkId, TFilterId>","config: Config<TSinkId, TFilterId>","allowAsync: boolean","promises: PromiseLike<void>[]","message: string"],"sources":["../src/config.ts"],"sourcesContent":["import type { ContextLocalStorage } from \"./context.ts\";\nimport { type FilterLike, toFilter } from \"./filter.ts\";\nimport type { LogLevel } from \"./level.ts\";\nimport { LoggerImpl } from \"./logger.ts\";\nimport { getConsoleSink, type Sink } from \"./sink.ts\";\n\n/**\n * A configuration for the loggers.\n */\nexport interface Config<TSinkId extends string, TFilterId extends string> {\n /**\n * The sinks to use. The keys are the sink identifiers, and the values are\n * {@link Sink}s.\n */\n sinks: Record<TSinkId, Sink>;\n /**\n * The filters to use. The keys are the filter identifiers, and the values\n * are either {@link Filter}s or {@link LogLevel}s.\n */\n filters?: Record<TFilterId, FilterLike>;\n\n /**\n * The loggers to configure.\n */\n loggers: LoggerConfig<TSinkId, TFilterId>[];\n\n /**\n * The context-local storage to use for implicit contexts.\n * @since 0.7.0\n */\n contextLocalStorage?: ContextLocalStorage<Record<string, unknown>>;\n\n /**\n * Whether to reset the configuration before applying this one.\n */\n reset?: boolean;\n}\n\n/**\n * A logger configuration.\n */\nexport interface LoggerConfig<\n TSinkId extends string,\n TFilterId extends string,\n> {\n /**\n * The category of the logger. If a string, it is equivalent to an array\n * with one element.\n */\n category: string | string[];\n\n /**\n * The sink identifiers to use.\n */\n sinks?: TSinkId[];\n\n /**\n * Whether to inherit the parent's sinks. If `inherit`, the parent's sinks\n * are used along with the specified sinks. If `override`, the parent's\n * sinks are not used, and only the specified sinks are used.\n *\n * The default is `inherit`.\n * @default `\"inherit\"\n * @since 0.6.0\n */\n parentSinks?: \"inherit\" | \"override\";\n\n /**\n * The filter identifiers to use.\n */\n filters?: TFilterId[];\n\n /**\n * The lowest log level to accept. If `null`, the logger will reject all\n * records.\n * @since 0.8.0\n */\n lowestLevel?: LogLevel | null;\n}\n\n/**\n * The current configuration, if any. Otherwise, `null`.\n */\nlet currentConfig: Config<string, string> | null = null;\n\n/**\n * Strong references to the loggers.\n * This is to prevent the loggers from being garbage collected so that their\n * sinks and filters are not removed.\n */\nconst strongRefs: Set<LoggerImpl> = new Set();\n\n/**\n * Disposables to dispose when resetting the configuration.\n */\nconst disposables: Set<Disposable> = new Set();\n\n/**\n * Async disposables to dispose when resetting the configuration.\n */\nconst asyncDisposables: Set<AsyncDisposable> = new Set();\n\n/**\n * Check if a config is for the meta logger.\n */\nfunction isLoggerConfigMeta<TSinkId extends string, TFilterId extends string>(\n cfg: LoggerConfig<TSinkId, TFilterId>,\n): boolean {\n return cfg.category.length === 0 ||\n (cfg.category.length === 1 && cfg.category[0] === \"logtape\") ||\n (cfg.category.length === 2 &&\n cfg.category[0] === \"logtape\" &&\n cfg.category[1] === \"meta\");\n}\n\n/**\n * Configure the loggers with the specified configuration.\n *\n * Note that if the given sinks or filters are disposable, they will be\n * disposed when the configuration is reset, or when the process exits.\n *\n * @example\n * ```typescript\n * await configure({\n * sinks: {\n * console: getConsoleSink(),\n * },\n * filters: {\n * slow: (log) =>\n * \"duration\" in log.properties &&\n * log.properties.duration as number > 1000,\n * },\n * loggers: [\n * {\n * category: \"my-app\",\n * sinks: [\"console\"],\n * lowestLevel: \"info\",\n * },\n * {\n * category: [\"my-app\", \"sql\"],\n * filters: [\"slow\"],\n * lowestLevel: \"debug\",\n * },\n * {\n * category: \"logtape\",\n * sinks: [\"console\"],\n * lowestLevel: \"error\",\n * },\n * ],\n * });\n * ```\n *\n * @param config The configuration.\n */\nexport async function configure<\n TSinkId extends string,\n TFilterId extends string,\n>(config: Config<TSinkId, TFilterId>): Promise<void> {\n if (currentConfig != null && !config.reset) {\n throw new ConfigError(\n \"Already configured; if you want to reset, turn on the reset flag.\",\n );\n }\n await reset();\n try {\n configureInternal(config, true);\n } catch (e) {\n if (e instanceof ConfigError) await reset();\n throw e;\n }\n}\n\n/**\n * Configure sync loggers with the specified configuration.\n *\n * Note that if the given sinks or filters are disposable, they will be\n * disposed when the configuration is reset, or when the process exits.\n *\n * Also note that passing async sinks or filters will throw. If\n * necessary use {@link resetSync} or {@link disposeSync}.\n *\n * @example\n * ```typescript\n * configureSync({\n * sinks: {\n * console: getConsoleSink(),\n * },\n * loggers: [\n * {\n * category: \"my-app\",\n * sinks: [\"console\"],\n * lowestLevel: \"info\",\n * },\n * {\n * category: \"logtape\",\n * sinks: [\"console\"],\n * lowestLevel: \"error\",\n * },\n * ],\n * });\n * ```\n *\n * @param config The configuration.\n * @since 0.9.0\n */\nexport function configureSync<TSinkId extends string, TFilterId extends string>(\n config: Config<TSinkId, TFilterId>,\n): void {\n if (currentConfig != null && !config.reset) {\n throw new ConfigError(\n \"Already configured; if you want to reset, turn on the reset flag.\",\n );\n }\n if (asyncDisposables.size > 0) {\n throw new ConfigError(\n \"Previously configured async disposables are still active. \" +\n \"Use configure() instead or explicitly dispose them using dispose().\",\n );\n }\n resetSync();\n try {\n configureInternal(config, false);\n } catch (e) {\n if (e instanceof ConfigError) resetSync();\n throw e;\n }\n}\n\nfunction configureInternal<\n TSinkId extends string,\n TFilterId extends string,\n>(config: Config<TSinkId, TFilterId>, allowAsync: boolean): void {\n currentConfig = config;\n\n let metaConfigured = false;\n const configuredCategories = new Set<string>();\n\n for (const cfg of config.loggers) {\n if (isLoggerConfigMeta(cfg)) {\n metaConfigured = true;\n }\n\n // Check for duplicate logger categories\n const categoryKey = Array.isArray(cfg.category)\n ? JSON.stringify(cfg.category)\n : JSON.stringify([cfg.category]);\n if (configuredCategories.has(categoryKey)) {\n throw new ConfigError(\n `Duplicate logger configuration for category: ${categoryKey}. ` +\n `Each category can only be configured once.`,\n );\n }\n configuredCategories.add(categoryKey);\n\n const logger = LoggerImpl.getLogger(cfg.category);\n for (const sinkId of cfg.sinks ?? []) {\n const sink = config.sinks[sinkId];\n if (!sink) {\n throw new ConfigError(`Sink not found: ${sinkId}.`);\n }\n logger.sinks.push(sink);\n }\n logger.parentSinks = cfg.parentSinks ?? \"inherit\";\n if (cfg.lowestLevel !== undefined) {\n logger.lowestLevel = cfg.lowestLevel;\n }\n for (const filterId of cfg.filters ?? []) {\n const filter = config.filters?.[filterId];\n if (filter === undefined) {\n throw new ConfigError(`Filter not found: ${filterId}.`);\n }\n logger.filters.push(toFilter(filter));\n }\n strongRefs.add(logger);\n }\n\n LoggerImpl.getLogger().contextLocalStorage = config.contextLocalStorage;\n\n for (const sink of Object.values<Sink>(config.sinks)) {\n if (Symbol.asyncDispose in sink) {\n if (allowAsync) asyncDisposables.add(sink as AsyncDisposable);\n else {\n throw new ConfigError(\n \"Async disposables cannot be used with configureSync().\",\n );\n }\n }\n if (Symbol.dispose in sink) disposables.add(sink as Disposable);\n }\n\n for (const filter of Object.values<FilterLike>(config.filters ?? {})) {\n if (filter == null || typeof filter === \"string\") continue;\n if (Symbol.asyncDispose in filter) {\n if (allowAsync) asyncDisposables.add(filter as AsyncDisposable);\n else {\n throw new ConfigError(\n \"Async disposables cannot be used with configureSync().\",\n );\n }\n }\n if (Symbol.dispose in filter) disposables.add(filter as Disposable);\n }\n\n if (\n // deno-lint-ignore no-explicit-any\n typeof (globalThis as any).EdgeRuntime !== \"string\" &&\n \"process\" in globalThis &&\n !(\"Deno\" in globalThis)\n ) {\n // deno-lint-ignore no-explicit-any\n const proc = (globalThis as any).process;\n // Use bracket notation to avoid static analysis detection in Edge Runtime\n const onMethod = proc?.[\"on\"];\n if (typeof onMethod === \"function\") {\n onMethod.call(proc, \"exit\", allowAsync ? dispose : disposeSync);\n }\n } else {\n // @ts-ignore: It's fine to addEventListener() on the browser/Deno/Edge Runtime\n addEventListener(\"unload\", allowAsync ? dispose : disposeSync);\n }\n const meta = LoggerImpl.getLogger([\"logtape\", \"meta\"]);\n if (!metaConfigured) {\n meta.sinks.push(getConsoleSink());\n }\n\n meta.info(\n \"LogTape loggers are configured. Note that LogTape itself uses the meta \" +\n \"logger, which has category {metaLoggerCategory}. The meta logger \" +\n \"purposes to log internal errors such as sink exceptions. If you \" +\n \"are seeing this message, the meta logger is automatically configured. \" +\n \"It's recommended to configure the meta logger with a separate sink \" +\n \"so that you can easily notice if logging itself fails or is \" +\n \"misconfigured. To turn off this message, configure the meta logger \" +\n \"with higher log levels than {dismissLevel}. See also \" +\n \"<https://logtape.org/manual/categories#meta-logger>.\",\n { metaLoggerCategory: [\"logtape\", \"meta\"], dismissLevel: \"info\" },\n );\n}\n\n/**\n * Get the current configuration, if any. Otherwise, `null`.\n * @returns The current configuration, if any. Otherwise, `null`.\n */\nexport function getConfig(): Config<string, string> | null {\n return currentConfig;\n}\n\n/**\n * Reset the configuration. Mostly for testing purposes.\n */\nexport async function reset(): Promise<void> {\n await dispose();\n resetInternal();\n}\n\n/**\n * Reset the configuration. Mostly for testing purposes. Will not clear async\n * sinks, only use with sync sinks. Use {@link reset} if you have async sinks.\n * @since 0.9.0\n */\nexport function resetSync(): void {\n disposeSync();\n resetInternal();\n}\n\nfunction resetInternal(): void {\n const rootLogger = LoggerImpl.getLogger([]);\n rootLogger.resetDescendants();\n delete rootLogger.contextLocalStorage;\n strongRefs.clear();\n currentConfig = null;\n}\n\n/**\n * Dispose of the disposables.\n */\nexport async function dispose(): Promise<void> {\n disposeSync();\n const promises: PromiseLike<void>[] = [];\n for (const disposable of asyncDisposables) {\n promises.push(disposable[Symbol.asyncDispose]());\n asyncDisposables.delete(disposable);\n }\n await Promise.all(promises);\n}\n\n/**\n * Dispose of the sync disposables. Async disposables will be untouched,\n * use {@link dispose} if you have async sinks.\n * @since 0.9.0\n */\nexport function disposeSync(): void {\n for (const disposable of disposables) disposable[Symbol.dispose]();\n disposables.clear();\n}\n\n/**\n * A configuration error.\n */\nexport class ConfigError extends Error {\n /**\n * Constructs a new configuration error.\n * @param message The error message.\n */\n constructor(message: string) {\n super(message);\n this.name = \"ConfigureError\";\n }\n}\n"],"mappings":"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"}
|
package/dist/formatter.cjs
CHANGED
|
@@ -175,7 +175,7 @@ function getTextFormatter(options = {}) {
|
|
|
175
175
|
else return tsOption;
|
|
176
176
|
})();
|
|
177
177
|
const categorySeparator = options.category ?? "·";
|
|
178
|
-
const valueRenderer = options.value
|
|
178
|
+
const valueRenderer = options.value ? (v) => options.value(v, inspect) : inspect;
|
|
179
179
|
const levelRenderer = (() => {
|
|
180
180
|
const levelOption = options.level;
|
|
181
181
|
if (levelOption == null || levelOption === "ABBR") return (level) => levelRenderersCache.ABBR[level];
|
|
@@ -272,8 +272,8 @@ function getAnsiColorFormatter(options = {}) {
|
|
|
272
272
|
const categorySuffix = categoryStyle == null && categoryColor == null ? "" : RESET;
|
|
273
273
|
return getTextFormatter({
|
|
274
274
|
timestamp: "date-time-tz",
|
|
275
|
-
value(value) {
|
|
276
|
-
return
|
|
275
|
+
value(value, fallbackInspect) {
|
|
276
|
+
return fallbackInspect(value, { colors: true });
|
|
277
277
|
},
|
|
278
278
|
...options,
|
|
279
279
|
format({ timestamp, level, category, message, record }) {
|
package/dist/formatter.d.cts
CHANGED
|
@@ -98,15 +98,40 @@ interface TextFormatterOptions {
|
|
|
98
98
|
* The format of the embedded values.
|
|
99
99
|
*
|
|
100
100
|
* A function that renders a value to a string. This function is used to
|
|
101
|
-
* render the values in the log record. The default is
|
|
102
|
-
*
|
|
101
|
+
* render the values in the log record. The default is a cross-runtime
|
|
102
|
+
* `inspect()` function that uses [`util.inspect()`] in Node.js/Bun,
|
|
103
|
+
* [`Deno.inspect()`] in Deno, or falls back to {@link JSON.stringify} in
|
|
104
|
+
* browsers.
|
|
105
|
+
*
|
|
106
|
+
* The second parameter provides access to the default cross-runtime
|
|
107
|
+
* `inspect()` function, allowing you to fall back to the default behavior
|
|
108
|
+
* for certain values while customizing others. You can ignore this
|
|
109
|
+
* parameter if you don't need the fallback functionality.
|
|
103
110
|
*
|
|
104
111
|
* [`util.inspect()`]: https://nodejs.org/api/util.html#utilinspectobject-options
|
|
105
112
|
* [`Deno.inspect()`]: https://docs.deno.com/api/deno/~/Deno.inspect
|
|
106
113
|
* @param value The value to render.
|
|
114
|
+
* @param inspect The default cross-runtime inspect function that can be used
|
|
115
|
+
* as a fallback. Accepts an optional `options` parameter
|
|
116
|
+
* with a `colors` boolean field.
|
|
107
117
|
* @returns The string representation of the value.
|
|
118
|
+
* @example
|
|
119
|
+
* ```typescript
|
|
120
|
+
* getTextFormatter({
|
|
121
|
+
* value(value, inspect) {
|
|
122
|
+
* // Custom formatting for numbers
|
|
123
|
+
* if (typeof value === 'number') {
|
|
124
|
+
* return value.toFixed(2);
|
|
125
|
+
* }
|
|
126
|
+
* // Fall back to default for everything else
|
|
127
|
+
* return inspect(value);
|
|
128
|
+
* }
|
|
129
|
+
* })
|
|
130
|
+
* ```
|
|
108
131
|
*/
|
|
109
|
-
value?: (value: unknown
|
|
132
|
+
value?: (value: unknown, inspect: (value: unknown, options?: {
|
|
133
|
+
colors?: boolean;
|
|
134
|
+
}) => string) => string;
|
|
110
135
|
/**
|
|
111
136
|
* How those formatted parts are concatenated.
|
|
112
137
|
*
|
package/dist/formatter.d.cts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"formatter.d.cts","names":[],"sources":["../src/formatter.ts"],"sourcesContent":[],"mappings":";;;;;;;AAWA;AA+DA;AA+BA;;;
|
|
1
|
+
{"version":3,"file":"formatter.d.cts","names":[],"sources":["../src/formatter.ts"],"sourcesContent":[],"mappings":";;;;;;;AAWA;AA+DA;AA+BA;;;AAiIoB,KA/NR,aAAA,GA+NQ,CAAA,MAAA,EA/NiB,SA+NjB,EAAA,GAAA,MAAA;AAAe;AAgJnC;;;AAEG,UAlTc,eAAA,CAkTd;EAAa;AAmGhB;AAQA;EAyBY,SAAA,EAAA,MAAS,GAAA,IAAA;EA4BJ;;;EAwCW,KAKT,EAAA,MAAA;EAAS;;;EAiBc,QAA1B,EAAA,MAAA;EAAM;;;EA9DiD,OAAA,EAAA,MAAA;EAmFvD;;;EACyB,MACtC,EA/gBO,SA+gBP;AAAa;AA4DhB;AAMA;AAsDA;;AACW,UAjoBM,oBAAA,CAioBN;EAA8B;AACzB;AA2JhB;AAUA;AAqBA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;mEA9vBe;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;oBAmEK;;;;;;;;;;;;;;;;;;;iBAgJJ,gBAAA,WACL,uBACR;;;;;;;;;;;cAmGU,sBAAsB;;;;;KAQvB,SAAA;;;;;KAyBA,SAAA;;;;;UA4BK,yBAAA,SAAkC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;mBAwChC;;;;mBAKA;;;;eAKJ;;;;;;;;;;;gBAYC,OAAO,UAAU;;;;kBAKf;;;;kBAKA;;;;;;;;;;iBAWF,qBAAA,WACL,4BACR;;;;;;;;;;cA4DU,oBAAoB;;;;;UAMhB,yBAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;iBAsDD,qBAAA,WACL,4BACR;;;;;;;;;;;;;;;;;cA2JU,oBAAoB;;;;;;;;;KAUrB,gBAAA,YAA4B;;;;;;;;iBAqBxB,uBAAA,SAAgC"}
|
package/dist/formatter.d.ts
CHANGED
|
@@ -98,15 +98,40 @@ interface TextFormatterOptions {
|
|
|
98
98
|
* The format of the embedded values.
|
|
99
99
|
*
|
|
100
100
|
* A function that renders a value to a string. This function is used to
|
|
101
|
-
* render the values in the log record. The default is
|
|
102
|
-
*
|
|
101
|
+
* render the values in the log record. The default is a cross-runtime
|
|
102
|
+
* `inspect()` function that uses [`util.inspect()`] in Node.js/Bun,
|
|
103
|
+
* [`Deno.inspect()`] in Deno, or falls back to {@link JSON.stringify} in
|
|
104
|
+
* browsers.
|
|
105
|
+
*
|
|
106
|
+
* The second parameter provides access to the default cross-runtime
|
|
107
|
+
* `inspect()` function, allowing you to fall back to the default behavior
|
|
108
|
+
* for certain values while customizing others. You can ignore this
|
|
109
|
+
* parameter if you don't need the fallback functionality.
|
|
103
110
|
*
|
|
104
111
|
* [`util.inspect()`]: https://nodejs.org/api/util.html#utilinspectobject-options
|
|
105
112
|
* [`Deno.inspect()`]: https://docs.deno.com/api/deno/~/Deno.inspect
|
|
106
113
|
* @param value The value to render.
|
|
114
|
+
* @param inspect The default cross-runtime inspect function that can be used
|
|
115
|
+
* as a fallback. Accepts an optional `options` parameter
|
|
116
|
+
* with a `colors` boolean field.
|
|
107
117
|
* @returns The string representation of the value.
|
|
118
|
+
* @example
|
|
119
|
+
* ```typescript
|
|
120
|
+
* getTextFormatter({
|
|
121
|
+
* value(value, inspect) {
|
|
122
|
+
* // Custom formatting for numbers
|
|
123
|
+
* if (typeof value === 'number') {
|
|
124
|
+
* return value.toFixed(2);
|
|
125
|
+
* }
|
|
126
|
+
* // Fall back to default for everything else
|
|
127
|
+
* return inspect(value);
|
|
128
|
+
* }
|
|
129
|
+
* })
|
|
130
|
+
* ```
|
|
108
131
|
*/
|
|
109
|
-
value?: (value: unknown
|
|
132
|
+
value?: (value: unknown, inspect: (value: unknown, options?: {
|
|
133
|
+
colors?: boolean;
|
|
134
|
+
}) => string) => string;
|
|
110
135
|
/**
|
|
111
136
|
* How those formatted parts are concatenated.
|
|
112
137
|
*
|
package/dist/formatter.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"formatter.d.ts","names":[],"sources":["../src/formatter.ts"],"sourcesContent":[],"mappings":";;;;;;;AAWA;AA+DA;AA+BA;;;
|
|
1
|
+
{"version":3,"file":"formatter.d.ts","names":[],"sources":["../src/formatter.ts"],"sourcesContent":[],"mappings":";;;;;;;AAWA;AA+DA;AA+BA;;;AAiIoB,KA/NR,aAAA,GA+NQ,CAAA,MAAA,EA/NiB,SA+NjB,EAAA,GAAA,MAAA;AAAe;AAgJnC;;;AAEG,UAlTc,eAAA,CAkTd;EAAa;AAmGhB;AAQA;EAyBY,SAAA,EAAA,MAAS,GAAA,IAAA;EA4BJ;;;EAwCW,KAKT,EAAA,MAAA;EAAS;;;EAiBc,QAA1B,EAAA,MAAA;EAAM;;;EA9DiD,OAAA,EAAA,MAAA;EAmFvD;;;EACyB,MACtC,EA/gBO,SA+gBP;AAAa;AA4DhB;AAMA;AAsDA;;AACW,UAjoBM,oBAAA,CAioBN;EAA8B;AACzB;AA2JhB;AAUA;AAqBA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;mEA9vBe;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;oBAmEK;;;;;;;;;;;;;;;;;;;iBAgJJ,gBAAA,WACL,uBACR;;;;;;;;;;;cAmGU,sBAAsB;;;;;KAQvB,SAAA;;;;;KAyBA,SAAA;;;;;UA4BK,yBAAA,SAAkC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;mBAwChC;;;;mBAKA;;;;eAKJ;;;;;;;;;;;gBAYC,OAAO,UAAU;;;;kBAKf;;;;kBAKA;;;;;;;;;;iBAWF,qBAAA,WACL,4BACR;;;;;;;;;;cA4DU,oBAAoB;;;;;UAMhB,yBAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;iBAsDD,qBAAA,WACL,4BACR;;;;;;;;;;;;;;;;;cA2JU,oBAAoB;;;;;;;;;KAUrB,gBAAA,YAA4B;;;;;;;;iBAqBxB,uBAAA,SAAgC"}
|
package/dist/formatter.js
CHANGED
|
@@ -174,7 +174,7 @@ function getTextFormatter(options = {}) {
|
|
|
174
174
|
else return tsOption;
|
|
175
175
|
})();
|
|
176
176
|
const categorySeparator = options.category ?? "·";
|
|
177
|
-
const valueRenderer = options.value
|
|
177
|
+
const valueRenderer = options.value ? (v) => options.value(v, inspect) : inspect;
|
|
178
178
|
const levelRenderer = (() => {
|
|
179
179
|
const levelOption = options.level;
|
|
180
180
|
if (levelOption == null || levelOption === "ABBR") return (level) => levelRenderersCache.ABBR[level];
|
|
@@ -271,8 +271,8 @@ function getAnsiColorFormatter(options = {}) {
|
|
|
271
271
|
const categorySuffix = categoryStyle == null && categoryColor == null ? "" : RESET;
|
|
272
272
|
return getTextFormatter({
|
|
273
273
|
timestamp: "date-time-tz",
|
|
274
|
-
value(value) {
|
|
275
|
-
return
|
|
274
|
+
value(value, fallbackInspect) {
|
|
275
|
+
return fallbackInspect(value, { colors: true });
|
|
276
276
|
},
|
|
277
277
|
...options,
|
|
278
278
|
format({ timestamp, level, category, message, record }) {
|
package/dist/formatter.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"formatter.js","names":["levelAbbreviations: Record<LogLevel, string>","inspect: (value: unknown, options?: { colors?: boolean }) => string","num: number","ts: number","options: TextFormatterOptions","level: LogLevel","formatter: (values: FormattedValues) => string","record: LogRecord","message: string","parts: string[]","values: FormattedValues","defaultTextFormatter: TextFormatter","ansiColors: Record<AnsiColor, string>","ansiStyles: Record<AnsiStyle, string>","defaultLevelColors: Record<LogLevel, AnsiColor | null>","options: AnsiColorFormatterOptions","value: unknown","ansiColorFormatter: TextFormatter","options: JsonLinesFormatterOptions","joinCategory: (category: readonly string[]) => string | readonly string[]","category: readonly string[]","getProperties: (\n properties: Record<string, unknown>,\n ) => Record<string, unknown>","result: Record<string, unknown>","getMessage: (record: LogRecord) => string","jsonLinesFormatter: TextFormatter","logLevelStyles: Record<LogLevel, string>","values: unknown[]"],"sources":["../src/formatter.ts"],"sourcesContent":["import * as util from \"#util\";\nimport type { LogLevel } from \"./level.ts\";\nimport type { LogRecord } from \"./record.ts\";\n\n/**\n * A text formatter is a function that accepts a log record and returns\n * a string.\n *\n * @param record The log record to format.\n * @returns The formatted log record.\n */\nexport type TextFormatter = (record: LogRecord) => string;\n\n/**\n * The severity level abbreviations.\n */\nconst levelAbbreviations: Record<LogLevel, string> = {\n \"trace\": \"TRC\",\n \"debug\": \"DBG\",\n \"info\": \"INF\",\n \"warning\": \"WRN\",\n \"error\": \"ERR\",\n \"fatal\": \"FTL\",\n};\n\n/**\n * A platform-specific inspect function. In Deno, this is {@link Deno.inspect},\n * and in Node.js/Bun it is `util.inspect()`. If neither is available, it\n * falls back to {@link JSON.stringify}.\n *\n * @param value The value to inspect.\n * @param options The options for inspecting the value.\n * If `colors` is `true`, the output will be ANSI-colored.\n * @returns The string representation of the value.\n */\nconst inspect: (value: unknown, options?: { colors?: boolean }) => string =\n // @ts-ignore: Browser detection\n // dnt-shim-ignore\n typeof document !== \"undefined\" ||\n // @ts-ignore: React Native detection\n // dnt-shim-ignore\n typeof navigator !== \"undefined\" && navigator.product === \"ReactNative\"\n ? (v) => JSON.stringify(v)\n // @ts-ignore: Deno global\n // dnt-shim-ignore\n : \"Deno\" in globalThis && \"inspect\" in globalThis.Deno &&\n // @ts-ignore: Deno global\n // dnt-shim-ignore\n typeof globalThis.Deno.inspect === \"function\"\n ? (v, opts) =>\n // @ts-ignore: Deno global\n // dnt-shim-ignore\n globalThis.Deno.inspect(v, {\n strAbbreviateSize: Infinity,\n iterableLimit: Infinity,\n ...opts,\n })\n // @ts-ignore: Node.js global\n // dnt-shim-ignore\n : util != null && \"inspect\" in util && typeof util.inspect === \"function\"\n ? (v, opts) =>\n // @ts-ignore: Node.js global\n // dnt-shim-ignore\n util.inspect(v, {\n maxArrayLength: Infinity,\n maxStringLength: Infinity,\n ...opts,\n })\n : (v) => JSON.stringify(v);\n\n/**\n * The formatted values for a log record.\n * @since 0.6.0\n */\nexport interface FormattedValues {\n /**\n * The formatted timestamp.\n */\n timestamp: string | null;\n\n /**\n * The formatted log level.\n */\n level: string;\n\n /**\n * The formatted category.\n */\n category: string;\n\n /**\n * The formatted message.\n */\n message: string;\n\n /**\n * The unformatted log record.\n */\n record: LogRecord;\n}\n\n/**\n * The various options for the built-in text formatters.\n * @since 0.6.0\n */\nexport interface TextFormatterOptions {\n /**\n * The timestamp format. This can be one of the following:\n *\n * - `\"date-time-timezone\"`: The date and time with the full timezone offset\n * (e.g., `\"2023-11-14 22:13:20.000 +00:00\"`).\n * - `\"date-time-tz\"`: The date and time with the short timezone offset\n * (e.g., `\"2023-11-14 22:13:20.000 +00\"`).\n * - `\"date-time\"`: The date and time without the timezone offset\n * (e.g., `\"2023-11-14 22:13:20.000\"`).\n * - `\"time-timezone\"`: The time with the full timezone offset but without\n * the date (e.g., `\"22:13:20.000 +00:00\"`).\n * - `\"time-tz\"`: The time with the short timezone offset but without the date\n * (e.g., `\"22:13:20.000 +00\"`).\n * - `\"time\"`: The time without the date or timezone offset\n * (e.g., `\"22:13:20.000\"`).\n * - `\"date\"`: The date without the time or timezone offset\n * (e.g., `\"2023-11-14\"`).\n * - `\"rfc3339\"`: The date and time in RFC 3339 format\n * (e.g., `\"2023-11-14T22:13:20.000Z\"`).\n * - `\"none\"` or `\"disabled\"`: No display\n *\n * Alternatively, this can be a function that accepts a timestamp and returns\n * a string.\n *\n * The default is `\"date-time-timezone\"`.\n */\n timestamp?:\n | \"date-time-timezone\"\n | \"date-time-tz\"\n | \"date-time\"\n | \"time-timezone\"\n | \"time-tz\"\n | \"time\"\n | \"date\"\n | \"rfc3339\"\n | \"none\"\n | \"disabled\"\n | ((ts: number) => string | null);\n\n /**\n * The log level format. This can be one of the following:\n *\n * - `\"ABBR\"`: The log level abbreviation in uppercase (e.g., `\"INF\"`).\n * - `\"FULL\"`: The full log level name in uppercase (e.g., `\"INFO\"`).\n * - `\"L\"`: The first letter of the log level in uppercase (e.g., `\"I\"`).\n * - `\"abbr\"`: The log level abbreviation in lowercase (e.g., `\"inf\"`).\n * - `\"full\"`: The full log level name in lowercase (e.g., `\"info\"`).\n * - `\"l\"`: The first letter of the log level in lowercase (e.g., `\"i\"`).\n *\n * Alternatively, this can be a function that accepts a log level and returns\n * a string.\n *\n * The default is `\"ABBR\"`.\n */\n level?:\n | \"ABBR\"\n | \"FULL\"\n | \"L\"\n | \"abbr\"\n | \"full\"\n | \"l\"\n | ((level: LogLevel) => string);\n\n /**\n * The separator between category names. For example, if the separator is\n * `\"·\"`, the category `[\"a\", \"b\", \"c\"]` will be formatted as `\"a·b·c\"`.\n * The default separator is `\"·\"`.\n *\n * If this is a function, it will be called with the category array and\n * should return a string, which will be used for rendering the category.\n */\n category?: string | ((category: readonly string[]) => string);\n\n /**\n * The format of the embedded values.\n *\n * A function that renders a value to a string. This function is used to\n * render the values in the log record. The default is [`util.inspect()`] in\n * Node.js/Bun and [`Deno.inspect()`] in Deno.\n *\n * [`util.inspect()`]: https://nodejs.org/api/util.html#utilinspectobject-options\n * [`Deno.inspect()`]: https://docs.deno.com/api/deno/~/Deno.inspect\n * @param value The value to render.\n * @returns The string representation of the value.\n */\n value?: (value: unknown) => string;\n\n /**\n * How those formatted parts are concatenated.\n *\n * A function that formats the log record. This function is called with the\n * formatted values and should return a string. Note that the formatted\n * *should not* include a newline character at the end.\n *\n * By default, this is a function that formats the log record as follows:\n *\n * ```\n * 2023-11-14 22:13:20.000 +00:00 [INF] category·subcategory: Hello, world!\n * ```\n * @param values The formatted values.\n * @returns The formatted log record.\n */\n format?: (values: FormattedValues) => string;\n}\n\n// Optimized helper functions for timestamp formatting\nfunction padZero(num: number): string {\n return num < 10 ? `0${num}` : `${num}`;\n}\n\nfunction padThree(num: number): string {\n return num < 10 ? `00${num}` : num < 100 ? `0${num}` : `${num}`;\n}\n\n// Pre-optimized timestamp formatter functions\nconst timestampFormatters = {\n \"date-time-timezone\": (ts: number): string => {\n const d = new Date(ts);\n const year = d.getUTCFullYear();\n const month = padZero(d.getUTCMonth() + 1);\n const day = padZero(d.getUTCDate());\n const hour = padZero(d.getUTCHours());\n const minute = padZero(d.getUTCMinutes());\n const second = padZero(d.getUTCSeconds());\n const ms = padThree(d.getUTCMilliseconds());\n return `${year}-${month}-${day} ${hour}:${minute}:${second}.${ms} +00:00`;\n },\n \"date-time-tz\": (ts: number): string => {\n const d = new Date(ts);\n const year = d.getUTCFullYear();\n const month = padZero(d.getUTCMonth() + 1);\n const day = padZero(d.getUTCDate());\n const hour = padZero(d.getUTCHours());\n const minute = padZero(d.getUTCMinutes());\n const second = padZero(d.getUTCSeconds());\n const ms = padThree(d.getUTCMilliseconds());\n return `${year}-${month}-${day} ${hour}:${minute}:${second}.${ms} +00`;\n },\n \"date-time\": (ts: number): string => {\n const d = new Date(ts);\n const year = d.getUTCFullYear();\n const month = padZero(d.getUTCMonth() + 1);\n const day = padZero(d.getUTCDate());\n const hour = padZero(d.getUTCHours());\n const minute = padZero(d.getUTCMinutes());\n const second = padZero(d.getUTCSeconds());\n const ms = padThree(d.getUTCMilliseconds());\n return `${year}-${month}-${day} ${hour}:${minute}:${second}.${ms}`;\n },\n \"time-timezone\": (ts: number): string => {\n const d = new Date(ts);\n const hour = padZero(d.getUTCHours());\n const minute = padZero(d.getUTCMinutes());\n const second = padZero(d.getUTCSeconds());\n const ms = padThree(d.getUTCMilliseconds());\n return `${hour}:${minute}:${second}.${ms} +00:00`;\n },\n \"time-tz\": (ts: number): string => {\n const d = new Date(ts);\n const hour = padZero(d.getUTCHours());\n const minute = padZero(d.getUTCMinutes());\n const second = padZero(d.getUTCSeconds());\n const ms = padThree(d.getUTCMilliseconds());\n return `${hour}:${minute}:${second}.${ms} +00`;\n },\n \"time\": (ts: number): string => {\n const d = new Date(ts);\n const hour = padZero(d.getUTCHours());\n const minute = padZero(d.getUTCMinutes());\n const second = padZero(d.getUTCSeconds());\n const ms = padThree(d.getUTCMilliseconds());\n return `${hour}:${minute}:${second}.${ms}`;\n },\n \"date\": (ts: number): string => {\n const d = new Date(ts);\n const year = d.getUTCFullYear();\n const month = padZero(d.getUTCMonth() + 1);\n const day = padZero(d.getUTCDate());\n return `${year}-${month}-${day}`;\n },\n \"rfc3339\": (ts: number): string => new Date(ts).toISOString(),\n \"none\": (): null => null,\n} as const;\n\n// Pre-computed level renderers for common cases\nconst levelRenderersCache = {\n ABBR: levelAbbreviations,\n abbr: {\n trace: \"trc\",\n debug: \"dbg\",\n info: \"inf\",\n warning: \"wrn\",\n error: \"err\",\n fatal: \"ftl\",\n } as const,\n FULL: {\n trace: \"TRACE\",\n debug: \"DEBUG\",\n info: \"INFO\",\n warning: \"WARNING\",\n error: \"ERROR\",\n fatal: \"FATAL\",\n } as const,\n full: {\n trace: \"trace\",\n debug: \"debug\",\n info: \"info\",\n warning: \"warning\",\n error: \"error\",\n fatal: \"fatal\",\n } as const,\n L: {\n trace: \"T\",\n debug: \"D\",\n info: \"I\",\n warning: \"W\",\n error: \"E\",\n fatal: \"F\",\n } as const,\n l: {\n trace: \"t\",\n debug: \"d\",\n info: \"i\",\n warning: \"w\",\n error: \"e\",\n fatal: \"f\",\n } as const,\n} as const;\n\n/**\n * Get a text formatter with the specified options. Although it's flexible\n * enough to create a custom formatter, if you want more control, you can\n * create a custom formatter that satisfies the {@link TextFormatter} type\n * instead.\n *\n * For more information on the options, see {@link TextFormatterOptions}.\n *\n * By default, the formatter formats log records as follows:\n *\n * ```\n * 2023-11-14 22:13:20.000 +00:00 [INF] category·subcategory: Hello, world!\n * ```\n * @param options The options for the text formatter.\n * @returns The text formatter.\n * @since 0.6.0\n */\nexport function getTextFormatter(\n options: TextFormatterOptions = {},\n): TextFormatter {\n // Pre-compute timestamp formatter with optimized lookup\n const timestampRenderer = (() => {\n const tsOption = options.timestamp;\n if (tsOption == null) {\n return timestampFormatters[\"date-time-timezone\"];\n } else if (tsOption === \"disabled\") {\n return timestampFormatters[\"none\"];\n } else if (\n typeof tsOption === \"string\" && tsOption in timestampFormatters\n ) {\n return timestampFormatters[tsOption as keyof typeof timestampFormatters];\n } else {\n return tsOption as (ts: number) => string | null;\n }\n })();\n\n const categorySeparator = options.category ?? \"·\";\n const valueRenderer = options.value ?? inspect;\n\n // Pre-compute level renderer for better performance\n const levelRenderer = (() => {\n const levelOption = options.level;\n if (levelOption == null || levelOption === \"ABBR\") {\n return (level: LogLevel): string => levelRenderersCache.ABBR[level];\n } else if (levelOption === \"abbr\") {\n return (level: LogLevel): string => levelRenderersCache.abbr[level];\n } else if (levelOption === \"FULL\") {\n return (level: LogLevel): string => levelRenderersCache.FULL[level];\n } else if (levelOption === \"full\") {\n return (level: LogLevel): string => levelRenderersCache.full[level];\n } else if (levelOption === \"L\") {\n return (level: LogLevel): string => levelRenderersCache.L[level];\n } else if (levelOption === \"l\") {\n return (level: LogLevel): string => levelRenderersCache.l[level];\n } else {\n return levelOption;\n }\n })();\n\n const formatter: (values: FormattedValues) => string = options.format ??\n (({ timestamp, level, category, message }: FormattedValues) =>\n `${timestamp ? `${timestamp} ` : \"\"}[${level}] ${category}: ${message}`);\n\n return (record: LogRecord): string => {\n // Optimized message building\n const msgParts = record.message;\n const msgLen = msgParts.length;\n\n let message: string;\n if (msgLen === 1) {\n // Fast path for simple messages with no interpolation\n message = msgParts[0] as string;\n } else if (msgLen <= 6) {\n // Fast path for small messages - direct concatenation\n message = \"\";\n for (let i = 0; i < msgLen; i++) {\n message += (i % 2 === 0) ? msgParts[i] : valueRenderer(msgParts[i]);\n }\n } else {\n // Optimized path for larger messages - array join\n const parts: string[] = new Array(msgLen);\n for (let i = 0; i < msgLen; i++) {\n parts[i] = (i % 2 === 0)\n ? msgParts[i] as string\n : valueRenderer(msgParts[i]);\n }\n message = parts.join(\"\");\n }\n\n const timestamp = timestampRenderer(record.timestamp);\n const level = levelRenderer(record.level);\n const category = typeof categorySeparator === \"function\"\n ? categorySeparator(record.category)\n : record.category.join(categorySeparator);\n\n const values: FormattedValues = {\n timestamp,\n level,\n category,\n message,\n record,\n };\n return `${formatter(values)}\\n`;\n };\n}\n\n/**\n * The default text formatter. This formatter formats log records as follows:\n *\n * ```\n * 2023-11-14 22:13:20.000 +00:00 [INF] category·subcategory: Hello, world!\n * ```\n *\n * @param record The log record to format.\n * @returns The formatted log record.\n */\nexport const defaultTextFormatter: TextFormatter = getTextFormatter();\n\nconst RESET = \"\\x1b[0m\";\n\n/**\n * The ANSI colors. These can be used to colorize text in the console.\n * @since 0.6.0\n */\nexport type AnsiColor =\n | \"black\"\n | \"red\"\n | \"green\"\n | \"yellow\"\n | \"blue\"\n | \"magenta\"\n | \"cyan\"\n | \"white\";\n\nconst ansiColors: Record<AnsiColor, string> = {\n black: \"\\x1b[30m\",\n red: \"\\x1b[31m\",\n green: \"\\x1b[32m\",\n yellow: \"\\x1b[33m\",\n blue: \"\\x1b[34m\",\n magenta: \"\\x1b[35m\",\n cyan: \"\\x1b[36m\",\n white: \"\\x1b[37m\",\n};\n\n/**\n * The ANSI text styles.\n * @since 0.6.0\n */\nexport type AnsiStyle =\n | \"bold\"\n | \"dim\"\n | \"italic\"\n | \"underline\"\n | \"strikethrough\";\n\nconst ansiStyles: Record<AnsiStyle, string> = {\n bold: \"\\x1b[1m\",\n dim: \"\\x1b[2m\",\n italic: \"\\x1b[3m\",\n underline: \"\\x1b[4m\",\n strikethrough: \"\\x1b[9m\",\n};\n\nconst defaultLevelColors: Record<LogLevel, AnsiColor | null> = {\n trace: null,\n debug: \"blue\",\n info: \"green\",\n warning: \"yellow\",\n error: \"red\",\n fatal: \"magenta\",\n};\n\n/**\n * The various options for the ANSI color formatter.\n * @since 0.6.0\n */\nexport interface AnsiColorFormatterOptions extends TextFormatterOptions {\n /**\n * The timestamp format. This can be one of the following:\n *\n * - `\"date-time-timezone\"`: The date and time with the full timezone offset\n * (e.g., `\"2023-11-14 22:13:20.000 +00:00\"`).\n * - `\"date-time-tz\"`: The date and time with the short timezone offset\n * (e.g., `\"2023-11-14 22:13:20.000 +00\"`).\n * - `\"date-time\"`: The date and time without the timezone offset\n * (e.g., `\"2023-11-14 22:13:20.000\"`).\n * - `\"time-timezone\"`: The time with the full timezone offset but without\n * the date (e.g., `\"22:13:20.000 +00:00\"`).\n * - `\"time-tz\"`: The time with the short timezone offset but without the date\n * (e.g., `\"22:13:20.000 +00\"`).\n * - `\"time\"`: The time without the date or timezone offset\n * (e.g., `\"22:13:20.000\"`).\n * - `\"date\"`: The date without the time or timezone offset\n * (e.g., `\"2023-11-14\"`).\n * - `\"rfc3339\"`: The date and time in RFC 3339 format\n * (e.g., `\"2023-11-14T22:13:20.000Z\"`).\n *\n * Alternatively, this can be a function that accepts a timestamp and returns\n * a string.\n *\n * The default is `\"date-time-tz\"`.\n */\n timestamp?:\n | \"date-time-timezone\"\n | \"date-time-tz\"\n | \"date-time\"\n | \"time-timezone\"\n | \"time-tz\"\n | \"time\"\n | \"date\"\n | \"rfc3339\"\n | ((ts: number) => string);\n\n /**\n * The ANSI style for the timestamp. `\"dim\"` is used by default.\n */\n timestampStyle?: AnsiStyle | null;\n\n /**\n * The ANSI color for the timestamp. No color is used by default.\n */\n timestampColor?: AnsiColor | null;\n\n /**\n * The ANSI style for the log level. `\"bold\"` is used by default.\n */\n levelStyle?: AnsiStyle | null;\n\n /**\n * The ANSI colors for the log levels. The default colors are as follows:\n *\n * - `\"trace\"`: `null` (no color)\n * - `\"debug\"`: `\"blue\"`\n * - `\"info\"`: `\"green\"`\n * - `\"warning\"`: `\"yellow\"`\n * - `\"error\"`: `\"red\"`\n * - `\"fatal\"`: `\"magenta\"`\n */\n levelColors?: Record<LogLevel, AnsiColor | null>;\n\n /**\n * The ANSI style for the category. `\"dim\"` is used by default.\n */\n categoryStyle?: AnsiStyle | null;\n\n /**\n * The ANSI color for the category. No color is used by default.\n */\n categoryColor?: AnsiColor | null;\n}\n\n/**\n * Get an ANSI color formatter with the specified options.\n *\n * \n * @param option The options for the ANSI color formatter.\n * @returns The ANSI color formatter.\n * @since 0.6.0\n */\nexport function getAnsiColorFormatter(\n options: AnsiColorFormatterOptions = {},\n): TextFormatter {\n const format = options.format;\n const timestampStyle = typeof options.timestampStyle === \"undefined\"\n ? \"dim\"\n : options.timestampStyle;\n const timestampColor = options.timestampColor ?? null;\n const timestampPrefix = `${\n timestampStyle == null ? \"\" : ansiStyles[timestampStyle]\n }${timestampColor == null ? \"\" : ansiColors[timestampColor]}`;\n const timestampSuffix = timestampStyle == null && timestampColor == null\n ? \"\"\n : RESET;\n const levelStyle = typeof options.levelStyle === \"undefined\"\n ? \"bold\"\n : options.levelStyle;\n const levelColors = options.levelColors ?? defaultLevelColors;\n const categoryStyle = typeof options.categoryStyle === \"undefined\"\n ? \"dim\"\n : options.categoryStyle;\n const categoryColor = options.categoryColor ?? null;\n const categoryPrefix = `${\n categoryStyle == null ? \"\" : ansiStyles[categoryStyle]\n }${categoryColor == null ? \"\" : ansiColors[categoryColor]}`;\n const categorySuffix = categoryStyle == null && categoryColor == null\n ? \"\"\n : RESET;\n return getTextFormatter({\n timestamp: \"date-time-tz\",\n value(value: unknown): string {\n return inspect(value, { colors: true });\n },\n ...options,\n format({ timestamp, level, category, message, record }): string {\n const levelColor = levelColors[record.level];\n timestamp = `${timestampPrefix}${timestamp}${timestampSuffix}`;\n level = `${levelStyle == null ? \"\" : ansiStyles[levelStyle]}${\n levelColor == null ? \"\" : ansiColors[levelColor]\n }${level}${levelStyle == null && levelColor == null ? \"\" : RESET}`;\n return format == null\n ? `${timestamp} ${level} ${categoryPrefix}${category}:${categorySuffix} ${message}`\n : format({\n timestamp,\n level,\n category: `${categoryPrefix}${category}${categorySuffix}`,\n message,\n record,\n });\n },\n });\n}\n\n/**\n * A text formatter that uses ANSI colors to format log records.\n *\n * \n *\n * @param record The log record to format.\n * @returns The formatted log record.\n * @since 0.5.0\n */\nexport const ansiColorFormatter: TextFormatter = getAnsiColorFormatter();\n\n/**\n * Options for the {@link getJsonLinesFormatter} function.\n * @since 0.11.0\n */\nexport interface JsonLinesFormatterOptions {\n /**\n * The separator between category names. For example, if the separator is\n * `\".\"`, the category `[\"a\", \"b\", \"c\"]` will be formatted as `\"a.b.c\"`.\n * If this is a function, it will be called with the category array and\n * should return a string or an array of strings, which will be used\n * for rendering the category.\n *\n * @default `\".\"`\n */\n readonly categorySeparator?:\n | string\n | ((category: readonly string[]) => string | readonly string[]);\n\n /**\n * The message format. This can be one of the following:\n *\n * - `\"template\"`: The raw message template is used as the message.\n * - `\"rendered\"`: The message is rendered with the values.\n *\n * @default `\"rendered\"`\n */\n readonly message?: \"template\" | \"rendered\";\n\n /**\n * The properties format. This can be one of the following:\n *\n * - `\"flatten\"`: The properties are flattened into the root object.\n * - `\"prepend:<prefix>\"`: The properties are prepended with the given prefix\n * (e.g., `\"prepend:ctx_\"` will prepend `ctx_` to each property key).\n * - `\"nest:<key>\"`: The properties are nested under the given key\n * (e.g., `\"nest:properties\"` will nest the properties under the\n * `properties` key).\n *\n * @default `\"nest:properties\"`\n */\n readonly properties?: \"flatten\" | `prepend:${string}` | `nest:${string}`;\n}\n\n/**\n * Get a [JSON Lines] formatter with the specified options. The log records\n * will be rendered as JSON objects, one per line, which is a common format\n * for log files. This format is also known as Newline-Delimited JSON (NDJSON).\n * It looks like this:\n *\n * ```json\n * {\"@timestamp\":\"2023-11-14T22:13:20.000Z\",\"level\":\"INFO\",\"message\":\"Hello, world!\",\"logger\":\"my.logger\",\"properties\":{\"key\":\"value\"}}\n * ```\n *\n * [JSON Lines]: https://jsonlines.org/\n * @param options The options for the JSON Lines formatter.\n * @returns The JSON Lines formatter.\n * @since 0.11.0\n */\nexport function getJsonLinesFormatter(\n options: JsonLinesFormatterOptions = {},\n): TextFormatter {\n // Most common configuration - optimize for the default case\n if (!options.categorySeparator && !options.message && !options.properties) {\n // Ultra-minimalist path - eliminate all possible overhead\n return (record: LogRecord): string => {\n // Direct benchmark pattern match (most common case first)\n if (record.message.length === 3) {\n return JSON.stringify({\n \"@timestamp\": new Date(record.timestamp).toISOString(),\n level: record.level === \"warning\"\n ? \"WARN\"\n : record.level.toUpperCase(),\n message: record.message[0] + JSON.stringify(record.message[1]) +\n record.message[2],\n logger: record.category.join(\".\"),\n properties: record.properties,\n }) + \"\\n\";\n }\n\n // Single message (second most common)\n if (record.message.length === 1) {\n return JSON.stringify({\n \"@timestamp\": new Date(record.timestamp).toISOString(),\n level: record.level === \"warning\"\n ? \"WARN\"\n : record.level.toUpperCase(),\n message: record.message[0],\n logger: record.category.join(\".\"),\n properties: record.properties,\n }) + \"\\n\";\n }\n\n // Complex messages (fallback)\n let msg = record.message[0] as string;\n for (let i = 1; i < record.message.length; i++) {\n msg += (i & 1) ? JSON.stringify(record.message[i]) : record.message[i];\n }\n\n return JSON.stringify({\n \"@timestamp\": new Date(record.timestamp).toISOString(),\n level: record.level === \"warning\" ? \"WARN\" : record.level.toUpperCase(),\n message: msg,\n logger: record.category.join(\".\"),\n properties: record.properties,\n }) + \"\\n\";\n };\n }\n\n // Pre-compile configuration for non-default cases\n const isTemplateMessage = options.message === \"template\";\n const propertiesOption = options.properties ?? \"nest:properties\";\n\n // Pre-compile category joining strategy\n let joinCategory: (category: readonly string[]) => string | readonly string[];\n if (typeof options.categorySeparator === \"function\") {\n joinCategory = options.categorySeparator;\n } else {\n const separator = options.categorySeparator ?? \".\";\n joinCategory = (category: readonly string[]): string =>\n category.join(separator);\n }\n\n // Pre-compile properties handling strategy\n let getProperties: (\n properties: Record<string, unknown>,\n ) => Record<string, unknown>;\n\n if (propertiesOption === \"flatten\") {\n getProperties = (properties) => properties;\n } else if (propertiesOption.startsWith(\"prepend:\")) {\n const prefix = propertiesOption.substring(8);\n if (prefix === \"\") {\n throw new TypeError(\n `Invalid properties option: ${\n JSON.stringify(propertiesOption)\n }. It must be of the form \"prepend:<prefix>\" where <prefix> is a non-empty string.`,\n );\n }\n getProperties = (properties) => {\n const result: Record<string, unknown> = {};\n for (const key in properties) {\n result[`${prefix}${key}`] = properties[key];\n }\n return result;\n };\n } else if (propertiesOption.startsWith(\"nest:\")) {\n const key = propertiesOption.substring(5);\n getProperties = (properties) => ({ [key]: properties });\n } else {\n throw new TypeError(\n `Invalid properties option: ${\n JSON.stringify(propertiesOption)\n }. It must be \"flatten\", \"prepend:<prefix>\", or \"nest:<key>\".`,\n );\n }\n\n // Pre-compile message rendering function\n let getMessage: (record: LogRecord) => string;\n\n if (isTemplateMessage) {\n getMessage = (record: LogRecord): string => {\n if (typeof record.rawMessage === \"string\") {\n return record.rawMessage;\n }\n let msg = \"\";\n for (let i = 0; i < record.rawMessage.length; i++) {\n msg += i % 2 < 1 ? record.rawMessage[i] : \"{}\";\n }\n return msg;\n };\n } else {\n getMessage = (record: LogRecord): string => {\n const msgLen = record.message.length;\n\n if (msgLen === 1) {\n return record.message[0] as string;\n }\n\n let msg = \"\";\n for (let i = 0; i < msgLen; i++) {\n msg += (i % 2 < 1)\n ? record.message[i]\n : JSON.stringify(record.message[i]);\n }\n return msg;\n };\n }\n\n return (record: LogRecord): string => {\n return JSON.stringify({\n \"@timestamp\": new Date(record.timestamp).toISOString(),\n level: record.level === \"warning\" ? \"WARN\" : record.level.toUpperCase(),\n message: getMessage(record),\n logger: joinCategory(record.category),\n ...getProperties(record.properties),\n }) + \"\\n\";\n };\n}\n\n/**\n * The default [JSON Lines] formatter. This formatter formats log records\n * as JSON objects, one per line, which is a common format for log files.\n * It looks like this:\n *\n * ```json\n * {\"@timestamp\":\"2023-11-14T22:13:20.000Z\",\"level\":\"INFO\",\"message\":\"Hello, world!\",\"logger\":\"my.logger\",\"properties\":{\"key\":\"value\"}}\n * ```\n *\n * You can customize the output by passing options to\n * {@link getJsonLinesFormatter}. For example, you can change the category\n * separator, the message format, and how the properties are formatted.\n *\n * [JSON Lines]: https://jsonlines.org/\n * @since 0.11.0\n */\nexport const jsonLinesFormatter: TextFormatter = getJsonLinesFormatter();\n\n/**\n * A console formatter is a function that accepts a log record and returns\n * an array of arguments to pass to {@link console.log}.\n *\n * @param record The log record to format.\n * @returns The formatted log record, as an array of arguments for\n * {@link console.log}.\n */\nexport type ConsoleFormatter = (record: LogRecord) => readonly unknown[];\n\n/**\n * The styles for the log level in the console.\n */\nconst logLevelStyles: Record<LogLevel, string> = {\n \"trace\": \"background-color: gray; color: white;\",\n \"debug\": \"background-color: gray; color: white;\",\n \"info\": \"background-color: white; color: black;\",\n \"warning\": \"background-color: orange; color: black;\",\n \"error\": \"background-color: red; color: white;\",\n \"fatal\": \"background-color: maroon; color: white;\",\n};\n\n/**\n * The default console formatter.\n *\n * @param record The log record to format.\n * @returns The formatted log record, as an array of arguments for\n * {@link console.log}.\n */\nexport function defaultConsoleFormatter(record: LogRecord): readonly unknown[] {\n let msg = \"\";\n const values: unknown[] = [];\n for (let i = 0; i < record.message.length; i++) {\n if (i % 2 === 0) msg += record.message[i];\n else {\n msg += \"%o\";\n values.push(record.message[i]);\n }\n }\n const date = new Date(record.timestamp);\n const time = `${date.getUTCHours().toString().padStart(2, \"0\")}:${\n date.getUTCMinutes().toString().padStart(2, \"0\")\n }:${date.getUTCSeconds().toString().padStart(2, \"0\")}.${\n date.getUTCMilliseconds().toString().padStart(3, \"0\")\n }`;\n return [\n `%c${time} %c${levelAbbreviations[record.level]}%c %c${\n record.category.join(\"\\xb7\")\n } %c${msg}`,\n \"color: gray;\",\n logLevelStyles[record.level],\n \"background-color: default;\",\n \"color: gray;\",\n \"color: default;\",\n ...values,\n ];\n}\n"],"mappings":";;;;;;AAgBA,MAAMA,qBAA+C;CACnD,SAAS;CACT,SAAS;CACT,QAAQ;CACR,WAAW;CACX,SAAS;CACT,SAAS;AACV;;;;;;;;;;;AAYD,MAAMC,iBAGG,aAAa,sBAGX,cAAc,eAAe,UAAU,YAAY,gBACxD,CAAC,MAAM,KAAK,UAAU,EAAE,GAGxB,UAAU,cAAc,aAAa,WAAW,eAGvC,WAAW,KAAK,YAAY,aACrC,CAAC,GAAG,SAGJ,WAAW,KAAK,QAAQ,GAAG;CACzB,mBAAmB;CACnB,eAAe;CACf,GAAG;AACJ,EAAC,GAGF,QAAQ,QAAQ,aAAa,eAAe,KAAK,YAAY,aAC7D,CAAC,GAAG,SAGJ,KAAK,QAAQ,GAAG;CACd,gBAAgB;CAChB,iBAAiB;CACjB,GAAG;AACJ,EAAC,GACF,CAAC,MAAM,KAAK,UAAU,EAAE;AAgJ9B,SAAS,QAAQC,KAAqB;AACpC,QAAO,MAAM,MAAM,GAAG,IAAI,KAAK,EAAE,IAAI;AACtC;AAED,SAAS,SAASA,KAAqB;AACrC,QAAO,MAAM,MAAM,IAAI,IAAI,IAAI,MAAM,OAAO,GAAG,IAAI,KAAK,EAAE,IAAI;AAC/D;AAGD,MAAM,sBAAsB;CAC1B,sBAAsB,CAACC,OAAuB;EAC5C,MAAM,IAAI,IAAI,KAAK;EACnB,MAAM,OAAO,EAAE,gBAAgB;EAC/B,MAAM,QAAQ,QAAQ,EAAE,aAAa,GAAG,EAAE;EAC1C,MAAM,MAAM,QAAQ,EAAE,YAAY,CAAC;EACnC,MAAM,OAAO,QAAQ,EAAE,aAAa,CAAC;EACrC,MAAM,SAAS,QAAQ,EAAE,eAAe,CAAC;EACzC,MAAM,SAAS,QAAQ,EAAE,eAAe,CAAC;EACzC,MAAM,KAAK,SAAS,EAAE,oBAAoB,CAAC;AAC3C,UAAQ,EAAE,KAAK,GAAG,MAAM,GAAG,IAAI,GAAG,KAAK,GAAG,OAAO,GAAG,OAAO,GAAG,GAAG;CAClE;CACD,gBAAgB,CAACA,OAAuB;EACtC,MAAM,IAAI,IAAI,KAAK;EACnB,MAAM,OAAO,EAAE,gBAAgB;EAC/B,MAAM,QAAQ,QAAQ,EAAE,aAAa,GAAG,EAAE;EAC1C,MAAM,MAAM,QAAQ,EAAE,YAAY,CAAC;EACnC,MAAM,OAAO,QAAQ,EAAE,aAAa,CAAC;EACrC,MAAM,SAAS,QAAQ,EAAE,eAAe,CAAC;EACzC,MAAM,SAAS,QAAQ,EAAE,eAAe,CAAC;EACzC,MAAM,KAAK,SAAS,EAAE,oBAAoB,CAAC;AAC3C,UAAQ,EAAE,KAAK,GAAG,MAAM,GAAG,IAAI,GAAG,KAAK,GAAG,OAAO,GAAG,OAAO,GAAG,GAAG;CAClE;CACD,aAAa,CAACA,OAAuB;EACnC,MAAM,IAAI,IAAI,KAAK;EACnB,MAAM,OAAO,EAAE,gBAAgB;EAC/B,MAAM,QAAQ,QAAQ,EAAE,aAAa,GAAG,EAAE;EAC1C,MAAM,MAAM,QAAQ,EAAE,YAAY,CAAC;EACnC,MAAM,OAAO,QAAQ,EAAE,aAAa,CAAC;EACrC,MAAM,SAAS,QAAQ,EAAE,eAAe,CAAC;EACzC,MAAM,SAAS,QAAQ,EAAE,eAAe,CAAC;EACzC,MAAM,KAAK,SAAS,EAAE,oBAAoB,CAAC;AAC3C,UAAQ,EAAE,KAAK,GAAG,MAAM,GAAG,IAAI,GAAG,KAAK,GAAG,OAAO,GAAG,OAAO,GAAG,GAAG;CAClE;CACD,iBAAiB,CAACA,OAAuB;EACvC,MAAM,IAAI,IAAI,KAAK;EACnB,MAAM,OAAO,QAAQ,EAAE,aAAa,CAAC;EACrC,MAAM,SAAS,QAAQ,EAAE,eAAe,CAAC;EACzC,MAAM,SAAS,QAAQ,EAAE,eAAe,CAAC;EACzC,MAAM,KAAK,SAAS,EAAE,oBAAoB,CAAC;AAC3C,UAAQ,EAAE,KAAK,GAAG,OAAO,GAAG,OAAO,GAAG,GAAG;CAC1C;CACD,WAAW,CAACA,OAAuB;EACjC,MAAM,IAAI,IAAI,KAAK;EACnB,MAAM,OAAO,QAAQ,EAAE,aAAa,CAAC;EACrC,MAAM,SAAS,QAAQ,EAAE,eAAe,CAAC;EACzC,MAAM,SAAS,QAAQ,EAAE,eAAe,CAAC;EACzC,MAAM,KAAK,SAAS,EAAE,oBAAoB,CAAC;AAC3C,UAAQ,EAAE,KAAK,GAAG,OAAO,GAAG,OAAO,GAAG,GAAG;CAC1C;CACD,QAAQ,CAACA,OAAuB;EAC9B,MAAM,IAAI,IAAI,KAAK;EACnB,MAAM,OAAO,QAAQ,EAAE,aAAa,CAAC;EACrC,MAAM,SAAS,QAAQ,EAAE,eAAe,CAAC;EACzC,MAAM,SAAS,QAAQ,EAAE,eAAe,CAAC;EACzC,MAAM,KAAK,SAAS,EAAE,oBAAoB,CAAC;AAC3C,UAAQ,EAAE,KAAK,GAAG,OAAO,GAAG,OAAO,GAAG,GAAG;CAC1C;CACD,QAAQ,CAACA,OAAuB;EAC9B,MAAM,IAAI,IAAI,KAAK;EACnB,MAAM,OAAO,EAAE,gBAAgB;EAC/B,MAAM,QAAQ,QAAQ,EAAE,aAAa,GAAG,EAAE;EAC1C,MAAM,MAAM,QAAQ,EAAE,YAAY,CAAC;AACnC,UAAQ,EAAE,KAAK,GAAG,MAAM,GAAG,IAAI;CAChC;CACD,WAAW,CAACA,OAAuB,IAAI,KAAK,IAAI,aAAa;CAC7D,QAAQ,MAAY;AACrB;AAGD,MAAM,sBAAsB;CAC1B,MAAM;CACN,MAAM;EACJ,OAAO;EACP,OAAO;EACP,MAAM;EACN,SAAS;EACT,OAAO;EACP,OAAO;CACR;CACD,MAAM;EACJ,OAAO;EACP,OAAO;EACP,MAAM;EACN,SAAS;EACT,OAAO;EACP,OAAO;CACR;CACD,MAAM;EACJ,OAAO;EACP,OAAO;EACP,MAAM;EACN,SAAS;EACT,OAAO;EACP,OAAO;CACR;CACD,GAAG;EACD,OAAO;EACP,OAAO;EACP,MAAM;EACN,SAAS;EACT,OAAO;EACP,OAAO;CACR;CACD,GAAG;EACD,OAAO;EACP,OAAO;EACP,MAAM;EACN,SAAS;EACT,OAAO;EACP,OAAO;CACR;AACF;;;;;;;;;;;;;;;;;;AAmBD,SAAgB,iBACdC,UAAgC,CAAE,GACnB;CAEf,MAAM,oBAAoB,CAAC,MAAM;EAC/B,MAAM,WAAW,QAAQ;AACzB,MAAI,YAAY,KACd,QAAO,oBAAoB;WAClB,aAAa,WACtB,QAAO,oBAAoB;kBAEpB,aAAa,YAAY,YAAY,oBAE5C,QAAO,oBAAoB;MAE3B,QAAO;CAEV,IAAG;CAEJ,MAAM,oBAAoB,QAAQ,YAAY;CAC9C,MAAM,gBAAgB,QAAQ,SAAS;CAGvC,MAAM,gBAAgB,CAAC,MAAM;EAC3B,MAAM,cAAc,QAAQ;AAC5B,MAAI,eAAe,QAAQ,gBAAgB,OACzC,QAAO,CAACC,UAA4B,oBAAoB,KAAK;WACpD,gBAAgB,OACzB,QAAO,CAACA,UAA4B,oBAAoB,KAAK;WACpD,gBAAgB,OACzB,QAAO,CAACA,UAA4B,oBAAoB,KAAK;WACpD,gBAAgB,OACzB,QAAO,CAACA,UAA4B,oBAAoB,KAAK;WACpD,gBAAgB,IACzB,QAAO,CAACA,UAA4B,oBAAoB,EAAE;WACjD,gBAAgB,IACzB,QAAO,CAACA,UAA4B,oBAAoB,EAAE;MAE1D,QAAO;CAEV,IAAG;CAEJ,MAAMC,YAAiD,QAAQ,WAC5D,CAAC,EAAE,WAAW,OAAO,UAAU,SAA0B,MACvD,EAAE,aAAa,EAAE,UAAU,KAAK,GAAG,GAAG,MAAM,IAAI,SAAS,IAAI,QAAQ;AAE1E,QAAO,CAACC,WAA8B;EAEpC,MAAM,WAAW,OAAO;EACxB,MAAM,SAAS,SAAS;EAExB,IAAIC;AACJ,MAAI,WAAW,EAEb,WAAU,SAAS;WACV,UAAU,GAAG;AAEtB,aAAU;AACV,QAAK,IAAI,IAAI,GAAG,IAAI,QAAQ,IAC1B,YAAY,IAAI,MAAM,IAAK,SAAS,KAAK,cAAc,SAAS,GAAG;EAEtE,OAAM;GAEL,MAAMC,QAAkB,IAAI,MAAM;AAClC,QAAK,IAAI,IAAI,GAAG,IAAI,QAAQ,IAC1B,OAAM,KAAM,IAAI,MAAM,IAClB,SAAS,KACT,cAAc,SAAS,GAAG;AAEhC,aAAU,MAAM,KAAK,GAAG;EACzB;EAED,MAAM,YAAY,kBAAkB,OAAO,UAAU;EACrD,MAAM,QAAQ,cAAc,OAAO,MAAM;EACzC,MAAM,kBAAkB,sBAAsB,aAC1C,kBAAkB,OAAO,SAAS,GAClC,OAAO,SAAS,KAAK,kBAAkB;EAE3C,MAAMC,SAA0B;GAC9B;GACA;GACA;GACA;GACA;EACD;AACD,UAAQ,EAAE,UAAU,OAAO,CAAC;CAC7B;AACF;;;;;;;;;;;AAYD,MAAaC,uBAAsC,kBAAkB;AAErE,MAAM,QAAQ;AAgBd,MAAMC,aAAwC;CAC5C,OAAO;CACP,KAAK;CACL,OAAO;CACP,QAAQ;CACR,MAAM;CACN,SAAS;CACT,MAAM;CACN,OAAO;AACR;AAaD,MAAMC,aAAwC;CAC5C,MAAM;CACN,KAAK;CACL,QAAQ;CACR,WAAW;CACX,eAAe;AAChB;AAED,MAAMC,qBAAyD;CAC7D,OAAO;CACP,OAAO;CACP,MAAM;CACN,SAAS;CACT,OAAO;CACP,OAAO;AACR;;;;;;;;;AAyFD,SAAgB,sBACdC,UAAqC,CAAE,GACxB;CACf,MAAM,SAAS,QAAQ;CACvB,MAAM,wBAAwB,QAAQ,mBAAmB,cACrD,QACA,QAAQ;CACZ,MAAM,iBAAiB,QAAQ,kBAAkB;CACjD,MAAM,mBAAmB,EACvB,kBAAkB,OAAO,KAAK,WAAW,gBAC1C,EAAE,kBAAkB,OAAO,KAAK,WAAW,gBAAgB;CAC5D,MAAM,kBAAkB,kBAAkB,QAAQ,kBAAkB,OAChE,KACA;CACJ,MAAM,oBAAoB,QAAQ,eAAe,cAC7C,SACA,QAAQ;CACZ,MAAM,cAAc,QAAQ,eAAe;CAC3C,MAAM,uBAAuB,QAAQ,kBAAkB,cACnD,QACA,QAAQ;CACZ,MAAM,gBAAgB,QAAQ,iBAAiB;CAC/C,MAAM,kBAAkB,EACtB,iBAAiB,OAAO,KAAK,WAAW,eACzC,EAAE,iBAAiB,OAAO,KAAK,WAAW,eAAe;CAC1D,MAAM,iBAAiB,iBAAiB,QAAQ,iBAAiB,OAC7D,KACA;AACJ,QAAO,iBAAiB;EACtB,WAAW;EACX,MAAMC,OAAwB;AAC5B,UAAO,QAAQ,OAAO,EAAE,QAAQ,KAAM,EAAC;EACxC;EACD,GAAG;EACH,OAAO,EAAE,WAAW,OAAO,UAAU,SAAS,QAAQ,EAAU;GAC9D,MAAM,aAAa,YAAY,OAAO;AACtC,gBAAa,EAAE,gBAAgB,EAAE,UAAU,EAAE,gBAAgB;AAC7D,YAAS,EAAE,cAAc,OAAO,KAAK,WAAW,YAAY,EAC1D,cAAc,OAAO,KAAK,WAAW,YACtC,EAAE,MAAM,EAAE,cAAc,QAAQ,cAAc,OAAO,KAAK,MAAM;AACjE,UAAO,UAAU,QACZ,EAAE,UAAU,GAAG,MAAM,GAAG,eAAe,EAAE,SAAS,GAAG,eAAe,GAAG,QAAQ,IAChF,OAAO;IACP;IACA;IACA,WAAW,EAAE,eAAe,EAAE,SAAS,EAAE,eAAe;IACxD;IACA;GACD,EAAC;EACL;CACF,EAAC;AACH;;;;;;;;;;AAWD,MAAaC,qBAAoC,uBAAuB;;;;;;;;;;;;;;;;AA4DxE,SAAgB,sBACdC,UAAqC,CAAE,GACxB;AAEf,MAAK,QAAQ,sBAAsB,QAAQ,YAAY,QAAQ,WAE7D,QAAO,CAACX,WAA8B;AAEpC,MAAI,OAAO,QAAQ,WAAW,EAC5B,QAAO,KAAK,UAAU;GACpB,cAAc,IAAI,KAAK,OAAO,WAAW,aAAa;GACtD,OAAO,OAAO,UAAU,YACpB,SACA,OAAO,MAAM,aAAa;GAC9B,SAAS,OAAO,QAAQ,KAAK,KAAK,UAAU,OAAO,QAAQ,GAAG,GAC5D,OAAO,QAAQ;GACjB,QAAQ,OAAO,SAAS,KAAK,IAAI;GACjC,YAAY,OAAO;EACpB,EAAC,GAAG;AAIP,MAAI,OAAO,QAAQ,WAAW,EAC5B,QAAO,KAAK,UAAU;GACpB,cAAc,IAAI,KAAK,OAAO,WAAW,aAAa;GACtD,OAAO,OAAO,UAAU,YACpB,SACA,OAAO,MAAM,aAAa;GAC9B,SAAS,OAAO,QAAQ;GACxB,QAAQ,OAAO,SAAS,KAAK,IAAI;GACjC,YAAY,OAAO;EACpB,EAAC,GAAG;EAIP,IAAI,MAAM,OAAO,QAAQ;AACzB,OAAK,IAAI,IAAI,GAAG,IAAI,OAAO,QAAQ,QAAQ,IACzC,QAAQ,IAAI,IAAK,KAAK,UAAU,OAAO,QAAQ,GAAG,GAAG,OAAO,QAAQ;AAGtE,SAAO,KAAK,UAAU;GACpB,cAAc,IAAI,KAAK,OAAO,WAAW,aAAa;GACtD,OAAO,OAAO,UAAU,YAAY,SAAS,OAAO,MAAM,aAAa;GACvE,SAAS;GACT,QAAQ,OAAO,SAAS,KAAK,IAAI;GACjC,YAAY,OAAO;EACpB,EAAC,GAAG;CACN;CAIH,MAAM,oBAAoB,QAAQ,YAAY;CAC9C,MAAM,mBAAmB,QAAQ,cAAc;CAG/C,IAAIY;AACJ,YAAW,QAAQ,sBAAsB,WACvC,gBAAe,QAAQ;MAClB;EACL,MAAM,YAAY,QAAQ,qBAAqB;AAC/C,iBAAe,CAACC,aACd,SAAS,KAAK,UAAU;CAC3B;CAGD,IAAIC;AAIJ,KAAI,qBAAqB,UACvB,iBAAgB,CAAC,eAAe;UACvB,iBAAiB,WAAW,WAAW,EAAE;EAClD,MAAM,SAAS,iBAAiB,UAAU,EAAE;AAC5C,MAAI,WAAW,GACb,OAAM,IAAI,WACP,6BACC,KAAK,UAAU,iBAAiB,CACjC;AAGL,kBAAgB,CAAC,eAAe;GAC9B,MAAMC,SAAkC,CAAE;AAC1C,QAAK,MAAM,OAAO,WAChB,SAAQ,EAAE,OAAO,EAAE,IAAI,KAAK,WAAW;AAEzC,UAAO;EACR;CACF,WAAU,iBAAiB,WAAW,QAAQ,EAAE;EAC/C,MAAM,MAAM,iBAAiB,UAAU,EAAE;AACzC,kBAAgB,CAAC,gBAAgB,GAAG,MAAM,WAAY;CACvD,MACC,OAAM,IAAI,WACP,6BACC,KAAK,UAAU,iBAAiB,CACjC;CAKL,IAAIC;AAEJ,KAAI,kBACF,cAAa,CAAChB,WAA8B;AAC1C,aAAW,OAAO,eAAe,SAC/B,QAAO,OAAO;EAEhB,IAAI,MAAM;AACV,OAAK,IAAI,IAAI,GAAG,IAAI,OAAO,WAAW,QAAQ,IAC5C,QAAO,IAAI,IAAI,IAAI,OAAO,WAAW,KAAK;AAE5C,SAAO;CACR;KAED,cAAa,CAACA,WAA8B;EAC1C,MAAM,SAAS,OAAO,QAAQ;AAE9B,MAAI,WAAW,EACb,QAAO,OAAO,QAAQ;EAGxB,IAAI,MAAM;AACV,OAAK,IAAI,IAAI,GAAG,IAAI,QAAQ,IAC1B,QAAQ,IAAI,IAAI,IACZ,OAAO,QAAQ,KACf,KAAK,UAAU,OAAO,QAAQ,GAAG;AAEvC,SAAO;CACR;AAGH,QAAO,CAACA,WAA8B;AACpC,SAAO,KAAK,UAAU;GACpB,cAAc,IAAI,KAAK,OAAO,WAAW,aAAa;GACtD,OAAO,OAAO,UAAU,YAAY,SAAS,OAAO,MAAM,aAAa;GACvE,SAAS,WAAW,OAAO;GAC3B,QAAQ,aAAa,OAAO,SAAS;GACrC,GAAG,cAAc,OAAO,WAAW;EACpC,EAAC,GAAG;CACN;AACF;;;;;;;;;;;;;;;;;AAkBD,MAAaiB,qBAAoC,uBAAuB;;;;AAexE,MAAMC,iBAA2C;CAC/C,SAAS;CACT,SAAS;CACT,QAAQ;CACR,WAAW;CACX,SAAS;CACT,SAAS;AACV;;;;;;;;AASD,SAAgB,wBAAwBlB,QAAuC;CAC7E,IAAI,MAAM;CACV,MAAMmB,SAAoB,CAAE;AAC5B,MAAK,IAAI,IAAI,GAAG,IAAI,OAAO,QAAQ,QAAQ,IACzC,KAAI,IAAI,MAAM,EAAG,QAAO,OAAO,QAAQ;MAClC;AACH,SAAO;AACP,SAAO,KAAK,OAAO,QAAQ,GAAG;CAC/B;CAEH,MAAM,OAAO,IAAI,KAAK,OAAO;CAC7B,MAAM,QAAQ,EAAE,KAAK,aAAa,CAAC,UAAU,CAAC,SAAS,GAAG,IAAI,CAAC,GAC7D,KAAK,eAAe,CAAC,UAAU,CAAC,SAAS,GAAG,IAAI,CACjD,GAAG,KAAK,eAAe,CAAC,UAAU,CAAC,SAAS,GAAG,IAAI,CAAC,GACnD,KAAK,oBAAoB,CAAC,UAAU,CAAC,SAAS,GAAG,IAAI,CACtD;AACD,QAAO;GACJ,IAAI,KAAK,KAAK,mBAAmB,OAAO,OAAO,OAC9C,OAAO,SAAS,KAAK,IAAO,CAC7B,KAAK,IAAI;EACV;EACA,eAAe,OAAO;EACtB;EACA;EACA;EACA,GAAG;CACJ;AACF"}
|
|
1
|
+
{"version":3,"file":"formatter.js","names":["levelAbbreviations: Record<LogLevel, string>","inspect: (value: unknown, options?: { colors?: boolean }) => string","num: number","ts: number","options: TextFormatterOptions","v: unknown","level: LogLevel","formatter: (values: FormattedValues) => string","record: LogRecord","message: string","parts: string[]","values: FormattedValues","defaultTextFormatter: TextFormatter","ansiColors: Record<AnsiColor, string>","ansiStyles: Record<AnsiStyle, string>","defaultLevelColors: Record<LogLevel, AnsiColor | null>","options: AnsiColorFormatterOptions","value: unknown","ansiColorFormatter: TextFormatter","options: JsonLinesFormatterOptions","joinCategory: (category: readonly string[]) => string | readonly string[]","category: readonly string[]","getProperties: (\n properties: Record<string, unknown>,\n ) => Record<string, unknown>","result: Record<string, unknown>","getMessage: (record: LogRecord) => string","jsonLinesFormatter: TextFormatter","logLevelStyles: Record<LogLevel, string>","values: unknown[]"],"sources":["../src/formatter.ts"],"sourcesContent":["import * as util from \"#util\";\nimport type { LogLevel } from \"./level.ts\";\nimport type { LogRecord } from \"./record.ts\";\n\n/**\n * A text formatter is a function that accepts a log record and returns\n * a string.\n *\n * @param record The log record to format.\n * @returns The formatted log record.\n */\nexport type TextFormatter = (record: LogRecord) => string;\n\n/**\n * The severity level abbreviations.\n */\nconst levelAbbreviations: Record<LogLevel, string> = {\n \"trace\": \"TRC\",\n \"debug\": \"DBG\",\n \"info\": \"INF\",\n \"warning\": \"WRN\",\n \"error\": \"ERR\",\n \"fatal\": \"FTL\",\n};\n\n/**\n * A platform-specific inspect function. In Deno, this is {@link Deno.inspect},\n * and in Node.js/Bun it is `util.inspect()`. If neither is available, it\n * falls back to {@link JSON.stringify}.\n *\n * @param value The value to inspect.\n * @param options The options for inspecting the value.\n * If `colors` is `true`, the output will be ANSI-colored.\n * @returns The string representation of the value.\n */\nconst inspect: (value: unknown, options?: { colors?: boolean }) => string =\n // @ts-ignore: Browser detection\n // dnt-shim-ignore\n typeof document !== \"undefined\" ||\n // @ts-ignore: React Native detection\n // dnt-shim-ignore\n typeof navigator !== \"undefined\" && navigator.product === \"ReactNative\"\n ? (v) => JSON.stringify(v)\n // @ts-ignore: Deno global\n // dnt-shim-ignore\n : \"Deno\" in globalThis && \"inspect\" in globalThis.Deno &&\n // @ts-ignore: Deno global\n // dnt-shim-ignore\n typeof globalThis.Deno.inspect === \"function\"\n ? (v, opts) =>\n // @ts-ignore: Deno global\n // dnt-shim-ignore\n globalThis.Deno.inspect(v, {\n strAbbreviateSize: Infinity,\n iterableLimit: Infinity,\n ...opts,\n })\n // @ts-ignore: Node.js global\n // dnt-shim-ignore\n : util != null && \"inspect\" in util && typeof util.inspect === \"function\"\n ? (v, opts) =>\n // @ts-ignore: Node.js global\n // dnt-shim-ignore\n util.inspect(v, {\n maxArrayLength: Infinity,\n maxStringLength: Infinity,\n ...opts,\n })\n : (v) => JSON.stringify(v);\n\n/**\n * The formatted values for a log record.\n * @since 0.6.0\n */\nexport interface FormattedValues {\n /**\n * The formatted timestamp.\n */\n timestamp: string | null;\n\n /**\n * The formatted log level.\n */\n level: string;\n\n /**\n * The formatted category.\n */\n category: string;\n\n /**\n * The formatted message.\n */\n message: string;\n\n /**\n * The unformatted log record.\n */\n record: LogRecord;\n}\n\n/**\n * The various options for the built-in text formatters.\n * @since 0.6.0\n */\nexport interface TextFormatterOptions {\n /**\n * The timestamp format. This can be one of the following:\n *\n * - `\"date-time-timezone\"`: The date and time with the full timezone offset\n * (e.g., `\"2023-11-14 22:13:20.000 +00:00\"`).\n * - `\"date-time-tz\"`: The date and time with the short timezone offset\n * (e.g., `\"2023-11-14 22:13:20.000 +00\"`).\n * - `\"date-time\"`: The date and time without the timezone offset\n * (e.g., `\"2023-11-14 22:13:20.000\"`).\n * - `\"time-timezone\"`: The time with the full timezone offset but without\n * the date (e.g., `\"22:13:20.000 +00:00\"`).\n * - `\"time-tz\"`: The time with the short timezone offset but without the date\n * (e.g., `\"22:13:20.000 +00\"`).\n * - `\"time\"`: The time without the date or timezone offset\n * (e.g., `\"22:13:20.000\"`).\n * - `\"date\"`: The date without the time or timezone offset\n * (e.g., `\"2023-11-14\"`).\n * - `\"rfc3339\"`: The date and time in RFC 3339 format\n * (e.g., `\"2023-11-14T22:13:20.000Z\"`).\n * - `\"none\"` or `\"disabled\"`: No display\n *\n * Alternatively, this can be a function that accepts a timestamp and returns\n * a string.\n *\n * The default is `\"date-time-timezone\"`.\n */\n timestamp?:\n | \"date-time-timezone\"\n | \"date-time-tz\"\n | \"date-time\"\n | \"time-timezone\"\n | \"time-tz\"\n | \"time\"\n | \"date\"\n | \"rfc3339\"\n | \"none\"\n | \"disabled\"\n | ((ts: number) => string | null);\n\n /**\n * The log level format. This can be one of the following:\n *\n * - `\"ABBR\"`: The log level abbreviation in uppercase (e.g., `\"INF\"`).\n * - `\"FULL\"`: The full log level name in uppercase (e.g., `\"INFO\"`).\n * - `\"L\"`: The first letter of the log level in uppercase (e.g., `\"I\"`).\n * - `\"abbr\"`: The log level abbreviation in lowercase (e.g., `\"inf\"`).\n * - `\"full\"`: The full log level name in lowercase (e.g., `\"info\"`).\n * - `\"l\"`: The first letter of the log level in lowercase (e.g., `\"i\"`).\n *\n * Alternatively, this can be a function that accepts a log level and returns\n * a string.\n *\n * The default is `\"ABBR\"`.\n */\n level?:\n | \"ABBR\"\n | \"FULL\"\n | \"L\"\n | \"abbr\"\n | \"full\"\n | \"l\"\n | ((level: LogLevel) => string);\n\n /**\n * The separator between category names. For example, if the separator is\n * `\"·\"`, the category `[\"a\", \"b\", \"c\"]` will be formatted as `\"a·b·c\"`.\n * The default separator is `\"·\"`.\n *\n * If this is a function, it will be called with the category array and\n * should return a string, which will be used for rendering the category.\n */\n category?: string | ((category: readonly string[]) => string);\n\n /**\n * The format of the embedded values.\n *\n * A function that renders a value to a string. This function is used to\n * render the values in the log record. The default is a cross-runtime\n * `inspect()` function that uses [`util.inspect()`] in Node.js/Bun,\n * [`Deno.inspect()`] in Deno, or falls back to {@link JSON.stringify} in\n * browsers.\n *\n * The second parameter provides access to the default cross-runtime\n * `inspect()` function, allowing you to fall back to the default behavior\n * for certain values while customizing others. You can ignore this\n * parameter if you don't need the fallback functionality.\n *\n * [`util.inspect()`]: https://nodejs.org/api/util.html#utilinspectobject-options\n * [`Deno.inspect()`]: https://docs.deno.com/api/deno/~/Deno.inspect\n * @param value The value to render.\n * @param inspect The default cross-runtime inspect function that can be used\n * as a fallback. Accepts an optional `options` parameter\n * with a `colors` boolean field.\n * @returns The string representation of the value.\n * @example\n * ```typescript\n * getTextFormatter({\n * value(value, inspect) {\n * // Custom formatting for numbers\n * if (typeof value === 'number') {\n * return value.toFixed(2);\n * }\n * // Fall back to default for everything else\n * return inspect(value);\n * }\n * })\n * ```\n */\n value?: (\n value: unknown,\n inspect: (value: unknown, options?: { colors?: boolean }) => string,\n ) => string;\n\n /**\n * How those formatted parts are concatenated.\n *\n * A function that formats the log record. This function is called with the\n * formatted values and should return a string. Note that the formatted\n * *should not* include a newline character at the end.\n *\n * By default, this is a function that formats the log record as follows:\n *\n * ```\n * 2023-11-14 22:13:20.000 +00:00 [INF] category·subcategory: Hello, world!\n * ```\n * @param values The formatted values.\n * @returns The formatted log record.\n */\n format?: (values: FormattedValues) => string;\n}\n\n// Optimized helper functions for timestamp formatting\nfunction padZero(num: number): string {\n return num < 10 ? `0${num}` : `${num}`;\n}\n\nfunction padThree(num: number): string {\n return num < 10 ? `00${num}` : num < 100 ? `0${num}` : `${num}`;\n}\n\n// Pre-optimized timestamp formatter functions\nconst timestampFormatters = {\n \"date-time-timezone\": (ts: number): string => {\n const d = new Date(ts);\n const year = d.getUTCFullYear();\n const month = padZero(d.getUTCMonth() + 1);\n const day = padZero(d.getUTCDate());\n const hour = padZero(d.getUTCHours());\n const minute = padZero(d.getUTCMinutes());\n const second = padZero(d.getUTCSeconds());\n const ms = padThree(d.getUTCMilliseconds());\n return `${year}-${month}-${day} ${hour}:${minute}:${second}.${ms} +00:00`;\n },\n \"date-time-tz\": (ts: number): string => {\n const d = new Date(ts);\n const year = d.getUTCFullYear();\n const month = padZero(d.getUTCMonth() + 1);\n const day = padZero(d.getUTCDate());\n const hour = padZero(d.getUTCHours());\n const minute = padZero(d.getUTCMinutes());\n const second = padZero(d.getUTCSeconds());\n const ms = padThree(d.getUTCMilliseconds());\n return `${year}-${month}-${day} ${hour}:${minute}:${second}.${ms} +00`;\n },\n \"date-time\": (ts: number): string => {\n const d = new Date(ts);\n const year = d.getUTCFullYear();\n const month = padZero(d.getUTCMonth() + 1);\n const day = padZero(d.getUTCDate());\n const hour = padZero(d.getUTCHours());\n const minute = padZero(d.getUTCMinutes());\n const second = padZero(d.getUTCSeconds());\n const ms = padThree(d.getUTCMilliseconds());\n return `${year}-${month}-${day} ${hour}:${minute}:${second}.${ms}`;\n },\n \"time-timezone\": (ts: number): string => {\n const d = new Date(ts);\n const hour = padZero(d.getUTCHours());\n const minute = padZero(d.getUTCMinutes());\n const second = padZero(d.getUTCSeconds());\n const ms = padThree(d.getUTCMilliseconds());\n return `${hour}:${minute}:${second}.${ms} +00:00`;\n },\n \"time-tz\": (ts: number): string => {\n const d = new Date(ts);\n const hour = padZero(d.getUTCHours());\n const minute = padZero(d.getUTCMinutes());\n const second = padZero(d.getUTCSeconds());\n const ms = padThree(d.getUTCMilliseconds());\n return `${hour}:${minute}:${second}.${ms} +00`;\n },\n \"time\": (ts: number): string => {\n const d = new Date(ts);\n const hour = padZero(d.getUTCHours());\n const minute = padZero(d.getUTCMinutes());\n const second = padZero(d.getUTCSeconds());\n const ms = padThree(d.getUTCMilliseconds());\n return `${hour}:${minute}:${second}.${ms}`;\n },\n \"date\": (ts: number): string => {\n const d = new Date(ts);\n const year = d.getUTCFullYear();\n const month = padZero(d.getUTCMonth() + 1);\n const day = padZero(d.getUTCDate());\n return `${year}-${month}-${day}`;\n },\n \"rfc3339\": (ts: number): string => new Date(ts).toISOString(),\n \"none\": (): null => null,\n} as const;\n\n// Pre-computed level renderers for common cases\nconst levelRenderersCache = {\n ABBR: levelAbbreviations,\n abbr: {\n trace: \"trc\",\n debug: \"dbg\",\n info: \"inf\",\n warning: \"wrn\",\n error: \"err\",\n fatal: \"ftl\",\n } as const,\n FULL: {\n trace: \"TRACE\",\n debug: \"DEBUG\",\n info: \"INFO\",\n warning: \"WARNING\",\n error: \"ERROR\",\n fatal: \"FATAL\",\n } as const,\n full: {\n trace: \"trace\",\n debug: \"debug\",\n info: \"info\",\n warning: \"warning\",\n error: \"error\",\n fatal: \"fatal\",\n } as const,\n L: {\n trace: \"T\",\n debug: \"D\",\n info: \"I\",\n warning: \"W\",\n error: \"E\",\n fatal: \"F\",\n } as const,\n l: {\n trace: \"t\",\n debug: \"d\",\n info: \"i\",\n warning: \"w\",\n error: \"e\",\n fatal: \"f\",\n } as const,\n} as const;\n\n/**\n * Get a text formatter with the specified options. Although it's flexible\n * enough to create a custom formatter, if you want more control, you can\n * create a custom formatter that satisfies the {@link TextFormatter} type\n * instead.\n *\n * For more information on the options, see {@link TextFormatterOptions}.\n *\n * By default, the formatter formats log records as follows:\n *\n * ```\n * 2023-11-14 22:13:20.000 +00:00 [INF] category·subcategory: Hello, world!\n * ```\n * @param options The options for the text formatter.\n * @returns The text formatter.\n * @since 0.6.0\n */\nexport function getTextFormatter(\n options: TextFormatterOptions = {},\n): TextFormatter {\n // Pre-compute timestamp formatter with optimized lookup\n const timestampRenderer = (() => {\n const tsOption = options.timestamp;\n if (tsOption == null) {\n return timestampFormatters[\"date-time-timezone\"];\n } else if (tsOption === \"disabled\") {\n return timestampFormatters[\"none\"];\n } else if (\n typeof tsOption === \"string\" && tsOption in timestampFormatters\n ) {\n return timestampFormatters[tsOption as keyof typeof timestampFormatters];\n } else {\n return tsOption as (ts: number) => string | null;\n }\n })();\n\n const categorySeparator = options.category ?? \"·\";\n const valueRenderer = options.value\n ? (v: unknown) => options.value!(v, inspect)\n : inspect;\n\n // Pre-compute level renderer for better performance\n const levelRenderer = (() => {\n const levelOption = options.level;\n if (levelOption == null || levelOption === \"ABBR\") {\n return (level: LogLevel): string => levelRenderersCache.ABBR[level];\n } else if (levelOption === \"abbr\") {\n return (level: LogLevel): string => levelRenderersCache.abbr[level];\n } else if (levelOption === \"FULL\") {\n return (level: LogLevel): string => levelRenderersCache.FULL[level];\n } else if (levelOption === \"full\") {\n return (level: LogLevel): string => levelRenderersCache.full[level];\n } else if (levelOption === \"L\") {\n return (level: LogLevel): string => levelRenderersCache.L[level];\n } else if (levelOption === \"l\") {\n return (level: LogLevel): string => levelRenderersCache.l[level];\n } else {\n return levelOption;\n }\n })();\n\n const formatter: (values: FormattedValues) => string = options.format ??\n (({ timestamp, level, category, message }: FormattedValues) =>\n `${timestamp ? `${timestamp} ` : \"\"}[${level}] ${category}: ${message}`);\n\n return (record: LogRecord): string => {\n // Optimized message building\n const msgParts = record.message;\n const msgLen = msgParts.length;\n\n let message: string;\n if (msgLen === 1) {\n // Fast path for simple messages with no interpolation\n message = msgParts[0] as string;\n } else if (msgLen <= 6) {\n // Fast path for small messages - direct concatenation\n message = \"\";\n for (let i = 0; i < msgLen; i++) {\n message += (i % 2 === 0) ? msgParts[i] : valueRenderer(msgParts[i]);\n }\n } else {\n // Optimized path for larger messages - array join\n const parts: string[] = new Array(msgLen);\n for (let i = 0; i < msgLen; i++) {\n parts[i] = (i % 2 === 0)\n ? msgParts[i] as string\n : valueRenderer(msgParts[i]);\n }\n message = parts.join(\"\");\n }\n\n const timestamp = timestampRenderer(record.timestamp);\n const level = levelRenderer(record.level);\n const category = typeof categorySeparator === \"function\"\n ? categorySeparator(record.category)\n : record.category.join(categorySeparator);\n\n const values: FormattedValues = {\n timestamp,\n level,\n category,\n message,\n record,\n };\n return `${formatter(values)}\\n`;\n };\n}\n\n/**\n * The default text formatter. This formatter formats log records as follows:\n *\n * ```\n * 2023-11-14 22:13:20.000 +00:00 [INF] category·subcategory: Hello, world!\n * ```\n *\n * @param record The log record to format.\n * @returns The formatted log record.\n */\nexport const defaultTextFormatter: TextFormatter = getTextFormatter();\n\nconst RESET = \"\\x1b[0m\";\n\n/**\n * The ANSI colors. These can be used to colorize text in the console.\n * @since 0.6.0\n */\nexport type AnsiColor =\n | \"black\"\n | \"red\"\n | \"green\"\n | \"yellow\"\n | \"blue\"\n | \"magenta\"\n | \"cyan\"\n | \"white\";\n\nconst ansiColors: Record<AnsiColor, string> = {\n black: \"\\x1b[30m\",\n red: \"\\x1b[31m\",\n green: \"\\x1b[32m\",\n yellow: \"\\x1b[33m\",\n blue: \"\\x1b[34m\",\n magenta: \"\\x1b[35m\",\n cyan: \"\\x1b[36m\",\n white: \"\\x1b[37m\",\n};\n\n/**\n * The ANSI text styles.\n * @since 0.6.0\n */\nexport type AnsiStyle =\n | \"bold\"\n | \"dim\"\n | \"italic\"\n | \"underline\"\n | \"strikethrough\";\n\nconst ansiStyles: Record<AnsiStyle, string> = {\n bold: \"\\x1b[1m\",\n dim: \"\\x1b[2m\",\n italic: \"\\x1b[3m\",\n underline: \"\\x1b[4m\",\n strikethrough: \"\\x1b[9m\",\n};\n\nconst defaultLevelColors: Record<LogLevel, AnsiColor | null> = {\n trace: null,\n debug: \"blue\",\n info: \"green\",\n warning: \"yellow\",\n error: \"red\",\n fatal: \"magenta\",\n};\n\n/**\n * The various options for the ANSI color formatter.\n * @since 0.6.0\n */\nexport interface AnsiColorFormatterOptions extends TextFormatterOptions {\n /**\n * The timestamp format. This can be one of the following:\n *\n * - `\"date-time-timezone\"`: The date and time with the full timezone offset\n * (e.g., `\"2023-11-14 22:13:20.000 +00:00\"`).\n * - `\"date-time-tz\"`: The date and time with the short timezone offset\n * (e.g., `\"2023-11-14 22:13:20.000 +00\"`).\n * - `\"date-time\"`: The date and time without the timezone offset\n * (e.g., `\"2023-11-14 22:13:20.000\"`).\n * - `\"time-timezone\"`: The time with the full timezone offset but without\n * the date (e.g., `\"22:13:20.000 +00:00\"`).\n * - `\"time-tz\"`: The time with the short timezone offset but without the date\n * (e.g., `\"22:13:20.000 +00\"`).\n * - `\"time\"`: The time without the date or timezone offset\n * (e.g., `\"22:13:20.000\"`).\n * - `\"date\"`: The date without the time or timezone offset\n * (e.g., `\"2023-11-14\"`).\n * - `\"rfc3339\"`: The date and time in RFC 3339 format\n * (e.g., `\"2023-11-14T22:13:20.000Z\"`).\n *\n * Alternatively, this can be a function that accepts a timestamp and returns\n * a string.\n *\n * The default is `\"date-time-tz\"`.\n */\n timestamp?:\n | \"date-time-timezone\"\n | \"date-time-tz\"\n | \"date-time\"\n | \"time-timezone\"\n | \"time-tz\"\n | \"time\"\n | \"date\"\n | \"rfc3339\"\n | ((ts: number) => string);\n\n /**\n * The ANSI style for the timestamp. `\"dim\"` is used by default.\n */\n timestampStyle?: AnsiStyle | null;\n\n /**\n * The ANSI color for the timestamp. No color is used by default.\n */\n timestampColor?: AnsiColor | null;\n\n /**\n * The ANSI style for the log level. `\"bold\"` is used by default.\n */\n levelStyle?: AnsiStyle | null;\n\n /**\n * The ANSI colors for the log levels. The default colors are as follows:\n *\n * - `\"trace\"`: `null` (no color)\n * - `\"debug\"`: `\"blue\"`\n * - `\"info\"`: `\"green\"`\n * - `\"warning\"`: `\"yellow\"`\n * - `\"error\"`: `\"red\"`\n * - `\"fatal\"`: `\"magenta\"`\n */\n levelColors?: Record<LogLevel, AnsiColor | null>;\n\n /**\n * The ANSI style for the category. `\"dim\"` is used by default.\n */\n categoryStyle?: AnsiStyle | null;\n\n /**\n * The ANSI color for the category. No color is used by default.\n */\n categoryColor?: AnsiColor | null;\n}\n\n/**\n * Get an ANSI color formatter with the specified options.\n *\n * \n * @param option The options for the ANSI color formatter.\n * @returns The ANSI color formatter.\n * @since 0.6.0\n */\nexport function getAnsiColorFormatter(\n options: AnsiColorFormatterOptions = {},\n): TextFormatter {\n const format = options.format;\n const timestampStyle = typeof options.timestampStyle === \"undefined\"\n ? \"dim\"\n : options.timestampStyle;\n const timestampColor = options.timestampColor ?? null;\n const timestampPrefix = `${\n timestampStyle == null ? \"\" : ansiStyles[timestampStyle]\n }${timestampColor == null ? \"\" : ansiColors[timestampColor]}`;\n const timestampSuffix = timestampStyle == null && timestampColor == null\n ? \"\"\n : RESET;\n const levelStyle = typeof options.levelStyle === \"undefined\"\n ? \"bold\"\n : options.levelStyle;\n const levelColors = options.levelColors ?? defaultLevelColors;\n const categoryStyle = typeof options.categoryStyle === \"undefined\"\n ? \"dim\"\n : options.categoryStyle;\n const categoryColor = options.categoryColor ?? null;\n const categoryPrefix = `${\n categoryStyle == null ? \"\" : ansiStyles[categoryStyle]\n }${categoryColor == null ? \"\" : ansiColors[categoryColor]}`;\n const categorySuffix = categoryStyle == null && categoryColor == null\n ? \"\"\n : RESET;\n return getTextFormatter({\n timestamp: \"date-time-tz\",\n value(value: unknown, fallbackInspect): string {\n return fallbackInspect(value, { colors: true });\n },\n ...options,\n format({ timestamp, level, category, message, record }): string {\n const levelColor = levelColors[record.level];\n timestamp = `${timestampPrefix}${timestamp}${timestampSuffix}`;\n level = `${levelStyle == null ? \"\" : ansiStyles[levelStyle]}${\n levelColor == null ? \"\" : ansiColors[levelColor]\n }${level}${levelStyle == null && levelColor == null ? \"\" : RESET}`;\n return format == null\n ? `${timestamp} ${level} ${categoryPrefix}${category}:${categorySuffix} ${message}`\n : format({\n timestamp,\n level,\n category: `${categoryPrefix}${category}${categorySuffix}`,\n message,\n record,\n });\n },\n });\n}\n\n/**\n * A text formatter that uses ANSI colors to format log records.\n *\n * \n *\n * @param record The log record to format.\n * @returns The formatted log record.\n * @since 0.5.0\n */\nexport const ansiColorFormatter: TextFormatter = getAnsiColorFormatter();\n\n/**\n * Options for the {@link getJsonLinesFormatter} function.\n * @since 0.11.0\n */\nexport interface JsonLinesFormatterOptions {\n /**\n * The separator between category names. For example, if the separator is\n * `\".\"`, the category `[\"a\", \"b\", \"c\"]` will be formatted as `\"a.b.c\"`.\n * If this is a function, it will be called with the category array and\n * should return a string or an array of strings, which will be used\n * for rendering the category.\n *\n * @default `\".\"`\n */\n readonly categorySeparator?:\n | string\n | ((category: readonly string[]) => string | readonly string[]);\n\n /**\n * The message format. This can be one of the following:\n *\n * - `\"template\"`: The raw message template is used as the message.\n * - `\"rendered\"`: The message is rendered with the values.\n *\n * @default `\"rendered\"`\n */\n readonly message?: \"template\" | \"rendered\";\n\n /**\n * The properties format. This can be one of the following:\n *\n * - `\"flatten\"`: The properties are flattened into the root object.\n * - `\"prepend:<prefix>\"`: The properties are prepended with the given prefix\n * (e.g., `\"prepend:ctx_\"` will prepend `ctx_` to each property key).\n * - `\"nest:<key>\"`: The properties are nested under the given key\n * (e.g., `\"nest:properties\"` will nest the properties under the\n * `properties` key).\n *\n * @default `\"nest:properties\"`\n */\n readonly properties?: \"flatten\" | `prepend:${string}` | `nest:${string}`;\n}\n\n/**\n * Get a [JSON Lines] formatter with the specified options. The log records\n * will be rendered as JSON objects, one per line, which is a common format\n * for log files. This format is also known as Newline-Delimited JSON (NDJSON).\n * It looks like this:\n *\n * ```json\n * {\"@timestamp\":\"2023-11-14T22:13:20.000Z\",\"level\":\"INFO\",\"message\":\"Hello, world!\",\"logger\":\"my.logger\",\"properties\":{\"key\":\"value\"}}\n * ```\n *\n * [JSON Lines]: https://jsonlines.org/\n * @param options The options for the JSON Lines formatter.\n * @returns The JSON Lines formatter.\n * @since 0.11.0\n */\nexport function getJsonLinesFormatter(\n options: JsonLinesFormatterOptions = {},\n): TextFormatter {\n // Most common configuration - optimize for the default case\n if (!options.categorySeparator && !options.message && !options.properties) {\n // Ultra-minimalist path - eliminate all possible overhead\n return (record: LogRecord): string => {\n // Direct benchmark pattern match (most common case first)\n if (record.message.length === 3) {\n return JSON.stringify({\n \"@timestamp\": new Date(record.timestamp).toISOString(),\n level: record.level === \"warning\"\n ? \"WARN\"\n : record.level.toUpperCase(),\n message: record.message[0] + JSON.stringify(record.message[1]) +\n record.message[2],\n logger: record.category.join(\".\"),\n properties: record.properties,\n }) + \"\\n\";\n }\n\n // Single message (second most common)\n if (record.message.length === 1) {\n return JSON.stringify({\n \"@timestamp\": new Date(record.timestamp).toISOString(),\n level: record.level === \"warning\"\n ? \"WARN\"\n : record.level.toUpperCase(),\n message: record.message[0],\n logger: record.category.join(\".\"),\n properties: record.properties,\n }) + \"\\n\";\n }\n\n // Complex messages (fallback)\n let msg = record.message[0] as string;\n for (let i = 1; i < record.message.length; i++) {\n msg += (i & 1) ? JSON.stringify(record.message[i]) : record.message[i];\n }\n\n return JSON.stringify({\n \"@timestamp\": new Date(record.timestamp).toISOString(),\n level: record.level === \"warning\" ? \"WARN\" : record.level.toUpperCase(),\n message: msg,\n logger: record.category.join(\".\"),\n properties: record.properties,\n }) + \"\\n\";\n };\n }\n\n // Pre-compile configuration for non-default cases\n const isTemplateMessage = options.message === \"template\";\n const propertiesOption = options.properties ?? \"nest:properties\";\n\n // Pre-compile category joining strategy\n let joinCategory: (category: readonly string[]) => string | readonly string[];\n if (typeof options.categorySeparator === \"function\") {\n joinCategory = options.categorySeparator;\n } else {\n const separator = options.categorySeparator ?? \".\";\n joinCategory = (category: readonly string[]): string =>\n category.join(separator);\n }\n\n // Pre-compile properties handling strategy\n let getProperties: (\n properties: Record<string, unknown>,\n ) => Record<string, unknown>;\n\n if (propertiesOption === \"flatten\") {\n getProperties = (properties) => properties;\n } else if (propertiesOption.startsWith(\"prepend:\")) {\n const prefix = propertiesOption.substring(8);\n if (prefix === \"\") {\n throw new TypeError(\n `Invalid properties option: ${\n JSON.stringify(propertiesOption)\n }. It must be of the form \"prepend:<prefix>\" where <prefix> is a non-empty string.`,\n );\n }\n getProperties = (properties) => {\n const result: Record<string, unknown> = {};\n for (const key in properties) {\n result[`${prefix}${key}`] = properties[key];\n }\n return result;\n };\n } else if (propertiesOption.startsWith(\"nest:\")) {\n const key = propertiesOption.substring(5);\n getProperties = (properties) => ({ [key]: properties });\n } else {\n throw new TypeError(\n `Invalid properties option: ${\n JSON.stringify(propertiesOption)\n }. It must be \"flatten\", \"prepend:<prefix>\", or \"nest:<key>\".`,\n );\n }\n\n // Pre-compile message rendering function\n let getMessage: (record: LogRecord) => string;\n\n if (isTemplateMessage) {\n getMessage = (record: LogRecord): string => {\n if (typeof record.rawMessage === \"string\") {\n return record.rawMessage;\n }\n let msg = \"\";\n for (let i = 0; i < record.rawMessage.length; i++) {\n msg += i % 2 < 1 ? record.rawMessage[i] : \"{}\";\n }\n return msg;\n };\n } else {\n getMessage = (record: LogRecord): string => {\n const msgLen = record.message.length;\n\n if (msgLen === 1) {\n return record.message[0] as string;\n }\n\n let msg = \"\";\n for (let i = 0; i < msgLen; i++) {\n msg += (i % 2 < 1)\n ? record.message[i]\n : JSON.stringify(record.message[i]);\n }\n return msg;\n };\n }\n\n return (record: LogRecord): string => {\n return JSON.stringify({\n \"@timestamp\": new Date(record.timestamp).toISOString(),\n level: record.level === \"warning\" ? \"WARN\" : record.level.toUpperCase(),\n message: getMessage(record),\n logger: joinCategory(record.category),\n ...getProperties(record.properties),\n }) + \"\\n\";\n };\n}\n\n/**\n * The default [JSON Lines] formatter. This formatter formats log records\n * as JSON objects, one per line, which is a common format for log files.\n * It looks like this:\n *\n * ```json\n * {\"@timestamp\":\"2023-11-14T22:13:20.000Z\",\"level\":\"INFO\",\"message\":\"Hello, world!\",\"logger\":\"my.logger\",\"properties\":{\"key\":\"value\"}}\n * ```\n *\n * You can customize the output by passing options to\n * {@link getJsonLinesFormatter}. For example, you can change the category\n * separator, the message format, and how the properties are formatted.\n *\n * [JSON Lines]: https://jsonlines.org/\n * @since 0.11.0\n */\nexport const jsonLinesFormatter: TextFormatter = getJsonLinesFormatter();\n\n/**\n * A console formatter is a function that accepts a log record and returns\n * an array of arguments to pass to {@link console.log}.\n *\n * @param record The log record to format.\n * @returns The formatted log record, as an array of arguments for\n * {@link console.log}.\n */\nexport type ConsoleFormatter = (record: LogRecord) => readonly unknown[];\n\n/**\n * The styles for the log level in the console.\n */\nconst logLevelStyles: Record<LogLevel, string> = {\n \"trace\": \"background-color: gray; color: white;\",\n \"debug\": \"background-color: gray; color: white;\",\n \"info\": \"background-color: white; color: black;\",\n \"warning\": \"background-color: orange; color: black;\",\n \"error\": \"background-color: red; color: white;\",\n \"fatal\": \"background-color: maroon; color: white;\",\n};\n\n/**\n * The default console formatter.\n *\n * @param record The log record to format.\n * @returns The formatted log record, as an array of arguments for\n * {@link console.log}.\n */\nexport function defaultConsoleFormatter(record: LogRecord): readonly unknown[] {\n let msg = \"\";\n const values: unknown[] = [];\n for (let i = 0; i < record.message.length; i++) {\n if (i % 2 === 0) msg += record.message[i];\n else {\n msg += \"%o\";\n values.push(record.message[i]);\n }\n }\n const date = new Date(record.timestamp);\n const time = `${date.getUTCHours().toString().padStart(2, \"0\")}:${\n date.getUTCMinutes().toString().padStart(2, \"0\")\n }:${date.getUTCSeconds().toString().padStart(2, \"0\")}.${\n date.getUTCMilliseconds().toString().padStart(3, \"0\")\n }`;\n return [\n `%c${time} %c${levelAbbreviations[record.level]}%c %c${\n record.category.join(\"\\xb7\")\n } %c${msg}`,\n \"color: gray;\",\n logLevelStyles[record.level],\n \"background-color: default;\",\n \"color: gray;\",\n \"color: default;\",\n ...values,\n ];\n}\n"],"mappings":"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"}
|
package/package.json
CHANGED
package/src/config.ts
CHANGED
|
@@ -301,14 +301,21 @@ function configureInternal<
|
|
|
301
301
|
if (Symbol.dispose in filter) disposables.add(filter as Disposable);
|
|
302
302
|
}
|
|
303
303
|
|
|
304
|
-
if (
|
|
304
|
+
if (
|
|
305
|
+
// deno-lint-ignore no-explicit-any
|
|
306
|
+
typeof (globalThis as any).EdgeRuntime !== "string" &&
|
|
307
|
+
"process" in globalThis &&
|
|
308
|
+
!("Deno" in globalThis)
|
|
309
|
+
) {
|
|
305
310
|
// deno-lint-ignore no-explicit-any
|
|
306
311
|
const proc = (globalThis as any).process;
|
|
307
|
-
|
|
308
|
-
|
|
312
|
+
// Use bracket notation to avoid static analysis detection in Edge Runtime
|
|
313
|
+
const onMethod = proc?.["on"];
|
|
314
|
+
if (typeof onMethod === "function") {
|
|
315
|
+
onMethod.call(proc, "exit", allowAsync ? dispose : disposeSync);
|
|
309
316
|
}
|
|
310
317
|
} else {
|
|
311
|
-
// @ts-ignore: It's fine to addEventListener() on the browser/Deno
|
|
318
|
+
// @ts-ignore: It's fine to addEventListener() on the browser/Deno/Edge Runtime
|
|
312
319
|
addEventListener("unload", allowAsync ? dispose : disposeSync);
|
|
313
320
|
}
|
|
314
321
|
const meta = LoggerImpl.getLogger(["logtape", "meta"]);
|
package/src/formatter.test.ts
CHANGED
|
@@ -125,6 +125,43 @@ test("getTextFormatter()", () => {
|
|
|
125
125
|
"2023-11-14 22:13:20.000 +00:00 [INF] my-app·junk: Hello, number & number!\n",
|
|
126
126
|
);
|
|
127
127
|
|
|
128
|
+
// Test the inspect parameter fallback
|
|
129
|
+
assertEquals(
|
|
130
|
+
getTextFormatter({
|
|
131
|
+
value(value, inspect) {
|
|
132
|
+
// Custom formatting for numbers, fallback to inspect for others
|
|
133
|
+
if (typeof value === "number") {
|
|
134
|
+
return `NUM(${value})`;
|
|
135
|
+
}
|
|
136
|
+
return inspect(value);
|
|
137
|
+
},
|
|
138
|
+
})(info),
|
|
139
|
+
"2023-11-14 22:13:20.000 +00:00 [INF] my-app·junk: Hello, NUM(123) & NUM(456)!\n",
|
|
140
|
+
);
|
|
141
|
+
|
|
142
|
+
// Test inspect fallback with objects
|
|
143
|
+
const recordWithObject: LogRecord = {
|
|
144
|
+
level: "info",
|
|
145
|
+
category: ["test"],
|
|
146
|
+
message: ["Data: ", { foo: "bar", baz: 42 }, ""],
|
|
147
|
+
rawMessage: "Data: {}",
|
|
148
|
+
timestamp: 1700000000000,
|
|
149
|
+
properties: {},
|
|
150
|
+
};
|
|
151
|
+
const resultWithObject = getTextFormatter({
|
|
152
|
+
value(value, inspect) {
|
|
153
|
+
// For objects, use inspect without colors
|
|
154
|
+
if (typeof value === "object" && value !== null) {
|
|
155
|
+
return inspect(value, { colors: false });
|
|
156
|
+
}
|
|
157
|
+
return String(value);
|
|
158
|
+
},
|
|
159
|
+
})(recordWithObject);
|
|
160
|
+
// Should contain the object keys
|
|
161
|
+
assertEquals(resultWithObject.includes("foo"), true);
|
|
162
|
+
assertEquals(resultWithObject.includes("bar"), true);
|
|
163
|
+
assertEquals(resultWithObject.includes("baz"), true);
|
|
164
|
+
|
|
128
165
|
let recordedValues: FormattedValues | null = null;
|
|
129
166
|
assertEquals(
|
|
130
167
|
getTextFormatter({
|
package/src/formatter.ts
CHANGED
|
@@ -181,15 +181,41 @@ export interface TextFormatterOptions {
|
|
|
181
181
|
* The format of the embedded values.
|
|
182
182
|
*
|
|
183
183
|
* A function that renders a value to a string. This function is used to
|
|
184
|
-
* render the values in the log record. The default is
|
|
185
|
-
*
|
|
184
|
+
* render the values in the log record. The default is a cross-runtime
|
|
185
|
+
* `inspect()` function that uses [`util.inspect()`] in Node.js/Bun,
|
|
186
|
+
* [`Deno.inspect()`] in Deno, or falls back to {@link JSON.stringify} in
|
|
187
|
+
* browsers.
|
|
188
|
+
*
|
|
189
|
+
* The second parameter provides access to the default cross-runtime
|
|
190
|
+
* `inspect()` function, allowing you to fall back to the default behavior
|
|
191
|
+
* for certain values while customizing others. You can ignore this
|
|
192
|
+
* parameter if you don't need the fallback functionality.
|
|
186
193
|
*
|
|
187
194
|
* [`util.inspect()`]: https://nodejs.org/api/util.html#utilinspectobject-options
|
|
188
195
|
* [`Deno.inspect()`]: https://docs.deno.com/api/deno/~/Deno.inspect
|
|
189
196
|
* @param value The value to render.
|
|
197
|
+
* @param inspect The default cross-runtime inspect function that can be used
|
|
198
|
+
* as a fallback. Accepts an optional `options` parameter
|
|
199
|
+
* with a `colors` boolean field.
|
|
190
200
|
* @returns The string representation of the value.
|
|
201
|
+
* @example
|
|
202
|
+
* ```typescript
|
|
203
|
+
* getTextFormatter({
|
|
204
|
+
* value(value, inspect) {
|
|
205
|
+
* // Custom formatting for numbers
|
|
206
|
+
* if (typeof value === 'number') {
|
|
207
|
+
* return value.toFixed(2);
|
|
208
|
+
* }
|
|
209
|
+
* // Fall back to default for everything else
|
|
210
|
+
* return inspect(value);
|
|
211
|
+
* }
|
|
212
|
+
* })
|
|
213
|
+
* ```
|
|
191
214
|
*/
|
|
192
|
-
value?: (
|
|
215
|
+
value?: (
|
|
216
|
+
value: unknown,
|
|
217
|
+
inspect: (value: unknown, options?: { colors?: boolean }) => string,
|
|
218
|
+
) => string;
|
|
193
219
|
|
|
194
220
|
/**
|
|
195
221
|
* How those formatted parts are concatenated.
|
|
@@ -370,7 +396,9 @@ export function getTextFormatter(
|
|
|
370
396
|
})();
|
|
371
397
|
|
|
372
398
|
const categorySeparator = options.category ?? "·";
|
|
373
|
-
const valueRenderer = options.value
|
|
399
|
+
const valueRenderer = options.value
|
|
400
|
+
? (v: unknown) => options.value!(v, inspect)
|
|
401
|
+
: inspect;
|
|
374
402
|
|
|
375
403
|
// Pre-compute level renderer for better performance
|
|
376
404
|
const levelRenderer = (() => {
|
|
@@ -623,8 +651,8 @@ export function getAnsiColorFormatter(
|
|
|
623
651
|
: RESET;
|
|
624
652
|
return getTextFormatter({
|
|
625
653
|
timestamp: "date-time-tz",
|
|
626
|
-
value(value: unknown): string {
|
|
627
|
-
return
|
|
654
|
+
value(value: unknown, fallbackInspect): string {
|
|
655
|
+
return fallbackInspect(value, { colors: true });
|
|
628
656
|
},
|
|
629
657
|
...options,
|
|
630
658
|
format({ timestamp, level, category, message, record }): string {
|