Community TypeScript Snippet
A Request/Response Logger That Does Not Leak Secrets
The redact-by-key logger I add to every Node service before it touches production logs. Catches headers, JWTs, card numbers, and Stripe keys without paying for a SIEM scrubber.
A Request/Response Logger That Does Not Leak Secrets
The redact-by-key logger I add to every Node service before it touches production logs. Catches headers, JWTs, card numbers, and Stripe keys without paying for a SIEM scrubber.
By @nadiaali
March 6, 2026
·
Updated May 20, 2026
434 views
14
4.4 (15)
I have shipped this redactor in three companies because the failure mode of NOT having it is so loud: one accidental console.log(req.headers) and your bearer tokens land in CloudWatch forever. It does two passes per value. First, a key-name check against a denylist (authorization, password, token, common card-field names). Second, a regex sweep over string values for the patterns that look like secrets even when the key is benign, like a customer-supplied note containing sk_test_.... Keep both layers; key-only redaction misses the notes field every time.
This is the integration shape I use with Express, Fastify, and NestJS interceptors. The middleware logs ONE line per exchange (request + response + duration) so the structured-log query later is path=/login | err. Calling redact on both req.body and res.body means we are safe even when the response echoes the request (a real bug I found while writing this once). Inline timing keeps me honest about which endpoints are slowing down. The console.log(JSON.stringify(...)) is intentional, not lazy: matching pino or bunyan line-format means our log shipper does not need a parser.
Once /checkout hits 1k req/sec, logging every body costs more than the rest of the service combined. The fix is sampling: keep the line for every request (cheap), but only attach the redacted body 1-in-N times AND always on errors. The correlationId ties the sampled-body line back to the unsampled lines via your trace ID. We tuned SAMPLE_RATE per-route in production; checkout is 100, profile reads are 1000, internal health pings are 0 (just a counter). Reach for this the moment your log bill becomes a topic at standup.
