Structured logging
Think of it like… a flight recorder that automatically bleeps out anything that sounds like a password before it writes to the tape. You get a faithful record of what happened, minus the secrets you never wanted on disk.
DaloyJS ships a tiny, zero-dependency structured logger. Every app gets one by default at app.log, and every request handler gets a request-scoped child logger at ctx.state.log that is already bound to the request id. Records are emitted as single-line JSON so they drop straight into Loki, Datadog, CloudWatch, or any log aggregator.
The headline feature is secure-by-default redaction: common credential keys (authorization, cookie, password, token, provider API keys, and more) are replaced with [REDACTED] at any depth, and string values shaped like a JWT or an opaque provider token are scrubbed even when they appear under an innocent key.
The default logger
You do not need to wire anything up. Construct an App and a createLogger({ level: "info" }) instance is attached automatically.
The app-level logger is also available directly when you are outside a request (startup, scheduled jobs, shutdown):
Choosing a level
Levels follow the familiar pino ordering: trace (10), debug (20), info (30), warn (40), error (50), fatal (60). Records below the configured level are dropped. Set the level when you construct the app:
Calling the logger
Every level method accepts either a message string, or an object of structured fields followed by an optional message. Prefer the object form so your fields stay queryable.
Child loggers
Use child() to bind fields that should appear on every subsequent record. This is how the request id is attached to ctx.state.log, and it is the right tool for per-component or per-job context.
Redaction (secure by default)
Redaction is on by default. Keys are matched case-insensitively at any depth and replaced with [REDACTED]. The built-in DEFAULT_REDACT_KEYS list covers the usual suspects plus AI / LLM provider credential headers. In addition, any string value shaped like a JWT (eyJ…) or an opaque provider token (GitHub ghp_…, AWS AKIA…, Stripe sk_live_…, OpenAI sk-…, and more) is scrubbed regardless of its key.
Extend the defaults with your own keys, change the replacement string, or opt out entirely:
Do not turn redaction off in production. The default list exists because these exact keys are the ones most commonly observed leaking secrets into log aggregators in real-world incidents.
Bring your own logger (pino, winston)
The logger option accepts any object implementing the Logger interface (the trace/debug/info/warn/error/fatal methods plus child()). pino already matches this shape, so you can pass it directly:
When you bring your own logger, redaction becomes that logger's responsibility, so configure pino's redact option to match the protection DaloyJS gives you for free.
Customizing the output sink
By default records are written to stdout. Provide a write function to redirect them (for example, to a buffer in tests):
When to reach for it
- Inside handlers: use
ctx.state.logso every line is correlated to the request id automatically. - Background work: derive a
app.log.child({ component })so jobs are easy to filter. - Security-sensitive payloads: rely on the default redaction rather than hand-stripping fields before you log them.