Skip to content
AITroveRead. Build. Understand.
Make this comfortable

Spring async trace context: follow work across executor threads

Last updated: 1 Oct 20265 min read
tutorial
IntermediateBy AITrove Editorial

A trace context ties related observations together while a request moves through services and background work. An @Async method runs on an executor thread, so thread-local context from the request thread is not automatically present there.

Opt in to the intended propagation

Spring Boot can propagate observation context through its auto-configured AsyncTaskExecutor when configured to do so. This carries diagnostic context, not a durable task record. The job still needs bounded admission, explicit failure handling and a plan for process loss. Check which executor your @Async method actually uses; a custom executor may need its own propagation setup.

properties
spring.task.execution.propagate-context=true
management.tracing.sampling.probability=0.10
Java
package in.aitrove.receipts;

import io.micrometer.observation.Observation;
import io.micrometer.observation.ObservationRegistry;
import java.util.concurrent.CompletableFuture;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;

@Service
class ReceiptReviewTask {
    private final ObservationRegistry observations;

    ReceiptReviewTask(ObservationRegistry observations) {
        this.observations = observations;
    }

    @Async
    public CompletableFuture<String> review(long receiptId) {
        String outcome = Observation.createNotStarted("receipt.review", observations)
            .lowCardinalityKeyValue("result", "accepted")
            .observe(() -> "reviewed-" + receiptId);
        return CompletableFuture.completedFuture(outcome);
    }
}

The returned Future makes completion and failure observable to the caller. The example’s work is deliberately local; a real review must not claim durable execution merely because the method returned a Future. Sampling reduces exported trace volume, not the cost of the business work. Use bounded values such as result=accepted in low-cardinality tags; receipt IDs belong in controlled logs or trace attributes subject to your privacy policy, not metrics labels.

Common Mistakes

  • Assuming @Async preserves every ThreadLocal, including authentication, across threads.
  • Dropping the Future and losing the worker exception.
  • Putting each receipt ID into a metric tag.
  • Calling an in-process async task a recoverable queue.

Read next

Executor capacity, metric tag limits, and durable delivery.

spring
spring-boot
async-trace-context
Storage details