Skip to content
Judgment Labs
Esc
navigateopen⌘Jpreview
On this page

Trace distributed agents

Keep one trace connected as a request crosses services.

Distributed tracing keeps one live request in a single Judgment trace as it crosses services. It does this by propagating OpenTelemetry context between them. Use Durable work instead when a queue, retry, or human approval wait resumes the work later.

Use the same Judgment project across all services. Give each service a distinct service.name resource attribute when you initialize its tracer, not on individual spans. One trace then shows every hop while the services stay distinguishable.

Set the session ID on the upstream root before injecting the outgoing headers. The SDK includes that ID in Judgment baggage. Tracer.continue_trace() and Tracer.continueTrace() restore the baggage with the trace context, so downstream child spans inherit the same session ID automatically.

Prerequisites

  • Configure the same Judgment organization and project in every participating service. See Project routing.
  • Give each service a distinct service.name resource attribute.
  • Initialize the tracer before any service creates spans.
  • Identify the upstream root and the outgoing and incoming request boundaries.

Send trace state

Inject the current trace context into the outgoing request:

from judgeval.trace.propagation import inject

headers = {}
inject(headers)
response = httpx.post(DOWNSTREAM_URL, json=payload, headers=headers)
import { propagation } from "judgeval";

const headers: Record<string, string> = {};
propagation.inject(headers);
await fetch(DOWNSTREAM_URL, { method: "POST", headers, body });

Receive trace state

Continue the trace before starting child spans. A span that starts first gets a new trace ID, which makes the request appear as two unrelated traces. Middleware is a reliable place to continue the trace because it covers every incoming request:

with Tracer.continue_trace(request.headers):
    result = handle_request(payload)
await Tracer.continueTrace(req.headers, async () => {
    return handleRequest(payload);
});

Delayed queue work needs a stable job or session ID instead of the live request’s trace context. Start a new trace when the worker receives the task. See Durable work.

Verify

First run the shared verification checklist. Then send one request through both services. The downstream spans should appear under the upstream root in one trace. Two traces mean the context did not arrive or was restored after a downstream span had already started.

Troubleshooting

Symptom Check
Two traces appear for one request Continue the incoming context before starting any downstream span.
Downstream spans are missing Confirm the carrier headers reached the service and its tracer exports to the same project.
Downstream session is missing Set the session ID before injecting upstream context and use continue_trace or continueTrace to restore baggage.
Queue work extends the live trace Start a new trace when the queued job begins and reuse only the stable session ID.

Was this page helpful?