Skip to content

[#3594] feature(messaging, extensions/spring): introduce AF5 tracing support#4713

Draft
MateuszNaKodach wants to merge 2 commits into
feat/3594-af5-revise-distributed-tracingfrom
3594-distributed-tracing
Draft

[#3594] feature(messaging, extensions/spring): introduce AF5 tracing support#4713
MateuszNaKodach wants to merge 2 commits into
feat/3594-af5-revise-distributed-tracingfrom
3594-distributed-tracing

Conversation

@MateuszNaKodach

@MateuszNaKodach MateuszNaKodach commented Jul 6, 2026

Copy link
Copy Markdown
Contributor

WORK IN PROGRESS, DO NOT REVIEW NOW!!!!

This PR assumes that following PR will be merged to main first:

Changes from AF4:

  • attributes naming
  • Operation span naming

What is not covered by this PR:

  • OTel Context propagation to instrumentation based on thread-local like JDBC, JPA, gRPC etc. It will be the subject of net PRs.

@MateuszNaKodach MateuszNaKodach self-assigned this Jul 6, 2026
@MateuszNaKodach MateuszNaKodach added the Priority 1: Must Highest priority. A release cannot be made if this issue isn’t resolved. label Jul 6, 2026
@MateuszNaKodach MateuszNaKodach added this to the Release 5.2.0 milestone Jul 6, 2026
@MateuszNaKodach MateuszNaKodach added the Type: Feature Use to signal an issue is completely new to the project. label Jul 6, 2026
@MateuszNaKodach MateuszNaKodach changed the base branch from main to feat/3594-af5-revise-distributed-tracing July 7, 2026 11:58
@MateuszNaKodach MateuszNaKodach force-pushed the 3594-distributed-tracing branch 4 times, most recently from b8a87b0 to aa8be01 Compare July 7, 2026 14:18
@MateuszNaKodach MateuszNaKodach force-pushed the 3594-distributed-tracing branch 7 times, most recently from 0c7b974 to 888a0c2 Compare July 7, 2026 15:37
@MateuszNaKodach MateuszNaKodach changed the title [#3594] feat: introduce AF5 distributed tracing support [#3594] feature(messaging): introduce AF5 tracing support Jul 7, 2026
@MateuszNaKodach MateuszNaKodach force-pushed the 3594-distributed-tracing branch from 888a0c2 to f7a94d4 Compare July 7, 2026 15:39
Comment on lines +113 to +126
public MessageStream.Empty<Message> handle(EventMessage event, ProcessingContext context) {
// Open the batch span once per batch, on the first event -- unless disabled by configuration. Note: the span
// is started (which mutates the context via putResource) OUTSIDE a computeResourceIfAbsent supplier --
// mutating a ProcessingContext from within such a supplier is rejected as a re-entrant ("recursive") update.
if (!disableBatchTrace && context.getResource(BATCH_SPAN_KEY) == null) {
context.putResource(BATCH_SPAN_KEY, openBatchSpan(context));
}
String spanName = PROCESS_SPAN + " " + event.type().qualifiedName().name();
Span handlerSpan = continuesPublisherTrace(event)
? spanFactory.createHandlerSpan(spanName, event, context)
: spanFactory.createDisconnectedHandlerSpan(spanName, event, context);
handlerSpan.start(context);
return delegate.handle(event, context);
}

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

TODO:
investigate that and how it works.

* Decides whether the per-event handler span continues the publisher's trace: only when
* {@code distributedInSameTrace} is enabled AND the event is younger than
* {@code distributedInSameTraceTimeLimit}. Stale events -- typically replays -- get a disconnected span (new trace
* linked back to the publisher) so they do not stretch a long-finished trace (AF4 parity).

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

TODO: remove af4 references.

Comment on lines +145 to 147
if (Segment.fromContext(context).isEmpty()) {
return NoOpSpanFactory.INSTANCE.createRootSpan(BATCH_SPAN, context);
}

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why is it needed?

* Each subscribed {@link QueryHandler} is wrapped so that handling opens a handler span -- parented on the dispatch span
* via the propagated context -- bound to the handling {@link ProcessingContext}'s lifecycle.
* <p>
* Update emission (the AF4 {@code QueryUpdateEmitter} concern) is traced here too: AF5's

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Remove this AF4 reference.

* <p>
* This is best-effort: the attribute is present only when a legacy aggregate-based event storage engine populated the
* resource. It is absent for dynamic-consistency-boundary / entity-based operations and whenever no context is
* available. This provider intentionally does not reference any Axon Framework 4-era message type.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Remove Axon Framework 4 references.

* <p>
* Creating {@link Span spans} is the responsibility of the {@link SpanFactory} which should be implemented by the
* tracing provider of choice.
* <b>No {@code ThreadLocal}.</b> Parent/child relationships are never derived from a thread-bound "current span".

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Explanation:
This may change in the future commits in order to support nested with invocations of things like JPA/JDBC/gRPC etc.

@MateuszNaKodach MateuszNaKodach force-pushed the 3594-distributed-tracing branch 2 times, most recently from 94fa15b to 0854a88 Compare July 7, 2026 16:13
* @param context the active processing context, or {@code null} when none is available
* @return the created span (not yet started)
*/
Span createRootTrace(Supplier<String> operationNameSupplier);

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Explanation:
Suppliers were removed, because now we do not wrap internal invocations, if there is no provided SpanFactory.

Add distributed tracing support to axon-messaging using AF5 component configuration, decorator ordering and ProcessingContext semantics.

The core tracing API lives in org.axonframework.messaging.tracing. Command, event and query decorators live in their component-owned tracing packages:

- org.axonframework.messaging.commandhandling.tracing
- org.axonframework.messaging.eventhandling.tracing
- org.axonframework.messaging.queryhandling.tracing

Implement the AF5 tracing API:

- SpanFactory is the single public span-creation contract.
- SpanFactory implementations receive SpanAttributesProvider instances at construction time through SpanAttributesProviderRegistry.
- Tracing is disabled by omitting a SpanFactory component; enhancers then leave messaging components undecorated.
- Span parent resolution uses ProcessingContext and message metadata, never ThreadLocal state.
- NoOpSpanFactory is a test/composition null-object.
- MultiSpanFactory and LoggingSpanFactory follow the provider-list construction model.

Add messaging tracing configuration:

- TracingConfigurationDefaults and TracingConfigurationOrder.
- MessagingTracingSettings.
- MessagingTracingConfigurationEnhancer.
- MessagingSpanAttributesProviderConfigurationEnhancer.
- ServiceLoader entries for configuration and handler enhancement.
- @RegistrationScope on tracing enhancers to prevent duplicate decorator registration in child component registries.
- Near-maximal tracing decorator order so spans wrap inner framework decorators and measure caller-visible operations.

Add messaging instrumentation:

- TracingCommandBus opens dispatch and handle spans.
- TracingEventSink and TracingEventBus trace publish and commit operations while preserving EventSink/EventBus type compatibility.
- TracingEventHandlingComponent supports batch spans, distributed-in-same-trace behavior, disconnected handler spans and time-limit settings.
- TracingQueryBus covers query, subscription query, update emission and subscription completion operations.
- TracingHandlerEnhancerDefinition is contributed through the standard HandlerEnhancerDefinition ServiceLoader entry and wraps command, event and query handling members generically.
- MetadataSpanAttributesProvider reads staged correlation data from the ProcessingContext so dispatch spans observe the metadata that interceptors place on outgoing messages.

Update the existing tracing surface:

- Use the single SpanFactory contract instead of per-component span factory families.
- Keep built-in messaging attributes focused on message-id, message-type and metadata.
- Add registry-based provider contribution.
- Add focused unit coverage for command, event, query, handler and attribute-provider tracing.
- Adapt logging-span tests to the messaging module's Log4j test infrastructure.

Resolve SpanFactory absence via the ComponentNotFoundException contract only:
a Spring-backed Configuration also signals absence through this same
exception type, so the extra RuntimeException/getOptionalComponent
fallback previously carried here was dead defensive code.

Introduce EntityIdSpanAttributesProvider, a plain holder for the generic
"axoniq.entity.id" key. AggregateIdentifierSpanAttributesProvider remains
the dedicated, legacy-only provider for events sourced through an
aggregate-based event storage engine; its javadoc now states this
explicitly, and DCB events are tagged by EventTagsSpanAttributesProvider
instead.

TracingHandlerEnhancerDefinition drops its explicit-SpanFactory
constructor: ApplicationContext#component only ever throws
ComponentNotFoundException by contract, so the UnsupportedOperationException
branch it guarded against (a ProcessingContext with no ApplicationContext
at all) never occurs on the real handler-invocation path, only in
hand-rolled tests. The constructor existed purely to let tests skip
building a StubProcessingContext; tests now do that directly instead.

Drop MetadataContextPropagator and its PublicApiSurfaceTest entry: a
designed-but-unconsumed extension point with no implementers, no SPI
registration, and no caller anywhere in the framework. The actual
propagation path is Span#propagateContext(Message), implemented by
backend SpanFactory bindings outside this repository.
…racing auto-configuration

Add Spring Boot auto-configuration for AF5 messaging tracing, translating
the axon.tracing.* properties into the framework's native tracing
configuration.

- TracingProperties binds the axon.tracing namespace: the master switch
  and the messaging-owned per-component toggles (command-bus, event-sink,
  event-processor with batch/distributed sub-toggles, query-bus) plus the
  message-id, message-type and metadata attribute-provider toggles.
- TracingAutoConfiguration registers MessagingTracingSettings via
  registerIfNotPresent, so a user-defined settings bean or component
  always wins, and bridges user-declared SpanAttributesProvider beans
  into the SpanAttributesProviderRegistry.
- showEventSourcingHandlers is deliberately not yet exposed as a Spring
  property here: it is an @EventSourcingHandler concern wired up
  alongside the eventsourcing tracing auto-configuration, even though the
  toggle itself lives on this messaging-owned settings record.
- The SpanFactory stays an optional backend contribution: without one,
  the ServiceLoader-discovered tracing enhancers leave every component
  undecorated, and the whole configuration backs off when
  axon.tracing.enabled=false.
- Register the auto-configuration in AutoConfiguration.imports and add
  focused ApplicationContextRunner coverage asserting the property
  translation at the framework-component level.
@MateuszNaKodach MateuszNaKodach force-pushed the 3594-distributed-tracing branch from 0854a88 to 9826ace Compare July 7, 2026 18:04
@MateuszNaKodach MateuszNaKodach changed the title [#3594] feature(messaging): introduce AF5 tracing support [#3594] feature(messaging, extension/spring-boot): introduce AF5 tracing support Jul 7, 2026
@MateuszNaKodach MateuszNaKodach changed the title [#3594] feature(messaging, extension/spring-boot): introduce AF5 tracing support [#3594] feature(messaging, extensions/spring): introduce AF5 tracing support Jul 7, 2026
@smcvb smcvb changed the title [#3594] feature(messaging, extensions/spring): introduce AF5 tracing support [#3594] Reintroduce Distributed Tracing Jul 8, 2026
@MateuszNaKodach MateuszNaKodach changed the title [#3594] Reintroduce Distributed Tracing [#3594] feature(messaging): introduce AF5 tracing support Jul 8, 2026
@MateuszNaKodach MateuszNaKodach changed the title [#3594] feature(messaging): introduce AF5 tracing support [#3594] feature(messaging, extensions/spring): introduce AF5 tracing support Jul 8, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Priority 1: Must Highest priority. A release cannot be made if this issue isn’t resolved. Type: Feature Use to signal an issue is completely new to the project.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant