Distributed Tracing

Praxis can export HTTP spans to an OTLP collector when built with the otel feature.

Versions marked “overview” do not contain this page. Selecting one opens that version’s documentation overview.

On this page

Praxis can export HTTP spans to an OTLP collector when built with the otel feature. Without that feature, Praxis does not create OTel spans and does not require a collector. The trace_context filter remains usable as a header-only propagation option.

Configure OTLP export

Set the top-level telemetry section and build with otel:

telemetry:
  otlp_endpoint: "http://otel-collector:4317"
  sampling_rate: 0.1
  service_name: "praxis-edge"

Supply exporter credentials through OTEL_EXPORTER_OTLP_HEADERS, populated from a deployment-managed Secret or equivalent environment reference. Praxis also accepts telemetry.otlp_headers for deployments that manage the config outside overlays; avoid putting secret values in checked-in YAML or routing overlays. Exporter headers are sent only to the collector and are never copied to proxy requests, traces, or logs. Sampling is parent based: a sampled inbound parent is continued, an unsampled parent stays unsampled, and new roots use sampling_rate (or the configured default sampler). sampling_rate is not a hard export-volume limit: a valid sampled remote parent is honored even when the local root sampling rate is zero. On listeners that accept trace context from untrusted callers, those callers can increase tracing work and collector ingest by sending sampled contexts. Apply ingress controls where a hard telemetry-volume limit is required; a listener-level trace-context trust policy is outside this configuration.

The application must initialize tracing from the loaded configuration and retain the returned TracingGuard for the server lifetime. The guard shuts down the OTLP provider and flushes pending spans during normal shutdown.

Context propagation

With otel enabled and an exporter configured, Praxis validates a single inbound W3C traceparent before creating the HTTP server span. A valid remote context becomes that server span’s parent, including its sampled flag and validated tracestate. Missing, malformed, or duplicate traceparent fields start a local root; malformed trace state is discarded.

Praxis creates an HTTP CLIENT span for each actual upstream request attempt. The final outbound header hook injects that span’s context after filter header mutations, replacing conflicting client-supplied or filter-generated trace headers. The next Praxis gateway therefore receives the exported client span as its remote parent. Retries and requests sent over pooled connections each create a fresh client span and inject that attempt’s ID.

Framework sub-requests use the same client span and injection path. When OTel is unavailable, the trace_context filter keeps its existing header-only behavior, including its synthetic hop ID and x-request-id correlation.

Exported spans and data

HTTP request handling emits SERVER spans. Each proxied HTTP attempt and framework sub-request emits a CLIENT span; the existing upstream_exchange span remains an INTERNAL child for connection and response details. Filter execution emits bounded filter spans when debug level tracing is enabled. The iterative-request-router build feature emits filtered_subrequest step spans beneath the active request/filter context, with step name and iteration. Those routing spans contain no prompt or body data.

Server spans also record the request URL path and User-Agent. The optional request_id filter can record a validated client-supplied request ID. These values can contain sensitive or identifying information even though Praxis does not record request or response bodies, Authorization or cookie headers, or exporter credentials. Avoid placing secrets or personal data in URL paths, User-Agent values, or request IDs, and control access and retention for the collector accordingly. Redacting or hashing these fields would change existing trace attributes and should be considered as a separate design decision.

Sampling may omit spans; a propagated unsampled context intentionally has no exported span IDs to inspect.

Verification

This command runs two local Praxis proxies through an OTLP test receiver:

cargo test --all-features -p praxis-tests-integration --test otel_cross_gateway -- --nocapture

The test parses the exported IDs and checks the chain edge SERVER -> edge CLIENT -> provider SERVER -> provider CLIENT, checks the backend’s received client span ID, exercises missing/malformed/unsampled contexts, and scans exported span data for body and credential sentinels.