Interface Span
A Span is an abstraction that lets Axon Framework offer tracing capabilities without depending on a
specific tracing provider. A span is opened by calling start() and ended by closing the returned
SpanScope; closing is idempotent (see SpanScope.close()).
Parent/child relationships are explicit. Every span is created with a
ProcessingContext passed to the SpanFactory factory
method, and its parent is resolved at creation time from that context's active SpanScope (see
SpanScope.RESOURCE_KEY). Parenting across asynchronous or process boundaries instead rides on message
metadata via propagateContext(Message) on the dispatch side and
SpanFactory.createHandlerSpan(String, Message, org.axonframework.messaging.core.unitofwork.ProcessingContext)
on the handling side.
Two flavors of span cover every use in the framework, distinguished by how they interact with a context's active scope:
- Branch-scoped -- the span covers one sub-operation of a context (a per-event handler span, dispatch,
or repository operation). Use
branch(ProcessingContext, Function),branchAsync(ProcessingContext, Function)orbranchStream(ProcessingContext, Function). Each starts the span, hands the operation a context branch carrying the scope (viaSpanScope.addToContext(ProcessingContext, SpanScope)) so the operation's own children parent under it, executes the operation within the scope (viaSpanScope.within(Supplier)), and closes the scope when the operation's own result terminates -- never when the enclosing context completes. - Lifecycle-covering -- the span is the context's dominant operation for its entire lifetime (a
streaming-processor batch span or a per-command/query handler span). Use
coverLifecycle(ProcessingContext): it records this span's scope on the context's root (last-writer-wins) and closes it when the context completes.
- Since:
- 4.6.0
- Author:
- Mateusz Nowak, Mitchell Herrijgers
- See Also:
-
Method Summary
Modifier and TypeMethodDescriptionaddAttribute(String key, String value) Adds an attribute to the span, providing extra information to the APM tooling.default <T> Tbranch(@Nullable ProcessingContext context, Function<@Nullable ProcessingContext, T> operation) Runs the given value-producing operation as a branch-scoped span: starts this span, hands the operation a context branch carrying this span's scope (viaSpanScope.addToContext(ProcessingContext, SpanScope)) so the operation's own children parent under this span, executes the operation within the scope (viaSpanScope.within(Supplier)), and ends the span when the operation returns -- deterministically, on both the value and the throw path.default <T> CompletableFuture<T> branchAsync(@Nullable ProcessingContext context, Function<@Nullable ProcessingContext, CompletableFuture<T>> operation) Runs the given asynchronous operation as a branch-scoped span: starts this span, hands the operation a context branch carrying this span's scope (viaSpanScope.addToContext(ProcessingContext, SpanScope)) so the operation's own children -- including those created in asynchronous continuations -- parent under this span, executes the operation's synchronous frame within the scope (viaSpanScope.within(Supplier)), and ends the span when the returnedCompletableFuturecompletes (normally or exceptionally).default <M extends Message>
MessageStream<M> branchStream(@Nullable ProcessingContext context, Function<@Nullable ProcessingContext, MessageStream<M>> operation) Runs the givenMessageStream-producing operation as a branch-scoped span: starts this span, hands the operation a context branch carrying this span's scope (viaSpanScope.addToContext(ProcessingContext, SpanScope)) so the operation's own children -- including those created in asynchronous continuations -- parent under this span, executes the operation's synchronous frame within the scope (viaSpanScope.within(Supplier)), and ends the span when the returned stream terminates: on normal completion, or on an error (recorded on the span first).default SpanScopecoverLifecycle(ProcessingContext context) Starts this span to cover the givenProcessingContext's lifecycle: starts the span, records itsSpanScopeon the context's root underSpanScope.RESOURCE_KEY(last-writer-wins -- matchingMessage.RESOURCE_KEY), records any processing error on the span, and closes the scope when the context completes (on both the success and error paths).<M extends Message>
MpropagateContext(M message) Returns a copy of the givenmessagewith this span's tracing context injected into its metadata, so a remote or asynchronous handler can continue the same trace by extracting it (seeSpanFactory.createHandlerSpan(String, Message, org.axonframework.messaging.core.unitofwork.ProcessingContext)).Records the given exception against the span and marks the span as errored.start()Starts this span and returns itsSpanScope, without writing anything to aProcessingContext.
-
Method Details
-
start
SpanScope start()Starts this span and returns itsSpanScope, without writing anything to aProcessingContext. This is the imperative edge underlying the branch-scoped flavor: the caller is responsible for making the returned scope reachable to the sub-operation it covers -- typically viaSpanScope.addToContext(ProcessingContext, SpanScope)-- and for closing it explicitly when that sub-operation's own result terminates. Closing is idempotent; seeSpanScope.close().- Returns:
- the
SpanScopegoverning this span; nevernull
-
coverLifecycle
Starts this span to cover the givenProcessingContext's lifecycle: starts the span, records itsSpanScopeon the context's root underSpanScope.RESOURCE_KEY(last-writer-wins -- matchingMessage.RESOURCE_KEY), records any processing error on the span, and closes the scope when the context completes (on both the success and error paths). This is the context-lifetime counterpart tostart(): use it only when this span is the context's dominant operation for its entire lifetime (a batch span; a per-command/query handler span) -- never for a span that covers just one sub-operation of a longer-lived context, which would silently steal every other sub-operation's parent for the rest of the context's lifetime.- Parameters:
context- the processing context whose lifecycle the span is bound to- Returns:
- the started
SpanScope, also recorded oncontextunderSpanScope.RESOURCE_KEY - Since:
- 5.3.0
- Implementation Requirements:
- This method is a fixed composition of
start()andrecordException(Throwable): the framework relies on the scope being recorded on the context's root and closed exactly once when the context completes, on both the success and error paths. Redefining it changes those guarantees for every framework call site at once; provider-specific scope execution belongs inSpanScope.within(Supplier)instead.
-
addAttribute
Adds an attribute to the span, providing extra information to the APM tooling. Implementations returnthisfor fluent chaining.- Parameters:
key- the attribute keyvalue- the attribute value- Returns:
- this span, for fluent interfacing
-
recordException
Records the given exception against the span and marks the span as errored. This does NOT end the span; the span is ended when itsSpanScopeis closed.- Parameters:
t- the exception to record- Returns:
- this span, for fluent interfacing
-
propagateContext
Returns a copy of the givenmessagewith this span's tracing context injected into its metadata, so a remote or asynchronous handler can continue the same trace by extracting it (seeSpanFactory.createHandlerSpan(String, Message, org.axonframework.messaging.core.unitofwork.ProcessingContext)). The span propagates itself, without consulting ambient state. Implementations that perform no propagation (no-op, logging) return the input unchanged and never throw.- Type Parameters:
M- the message type- Parameters:
message- the message to enrich with this span's tracing context- Returns:
- the message carrying this span's propagated tracing context (possibly the same instance)
-
branch
default <T> T branch(@Nullable ProcessingContext context, Function<@Nullable ProcessingContext, T> operation) Runs the given value-producing operation as a branch-scoped span: starts this span, hands the operation a context branch carrying this span's scope (viaSpanScope.addToContext(ProcessingContext, SpanScope)) so the operation's own children parent under this span, executes the operation within the scope (viaSpanScope.within(Supplier)), and ends the span when the operation returns -- deterministically, on both the value and the throw path. Exceptions are recorded on the span and rethrown. Whencontextisnull, the operation receivesnulland no branch is created.- Type Parameters:
T- the produced value type- Parameters:
context- the processing context to branch for the operation, ornullwhen none is availableoperation- the value-producing block to run, receiving the branched context (ornull)- Returns:
- the value produced by
operation - Since:
- 5.3.0
- Implementation Requirements:
- This method is a fixed composition of
start(),recordException(Throwable), andSpanScope.within(Supplier): the framework relies on the span ending exactly once, on both the value and the throw path, with failures recorded before the scope closes. Redefining it changes those guarantees for every framework call site at once; provider-specific behavior belongs inSpanScope.within(Supplier), the extension point this composition already calls.
-
branchAsync
default <T> CompletableFuture<T> branchAsync(@Nullable ProcessingContext context, Function<@Nullable ProcessingContext, CompletableFuture<T>> operation) Runs the given asynchronous operation as a branch-scoped span: starts this span, hands the operation a context branch carrying this span's scope (viaSpanScope.addToContext(ProcessingContext, SpanScope)) so the operation's own children -- including those created in asynchronous continuations -- parent under this span, executes the operation's synchronous frame within the scope (viaSpanScope.within(Supplier)), and ends the span when the returnedCompletableFuturecompletes (normally or exceptionally). A failure of the future is recorded on the span; a synchronous failure of the operation itself is recorded, the span ended, and the throwable rethrown.When
contextis non-null, a close-only leak backstop is also registered viaProcessingLifecycle.doFinally(java.util.function.Consumer<org.axonframework.messaging.core.unitofwork.ProcessingContext>): should the framework abandon the returned future's completion path, the span still ends when the context does. Closing is idempotent, so the backstop and the primary close may overlap safely.- Type Parameters:
T- the future's result type- Parameters:
context- the processing context to branch for the operation, ornullwhen none is availableoperation- the block producing theCompletableFutureto trace, receiving the branched context (ornull)- Returns:
- a future that completes with the same result/exception as the operation's future
- Since:
- 5.3.0
- Implementation Requirements:
- This method is a fixed composition of
start(),recordException(Throwable), andSpanScope.within(Supplier): the framework relies on the span ending exactly once when the returned future completes -- with the context-completion backstop as the only other closer -- and on failures being recorded before the scope closes. Redefining it changes those guarantees for every framework call site at once; provider-specific behavior belongs inSpanScope.within(Supplier), the extension point this composition already calls.
-
branchStream
default <M extends Message> MessageStream<M> branchStream(@Nullable ProcessingContext context, Function<@Nullable ProcessingContext, MessageStream<M>> operation) Runs the givenMessageStream-producing operation as a branch-scoped span: starts this span, hands the operation a context branch carrying this span's scope (viaSpanScope.addToContext(ProcessingContext, SpanScope)) so the operation's own children -- including those created in asynchronous continuations -- parent under this span, executes the operation's synchronous frame within the scope (viaSpanScope.within(Supplier)), and ends the span when the returned stream terminates: on normal completion, or on an error (recorded on the span first). A synchronous failure of the operation itself is recorded, the span ended, and the throwable rethrown.When
contextis non-null, a close-only leak backstop is also registered viaProcessingLifecycle.doFinally(java.util.function.Consumer<org.axonframework.messaging.core.unitofwork.ProcessingContext>): should the framework abandon the returned stream before it terminates, the span still ends when the context does. Closing is idempotent, so the backstop and the primary close may overlap safely.- Type Parameters:
M- the type ofMessagecarried by the stream- Parameters:
context- the processing context to branch for the operation, ornullwhen none is availableoperation- the block producing theMessageStreamto trace, receiving the branched context (ornull)- Returns:
- a stream completing with the same entries/error as the operation's stream, ending this span on termination
- Since:
- 5.3.0
- Implementation Requirements:
- This method is a fixed composition of
start(),recordException(Throwable), andSpanScope.within(Supplier): the framework relies on the span ending exactly once when the returned stream terminates -- with the context-completion backstop as the only other closer -- and on failures being recorded before the scope closes. Redefining it changes those guarantees for every framework call site at once; provider-specific behavior belongs inSpanScope.within(Supplier), the extension point this composition already calls.
-