Skip to content

Commit 4e7dd5f

Browse files
committed
GH-637 - Enable TraceContext propagation across asynchronous threads.
We're now registering a ThreadPoolTaskScheduler and customize the resources created to propagate the Trace context into threads created by it. This is necessary to connect the tracing spans created for events handled asynchronously.
1 parent 324e824 commit 4e7dd5f

File tree

1 file changed

+47
-0
lines changed

1 file changed

+47
-0
lines changed

spring-modulith-observability/src/main/java/org/springframework/modulith/observability/autoconfigure/ModuleObservabilityAutoConfiguration.java

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,22 +22,36 @@
2222
import brave.handler.MutableSpan;
2323
import brave.handler.SpanHandler;
2424
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;
2529
import io.micrometer.tracing.Tracer;
2630

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+
2737
import org.springframework.beans.factory.ObjectProvider;
2838
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
39+
import org.springframework.boot.autoconfigure.AutoConfigureBefore;
2940
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
3041
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
42+
import org.springframework.boot.autoconfigure.task.TaskExecutionAutoConfiguration;
3143
import org.springframework.context.annotation.Bean;
3244
import org.springframework.context.annotation.Configuration;
3345
import org.springframework.modulith.observability.ModuleEventListener;
3446
import org.springframework.modulith.observability.ModuleTracingBeanPostProcessor;
3547
import org.springframework.modulith.runtime.ApplicationModulesRuntime;
48+
import org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler;
3649

3750
/**
3851
* @author Oliver Drotbohm
3952
*/
4053
@Configuration(proxyBeanMethods = false)
54+
@AutoConfigureBefore(TaskExecutionAutoConfiguration.class)
4155
@ConditionalOnProperty(name = "management.tracing.enabled", havingValue = "true", matchIfMissing = true)
4256
class ModuleObservabilityAutoConfiguration {
4357

@@ -53,6 +67,39 @@ static ModuleEventListener tracingModuleEventListener(ApplicationModulesRuntime
5367
return new ModuleEventListener(runtime, () -> tracer.getObject());
5468
}
5569

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+
56103
/**
57104
* Brave-specific auto configuration.
58105
*

0 commit comments

Comments
 (0)