@warlock.js/logger 5.23.2 → 5.25.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.
- package/CHANGELOG.md +12 -0
- package/README.md +153 -153
- package/cjs/index.cjs.map +1 -1
- package/esm/channels/console-log.mjs.map +1 -1
- package/esm/channels/file-log.mjs.map +1 -1
- package/esm/channels/json-file-log.mjs.map +1 -1
- package/esm/channels/sentry-log.mjs.map +1 -1
- package/esm/log-channel.mjs.map +1 -1
- package/esm/logger.mjs.map +1 -1
- package/esm/utils/capture-unhandled-errors.mjs.map +1 -1
- package/esm/utils/clear-message.mjs.map +1 -1
- package/llms-full.txt +1413 -1413
- package/package.json +2 -2
- package/skills/capture-unhandled-errors/SKILL.md +111 -111
- package/skills/configure-logger/SKILL.md +105 -105
- package/skills/filter-log-entries/SKILL.md +120 -120
- package/skills/flush-logs-on-shutdown/SKILL.md +117 -117
- package/skills/overview/SKILL.md +93 -93
- package/skills/pick-log-channel/SKILL.md +155 -155
- package/skills/redact-sensitive-log-fields/SKILL.md +156 -156
- package/skills/ship-logs-to-sentry/SKILL.md +118 -118
- package/skills/test-logging-code/SKILL.md +177 -177
- package/skills/use-log-helpers/SKILL.md +66 -66
- package/skills/write-custom-log-channel/SKILL.md +190 -190
package/skills/overview/SKILL.md
CHANGED
|
@@ -1,94 +1,94 @@
|
|
|
1
|
-
---
|
|
2
|
-
name: overview
|
|
3
|
-
description: "@warlock.js/logger overview for orientation and choosing the focused skill for the task."
|
|
4
|
-
---
|
|
5
|
-
|
|
6
|
-
# `@warlock.js/logger` — overview
|
|
7
|
-
|
|
8
|
-
Structured logging for Node. Six severity levels (with `fatal` strictly above `error`), a singleton plus a `Logger` class, channel-based fan-out (one entry → many sinks), PII redaction as a floor that channels can extend, buffered file writes with signal-triggered flush on shutdown, an awaitable async `log.flush()` for network/async channels, an optional Sentry channel, and a couple of ergonomic helpers (`timer`, `assert`) that turn boilerplate into one-liners.
|
|
9
|
-
|
|
10
|
-
Ships standalone — `@warlock.js/core` is not required. Drop it into any Node project.
|
|
11
|
-
|
|
12
|
-
## Server-only package
|
|
13
|
-
|
|
1
|
+
---
|
|
2
|
+
name: overview
|
|
3
|
+
description: "@warlock.js/logger overview for orientation and choosing the focused skill for the task."
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# `@warlock.js/logger` — overview
|
|
7
|
+
|
|
8
|
+
Structured logging for Node. Six severity levels (with `fatal` strictly above `error`), a singleton plus a `Logger` class, channel-based fan-out (one entry → many sinks), PII redaction as a floor that channels can extend, buffered file writes with signal-triggered flush on shutdown, an awaitable async `log.flush()` for network/async channels, an optional Sentry channel, and a couple of ergonomic helpers (`timer`, `assert`) that turn boilerplate into one-liners.
|
|
9
|
+
|
|
10
|
+
Ships standalone — `@warlock.js/core` is not required. Drop it into any Node project.
|
|
11
|
+
|
|
12
|
+
## Server-only package
|
|
13
|
+
|
|
14
14
|
`@warlock.js/logger`'s entire runtime surface is server-only — its `package.json` declares `"warlock": { "environment": "server" }`. This is build-boundary metadata read by `@warlock.js/web`'s Gate A (import resolution) and Gate C (emitted-bundle verification), not application behavior. App code that reaches the client bundle (including page and layout modules) must not value-import `@warlock.js/logger` — Gate A refuses the build. A type-only import (`import type { ... }`) is allowed. Server loaders, controllers, and modules may import it freely.
|
|
15
|
-
|
|
16
|
-
## When to reach for it
|
|
17
|
-
|
|
18
|
-
- Building a Node service that needs **structured** logs (key-value pairs, not bare strings) and you want them to land in multiple destinations (console for dev, JSON file for prod, third-party sink for audits) without rewriting the call sites.
|
|
19
|
-
- You'd reach for **pino** or **winston** but want a smaller surface that's already wired into Warlock conventions (`module / action / message` shape, redaction floor, signal flush built-in).
|
|
20
|
-
- Your team agrees that **`console.log` doesn't survive contact with production** — you need filtering, level routing, channel-specific sinks, and a redaction story before secrets leak into Slack/Datadog.
|
|
21
|
-
|
|
22
|
-
Skip if your code is a throwaway script where `console.log` is genuinely fine — there's no value in adding a dependency for one-off logs.
|
|
23
|
-
|
|
24
|
-
## The mental model in one paragraph
|
|
25
|
-
|
|
26
|
-
You write `log.info("auth", "login", "user signed in", { userId })`. The logger fans that single entry out to every registered channel (`ConsoleLog`, `FileLog`, `JSONFileLog`, `SentryLog`, or your custom subclass). Each channel decides whether to emit it (per-level whitelist, per-channel filter predicate, logger-wide minimum severity). Redaction runs once at the logger level and can be extended per channel — never relaxed. Buffered channels (file + JSON file) drain on flush — synchronously via `log.flushSync()` / `enableAutoFlush(['SIGINT', 'SIGTERM', 'beforeExit'])`, or asynchronously via `await log.flush()` (the only path that works for network channels like `SentryLog`). That's the whole package.
|
|
27
|
-
|
|
28
|
-
## Skills index
|
|
29
|
-
|
|
30
|
-
Eleven task skills cover everything. Load the one that matches your job — most callers only ever need `logger-basics` + `configure-logger` + `pick-log-channel`.
|
|
31
|
-
|
|
32
|
-
### Foundations
|
|
33
|
-
|
|
34
|
-
#### [`logger-basics`](@warlock.js/logger/logger-basics/SKILL.md)
|
|
35
|
-
Start here. The `log` singleton, the six levels (`debug` / `info` / `warn` / `error` / `success` / `fatal`), how fan-out works, the `module / action / message / context` shape every entry carries.
|
|
36
|
-
|
|
37
|
-
#### [`configure-logger`](@warlock.js/logger/configure-logger/SKILL.md)
|
|
38
|
-
Wire channels at boot — `log.addChannel`, `log.setChannels`, `log.configure({ channels, autoFlushOn, redact, minLevel })`. Branch on `NODE_ENV`, replace the channel list, isolate a library's logger from the host singleton.
|
|
39
|
-
|
|
40
|
-
### Channels
|
|
41
|
-
|
|
42
|
-
#### [`pick-log-channel`](@warlock.js/logger/pick-log-channel/SKILL.md)
|
|
43
|
-
Pick one of the four built-ins: `ConsoleLog` (terminal, colored), `FileLog` (plain `.log` on disk with rotation), `JSONFileLog` (structured JSON for aggregators — Datadog, Loki, ELK), `SentryLog` (errors + breadcrumbs to Sentry; `@sentry/node` is an optional peer).
|
|
44
|
-
|
|
45
|
-
#### [`ship-logs-to-sentry`](@warlock.js/logger/ship-logs-to-sentry/SKILL.md)
|
|
46
|
-
The `SentryLog` channel in depth — event-vs-breadcrumb level mapping, dual init modes (reuse an existing client or pass `options`), draining via `Sentry.flush(timeout)`, behavior when the optional peer isn't installed.
|
|
47
|
-
|
|
48
|
-
#### [`write-custom-log-channel`](@warlock.js/logger/write-custom-log-channel/SKILL.md)
|
|
49
|
-
Extend `LogChannel<Options>` for sinks the built-ins don't cover — Slack, HTTP endpoint, in-memory buffer, database. The lazy `init()` lifecycle (`setTimeout(0)`), the `terminal: true/false` ANSI-stripping behavior, and the difference between `flush()` (async, network) and `flushSync()` (sync, files) are subtle — read this skill before subclassing.
|
|
50
|
-
|
|
51
|
-
### Production concerns
|
|
52
|
-
|
|
53
|
-
#### [`redact-sensitive-log-fields`](@warlock.js/logger/redact-sensitive-log-fields/SKILL.md)
|
|
54
|
-
Strip secrets before they reach a sink. Logger-wide `setRedact({ paths, censor })` is the security floor; per-channel `redact` configs add paths (never remove). Dotted-glob paths (`*`, `**`); censor as string or function `(value, path) => any`.
|
|
55
|
-
|
|
56
|
-
#### [`filter-log-entries`](@warlock.js/logger/filter-log-entries/SKILL.md)
|
|
57
|
-
Drop entries before they cost anything. Logger-wide `setMinLevel("info")` is the fast path; per-channel `levels` array + `filter` predicate for fine control.
|
|
58
|
-
|
|
59
|
-
#### [`flush-logs-on-shutdown`](@warlock.js/logger/flush-logs-on-shutdown/SKILL.md)
|
|
60
|
-
Buffered channels need explicit drain. `log.flushSync()` (sync) for file channels — also wired by `enableAutoFlush(['SIGINT', 'SIGTERM', 'SIGHUP', 'SIGBREAK', 'SIGUSR2', 'beforeExit'])`. `await log.flush()` (async) for network/async channels like `SentryLog` — the only path that can await an HTTPS round-trip on a graceful shutdown.
|
|
61
|
-
|
|
62
|
-
#### [`capture-unhandled-errors`](@warlock.js/logger/capture-unhandled-errors/SKILL.md)
|
|
63
|
-
`captureAnyUnhandledRejection()` hooks `unhandledRejection` (→ `log.error("app", ...)`, process kept alive) and `uncaughtException` (→ `log.fatal("app", ...)` then `process.exit(1)` — restoring the non-zero exit the listener would otherwise suppress, so a fatal crash never becomes a silent `exit 0`; opt out with `{ exitOnUncaughtException: false }`). It flushes before exiting and falls back to `console.error` when no terminal channel is set. One call at startup.
|
|
64
|
-
|
|
65
|
-
### Ergonomics + testing
|
|
66
|
-
|
|
67
|
-
#### [`use-log-helpers`](@warlock.js/logger/use-log-helpers/SKILL.md)
|
|
68
|
-
Two shortcuts every `Logger` exposes: `log.assert(condition, module, action, message, context?)` logs an error only when the condition is falsy (free on the happy path); `log.timer(module, action)` returns an end-function that emits `info` with a measured `durationMs`.
|
|
69
|
-
|
|
70
|
-
#### [`test-logging-code`](@warlock.js/logger/test-logging-code/SKILL.md)
|
|
71
|
-
Silence the logger globally in tests via `log.setChannels([])` in `setupFiles`. Assert specific entries with a capturing `LogChannel` subclass — it proves an entry was actually delivered through the pipeline (filters, redaction), not merely that a method was called, and it isolates cleanly by swapping `log.channels`.
|
|
72
|
-
|
|
73
|
-
## Built-in channels at a glance
|
|
74
|
-
|
|
75
|
-
| Channel | Sink | `terminal` | Buffered |
|
|
76
|
-
| --- | --- | --- | --- |
|
|
77
|
-
| `ConsoleLog` | `process.stdout` | `true` (colors kept) | no |
|
|
78
|
-
| `FileLog` | `.log` files | `false` (ANSI stripped) | yes (5s timer or 100-entry buffer) |
|
|
79
|
-
| `JSONFileLog` | `.json` files | `false` (ANSI stripped) | yes (same buffering) |
|
|
80
|
-
| `SentryLog` | Sentry events + breadcrumbs | `false` | via the Sentry SDK's own transport (`@sentry/node` is an optional peer) |
|
|
81
|
-
|
|
82
|
-
`terminal: true` is the flag that decides whether the channel sees raw colored messages or stripped plain text. Custom channels: pick `true` if you write to a terminal, `false` for anything else.
|
|
83
|
-
|
|
84
|
-
## What this package deliberately doesn't do
|
|
85
|
-
|
|
86
|
-
- **Distributed tracing.** Use OpenTelemetry. Logger gets you structured local logs with `module / action`; trace correlation is a different problem.
|
|
87
|
-
- **Log aggregator integrations beyond Sentry.** `SentryLog` is bundled (with `@sentry/node` as an optional peer). For Datadog, Loki, ELK, etc., either write a custom channel that POSTs to the aggregator or use `JSONFileLog` + a sidecar (fluentbit, vector, promtail).
|
|
88
|
-
- **Pretty-printing of arbitrary objects.** `ConsoleLog` has a `showContext` flag that runs `util.inspect` on the context object; for richer formatting, use `JSONFileLog` and view the file through your favorite viewer.
|
|
89
|
-
- **Log analysis.** Querying / aggregating / alerting is on the sink side (Loki, ELK, Datadog).
|
|
90
|
-
|
|
91
|
-
## See also
|
|
92
|
-
|
|
93
|
-
- [`@warlock.js/core/warlock-conventions`](@warlock.js/core/warlock-conventions/SKILL.md) — the parent framework's conventions; logger is one of its foundation packages and ships transitively when you install core.
|
|
94
|
-
- When synced via agent-kit, this `overview/SKILL.md` is flattened to the front-door skill `.claude/skills/warlock-js-logger-overview/` — every cross-link above uses the `@warlock.js/logger/<skill>/SKILL.md` name form so it survives that flattening.
|
|
15
|
+
|
|
16
|
+
## When to reach for it
|
|
17
|
+
|
|
18
|
+
- Building a Node service that needs **structured** logs (key-value pairs, not bare strings) and you want them to land in multiple destinations (console for dev, JSON file for prod, third-party sink for audits) without rewriting the call sites.
|
|
19
|
+
- You'd reach for **pino** or **winston** but want a smaller surface that's already wired into Warlock conventions (`module / action / message` shape, redaction floor, signal flush built-in).
|
|
20
|
+
- Your team agrees that **`console.log` doesn't survive contact with production** — you need filtering, level routing, channel-specific sinks, and a redaction story before secrets leak into Slack/Datadog.
|
|
21
|
+
|
|
22
|
+
Skip if your code is a throwaway script where `console.log` is genuinely fine — there's no value in adding a dependency for one-off logs.
|
|
23
|
+
|
|
24
|
+
## The mental model in one paragraph
|
|
25
|
+
|
|
26
|
+
You write `log.info("auth", "login", "user signed in", { userId })`. The logger fans that single entry out to every registered channel (`ConsoleLog`, `FileLog`, `JSONFileLog`, `SentryLog`, or your custom subclass). Each channel decides whether to emit it (per-level whitelist, per-channel filter predicate, logger-wide minimum severity). Redaction runs once at the logger level and can be extended per channel — never relaxed. Buffered channels (file + JSON file) drain on flush — synchronously via `log.flushSync()` / `enableAutoFlush(['SIGINT', 'SIGTERM', 'beforeExit'])`, or asynchronously via `await log.flush()` (the only path that works for network channels like `SentryLog`). That's the whole package.
|
|
27
|
+
|
|
28
|
+
## Skills index
|
|
29
|
+
|
|
30
|
+
Eleven task skills cover everything. Load the one that matches your job — most callers only ever need `logger-basics` + `configure-logger` + `pick-log-channel`.
|
|
31
|
+
|
|
32
|
+
### Foundations
|
|
33
|
+
|
|
34
|
+
#### [`logger-basics`](@warlock.js/logger/logger-basics/SKILL.md)
|
|
35
|
+
Start here. The `log` singleton, the six levels (`debug` / `info` / `warn` / `error` / `success` / `fatal`), how fan-out works, the `module / action / message / context` shape every entry carries.
|
|
36
|
+
|
|
37
|
+
#### [`configure-logger`](@warlock.js/logger/configure-logger/SKILL.md)
|
|
38
|
+
Wire channels at boot — `log.addChannel`, `log.setChannels`, `log.configure({ channels, autoFlushOn, redact, minLevel })`. Branch on `NODE_ENV`, replace the channel list, isolate a library's logger from the host singleton.
|
|
39
|
+
|
|
40
|
+
### Channels
|
|
41
|
+
|
|
42
|
+
#### [`pick-log-channel`](@warlock.js/logger/pick-log-channel/SKILL.md)
|
|
43
|
+
Pick one of the four built-ins: `ConsoleLog` (terminal, colored), `FileLog` (plain `.log` on disk with rotation), `JSONFileLog` (structured JSON for aggregators — Datadog, Loki, ELK), `SentryLog` (errors + breadcrumbs to Sentry; `@sentry/node` is an optional peer).
|
|
44
|
+
|
|
45
|
+
#### [`ship-logs-to-sentry`](@warlock.js/logger/ship-logs-to-sentry/SKILL.md)
|
|
46
|
+
The `SentryLog` channel in depth — event-vs-breadcrumb level mapping, dual init modes (reuse an existing client or pass `options`), draining via `Sentry.flush(timeout)`, behavior when the optional peer isn't installed.
|
|
47
|
+
|
|
48
|
+
#### [`write-custom-log-channel`](@warlock.js/logger/write-custom-log-channel/SKILL.md)
|
|
49
|
+
Extend `LogChannel<Options>` for sinks the built-ins don't cover — Slack, HTTP endpoint, in-memory buffer, database. The lazy `init()` lifecycle (`setTimeout(0)`), the `terminal: true/false` ANSI-stripping behavior, and the difference between `flush()` (async, network) and `flushSync()` (sync, files) are subtle — read this skill before subclassing.
|
|
50
|
+
|
|
51
|
+
### Production concerns
|
|
52
|
+
|
|
53
|
+
#### [`redact-sensitive-log-fields`](@warlock.js/logger/redact-sensitive-log-fields/SKILL.md)
|
|
54
|
+
Strip secrets before they reach a sink. Logger-wide `setRedact({ paths, censor })` is the security floor; per-channel `redact` configs add paths (never remove). Dotted-glob paths (`*`, `**`); censor as string or function `(value, path) => any`.
|
|
55
|
+
|
|
56
|
+
#### [`filter-log-entries`](@warlock.js/logger/filter-log-entries/SKILL.md)
|
|
57
|
+
Drop entries before they cost anything. Logger-wide `setMinLevel("info")` is the fast path; per-channel `levels` array + `filter` predicate for fine control.
|
|
58
|
+
|
|
59
|
+
#### [`flush-logs-on-shutdown`](@warlock.js/logger/flush-logs-on-shutdown/SKILL.md)
|
|
60
|
+
Buffered channels need explicit drain. `log.flushSync()` (sync) for file channels — also wired by `enableAutoFlush(['SIGINT', 'SIGTERM', 'SIGHUP', 'SIGBREAK', 'SIGUSR2', 'beforeExit'])`. `await log.flush()` (async) for network/async channels like `SentryLog` — the only path that can await an HTTPS round-trip on a graceful shutdown.
|
|
61
|
+
|
|
62
|
+
#### [`capture-unhandled-errors`](@warlock.js/logger/capture-unhandled-errors/SKILL.md)
|
|
63
|
+
`captureAnyUnhandledRejection()` hooks `unhandledRejection` (→ `log.error("app", ...)`, process kept alive) and `uncaughtException` (→ `log.fatal("app", ...)` then `process.exit(1)` — restoring the non-zero exit the listener would otherwise suppress, so a fatal crash never becomes a silent `exit 0`; opt out with `{ exitOnUncaughtException: false }`). It flushes before exiting and falls back to `console.error` when no terminal channel is set. One call at startup.
|
|
64
|
+
|
|
65
|
+
### Ergonomics + testing
|
|
66
|
+
|
|
67
|
+
#### [`use-log-helpers`](@warlock.js/logger/use-log-helpers/SKILL.md)
|
|
68
|
+
Two shortcuts every `Logger` exposes: `log.assert(condition, module, action, message, context?)` logs an error only when the condition is falsy (free on the happy path); `log.timer(module, action)` returns an end-function that emits `info` with a measured `durationMs`.
|
|
69
|
+
|
|
70
|
+
#### [`test-logging-code`](@warlock.js/logger/test-logging-code/SKILL.md)
|
|
71
|
+
Silence the logger globally in tests via `log.setChannels([])` in `setupFiles`. Assert specific entries with a capturing `LogChannel` subclass — it proves an entry was actually delivered through the pipeline (filters, redaction), not merely that a method was called, and it isolates cleanly by swapping `log.channels`.
|
|
72
|
+
|
|
73
|
+
## Built-in channels at a glance
|
|
74
|
+
|
|
75
|
+
| Channel | Sink | `terminal` | Buffered |
|
|
76
|
+
| --- | --- | --- | --- |
|
|
77
|
+
| `ConsoleLog` | `process.stdout` | `true` (colors kept) | no |
|
|
78
|
+
| `FileLog` | `.log` files | `false` (ANSI stripped) | yes (5s timer or 100-entry buffer) |
|
|
79
|
+
| `JSONFileLog` | `.json` files | `false` (ANSI stripped) | yes (same buffering) |
|
|
80
|
+
| `SentryLog` | Sentry events + breadcrumbs | `false` | via the Sentry SDK's own transport (`@sentry/node` is an optional peer) |
|
|
81
|
+
|
|
82
|
+
`terminal: true` is the flag that decides whether the channel sees raw colored messages or stripped plain text. Custom channels: pick `true` if you write to a terminal, `false` for anything else.
|
|
83
|
+
|
|
84
|
+
## What this package deliberately doesn't do
|
|
85
|
+
|
|
86
|
+
- **Distributed tracing.** Use OpenTelemetry. Logger gets you structured local logs with `module / action`; trace correlation is a different problem.
|
|
87
|
+
- **Log aggregator integrations beyond Sentry.** `SentryLog` is bundled (with `@sentry/node` as an optional peer). For Datadog, Loki, ELK, etc., either write a custom channel that POSTs to the aggregator or use `JSONFileLog` + a sidecar (fluentbit, vector, promtail).
|
|
88
|
+
- **Pretty-printing of arbitrary objects.** `ConsoleLog` has a `showContext` flag that runs `util.inspect` on the context object; for richer formatting, use `JSONFileLog` and view the file through your favorite viewer.
|
|
89
|
+
- **Log analysis.** Querying / aggregating / alerting is on the sink side (Loki, ELK, Datadog).
|
|
90
|
+
|
|
91
|
+
## See also
|
|
92
|
+
|
|
93
|
+
- [`@warlock.js/core/warlock-conventions`](@warlock.js/core/warlock-conventions/SKILL.md) — the parent framework's conventions; logger is one of its foundation packages and ships transitively when you install core.
|
|
94
|
+
- When synced via agent-kit, this `overview/SKILL.md` is flattened to the front-door skill `.claude/skills/warlock-js-logger-overview/` — every cross-link above uses the `@warlock.js/logger/<skill>/SKILL.md` name form so it survives that flattening.
|
|
@@ -1,155 +1,155 @@
|
|
|
1
|
-
---
|
|
2
|
-
name: pick-log-channel
|
|
3
|
-
description: '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`.'
|
|
4
|
-
---
|
|
5
|
-
|
|
6
|
-
# Channels — which one to pick and how to configure it
|
|
7
|
-
|
|
8
|
-
Four built-in channels (`SentryLog` needs the optional `@sentry/node` peer). A channel is a destination for a log entry — the logger fans out every entry to every registered channel in parallel.
|
|
9
|
-
|
|
10
|
-
## The decision
|
|
11
|
-
|
|
12
|
-
| Need | Pick |
|
|
13
|
-
|---|---|
|
|
14
|
-
| Local dev, colored output in the terminal | `ConsoleLog` |
|
|
15
|
-
| Plain text `.log` files on disk — humans read them | `FileLog` |
|
|
16
|
-
| Structured `.json` files — a log aggregator (Loki / Datadog / Elastic) reads them | `JSONFileLog` |
|
|
17
|
-
| Errors & warnings into Sentry (events + breadcrumbs) | `SentryLog` |
|
|
18
|
-
|
|
19
|
-
Most production setups use **two** channels: `ConsoleLog` + one file channel. Dev uses `ConsoleLog` only.
|
|
20
|
-
|
|
21
|
-
## `ConsoleLog`
|
|
22
|
-
|
|
23
|
-
Zero config. Colored, icon-prefixed lines to the terminal.
|
|
24
|
-
|
|
25
|
-
```ts
|
|
26
|
-
import { ConsoleLog } from "@warlock.js/logger";
|
|
27
|
-
|
|
28
|
-
new ConsoleLog();
|
|
29
|
-
// ⚙ debug (10:22:00.000) [auth] [hashPassword] Hashing started
|
|
30
|
-
// ℹ info (10:22:01.482) [users] [register] New user created
|
|
31
|
-
// ✗ error (10:22:03.111) [payments] [charge] Card declined
|
|
32
|
-
```
|
|
33
|
-
|
|
34
|
-
Properties:
|
|
35
|
-
- `name = "console"`, `terminal = true`
|
|
36
|
-
- Accepts `ConsoleLogConfig` — `levels`, `filter`, `dateFormat`, `showContext`, `contextDepth`
|
|
37
|
-
- If `message` is an object, a second `console.log(message)` is issued so Node's inspector can expand it
|
|
38
|
-
|
|
39
|
-
### Showing context
|
|
40
|
-
|
|
41
|
-
By default `ConsoleLog` drops the `context` payload (the file/JSON channels still keep it). Flip `showContext: true` to render it on a second line — useful in development:
|
|
42
|
-
|
|
43
|
-
```ts
|
|
44
|
-
new ConsoleLog({ showContext: true });
|
|
45
|
-
|
|
46
|
-
log.info("payments", "charge", "card declined", { userId: 42, amount: 1999 });
|
|
47
|
-
// ℹ info (…) [payments] [charge] card declined
|
|
48
|
-
// ↳ { userId: 42, amount: 1999 }
|
|
49
|
-
```
|
|
50
|
-
|
|
51
|
-
Tune `contextDepth` (default `4`) to clamp how deep `util.inspect` recurses into nested objects.
|
|
52
|
-
|
|
53
|
-
## `FileLog`
|
|
54
|
-
|
|
55
|
-
Plain text. Buffers in memory, flushes to disk periodically.
|
|
56
|
-
|
|
57
|
-
```ts
|
|
58
|
-
import { FileLog } from "@warlock.js/logger";
|
|
59
|
-
|
|
60
|
-
new FileLog({
|
|
61
|
-
storagePath: "./storage/logs", // default: process.cwd() + "/storage/logs"
|
|
62
|
-
name: "app", // default: "app"
|
|
63
|
-
extension: "log", // default: "log"
|
|
64
|
-
chunk: "daily", // "single" (default) | "daily" | "hourly"
|
|
65
|
-
rotate: true, // default: true
|
|
66
|
-
maxFileSize: 10 * 1024 * 1024, // default: 10MB — triggers rotation
|
|
67
|
-
maxMessagesToWrite: 100, // default: 100 — flush threshold
|
|
68
|
-
groupBy: ["level", "module"], // optional subdirectory nesting
|
|
69
|
-
});
|
|
70
|
-
```
|
|
71
|
-
|
|
72
|
-
Line format: `[date time] [level] [module][action]: message` — or a `[trace]` block when `message` is an `Error`.
|
|
73
|
-
|
|
74
|
-
### Key gotchas
|
|
75
|
-
|
|
76
|
-
- **Buffers!** Messages sit in memory until either `maxMessagesToWrite` is reached, 5 seconds pass, or `flushSync()` is called. A process that crashes without flushing loses buffered entries.
|
|
77
|
-
- **`chunk: "daily"` picks a filename per day.** File name becomes `DD-MM-YYYY.log`. Combined with `rotate: true`, rotated archives get `Date.now()` suffixed.
|
|
78
|
-
- **`groupBy` nests directories.** `groupBy: ["level", "module"]` produces `storage/logs/error/payments/app.log`. Order matters.
|
|
79
|
-
- **Dispose channels you discard.** A live `FileLog` keeps a 5-second flush interval running. If you swap the channel list at runtime (reconfigure the logger), call `channel.dispose()` on the old instance — it clears that timer and drains the buffer one last time. Skipping it leaks one timer per discarded channel and keeps the event loop alive. (Channels that live for the whole process don't need this — process exit clears the timer.)
|
|
80
|
-
|
|
81
|
-
## `JSONFileLog`
|
|
82
|
-
|
|
83
|
-
Subclass of `FileLog` — same buffering, chunking, rotation, grouping. Output is a JSON object with a `messages` array:
|
|
84
|
-
|
|
85
|
-
```json
|
|
86
|
-
{
|
|
87
|
-
"messages": [
|
|
88
|
-
{
|
|
89
|
-
"content": "Card declined",
|
|
90
|
-
"level": "error",
|
|
91
|
-
"date": "15-03-2024 10:22:03",
|
|
92
|
-
"module": "payments",
|
|
93
|
-
"action": "charge",
|
|
94
|
-
"stack": [
|
|
95
|
-
"Error: Card declined",
|
|
96
|
-
" at chargeCard (/app/src/payments.ts:42:11)"
|
|
97
|
-
]
|
|
98
|
-
}
|
|
99
|
-
]
|
|
100
|
-
}
|
|
101
|
-
```
|
|
102
|
-
|
|
103
|
-
Differences from `FileLog`:
|
|
104
|
-
- `name = "fileJson"` (**not** `"json"` — use this exact string for `log.channel("fileJson")`)
|
|
105
|
-
- `extension` is always `"json"` — the option is silently ignored
|
|
106
|
-
- Error `stack` is stored as `string[]` (split on newlines) — easy to query in aggregators
|
|
107
|
-
- `content` holds the original user-supplied `message` (not a pre-formatted line)
|
|
108
|
-
- Corrupted existing file → reinitialized to `{ messages: [] }` on next write (does not throw)
|
|
109
|
-
- **Safe serialization by construction.** All writes go through `safe-stable-stringify` with a custom `Error` replacer — circular refs become `"[Circular]"`, BigInt is stringified, functions/symbols are dropped, nested `Error` instances expand to `{ name, message, stack, ...enumerable }`. A context payload with a class graph or circular reference will never throw during the write.
|
|
110
|
-
|
|
111
|
-
## `SentryLog`
|
|
112
|
-
|
|
113
|
-
Forwards entries to Sentry — `error` / `warn` become events (`captureException` for `Error` messages, `captureMessage` otherwise), every other level a breadcrumb (no quota). `@sentry/node` is an optional peer, lazily imported; pass an existing `client` or `options`.
|
|
114
|
-
|
|
115
|
-
```ts
|
|
116
|
-
import * as Sentry from "@sentry/node";
|
|
117
|
-
import { SentryLog } from "@warlock.js/logger";
|
|
118
|
-
|
|
119
|
-
new SentryLog({ client: Sentry, eventLevels: ["error", "warn"] });
|
|
120
|
-
```
|
|
121
|
-
|
|
122
|
-
Full guide — level mapping, init modes, shutdown draining: [`ship-logs-to-sentry`](@warlock.js/logger/ship-logs-to-sentry/SKILL.md).
|
|
123
|
-
|
|
124
|
-
## Shared config — `BasicLogConfigurations`
|
|
125
|
-
|
|
126
|
-
Every channel constructor accepts at minimum:
|
|
127
|
-
|
|
128
|
-
```ts
|
|
129
|
-
type BasicLogConfigurations = {
|
|
130
|
-
levels?: LogLevel[]; // whitelist — omit or [] to allow all
|
|
131
|
-
filter?: (data: LoggingData) => boolean; // custom predicate
|
|
132
|
-
dateFormat?: { date?: string; time?: string }; // Day.js format strings
|
|
133
|
-
context?: (data) => Promise<Record<string, any>>; // reserved — not yet read
|
|
134
|
-
};
|
|
135
|
-
```
|
|
136
|
-
|
|
137
|
-
Concrete file channels extend this with their storage/chunk/rotate/groupBy options via intersection.
|
|
138
|
-
|
|
139
|
-
## Picking a channel by name at runtime
|
|
140
|
-
|
|
141
|
-
```ts
|
|
142
|
-
log.channel("console"); // → ConsoleLog | undefined
|
|
143
|
-
log.channel("file"); // → FileLog | undefined
|
|
144
|
-
log.channel("fileJson"); // ← note the name — NOT "json"
|
|
145
|
-
log.channel("sentry"); // → SentryLog | undefined
|
|
146
|
-
```
|
|
147
|
-
|
|
148
|
-
If two channels share a `name`, only one is reachable this way — the search returns the first match.
|
|
149
|
-
|
|
150
|
-
## See also
|
|
151
|
-
|
|
152
|
-
- [`@warlock.js/logger/configure-logger/SKILL.md`](@warlock.js/logger/configure-logger/SKILL.md) — registering channels at startup
|
|
153
|
-
- [`@warlock.js/logger/filter-log-entries/SKILL.md`](@warlock.js/logger/filter-log-entries/SKILL.md) — `levels` and `filter` config in detail
|
|
154
|
-
- [`@warlock.js/logger/ship-logs-to-sentry/SKILL.md`](@warlock.js/logger/ship-logs-to-sentry/SKILL.md) — the `SentryLog` channel in depth
|
|
155
|
-
- [`@warlock.js/logger/write-custom-log-channel/SKILL.md`](@warlock.js/logger/write-custom-log-channel/SKILL.md) — extending `LogChannel` for custom sinks
|
|
1
|
+
---
|
|
2
|
+
name: pick-log-channel
|
|
3
|
+
description: '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`.'
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Channels — which one to pick and how to configure it
|
|
7
|
+
|
|
8
|
+
Four built-in channels (`SentryLog` needs the optional `@sentry/node` peer). A channel is a destination for a log entry — the logger fans out every entry to every registered channel in parallel.
|
|
9
|
+
|
|
10
|
+
## The decision
|
|
11
|
+
|
|
12
|
+
| Need | Pick |
|
|
13
|
+
|---|---|
|
|
14
|
+
| Local dev, colored output in the terminal | `ConsoleLog` |
|
|
15
|
+
| Plain text `.log` files on disk — humans read them | `FileLog` |
|
|
16
|
+
| Structured `.json` files — a log aggregator (Loki / Datadog / Elastic) reads them | `JSONFileLog` |
|
|
17
|
+
| Errors & warnings into Sentry (events + breadcrumbs) | `SentryLog` |
|
|
18
|
+
|
|
19
|
+
Most production setups use **two** channels: `ConsoleLog` + one file channel. Dev uses `ConsoleLog` only.
|
|
20
|
+
|
|
21
|
+
## `ConsoleLog`
|
|
22
|
+
|
|
23
|
+
Zero config. Colored, icon-prefixed lines to the terminal.
|
|
24
|
+
|
|
25
|
+
```ts
|
|
26
|
+
import { ConsoleLog } from "@warlock.js/logger";
|
|
27
|
+
|
|
28
|
+
new ConsoleLog();
|
|
29
|
+
// ⚙ debug (10:22:00.000) [auth] [hashPassword] Hashing started
|
|
30
|
+
// ℹ info (10:22:01.482) [users] [register] New user created
|
|
31
|
+
// ✗ error (10:22:03.111) [payments] [charge] Card declined
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
Properties:
|
|
35
|
+
- `name = "console"`, `terminal = true`
|
|
36
|
+
- Accepts `ConsoleLogConfig` — `levels`, `filter`, `dateFormat`, `showContext`, `contextDepth`
|
|
37
|
+
- If `message` is an object, a second `console.log(message)` is issued so Node's inspector can expand it
|
|
38
|
+
|
|
39
|
+
### Showing context
|
|
40
|
+
|
|
41
|
+
By default `ConsoleLog` drops the `context` payload (the file/JSON channels still keep it). Flip `showContext: true` to render it on a second line — useful in development:
|
|
42
|
+
|
|
43
|
+
```ts
|
|
44
|
+
new ConsoleLog({ showContext: true });
|
|
45
|
+
|
|
46
|
+
log.info("payments", "charge", "card declined", { userId: 42, amount: 1999 });
|
|
47
|
+
// ℹ info (…) [payments] [charge] card declined
|
|
48
|
+
// ↳ { userId: 42, amount: 1999 }
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
Tune `contextDepth` (default `4`) to clamp how deep `util.inspect` recurses into nested objects.
|
|
52
|
+
|
|
53
|
+
## `FileLog`
|
|
54
|
+
|
|
55
|
+
Plain text. Buffers in memory, flushes to disk periodically.
|
|
56
|
+
|
|
57
|
+
```ts
|
|
58
|
+
import { FileLog } from "@warlock.js/logger";
|
|
59
|
+
|
|
60
|
+
new FileLog({
|
|
61
|
+
storagePath: "./storage/logs", // default: process.cwd() + "/storage/logs"
|
|
62
|
+
name: "app", // default: "app"
|
|
63
|
+
extension: "log", // default: "log"
|
|
64
|
+
chunk: "daily", // "single" (default) | "daily" | "hourly"
|
|
65
|
+
rotate: true, // default: true
|
|
66
|
+
maxFileSize: 10 * 1024 * 1024, // default: 10MB — triggers rotation
|
|
67
|
+
maxMessagesToWrite: 100, // default: 100 — flush threshold
|
|
68
|
+
groupBy: ["level", "module"], // optional subdirectory nesting
|
|
69
|
+
});
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
Line format: `[date time] [level] [module][action]: message` — or a `[trace]` block when `message` is an `Error`.
|
|
73
|
+
|
|
74
|
+
### Key gotchas
|
|
75
|
+
|
|
76
|
+
- **Buffers!** Messages sit in memory until either `maxMessagesToWrite` is reached, 5 seconds pass, or `flushSync()` is called. A process that crashes without flushing loses buffered entries.
|
|
77
|
+
- **`chunk: "daily"` picks a filename per day.** File name becomes `DD-MM-YYYY.log`. Combined with `rotate: true`, rotated archives get `Date.now()` suffixed.
|
|
78
|
+
- **`groupBy` nests directories.** `groupBy: ["level", "module"]` produces `storage/logs/error/payments/app.log`. Order matters.
|
|
79
|
+
- **Dispose channels you discard.** A live `FileLog` keeps a 5-second flush interval running. If you swap the channel list at runtime (reconfigure the logger), call `channel.dispose()` on the old instance — it clears that timer and drains the buffer one last time. Skipping it leaks one timer per discarded channel and keeps the event loop alive. (Channels that live for the whole process don't need this — process exit clears the timer.)
|
|
80
|
+
|
|
81
|
+
## `JSONFileLog`
|
|
82
|
+
|
|
83
|
+
Subclass of `FileLog` — same buffering, chunking, rotation, grouping. Output is a JSON object with a `messages` array:
|
|
84
|
+
|
|
85
|
+
```json
|
|
86
|
+
{
|
|
87
|
+
"messages": [
|
|
88
|
+
{
|
|
89
|
+
"content": "Card declined",
|
|
90
|
+
"level": "error",
|
|
91
|
+
"date": "15-03-2024 10:22:03",
|
|
92
|
+
"module": "payments",
|
|
93
|
+
"action": "charge",
|
|
94
|
+
"stack": [
|
|
95
|
+
"Error: Card declined",
|
|
96
|
+
" at chargeCard (/app/src/payments.ts:42:11)"
|
|
97
|
+
]
|
|
98
|
+
}
|
|
99
|
+
]
|
|
100
|
+
}
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
Differences from `FileLog`:
|
|
104
|
+
- `name = "fileJson"` (**not** `"json"` — use this exact string for `log.channel("fileJson")`)
|
|
105
|
+
- `extension` is always `"json"` — the option is silently ignored
|
|
106
|
+
- Error `stack` is stored as `string[]` (split on newlines) — easy to query in aggregators
|
|
107
|
+
- `content` holds the original user-supplied `message` (not a pre-formatted line)
|
|
108
|
+
- Corrupted existing file → reinitialized to `{ messages: [] }` on next write (does not throw)
|
|
109
|
+
- **Safe serialization by construction.** All writes go through `safe-stable-stringify` with a custom `Error` replacer — circular refs become `"[Circular]"`, BigInt is stringified, functions/symbols are dropped, nested `Error` instances expand to `{ name, message, stack, ...enumerable }`. A context payload with a class graph or circular reference will never throw during the write.
|
|
110
|
+
|
|
111
|
+
## `SentryLog`
|
|
112
|
+
|
|
113
|
+
Forwards entries to Sentry — `error` / `warn` become events (`captureException` for `Error` messages, `captureMessage` otherwise), every other level a breadcrumb (no quota). `@sentry/node` is an optional peer, lazily imported; pass an existing `client` or `options`.
|
|
114
|
+
|
|
115
|
+
```ts
|
|
116
|
+
import * as Sentry from "@sentry/node";
|
|
117
|
+
import { SentryLog } from "@warlock.js/logger";
|
|
118
|
+
|
|
119
|
+
new SentryLog({ client: Sentry, eventLevels: ["error", "warn"] });
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
Full guide — level mapping, init modes, shutdown draining: [`ship-logs-to-sentry`](@warlock.js/logger/ship-logs-to-sentry/SKILL.md).
|
|
123
|
+
|
|
124
|
+
## Shared config — `BasicLogConfigurations`
|
|
125
|
+
|
|
126
|
+
Every channel constructor accepts at minimum:
|
|
127
|
+
|
|
128
|
+
```ts
|
|
129
|
+
type BasicLogConfigurations = {
|
|
130
|
+
levels?: LogLevel[]; // whitelist — omit or [] to allow all
|
|
131
|
+
filter?: (data: LoggingData) => boolean; // custom predicate
|
|
132
|
+
dateFormat?: { date?: string; time?: string }; // Day.js format strings
|
|
133
|
+
context?: (data) => Promise<Record<string, any>>; // reserved — not yet read
|
|
134
|
+
};
|
|
135
|
+
```
|
|
136
|
+
|
|
137
|
+
Concrete file channels extend this with their storage/chunk/rotate/groupBy options via intersection.
|
|
138
|
+
|
|
139
|
+
## Picking a channel by name at runtime
|
|
140
|
+
|
|
141
|
+
```ts
|
|
142
|
+
log.channel("console"); // → ConsoleLog | undefined
|
|
143
|
+
log.channel("file"); // → FileLog | undefined
|
|
144
|
+
log.channel("fileJson"); // ← note the name — NOT "json"
|
|
145
|
+
log.channel("sentry"); // → SentryLog | undefined
|
|
146
|
+
```
|
|
147
|
+
|
|
148
|
+
If two channels share a `name`, only one is reachable this way — the search returns the first match.
|
|
149
|
+
|
|
150
|
+
## See also
|
|
151
|
+
|
|
152
|
+
- [`@warlock.js/logger/configure-logger/SKILL.md`](@warlock.js/logger/configure-logger/SKILL.md) — registering channels at startup
|
|
153
|
+
- [`@warlock.js/logger/filter-log-entries/SKILL.md`](@warlock.js/logger/filter-log-entries/SKILL.md) — `levels` and `filter` config in detail
|
|
154
|
+
- [`@warlock.js/logger/ship-logs-to-sentry/SKILL.md`](@warlock.js/logger/ship-logs-to-sentry/SKILL.md) — the `SentryLog` channel in depth
|
|
155
|
+
- [`@warlock.js/logger/write-custom-log-channel/SKILL.md`](@warlock.js/logger/write-custom-log-channel/SKILL.md) — extending `LogChannel` for custom sinks
|