22
22
import brave .handler .MutableSpan ;
23
23
import brave .handler .SpanHandler ;
24
24
import brave .propagation .TraceContext ;
25
+ import io .micrometer .context .ContextExecutorService ;
26
+ import io .micrometer .context .ContextScheduledExecutorService ;
27
+ import io .micrometer .context .ContextSnapshot ;
28
+ import io .micrometer .context .ContextSnapshotFactory ;
25
29
import io .micrometer .tracing .Tracer ;
26
30
31
+ import java .util .concurrent .ExecutorService ;
32
+ import java .util .concurrent .RejectedExecutionHandler ;
33
+ import java .util .concurrent .ScheduledExecutorService ;
34
+ import java .util .concurrent .ThreadFactory ;
35
+ import java .util .function .Supplier ;
36
+
27
37
import org .springframework .beans .factory .ObjectProvider ;
28
38
import org .springframework .beans .factory .config .ConfigurableListableBeanFactory ;
39
+ import org .springframework .boot .autoconfigure .AutoConfigureBefore ;
29
40
import org .springframework .boot .autoconfigure .condition .ConditionalOnClass ;
30
41
import org .springframework .boot .autoconfigure .condition .ConditionalOnProperty ;
42
+ import org .springframework .boot .autoconfigure .task .TaskExecutionAutoConfiguration ;
31
43
import org .springframework .context .annotation .Bean ;
32
44
import org .springframework .context .annotation .Configuration ;
33
45
import org .springframework .modulith .observability .ModuleEventListener ;
34
46
import org .springframework .modulith .observability .ModuleTracingBeanPostProcessor ;
35
47
import org .springframework .modulith .runtime .ApplicationModulesRuntime ;
48
+ import org .springframework .scheduling .concurrent .ThreadPoolTaskScheduler ;
36
49
37
50
/**
38
51
* @author Oliver Drotbohm
39
52
*/
40
53
@ Configuration (proxyBeanMethods = false )
54
+ @ AutoConfigureBefore (TaskExecutionAutoConfiguration .class )
41
55
@ ConditionalOnProperty (name = "management.tracing.enabled" , havingValue = "true" , matchIfMissing = true )
42
56
class ModuleObservabilityAutoConfiguration {
43
57
@@ -53,6 +67,39 @@ static ModuleEventListener tracingModuleEventListener(ApplicationModulesRuntime
53
67
return new ModuleEventListener (runtime , () -> tracer .getObject ());
54
68
}
55
69
70
+ /**
71
+ * Custom override of default {@link ThreadPoolTaskScheduler} to make sure asynchronous method invocations get the
72
+ * {@link io.micrometer.tracing.handler.TracingObservationHandler.TracingContext} forwarded into threads spawned for
73
+ * those methods. <em>The name of the bean is important for it to be picked up by the async invocation
74
+ * infrastructure!</em>
75
+ */
76
+ @ Bean (name = "taskExecutor" , destroyMethod = "shutdown" )
77
+ ThreadPoolTaskScheduler threadPoolTaskScheduler () {
78
+
79
+ ThreadPoolTaskScheduler threadPoolTaskScheduler = new ThreadPoolTaskScheduler () {
80
+
81
+ private static final long serialVersionUID = -3935299327010101697L ;
82
+ private final Supplier <ContextSnapshot > captureAll = () -> ContextSnapshotFactory .builder ().build ().captureAll ();
83
+
84
+ @ Override
85
+ protected ExecutorService initializeExecutor (ThreadFactory threadFactory ,
86
+ RejectedExecutionHandler rejectedExecutionHandler ) {
87
+
88
+ return ContextExecutorService .wrap (super .initializeExecutor (threadFactory , rejectedExecutionHandler ),
89
+ captureAll );
90
+ }
91
+
92
+ @ Override
93
+ public ScheduledExecutorService getScheduledExecutor () throws IllegalStateException {
94
+ return ContextScheduledExecutorService .wrap (super .getScheduledExecutor (), captureAll );
95
+ }
96
+ };
97
+
98
+ threadPoolTaskScheduler .initialize ();
99
+
100
+ return threadPoolTaskScheduler ;
101
+ }
102
+
56
103
/**
57
104
* Brave-specific auto configuration.
58
105
*
0 commit comments