
Distributed tracing demo in Rust using OpenTelemetry, OTLP/HTTP, and Jaeger.
This project implements a minimal yet realistic distributed tracing example. A request begins in a simulated “frontend”, then calls a simulated “ads-service” while keeping the same trace and span context. This makes it possible to visualize the entire call chain inside Jaeger.
This example covers the fundamental components of the OpenTelemetry ecosystem:
We manually create spans using tracing + tracing-opentelemetry, allowing us to record events, attributes, and parent-child relationships.
We use OTLP in HTTP mode to send traces directly into Jaeger without the OpenTelemetry collector.
Endpoint used:
http://localhost:4318/v1/traces
This example demonstrates real distributed tracing by propagating context between separate “services”.
All span structure, attributes, and hierarchy can be inspected visually.
frontend (root span)
│
▼
ads-service (child span)
Each component creates its own spans, but they all belong to the same trace.
Using only Docker (no docker-compose):
docker run \
-e COLLECTOR_OTLP_ENABLED=true \
-p 16686:16686 \
-p 4317:4317 \
-p 4318:4318 \
jaegertracing/all-in-one:latest
Then open:
http://localhost:16686
cargo run
You should see logs such as:
frontend: received request
ads-service: processing ads data
If Jaeger is running, the spans will appear automatically.
In the UI, search for the service:
jaeger-http-demo
You’ll see:

OTLP is the native OpenTelemetry protocol.
This example uses the HTTP version, sending spans directly to Jaeger’s /v1/traces.
No Collector in the middle. Jaeger receives and displays traces.
let tracer = global::tracer("jaeger-http-demo");
let root_span = span!(Level::INFO, "frontend", user_id = 42);
prop.inject_context(&cx, &mut injector);
prop.extract(&extractor)
Because: