Tracing context is not propagated when using a CompletableFuture

The following test is not working as it should

@Test
void testContextPropagation() throws ExecutionException, InterruptedException {
    // create a new trace using the micrometer tracer coming from the Spring Boot context
    ScopedSpan scopedSpan = tracer.startScopedSpan("trigger the creation of a new trace...");
    logger.info("CONTEXT: {}", tracer.currentTraceContext().context());
    CompletableFuture.supplyAsync(() -> {
        logger.info("CONTEXT: {}", tracer.currentTraceContext().context());
        return null;
    }, ContextSnapshotFactory.builder().build().captureAll().wrapExecutor(Executors.newSingleThreadExecutor())).get();
    scopedSpan.end();
}

The ouptut of this test will be

CONTEXT: 661e6f0f5965abb6cb8ddf9d107eea2c/cb8ddf9d107eea2c
CONTEXT: null

while it should be

CONTEXT: 661e6f0f5965abb6cb8ddf9d107eea2c/cb8ddf9d107eea2c
CONTEXT: 661e6f0f5965abb6cb8ddf9d107eea2c/cb8ddf9d107eea2c

The expected behaviour would be to keep the trace context into the CompletableFuture but this is not the case.

Spring Boot version: 3.2.4

Any help on that topic is more than welcome. The Spring Boot and micrometer documentation around tracing is quite poor. Did I misunderstand something?

The idea would be to keep the tracing context to allow forwarding it to the next layers, including baggages...

Upvotes: 0

Views: 1206

Answers (1)

Jonatan Ivanov
Jonatan Ivanov

Reputation: 6931

You can use ContextExecutorService (or ContextScheduledExecutorService) to automatically propagate tracing information using CompletableFuture but you need to pass that executor to CompletableFuture (see its javadoc):

ContextExecutorService.wrap(Executors.newSingleThreadExecutor());

and you might need to register an accessor:

ContextRegistry.getInstance().registerThreadLocalAccessor(...);

If you cannot do this for some reason, you need to do it manually, see this issue: context-propagation#138 or check one of our tests that does this: CurrentObservationTest:

@Test
void testManualContextPropagation() throws Exception {
    Span newSpan = this.tracer.nextSpan().name("test").start();
    try (SpanInScope ignored = this.tracer.withSpan(newSpan)) {
        System.out.println("CONTEXT: " + tracer.currentSpan().context());
    }
    CompletableFuture.runAsync(() -> {
        try (SpanInScope ignored = this.tracer.withSpan(newSpan)) {
            System.out.println("CONTEXT: " + tracer.currentSpan().context());
        }
        finally {
            newSpan.end();
        }
    }).get();
}

Upvotes: 2

Related Questions