Field guide · operations
Structured logging without runtime dependencies?
Short answer
JSON calls alone are insufficient. Define five contracts: a stable NDJSON envelope, context through child loggers, redaction before delivery, a bounded backpressure policy, and root logger closure after inbound traffic stops. swm-logs 0.1.1 implements formatting and lifecycle while leaving storage, retries, and network delivery to the application.
- NDJSON
- format
- 5
- explicit contracts
- 64 KiB
- buffer default
Scope
This guidance targets native ESM on Node.js 22/24. “Zero dependencies” describes the package runtime surface; an external log collection system is still required.
Minimum structured logging contract
| Concern | Contract | Verification |
|---|---|---|
| Event | One JSON record per line; level, time, and msg keep stable types. | A line reader parses the stream without multiline exceptions. |
| Context | A child logger inherits service and request bindings without mutating its parent. | requestId appears on every request event and never leaks to another request. |
| Secrets | Exact and wildcard paths are redacted before the formatter and transport. | Tests cover tokens, passwords, and dynamic headers or cookies. |
| Backpressure | Immediate mode is valid only without a strict limit; otherwise use a bounded transport. | max queue, overflow policy, and the loss counter are explicit. |
| Shutdown | Stop new requests, drain work, then await rootLogger.close(). | An integration test proves that the final event is delivered. |
| Crash path | fatal + flushSync(); durable guarantees require a file descriptor or a transport guarantee. | A separate-process test exits abnormally and checks the final record. |
Minimum production setup
- 01
A platform agent collects stdout
Start with immediate outputBut measure the Node.js stream queue and do not describe it as bounded.
- 02
Loss must be bounded and measurable
Implement a bounded transportFix queue size, overflow policy, retries, and an independent loss metric.
- 03
Durable local writes are required
Use a file descriptorVerify flushSync() and own rotation and retention separately.
Solution boundaries
- A logger does not replace a collector, storage, retention, or alerting.
- Pretty output belongs in the development pipeline and must not change the production envelope.
- A field list alone cannot protect secrets; schemas and redaction tests must evolve together.
Verifiable contract
The page is based on the published package and its fixed README snapshot.