Distributed Tracing
Correlate frontend errors with the backend requests that caused them. A browser client sends
a tracepath-trace-id header with each request; the backend puts that value on its server
span, and TracePath lands both sides on one distributed trace.
The browser half is not available yet. The TracePath browser SDK, which is what generates and injects the header, is not published. The backend half below works today and is worth adding now: it is a few lines in your own OpenTelemetry middleware, and it also lets any client you control — a mobile app, a CLI, another service — join a trace by sending the same header.
The contract
- The client generates a UUID for an outgoing HTTP request.
- It sends that UUID as a
tracepath-trace-idrequest header. - The backend reads the header and sets it on the active server span as the attribute
tracepath.distributed_trace_id. - TracePath uses that attribute as the row's distributed trace ID, in place of the one derived from the OTel trace ID, so both sides join.
- In the dashboard, View distributed trace on the endpoint's detail page shows the client-side event as a second node.
The value must be a bare UUID. The backend parses it, and anything that does not parse is
ignored silently, leaving the row on its OTel-derived trace ID. This matters when a header is
set twice: XMLHttpRequest.setRequestHeader combines duplicate header names into
"<uuid1>, <uuid2>", which is not a valid UUID, so the correlation is dropped rather than
guessed at.
Backend setup (works today)
Read the header, annotate the span, and — optionally — echo it back so a client can read the ID it was assigned after the request settles:
import { trace } from "@opentelemetry/api";
app.use((req, res, next) => {
const id = req.headers["tracepath-trace-id"];
if (id) {
trace.getActiveSpan()?.setAttribute("tracepath.distributed_trace_id", id);
res.setHeader("tracepath-trace-id", id);
}
next();
});Register it after your OpenTelemetry instrumentation, so there is an active server span to annotate. The same three lines translate directly to any language: read the header, call your tracer's set-attribute on the active span, echo the header on the response. See the OpenTelemetry guides for the exporter setup itself.
If the client is on a different origin, add tracepath-trace-id to
Access-Control-Expose-Headers so the echoed header is readable.
Planned browser behaviour
When the browser SDK ships, initialization will instrument window.fetch and
XMLHttpRequest and inject the header on same-origin requests only, so the ID never
leaks to a third-party service. Because Axios uses XMLHttpRequest in the browser, Axios
requests will be covered with no extra setup — adding a second interceptor that sets the same
header is what produces the doubled, unparseable value described above.
An exception captured while a request is still in flight will be tagged with that
request's ID automatically. Once the request settles the active ID is cleared, so an error
raised in a .then() handler or after an await is stored unlinked unless you read the
active ID synchronously, right after starting the request, and pass it with the capture. If
your backend echoes the header, reading it off the response after the await gives you the
same value.
Related
- OpenTelemetry: instrumenting the backend that closes this loop
- Initialization: the rest of the planned browser SDK surface