Every HTTP request now gets a requestId (inbound X-Request-Id header is
honored, otherwise a fresh UUID is minted) and the value is echoed on
the response. The server wraps the request in
\`requestContext.run({ requestId }, ...)\` — an AsyncLocalStorage scope —
so pino's \`mixin\` callback can read it on every log call without the
caller threading it through.
Net effect: \`logger.info({ ... }, \"db error\")\` from a loader, action,
or downstream lib now lands in JSON with a \`requestId\` field, making
cross-handler debugging trivial (\`grep requestId=abc-123\` returns the
full request trace).
Out of scope here:
- Planner gets the same treatment (separate, smaller PR after this lands).
- BRouter / Fedify outbound calls don't propagate the requestId yet —
those are HTTP boundaries where we'd add it as a header, but the
audit value was the in-process trace.
Tests:
- logger.server.test.ts (2 cases — als-bound info tags requestId; no
context = no tag).
Full repo: pnpm typecheck, pnpm lint, pnpm test all green.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
28 lines
992 B
TypeScript
28 lines
992 B
TypeScript
import pino from "pino";
|
|
import { AsyncLocalStorage } from "node:async_hooks";
|
|
|
|
/**
|
|
* Per-request context propagated through the async stack. The HTTP
|
|
* server wraps each request in `requestContext.run({ requestId }, ...)`
|
|
* so every downstream `logger.info(...)` automatically tags log lines
|
|
* with `requestId` — no need to thread it through every call site.
|
|
*/
|
|
export interface RequestContext {
|
|
requestId: string;
|
|
}
|
|
|
|
export const requestContext = new AsyncLocalStorage<RequestContext>();
|
|
|
|
export const logger = pino({
|
|
level: process.env.LOG_LEVEL ?? "info",
|
|
// Mixin runs on every log call and merges its return value into the
|
|
// emitted record. Reading from ALS here is what makes requestId
|
|
// automatic for every downstream logger.info/warn/error.
|
|
mixin: () => {
|
|
const ctx = requestContext.getStore();
|
|
return ctx ? { requestId: ctx.requestId } : {};
|
|
},
|
|
...(process.env.NODE_ENV !== "production"
|
|
? { transport: { target: "pino-pretty" } }
|
|
: {}),
|
|
});
|