@warlock.js/logger 4.1.14 → 4.2.0

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.
Files changed (61) hide show
  1. package/CHANGELOG.md +30 -0
  2. package/LICENSE +21 -0
  3. package/README.md +146 -145
  4. package/cjs/index.cjs +276 -11
  5. package/cjs/index.cjs.map +1 -1
  6. package/esm/channels/console-log.d.mts +1 -1
  7. package/esm/channels/console-log.d.mts.map +1 -1
  8. package/esm/channels/console-log.mjs +4 -1
  9. package/esm/channels/console-log.mjs.map +1 -1
  10. package/esm/channels/file-log.d.mts +13 -3
  11. package/esm/channels/file-log.d.mts.map +1 -1
  12. package/esm/channels/file-log.mjs +15 -1
  13. package/esm/channels/file-log.mjs.map +1 -1
  14. package/esm/channels/index.mjs +1 -0
  15. package/esm/channels/json-file-log.d.mts +1 -1
  16. package/esm/channels/json-file-log.d.mts.map +1 -1
  17. package/esm/channels/json-file-log.mjs +1 -1
  18. package/esm/channels/json-file-log.mjs.map +1 -1
  19. package/esm/channels/sentry-log.d.mts +182 -0
  20. package/esm/channels/sentry-log.d.mts.map +1 -0
  21. package/esm/channels/sentry-log.mjs +202 -0
  22. package/esm/channels/sentry-log.mjs.map +1 -0
  23. package/esm/index.d.mts +2 -1
  24. package/esm/index.mjs +2 -1
  25. package/esm/log-channel.d.mts +9 -1
  26. package/esm/log-channel.d.mts.map +1 -1
  27. package/esm/log-channel.mjs +1 -1
  28. package/esm/log-channel.mjs.map +1 -1
  29. package/esm/logger.d.mts +32 -1
  30. package/esm/logger.d.mts.map +1 -1
  31. package/esm/logger.mjs +44 -2
  32. package/esm/logger.mjs.map +1 -1
  33. package/esm/redact/redact.d.mts +1 -1
  34. package/esm/redact/redact.d.mts.map +1 -1
  35. package/esm/redact/redact.mjs +1 -1
  36. package/esm/redact/redact.mjs.map +1 -1
  37. package/esm/types.d.mts +19 -3
  38. package/esm/types.d.mts.map +1 -1
  39. package/esm/utils/capture-unhandled-errors.d.mts +9 -1
  40. package/esm/utils/capture-unhandled-errors.d.mts.map +1 -1
  41. package/esm/utils/capture-unhandled-errors.mjs +10 -2
  42. package/esm/utils/capture-unhandled-errors.mjs.map +1 -1
  43. package/esm/utils/clear-message.d.mts +1 -1
  44. package/esm/utils/clear-message.d.mts.map +1 -1
  45. package/esm/utils/clear-message.mjs +1 -1
  46. package/esm/utils/clear-message.mjs.map +1 -1
  47. package/esm/utils/safe-json-stringify.d.mts +1 -1
  48. package/esm/utils/safe-json-stringify.d.mts.map +1 -1
  49. package/esm/utils/safe-json-stringify.mjs +1 -1
  50. package/esm/utils/safe-json-stringify.mjs.map +1 -1
  51. package/llms-full.txt +1497 -1296
  52. package/llms.txt +20 -19
  53. package/package.json +5 -2
  54. package/skills/capture-unhandled-errors/SKILL.md +103 -103
  55. package/skills/filter-log-entries/SKILL.md +120 -120
  56. package/skills/flush-logs-on-shutdown/SKILL.md +117 -91
  57. package/skills/logger-basics/SKILL.md +88 -85
  58. package/skills/overview/SKILL.md +90 -86
  59. package/skills/pick-log-channel/SKILL.md +155 -139
  60. package/skills/ship-logs-to-sentry/SKILL.md +118 -0
  61. package/skills/write-custom-log-channel/SKILL.md +190 -160
package/llms.txt CHANGED
@@ -1,19 +1,20 @@
1
- # Warlock Logger
2
-
3
- > Package: `@warlock.js/logger`
4
-
5
- > A powerful logging system for messages and errors in nodejs.
6
-
7
- ## Skills
8
-
9
- - [capture-unhandled-errors](@warlock.js/logger/capture-unhandled-errors/SKILL.md): captureAnyUnhandledRejection() installs process.on('unhandledRejection') + ('uncaughtException') listeners routing failures through log.error('app', ...). Triggers: `captureAnyUnhandledRejection`, `unhandledRejection`, `uncaughtException`, `log.error`; "log unhandled promise rejections", "catch uncaught exceptions to a file", "record crashes before exit", "global error handler with logger"; typical import `import { captureAnyUnhandledRejection, log } from "@warlock.js/logger"`. Skip: flushing — `@warlock.js/logger/flush-logs-on-shutdown/SKILL.md`; filtering — `@warlock.js/logger/filter-log-entries/SKILL.md`; competing `Sentry.init`, `@sentry/node`; native `process.on('unhandledRejection')`.
10
- - [configure-logger](@warlock.js/logger/configure-logger/SKILL.md): Register channels via log.addChannel / log.setChannels / log.configure({channels, autoFlushOn, redact, minLevel}) at boot. Triggers: `log.configure`, `log.addChannel`, `log.setChannels`, `Logger`, `autoFlushOn`, `disableAutoFlush`; "wire channels at startup", "branch logger by NODE_ENV", "isolate a library's logger", "replace channel list"; typical import `import { log, Logger, ConsoleLog, FileLog } from "@warlock.js/logger"`. Skip: channel picks — `@warlock.js/logger/pick-log-channel/SKILL.md`; flushing — `@warlock.js/logger/flush-logs-on-shutdown/SKILL.md`; redaction — `@warlock.js/logger/redact-sensitive-log-fields/SKILL.md`; competing libs `winston.createLogger`, `pino`.
11
- - [filter-log-entries](@warlock.js/logger/filter-log-entries/SKILL.md): Drop log entries — per-channel levels whitelist, per-channel filter predicate, logger-wide setMinLevel(level) fast path. Triggers: `levels`, `filter`, `minLevel`, `log.setMinLevel`, `shouldBeLogged`, `LoggingData`, `LogLevel`; "silence a noisy module", "route errors to a dedicated file", "raise global severity floor", "drop debug logs in prod"; typical import `import { log } from "@warlock.js/logger"`. Skip: custom sinks — `@warlock.js/logger/write-custom-log-channel/SKILL.md`; channel picks — `@warlock.js/logger/pick-log-channel/SKILL.md`; competing libs `pino.levels`, `winston.format.filter`, `debug` env var.
12
- - [flush-logs-on-shutdown](@warlock.js/logger/flush-logs-on-shutdown/SKILL.md): Drain buffered channels before exit — log.flushSync() or log.configure({autoFlushOn: ['SIGINT', 'SIGTERM', 'beforeExit']}) installs handlers that re-raise the signal. Triggers: `log.flushSync`, `autoFlushOn`, `enableAutoFlush`, `disableAutoFlush`, `SIGINT`, `SIGTERM`, `beforeExit`; "drain logs before exit", "wire SIGTERM for container shutdown", "my logs never showed after a crash", "graceful shutdown logging"; typical import `import { log, FileLog } from "@warlock.js/logger"`. Skip: error capture — `@warlock.js/logger/capture-unhandled-errors/SKILL.md`; custom sinks — `@warlock.js/logger/write-custom-log-channel/SKILL.md`; competing `pino.final`, `winston.end`; native `process.on('exit')`.
13
- - [logger-basics](@warlock.js/logger/logger-basics/SKILL.md): Start with @warlock.js/logger — the log singleton, five levels (debug / info / warn / error / success), channel fan-out, foundations. Triggers: `log`, `Logger`, `log.info`, `log.error`, `log.debug`, `log.warn`, `log.success`, `ConsoleLog`, `FileLog`, `JSONFileLog`; "how do I log in node", "warlock logger basics", "which logger skill do I need"; typical import `import { log, ConsoleLog, FileLog } from "@warlock.js/logger"`. Skip: channel picks — `@warlock.js/logger/pick-log-channel/SKILL.md`; setup — `@warlock.js/logger/configure-logger/SKILL.md`; competing libs `winston`, `pino`, `bunyan`, `log4js`, `signale`; native `console.log`.
14
- - [overview](@warlock.js/logger/overview/SKILL.md): Front-door orientation for `@warlock.js/logger` — structured channel-based logging with five severity levels, PII redaction floor, buffered file/JSON channels, signal-flush on shutdown, ergonomic helpers (timer, assert). Standalone — no `@warlock.js/core` required. TRIGGER when: code imports anything from `@warlock.js/logger`; user asks "what does @warlock.js/logger do", "compare with pino / winston / bunyan", "structured logging for Node", "which logger should I use", "how do channels work"; package.json adds `@warlock.js/logger`. Skip: specific task already known — load the matching task skill directly (`logger-basics`, `configure-logger`, `pick-log-channel`, `write-custom-log-channel`, `redact-sensitive-log-fields`, `filter-log-entries`, `flush-logs-on-shutdown`, `capture-unhandled-errors`, `use-log-helpers`, `test-logging-code`); plain `console.log` in throwaway scripts.
15
- - [pick-log-channel](@warlock.js/logger/pick-log-channel/SKILL.md): Pick one of the three built-in channels — ConsoleLog (terminal), FileLog (plain text on disk), JSONFileLog (structured JSON for aggregators like Loki / Datadog / Elastic). Triggers: `ConsoleLog`, `FileLog`, `JSONFileLog`, `chunk`, `rotate`, `groupBy`, `maxFileSize`, `showContext`, `log.channel`; "log to a file", "rotate log files", "daily log chunks", "json logs for datadog / loki / elastic"; typical import `import { ConsoleLog, FileLog, JSONFileLog } from "@warlock.js/logger"`. Skip: custom sinks — `@warlock.js/logger/write-custom-log-channel/SKILL.md`; registration — `@warlock.js/logger/configure-logger/SKILL.md`; competing libs `winston-daily-rotate-file`, `pino-pretty`.
16
- - [redact-sensitive-log-fields](@warlock.js/logger/redact-sensitive-log-fields/SKILL.md): Strip secrets from log output — two-layer additive redaction via log.configure({redact: {paths}}) (logger floor) + per-channel redact (more paths on top). Dotted glob paths (*, **). Triggers: `redact`, `paths`, `censor`, `log.setRedact`, `applyRedact`; "redact passwords in logs", "strip tokens from log output", "hide authorization headers", "scrub PII before logging"; typical import `import { log } from "@warlock.js/logger"`. Skip: filtering — `@warlock.js/logger/filter-log-entries/SKILL.md`; custom sinks — `@warlock.js/logger/write-custom-log-channel/SKILL.md`; competing libs `pino.redact`, `fast-redact`.
17
- - [test-logging-code](@warlock.js/logger/test-logging-code/SKILL.md): Test code that touches the loggersilence globally via log.setChannels([]) in setupFiles, assert specific log lines via a capturing LogChannel subclass (prefer it over vi.spyOn it asserts on delivered entries, not just method calls, and isolates the shared singleton cleanly). Triggers: `log.setChannels`, `LogChannel`, `LoggingData`, `Logger`, `log.channels`; "silence logger in vitest", "assert a log line was emitted", "capture log output in tests", "test code that logs"; typical import `import { log, Logger, LogChannel, type LoggingData } from "@warlock.js/logger"`. Skip: custom sinks — `@warlock.js/logger/write-custom-log-channel/SKILL.md`; filtering — `@warlock.js/logger/filter-log-entries/SKILL.md`; competing `vi.spyOn(console)`, `jest.spyOn`.
18
- - [use-log-helpers](@warlock.js/logger/use-log-helpers/SKILL.md): Two DX shortcuts on every Logger — log.assert(condition, module, action, message, context?) logs an error when condition is falsy (free on the happy path), log.timer(module, action) returns an end-function emitting an info entry with measured duration. Triggers: `log.assert`, `log.timer`, `durationMs`; "assert an invariant via logger", "measure how long an operation took", "time a request", "log operation duration"; typical import `import { log } from "@warlock.js/logger"`. Skip: basics — `@warlock.js/logger/logger-basics/SKILL.md`; filtering — `@warlock.js/logger/filter-log-entries/SKILL.md`; competing `console.assert`, `console.time`, `console.timeEnd`, `perf_hooks.performance.now`.
19
- - [write-custom-log-channel](@warlock.js/logger/write-custom-log-channel/SKILL.md): Extend the abstract LogChannel class for custom sinks Slack, database, HTTP endpoint, in-memory buffer. Triggers: `LogChannel`, `LogContract`, `LoggingData`, `shouldBeLogged`, `init`, `flushSync`, `terminal`; "log to slack", "log to a database", "send logs to datadog / loki HTTP api", "in-memory test capture channel", "build a custom log sink"; typical import `import { LogChannel, type LoggingData, type LogContract } from "@warlock.js/logger"`. Skip: built-in channels — `@warlock.js/logger/pick-log-channel/SKILL.md`; filtering — `@warlock.js/logger/filter-log-entries/SKILL.md`; competing libs `winston-transport`, `pino-transport`.
1
+ # Warlock Logger
2
+
3
+ > Package: `@warlock.js/logger`
4
+
5
+ > A powerful logging system for messages and errors in nodejs.
6
+
7
+ ## Skills
8
+
9
+ - [capture-unhandled-errors](@warlock.js/logger/capture-unhandled-errors/SKILL.md): captureAnyUnhandledRejection() installs process.on('unhandledRejection') log.error and process.on('uncaughtException') log.fatal so process-level failures land in your configured channels. Triggers: `captureAnyUnhandledRejection`, `unhandledRejection`, `uncaughtException`, `log.error`, `log.fatal`; "log unhandled promise rejections", "catch uncaught exceptions to a file", "record crashes before exit", "global error handler with logger"; typical import `import { captureAnyUnhandledRejection, log } from "@warlock.js/logger"`. Skip: flushing — `@warlock.js/logger/flush-logs-on-shutdown/SKILL.md`; filtering — `@warlock.js/logger/filter-log-entries/SKILL.md`; competing `Sentry.init`, `@sentry/node`; native `process.on('unhandledRejection')`.
10
+ - [configure-logger](@warlock.js/logger/configure-logger/SKILL.md): Register channels via log.addChannel / log.setChannels / log.configure({channels, autoFlushOn, redact, minLevel}) at boot. Triggers: `log.configure`, `log.addChannel`, `log.setChannels`, `Logger`, `autoFlushOn`, `disableAutoFlush`; "wire channels at startup", "branch logger by NODE_ENV", "isolate a library's logger", "replace channel list"; typical import `import { log, Logger, ConsoleLog, FileLog } from "@warlock.js/logger"`. Skip: channel picks — `@warlock.js/logger/pick-log-channel/SKILL.md`; flushing — `@warlock.js/logger/flush-logs-on-shutdown/SKILL.md`; redaction — `@warlock.js/logger/redact-sensitive-log-fields/SKILL.md`; competing libs `winston.createLogger`, `pino`.
11
+ - [filter-log-entries](@warlock.js/logger/filter-log-entries/SKILL.md): Drop log entries — per-channel levels whitelist, per-channel filter predicate, logger-wide setMinLevel(level) fast path. Triggers: `levels`, `filter`, `minLevel`, `log.setMinLevel`, `shouldBeLogged`, `LoggingData`, `LogLevel`; "silence a noisy module", "route errors to a dedicated file", "raise global severity floor", "drop debug logs in prod"; typical import `import { log } from "@warlock.js/logger"`. Skip: custom sinks — `@warlock.js/logger/write-custom-log-channel/SKILL.md`; channel picks — `@warlock.js/logger/pick-log-channel/SKILL.md`; competing libs `pino.levels`, `winston.format.filter`, `debug` env var.
12
+ - [flush-logs-on-shutdown](@warlock.js/logger/flush-logs-on-shutdown/SKILL.md): Drain buffered channels before exit — log.flushSync() or log.configure({autoFlushOn: ['SIGINT', 'SIGTERM', 'beforeExit']}) installs handlers that re-raise the signal. Triggers: `log.flush`, `log.flushSync`, `autoFlushOn`, `enableAutoFlush`, `disableAutoFlush`, `SIGINT`, `SIGTERM`, `beforeExit`; "drain logs before exit", "await log.flush() before process.exit", "drain async or network channels on shutdown", "wire SIGTERM for container shutdown", "my logs never showed after a crash", "graceful shutdown logging"; typical import `import { log, FileLog } from "@warlock.js/logger"`. Skip: error capture — `@warlock.js/logger/capture-unhandled-errors/SKILL.md`; custom sinks — `@warlock.js/logger/write-custom-log-channel/SKILL.md`; competing `pino.final`, `winston.end`; native `process.on('exit')`.
13
+ - [logger-basics](@warlock.js/logger/logger-basics/SKILL.md): Start with @warlock.js/logger — the log singleton, six levels (debug / info / warn / error / success / fatal), channel fan-out, foundations. Triggers: `log`, `Logger`, `log.info`, `log.error`, `log.fatal`, `log.debug`, `log.warn`, `log.success`, `ConsoleLog`, `FileLog`, `JSONFileLog`; "how do I log in node", "warlock logger basics", "which logger skill do I need"; typical import `import { log, ConsoleLog, FileLog } from "@warlock.js/logger"`. Skip: channel picks — `@warlock.js/logger/pick-log-channel/SKILL.md`; setup — `@warlock.js/logger/configure-logger/SKILL.md`; competing libs `winston`, `pino`, `bunyan`, `log4js`, `signale`; native `console.log`.
14
+ - [overview](@warlock.js/logger/overview/SKILL.md): Front-door orientation for `@warlock.js/logger` — structured channel-based logging with six severity levels (debug / info / warn / error / success / fatal), PII redaction floor, buffered file/JSON channels, optional SentryLog forwarding, async log.flush() + signal-flush on shutdown, ergonomic helpers (timer, assert). Standalone — no `@warlock.js/core` required. TRIGGER when: code imports anything from `@warlock.js/logger`; user asks "what does @warlock.js/logger do", "compare with pino / winston / bunyan", "structured logging for Node", "which logger should I use", "how do channels work"; package.json adds `@warlock.js/logger`. Skip: specific task already known — load the matching task skill directly (`logger-basics`, `configure-logger`, `pick-log-channel`, `write-custom-log-channel`, `ship-logs-to-sentry`, `redact-sensitive-log-fields`, `filter-log-entries`, `flush-logs-on-shutdown`, `capture-unhandled-errors`, `use-log-helpers`, `test-logging-code`); plain `console.log` in throwaway scripts.
15
+ - [pick-log-channel](@warlock.js/logger/pick-log-channel/SKILL.md): Pick one of the four built-in channels — ConsoleLog (terminal), FileLog (plain text on disk), JSONFileLog (structured JSON for aggregators like Loki / Datadog / Elastic), SentryLog (forwards errors + breadcrumbs to Sentry). Triggers: `ConsoleLog`, `FileLog`, `JSONFileLog`, `SentryLog`, `chunk`, `rotate`, `groupBy`, `maxFileSize`, `showContext`, `log.channel`; "log to a file", "rotate log files", "daily log chunks", "json logs for datadog / loki / elastic", "send logs to Sentry"; typical import `import { ConsoleLog, FileLog, JSONFileLog, SentryLog } from "@warlock.js/logger"`. Skip: Sentry-specific setup — `@warlock.js/logger/ship-logs-to-sentry/SKILL.md`; custom sinks — `@warlock.js/logger/write-custom-log-channel/SKILL.md`; registration — `@warlock.js/logger/configure-logger/SKILL.md`; competing libs `winston-daily-rotate-file`, `pino-pretty`.
16
+ - [redact-sensitive-log-fields](@warlock.js/logger/redact-sensitive-log-fields/SKILL.md): Strip secrets from log output — two-layer additive redaction via log.configure({redact: {paths}}) (logger floor) + per-channel redact (more paths on top). Dotted glob paths (*, **). Triggers: `redact`, `paths`, `censor`, `log.setRedact`, `applyRedact`; "redact passwords in logs", "strip tokens from log output", "hide authorization headers", "scrub PII before logging"; typical import `import { log } from "@warlock.js/logger"`. Skip: filtering — `@warlock.js/logger/filter-log-entries/SKILL.md`; custom sinks — `@warlock.js/logger/write-custom-log-channel/SKILL.md`; competing libs `pino.redact`, `fast-redact`.
17
+ - [ship-logs-to-sentry](@warlock.js/logger/ship-logs-to-sentry/SKILL.md): Forward log entries to Sentry with the SentryLog channel error/warn become events (captureException/captureMessage), every other level a breadcrumb (no quota). @sentry/node is an OPTIONAL peer, lazily imported. Triggers: `SentryLog`, `@sentry/node`, `eventLevels`, `flushTimeout`, `Sentry.flush`, `captureException`, `addBreadcrumb`, `withScope`; "send logs to Sentry", "report errors to Sentry", "Sentry log channel", "Sentry breadcrumbs from logs", "log channel for Sentry"; typical import `import { SentryLog } from "@warlock.js/logger"`. Skip: file/console channels — `@warlock.js/logger/pick-log-channel/SKILL.md`; custom sinks — `@warlock.js/logger/write-custom-log-channel/SKILL.md`; graceful-shutdown flushing — `@warlock.js/logger/flush-logs-on-shutdown/SKILL.md`; Slack alerting recipe.
18
+ - [test-logging-code](@warlock.js/logger/test-logging-code/SKILL.md): Test code that touches the loggersilence globally via log.setChannels([]) in setupFiles, assert specific log lines via a capturing LogChannel subclass (prefer it over vi.spyOn it asserts on delivered entries, not just method calls, and isolates the shared singleton cleanly). Triggers: `log.setChannels`, `LogChannel`, `LoggingData`, `Logger`, `log.channels`; "silence logger in vitest", "assert a log line was emitted", "capture log output in tests", "test code that logs"; typical import `import { log, Logger, LogChannel, type LoggingData } from "@warlock.js/logger"`. Skip: custom sinks — `@warlock.js/logger/write-custom-log-channel/SKILL.md`; filtering — `@warlock.js/logger/filter-log-entries/SKILL.md`; competing `vi.spyOn(console)`, `jest.spyOn`.
19
+ - [use-log-helpers](@warlock.js/logger/use-log-helpers/SKILL.md): Two DX shortcuts on every Logger log.assert(condition, module, action, message, context?) logs an error when condition is falsy (free on the happy path), log.timer(module, action) returns an end-function emitting an info entry with measured duration. Triggers: `log.assert`, `log.timer`, `durationMs`; "assert an invariant via logger", "measure how long an operation took", "time a request", "log operation duration"; typical import `import { log } from "@warlock.js/logger"`. Skip: basics — `@warlock.js/logger/logger-basics/SKILL.md`; filtering — `@warlock.js/logger/filter-log-entries/SKILL.md`; competing `console.assert`, `console.time`, `console.timeEnd`, `perf_hooks.performance.now`.
20
+ - [write-custom-log-channel](@warlock.js/logger/write-custom-log-channel/SKILL.md): Extend the abstract LogChannel class for custom sinks — Slack, database, HTTP endpoint, in-memory buffer. Triggers: `LogChannel`, `LogContract`, `LoggingData`, `shouldBeLogged`, `init`, `flush`, `flushSync`, `terminal`; "log to slack", "log to a database", "send logs to datadog / loki HTTP api", "in-memory test capture channel", "build a custom log sink"; typical import `import { LogChannel, type LoggingData, type LogContract } from "@warlock.js/logger"`. Skip: built-in channels — `@warlock.js/logger/pick-log-channel/SKILL.md`; filtering — `@warlock.js/logger/filter-log-entries/SKILL.md`; competing libs `winston-transport`, `pino-transport`.
package/package.json CHANGED
@@ -15,13 +15,16 @@
15
15
  "url": "https://github.com/warlockjs/logger"
16
16
  },
17
17
  "dependencies": {
18
- "@warlock.js/fs": "*",
19
18
  "@mongez/copper": "^2.1.2",
20
19
  "@mongez/reinforcements": "^3.2.0",
20
+ "@warlock.js/fs": "4.2.0",
21
21
  "dayjs": "^1.11.9",
22
22
  "safe-stable-stringify": "^2.5.0"
23
23
  },
24
- "version": "4.1.14",
24
+ "peerDependencies": {
25
+ "@sentry/node": "^8.0.0 || ^9.0.0 || ^10.0.0"
26
+ },
27
+ "version": "4.2.0",
25
28
  "main": "./cjs/index.cjs",
26
29
  "module": "./esm/index.mjs",
27
30
  "types": "./esm/index.d.mts",
@@ -1,103 +1,103 @@
1
- ---
2
- name: capture-unhandled-errors
3
- description: 'captureAnyUnhandledRejection() installs process.on(''unhandledRejection'') + (''uncaughtException'') listeners routing failures through log.error(''app'', ...). Triggers: `captureAnyUnhandledRejection`, `unhandledRejection`, `uncaughtException`, `log.error`; "log unhandled promise rejections", "catch uncaught exceptions to a file", "record crashes before exit", "global error handler with logger"; typical import `import { captureAnyUnhandledRejection, log } from "@warlock.js/logger"`. Skip: flushing — `@warlock.js/logger/flush-logs-on-shutdown/SKILL.md`; filtering — `@warlock.js/logger/filter-log-entries/SKILL.md`; competing `Sentry.init`, `@sentry/node`; native `process.on(''unhandledRejection'')`.'
4
- ---
5
-
6
- # Error capture — routing Node's unhandled errors through the logger
7
-
8
- `captureAnyUnhandledRejection()` installs two process-level listeners so crashes are logged (not silently swallowed) before Node exits.
9
-
10
- ## What it does
11
-
12
- ```ts
13
- import { captureAnyUnhandledRejection } from "@warlock.js/logger";
14
-
15
- captureAnyUnhandledRejection();
16
- ```
17
-
18
- Registers:
19
- - `process.on("unhandledRejection", reason => log.error("app", "unhandledRejection", reason))`
20
- - `process.on("uncaughtException", error => log.error("app", "uncaughtException", error))`
21
-
22
- Nothing else the failure goes through `log.error` only, so it lands in your configured channels rather than bypassing them with a raw `console.log`.
23
-
24
- ## When to call it
25
-
26
- **Once**, at startup, **after** channels are registered. Typical place: immediately after your `log.configure({...})` call.
27
-
28
- ```ts title="src/index.ts"
29
- import {
30
- log,
31
- ConsoleLog,
32
- FileLog,
33
- captureAnyUnhandledRejection,
34
- } from "@warlock.js/logger";
35
-
36
- log.configure({
37
- channels: [new ConsoleLog(), new FileLog({ levels: ["error"] })],
38
- autoFlushOn: ["SIGINT", "SIGTERM", "beforeExit"], // ← important; see below
39
- });
40
-
41
- captureAnyUnhandledRejection();
42
- ```
43
-
44
- ## Pair with `autoFlushOn: ["beforeExit"]`
45
-
46
- Without a flush on exit, here's what happens on a crash:
47
-
48
- 1. Promise rejection fires → `log.error(...)` queues the error into `FileLog`'s buffer.
49
- 2. Node exits.
50
- 3. Buffer is never flushed. **The error that killed your app is lost.**
51
-
52
- Including `"beforeExit"` in `autoFlushOn` closes the gap. Node fires `beforeExit` after the rejection handler resolves, the logger flushes, then Node exits. See [`@warlock.js/logger/flush-logs-on-shutdown/SKILL.md`](@warlock.js/logger/flush-logs-on-shutdown/SKILL.md).
53
-
54
- ## Idempotency — don't call it twice
55
-
56
- Calling `captureAnyUnhandledRejection()` a second time registers a second pair of listeners. Your next rejection gets logged twice. There's no dedup; just call it once.
57
-
58
- ## What it does **not** do
59
-
60
- - **Does not swallow errors.** Node still exits after `uncaughtException` (this is the safe behavior — state is undefined). The logger just ensures the error is recorded first.
61
- - **Does not install Node's `--unhandled-rejections` policy.** That's a Node flag; set it in your launch script if you want strict mode.
62
- - **Does not hook `SIGTERM` / `SIGINT`** — use `enableAutoFlush` for signal flushes.
63
- - **Does not filter.** Every rejection/exception is logged at `error` level with `module: "app"`. Filter per-channel if some noise slips in.
64
-
65
- ## Checking an error was captured in tests
66
-
67
- Don't mock `process.on` — use a capturing channel and emit the listener directly:
68
-
69
- ```ts
70
- import { log, captureAnyUnhandledRejection, LogChannel } from "@warlock.js/logger";
71
- import type { LoggingData } from "@warlock.js/logger";
72
-
73
- class Capture extends LogChannel {
74
- public name = "capture";
75
- public received: LoggingData[] = [];
76
- public log(data: LoggingData) { this.received.push({ ...data }); }
77
- }
78
-
79
- it("routes unhandled rejections to the logger", async () => {
80
- const capture = new Capture();
81
- const originalChannels = log.channels;
82
- log.channels = [capture];
83
-
84
- captureAnyUnhandledRejection();
85
- process.emit("unhandledRejection", new Error("boom"), Promise.resolve());
86
-
87
- await new Promise((r) => setTimeout(r, 0));
88
-
89
- expect(capture.received[0]!.module).toBe("app");
90
- expect(capture.received[0]!.action).toBe("unhandledRejection");
91
-
92
- log.channels = originalChannels;
93
- });
94
- ```
95
-
96
- ## Module + action the capture uses
97
-
98
- Both listeners log with:
99
- - `module: "app"`
100
- - `action: "unhandledRejection"` or `action: "uncaughtException"`
101
- - `message`: the rejection reason / exception (keep it as the raw `Error` object — file channels capture the stack).
102
-
103
- If you want these routed to a specific file, filter on `data.module === "app"`. See [`@warlock.js/logger/filter-log-entries/SKILL.md`](@warlock.js/logger/filter-log-entries/SKILL.md).
1
+ ---
2
+ name: capture-unhandled-errors
3
+ description: 'captureAnyUnhandledRejection() installs process.on(''unhandledRejection'') log.error and process.on(''uncaughtException'') log.fatal so process-level failures land in your configured channels. Triggers: `captureAnyUnhandledRejection`, `unhandledRejection`, `uncaughtException`, `log.error`, `log.fatal`; "log unhandled promise rejections", "catch uncaught exceptions to a file", "record crashes before exit", "global error handler with logger"; typical import `import { captureAnyUnhandledRejection, log } from "@warlock.js/logger"`. Skip: flushing — `@warlock.js/logger/flush-logs-on-shutdown/SKILL.md`; filtering — `@warlock.js/logger/filter-log-entries/SKILL.md`; competing `Sentry.init`, `@sentry/node`; native `process.on(''unhandledRejection'')`.'
4
+ ---
5
+
6
+ # Error capture — routing Node's unhandled errors through the logger
7
+
8
+ `captureAnyUnhandledRejection()` installs two process-level listeners so crashes are logged (not silently swallowed) before Node exits.
9
+
10
+ ## What it does
11
+
12
+ ```ts
13
+ import { captureAnyUnhandledRejection } from "@warlock.js/logger";
14
+
15
+ captureAnyUnhandledRejection();
16
+ ```
17
+
18
+ Registers:
19
+ - `process.on("unhandledRejection", reason => log.error("app", "unhandledRejection", reason))`
20
+ - `process.on("uncaughtException", error => log.fatal("app", "uncaughtException", error))`
21
+
22
+ The split is intentional: an `uncaughtException` terminates the Node process by default, so it's semantically `fatal`. An `unhandledRejection` is a failure but not always process-ending (depends on Node's `--unhandled-rejections` policy and your app's recovery), so it stays at `error`. This makes "page on fatal" alerting clean only true crashes ring the pager.
23
+
24
+ ## When to call it
25
+
26
+ **Once**, at startup, **after** channels are registered. Typical place: immediately after your `log.configure({...})` call.
27
+
28
+ ```ts title="src/index.ts"
29
+ import {
30
+ log,
31
+ ConsoleLog,
32
+ FileLog,
33
+ captureAnyUnhandledRejection,
34
+ } from "@warlock.js/logger";
35
+
36
+ log.configure({
37
+ channels: [new ConsoleLog(), new FileLog({ levels: ["error"] })],
38
+ autoFlushOn: ["SIGINT", "SIGTERM", "beforeExit"], // ← important; see below
39
+ });
40
+
41
+ captureAnyUnhandledRejection();
42
+ ```
43
+
44
+ ## Pair with `autoFlushOn: ["beforeExit"]`
45
+
46
+ Without a flush on exit, here's what happens on a crash:
47
+
48
+ 1. Promise rejection fires → `log.error(...)` queues the error into `FileLog`'s buffer.
49
+ 2. Node exits.
50
+ 3. Buffer is never flushed. **The error that killed your app is lost.**
51
+
52
+ Including `"beforeExit"` in `autoFlushOn` closes the gap. Node fires `beforeExit` after the rejection handler resolves, the logger flushes, then Node exits. See [`@warlock.js/logger/flush-logs-on-shutdown/SKILL.md`](@warlock.js/logger/flush-logs-on-shutdown/SKILL.md).
53
+
54
+ ## Idempotency — don't call it twice
55
+
56
+ Calling `captureAnyUnhandledRejection()` a second time registers a second pair of listeners. Your next rejection gets logged twice. There's no dedup; just call it once.
57
+
58
+ ## What it does **not** do
59
+
60
+ - **Does not swallow errors.** Node still exits after `uncaughtException` (this is the safe behavior — state is undefined). The logger just ensures the error is recorded first.
61
+ - **Does not install Node's `--unhandled-rejections` policy.** That's a Node flag; set it in your launch script if you want strict mode.
62
+ - **Does not hook `SIGTERM` / `SIGINT`** — use `enableAutoFlush` for signal flushes.
63
+ - **Does not filter.** Every rejection is logged at `error` and every uncaught exception at `fatal`, both with `module: "app"`. Filter per-channel if some noise slips in.
64
+
65
+ ## Checking an error was captured in tests
66
+
67
+ Don't mock `process.on` — use a capturing channel and emit the listener directly:
68
+
69
+ ```ts
70
+ import { log, captureAnyUnhandledRejection, LogChannel } from "@warlock.js/logger";
71
+ import type { LoggingData } from "@warlock.js/logger";
72
+
73
+ class Capture extends LogChannel {
74
+ public name = "capture";
75
+ public received: LoggingData[] = [];
76
+ public log(data: LoggingData) { this.received.push({ ...data }); }
77
+ }
78
+
79
+ it("routes unhandled rejections to the logger", async () => {
80
+ const capture = new Capture();
81
+ const originalChannels = log.channels;
82
+ log.channels = [capture];
83
+
84
+ captureAnyUnhandledRejection();
85
+ process.emit("unhandledRejection", new Error("boom"), Promise.resolve());
86
+
87
+ await new Promise((r) => setTimeout(r, 0));
88
+
89
+ expect(capture.received[0]!.module).toBe("app");
90
+ expect(capture.received[0]!.action).toBe("unhandledRejection");
91
+
92
+ log.channels = originalChannels;
93
+ });
94
+ ```
95
+
96
+ ## Module + action the capture uses
97
+
98
+ Both listeners log with:
99
+ - `module: "app"`
100
+ - `action: "unhandledRejection"` (at `error`) or `action: "uncaughtException"` (at `fatal`)
101
+ - `message`: the rejection reason / exception (keep it as the raw `Error` object — file channels capture the stack).
102
+
103
+ If you want these routed to a specific file, filter on `data.module === "app"`. See [`@warlock.js/logger/filter-log-entries/SKILL.md`](@warlock.js/logger/filter-log-entries/SKILL.md).
@@ -1,120 +1,120 @@
1
- ---
2
- name: filter-log-entries
3
- description: 'Drop log entries — per-channel levels whitelist, per-channel filter predicate, logger-wide setMinLevel(level) fast path. Triggers: `levels`, `filter`, `minLevel`, `log.setMinLevel`, `shouldBeLogged`, `LoggingData`, `LogLevel`; "silence a noisy module", "route errors to a dedicated file", "raise global severity floor", "drop debug logs in prod"; typical import `import { log } from "@warlock.js/logger"`. Skip: custom sinks — `@warlock.js/logger/write-custom-log-channel/SKILL.md`; channel picks — `@warlock.js/logger/pick-log-channel/SKILL.md`; competing libs `pino.levels`, `winston.format.filter`, `debug` env var.'
4
- ---
5
-
6
- # Filtering — `levels` + `filter` predicate + `minLevel`
7
-
8
- Every channel can silently drop entries it doesn't care about. Three mechanisms stack: a logger-wide `minLevel` floor (cheapest), then per-channel `levels` whitelist, then per-channel `filter` predicate.
9
-
10
- ## 1. `levels` — the per-channel whitelist
11
-
12
- ```ts
13
- new FileLog({ levels: ["error", "warn"] });
14
- // debug/info/success entries → skipped
15
- // error/warn entries → written
16
- ```
17
-
18
- - Omitting `levels` (or passing `[]`) means **allow all five**.
19
- - No regex / no range — it's a literal whitelist of `LogLevel` strings.
20
-
21
- ## 2. `filter` — the per-channel custom predicate
22
-
23
- ```ts
24
- new ConsoleLog({
25
- filter: (data) => data.module !== "healthcheck",
26
- });
27
- // Every entry is passed to the predicate; return false → skip.
28
- ```
29
-
30
- - `data` is the full `LoggingData`: `{ type, module, action, message, context? }`.
31
- - Predicate runs **after** `levels` — an entry blocked by `levels` never reaches `filter`.
32
-
33
- ## 3. `minLevel` — the logger-wide severity floor
34
-
35
- For the common "drop everything below X" case, skip the per-channel `levels` array and use the logger-wide fast path:
36
-
37
- ```ts
38
- log.setMinLevel("info");
39
- // debug entries are dropped before fan-out — no channel ever sees them.
40
-
41
- log.configure({ minLevel: "warn" }); // shorthand inside configure()
42
- ```
43
-
44
- Severity ordering: `debug < info ≈ success < warn < error`. `success` is treated as informational severity — `setMinLevel("warn")` drops it.
45
-
46
- Pass `undefined` to clear:
47
-
48
- ```ts
49
- log.setMinLevel(undefined); // accept everything again
50
- ```
51
-
52
- This runs **before** the channel loop — cheaper than per-channel `levels` filters when you want a uniform floor. Per-channel `levels` and `filter` still run on top for channels that need a tighter or differently-shaped rule.
53
-
54
- ## Combining — real patterns
55
-
56
- ### Route errors to a dedicated file
57
-
58
- ```ts
59
- log.setChannels([
60
- new ConsoleLog(),
61
- new FileLog({
62
- name: "errors",
63
- levels: ["error", "warn"],
64
- chunk: "daily",
65
- }),
66
- ]);
67
- // ConsoleLog sees everything; errors.log only grows with warnings and errors.
68
- ```
69
-
70
- ### Silence a noisy module
71
-
72
- ```ts
73
- new ConsoleLog({
74
- filter: (data) => data.module !== "socket.io",
75
- });
76
- ```
77
-
78
- ### Keep the dev terminal focused
79
-
80
- ```ts
81
- // Only surface the subsystem you're actively working on
82
- new ConsoleLog({
83
- filter: (data) => data.module === "auth",
84
- });
85
- ```
86
-
87
- ### Errors always pass, info only for one module
88
-
89
- ```ts
90
- new ConsoleLog({
91
- filter: (data) => data.type === "error" || data.module === "payments",
92
- });
93
- ```
94
-
95
- ## Where filtering happens
96
-
97
- `LogChannel.shouldBeLogged(data)` runs both checks in order:
98
-
99
- ```ts
100
- // levels check — fast path
101
- if (this.config("levels")?.length && !this.config("levels").includes(data.type)) return false;
102
-
103
- // filter predicate — only runs if levels allowed it
104
- const filter = this.config("filter");
105
- if (filter) return filter(data);
106
-
107
- return true;
108
- ```
109
-
110
- If you extend `LogChannel` to write a custom channel, call `this.shouldBeLogged(data)` first thing inside your `log(data)` method — you inherit both mechanisms for free. See [`@warlock.js/logger/write-custom-log-channel/SKILL.md`](@warlock.js/logger/write-custom-log-channel/SKILL.md).
111
-
112
- ## Logger-wide custom filtering — not a thing
113
-
114
- There is no `logger.setGlobalFilter()`. Each channel filters itself. If you want the same predicate everywhere, pass it to every channel constructor (or wrap your channels in a helper).
115
-
116
- ## Performance note
117
-
118
- Filters run on **every** entry per channel. A synchronous, cheap predicate is fine. Avoid `await` inside — the channel receives a fully-formed `LoggingData` and the filter is sync-only (type: `(data: LoggingData) => boolean`).
119
-
120
- The `minLevel` check is the fastest of the three (single comparison before fan-out), so prefer it when "drop everything below X uniformly" matches your need.
1
+ ---
2
+ name: filter-log-entries
3
+ description: 'Drop log entries — per-channel levels whitelist, per-channel filter predicate, logger-wide setMinLevel(level) fast path. Triggers: `levels`, `filter`, `minLevel`, `log.setMinLevel`, `shouldBeLogged`, `LoggingData`, `LogLevel`; "silence a noisy module", "route errors to a dedicated file", "raise global severity floor", "drop debug logs in prod"; typical import `import { log } from "@warlock.js/logger"`. Skip: custom sinks — `@warlock.js/logger/write-custom-log-channel/SKILL.md`; channel picks — `@warlock.js/logger/pick-log-channel/SKILL.md`; competing libs `pino.levels`, `winston.format.filter`, `debug` env var.'
4
+ ---
5
+
6
+ # Filtering — `levels` + `filter` predicate + `minLevel`
7
+
8
+ Every channel can silently drop entries it doesn't care about. Three mechanisms stack: a logger-wide `minLevel` floor (cheapest), then per-channel `levels` whitelist, then per-channel `filter` predicate.
9
+
10
+ ## 1. `levels` — the per-channel whitelist
11
+
12
+ ```ts
13
+ new FileLog({ levels: ["error", "warn"] });
14
+ // debug/info/success entries → skipped
15
+ // error/warn entries → written
16
+ ```
17
+
18
+ - Omitting `levels` (or passing `[]`) means **allow all six**.
19
+ - No regex / no range — it's a literal whitelist of `LogLevel` strings.
20
+
21
+ ## 2. `filter` — the per-channel custom predicate
22
+
23
+ ```ts
24
+ new ConsoleLog({
25
+ filter: (data) => data.module !== "healthcheck",
26
+ });
27
+ // Every entry is passed to the predicate; return false → skip.
28
+ ```
29
+
30
+ - `data` is the full `LoggingData`: `{ type, module, action, message, context? }`.
31
+ - Predicate runs **after** `levels` — an entry blocked by `levels` never reaches `filter`.
32
+
33
+ ## 3. `minLevel` — the logger-wide severity floor
34
+
35
+ For the common "drop everything below X" case, skip the per-channel `levels` array and use the logger-wide fast path:
36
+
37
+ ```ts
38
+ log.setMinLevel("info");
39
+ // debug entries are dropped before fan-out — no channel ever sees them.
40
+
41
+ log.configure({ minLevel: "warn" }); // shorthand inside configure()
42
+ ```
43
+
44
+ Severity ordering: `debug < info ≈ success < warn < error < fatal`. `success` is treated as informational severity — `setMinLevel("warn")` drops it. `fatal` is strictly above `error`, so `setMinLevel("fatal")` admits only fatal entries (handy for "page me only on fatal" routing).
45
+
46
+ Pass `undefined` to clear:
47
+
48
+ ```ts
49
+ log.setMinLevel(undefined); // accept everything again
50
+ ```
51
+
52
+ This runs **before** the channel loop — cheaper than per-channel `levels` filters when you want a uniform floor. Per-channel `levels` and `filter` still run on top for channels that need a tighter or differently-shaped rule.
53
+
54
+ ## Combining — real patterns
55
+
56
+ ### Route errors to a dedicated file
57
+
58
+ ```ts
59
+ log.setChannels([
60
+ new ConsoleLog(),
61
+ new FileLog({
62
+ name: "errors",
63
+ levels: ["error", "warn"],
64
+ chunk: "daily",
65
+ }),
66
+ ]);
67
+ // ConsoleLog sees everything; errors.log only grows with warnings and errors.
68
+ ```
69
+
70
+ ### Silence a noisy module
71
+
72
+ ```ts
73
+ new ConsoleLog({
74
+ filter: (data) => data.module !== "socket.io",
75
+ });
76
+ ```
77
+
78
+ ### Keep the dev terminal focused
79
+
80
+ ```ts
81
+ // Only surface the subsystem you're actively working on
82
+ new ConsoleLog({
83
+ filter: (data) => data.module === "auth",
84
+ });
85
+ ```
86
+
87
+ ### Errors always pass, info only for one module
88
+
89
+ ```ts
90
+ new ConsoleLog({
91
+ filter: (data) => data.type === "error" || data.module === "payments",
92
+ });
93
+ ```
94
+
95
+ ## Where filtering happens
96
+
97
+ `LogChannel.shouldBeLogged(data)` runs both checks in order:
98
+
99
+ ```ts
100
+ // levels check — fast path
101
+ if (this.config("levels")?.length && !this.config("levels").includes(data.type)) return false;
102
+
103
+ // filter predicate — only runs if levels allowed it
104
+ const filter = this.config("filter");
105
+ if (filter) return filter(data);
106
+
107
+ return true;
108
+ ```
109
+
110
+ If you extend `LogChannel` to write a custom channel, call `this.shouldBeLogged(data)` first thing inside your `log(data)` method — you inherit both mechanisms for free. See [`@warlock.js/logger/write-custom-log-channel/SKILL.md`](@warlock.js/logger/write-custom-log-channel/SKILL.md).
111
+
112
+ ## Logger-wide custom filtering — not a thing
113
+
114
+ There is no `logger.setGlobalFilter()`. Each channel filters itself. If you want the same predicate everywhere, pass it to every channel constructor (or wrap your channels in a helper).
115
+
116
+ ## Performance note
117
+
118
+ Filters run on **every** entry per channel. A synchronous, cheap predicate is fine. Avoid `await` inside — the channel receives a fully-formed `LoggingData` and the filter is sync-only (type: `(data: LoggingData) => boolean`).
119
+
120
+ The `minLevel` check is the fastest of the three (single comparison before fan-out), so prefer it when "drop everything below X uniformly" matches your need.