Add ContextPropagatingTaskDecorator

Prior to this commit, `@Async` and `@EventListener` annotated methods
would lose the the logging and observation contexts whenever their
execution was scheduled on a different Thread.

The Context Propagation library supports this use case and can propagate
context values in ThreadLocals, Reactor Context and more.

This commit introduces a new `TaskDecorator` implementation that
leverages the Context Propagation library. When configured on a
`TaskExecutor`, this allows to properly propagate context value through
the execution of the task.

This implementation is completely optional and requires the
"io.micrometer:context-propagation" library on the classpath. Enabling
this feature must be done consciously and sometimes selectively, as
context propagation introduces some overhead.

Closes gh-31130
This commit is contained in:
Brian Clozel
2023-08-29 11:21:47 +02:00
parent d47c7f9552
commit f5a356c9c6
9 changed files with 347 additions and 14 deletions

View File

@@ -349,3 +349,28 @@ Instrumentation uses the `org.springframework.web.reactive.function.client.Clien
|===
[[observability.application-events]]
== Application Events and `@EventListener`
Spring Framework does not contribute Observations for xref:core/beans/context-introduction.adoc#context-functionality-events-annotation[`@EventListener` calls],
as they don't have the right semantics for such instrumentation.
By default, event publication and processing is done synchronously and on the same Thread.
This means that during the execution of that task, the ThreadLocals and logging context will be the same as the event publisher.
If the application configures globally a custom `ApplicationEventMulticaster` with a strategy that schedules event processing on different threads, this is no longer true.
All `@EventListener` methods will be processed on a different thread, outstide of the main event publication thread.
In these cases, the https://micrometer.io/docs/contextPropagation[Micrometer Context Propagation library] can help propagating such values and better correlate the processing of the events.
The application can configure the chosen `TaskExecutor` to use a `ContextPropagatingTaskDecorator` that decorates tasks and propagates context.
For this to work, the `io.micrometer:context-propagation` library must be present on the classpath:
include-code::./ApplicationEventsConfiguration[]
Similarly, if that asynchronous choice is made locally for each `@EventListener` annotated method, by adding an `@Async` method to it,
you can choose a `TaskExecutor` that propagates context by referring to it by its qualifier.
Given the following `TaskExecutor` bean definition, configured with the dedicated task decorator:
include-code::./EventAsyncExecutionConfiguration[]
Annotating event listeners with `@Async` and the relevant qualifier will achieve similar context propagation results:
include-code::./EmailNotificationListener[]