Interface Span


public interface Span
Represents one unit of traced work. One or more spans together form a trace, used to monitor and debug (distributed) applications.

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:

Since:
4.6.0
Author:
Mateusz Nowak, Mitchell Herrijgers
See Also:
  • Method Details

    • start

      SpanScope start()
      Starts this span and returns its SpanScope, without writing anything to a ProcessingContext. 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 via SpanScope.addToContext(ProcessingContext, SpanScope) -- and for closing it explicitly when that sub-operation's own result terminates. Closing is idempotent; see SpanScope.close().
      Returns:
      the SpanScope governing this span; never null
    • coverLifecycle

      default SpanScope coverLifecycle(ProcessingContext context)
      Starts this span to cover the given ProcessingContext's lifecycle: starts the span, records its SpanScope on the context's root under SpanScope.RESOURCE_KEY (last-writer-wins -- matching Message.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 to start(): 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 on context under SpanScope.RESOURCE_KEY
      Since:
      5.3.0
      Implementation Requirements:
      This method is a fixed composition of start() and recordException(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 in SpanScope.within(Supplier) instead.
    • addAttribute

      Span addAttribute(String key, String value)
      Adds an attribute to the span, providing extra information to the APM tooling. Implementations return this for fluent chaining.
      Parameters:
      key - the attribute key
      value - the attribute value
      Returns:
      this span, for fluent interfacing
    • recordException

      Span recordException(Throwable t)
      Records the given exception against the span and marks the span as errored. This does NOT end the span; the span is ended when its SpanScope is closed.
      Parameters:
      t - the exception to record
      Returns:
      this span, for fluent interfacing
    • propagateContext

      <M extends Message> M propagateContext(M message)
      Returns a copy of the given message with this span's tracing context injected into its metadata, so a remote or asynchronous handler can continue the same trace by extracting it (see SpanFactory.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 (via SpanScope.addToContext(ProcessingContext, SpanScope)) so the operation's own children parent under this span, executes the operation within the scope (via SpanScope.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. When context is null, the operation receives null and no branch is created.
      Type Parameters:
      T - the produced value type
      Parameters:
      context - the processing context to branch for the operation, or null when none is available
      operation - the value-producing block to run, receiving the branched context (or null)
      Returns:
      the value produced by operation
      Since:
      5.3.0
      Implementation Requirements:
      This method is a fixed composition of start(), recordException(Throwable), and SpanScope.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 in SpanScope.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 (via SpanScope.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 (via SpanScope.within(Supplier)), and ends the span when the returned CompletableFuture completes (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 context is non-null, a close-only leak backstop is also registered via ProcessingLifecycle.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, or null when none is available
      operation - the block producing the CompletableFuture to trace, receiving the branched context (or null)
      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), and SpanScope.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 in SpanScope.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 given MessageStream-producing operation as a branch-scoped span: starts this span, hands the operation a context branch carrying this span's scope (via SpanScope.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 (via SpanScope.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 context is non-null, a close-only leak backstop is also registered via ProcessingLifecycle.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 of Message carried by the stream
      Parameters:
      context - the processing context to branch for the operation, or null when none is available
      operation - the block producing the MessageStream to trace, receiving the branched context (or null)
      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), and SpanScope.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 in SpanScope.within(Supplier), the extension point this composition already calls.