@warlock.js/logger 4.0.174 → 4.1.1
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/README.md +145 -422
- package/cjs/index.cjs +1003 -0
- package/cjs/index.cjs.map +1 -0
- package/esm/channels/console-log.d.mts +40 -0
- package/esm/channels/console-log.d.mts.map +1 -0
- package/esm/channels/console-log.mjs +51 -0
- package/esm/channels/console-log.mjs.map +1 -0
- package/esm/channels/file-log.d.mts +194 -0
- package/esm/channels/file-log.d.mts.map +1 -0
- package/esm/channels/file-log.mjs +267 -0
- package/esm/channels/file-log.mjs.map +1 -0
- package/esm/channels/index.mjs +5 -0
- package/esm/channels/json-file-log.d.mts +33 -0
- package/esm/channels/json-file-log.d.mts.map +1 -0
- package/esm/channels/json-file-log.mjs +137 -0
- package/esm/channels/json-file-log.mjs.map +1 -0
- package/esm/index.d.mts +11 -0
- package/esm/index.mjs +13 -0
- package/esm/log-channel.d.mts +78 -0
- package/esm/log-channel.d.mts.map +1 -0
- package/esm/log-channel.mjs +75 -0
- package/esm/log-channel.mjs.map +1 -0
- package/esm/logger.d.mts +184 -0
- package/esm/logger.d.mts.map +1 -0
- package/esm/logger.mjs +282 -0
- package/esm/logger.mjs.map +1 -0
- package/esm/redact/redact.d.mts +25 -0
- package/esm/redact/redact.d.mts.map +1 -0
- package/esm/redact/redact.mjs +109 -0
- package/esm/redact/redact.mjs.map +1 -0
- package/esm/types.d.mts +129 -0
- package/esm/types.d.mts.map +1 -0
- package/esm/utils/capture-unhandled-errors.d.mts +16 -0
- package/esm/utils/capture-unhandled-errors.d.mts.map +1 -0
- package/esm/utils/capture-unhandled-errors.mjs +26 -0
- package/esm/utils/capture-unhandled-errors.mjs.map +1 -0
- package/esm/utils/clear-message.d.mts +8 -0
- package/esm/utils/clear-message.d.mts.map +1 -0
- package/esm/utils/clear-message.mjs +12 -0
- package/esm/utils/clear-message.mjs.map +1 -0
- package/esm/utils/index.mjs +5 -0
- package/esm/utils/safe-json-stringify.d.mts +14 -0
- package/esm/utils/safe-json-stringify.d.mts.map +1 -0
- package/esm/utils/safe-json-stringify.mjs +35 -0
- package/esm/utils/safe-json-stringify.mjs.map +1 -0
- package/llms-full.txt +1296 -0
- package/llms.txt +19 -0
- package/package.json +39 -39
- package/skills/capture-unhandled-errors/SKILL.md +103 -0
- package/skills/configure-logger/SKILL.md +105 -0
- package/skills/filter-log-entries/SKILL.md +120 -0
- package/skills/flush-logs-on-shutdown/SKILL.md +91 -0
- package/skills/logger-basics/SKILL.md +85 -0
- package/skills/overview/SKILL.md +86 -0
- package/skills/pick-log-channel/SKILL.md +139 -0
- package/skills/redact-sensitive-log-fields/SKILL.md +122 -0
- package/skills/test-logging-code/SKILL.md +169 -0
- package/skills/use-log-helpers/SKILL.md +66 -0
- package/skills/write-custom-log-channel/SKILL.md +160 -0
- package/cjs/channels/console-log.d.ts +0 -17
- package/cjs/channels/console-log.d.ts.map +0 -1
- package/cjs/channels/console-log.js +0 -47
- package/cjs/channels/console-log.js.map +0 -1
- package/cjs/channels/file-log.d.ts +0 -171
- package/cjs/channels/file-log.d.ts.map +0 -1
- package/cjs/channels/file-log.js +0 -293
- package/cjs/channels/file-log.js.map +0 -1
- package/cjs/channels/index.d.ts +0 -4
- package/cjs/channels/index.d.ts.map +0 -1
- package/cjs/channels/json-file-log.d.ts +0 -33
- package/cjs/channels/json-file-log.d.ts.map +0 -1
- package/cjs/channels/json-file-log.js +0 -164
- package/cjs/channels/json-file-log.js.map +0 -1
- package/cjs/index.d.ts +0 -6
- package/cjs/index.d.ts.map +0 -1
- package/cjs/index.js +0 -1
- package/cjs/index.js.map +0 -1
- package/cjs/log-channel.d.ts +0 -67
- package/cjs/log-channel.d.ts.map +0 -1
- package/cjs/log-channel.js +0 -88
- package/cjs/log-channel.js.map +0 -1
- package/cjs/logger.d.ts +0 -62
- package/cjs/logger.d.ts.map +0 -1
- package/cjs/logger.js +0 -124
- package/cjs/logger.js.map +0 -1
- package/cjs/types.d.ts +0 -104
- package/cjs/types.d.ts.map +0 -1
- package/cjs/utils/capture-unhandled-errors.d.ts +0 -2
- package/cjs/utils/capture-unhandled-errors.d.ts.map +0 -1
- package/cjs/utils/capture-unhandled-errors.js +0 -12
- package/cjs/utils/capture-unhandled-errors.js.map +0 -1
- package/cjs/utils/clear-message.d.ts +0 -5
- package/cjs/utils/clear-message.d.ts.map +0 -1
- package/cjs/utils/clear-message.js +0 -9
- package/cjs/utils/clear-message.js.map +0 -1
- package/cjs/utils/index.d.ts +0 -3
- package/cjs/utils/index.d.ts.map +0 -1
- package/esm/channels/console-log.d.ts +0 -17
- package/esm/channels/console-log.d.ts.map +0 -1
- package/esm/channels/console-log.js +0 -47
- package/esm/channels/console-log.js.map +0 -1
- package/esm/channels/file-log.d.ts +0 -171
- package/esm/channels/file-log.d.ts.map +0 -1
- package/esm/channels/file-log.js +0 -293
- package/esm/channels/file-log.js.map +0 -1
- package/esm/channels/index.d.ts +0 -4
- package/esm/channels/index.d.ts.map +0 -1
- package/esm/channels/json-file-log.d.ts +0 -33
- package/esm/channels/json-file-log.d.ts.map +0 -1
- package/esm/channels/json-file-log.js +0 -164
- package/esm/channels/json-file-log.js.map +0 -1
- package/esm/index.d.ts +0 -6
- package/esm/index.d.ts.map +0 -1
- package/esm/index.js +0 -1
- package/esm/index.js.map +0 -1
- package/esm/log-channel.d.ts +0 -67
- package/esm/log-channel.d.ts.map +0 -1
- package/esm/log-channel.js +0 -88
- package/esm/log-channel.js.map +0 -1
- package/esm/logger.d.ts +0 -62
- package/esm/logger.d.ts.map +0 -1
- package/esm/logger.js +0 -124
- package/esm/logger.js.map +0 -1
- package/esm/types.d.ts +0 -104
- package/esm/types.d.ts.map +0 -1
- package/esm/utils/capture-unhandled-errors.d.ts +0 -2
- package/esm/utils/capture-unhandled-errors.d.ts.map +0 -1
- package/esm/utils/capture-unhandled-errors.js +0 -12
- package/esm/utils/capture-unhandled-errors.js.map +0 -1
- package/esm/utils/clear-message.d.ts +0 -5
- package/esm/utils/clear-message.d.ts.map +0 -1
- package/esm/utils/clear-message.js +0 -9
- package/esm/utils/clear-message.js.map +0 -1
- package/esm/utils/index.d.ts +0 -3
- package/esm/utils/index.d.ts.map +0 -1
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.cjs","names":["colors","EOL","Random"],"sources":["../../../../../@warlock.js/logger/src/log-channel.ts","../../../../../@warlock.js/logger/src/channels/console-log.ts","../../../../../@warlock.js/logger/src/channels/file-log.ts","../../../../../@warlock.js/logger/src/utils/safe-json-stringify.ts","../../../../../@warlock.js/logger/src/channels/json-file-log.ts","../../../../../@warlock.js/logger/src/redact/redact.ts","../../../../../@warlock.js/logger/src/utils/clear-message.ts","../../../../../@warlock.js/logger/src/logger.ts","../../../../../@warlock.js/logger/src/utils/capture-unhandled-errors.ts"],"sourcesContent":["import type {\r\n BasicLogConfigurations,\r\n LogContract,\r\n LoggingData,\r\n RedactConfig,\r\n} from \"./types\";\r\n\r\nexport abstract class LogChannel<\r\n Options extends BasicLogConfigurations = BasicLogConfigurations,\r\n> implements LogContract\r\n{\r\n /**\r\n * Channel name\r\n */\r\n public name!: string;\r\n\r\n /**\r\n * Channel description\r\n */\r\n public description?: string;\r\n\r\n /**\r\n * Determine if channel is logging in terminal\r\n */\r\n public terminal = false;\r\n\r\n /**\r\n * Default Configurations\r\n */\r\n protected defaultConfigurations: Options = {} as Options;\r\n\r\n /**\r\n * Channel configurations\r\n */\r\n protected channelConfigurations: Options = {} as Options; //\r\n\r\n /**\r\n * Determine whether the channel is fully initialized\r\n */\r\n protected isInitialized = false;\r\n\r\n /**\r\n * Constructor\r\n */\r\n public constructor(configurations?: Options) {\r\n if (configurations) {\r\n this.setConfigurations(configurations);\r\n }\r\n\r\n setTimeout(async () => {\r\n if (this.init) {\r\n await this.init();\r\n }\r\n\r\n this.isInitialized = true;\r\n }, 0);\r\n }\r\n\r\n /**\r\n * Initialize the channel\r\n */\r\n protected init?(): void | Promise<void>;\r\n\r\n /**\r\n * Get config value\r\n */\r\n protected config<K extends keyof Options>(key: K): Options[K] {\r\n return (\r\n this.channelConfigurations[key] ?? (this.defaultConfigurations ?? {})[key]\r\n );\r\n }\r\n\r\n /**\r\n * Set configurations\r\n */\r\n protected setConfigurations(configurations: Options) {\r\n this.channelConfigurations = {\r\n ...this.channelConfigurations,\r\n ...configurations,\r\n };\r\n\r\n return this;\r\n }\r\n\r\n /**\r\n * Determine if the message should be logged\r\n */\r\n protected shouldBeLogged(data: LoggingData): boolean {\r\n // check for debug mode\r\n const allowedLevels = this.config(\"levels\");\r\n\r\n if (allowedLevels?.length && !allowedLevels.includes(data.type))\r\n return false;\r\n\r\n const filter = this.config(\"filter\");\r\n\r\n if (filter) {\r\n return filter(data);\r\n }\r\n\r\n return true;\r\n }\r\n\r\n /**\r\n * Log the given message\r\n */\r\n public abstract log(data: LoggingData): void | Promise<void>;\r\n\r\n /**\r\n * Synchronously flush messages\r\n */\r\n public flushSync?(): void;\r\n\r\n /**\r\n * Read the channel's redact config (if any). Used by `Logger` to apply\r\n * per-channel additive redaction on top of the logger-wide floor.\r\n * Subclasses normally don't override this — set `redact` in your channel\r\n * configuration instead.\r\n */\r\n public getRedactConfig(): RedactConfig | undefined {\r\n return this.config(\"redact\" as keyof Options) as\r\n | RedactConfig\r\n | undefined;\r\n }\r\n\r\n /**\r\n * Get date and time formats\r\n */\r\n protected getDateAndTimeFormat() {\r\n const dateFormat = this.config(\"dateFormat\");\r\n const date = dateFormat?.date ?? \"DD-MM-YYYY\";\r\n const time = dateFormat?.time ?? \"HH:mm:ss\";\r\n\r\n return { date, time };\r\n }\r\n\r\n /**\r\n * get basic configurations with the given ones\r\n */\r\n protected withBasicConfigurations(configurations: Partial<Options>): Options {\r\n return {\r\n filter: () => true,\r\n ...configurations,\r\n } as any as Options;\r\n }\r\n}\r\n","import { colors } from \"@mongez/copper\";\r\nimport { inspect } from \"util\";\r\nimport { LogChannel } from \"../log-channel\";\r\nimport type { BasicLogConfigurations, LoggingData } from \"../types\";\r\n\r\nexport type ConsoleLogConfig = BasicLogConfigurations & {\r\n /**\r\n * Render the log entry's `context` object on a second line after the main\r\n * message. When `false`, context is silently dropped (the historical\r\n * behavior). When `true`, contexts are pretty-printed with `util.inspect`\r\n * — colored, depth-limited, ideal for development. Persistent channels\r\n * (`FileLog`, `JSONFileLog`) always retain context regardless of this flag.\r\n *\r\n * @default false\r\n */\r\n showContext?: boolean;\r\n /**\r\n * Depth passed to `util.inspect` when rendering context. Only applies when\r\n * `showContext` is enabled.\r\n *\r\n * @default 4\r\n */\r\n contextDepth?: number;\r\n};\r\n\r\nexport class ConsoleLog extends LogChannel<ConsoleLogConfig> {\r\n /**\r\n * {@inheritdoc}\r\n */\r\n public name = \"console\";\r\n\r\n /**\r\n * Determine if channel is logging in terminal\r\n */\r\n public terminal = true;\r\n\r\n /**\r\n * {@inheritdoc}\r\n */\r\n public log(data: LoggingData) {\r\n const { module, action, message, type: level } = data;\r\n\r\n if (!this.shouldBeLogged(data)) return;\r\n\r\n // display date and time with milliseconds\r\n const date = new Date().toISOString(); // i.e 2021-01-01T00:00:00.000Z\r\n switch (level) {\r\n case \"debug\":\r\n // add a debug icon\r\n console.log(\r\n colors.magentaBright(\"⚙\"),\r\n colors.yellow(`(${date})`),\r\n colors.cyan(`[${module}]`),\r\n colors.magenta(`[${action}]`),\r\n colors.magentaBright(message),\r\n );\r\n break;\r\n case \"info\":\r\n // add an info icon\r\n console.log(\r\n colors.blueBright(\"ℹ\"),\r\n colors.yellow(`(${date})`),\r\n colors.cyan(`[${module}]`),\r\n colors.magenta(`[${action}]`),\r\n colors.blueBright(message),\r\n );\r\n break;\r\n case \"warn\":\r\n // add a warning icon\r\n console.log(\r\n colors.yellow(\"⚠\"),\r\n colors.yellow(`(${date})`),\r\n colors.cyan(`[${module}]`),\r\n colors.magenta(`[${action}]`),\r\n colors.yellowBright(message),\r\n );\r\n break;\r\n case \"error\":\r\n // add an error icon\r\n console.log(\r\n colors.red(\"✗\"),\r\n colors.yellow(`(${date})`),\r\n colors.cyan(`[${module}]`),\r\n colors.magenta(`[${action}]`),\r\n colors.redBright(message),\r\n );\r\n break;\r\n\r\n case \"success\":\r\n // add a success icon\r\n console.log(\r\n colors.green(\"✓\"),\r\n colors.yellow(`(${date})`),\r\n colors.cyan(`[${module}]`),\r\n colors.magenta(`[${action}]`),\r\n colors.greenBright(message),\r\n );\r\n break;\r\n\r\n default:\r\n console.log(\r\n \"[log]\",\r\n colors.yellow(`(${date})`),\r\n colors.cyan(`[${module}]`),\r\n colors.magenta(`[${action}]`),\r\n message,\r\n );\r\n }\r\n\r\n if (typeof message === \"object\") {\r\n console.log(message);\r\n }\r\n\r\n // Render context on a second line when explicitly enabled. We only\r\n // attempt rendering if there's anything meaningful to show — empty\r\n // objects clutter the terminal without adding signal.\r\n if (this.config(\"showContext\") && data.context && Object.keys(data.context).length > 0) {\r\n const depth = this.config(\"contextDepth\") ?? 4;\r\n console.log(\r\n colors.gray(\" ↳\"),\r\n inspect(data.context, { colors: true, depth, breakLength: 80 }),\r\n );\r\n }\r\n }\r\n}\r\n","import { ensureDirectoryAsync } from \"@warlock.js/fs\";\r\nimport dayjs from \"dayjs\";\r\nimport fs from \"fs\";\r\nimport { EOL } from \"os\";\r\nimport path from \"path\";\r\nimport { LogChannel } from \"../log-channel\";\r\nimport type {\r\n BasicLogConfigurations,\r\n LogContract,\r\n LoggingData,\r\n LogLevel,\r\n LogMessage,\r\n} from \"../types\";\r\n\r\n// TODO: Add max messages per file before rotation\r\n\r\nexport type FileLogConfig = BasicLogConfigurations & {\r\n storagePath?: string;\r\n /**\r\n * File name, without extension\r\n */\r\n name?: string;\r\n /**\r\n * chunk mode\r\n * If set to `single`, the logs will be created in a single file, unless the rotate is set to true\r\n * If set to `daily`, the logs will be created in a daily file, unless the rotate is set to true\r\n * If set to `hourly`, the logs will be created in an hourly file, unless the rotate is set to true\r\n * @default single\r\n */\r\n chunk?: \"single\" | \"daily\" | \"hourly\";\r\n /**\r\n * Whether to rotate the file\r\n *\r\n * @default true\r\n */\r\n rotate?: boolean;\r\n /**\r\n * File Extension\r\n *\r\n * @default log\r\n */\r\n extension?: string;\r\n /**\r\n * If rotate is set, the rotate name will be added to the file name suffixed with `-`\r\n *\r\n * @default DD-MM-YYYY\r\n */\r\n rotateFileName?: string;\r\n /**\r\n * Max file size before rotating the file\r\n *\r\n * @default 10MB\r\n */\r\n maxFileSize?: number;\r\n /**\r\n * Set the max messages that needs to be added before writing to the file\r\n *\r\n * @default 100\r\n */\r\n maxMessagesToWrite?: number;\r\n /**\r\n * Group logs by\r\n * Please note that the order matters here\r\n * For example, if you set `groupBy: ['level', 'module']`, the logs will be added in level name first, then by module\r\n *\r\n * @default none\r\n */\r\n groupBy?: (\"level\" | \"module\" | \"action\")[];\r\n /**\r\n * Define what levels should be logged\r\n *\r\n * @default all\r\n */\r\n levels?: LogLevel[];\r\n /**\r\n * Date and time format\r\n */\r\n dateFormat?: {\r\n date?: string;\r\n time?: string;\r\n };\r\n};\r\n\r\nexport class FileLog extends LogChannel<FileLogConfig> implements LogContract {\r\n /**\r\n * {@inheritdoc}\r\n */\r\n public name = \"file\";\r\n\r\n /**\r\n * Messages buffer\r\n */\r\n protected messages: LogMessage[] = [];\r\n\r\n /**\r\n * Grouped messages\r\n */\r\n protected groupedMessages: Record<string, LogMessage[]> = {};\r\n\r\n /**\r\n * Default channel configurations\r\n */\r\n protected defaultConfigurations: FileLogConfig = {\r\n storagePath: process.cwd() + \"/storage/logs\",\r\n rotate: true,\r\n name: \"app\",\r\n extension: \"log\",\r\n chunk: \"single\",\r\n maxMessagesToWrite: 100,\r\n filter: () => true,\r\n maxFileSize: 10 * 1024 * 1024, // 10MB\r\n get rotateFileName() {\r\n return dayjs().format(\"DD-MM-YYYY\");\r\n },\r\n dateFormat: {\r\n date: \"DD-MM-YYYY\",\r\n time: \"HH:mm:ss\",\r\n },\r\n };\r\n\r\n /**\r\n * Last write time\r\n */\r\n protected lastWriteTime = Date.now();\r\n\r\n /**\r\n * A flag to determine if the file is being written\r\n */\r\n protected isWriting = false;\r\n\r\n /**\r\n * Handle for the periodic flush interval. Stored so it can be cleared\r\n * in `dispose()` — long-lived processes that create channels dynamically\r\n * would otherwise leak one timer per channel.\r\n */\r\n protected flushIntervalHandle?: NodeJS.Timeout;\r\n\r\n /**\r\n * Check file size for file rotation\r\n */\r\n protected async checkAndRotateFile(filePath = this.filePath) {\r\n if (!this.config(\"rotate\")) return;\r\n\r\n try {\r\n const stats = await fs.promises.stat(filePath);\r\n if (stats.size >= this.config(\"maxFileSize\")!) {\r\n await this.rotateLogFile();\r\n }\r\n } catch (error: any) {\r\n // ENOENT is expected when the file hasn't been created yet — there is\r\n // nothing to rotate, so stay silent. Surface anything else.\r\n if (error.code !== \"ENOENT\") {\r\n console.error(\"Error checking log file:\", error);\r\n }\r\n }\r\n }\r\n\r\n /**\r\n * Rotate log file\r\n */\r\n protected async rotateLogFile() {\r\n const fileName = `${this.fileName}-${this.config(\"rotateFileName\")}-${Date.now()}`;\r\n\r\n const extension = this.extension;\r\n\r\n const rotatedFilePath = path.join(this.storagePath, `${fileName}.${extension}`);\r\n\r\n await fs.promises.rename(this.filePath, rotatedFilePath).catch((error) => {\r\n console.error(\"Error rotating file:\", error);\r\n });\r\n }\r\n\r\n /**\r\n * Flush messages\r\n *\r\n * Starts a periodic re-check so low-traffic channels don't sit on buffered\r\n * entries indefinitely. The handle is stored on the instance so `dispose()`\r\n * can stop it — without this, every channel leaks a timer for the lifetime\r\n * of the process.\r\n */\r\n protected initMessageFlush() {\r\n this.flushIntervalHandle = setInterval(() => {\r\n if (\r\n this.messages.length > 0 &&\r\n (this.messages.length >= this.maxMessagesToWrite || Date.now() - this.lastWriteTime > 5000)\r\n ) {\r\n this.writeMessagesToFile();\r\n }\r\n }, 5000);\r\n }\r\n\r\n /**\r\n * Stop the background flush interval and drain any buffered entries.\r\n *\r\n * Call this when discarding a channel (e.g. reconfiguring the logger at\r\n * runtime) so the 5-second timer doesn't keep the event loop alive. Safe to\r\n * call more than once.\r\n */\r\n public dispose(): void {\r\n if (this.flushIntervalHandle) {\r\n clearInterval(this.flushIntervalHandle);\r\n this.flushIntervalHandle = undefined;\r\n }\r\n\r\n this.flushSync();\r\n }\r\n\r\n /**\r\n * Get file path\r\n */\r\n public get filePath() {\r\n const fileName = this.fileName;\r\n\r\n const extension = this.extension;\r\n\r\n return path.join(this.storagePath, `${fileName}.${extension}`);\r\n }\r\n\r\n /**\r\n * Get max messages\r\n */\r\n protected get maxMessagesToWrite(): number {\r\n return this.config(\"maxMessagesToWrite\")!;\r\n }\r\n\r\n /**\r\n * Get file name\r\n */\r\n public get fileName(): string {\r\n const debugLevel = this.config(\"chunk\")!;\r\n\r\n switch (debugLevel) {\r\n case \"single\":\r\n default:\r\n return this.config(\"name\")!;\r\n case \"daily\":\r\n return dayjs().format(\"DD-MM-YYYY\");\r\n case \"hourly\":\r\n return dayjs().format(\"DD-MM-YYYY-HH-00-00-a\");\r\n }\r\n }\r\n\r\n /**\r\n * Get file extension\r\n */\r\n public get extension(): string {\r\n return this.config(\"extension\")!;\r\n }\r\n\r\n /**\r\n * Get content\r\n */\r\n protected get content() {\r\n return this.messages.map((message) => message.content).join(EOL) + EOL;\r\n }\r\n\r\n /**\r\n * Get storage path\r\n */\r\n public get storagePath(): string {\r\n return this.config(\"storagePath\")!;\r\n }\r\n\r\n /**\r\n * {@inheritdoc}\r\n */\r\n protected async init() {\r\n const logsDirectory = this.storagePath;\r\n\r\n await ensureDirectoryAsync(logsDirectory);\r\n\r\n this.initMessageFlush();\r\n }\r\n\r\n /**\r\n * Synchronously flush messages\r\n */\r\n public flushSync(): void {\r\n if (this.messages.length === 0 && Object.keys(this.groupedMessages).length === 0) return;\r\n\r\n if (this.messagedShouldBeGrouped) {\r\n this.prepareGroupedMessages();\r\n for (const key in this.groupedMessages) {\r\n const directoryPath = path.join(this.storagePath, key);\r\n fs.mkdirSync(directoryPath, { recursive: true });\r\n const filePath = path.join(directoryPath, `${this.fileName}.${this.extension}`);\r\n const content = this.groupedMessages[key].map((message) => message.content).join(EOL) + EOL;\r\n fs.appendFileSync(filePath, content);\r\n }\r\n } else {\r\n fs.mkdirSync(this.storagePath, { recursive: true });\r\n fs.appendFileSync(this.filePath, this.content);\r\n }\r\n\r\n this.onSave();\r\n }\r\n\r\n /**\r\n * {@inheritdoc}\r\n */\r\n public async log(data: LoggingData) {\r\n const { module, action, message, type: level, context } = data;\r\n\r\n if (!this.shouldBeLogged(data)) return;\r\n\r\n const { date: dateFormat, time } = this.getDateAndTimeFormat();\r\n\r\n const date = dayjs().format(dateFormat + \" \" + time);\r\n\r\n let content = `[${date}] [${level}] [${module}][${action}]: `;\r\n\r\n let stack: string | undefined;\r\n\r\n // check if message is an instance of Error\r\n if (message instanceof Error) {\r\n // in that case we need to store the error message and stack trace\r\n content += message.message + EOL;\r\n content += `[trace]` + EOL;\r\n content += message.stack;\r\n stack = message.stack;\r\n } else {\r\n content += message;\r\n }\r\n\r\n this.messages.push({\r\n content,\r\n level,\r\n date,\r\n module,\r\n action,\r\n stack,\r\n context,\r\n timestamp: new Date().toISOString(),\r\n });\r\n\r\n await this.checkIfMessagesShouldBeWritten(); // Immediate check on buffer size\r\n }\r\n\r\n /**\r\n * Check if messages should be written\r\n */\r\n protected async checkIfMessagesShouldBeWritten() {\r\n if (this.messages.length >= this.maxMessagesToWrite || Date.now() - this.lastWriteTime > 5000) {\r\n await this.writeMessagesToFile();\r\n }\r\n }\r\n\r\n /**\r\n * Should be called after messages are saved\r\n */\r\n protected onSave() {\r\n this.messages = [];\r\n this.groupedMessages = {};\r\n this.isWriting = false;\r\n this.lastWriteTime = Date.now();\r\n }\r\n\r\n /**\r\n * Check if messages should be grouped\r\n */\r\n protected get messagedShouldBeGrouped(): boolean {\r\n return Number(this.config(\"groupBy\")?.length) > 0;\r\n }\r\n\r\n /**\r\n * Write messages to the file\r\n */\r\n protected async writeMessagesToFile() {\r\n if (this.messages.length === 0 || this.isWriting || !this.isInitialized) return;\r\n\r\n this.isWriting = true;\r\n\r\n if (this.messagedShouldBeGrouped) {\r\n return await this.writeGroupedMessagesToFile();\r\n }\r\n\r\n await this.checkAndRotateFile(); // Ensure we check file size before writing\r\n\r\n try {\r\n await this.write(this.filePath, this.content);\r\n this.onSave();\r\n } catch (error) {\r\n console.error(\"Failed to write log:\", error);\r\n // Implement fallback logic here\r\n this.isWriting = false;\r\n }\r\n }\r\n\r\n /**\r\n * Write grouped messages to the file\r\n */\r\n protected async writeGroupedMessagesToFile(): Promise<void> {\r\n // first step, is to group the messages\r\n this.prepareGroupedMessages();\r\n\r\n // now each key in the grouped messages, represents the directory path that should extend the storage path\r\n for (const key in this.groupedMessages) {\r\n const directoryPath = path.join(this.storagePath, key);\r\n\r\n await ensureDirectoryAsync(directoryPath);\r\n\r\n const filePath = path.join(directoryPath, `${this.fileName}.${this.extension}`);\r\n\r\n await this.checkAndRotateFile(filePath); // Ensure we check file size before writing\r\n\r\n const content = this.groupedMessages[key].map((message) => message.content).join(EOL) + EOL;\r\n\r\n try {\r\n await this.write(filePath, content);\r\n } catch (error) {\r\n console.error(\"Failed to write log:\", error);\r\n }\r\n }\r\n\r\n this.onSave();\r\n this.isWriting = false;\r\n }\r\n\r\n /**\r\n * Prepare grouped messages\r\n */\r\n protected prepareGroupedMessages(): void {\r\n this.messages.forEach((message) => {\r\n const key = this.config(\"groupBy\")!\r\n .map((groupKey) => encodeURIComponent(message[groupKey]))\r\n .join(\"/\");\r\n\r\n this.groupedMessages[key] = this.groupedMessages[key] || [];\r\n this.groupedMessages[key].push(message);\r\n });\r\n }\r\n\r\n /**\r\n * Start writing to the file\r\n */\r\n protected async write(filePath: string, content: string) {\r\n return new Promise((resolve, reject) => {\r\n const writer = fs.createWriteStream(filePath, { flags: \"a\" });\r\n\r\n writer.write(content, (error) => {\r\n writer.end();\r\n if (error) {\r\n reject(error);\r\n } else {\r\n resolve(true);\r\n }\r\n });\r\n });\r\n }\r\n}\r\n","import { stringify as safeStableStringify } from \"safe-stable-stringify\";\n\n/**\n * Replacer that surfaces Error data — `name`, `message`, and `stack` are\n * non-enumerable on `Error`, so neither default JSON serialization nor an\n * object spread captures them (both produce `{}`). They are copied explicitly;\n * the trailing spread then captures any additional enumerable props the caller\n * (or a subclass) attached, such as a `code` field.\n */\nfunction errorReplacer<Value = unknown>(\n _key: string,\n value: Value,\n): Record<string, unknown> | Value {\n if (value instanceof Error) {\n // Spread first, then the explicit Error fields. `name`/`message`/`stack`\n // are non-enumerable on `Error`, so the spread never carries them — the\n // explicit assignments are what surface them. Placing the spread first\n // means those explicit keys legitimately take precedence over any\n // identically-named *enumerable* prop a subclass attached (resolving the\n // duplicate-key warning). Because `safe-stable-stringify` sorts keys, the\n // insertion order here does not affect the serialized bytes.\n return {\n ...value,\n name: value.name,\n message: value.message,\n stack: value.stack,\n };\n }\n\n return value;\n}\n\n/**\n * JSON-serialize log payloads safely. Circular refs, BigInt, and repeated\n * non-tree references are handled by `safe-stable-stringify`; functions and\n * symbols are dropped (standard JSON behavior); Errors are expanded via\n * `errorReplacer`. Class instances serialize as their enumerable props.\n *\n * @example\n * await fs.promises.writeFile(filePath, safeJsonStringify(payload, 2));\n */\nexport function safeJsonStringify(value: unknown, space?: number): string {\n return safeStableStringify(value, errorReplacer, space) ?? \"\";\n}\n","import { ensureDirectoryAsync, fileExistsAsync, getJsonFileAsync } from \"@warlock.js/fs\";\r\nimport dayjs from \"dayjs\";\r\nimport fs from \"fs\";\r\nimport path from \"path\";\r\nimport type { LogContract, LogMessage, LoggingData } from \"../types\";\r\nimport { safeJsonStringify } from \"../utils/safe-json-stringify\";\r\nimport { FileLog } from \"./file-log\";\r\n\r\nexport class JSONFileLog extends FileLog implements LogContract {\r\n /**\r\n * {@inheritdoc}\r\n */\r\n public name = \"fileJson\";\r\n\r\n /**\r\n * Get file extension\r\n */\r\n public get extension(): string {\r\n return \"json\";\r\n }\r\n\r\n /**\r\n * Synchronously flush messages\r\n */\r\n public flushSync(): void {\r\n if (this.messages.length === 0 && Object.keys(this.groupedMessages).length === 0) return;\r\n\r\n if (this.messagedShouldBeGrouped) {\r\n this.prepareGroupedMessages();\r\n for (const key in this.groupedMessages) {\r\n const directoryPath = path.join(this.storagePath, key);\r\n fs.mkdirSync(directoryPath, { recursive: true });\r\n const filePath = path.join(directoryPath, `${this.fileName}.${this.extension}`);\r\n\r\n let fileContents = { messages: [] as any[] };\r\n if (fs.existsSync(filePath)) {\r\n try {\r\n fileContents = JSON.parse(fs.readFileSync(filePath, \"utf-8\"));\r\n if (!Array.isArray(fileContents.messages)) fileContents.messages = [];\r\n } catch (e) {\r\n fileContents = { messages: [] };\r\n }\r\n }\r\n fileContents.messages.push(...this.groupedMessages[key]);\r\n fs.writeFileSync(filePath, safeJsonStringify(fileContents, 2));\r\n }\r\n } else {\r\n fs.mkdirSync(this.storagePath, { recursive: true });\r\n let fileContents = { messages: [] as any[] };\r\n if (fs.existsSync(this.filePath)) {\r\n try {\r\n fileContents = JSON.parse(fs.readFileSync(this.filePath, \"utf-8\"));\r\n if (!Array.isArray(fileContents.messages)) fileContents.messages = [];\r\n } catch (e) {\r\n fileContents = { messages: [] };\r\n }\r\n }\r\n fileContents.messages.push(...this.messages);\r\n fs.writeFileSync(this.filePath, safeJsonStringify(fileContents, 2));\r\n }\r\n\r\n this.onSave();\r\n }\r\n\r\n /**\r\n * {@inheritdoc}\r\n */\r\n public async log(data: LoggingData) {\r\n let stack: string[] | undefined;\r\n\r\n if (data.message instanceof Error) {\r\n stack = data.message.stack?.split(\"\\n\");\r\n data.message = data.message.message;\r\n }\r\n\r\n const { module, action, message, type: level, context } = data;\r\n\r\n if (!this.shouldBeLogged(data)) return;\r\n\r\n const { date: dateFormat, time } = this.getDateAndTimeFormat();\r\n\r\n const date = dayjs().format(dateFormat + \" \" + time);\r\n\r\n this.messages.push({\r\n content: message,\r\n level,\r\n date,\r\n module,\r\n action,\r\n stack,\r\n context,\r\n timestamp: new Date().toISOString(),\r\n } as LogMessage);\r\n\r\n await this.checkIfMessagesShouldBeWritten(); // Immediate check on buffer size\r\n }\r\n\r\n /**\r\n * Write messages to the file\r\n */\r\n protected async writeMessagesToFile(): Promise<void> {\r\n if (this.messages.length === 0 || this.isWriting || !this.isInitialized) return;\r\n\r\n this.isWriting = true;\r\n\r\n if (this.messagedShouldBeGrouped) {\r\n return await this.writeGroupedMessagesToFile();\r\n }\r\n\r\n await this.checkAndRotateFile(); // Ensure file rotation is handled\r\n\r\n let fileContents = { messages: [] as any[] };\r\n\r\n if (await fileExistsAsync(this.filePath)) {\r\n try {\r\n fileContents = (await getJsonFileAsync(this.filePath)) as { messages: any[] };\r\n } catch (error) {\r\n console.error(\"Error reading log file, reinitializing:\", error);\r\n fileContents = { messages: [] }; // Reinitialize the file if corrupted\r\n }\r\n } else {\r\n fileContents = { messages: [] }; // Reinitialize the file if corrupted\r\n }\r\n\r\n fileContents.messages.push(...this.messages);\r\n\r\n try {\r\n await fs.promises.writeFile(this.filePath, safeJsonStringify(fileContents, 2));\r\n\r\n this.onSave();\r\n } catch (error) {\r\n console.error(\"Failed to write log:\", error);\r\n // Implement fallback logic here\r\n this.isWriting = false;\r\n }\r\n }\r\n\r\n /**\r\n * Write grouped messages to the file\r\n */\r\n protected async writeGroupedMessagesToFile(): Promise<void> {\r\n // first step, is to group the messages\r\n this.prepareGroupedMessages();\r\n\r\n // now each key in the grouped messages, represents the directory path that should extend the storage path\r\n for (const key in this.groupedMessages) {\r\n const directoryPath = path.join(this.storagePath, key);\r\n\r\n await ensureDirectoryAsync(directoryPath);\r\n\r\n const filePath = path.join(directoryPath, `${this.fileName}.${this.extension}`);\r\n\r\n await this.checkAndRotateFile(filePath); // Ensure we check file size before writing\r\n\r\n let fileContents: { messages: any[] } = { messages: [] };\r\n if (await fileExistsAsync(filePath)) {\r\n try {\r\n fileContents = (await getJsonFileAsync(filePath)) as { messages: any[] };\r\n } catch (error) {\r\n console.error(\"Error reading log file, reinitializing:\", error);\r\n fileContents = { messages: [] };\r\n }\r\n } else {\r\n fileContents = { messages: [] };\r\n }\r\n\r\n fileContents.messages.push(...this.groupedMessages[key]);\r\n\r\n try {\r\n await fs.promises.writeFile(filePath, safeJsonStringify(fileContents, 2));\r\n } catch (error) {\r\n console.error(\"Failed to write log:\", error);\r\n this.isWriting = false;\r\n }\r\n }\r\n\r\n this.onSave();\r\n }\r\n}\r\n","import type { LoggingData, RedactCensor, RedactConfig } from \"../types\";\n\n/**\n * Deep-clone a value with structural fidelity for log entries — handles plain\n * objects, arrays, `Date`, `Error`, and primitives. Anything else is copied\n * by reference (we only redact paths through plain objects/arrays anyway,\n * and rebuilding e.g. a `Buffer` would change semantics).\n *\n * Purpose-built rather than reaching for `structuredClone`: `Error` instances\n * lose their `message`/`stack` under `structuredClone` in some Node versions,\n * and the logger pipeline carries them often.\n */\nfunction cloneEntry<T>(value: T, seen = new WeakMap<object, any>()): T {\n if (value === null || typeof value !== \"object\") {\n return value;\n }\n\n if (seen.has(value as unknown as object)) {\n return seen.get(value as unknown as object);\n }\n\n if (value instanceof Date) {\n return new Date(value.getTime()) as unknown as T;\n }\n\n if (value instanceof Error) {\n const copy = new (value.constructor as ErrorConstructor)(value.message);\n copy.stack = value.stack;\n copy.name = value.name;\n return copy as unknown as T;\n }\n\n if (Array.isArray(value)) {\n const arr: any[] = [];\n seen.set(value as unknown as object, arr);\n for (const item of value) {\n arr.push(cloneEntry(item, seen));\n }\n return arr as unknown as T;\n }\n\n const out: Record<string, any> = {};\n seen.set(value as unknown as object, out);\n for (const key of Object.keys(value as Record<string, any>)) {\n out[key] = cloneEntry((value as Record<string, any>)[key], seen);\n }\n return out as unknown as T;\n}\n\n/**\n * Apply a single censor decision to a value. String censors are returned\n * verbatim; function censors receive the original value plus the dotted\n * path so callers can implement value-aware redaction (mask all but the\n * last 4 chars, hash, etc.).\n */\nfunction applyCensor(value: any, censor: RedactCensor, path: string[]): any {\n if (typeof censor === \"function\") {\n return censor(value, path.join(\".\"));\n }\n return censor;\n}\n\n/**\n * Walk `target` following the remaining `segments` of a path pattern,\n * replacing matched leaves via `censor`. Operates in place — the caller\n * is responsible for cloning before calling.\n *\n * Wildcards:\n * - `*` matches exactly one segment (any key on a plain object, any index\n * on an array — stringified for the path that's passed to a function\n * censor).\n * - `**` matches zero or more segments greedily; the rest of the pattern\n * is then attempted at the current level and at every descendant.\n */\nfunction redactAtPath(\n target: any,\n segments: string[],\n censor: RedactCensor,\n pathTrail: string[],\n): void {\n if (target === null || typeof target !== \"object\") {\n return;\n }\n\n if (segments.length === 0) {\n return;\n }\n\n const [head, ...rest] = segments;\n\n if (head === \"**\") {\n // Try matching `rest` at the current level (the zero-segment match\n // case), then recurse into every child carrying the `**` forward so\n // it keeps matching at deeper levels too.\n if (rest.length > 0) {\n redactAtPath(target, rest, censor, pathTrail);\n }\n const keys = Array.isArray(target)\n ? target.map((_, index) => String(index))\n : Object.keys(target);\n for (const key of keys) {\n redactAtPath(target[key], segments, censor, [...pathTrail, key]);\n }\n return;\n }\n\n const keysToVisit =\n head === \"*\"\n ? Array.isArray(target)\n ? target.map((_, index) => String(index))\n : Object.keys(target)\n : Array.isArray(target)\n ? // Numeric segment can index into an array.\n /^\\d+$/.test(head) && Number(head) < target.length\n ? [head]\n : []\n : Object.prototype.hasOwnProperty.call(target, head)\n ? [head]\n : [];\n\n for (const key of keysToVisit) {\n if (rest.length === 0) {\n target[key] = applyCensor(target[key], censor, [...pathTrail, key]);\n } else {\n redactAtPath(target[key], rest, censor, [...pathTrail, key]);\n }\n }\n}\n\n/**\n * Produce a new `LoggingData` with every path in `config.paths` replaced\n * by `config.censor`. The original entry is never mutated — channels and\n * other call sites can hold references to the input safely.\n *\n * No-op (returns the input by reference) when `config` is `undefined` or\n * its `paths` array is empty, so the fast path stays fast.\n */\nexport function applyRedact(\n data: LoggingData,\n config: RedactConfig | undefined,\n): LoggingData {\n if (!config || config.paths.length === 0) {\n return data;\n }\n\n const censor = config.censor ?? \"[REDACTED]\";\n const cloned = cloneEntry(data);\n\n for (const pattern of config.paths) {\n const segments = pattern.split(\".\").filter((segment) => segment.length > 0);\n if (segments.length === 0) continue;\n redactAtPath(cloned, segments, censor, []);\n }\n\n return cloned;\n}\n\n/**\n * Combine two redact configs into one effective config. Used to merge a\n * channel's additive paths on top of the logger-wide floor.\n *\n * - `paths` are concatenated; duplicates are kept (the matcher tolerates\n * them, and de-duping cross-config would mask a developer typo).\n * - `censor` from the channel wins; falls back to the logger's; falls back\n * to the default `\"[REDACTED]\"`.\n */\nexport function mergeRedact(\n base: RedactConfig | undefined,\n extra: RedactConfig | undefined,\n): RedactConfig | undefined {\n if (!base && !extra) return undefined;\n if (!base) return extra;\n if (!extra) return base;\n\n return {\n paths: [...base.paths, ...extra.paths],\n censor: extra.censor ?? base.censor,\n };\n}\n","/**\r\n * Clear message from any terminal codes\r\n */\r\nexport function clearMessage(message: any) {\r\n if (typeof message !== \"string\") return message;\r\n\r\n // eslint-disable-next-line no-control-regex\r\n return message.replace(/\\u001b[^m]*?m/g, \"\");\r\n}\r\n","import { Random } from \"@mongez/reinforcements\";\r\nimport type { LogChannel } from \"./log-channel\";\r\nimport { applyRedact, mergeRedact } from \"./redact\";\r\nimport type {\r\n AutoFlushEvent,\r\n LoggingData,\r\n LogLevel,\r\n OmittedLoggingData,\r\n RedactConfig,\r\n} from \"./types\";\r\nimport { clearMessage } from \"./utils/clear-message\";\r\n\r\nconst SIGNAL_EVENTS: ReadonlySet<AutoFlushEvent> = new Set([\r\n \"SIGINT\",\r\n \"SIGTERM\",\r\n \"SIGHUP\",\r\n \"SIGBREAK\",\r\n \"SIGUSR2\",\r\n]);\r\n\r\n/**\r\n * Severity ranks used by `setMinLevel`. Higher number = more severe. The\r\n * ordering matches conventional log-level hierarchies: `debug` is noisiest\r\n * and easiest to drop; `error` is the loudest and never dropped by the\r\n * minimum-level filter. `success` sits beside `info` — it's an informational\r\n * outcome, not a warning.\r\n */\r\nconst LEVEL_RANK: Record<LogLevel, number> = {\r\n debug: 0,\r\n info: 1,\r\n success: 1,\r\n warn: 2,\r\n error: 3,\r\n};\r\n\r\nexport class Logger {\r\n /**\r\n * Current channel\r\n */\r\n public channels: LogChannel[] = [];\r\n\r\n public id = \"logger-\" + Random.string(32);\r\n\r\n /**\r\n * Registered auto-flush handlers, keyed by event name. Stored so repeated\r\n * calls to `enableAutoFlush` replace rather than stack, and so\r\n * `disableAutoFlush` can remove them cleanly.\r\n */\r\n private autoFlushHandlers = new Map<AutoFlushEvent, () => void>();\r\n\r\n /**\r\n * Logger-wide minimum severity. When set, entries below this level are\r\n * dropped before any channel is invoked — cheaper than per-channel `levels`\r\n * filters because the fan-out loop is skipped entirely. `undefined` means\r\n * no minimum (every entry reaches every channel that accepts it).\r\n */\r\n private minLevel?: LogLevel;\r\n\r\n /**\r\n * Logger-wide redaction floor. Applied once before fan-out — every\r\n * channel receives an entry with these paths already censored. Channel\r\n * configs can extend the path list (additive); they cannot remove paths\r\n * set here.\r\n */\r\n private redactConfig?: RedactConfig;\r\n\r\n /**\r\n * Add a new channel\r\n */\r\n public addChannel(channel: LogChannel) {\r\n this.channels.push(channel);\r\n\r\n return this;\r\n }\r\n\r\n /**\r\n * Set base configurations\r\n */\r\n public configure(config: {\r\n channels?: LogChannel[];\r\n autoFlushOn?: AutoFlushEvent[];\r\n minLevel?: LogLevel;\r\n redact?: RedactConfig;\r\n }) {\r\n if (config.channels) {\r\n this.channels = config.channels;\r\n }\r\n\r\n if (config.autoFlushOn) {\r\n this.enableAutoFlush(config.autoFlushOn);\r\n }\r\n\r\n if (config.minLevel !== undefined) {\r\n this.setMinLevel(config.minLevel);\r\n }\r\n\r\n if (config.redact !== undefined) {\r\n this.setRedact(config.redact);\r\n }\r\n\r\n return this;\r\n }\r\n\r\n /**\r\n * Set the logger-wide redaction floor. Applied to every entry before\r\n * fan-out; channel configs add more paths on top, never fewer. Pass\r\n * `undefined` to clear.\r\n *\r\n * @example\r\n * log.setRedact({\r\n * paths: [\"context.password\", \"context.*.token\"],\r\n * censor: \"[REDACTED]\",\r\n * });\r\n */\r\n public setRedact(config: RedactConfig | undefined): this {\r\n this.redactConfig = config;\r\n return this;\r\n }\r\n\r\n /**\r\n * Read the active logger-wide redact config (or `undefined`).\r\n */\r\n public getRedact(): RedactConfig | undefined {\r\n return this.redactConfig;\r\n }\r\n\r\n /**\r\n * Drop every entry whose severity is below `level` before fan-out. Cheaper\r\n * than per-channel `levels` filters because the loop never runs and no\r\n * channel receives the entry. Pass `undefined` to clear and accept all\r\n * levels again.\r\n *\r\n * @example\r\n * // production: silence debug noise everywhere at once\r\n * logger.setMinLevel(\"info\");\r\n */\r\n public setMinLevel(level: LogLevel | undefined): this {\r\n this.minLevel = level;\r\n return this;\r\n }\r\n\r\n /**\r\n * Read the active minimum severity (or `undefined` when none is set).\r\n */\r\n public getMinLevel(): LogLevel | undefined {\r\n return this.minLevel;\r\n }\r\n\r\n /**\r\n * Set channels\r\n */\r\n public setChannels(channels: LogChannel[]) {\r\n this.channels = channels;\r\n\r\n return this;\r\n }\r\n\r\n /**\r\n * Normalize log data to a single object\r\n */\r\n private normalizeLogData(\r\n dataOrModule: LoggingData | OmittedLoggingData | string,\r\n action?: string,\r\n message: any = \"\",\r\n level?: LogLevel,\r\n context?: Record<string, any>,\r\n ): LoggingData {\r\n if (typeof dataOrModule === \"object\") {\r\n // If level is provided, override type\r\n return {\r\n type: (level || (dataOrModule as any).type || \"info\") as LogLevel,\r\n module: dataOrModule.module,\r\n action: dataOrModule.action,\r\n message: dataOrModule.message,\r\n ...(context ? { context } : dataOrModule.context ? { context: dataOrModule.context } : {}),\r\n };\r\n }\r\n return {\r\n type: (level || \"info\") as LogLevel,\r\n module: dataOrModule,\r\n action: action as string,\r\n message,\r\n ...(context ? { context } : {}),\r\n };\r\n }\r\n\r\n /**\r\n * Make log\r\n *\r\n * Fans out a single log entry to every registered channel. Non-terminal\r\n * channels receive a copy whose `message` has had ANSI color codes stripped\r\n * — each channel sees its own shallow clone so one channel cannot observe\r\n * another's mutations (e.g. a later terminal channel still sees the original\r\n * colored message).\r\n */\r\n public async log(data: LoggingData) {\r\n if (this.minLevel && LEVEL_RANK[data.type] < LEVEL_RANK[this.minLevel]) {\r\n return this;\r\n }\r\n\r\n // Apply the logger-wide redact floor once. Every channel sees the\r\n // result; no channel can undo a logger-wide redaction (additive-only\r\n // semantics).\r\n const baseEntry = applyRedact(data, this.redactConfig);\r\n\r\n for (const channel of this.channels) {\r\n const channelRedact = channel.getRedactConfig?.();\r\n const effectiveRedact = channelRedact\r\n ? mergeRedact(this.redactConfig, channelRedact)\r\n : undefined;\r\n\r\n // When the channel adds paths, redact again from `data` rather than\r\n // from `baseEntry` so the merged config (which already contains the\r\n // logger-wide paths) does the full pass — avoids double-cloning the\r\n // already-redacted base.\r\n let payload = effectiveRedact ? applyRedact(data, effectiveRedact) : baseEntry;\r\n\r\n if (channel.terminal === false) {\r\n payload = { ...payload, message: clearMessage(payload.message) };\r\n }\r\n\r\n channel.log(payload);\r\n }\r\n\r\n return this;\r\n }\r\n\r\n /**\r\n * Make debug log\r\n */\r\n public debug(\r\n dataOrModule: OmittedLoggingData | string,\r\n action?: string,\r\n message: any = \"\",\r\n context?: Record<string, any>,\r\n ) {\r\n const data = this.normalizeLogData(dataOrModule, action, message, \"debug\", context);\r\n return this.log(data);\r\n }\r\n\r\n /**\r\n * Make info log\r\n */\r\n public info(\r\n dataOrModule: OmittedLoggingData | string,\r\n action?: string,\r\n message: any = \"\",\r\n context?: Record<string, any>,\r\n ) {\r\n const data = this.normalizeLogData(dataOrModule, action, message, \"info\", context);\r\n return this.log(data);\r\n }\r\n\r\n /**\r\n * Make warn log\r\n */\r\n public warn(\r\n dataOrModule: OmittedLoggingData | string,\r\n action?: string,\r\n message: any = \"\",\r\n context?: Record<string, any>,\r\n ) {\r\n const data = this.normalizeLogData(dataOrModule, action, message, \"warn\", context);\r\n return this.log(data);\r\n }\r\n\r\n /**\r\n * Make error log\r\n */\r\n public error(\r\n dataOrModule: OmittedLoggingData | string,\r\n action?: string,\r\n message: any = \"\",\r\n context?: Record<string, any>,\r\n ) {\r\n const data = this.normalizeLogData(dataOrModule, action, message, \"error\", context);\r\n return this.log(data);\r\n }\r\n\r\n /**\r\n * Make success log\r\n */\r\n public success(\r\n dataOrModule: OmittedLoggingData | string,\r\n action?: string,\r\n message: any = \"\",\r\n context?: Record<string, any>,\r\n ) {\r\n const data = this.normalizeLogData(dataOrModule, action, message, \"success\", context);\r\n\r\n return this.log(data);\r\n }\r\n\r\n /**\r\n * Log an `error` entry when `condition` is falsy. No-op otherwise — the\r\n * entry is never built and channels are not invoked, so this is genuinely\r\n * free in the happy path. Mirrors the spirit of `console.assert` but routes\r\n * through the logger pipeline so persistent channels capture failures.\r\n *\r\n * @example\r\n * log.assert(user !== null, \"auth\", \"session\", \"user vanished mid-flight\", { sessionId });\r\n */\r\n public assert(\r\n condition: unknown,\r\n module: string,\r\n action: string,\r\n message: any,\r\n context?: Record<string, any>,\r\n ): Promise<Logger> | Logger {\r\n if (condition) return this;\r\n return this.error(module, action, message, context);\r\n }\r\n\r\n /**\r\n * Start a duration timer. The returned function emits an `info` entry\r\n * with `completed in <ms>ms` and a `durationMs` field in `context` when\r\n * called. Pass an object to `end()` to merge extra fields into context.\r\n *\r\n * @example\r\n * const end = log.timer(\"db\", \"users.findById\");\r\n * const user = await usersRepo.findById(id);\r\n * end({ id, found: !!user });\r\n */\r\n public timer(\r\n module: string,\r\n action: string,\r\n ): (extra?: Record<string, any>) => Promise<Logger> {\r\n const startedAt = Date.now();\r\n return (extra?: Record<string, any>) => {\r\n const durationMs = Date.now() - startedAt;\r\n return this.info(module, action, `completed in ${durationMs}ms`, {\r\n durationMs,\r\n ...(extra ?? {}),\r\n });\r\n };\r\n }\r\n\r\n /**\r\n * Get channel by name\r\n */\r\n public channel(name: string) {\r\n return this.channels.find((channel) => channel.name === name);\r\n }\r\n\r\n /**\r\n * Synchronously flush logs\r\n */\r\n public flushSync() {\r\n for (const channel of this.channels) {\r\n if (channel.flushSync) {\r\n channel.flushSync();\r\n }\r\n }\r\n }\r\n\r\n /**\r\n * Register one process-level handler per event that calls `flushSync()`\r\n * before the process terminates.\r\n *\r\n * For signal events (`SIGINT`, `SIGTERM`, `SIGHUP`, `SIGBREAK`, `SIGUSR2`)\r\n * the handler flushes and then re-raises the signal so Node's default exit\r\n * behavior runs. For `beforeExit`, the handler flushes in place — Node exits\r\n * naturally afterwards.\r\n *\r\n * Idempotent: calling with the same events replaces the previous handlers.\r\n * Call `disableAutoFlush()` to unregister.\r\n *\r\n * @example\r\n * log.configure({\r\n * channels: [new ConsoleLog(), new FileLog()],\r\n * autoFlushOn: [\"SIGINT\", \"SIGTERM\", \"beforeExit\"],\r\n * });\r\n */\r\n public enableAutoFlush(events: AutoFlushEvent[]): this {\r\n this.disableAutoFlush();\r\n\r\n for (const event of events) {\r\n const handler = SIGNAL_EVENTS.has(event)\r\n ? () => {\r\n this.flushSync();\r\n process.off(event, handler);\r\n process.kill(process.pid, event as NodeJS.Signals);\r\n }\r\n : () => {\r\n this.flushSync();\r\n };\r\n\r\n process.on(event, handler);\r\n this.autoFlushHandlers.set(event, handler);\r\n }\r\n\r\n return this;\r\n }\r\n\r\n /**\r\n * Remove every handler previously registered by `enableAutoFlush`.\r\n * Safe to call when no handlers are registered.\r\n */\r\n public disableAutoFlush(): this {\r\n for (const [event, handler] of this.autoFlushHandlers) {\r\n process.off(event, handler);\r\n }\r\n\r\n this.autoFlushHandlers.clear();\r\n\r\n return this;\r\n }\r\n}\r\n\r\n/**\r\n * The package singleton. Use this for everyday logging — `log.info(...)`,\r\n * `log.error(...)`, `log.configure(...)`. Custom logger instances can be\r\n * created by instantiating `Logger` directly.\r\n *\r\n * The name is intentionally short: `log` reads naturally at the call site\r\n * (`log.info(\"auth\", \"login\", \"ok\")`) and matches the convention used in\r\n * pino, bunyan, and most JS logging tutorials.\r\n *\r\n * Note that `log` is a `Logger` instance, **not** a function — the bare\r\n * callable form was removed when the dual `log` / `logger` exports were\r\n * collapsed into a single name. Use `log.info(...)` (or any other level\r\n * shortcut) to emit entries.\r\n */\r\nexport const log = new Logger();\r\n","import { log } from \"../logger\";\r\n\r\n/**\r\n * Route Node's process-level failure events through the logger so they land in\r\n * every configured channel with full stack context. Registers one listener for\r\n * `unhandledRejection` and one for `uncaughtException`; call once at startup\r\n * after channels are configured. Pair with `autoFlushOn: [\"beforeExit\"]` so the\r\n * final entry survives the process exit that follows an uncaught exception.\r\n *\r\n * @example\r\n * log.configure({ channels: [new ConsoleLog(), new FileLog()] });\r\n * captureAnyUnhandledRejection();\r\n */\r\nexport function captureAnyUnhandledRejection() {\r\n process.on(\"unhandledRejection\", (reason: any) => {\r\n log.error(\"app\", \"unhandledRejection\", reason);\r\n });\r\n\r\n process.on(\"uncaughtException\", (error) => {\r\n log.error(\"app\", \"uncaughtException\", error);\r\n });\r\n}\r\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAOA,IAAsB,aAAtB,MAGA;;;;CAkCE,AAAO,YAAY,gBAA0B;kBApB3B;+BAKyB,CAAC;+BAKD,CAAC;uBAKlB;EAMxB,IAAI,gBACF,KAAK,kBAAkB,cAAc;EAGvC,WAAW,YAAY;GACrB,IAAI,KAAK,MACP,MAAM,KAAK,KAAK;GAGlB,KAAK,gBAAgB;EACvB,GAAG,CAAC;CACN;;;;CAUA,AAAU,OAAgC,KAAoB;EAC5D,OACE,KAAK,sBAAsB,SAAS,KAAK,yBAAyB,CAAC,GAAG;CAE1E;;;;CAKA,AAAU,kBAAkB,gBAAyB;EACnD,KAAK,wBAAwB;GAC3B,GAAG,KAAK;GACR,GAAG;EACL;EAEA,OAAO;CACT;;;;CAKA,AAAU,eAAe,MAA4B;EAEnD,MAAM,gBAAgB,KAAK,OAAO,QAAQ;EAE1C,IAAI,eAAe,UAAU,CAAC,cAAc,SAAS,KAAK,IAAI,GAC5D,OAAO;EAET,MAAM,SAAS,KAAK,OAAO,QAAQ;EAEnC,IAAI,QACF,OAAO,OAAO,IAAI;EAGpB,OAAO;CACT;;;;;;;CAkBA,AAAO,kBAA4C;EACjD,OAAO,KAAK,OAAO,QAAyB;CAG9C;;;;CAKA,AAAU,uBAAuB;EAC/B,MAAM,aAAa,KAAK,OAAO,YAAY;EAI3C,OAAO;GAAE,MAHI,YAAY,QAAQ;GAGlB,MAFF,YAAY,QAAQ;EAEb;CACtB;;;;CAKA,AAAU,wBAAwB,gBAA2C;EAC3E,OAAO;GACL,cAAc;GACd,GAAG;EACL;CACF;AACF;;;;ACxHA,IAAa,aAAb,cAAgC,WAA6B;;;cAI7C;kBAKI;;;;;CAKlB,AAAO,IAAI,MAAmB;EAC5B,MAAM,EAAE,QAAQ,QAAQ,SAAS,MAAM,UAAU;EAEjD,IAAI,CAAC,KAAK,eAAe,IAAI,GAAG;EAGhC,MAAM,wBAAO,IAAI,KAAK,GAAE,YAAY;EACpC,QAAQ,OAAR;GACE,KAAK;IAEH,QAAQ,IACNA,sBAAO,cAAc,GAAG,GACxBA,sBAAO,OAAO,IAAI,KAAK,EAAE,GACzBA,sBAAO,KAAK,IAAI,OAAO,EAAE,GACzBA,sBAAO,QAAQ,IAAI,OAAO,EAAE,GAC5BA,sBAAO,cAAc,OAAO,CAC9B;IACA;GACF,KAAK;IAEH,QAAQ,IACNA,sBAAO,WAAW,GAAG,GACrBA,sBAAO,OAAO,IAAI,KAAK,EAAE,GACzBA,sBAAO,KAAK,IAAI,OAAO,EAAE,GACzBA,sBAAO,QAAQ,IAAI,OAAO,EAAE,GAC5BA,sBAAO,WAAW,OAAO,CAC3B;IACA;GACF,KAAK;IAEH,QAAQ,IACNA,sBAAO,OAAO,GAAG,GACjBA,sBAAO,OAAO,IAAI,KAAK,EAAE,GACzBA,sBAAO,KAAK,IAAI,OAAO,EAAE,GACzBA,sBAAO,QAAQ,IAAI,OAAO,EAAE,GAC5BA,sBAAO,aAAa,OAAO,CAC7B;IACA;GACF,KAAK;IAEH,QAAQ,IACNA,sBAAO,IAAI,GAAG,GACdA,sBAAO,OAAO,IAAI,KAAK,EAAE,GACzBA,sBAAO,KAAK,IAAI,OAAO,EAAE,GACzBA,sBAAO,QAAQ,IAAI,OAAO,EAAE,GAC5BA,sBAAO,UAAU,OAAO,CAC1B;IACA;GAEF,KAAK;IAEH,QAAQ,IACNA,sBAAO,MAAM,GAAG,GAChBA,sBAAO,OAAO,IAAI,KAAK,EAAE,GACzBA,sBAAO,KAAK,IAAI,OAAO,EAAE,GACzBA,sBAAO,QAAQ,IAAI,OAAO,EAAE,GAC5BA,sBAAO,YAAY,OAAO,CAC5B;IACA;GAEF,SACE,QAAQ,IACN,SACAA,sBAAO,OAAO,IAAI,KAAK,EAAE,GACzBA,sBAAO,KAAK,IAAI,OAAO,EAAE,GACzBA,sBAAO,QAAQ,IAAI,OAAO,EAAE,GAC5B,OACF;EACJ;EAEA,IAAI,OAAO,YAAY,UACrB,QAAQ,IAAI,OAAO;EAMrB,IAAI,KAAK,OAAO,aAAa,KAAK,KAAK,WAAW,OAAO,KAAK,KAAK,OAAO,EAAE,SAAS,GAAG;GACtF,MAAM,QAAQ,KAAK,OAAO,cAAc,KAAK;GAC7C,QAAQ,IACNA,sBAAO,KAAK,KAAK,qBACT,KAAK,SAAS;IAAE,QAAQ;IAAM;IAAO,aAAa;GAAG,CAAC,CAChE;EACF;CACF;AACF;;;;ACzCA,IAAa,UAAb,cAA6B,WAAiD;;;cAI9D;kBAKqB,CAAC;yBAKsB,CAAC;+BAKV;GAC/C,aAAa,QAAQ,IAAI,IAAI;GAC7B,QAAQ;GACR,MAAM;GACN,WAAW;GACX,OAAO;GACP,oBAAoB;GACpB,cAAc;GACd,aAAa,KAAK,OAAO;GACzB,IAAI,iBAAiB;IACnB,0BAAa,EAAE,OAAO,YAAY;GACpC;GACA,YAAY;IACV,MAAM;IACN,MAAM;GACR;EACF;uBAK0B,KAAK,IAAI;mBAKb;;;;;CAYtB,MAAgB,mBAAmB,WAAW,KAAK,UAAU;EAC3D,IAAI,CAAC,KAAK,OAAO,QAAQ,GAAG;EAE5B,IAAI;GAEF,KAAI,MADgB,WAAG,SAAS,KAAK,QAAQ,GACnC,QAAQ,KAAK,OAAO,aAAa,GACzC,MAAM,KAAK,cAAc;EAE7B,SAAS,OAAY;GAGnB,IAAI,MAAM,SAAS,UACjB,QAAQ,MAAM,4BAA4B,KAAK;EAEnD;CACF;;;;CAKA,MAAgB,gBAAgB;EAC9B,MAAM,WAAW,GAAG,KAAK,SAAS,GAAG,KAAK,OAAO,gBAAgB,EAAE,GAAG,KAAK,IAAI;EAE/E,MAAM,YAAY,KAAK;EAEvB,MAAM,kBAAkB,aAAK,KAAK,KAAK,aAAa,GAAG,SAAS,GAAG,WAAW;EAE9E,MAAM,WAAG,SAAS,OAAO,KAAK,UAAU,eAAe,EAAE,OAAO,UAAU;GACxE,QAAQ,MAAM,wBAAwB,KAAK;EAC7C,CAAC;CACH;;;;;;;;;CAUA,AAAU,mBAAmB;EAC3B,KAAK,sBAAsB,kBAAkB;GAC3C,IACE,KAAK,SAAS,SAAS,MACtB,KAAK,SAAS,UAAU,KAAK,sBAAsB,KAAK,IAAI,IAAI,KAAK,gBAAgB,MAEtF,KAAK,oBAAoB;EAE7B,GAAG,GAAI;CACT;;;;;;;;CASA,AAAO,UAAgB;EACrB,IAAI,KAAK,qBAAqB;GAC5B,cAAc,KAAK,mBAAmB;GACtC,KAAK,sBAAsB;EAC7B;EAEA,KAAK,UAAU;CACjB;;;;CAKA,IAAW,WAAW;EACpB,MAAM,WAAW,KAAK;EAEtB,MAAM,YAAY,KAAK;EAEvB,OAAO,aAAK,KAAK,KAAK,aAAa,GAAG,SAAS,GAAG,WAAW;CAC/D;;;;CAKA,IAAc,qBAA6B;EACzC,OAAO,KAAK,OAAO,oBAAoB;CACzC;;;;CAKA,IAAW,WAAmB;EAG5B,QAFmB,KAAK,OAAO,OAEd,GAAjB;GACE,KAAK;GACL,SACE,OAAO,KAAK,OAAO,MAAM;GAC3B,KAAK,SACH,0BAAa,EAAE,OAAO,YAAY;GACpC,KAAK,UACH,0BAAa,EAAE,OAAO,uBAAuB;EACjD;CACF;;;;CAKA,IAAW,YAAoB;EAC7B,OAAO,KAAK,OAAO,WAAW;CAChC;;;;CAKA,IAAc,UAAU;EACtB,OAAO,KAAK,SAAS,KAAK,YAAY,QAAQ,OAAO,EAAE,KAAKC,MAAG,IAAIA;CACrE;;;;CAKA,IAAW,cAAsB;EAC/B,OAAO,KAAK,OAAO,aAAa;CAClC;;;;CAKA,MAAgB,OAAO;EACrB,MAAM,gBAAgB,KAAK;EAE3B,+CAA2B,aAAa;EAExC,KAAK,iBAAiB;CACxB;;;;CAKA,AAAO,YAAkB;EACvB,IAAI,KAAK,SAAS,WAAW,KAAK,OAAO,KAAK,KAAK,eAAe,EAAE,WAAW,GAAG;EAElF,IAAI,KAAK,yBAAyB;GAChC,KAAK,uBAAuB;GAC5B,KAAK,MAAM,OAAO,KAAK,iBAAiB;IACtC,MAAM,gBAAgB,aAAK,KAAK,KAAK,aAAa,GAAG;IACrD,WAAG,UAAU,eAAe,EAAE,WAAW,KAAK,CAAC;IAC/C,MAAM,WAAW,aAAK,KAAK,eAAe,GAAG,KAAK,SAAS,GAAG,KAAK,WAAW;IAC9E,MAAM,UAAU,KAAK,gBAAgB,KAAK,KAAK,YAAY,QAAQ,OAAO,EAAE,KAAKA,MAAG,IAAIA;IACxF,WAAG,eAAe,UAAU,OAAO;GACrC;EACF,OAAO;GACL,WAAG,UAAU,KAAK,aAAa,EAAE,WAAW,KAAK,CAAC;GAClD,WAAG,eAAe,KAAK,UAAU,KAAK,OAAO;EAC/C;EAEA,KAAK,OAAO;CACd;;;;CAKA,MAAa,IAAI,MAAmB;EAClC,MAAM,EAAE,QAAQ,QAAQ,SAAS,MAAM,OAAO,YAAY;EAE1D,IAAI,CAAC,KAAK,eAAe,IAAI,GAAG;EAEhC,MAAM,EAAE,MAAM,YAAY,SAAS,KAAK,qBAAqB;EAE7D,MAAM,0BAAa,EAAE,OAAO,aAAa,MAAM,IAAI;EAEnD,IAAI,UAAU,IAAI,KAAK,KAAK,MAAM,KAAK,OAAO,IAAI,OAAO;EAEzD,IAAI;EAGJ,IAAI,mBAAmB,OAAO;GAE5B,WAAW,QAAQ,UAAUA;GAC7B,WAAW,YAAYA;GACvB,WAAW,QAAQ;GACnB,QAAQ,QAAQ;EAClB,OACE,WAAW;EAGb,KAAK,SAAS,KAAK;GACjB;GACA;GACA;GACA;GACA;GACA;GACA;GACA,4BAAW,IAAI,KAAK,GAAE,YAAY;EACpC,CAAC;EAED,MAAM,KAAK,+BAA+B;CAC5C;;;;CAKA,MAAgB,iCAAiC;EAC/C,IAAI,KAAK,SAAS,UAAU,KAAK,sBAAsB,KAAK,IAAI,IAAI,KAAK,gBAAgB,KACvF,MAAM,KAAK,oBAAoB;CAEnC;;;;CAKA,AAAU,SAAS;EACjB,KAAK,WAAW,CAAC;EACjB,KAAK,kBAAkB,CAAC;EACxB,KAAK,YAAY;EACjB,KAAK,gBAAgB,KAAK,IAAI;CAChC;;;;CAKA,IAAc,0BAAmC;EAC/C,OAAO,OAAO,KAAK,OAAO,SAAS,GAAG,MAAM,IAAI;CAClD;;;;CAKA,MAAgB,sBAAsB;EACpC,IAAI,KAAK,SAAS,WAAW,KAAK,KAAK,aAAa,CAAC,KAAK,eAAe;EAEzE,KAAK,YAAY;EAEjB,IAAI,KAAK,yBACP,OAAO,MAAM,KAAK,2BAA2B;EAG/C,MAAM,KAAK,mBAAmB;EAE9B,IAAI;GACF,MAAM,KAAK,MAAM,KAAK,UAAU,KAAK,OAAO;GAC5C,KAAK,OAAO;EACd,SAAS,OAAO;GACd,QAAQ,MAAM,wBAAwB,KAAK;GAE3C,KAAK,YAAY;EACnB;CACF;;;;CAKA,MAAgB,6BAA4C;EAE1D,KAAK,uBAAuB;EAG5B,KAAK,MAAM,OAAO,KAAK,iBAAiB;GACtC,MAAM,gBAAgB,aAAK,KAAK,KAAK,aAAa,GAAG;GAErD,+CAA2B,aAAa;GAExC,MAAM,WAAW,aAAK,KAAK,eAAe,GAAG,KAAK,SAAS,GAAG,KAAK,WAAW;GAE9E,MAAM,KAAK,mBAAmB,QAAQ;GAEtC,MAAM,UAAU,KAAK,gBAAgB,KAAK,KAAK,YAAY,QAAQ,OAAO,EAAE,KAAKA,MAAG,IAAIA;GAExF,IAAI;IACF,MAAM,KAAK,MAAM,UAAU,OAAO;GACpC,SAAS,OAAO;IACd,QAAQ,MAAM,wBAAwB,KAAK;GAC7C;EACF;EAEA,KAAK,OAAO;EACZ,KAAK,YAAY;CACnB;;;;CAKA,AAAU,yBAA+B;EACvC,KAAK,SAAS,SAAS,YAAY;GACjC,MAAM,MAAM,KAAK,OAAO,SAAS,EAC9B,KAAK,aAAa,mBAAmB,QAAQ,SAAS,CAAC,EACvD,KAAK,GAAG;GAEX,KAAK,gBAAgB,OAAO,KAAK,gBAAgB,QAAQ,CAAC;GAC1D,KAAK,gBAAgB,KAAK,KAAK,OAAO;EACxC,CAAC;CACH;;;;CAKA,MAAgB,MAAM,UAAkB,SAAiB;EACvD,OAAO,IAAI,SAAS,SAAS,WAAW;GACtC,MAAM,SAAS,WAAG,kBAAkB,UAAU,EAAE,OAAO,IAAI,CAAC;GAE5D,OAAO,MAAM,UAAU,UAAU;IAC/B,OAAO,IAAI;IACX,IAAI,OACF,OAAO,KAAK;SAEZ,QAAQ,IAAI;GAEhB,CAAC;EACH,CAAC;CACH;AACF;;;;;;;;;;;ACxbA,SAAS,cACP,MACA,OACiC;CACjC,IAAI,iBAAiB,OAQnB,OAAO;EACL,GAAG;EACH,MAAM,MAAM;EACZ,SAAS,MAAM;EACf,OAAO,MAAM;CACf;CAGF,OAAO;AACT;;;;;;;;;;AAWA,SAAgB,kBAAkB,OAAgB,OAAwB;CACxE,4CAA2B,OAAO,eAAe,KAAK,KAAK;AAC7D;;;;ACnCA,IAAa,cAAb,cAAiC,QAA+B;;;cAIhD;;;;;CAKd,IAAW,YAAoB;EAC7B,OAAO;CACT;;;;CAKA,AAAO,YAAkB;EACvB,IAAI,KAAK,SAAS,WAAW,KAAK,OAAO,KAAK,KAAK,eAAe,EAAE,WAAW,GAAG;EAElF,IAAI,KAAK,yBAAyB;GAChC,KAAK,uBAAuB;GAC5B,KAAK,MAAM,OAAO,KAAK,iBAAiB;IACtC,MAAM,gBAAgB,aAAK,KAAK,KAAK,aAAa,GAAG;IACrD,WAAG,UAAU,eAAe,EAAE,WAAW,KAAK,CAAC;IAC/C,MAAM,WAAW,aAAK,KAAK,eAAe,GAAG,KAAK,SAAS,GAAG,KAAK,WAAW;IAE9E,IAAI,eAAe,EAAE,UAAU,CAAC,EAAW;IAC3C,IAAI,WAAG,WAAW,QAAQ,GACxB,IAAI;KACF,eAAe,KAAK,MAAM,WAAG,aAAa,UAAU,OAAO,CAAC;KAC5D,IAAI,CAAC,MAAM,QAAQ,aAAa,QAAQ,GAAG,aAAa,WAAW,CAAC;IACtE,SAAS,GAAG;KACV,eAAe,EAAE,UAAU,CAAC,EAAE;IAChC;IAEF,aAAa,SAAS,KAAK,GAAG,KAAK,gBAAgB,IAAI;IACvD,WAAG,cAAc,UAAU,kBAAkB,cAAc,CAAC,CAAC;GAC/D;EACF,OAAO;GACL,WAAG,UAAU,KAAK,aAAa,EAAE,WAAW,KAAK,CAAC;GAClD,IAAI,eAAe,EAAE,UAAU,CAAC,EAAW;GAC3C,IAAI,WAAG,WAAW,KAAK,QAAQ,GAC7B,IAAI;IACF,eAAe,KAAK,MAAM,WAAG,aAAa,KAAK,UAAU,OAAO,CAAC;IACjE,IAAI,CAAC,MAAM,QAAQ,aAAa,QAAQ,GAAG,aAAa,WAAW,CAAC;GACtE,SAAS,GAAG;IACV,eAAe,EAAE,UAAU,CAAC,EAAE;GAChC;GAEF,aAAa,SAAS,KAAK,GAAG,KAAK,QAAQ;GAC3C,WAAG,cAAc,KAAK,UAAU,kBAAkB,cAAc,CAAC,CAAC;EACpE;EAEA,KAAK,OAAO;CACd;;;;CAKA,MAAa,IAAI,MAAmB;EAClC,IAAI;EAEJ,IAAI,KAAK,mBAAmB,OAAO;GACjC,QAAQ,KAAK,QAAQ,OAAO,MAAM,IAAI;GACtC,KAAK,UAAU,KAAK,QAAQ;EAC9B;EAEA,MAAM,EAAE,QAAQ,QAAQ,SAAS,MAAM,OAAO,YAAY;EAE1D,IAAI,CAAC,KAAK,eAAe,IAAI,GAAG;EAEhC,MAAM,EAAE,MAAM,YAAY,SAAS,KAAK,qBAAqB;EAE7D,MAAM,0BAAa,EAAE,OAAO,aAAa,MAAM,IAAI;EAEnD,KAAK,SAAS,KAAK;GACjB,SAAS;GACT;GACA;GACA;GACA;GACA;GACA;GACA,4BAAW,IAAI,KAAK,GAAE,YAAY;EACpC,CAAe;EAEf,MAAM,KAAK,+BAA+B;CAC5C;;;;CAKA,MAAgB,sBAAqC;EACnD,IAAI,KAAK,SAAS,WAAW,KAAK,KAAK,aAAa,CAAC,KAAK,eAAe;EAEzE,KAAK,YAAY;EAEjB,IAAI,KAAK,yBACP,OAAO,MAAM,KAAK,2BAA2B;EAG/C,MAAM,KAAK,mBAAmB;EAE9B,IAAI,eAAe,EAAE,UAAU,CAAC,EAAW;EAE3C,IAAI,0CAAsB,KAAK,QAAQ,GACrC,IAAI;GACF,eAAgB,2CAAuB,KAAK,QAAQ;EACtD,SAAS,OAAO;GACd,QAAQ,MAAM,2CAA2C,KAAK;GAC9D,eAAe,EAAE,UAAU,CAAC,EAAE;EAChC;OAEA,eAAe,EAAE,UAAU,CAAC,EAAE;EAGhC,aAAa,SAAS,KAAK,GAAG,KAAK,QAAQ;EAE3C,IAAI;GACF,MAAM,WAAG,SAAS,UAAU,KAAK,UAAU,kBAAkB,cAAc,CAAC,CAAC;GAE7E,KAAK,OAAO;EACd,SAAS,OAAO;GACd,QAAQ,MAAM,wBAAwB,KAAK;GAE3C,KAAK,YAAY;EACnB;CACF;;;;CAKA,MAAgB,6BAA4C;EAE1D,KAAK,uBAAuB;EAG5B,KAAK,MAAM,OAAO,KAAK,iBAAiB;GACtC,MAAM,gBAAgB,aAAK,KAAK,KAAK,aAAa,GAAG;GAErD,+CAA2B,aAAa;GAExC,MAAM,WAAW,aAAK,KAAK,eAAe,GAAG,KAAK,SAAS,GAAG,KAAK,WAAW;GAE9E,MAAM,KAAK,mBAAmB,QAAQ;GAEtC,IAAI,eAAoC,EAAE,UAAU,CAAC,EAAE;GACvD,IAAI,0CAAsB,QAAQ,GAChC,IAAI;IACF,eAAgB,2CAAuB,QAAQ;GACjD,SAAS,OAAO;IACd,QAAQ,MAAM,2CAA2C,KAAK;IAC9D,eAAe,EAAE,UAAU,CAAC,EAAE;GAChC;QAEA,eAAe,EAAE,UAAU,CAAC,EAAE;GAGhC,aAAa,SAAS,KAAK,GAAG,KAAK,gBAAgB,IAAI;GAEvD,IAAI;IACF,MAAM,WAAG,SAAS,UAAU,UAAU,kBAAkB,cAAc,CAAC,CAAC;GAC1E,SAAS,OAAO;IACd,QAAQ,MAAM,wBAAwB,KAAK;IAC3C,KAAK,YAAY;GACnB;EACF;EAEA,KAAK,OAAO;CACd;AACF;;;;;;;;;;;;;;ACtKA,SAAS,WAAc,OAAU,uBAAO,IAAI,QAAqB,GAAM;CACrE,IAAI,UAAU,QAAQ,OAAO,UAAU,UACrC,OAAO;CAGT,IAAI,KAAK,IAAI,KAA0B,GACrC,OAAO,KAAK,IAAI,KAA0B;CAG5C,IAAI,iBAAiB,MACnB,OAAO,IAAI,KAAK,MAAM,QAAQ,CAAC;CAGjC,IAAI,iBAAiB,OAAO;EAC1B,MAAM,OAAO,IAAK,MAAM,YAAiC,MAAM,OAAO;EACtE,KAAK,QAAQ,MAAM;EACnB,KAAK,OAAO,MAAM;EAClB,OAAO;CACT;CAEA,IAAI,MAAM,QAAQ,KAAK,GAAG;EACxB,MAAM,MAAa,CAAC;EACpB,KAAK,IAAI,OAA4B,GAAG;EACxC,KAAK,MAAM,QAAQ,OACjB,IAAI,KAAK,WAAW,MAAM,IAAI,CAAC;EAEjC,OAAO;CACT;CAEA,MAAM,MAA2B,CAAC;CAClC,KAAK,IAAI,OAA4B,GAAG;CACxC,KAAK,MAAM,OAAO,OAAO,KAAK,KAA4B,GACxD,IAAI,OAAO,WAAY,MAA8B,MAAM,IAAI;CAEjE,OAAO;AACT;;;;;;;AAQA,SAAS,YAAY,OAAY,QAAsB,MAAqB;CAC1E,IAAI,OAAO,WAAW,YACpB,OAAO,OAAO,OAAO,KAAK,KAAK,GAAG,CAAC;CAErC,OAAO;AACT;;;;;;;;;;;;;AAcA,SAAS,aACP,QACA,UACA,QACA,WACM;CACN,IAAI,WAAW,QAAQ,OAAO,WAAW,UACvC;CAGF,IAAI,SAAS,WAAW,GACtB;CAGF,MAAM,CAAC,MAAM,GAAG,QAAQ;CAExB,IAAI,SAAS,MAAM;EAIjB,IAAI,KAAK,SAAS,GAChB,aAAa,QAAQ,MAAM,QAAQ,SAAS;EAE9C,MAAM,OAAO,MAAM,QAAQ,MAAM,IAC7B,OAAO,KAAK,GAAG,UAAU,OAAO,KAAK,CAAC,IACtC,OAAO,KAAK,MAAM;EACtB,KAAK,MAAM,OAAO,MAChB,aAAa,OAAO,MAAM,UAAU,QAAQ,CAAC,GAAG,WAAW,GAAG,CAAC;EAEjE;CACF;CAEA,MAAM,cACJ,SAAS,MACL,MAAM,QAAQ,MAAM,IAClB,OAAO,KAAK,GAAG,UAAU,OAAO,KAAK,CAAC,IACtC,OAAO,KAAK,MAAM,IACpB,MAAM,QAAQ,MAAM,IAElB,QAAQ,KAAK,IAAI,KAAK,OAAO,IAAI,IAAI,OAAO,SAC1C,CAAC,IAAI,IACL,CAAC,IACH,OAAO,UAAU,eAAe,KAAK,QAAQ,IAAI,IAC/C,CAAC,IAAI,IACL,CAAC;CAEX,KAAK,MAAM,OAAO,aAChB,IAAI,KAAK,WAAW,GAClB,OAAO,OAAO,YAAY,OAAO,MAAM,QAAQ,CAAC,GAAG,WAAW,GAAG,CAAC;MAElE,aAAa,OAAO,MAAM,MAAM,QAAQ,CAAC,GAAG,WAAW,GAAG,CAAC;AAGjE;;;;;;;;;AAUA,SAAgB,YACd,MACA,QACa;CACb,IAAI,CAAC,UAAU,OAAO,MAAM,WAAW,GACrC,OAAO;CAGT,MAAM,SAAS,OAAO,UAAU;CAChC,MAAM,SAAS,WAAW,IAAI;CAE9B,KAAK,MAAM,WAAW,OAAO,OAAO;EAClC,MAAM,WAAW,QAAQ,MAAM,GAAG,EAAE,QAAQ,YAAY,QAAQ,SAAS,CAAC;EAC1E,IAAI,SAAS,WAAW,GAAG;EAC3B,aAAa,QAAQ,UAAU,QAAQ,CAAC,CAAC;CAC3C;CAEA,OAAO;AACT;;;;;;;;;;AAWA,SAAgB,YACd,MACA,OAC0B;CAC1B,IAAI,CAAC,QAAQ,CAAC,OAAO,OAAO;CAC5B,IAAI,CAAC,MAAM,OAAO;CAClB,IAAI,CAAC,OAAO,OAAO;CAEnB,OAAO;EACL,OAAO,CAAC,GAAG,KAAK,OAAO,GAAG,MAAM,KAAK;EACrC,QAAQ,MAAM,UAAU,KAAK;CAC/B;AACF;;;;;;;AC/KA,SAAgB,aAAa,SAAc;CACzC,IAAI,OAAO,YAAY,UAAU,OAAO;CAGxC,OAAO,QAAQ,QAAQ,kBAAkB,EAAE;AAC7C;;;;ACIA,MAAM,gBAA6C,IAAI,IAAI;CACzD;CACA;CACA;CACA;CACA;AACF,CAAC;;;;;;;;AASD,MAAM,aAAuC;CAC3C,OAAO;CACP,MAAM;CACN,SAAS;CACT,MAAM;CACN,OAAO;AACT;AAEA,IAAa,SAAb,MAAoB;;kBAIc,CAAC;YAErB,YAAYC,8BAAO,OAAO,EAAE;2CAOZ,IAAI,IAAgC;;;;;CAqBhE,AAAO,WAAW,SAAqB;EACrC,KAAK,SAAS,KAAK,OAAO;EAE1B,OAAO;CACT;;;;CAKA,AAAO,UAAU,QAKd;EACD,IAAI,OAAO,UACT,KAAK,WAAW,OAAO;EAGzB,IAAI,OAAO,aACT,KAAK,gBAAgB,OAAO,WAAW;EAGzC,IAAI,OAAO,aAAa,QACtB,KAAK,YAAY,OAAO,QAAQ;EAGlC,IAAI,OAAO,WAAW,QACpB,KAAK,UAAU,OAAO,MAAM;EAG9B,OAAO;CACT;;;;;;;;;;;;CAaA,AAAO,UAAU,QAAwC;EACvD,KAAK,eAAe;EACpB,OAAO;CACT;;;;CAKA,AAAO,YAAsC;EAC3C,OAAO,KAAK;CACd;;;;;;;;;;;CAYA,AAAO,YAAY,OAAmC;EACpD,KAAK,WAAW;EAChB,OAAO;CACT;;;;CAKA,AAAO,cAAoC;EACzC,OAAO,KAAK;CACd;;;;CAKA,AAAO,YAAY,UAAwB;EACzC,KAAK,WAAW;EAEhB,OAAO;CACT;;;;CAKA,AAAQ,iBACN,cACA,QACA,UAAe,IACf,OACA,SACa;EACb,IAAI,OAAO,iBAAiB,UAE1B,OAAO;GACL,MAAO,SAAU,aAAqB,QAAQ;GAC9C,QAAQ,aAAa;GACrB,QAAQ,aAAa;GACrB,SAAS,aAAa;GACtB,GAAI,UAAU,EAAE,QAAQ,IAAI,aAAa,UAAU,EAAE,SAAS,aAAa,QAAQ,IAAI,CAAC;EAC1F;EAEF,OAAO;GACL,MAAO,SAAS;GAChB,QAAQ;GACA;GACR;GACA,GAAI,UAAU,EAAE,QAAQ,IAAI,CAAC;EAC/B;CACF;;;;;;;;;;CAWA,MAAa,IAAI,MAAmB;EAClC,IAAI,KAAK,YAAY,WAAW,KAAK,QAAQ,WAAW,KAAK,WAC3D,OAAO;EAMT,MAAM,YAAY,YAAY,MAAM,KAAK,YAAY;EAErD,KAAK,MAAM,WAAW,KAAK,UAAU;GACnC,MAAM,gBAAgB,QAAQ,kBAAkB;GAChD,MAAM,kBAAkB,gBACpB,YAAY,KAAK,cAAc,aAAa,IAC5C;GAMJ,IAAI,UAAU,kBAAkB,YAAY,MAAM,eAAe,IAAI;GAErE,IAAI,QAAQ,aAAa,OACvB,UAAU;IAAE,GAAG;IAAS,SAAS,aAAa,QAAQ,OAAO;GAAE;GAGjE,QAAQ,IAAI,OAAO;EACrB;EAEA,OAAO;CACT;;;;CAKA,AAAO,MACL,cACA,QACA,UAAe,IACf,SACA;EACA,MAAM,OAAO,KAAK,iBAAiB,cAAc,QAAQ,SAAS,SAAS,OAAO;EAClF,OAAO,KAAK,IAAI,IAAI;CACtB;;;;CAKA,AAAO,KACL,cACA,QACA,UAAe,IACf,SACA;EACA,MAAM,OAAO,KAAK,iBAAiB,cAAc,QAAQ,SAAS,QAAQ,OAAO;EACjF,OAAO,KAAK,IAAI,IAAI;CACtB;;;;CAKA,AAAO,KACL,cACA,QACA,UAAe,IACf,SACA;EACA,MAAM,OAAO,KAAK,iBAAiB,cAAc,QAAQ,SAAS,QAAQ,OAAO;EACjF,OAAO,KAAK,IAAI,IAAI;CACtB;;;;CAKA,AAAO,MACL,cACA,QACA,UAAe,IACf,SACA;EACA,MAAM,OAAO,KAAK,iBAAiB,cAAc,QAAQ,SAAS,SAAS,OAAO;EAClF,OAAO,KAAK,IAAI,IAAI;CACtB;;;;CAKA,AAAO,QACL,cACA,QACA,UAAe,IACf,SACA;EACA,MAAM,OAAO,KAAK,iBAAiB,cAAc,QAAQ,SAAS,WAAW,OAAO;EAEpF,OAAO,KAAK,IAAI,IAAI;CACtB;;;;;;;;;;CAWA,AAAO,OACL,WACA,QACA,QACA,SACA,SAC0B;EAC1B,IAAI,WAAW,OAAO;EACtB,OAAO,KAAK,MAAM,QAAQ,QAAQ,SAAS,OAAO;CACpD;;;;;;;;;;;CAYA,AAAO,MACL,QACA,QACkD;EAClD,MAAM,YAAY,KAAK,IAAI;EAC3B,QAAQ,UAAgC;GACtC,MAAM,aAAa,KAAK,IAAI,IAAI;GAChC,OAAO,KAAK,KAAK,QAAQ,QAAQ,gBAAgB,WAAW,KAAK;IAC/D;IACA,GAAI,SAAS,CAAC;GAChB,CAAC;EACH;CACF;;;;CAKA,AAAO,QAAQ,MAAc;EAC3B,OAAO,KAAK,SAAS,MAAM,YAAY,QAAQ,SAAS,IAAI;CAC9D;;;;CAKA,AAAO,YAAY;EACjB,KAAK,MAAM,WAAW,KAAK,UACzB,IAAI,QAAQ,WACV,QAAQ,UAAU;CAGxB;;;;;;;;;;;;;;;;;;;CAoBA,AAAO,gBAAgB,QAAgC;EACrD,KAAK,iBAAiB;EAEtB,KAAK,MAAM,SAAS,QAAQ;GAC1B,MAAM,UAAU,cAAc,IAAI,KAAK,UAC7B;IACJ,KAAK,UAAU;IACf,QAAQ,IAAI,OAAO,OAAO;IAC1B,QAAQ,KAAK,QAAQ,KAAK,KAAuB;GACnD,UACM;IACJ,KAAK,UAAU;GACjB;GAEJ,QAAQ,GAAG,OAAO,OAAO;GACzB,KAAK,kBAAkB,IAAI,OAAO,OAAO;EAC3C;EAEA,OAAO;CACT;;;;;CAMA,AAAO,mBAAyB;EAC9B,KAAK,MAAM,CAAC,OAAO,YAAY,KAAK,mBAClC,QAAQ,IAAI,OAAO,OAAO;EAG5B,KAAK,kBAAkB,MAAM;EAE7B,OAAO;CACT;AACF;;;;;;;;;;;;;;;AAgBA,MAAa,MAAM,IAAI,OAAO;;;;;;;;;;;;;;;AC1Z9B,SAAgB,+BAA+B;CAC7C,QAAQ,GAAG,uBAAuB,WAAgB;EAChD,IAAI,MAAM,OAAO,sBAAsB,MAAM;CAC/C,CAAC;CAED,QAAQ,GAAG,sBAAsB,UAAU;EACzC,IAAI,MAAM,OAAO,qBAAqB,KAAK;CAC7C,CAAC;AACH"}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import { BasicLogConfigurations, LoggingData } from "../types.mjs";
|
|
2
|
+
import { LogChannel } from "../log-channel.mjs";
|
|
3
|
+
|
|
4
|
+
//#region ../../@warlock.js/logger/src/channels/console-log.d.ts
|
|
5
|
+
type ConsoleLogConfig = BasicLogConfigurations & {
|
|
6
|
+
/**
|
|
7
|
+
* Render the log entry's `context` object on a second line after the main
|
|
8
|
+
* message. When `false`, context is silently dropped (the historical
|
|
9
|
+
* behavior). When `true`, contexts are pretty-printed with `util.inspect`
|
|
10
|
+
* — colored, depth-limited, ideal for development. Persistent channels
|
|
11
|
+
* (`FileLog`, `JSONFileLog`) always retain context regardless of this flag.
|
|
12
|
+
*
|
|
13
|
+
* @default false
|
|
14
|
+
*/
|
|
15
|
+
showContext?: boolean;
|
|
16
|
+
/**
|
|
17
|
+
* Depth passed to `util.inspect` when rendering context. Only applies when
|
|
18
|
+
* `showContext` is enabled.
|
|
19
|
+
*
|
|
20
|
+
* @default 4
|
|
21
|
+
*/
|
|
22
|
+
contextDepth?: number;
|
|
23
|
+
};
|
|
24
|
+
declare class ConsoleLog extends LogChannel<ConsoleLogConfig> {
|
|
25
|
+
/**
|
|
26
|
+
* {@inheritdoc}
|
|
27
|
+
*/
|
|
28
|
+
name: string;
|
|
29
|
+
/**
|
|
30
|
+
* Determine if channel is logging in terminal
|
|
31
|
+
*/
|
|
32
|
+
terminal: boolean;
|
|
33
|
+
/**
|
|
34
|
+
* {@inheritdoc}
|
|
35
|
+
*/
|
|
36
|
+
log(data: LoggingData): void;
|
|
37
|
+
}
|
|
38
|
+
//#endregion
|
|
39
|
+
export { ConsoleLog, ConsoleLogConfig };
|
|
40
|
+
//# sourceMappingURL=console-log.d.mts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"console-log.d.mts","names":[],"sources":["../../../../../../@warlock.js/logger/src/channels/console-log.ts"],"mappings":";;;;KAKY,gBAAA,GAAmB,sBAAsB;;AAArD;;;;;;;;EAUE,WAAA;EAUW;;;;;;EAHX,YAAA;AAAA;AAAA,cAGW,UAAA,SAAmB,UAAA,CAAW,gBAAA;EAAA;;;EAIlC,IAAA;EAUU;;;EALV,QAAA;;;;EAKA,GAAA,CAAI,IAAA,EAAM,WAAA;AAAA"}
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import { LogChannel } from "../log-channel.mjs";
|
|
2
|
+
import { colors } from "@mongez/copper";
|
|
3
|
+
import { inspect } from "util";
|
|
4
|
+
|
|
5
|
+
//#region ../../@warlock.js/logger/src/channels/console-log.ts
|
|
6
|
+
var ConsoleLog = class extends LogChannel {
|
|
7
|
+
constructor(..._args) {
|
|
8
|
+
super(..._args);
|
|
9
|
+
this.name = "console";
|
|
10
|
+
this.terminal = true;
|
|
11
|
+
}
|
|
12
|
+
/**
|
|
13
|
+
* {@inheritdoc}
|
|
14
|
+
*/
|
|
15
|
+
log(data) {
|
|
16
|
+
const { module, action, message, type: level } = data;
|
|
17
|
+
if (!this.shouldBeLogged(data)) return;
|
|
18
|
+
const date = (/* @__PURE__ */ new Date()).toISOString();
|
|
19
|
+
switch (level) {
|
|
20
|
+
case "debug":
|
|
21
|
+
console.log(colors.magentaBright("⚙"), colors.yellow(`(${date})`), colors.cyan(`[${module}]`), colors.magenta(`[${action}]`), colors.magentaBright(message));
|
|
22
|
+
break;
|
|
23
|
+
case "info":
|
|
24
|
+
console.log(colors.blueBright("ℹ"), colors.yellow(`(${date})`), colors.cyan(`[${module}]`), colors.magenta(`[${action}]`), colors.blueBright(message));
|
|
25
|
+
break;
|
|
26
|
+
case "warn":
|
|
27
|
+
console.log(colors.yellow("⚠"), colors.yellow(`(${date})`), colors.cyan(`[${module}]`), colors.magenta(`[${action}]`), colors.yellowBright(message));
|
|
28
|
+
break;
|
|
29
|
+
case "error":
|
|
30
|
+
console.log(colors.red("✗"), colors.yellow(`(${date})`), colors.cyan(`[${module}]`), colors.magenta(`[${action}]`), colors.redBright(message));
|
|
31
|
+
break;
|
|
32
|
+
case "success":
|
|
33
|
+
console.log(colors.green("✓"), colors.yellow(`(${date})`), colors.cyan(`[${module}]`), colors.magenta(`[${action}]`), colors.greenBright(message));
|
|
34
|
+
break;
|
|
35
|
+
default: console.log("[log]", colors.yellow(`(${date})`), colors.cyan(`[${module}]`), colors.magenta(`[${action}]`), message);
|
|
36
|
+
}
|
|
37
|
+
if (typeof message === "object") console.log(message);
|
|
38
|
+
if (this.config("showContext") && data.context && Object.keys(data.context).length > 0) {
|
|
39
|
+
const depth = this.config("contextDepth") ?? 4;
|
|
40
|
+
console.log(colors.gray(" ↳"), inspect(data.context, {
|
|
41
|
+
colors: true,
|
|
42
|
+
depth,
|
|
43
|
+
breakLength: 80
|
|
44
|
+
}));
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
};
|
|
48
|
+
|
|
49
|
+
//#endregion
|
|
50
|
+
export { ConsoleLog };
|
|
51
|
+
//# sourceMappingURL=console-log.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"console-log.mjs","names":[],"sources":["../../../../../../@warlock.js/logger/src/channels/console-log.ts"],"sourcesContent":["import { colors } from \"@mongez/copper\";\r\nimport { inspect } from \"util\";\r\nimport { LogChannel } from \"../log-channel\";\r\nimport type { BasicLogConfigurations, LoggingData } from \"../types\";\r\n\r\nexport type ConsoleLogConfig = BasicLogConfigurations & {\r\n /**\r\n * Render the log entry's `context` object on a second line after the main\r\n * message. When `false`, context is silently dropped (the historical\r\n * behavior). When `true`, contexts are pretty-printed with `util.inspect`\r\n * — colored, depth-limited, ideal for development. Persistent channels\r\n * (`FileLog`, `JSONFileLog`) always retain context regardless of this flag.\r\n *\r\n * @default false\r\n */\r\n showContext?: boolean;\r\n /**\r\n * Depth passed to `util.inspect` when rendering context. Only applies when\r\n * `showContext` is enabled.\r\n *\r\n * @default 4\r\n */\r\n contextDepth?: number;\r\n};\r\n\r\nexport class ConsoleLog extends LogChannel<ConsoleLogConfig> {\r\n /**\r\n * {@inheritdoc}\r\n */\r\n public name = \"console\";\r\n\r\n /**\r\n * Determine if channel is logging in terminal\r\n */\r\n public terminal = true;\r\n\r\n /**\r\n * {@inheritdoc}\r\n */\r\n public log(data: LoggingData) {\r\n const { module, action, message, type: level } = data;\r\n\r\n if (!this.shouldBeLogged(data)) return;\r\n\r\n // display date and time with milliseconds\r\n const date = new Date().toISOString(); // i.e 2021-01-01T00:00:00.000Z\r\n switch (level) {\r\n case \"debug\":\r\n // add a debug icon\r\n console.log(\r\n colors.magentaBright(\"⚙\"),\r\n colors.yellow(`(${date})`),\r\n colors.cyan(`[${module}]`),\r\n colors.magenta(`[${action}]`),\r\n colors.magentaBright(message),\r\n );\r\n break;\r\n case \"info\":\r\n // add an info icon\r\n console.log(\r\n colors.blueBright(\"ℹ\"),\r\n colors.yellow(`(${date})`),\r\n colors.cyan(`[${module}]`),\r\n colors.magenta(`[${action}]`),\r\n colors.blueBright(message),\r\n );\r\n break;\r\n case \"warn\":\r\n // add a warning icon\r\n console.log(\r\n colors.yellow(\"⚠\"),\r\n colors.yellow(`(${date})`),\r\n colors.cyan(`[${module}]`),\r\n colors.magenta(`[${action}]`),\r\n colors.yellowBright(message),\r\n );\r\n break;\r\n case \"error\":\r\n // add an error icon\r\n console.log(\r\n colors.red(\"✗\"),\r\n colors.yellow(`(${date})`),\r\n colors.cyan(`[${module}]`),\r\n colors.magenta(`[${action}]`),\r\n colors.redBright(message),\r\n );\r\n break;\r\n\r\n case \"success\":\r\n // add a success icon\r\n console.log(\r\n colors.green(\"✓\"),\r\n colors.yellow(`(${date})`),\r\n colors.cyan(`[${module}]`),\r\n colors.magenta(`[${action}]`),\r\n colors.greenBright(message),\r\n );\r\n break;\r\n\r\n default:\r\n console.log(\r\n \"[log]\",\r\n colors.yellow(`(${date})`),\r\n colors.cyan(`[${module}]`),\r\n colors.magenta(`[${action}]`),\r\n message,\r\n );\r\n }\r\n\r\n if (typeof message === \"object\") {\r\n console.log(message);\r\n }\r\n\r\n // Render context on a second line when explicitly enabled. We only\r\n // attempt rendering if there's anything meaningful to show — empty\r\n // objects clutter the terminal without adding signal.\r\n if (this.config(\"showContext\") && data.context && Object.keys(data.context).length > 0) {\r\n const depth = this.config(\"contextDepth\") ?? 4;\r\n console.log(\r\n colors.gray(\" ↳\"),\r\n inspect(data.context, { colors: true, depth, breakLength: 80 }),\r\n );\r\n }\r\n }\r\n}\r\n"],"mappings":";;;;;AAyBA,IAAa,aAAb,cAAgC,WAA6B;;;cAI7C;kBAKI;;;;;CAKlB,AAAO,IAAI,MAAmB;EAC5B,MAAM,EAAE,QAAQ,QAAQ,SAAS,MAAM,UAAU;EAEjD,IAAI,CAAC,KAAK,eAAe,IAAI,GAAG;EAGhC,MAAM,wBAAO,IAAI,KAAK,GAAE,YAAY;EACpC,QAAQ,OAAR;GACE,KAAK;IAEH,QAAQ,IACN,OAAO,cAAc,GAAG,GACxB,OAAO,OAAO,IAAI,KAAK,EAAE,GACzB,OAAO,KAAK,IAAI,OAAO,EAAE,GACzB,OAAO,QAAQ,IAAI,OAAO,EAAE,GAC5B,OAAO,cAAc,OAAO,CAC9B;IACA;GACF,KAAK;IAEH,QAAQ,IACN,OAAO,WAAW,GAAG,GACrB,OAAO,OAAO,IAAI,KAAK,EAAE,GACzB,OAAO,KAAK,IAAI,OAAO,EAAE,GACzB,OAAO,QAAQ,IAAI,OAAO,EAAE,GAC5B,OAAO,WAAW,OAAO,CAC3B;IACA;GACF,KAAK;IAEH,QAAQ,IACN,OAAO,OAAO,GAAG,GACjB,OAAO,OAAO,IAAI,KAAK,EAAE,GACzB,OAAO,KAAK,IAAI,OAAO,EAAE,GACzB,OAAO,QAAQ,IAAI,OAAO,EAAE,GAC5B,OAAO,aAAa,OAAO,CAC7B;IACA;GACF,KAAK;IAEH,QAAQ,IACN,OAAO,IAAI,GAAG,GACd,OAAO,OAAO,IAAI,KAAK,EAAE,GACzB,OAAO,KAAK,IAAI,OAAO,EAAE,GACzB,OAAO,QAAQ,IAAI,OAAO,EAAE,GAC5B,OAAO,UAAU,OAAO,CAC1B;IACA;GAEF,KAAK;IAEH,QAAQ,IACN,OAAO,MAAM,GAAG,GAChB,OAAO,OAAO,IAAI,KAAK,EAAE,GACzB,OAAO,KAAK,IAAI,OAAO,EAAE,GACzB,OAAO,QAAQ,IAAI,OAAO,EAAE,GAC5B,OAAO,YAAY,OAAO,CAC5B;IACA;GAEF,SACE,QAAQ,IACN,SACA,OAAO,OAAO,IAAI,KAAK,EAAE,GACzB,OAAO,KAAK,IAAI,OAAO,EAAE,GACzB,OAAO,QAAQ,IAAI,OAAO,EAAE,GAC5B,OACF;EACJ;EAEA,IAAI,OAAO,YAAY,UACrB,QAAQ,IAAI,OAAO;EAMrB,IAAI,KAAK,OAAO,aAAa,KAAK,KAAK,WAAW,OAAO,KAAK,KAAK,OAAO,EAAE,SAAS,GAAG;GACtF,MAAM,QAAQ,KAAK,OAAO,cAAc,KAAK;GAC7C,QAAQ,IACN,OAAO,KAAK,KAAK,GACjB,QAAQ,KAAK,SAAS;IAAE,QAAQ;IAAM;IAAO,aAAa;GAAG,CAAC,CAChE;EACF;CACF;AACF"}
|
|
@@ -0,0 +1,194 @@
|
|
|
1
|
+
import { BasicLogConfigurations, LogContract, LogLevel, LogMessage, LoggingData } from "../types.mjs";
|
|
2
|
+
import { LogChannel } from "../log-channel.mjs";
|
|
3
|
+
|
|
4
|
+
//#region ../../@warlock.js/logger/src/channels/file-log.d.ts
|
|
5
|
+
type FileLogConfig = BasicLogConfigurations & {
|
|
6
|
+
storagePath?: string;
|
|
7
|
+
/**
|
|
8
|
+
* File name, without extension
|
|
9
|
+
*/
|
|
10
|
+
name?: string;
|
|
11
|
+
/**
|
|
12
|
+
* chunk mode
|
|
13
|
+
* If set to `single`, the logs will be created in a single file, unless the rotate is set to true
|
|
14
|
+
* If set to `daily`, the logs will be created in a daily file, unless the rotate is set to true
|
|
15
|
+
* If set to `hourly`, the logs will be created in an hourly file, unless the rotate is set to true
|
|
16
|
+
* @default single
|
|
17
|
+
*/
|
|
18
|
+
chunk?: "single" | "daily" | "hourly";
|
|
19
|
+
/**
|
|
20
|
+
* Whether to rotate the file
|
|
21
|
+
*
|
|
22
|
+
* @default true
|
|
23
|
+
*/
|
|
24
|
+
rotate?: boolean;
|
|
25
|
+
/**
|
|
26
|
+
* File Extension
|
|
27
|
+
*
|
|
28
|
+
* @default log
|
|
29
|
+
*/
|
|
30
|
+
extension?: string;
|
|
31
|
+
/**
|
|
32
|
+
* If rotate is set, the rotate name will be added to the file name suffixed with `-`
|
|
33
|
+
*
|
|
34
|
+
* @default DD-MM-YYYY
|
|
35
|
+
*/
|
|
36
|
+
rotateFileName?: string;
|
|
37
|
+
/**
|
|
38
|
+
* Max file size before rotating the file
|
|
39
|
+
*
|
|
40
|
+
* @default 10MB
|
|
41
|
+
*/
|
|
42
|
+
maxFileSize?: number;
|
|
43
|
+
/**
|
|
44
|
+
* Set the max messages that needs to be added before writing to the file
|
|
45
|
+
*
|
|
46
|
+
* @default 100
|
|
47
|
+
*/
|
|
48
|
+
maxMessagesToWrite?: number;
|
|
49
|
+
/**
|
|
50
|
+
* Group logs by
|
|
51
|
+
* Please note that the order matters here
|
|
52
|
+
* For example, if you set `groupBy: ['level', 'module']`, the logs will be added in level name first, then by module
|
|
53
|
+
*
|
|
54
|
+
* @default none
|
|
55
|
+
*/
|
|
56
|
+
groupBy?: ("level" | "module" | "action")[];
|
|
57
|
+
/**
|
|
58
|
+
* Define what levels should be logged
|
|
59
|
+
*
|
|
60
|
+
* @default all
|
|
61
|
+
*/
|
|
62
|
+
levels?: LogLevel[];
|
|
63
|
+
/**
|
|
64
|
+
* Date and time format
|
|
65
|
+
*/
|
|
66
|
+
dateFormat?: {
|
|
67
|
+
date?: string;
|
|
68
|
+
time?: string;
|
|
69
|
+
};
|
|
70
|
+
};
|
|
71
|
+
declare class FileLog extends LogChannel<FileLogConfig> implements LogContract {
|
|
72
|
+
/**
|
|
73
|
+
* {@inheritdoc}
|
|
74
|
+
*/
|
|
75
|
+
name: string;
|
|
76
|
+
/**
|
|
77
|
+
* Messages buffer
|
|
78
|
+
*/
|
|
79
|
+
protected messages: LogMessage[];
|
|
80
|
+
/**
|
|
81
|
+
* Grouped messages
|
|
82
|
+
*/
|
|
83
|
+
protected groupedMessages: Record<string, LogMessage[]>;
|
|
84
|
+
/**
|
|
85
|
+
* Default channel configurations
|
|
86
|
+
*/
|
|
87
|
+
protected defaultConfigurations: FileLogConfig;
|
|
88
|
+
/**
|
|
89
|
+
* Last write time
|
|
90
|
+
*/
|
|
91
|
+
protected lastWriteTime: number;
|
|
92
|
+
/**
|
|
93
|
+
* A flag to determine if the file is being written
|
|
94
|
+
*/
|
|
95
|
+
protected isWriting: boolean;
|
|
96
|
+
/**
|
|
97
|
+
* Handle for the periodic flush interval. Stored so it can be cleared
|
|
98
|
+
* in `dispose()` — long-lived processes that create channels dynamically
|
|
99
|
+
* would otherwise leak one timer per channel.
|
|
100
|
+
*/
|
|
101
|
+
protected flushIntervalHandle?: NodeJS.Timeout;
|
|
102
|
+
/**
|
|
103
|
+
* Check file size for file rotation
|
|
104
|
+
*/
|
|
105
|
+
protected checkAndRotateFile(filePath?: string): Promise<void>;
|
|
106
|
+
/**
|
|
107
|
+
* Rotate log file
|
|
108
|
+
*/
|
|
109
|
+
protected rotateLogFile(): Promise<void>;
|
|
110
|
+
/**
|
|
111
|
+
* Flush messages
|
|
112
|
+
*
|
|
113
|
+
* Starts a periodic re-check so low-traffic channels don't sit on buffered
|
|
114
|
+
* entries indefinitely. The handle is stored on the instance so `dispose()`
|
|
115
|
+
* can stop it — without this, every channel leaks a timer for the lifetime
|
|
116
|
+
* of the process.
|
|
117
|
+
*/
|
|
118
|
+
protected initMessageFlush(): void;
|
|
119
|
+
/**
|
|
120
|
+
* Stop the background flush interval and drain any buffered entries.
|
|
121
|
+
*
|
|
122
|
+
* Call this when discarding a channel (e.g. reconfiguring the logger at
|
|
123
|
+
* runtime) so the 5-second timer doesn't keep the event loop alive. Safe to
|
|
124
|
+
* call more than once.
|
|
125
|
+
*/
|
|
126
|
+
dispose(): void;
|
|
127
|
+
/**
|
|
128
|
+
* Get file path
|
|
129
|
+
*/
|
|
130
|
+
get filePath(): string;
|
|
131
|
+
/**
|
|
132
|
+
* Get max messages
|
|
133
|
+
*/
|
|
134
|
+
protected get maxMessagesToWrite(): number;
|
|
135
|
+
/**
|
|
136
|
+
* Get file name
|
|
137
|
+
*/
|
|
138
|
+
get fileName(): string;
|
|
139
|
+
/**
|
|
140
|
+
* Get file extension
|
|
141
|
+
*/
|
|
142
|
+
get extension(): string;
|
|
143
|
+
/**
|
|
144
|
+
* Get content
|
|
145
|
+
*/
|
|
146
|
+
protected get content(): string;
|
|
147
|
+
/**
|
|
148
|
+
* Get storage path
|
|
149
|
+
*/
|
|
150
|
+
get storagePath(): string;
|
|
151
|
+
/**
|
|
152
|
+
* {@inheritdoc}
|
|
153
|
+
*/
|
|
154
|
+
protected init(): Promise<void>;
|
|
155
|
+
/**
|
|
156
|
+
* Synchronously flush messages
|
|
157
|
+
*/
|
|
158
|
+
flushSync(): void;
|
|
159
|
+
/**
|
|
160
|
+
* {@inheritdoc}
|
|
161
|
+
*/
|
|
162
|
+
log(data: LoggingData): Promise<void>;
|
|
163
|
+
/**
|
|
164
|
+
* Check if messages should be written
|
|
165
|
+
*/
|
|
166
|
+
protected checkIfMessagesShouldBeWritten(): Promise<void>;
|
|
167
|
+
/**
|
|
168
|
+
* Should be called after messages are saved
|
|
169
|
+
*/
|
|
170
|
+
protected onSave(): void;
|
|
171
|
+
/**
|
|
172
|
+
* Check if messages should be grouped
|
|
173
|
+
*/
|
|
174
|
+
protected get messagedShouldBeGrouped(): boolean;
|
|
175
|
+
/**
|
|
176
|
+
* Write messages to the file
|
|
177
|
+
*/
|
|
178
|
+
protected writeMessagesToFile(): Promise<void>;
|
|
179
|
+
/**
|
|
180
|
+
* Write grouped messages to the file
|
|
181
|
+
*/
|
|
182
|
+
protected writeGroupedMessagesToFile(): Promise<void>;
|
|
183
|
+
/**
|
|
184
|
+
* Prepare grouped messages
|
|
185
|
+
*/
|
|
186
|
+
protected prepareGroupedMessages(): void;
|
|
187
|
+
/**
|
|
188
|
+
* Start writing to the file
|
|
189
|
+
*/
|
|
190
|
+
protected write(filePath: string, content: string): Promise<unknown>;
|
|
191
|
+
}
|
|
192
|
+
//#endregion
|
|
193
|
+
export { FileLog, FileLogConfig };
|
|
194
|
+
//# sourceMappingURL=file-log.d.mts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"file-log.d.mts","names":[],"sources":["../../../../../../@warlock.js/logger/src/channels/file-log.ts"],"mappings":";;;;KAgBY,aAAA,GAAgB,sBAAA;EAC1B,WAAA;EADU;;;EAKV,IAAA;EAL0B;;;;;;;EAa1B,KAAA;EA8BA;;;;;EAxBA,MAAA;EA4CE;;AAAI;AAIR;;EA1CE,SAAA;EA0CsC;;;;;EApCtC,cAAA;EA6F2D;;;;;EAvF3D,WAAA;EA0TmC;;;;;EApTnC,kBAAA;EAwB2E;;;;;;;EAhB3E,OAAA;EA8B2B;;;;;EAxB3B,MAAA,GAAS,QAAQ;EA8DP;;;EA1DV,UAAA;IACE,IAAA;IACA,IAAA;EAAA;AAAA;AAAA,cAIS,OAAA,SAAgB,UAAA,CAAW,aAAA,aAA0B,WAAA;EAmHzD;;;EA/GA,IAAA;EA8JI;;;EAAA,UAzJD,QAAA,EAAU,UAAA;EA8KA;;;EAAA,UAzKV,eAAA,EAAiB,MAAA,SAAe,UAAA;EA2MzB;;;EAAA,UAtMP,qBAAA,EAAuB,aAAA;EAwPvB;;;EAAA,UAnOA,aAAA;EA4QM;;;EAAA,UAvQN,SAAA;EAmTY;;;;AAAiC;EAAjC,UA5SZ,mBAAA,GAAsB,MAAA,CAAO,OAAA;;;;YAKvB,kBAAA,CAAmB,QAAA,YAAwB,OAAA;;;;YAoB3C,aAAA,CAAA,GAAa,OAAA;;;;;;;;;YAoBnB,gBAAA,CAAA;;;;;;;;EAkBH,OAAA,CAAA;;;;MAYI,QAAA,CAAA;;;;gBAWG,kBAAA,CAAA;;;;MAOH,QAAA,CAAA;;;;MAiBA,SAAA,CAAA;;;;gBAOG,OAAA,CAAA;;;;MAOH,WAAA,CAAA;;;;YAOK,IAAA,CAAA,GAAI,OAAA;;;;EAWb,SAAA,CAAA;;;;EAuBM,GAAA,CAAI,IAAA,EAAM,WAAA,GAAW,OAAA;;;;YAyClB,8BAAA,CAAA,GAA8B,OAAA;;;;YASpC,MAAA,CAAA;;;;gBAUI,uBAAA,CAAA;;;;YAOE,mBAAA,CAAA,GAAmB,OAAA;;;;YAwBnB,0BAAA,CAAA,GAA8B,OAAA;;;;YA8BpC,sBAAA,CAAA;;;;YAcM,KAAA,CAAM,QAAA,UAAkB,OAAA,WAAe,OAAA;AAAA"}
|