Akka.Streams Tracing
Phobos automatically propagates distributed trace context through Akka.Streams graphs, connecting actor message handlers, stream graph stages, and any user spans created inside stream lambdas into a single unbroken trace.
How It Works
Akka.NET 1.5.66+ ships a framework-owned "Akka.Streams" ActivitySource that emits per-stage spans for elements carrying a live parent ActivityContext. When an actor's Phobos-instrumented message handler offers an element to a stream while a trace span is active, that context flows with the element through every downstream stage.
Phobos registers this ActivitySource automatically when you call AddPhobosInstrumentation() on your TracerProviderBuilder. No additional configuration is required.
Spans are only emitted when an element carries a live parent context. If no trace is active at the point where an element enters the graph, no stream spans are emitted — there is zero overhead for untraced work.
Setup
Requirements
- Phobos >= 2.12.0
- Akka.NET >= 1.5.66
Enabling Akka.Streams Tracing (Default)
Akka.Streams tracing is on by default. If you already call AddPhobosInstrumentation(), no changes are needed:
services.AddOpenTelemetry()
.WithTracing(tracing => tracing
.AddPhobosInstrumentation() // registers "Akka.Streams" ActivitySource automatically
.AddOtlpExporter());
Disabling Akka.Streams Tracing
To opt out of stream-stage spans entirely, pass traceAkkaStreams: false:
services.AddOpenTelemetry()
.WithTracing(tracing => tracing
.AddPhobosInstrumentation(traceAkkaStreams: false)
.AddOtlpExporter());
When disabled, the "Akka.Streams" ActivitySource has no registered listener and the framework's HasListeners() guard short-circuits before any span is allocated — there is no performance cost.
Understanding the Trace Shape
A fully traced request flows from the HTTP layer through an actor and into a stream graph:
[http] POST /orders/{id} (ASP.NET HttpContext)
[actor] akka.msg.recv Order (Phobos actor span)
[stream] akka.stream.ingress QueueSource (element enters the graph)
[stream] akka.stream.stage SelectAsync (downstream processing stage)
[user] db.INSERT (SqlClient / HttpClient span)
Ingress spans (akka.stream.ingress) mark where an element carrying a live parent context enters the graph — typically a Source.Queue, Source.ActorRef, or similar external-producer source.
Stage spans (akka.stream.stage) are emitted for each processing stage the element flows through. Any span you create inside a stage lambda (e.g. an OpenTelemetry SqlClient or HttpClient instrumentation span) automatically parents to the stage span, which in turn parents to the ingress span, which parents to the actor's akka.msg.recv span — one unbroken trace, one trace ID.
Fan-In Stages and ActivityLinks
Fan-in stages — BatchWeighted, Merge, GroupedWithin, MergePreferred — merge elements from multiple upstream traces into a single downstream span. Because a span can have only one primary parent, the merged stage span picks the first contributing element's trace as its primary parent and attaches the remaining contributing traces as ActivityLinks.
In a trace viewer (Jaeger, Tempo, Aspire Dashboard), you can open the merged stage span's Links tab to jump to any of the contributing upstream traces.
Further Reading
For details on the underlying Akka.NET "Akka.Streams" ActivitySource — including which stages emit spans, how SlotContext carries ActivityContext per element, and the exact span naming conventions — see the Akka.NET documentation: