spawntrail
spawntrailContextual logging for Node via AsyncLocalStorage and Mapped Diagnostic Context (MDC). Attach request-scoped context to every log line, with any logger (winston, pino) and any framework (express, fastify, koa). Zero dependencies, TypeScript-first, ~6 KB.
# install
npm install spawntrailyarn add spawntrail# why it exists
I built this idea twice in 2021 and got it wrong both times: first with a process-wide singleton context that bled between concurrent requests, then with an AsyncLocalStorage rewrite that fixed isolation but froze the context at request start and stayed a proof of concept. spawntrail is the third version and the successor to both (express-session-logger is deprecated in its favor). It keeps context per request with AsyncLocalStorage and injects it at log time through each logger's own hook (a winston format, a pino mixin), so context added mid-request appears in later logs and the tool never gets locked to one logger or framework.
# features
One AsyncLocalStorage holds each request's context and propagates it across every async hop, isolated between concurrent requests: the 50-interleaved-scopes test the old singleton could never pass.
trail.winston() (a format) and trail.pino() (a mixin) stamp the live context onto each record as it is written, so a value you put() mid-request shows up in later lines.
put('user.id', 42), get, and del over a dependency-free dot-path API. Nested run() scopes inherit the parent context and do not leak their writes back up.
Any logger with a format, mixin, or .child hook; any framework via a plain run(bindings, fn). It feeds the logger you already have instead of replacing it.
# highlights
- AsyncLocalStorage core: per-request context isolated across every await, no singleton, no cls-hooked
- Mapped Diagnostic Context: put/get/del with dot-paths; nested scopes act as segments
- Injects at log time via the logger's own hook: winston() format, pino() mixin, bind() fallback for any .child() logger
- Logger- and framework-agnostic: winston or pino; express middleware plus a plain run() for fastify, koa, or workers
- Zero dependencies, TypeScript-first, ships ESM + CJS + types, ~6 KB
- Successor to express-session-logger and @one-broker-services/winston-session (2021)