Debug Tracing
Problem
Section titled “Problem”A request returns an unexpected status code and you need to identify which middleware in the pipeline rejected it.
Solution
Section titled “Solution”Enable debug mode to populate a _trace object on error responses.
The trace shows which middleware ran and which one triggered the
pipeline break.
import http from "node:http";import { handler, compose, logger, authorization, body,} from "@centralping/ergo";
const pipeline = compose( {fn: logger(), setPath: "log"}, {fn: authorization({ strategies }), setPath: "auth"}, {fn: body(), setPath: "body"}, (req, res, acc) => ({ response: { body: acc.body.parsed, statusCode: 200 }, }),);
const server = http.createServer( handler(pipeline, { debug: true }),);server.listen(3000);import createRouter, { graceful } from "@centralping/ergo-router";
const router = createRouter({ debug: true });
router.post("/items", { validate: { body: itemSchema }, execute: async (req, res, acc) => { const item = await db.create(acc.body.parsed); return { response: { statusCode: 201, body: item } }; },});
await graceful(router.handle(), { port: 3000 });When a request is rejected (e.g. missing authorization), the error
response body includes a _trace field:
{ "type": "https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/401", "title": "Unauthorized", "status": 401, "_trace": { "steps": ["log", "auth"], "breakAt": "auth" }}Explanation
Section titled “Explanation”The _trace Shape
Section titled “The _trace Shape”When debug is true, the pipeline initializes
a trace object on the response accumulator before running middleware:
responseAcc._trace = { steps: [], breakAt: undefined };As each middleware executes, its label (the setPath from the
{fn, setPath} config object, or the function name) is pushed onto steps.
When a middleware sets responseAcc.statusCode and triggers a pipeline
break, breakAt records that middleware’s label.
| Field | Type | Description |
|---|---|---|
steps | string[] | Middleware labels in execution order |
breakAt | string | undefined | Label of the middleware that caused the pipeline break |
RFC 9457 Extension Member
Section titled “RFC 9457 Extension Member”_trace is not in send()’s reserved key set. On error responses
(statusCode ≥ 400), it appears as an RFC 9457 extension member in the
response body alongside type, title, status, and detail.
On success responses (2xx), _trace is not included in the body.
Zero Overhead When Disabled
Section titled “Zero Overhead When Disabled”When debug is false (the default), no trace object is created and
the pipeline skips all label-tracking logic. There is no performance
cost in production.
Distinct from OpenTelemetry Tracing
Section titled “Distinct from OpenTelemetry Tracing”Pipeline debug tracing (_trace) is unrelated to the
OpenTelemetry tracing middleware. OTel tracing
populates domainAcc.trace with distributed trace context (trace ID,
span ID) for production observability. Debug tracing populates
responseAcc._trace with pipeline execution details for local
debugging. Both can be active simultaneously without conflict.