Motivation
Pekko Streams has no distributed tracing support. Users running Pekko in microservices cannot trace how elements flow through stream topologies. This is increasingly expected in production environments for observability.
Proposed design (based on Akka.NET reference implementation)
-
Trace context in Connection slots: Extend Connection in GraphInterpreter with a trace context field. Capture io.opentelemetry.context.Context.current() on element push.
-
Stage-scoped spans: In processPush, start an OpenTelemetry span scoped to the stage. Span name = stage operation (e.g., MapAsync, Filter).
-
Fan-in links: For fan-in stages (Batch, Merge, Concat, GroupedWeightedWithin), record ActivityLinks from all input connections.
-
Cross-async-boundary propagation: Extend OnNext events with trace context. BatchingActorInputBoundary buffers contexts in parallel arrays and restores them on dequeue.
-
Zero-cost when no listener: When no OpenTelemetry listener is registered, all tracing code should be a no-op with negligible overhead.
JVM-specific considerations
- Use
io.opentelemetry.api.trace.Tracer instead of .NET ActivitySource
- Use
io.opentelemetry.context.Context and ThreadLocal propagation
- Consider integration with Pekko's existing
ContextPropagation mechanism
- May need a new module/dependency for
opentelemetry-api
Scope
This is a large, cross-cutting feature (~2000+ lines across 20+ files). It should be broken into multiple PRs:
- Core trace context in GraphInterpreter Connection
- Stage-scoped spans in processPush
- Fan-in link support
- Cross-async-boundary propagation
- Source.Queue / Sink.Queue ingress/egress tracing
- Configuration and opt-in mechanism
References
Motivation
Pekko Streams has no distributed tracing support. Users running Pekko in microservices cannot trace how elements flow through stream topologies. This is increasingly expected in production environments for observability.
Proposed design (based on Akka.NET reference implementation)
Trace context in Connection slots: Extend
ConnectioninGraphInterpreterwith a trace context field. Captureio.opentelemetry.context.Context.current()on element push.Stage-scoped spans: In
processPush, start an OpenTelemetry span scoped to the stage. Span name = stage operation (e.g.,MapAsync,Filter).Fan-in links: For fan-in stages (Batch, Merge, Concat, GroupedWeightedWithin), record
ActivityLinks from all input connections.Cross-async-boundary propagation: Extend
OnNextevents with trace context.BatchingActorInputBoundarybuffers contexts in parallel arrays and restores them on dequeue.Zero-cost when no listener: When no OpenTelemetry listener is registered, all tracing code should be a no-op with negligible overhead.
JVM-specific considerations
io.opentelemetry.api.trace.Tracerinstead of .NETActivitySourceio.opentelemetry.context.ContextandThreadLocalpropagationContextPropagationmechanismopentelemetry-apiScope
This is a large, cross-cutting feature (~2000+ lines across 20+ files). It should be broken into multiple PRs:
References