@warlock.js/logger 5.34.1 → 5.34.3
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/llms-full.txt +78 -120
- package/llms.txt +12 -12
- package/package.json +2 -2
- package/skills/index.md +42 -2
- package/skills/overview/SKILL.md +0 -105
package/CHANGELOG.md
CHANGED
|
@@ -4,6 +4,18 @@ All notable changes to `@warlock.js/logger` are documented in this file.
|
|
|
4
4
|
|
|
5
5
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/). `@warlock.js/*` packages are released in lockstep — every package shares the same version number, so a version below may list only the changes that affected this package.
|
|
6
6
|
|
|
7
|
+
## 5.34.3 - 2026-10-20
|
|
8
|
+
|
|
9
|
+
### Changed
|
|
10
|
+
|
|
11
|
+
- Condensed the package agent skill description to at most 450 characters, with API names and package routing guidance.
|
|
12
|
+
|
|
13
|
+
## 5.34.2 - 2026-10-19
|
|
14
|
+
|
|
15
|
+
### Fixed
|
|
16
|
+
|
|
17
|
+
- Merge package orientation into the grouped skill index and remove the duplicate overview topic; preserve that orientation in generated LLM projections.
|
|
18
|
+
|
|
7
19
|
## 5.33.0 - 2026-10-19
|
|
8
20
|
|
|
9
21
|
### Added
|
package/llms-full.txt
CHANGED
|
@@ -4,7 +4,74 @@
|
|
|
4
4
|
|
|
5
5
|
> Generated artifact. Concatenates every SKILL.md and reference file under `@warlock.js/logger/skills/`. Re-run `node scripts/generate-llms.mjs` after any change.
|
|
6
6
|
|
|
7
|
-
##
|
|
7
|
+
## Package orientation
|
|
8
|
+
|
|
9
|
+
---
|
|
10
|
+
description: 'Structured logging with channels, filtering, redaction and shutdown flushing: `log`, `Logger`, `LogChannel`, `ConsoleLog`, `JSONFileLog`, `SentryLog` and `applyRedact`. Use for "log an error with context", "hide tokens", "write JSON logs", "send errors to Sentry" or "flush before exit". Not this package → @warlock.js/core for HTTP error responses; @warlock.js/ai-panoptic for agent traces and AI cost dashboards.'
|
|
11
|
+
---
|
|
12
|
+
|
|
13
|
+
# @warlock.js/logger
|
|
14
|
+
|
|
15
|
+
The `log` singleton (a `Logger`) sends each entry, with module, action, level and message, to every configured `LogChannel`. Channels decide where entries go (console, file, JSON file, Sentry, custom) and can filter by level or module.
|
|
16
|
+
|
|
17
|
+
Works standalone in Node projects; core is not required. Levels are `debug`, `info`, `warn`, `error`, `success`, and `fatal` (above `error`).
|
|
18
|
+
|
|
19
|
+
## The 80% path
|
|
20
|
+
|
|
21
|
+
1. Start with `logger-basics.md`.
|
|
22
|
+
2. Pick channels: `pick-log-channel.md`; set them up: `configure-logger.md`.
|
|
23
|
+
3. Log via `log.info/warn/error` or helpers: `use-log-helpers.md`.
|
|
24
|
+
4. Redact secrets: `redact-sensitive-log-fields.md`; narrow output: `filter-log-entries.md`.
|
|
25
|
+
5. Production safety: `capture-unhandled-errors.md`, `flush-logs-on-shutdown.md`, `ship-logs-to-sentry.md`.
|
|
26
|
+
6. Extend or test: `write-custom-log-channel.md`, `test-logging-code.md`.
|
|
27
|
+
|
|
28
|
+
## Conventions and pitfalls
|
|
29
|
+
|
|
30
|
+
- `fatal` does not flush or exit by itself; the caller awaits `log.flush()` and then exits.
|
|
31
|
+
- File channels buffer, so an unflushed process exit loses the tail of the log.
|
|
32
|
+
- Configure redaction before logging request bodies or auth data; the default key set is a floor, not a guarantee.
|
|
33
|
+
- Log structured context objects rather than interpolated strings, so channels and Sentry can index them.
|
|
34
|
+
|
|
35
|
+
## Server-only package
|
|
36
|
+
|
|
37
|
+
`@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.
|
|
38
|
+
|
|
39
|
+
## When to reach for it
|
|
40
|
+
|
|
41
|
+
- 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.
|
|
42
|
+
- 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).
|
|
43
|
+
- 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.
|
|
44
|
+
|
|
45
|
+
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.
|
|
46
|
+
|
|
47
|
+
## The mental model in one paragraph
|
|
48
|
+
|
|
49
|
+
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.
|
|
50
|
+
|
|
51
|
+
## Built-in channels at a glance
|
|
52
|
+
|
|
53
|
+
| Channel | Sink | `terminal` | Buffered |
|
|
54
|
+
| ------------- | --------------------------- | ----------------------- | ----------------------------------------------------------------------- |
|
|
55
|
+
| `ConsoleLog` | `process.stdout` | `true` (colors kept) | no |
|
|
56
|
+
| `FileLog` | `.log` files | `false` (ANSI stripped) | yes (5s timer or 100-entry buffer) |
|
|
57
|
+
| `JSONFileLog` | `.json` files | `false` (ANSI stripped) | yes (same buffering) |
|
|
58
|
+
| `SentryLog` | Sentry events + breadcrumbs | `false` | via the Sentry SDK's own transport (`@sentry/node` is an optional peer) |
|
|
59
|
+
|
|
60
|
+
`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.
|
|
61
|
+
|
|
62
|
+
## What this package deliberately doesn't do
|
|
63
|
+
|
|
64
|
+
- **Distributed tracing.** Use OpenTelemetry. Logger gets you structured local logs with `module / action`; trace correlation is a different problem.
|
|
65
|
+
- **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).
|
|
66
|
+
- **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.
|
|
67
|
+
- **Log analysis.** Querying / aggregating / alerting is on the sink side (Loki, ELK, Datadog).
|
|
68
|
+
|
|
69
|
+
## See also
|
|
70
|
+
|
|
71
|
+
- The `warlock-conventions` topic of the `warlock-js-core` skill — the parent framework's conventions; logger is one of its foundation packages and ships transitively when you install core.
|
|
72
|
+
|
|
73
|
+
|
|
74
|
+
## capture-unhandled-errors `@warlock.js/logger/skills/capture-unhandled-errors/SKILL.md`
|
|
8
75
|
|
|
9
76
|
---
|
|
10
77
|
name: capture-unhandled-errors
|
|
@@ -118,7 +185,7 @@ Both listeners log with:
|
|
|
118
185
|
If you want these routed to a specific file, filter on `data.module === "app"`. See the `filter-log-entries` topic.
|
|
119
186
|
|
|
120
187
|
|
|
121
|
-
## configure-logger `@warlock.js/logger/configure-logger/SKILL.md`
|
|
188
|
+
## configure-logger `@warlock.js/logger/skills/configure-logger/SKILL.md`
|
|
122
189
|
|
|
123
190
|
---
|
|
124
191
|
name: configure-logger
|
|
@@ -224,7 +291,7 @@ See the `redact-sensitive-log-fields` topic for the redact contract and the `fil
|
|
|
224
291
|
- The `flush-logs-on-shutdown` topic — `autoFlushOn` event behavior
|
|
225
292
|
|
|
226
293
|
|
|
227
|
-
## filter-log-entries `@warlock.js/logger/filter-log-entries/SKILL.md`
|
|
294
|
+
## filter-log-entries `@warlock.js/logger/skills/filter-log-entries/SKILL.md`
|
|
228
295
|
|
|
229
296
|
---
|
|
230
297
|
name: filter-log-entries
|
|
@@ -348,7 +415,7 @@ Filters run on **every** entry per channel. A synchronous, cheap predicate is fi
|
|
|
348
415
|
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.
|
|
349
416
|
|
|
350
417
|
|
|
351
|
-
## flush-logs-on-shutdown `@warlock.js/logger/flush-logs-on-shutdown/SKILL.md`
|
|
418
|
+
## flush-logs-on-shutdown `@warlock.js/logger/skills/flush-logs-on-shutdown/SKILL.md`
|
|
352
419
|
|
|
353
420
|
---
|
|
354
421
|
name: flush-logs-on-shutdown
|
|
@@ -488,7 +555,7 @@ resolves so shutdown can continue. Timeout does not cancel a channel's network
|
|
|
488
555
|
operation; its driver owns cancellation.
|
|
489
556
|
|
|
490
557
|
|
|
491
|
-
## logger-basics `@warlock.js/logger/logger-basics/SKILL.md`
|
|
558
|
+
## logger-basics `@warlock.js/logger/skills/logger-basics/SKILL.md`
|
|
492
559
|
|
|
493
560
|
---
|
|
494
561
|
name: logger-basics
|
|
@@ -577,116 +644,7 @@ Every call signature is the same — `module`, `action`, `message`, optional `co
|
|
|
577
644
|
- Don't shadow the import in local code: `for (const log of logEntries) { ... }` will hide the singleton inside that block. Rename loop variables (`entry`, `record`) when working with logger imports.
|
|
578
645
|
|
|
579
646
|
|
|
580
|
-
##
|
|
581
|
-
|
|
582
|
-
---
|
|
583
|
-
name: overview
|
|
584
|
-
description: "@warlock.js/logger overview for orientation and choosing the focused skill for the task."
|
|
585
|
-
---
|
|
586
|
-
|
|
587
|
-
# `@warlock.js/logger` — overview
|
|
588
|
-
|
|
589
|
-
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.
|
|
590
|
-
|
|
591
|
-
Ships standalone — `@warlock.js/core` is not required. Drop it into any Node project.
|
|
592
|
-
|
|
593
|
-
## Server-only package
|
|
594
|
-
|
|
595
|
-
`@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.
|
|
596
|
-
|
|
597
|
-
## When to reach for it
|
|
598
|
-
|
|
599
|
-
- 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.
|
|
600
|
-
- 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).
|
|
601
|
-
- 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.
|
|
602
|
-
|
|
603
|
-
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.
|
|
604
|
-
|
|
605
|
-
## The mental model in one paragraph
|
|
606
|
-
|
|
607
|
-
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.
|
|
608
|
-
|
|
609
|
-
## Skills index
|
|
610
|
-
|
|
611
|
-
Eleven task skills cover everything. Load the one that matches your job — most callers only ever need `logger-basics` + `configure-logger` + `pick-log-channel`.
|
|
612
|
-
|
|
613
|
-
### Foundations
|
|
614
|
-
|
|
615
|
-
#### `logger-basics`
|
|
616
|
-
|
|
617
|
-
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.
|
|
618
|
-
|
|
619
|
-
#### `configure-logger`
|
|
620
|
-
|
|
621
|
-
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.
|
|
622
|
-
|
|
623
|
-
### Channels
|
|
624
|
-
|
|
625
|
-
#### `pick-log-channel`
|
|
626
|
-
|
|
627
|
-
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).
|
|
628
|
-
|
|
629
|
-
#### `ship-logs-to-sentry`
|
|
630
|
-
|
|
631
|
-
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.
|
|
632
|
-
|
|
633
|
-
#### `write-custom-log-channel`
|
|
634
|
-
|
|
635
|
-
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.
|
|
636
|
-
|
|
637
|
-
### Production concerns
|
|
638
|
-
|
|
639
|
-
#### `redact-sensitive-log-fields`
|
|
640
|
-
|
|
641
|
-
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`.
|
|
642
|
-
|
|
643
|
-
#### `filter-log-entries`
|
|
644
|
-
|
|
645
|
-
Drop entries before they cost anything. Logger-wide `setMinLevel("info")` is the fast path; per-channel `levels` array + `filter` predicate for fine control.
|
|
646
|
-
|
|
647
|
-
#### `flush-logs-on-shutdown`
|
|
648
|
-
|
|
649
|
-
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.
|
|
650
|
-
|
|
651
|
-
#### `capture-unhandled-errors`
|
|
652
|
-
|
|
653
|
-
`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.
|
|
654
|
-
|
|
655
|
-
### Ergonomics + testing
|
|
656
|
-
|
|
657
|
-
#### `use-log-helpers`
|
|
658
|
-
|
|
659
|
-
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`.
|
|
660
|
-
|
|
661
|
-
#### `test-logging-code`
|
|
662
|
-
|
|
663
|
-
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`.
|
|
664
|
-
|
|
665
|
-
## Built-in channels at a glance
|
|
666
|
-
|
|
667
|
-
| Channel | Sink | `terminal` | Buffered |
|
|
668
|
-
| ------------- | --------------------------- | ----------------------- | ----------------------------------------------------------------------- |
|
|
669
|
-
| `ConsoleLog` | `process.stdout` | `true` (colors kept) | no |
|
|
670
|
-
| `FileLog` | `.log` files | `false` (ANSI stripped) | yes (5s timer or 100-entry buffer) |
|
|
671
|
-
| `JSONFileLog` | `.json` files | `false` (ANSI stripped) | yes (same buffering) |
|
|
672
|
-
| `SentryLog` | Sentry events + breadcrumbs | `false` | via the Sentry SDK's own transport (`@sentry/node` is an optional peer) |
|
|
673
|
-
|
|
674
|
-
`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.
|
|
675
|
-
|
|
676
|
-
## What this package deliberately doesn't do
|
|
677
|
-
|
|
678
|
-
- **Distributed tracing.** Use OpenTelemetry. Logger gets you structured local logs with `module / action`; trace correlation is a different problem.
|
|
679
|
-
- **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).
|
|
680
|
-
- **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.
|
|
681
|
-
- **Log analysis.** Querying / aggregating / alerting is on the sink side (Loki, ELK, Datadog).
|
|
682
|
-
|
|
683
|
-
## See also
|
|
684
|
-
|
|
685
|
-
- The `warlock-conventions` topic of the `warlock-js-core` skill — the parent framework's conventions; logger is one of its foundation packages and ships transitively when you install core.
|
|
686
|
-
- When synced via agent-kit, this `overview` topic is flattened to the front-door skill `.claude/skills/warlock-js-logger-overview/` — every cross-link above names the topic (and the owning skill, across packages) instead of a file path so it survives that flattening.
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
## pick-log-channel `@warlock.js/logger/pick-log-channel/SKILL.md`
|
|
647
|
+
## pick-log-channel `@warlock.js/logger/skills/pick-log-channel/SKILL.md`
|
|
690
648
|
|
|
691
649
|
---
|
|
692
650
|
name: pick-log-channel
|
|
@@ -857,7 +815,7 @@ If two channels share a `name`, only one is reachable this way — the search re
|
|
|
857
815
|
- The `write-custom-log-channel` topic — extending `LogChannel` for custom sinks
|
|
858
816
|
|
|
859
817
|
|
|
860
|
-
## redact-sensitive-log-fields `@warlock.js/logger/redact-sensitive-log-fields/SKILL.md`
|
|
818
|
+
## redact-sensitive-log-fields `@warlock.js/logger/skills/redact-sensitive-log-fields/SKILL.md`
|
|
861
819
|
|
|
862
820
|
---
|
|
863
821
|
name: redact-sensitive-log-fields
|
|
@@ -1062,7 +1020,7 @@ Keys are normalized; values are exact literal substrings (punctuation is literal
|
|
|
1062
1020
|
Registration retains keys/values in memory for the process lifetime. Removed/rotated values stay protected. It does not protect prior logs, transformed/encoded credentials, direct console calls, third-party loggers or credentials never registered. Core's `secrets` store registers its encountered keys/values automatically. Keep credentials out of logs even with this protection enabled.
|
|
1063
1021
|
|
|
1064
1022
|
|
|
1065
|
-
## ship-logs-to-sentry `@warlock.js/logger/ship-logs-to-sentry/SKILL.md`
|
|
1023
|
+
## ship-logs-to-sentry `@warlock.js/logger/skills/ship-logs-to-sentry/SKILL.md`
|
|
1066
1024
|
|
|
1067
1025
|
---
|
|
1068
1026
|
name: ship-logs-to-sentry
|
|
@@ -1184,7 +1142,7 @@ The channel never crashes your app: the dynamic import failure is swallowed, the
|
|
|
1184
1142
|
- The `write-custom-log-channel` topic — build your own sink
|
|
1185
1143
|
|
|
1186
1144
|
|
|
1187
|
-
## test-logging-code `@warlock.js/logger/test-logging-code/SKILL.md`
|
|
1145
|
+
## test-logging-code `@warlock.js/logger/skills/test-logging-code/SKILL.md`
|
|
1188
1146
|
|
|
1189
1147
|
---
|
|
1190
1148
|
name: test-logging-code
|
|
@@ -1367,7 +1325,7 @@ process.emit("uncaughtException", new Error("test"), "uncaughtException");
|
|
|
1367
1325
|
See the `capture-unhandled-errors` topic for a full example.
|
|
1368
1326
|
|
|
1369
1327
|
|
|
1370
|
-
## use-log-helpers `@warlock.js/logger/use-log-helpers/SKILL.md`
|
|
1328
|
+
## use-log-helpers `@warlock.js/logger/skills/use-log-helpers/SKILL.md`
|
|
1371
1329
|
|
|
1372
1330
|
---
|
|
1373
1331
|
name: use-log-helpers
|
|
@@ -1437,7 +1395,7 @@ end({ rowsProcessed: report.rowCount });
|
|
|
1437
1395
|
- `log.timer` shorthand binds to the singleton — see the `test-logging-code` topic for how to swap channels per test.
|
|
1438
1396
|
|
|
1439
1397
|
|
|
1440
|
-
## write-custom-log-channel `@warlock.js/logger/write-custom-log-channel/SKILL.md`
|
|
1398
|
+
## write-custom-log-channel `@warlock.js/logger/skills/write-custom-log-channel/SKILL.md`
|
|
1441
1399
|
|
|
1442
1400
|
---
|
|
1443
1401
|
name: write-custom-log-channel
|
package/llms.txt
CHANGED
|
@@ -6,15 +6,15 @@
|
|
|
6
6
|
|
|
7
7
|
## Skills
|
|
8
8
|
|
|
9
|
-
- [
|
|
10
|
-
- [
|
|
11
|
-
- [
|
|
12
|
-
- [
|
|
13
|
-
- [
|
|
14
|
-
- [
|
|
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 — the `ship-logs-to-sentry` topic; custom sinks — the `write-custom-log-channel` topic; registration — the `configure-logger` topic; 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 — a built-in secret-key denylist on by default (DEFAULT_REDACT_KEYS), plus two-layer additive redaction via log.configure({redact: {paths, keys}}) (logger floor) + per-channel redact (more on top). Dotted glob paths (*, **). Triggers: `redact`, `paths`, `keys`, `defaultKeys`, `censor`, `log.setRedact`, `applyRedact`, `maskEmail`, `maskEmailsInText`, `registerLogSecret`, `maskRegisteredLogSecrets`; "redact passwords in logs", "strip tokens from log output", "hide authorization headers", "scrub PII before logging", "turn off default redaction"; typical import `import { log } from "@warlock.js/logger"`. Skip: filtering — the `filter-log-entries` topic; custom sinks — the `write-custom-log-channel` topic; 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 — the `pick-log-channel` topic; custom sinks — the `write-custom-log-channel` topic; graceful-shutdown flushing — the `flush-logs-on-shutdown` topic; Slack alerting recipe.
|
|
18
|
-
- [test-logging-code](@warlock.js/logger/test-logging-code/SKILL.md): Test code that touches the logger — silence 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 — the `write-custom-log-channel` topic; filtering — the `filter-log-entries` topic; 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 — the `logger-basics` topic; filtering — the `filter-log-entries` topic; 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 — the `pick-log-channel` topic; filtering — the `filter-log-entries` topic; competing libs `winston-transport`, `pino-transport`.
|
|
9
|
+
- [Package orientation](@warlock.js/logger/skills/index.md): Structured logging with channels, filtering, redaction and shutdown flushing: `log`, `Logger`, `LogChannel`, `ConsoleLog`, `JSONFileLog`, `SentryLog` and `applyRedact`. Use for "log an error with context", "hide tokens", "write JSON logs", "send errors to Sentry" or "flush before exit". Not this package → @warlock.js/core for HTTP error responses; @warlock.js/ai-panoptic for agent traces and AI cost dashboards.
|
|
10
|
+
- [capture-unhandled-errors](@warlock.js/logger/skills/capture-unhandled-errors/SKILL.md): captureAnyUnhandledRejection() installs process.on('unhandledRejection') → log.error and process.on('uncaughtException') → log.fatal + process.exit(1) so process-level failures land in your channels and a fatal crash is never silently swallowed into exit 0. Triggers: `captureAnyUnhandledRejection`, `exitOnUncaughtException`, `unhandledRejection`, `uncaughtException`, `log.error`, `log.fatal`; "log unhandled promise rejections", "catch uncaught exceptions to a file", "record crashes before exit", "server exits 0 with no error", "silent exit / production server stopped", "global error handler with logger"; typical import `import { captureAnyUnhandledRejection, log } from "@warlock.js/logger"`. Skip: flushing — the `flush-logs-on-shutdown` topic; filtering — the `filter-log-entries` topic; competing `Sentry.init`, `@sentry/node`; native `process.on('unhandledRejection')`.
|
|
11
|
+
- [configure-logger](@warlock.js/logger/skills/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 — the `pick-log-channel` topic; flushing — the `flush-logs-on-shutdown` topic; redaction — the `redact-sensitive-log-fields` topic; competing libs `winston.createLogger`, `pino`.
|
|
12
|
+
- [filter-log-entries](@warlock.js/logger/skills/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 — the `write-custom-log-channel` topic; channel picks — the `pick-log-channel` topic; competing libs `pino.levels`, `winston.format.filter`, `debug` env var.
|
|
13
|
+
- [flush-logs-on-shutdown](@warlock.js/logger/skills/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`, `onChannelError`, `log.flush({ timeout })`, `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 — the `capture-unhandled-errors` topic; custom sinks — the `write-custom-log-channel` topic; competing `pino.final`, `winston.end`; native `process.on('exit')`.
|
|
14
|
+
- [logger-basics](@warlock.js/logger/skills/logger-basics/SKILL.md): Start here for @warlock.js/logger channels, structured entries, transports, and application logging setup.
|
|
15
|
+
- [pick-log-channel](@warlock.js/logger/skills/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 — the `ship-logs-to-sentry` topic; custom sinks — the `write-custom-log-channel` topic; registration — the `configure-logger` topic; competing libs `winston-daily-rotate-file`, `pino-pretty`.
|
|
16
|
+
- [redact-sensitive-log-fields](@warlock.js/logger/skills/redact-sensitive-log-fields/SKILL.md): Strip secrets from log output — a built-in secret-key denylist on by default (DEFAULT_REDACT_KEYS), plus two-layer additive redaction via log.configure({redact: {paths, keys}}) (logger floor) + per-channel redact (more on top). Dotted glob paths (*, **). Triggers: `redact`, `paths`, `keys`, `defaultKeys`, `censor`, `log.setRedact`, `applyRedact`, `maskEmail`, `maskEmailsInText`, `registerLogSecret`, `maskRegisteredLogSecrets`; "redact passwords in logs", "strip tokens from log output", "hide authorization headers", "scrub PII before logging", "turn off default redaction"; typical import `import { log } from "@warlock.js/logger"`. Skip: filtering — the `filter-log-entries` topic; custom sinks — the `write-custom-log-channel` topic; competing libs `pino.redact`, `fast-redact`.
|
|
17
|
+
- [ship-logs-to-sentry](@warlock.js/logger/skills/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 — the `pick-log-channel` topic; custom sinks — the `write-custom-log-channel` topic; graceful-shutdown flushing — the `flush-logs-on-shutdown` topic; Slack alerting recipe.
|
|
18
|
+
- [test-logging-code](@warlock.js/logger/skills/test-logging-code/SKILL.md): Test code that touches the logger — silence 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 — the `write-custom-log-channel` topic; filtering — the `filter-log-entries` topic; competing `vi.spyOn(console)`, `jest.spyOn`.
|
|
19
|
+
- [use-log-helpers](@warlock.js/logger/skills/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 — the `logger-basics` topic; filtering — the `filter-log-entries` topic; competing `console.assert`, `console.time`, `console.timeEnd`, `perf_hooks.performance.now`.
|
|
20
|
+
- [write-custom-log-channel](@warlock.js/logger/skills/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 — the `pick-log-channel` topic; filtering — the `filter-log-entries` topic; competing libs `winston-transport`, `pino-transport`.
|
package/package.json
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
"dependencies": {
|
|
5
5
|
"@mongez/copper": "^2.1.3",
|
|
6
6
|
"@mongez/reinforcements": "^4.0.2",
|
|
7
|
-
"@warlock.js/fs": "5.34.
|
|
7
|
+
"@warlock.js/fs": "5.34.3",
|
|
8
8
|
"dayjs": "^1.11.9",
|
|
9
9
|
"safe-stable-stringify": "^2.5.0"
|
|
10
10
|
},
|
|
@@ -33,7 +33,7 @@
|
|
|
33
33
|
"warlock": {
|
|
34
34
|
"environment": "server"
|
|
35
35
|
},
|
|
36
|
-
"version": "5.34.
|
|
36
|
+
"version": "5.34.3",
|
|
37
37
|
"main": "./cjs/index.cjs",
|
|
38
38
|
"module": "./esm/index.mjs",
|
|
39
39
|
"types": "./esm/index.d.mts",
|
package/skills/index.md
CHANGED
|
@@ -1,14 +1,16 @@
|
|
|
1
1
|
---
|
|
2
|
-
description: 'Structured logging
|
|
2
|
+
description: 'Structured logging with channels, filtering, redaction and shutdown flushing: `log`, `Logger`, `LogChannel`, `ConsoleLog`, `JSONFileLog`, `SentryLog` and `applyRedact`. Use for "log an error with context", "hide tokens", "write JSON logs", "send errors to Sentry" or "flush before exit". Not this package → @warlock.js/core for HTTP error responses; @warlock.js/ai-panoptic for agent traces and AI cost dashboards.'
|
|
3
3
|
---
|
|
4
4
|
|
|
5
5
|
# @warlock.js/logger
|
|
6
6
|
|
|
7
7
|
The `log` singleton (a `Logger`) sends each entry, with module, action, level and message, to every configured `LogChannel`. Channels decide where entries go (console, file, JSON file, Sentry, custom) and can filter by level or module.
|
|
8
8
|
|
|
9
|
+
Works standalone in Node projects; core is not required. Levels are `debug`, `info`, `warn`, `error`, `success`, and `fatal` (above `error`).
|
|
10
|
+
|
|
9
11
|
## The 80% path
|
|
10
12
|
|
|
11
|
-
1.
|
|
13
|
+
1. Start with `logger-basics.md`.
|
|
12
14
|
2. Pick channels: `pick-log-channel.md`; set them up: `configure-logger.md`.
|
|
13
15
|
3. Log via `log.info/warn/error` or helpers: `use-log-helpers.md`.
|
|
14
16
|
4. Redact secrets: `redact-sensitive-log-fields.md`; narrow output: `filter-log-entries.md`.
|
|
@@ -21,3 +23,41 @@ The `log` singleton (a `Logger`) sends each entry, with module, action, level an
|
|
|
21
23
|
- File channels buffer, so an unflushed process exit loses the tail of the log.
|
|
22
24
|
- Configure redaction before logging request bodies or auth data; the default key set is a floor, not a guarantee.
|
|
23
25
|
- Log structured context objects rather than interpolated strings, so channels and Sentry can index them.
|
|
26
|
+
|
|
27
|
+
## Server-only package
|
|
28
|
+
|
|
29
|
+
`@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.
|
|
30
|
+
|
|
31
|
+
## When to reach for it
|
|
32
|
+
|
|
33
|
+
- 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.
|
|
34
|
+
- 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).
|
|
35
|
+
- 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.
|
|
36
|
+
|
|
37
|
+
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.
|
|
38
|
+
|
|
39
|
+
## The mental model in one paragraph
|
|
40
|
+
|
|
41
|
+
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.
|
|
42
|
+
|
|
43
|
+
## Built-in channels at a glance
|
|
44
|
+
|
|
45
|
+
| Channel | Sink | `terminal` | Buffered |
|
|
46
|
+
| ------------- | --------------------------- | ----------------------- | ----------------------------------------------------------------------- |
|
|
47
|
+
| `ConsoleLog` | `process.stdout` | `true` (colors kept) | no |
|
|
48
|
+
| `FileLog` | `.log` files | `false` (ANSI stripped) | yes (5s timer or 100-entry buffer) |
|
|
49
|
+
| `JSONFileLog` | `.json` files | `false` (ANSI stripped) | yes (same buffering) |
|
|
50
|
+
| `SentryLog` | Sentry events + breadcrumbs | `false` | via the Sentry SDK's own transport (`@sentry/node` is an optional peer) |
|
|
51
|
+
|
|
52
|
+
`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.
|
|
53
|
+
|
|
54
|
+
## What this package deliberately doesn't do
|
|
55
|
+
|
|
56
|
+
- **Distributed tracing.** Use OpenTelemetry. Logger gets you structured local logs with `module / action`; trace correlation is a different problem.
|
|
57
|
+
- **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).
|
|
58
|
+
- **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.
|
|
59
|
+
- **Log analysis.** Querying / aggregating / alerting is on the sink side (Loki, ELK, Datadog).
|
|
60
|
+
|
|
61
|
+
## See also
|
|
62
|
+
|
|
63
|
+
- The `warlock-conventions` topic of the `warlock-js-core` skill — the parent framework's conventions; logger is one of its foundation packages and ships transitively when you install core.
|
package/skills/overview/SKILL.md
DELETED
|
@@ -1,105 +0,0 @@
|
|
|
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
|
-
`@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`
|
|
35
|
-
|
|
36
|
-
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.
|
|
37
|
-
|
|
38
|
-
#### `configure-logger`
|
|
39
|
-
|
|
40
|
-
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.
|
|
41
|
-
|
|
42
|
-
### Channels
|
|
43
|
-
|
|
44
|
-
#### `pick-log-channel`
|
|
45
|
-
|
|
46
|
-
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).
|
|
47
|
-
|
|
48
|
-
#### `ship-logs-to-sentry`
|
|
49
|
-
|
|
50
|
-
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.
|
|
51
|
-
|
|
52
|
-
#### `write-custom-log-channel`
|
|
53
|
-
|
|
54
|
-
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.
|
|
55
|
-
|
|
56
|
-
### Production concerns
|
|
57
|
-
|
|
58
|
-
#### `redact-sensitive-log-fields`
|
|
59
|
-
|
|
60
|
-
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`.
|
|
61
|
-
|
|
62
|
-
#### `filter-log-entries`
|
|
63
|
-
|
|
64
|
-
Drop entries before they cost anything. Logger-wide `setMinLevel("info")` is the fast path; per-channel `levels` array + `filter` predicate for fine control.
|
|
65
|
-
|
|
66
|
-
#### `flush-logs-on-shutdown`
|
|
67
|
-
|
|
68
|
-
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.
|
|
69
|
-
|
|
70
|
-
#### `capture-unhandled-errors`
|
|
71
|
-
|
|
72
|
-
`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.
|
|
73
|
-
|
|
74
|
-
### Ergonomics + testing
|
|
75
|
-
|
|
76
|
-
#### `use-log-helpers`
|
|
77
|
-
|
|
78
|
-
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`.
|
|
79
|
-
|
|
80
|
-
#### `test-logging-code`
|
|
81
|
-
|
|
82
|
-
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`.
|
|
83
|
-
|
|
84
|
-
## Built-in channels at a glance
|
|
85
|
-
|
|
86
|
-
| Channel | Sink | `terminal` | Buffered |
|
|
87
|
-
| ------------- | --------------------------- | ----------------------- | ----------------------------------------------------------------------- |
|
|
88
|
-
| `ConsoleLog` | `process.stdout` | `true` (colors kept) | no |
|
|
89
|
-
| `FileLog` | `.log` files | `false` (ANSI stripped) | yes (5s timer or 100-entry buffer) |
|
|
90
|
-
| `JSONFileLog` | `.json` files | `false` (ANSI stripped) | yes (same buffering) |
|
|
91
|
-
| `SentryLog` | Sentry events + breadcrumbs | `false` | via the Sentry SDK's own transport (`@sentry/node` is an optional peer) |
|
|
92
|
-
|
|
93
|
-
`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.
|
|
94
|
-
|
|
95
|
-
## What this package deliberately doesn't do
|
|
96
|
-
|
|
97
|
-
- **Distributed tracing.** Use OpenTelemetry. Logger gets you structured local logs with `module / action`; trace correlation is a different problem.
|
|
98
|
-
- **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).
|
|
99
|
-
- **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.
|
|
100
|
-
- **Log analysis.** Querying / aggregating / alerting is on the sink side (Loki, ELK, Datadog).
|
|
101
|
-
|
|
102
|
-
## See also
|
|
103
|
-
|
|
104
|
-
- The `warlock-conventions` topic of the `warlock-js-core` skill — the parent framework's conventions; logger is one of its foundation packages and ships transitively when you install core.
|
|
105
|
-
- When synced via agent-kit, this `overview` topic is flattened to the front-door skill `.claude/skills/warlock-js-logger-overview/` — every cross-link above names the topic (and the owning skill, across packages) instead of a file path so it survives that flattening.
|