How do I log trace id in Java Spring Async thread
Master System Design with Codemia
Enhance your system design skills with over 120 practice problems, detailed solutions, and hands-on exercises.
Introduction
In Spring applications, trace IDs are often stored in a thread-local logging context, so asynchronous execution can lose them unless you propagate context explicitly. This becomes a major debugging problem when @Async methods write logs without the request trace ID. A reliable setup copies diagnostic context from the caller thread into worker threads and clears it after execution.
Why Trace Context Is Lost with @Async
@Async runs methods on a thread pool. Logging frameworks usually keep context in MDC, which is thread-local. When work moves to another thread, the context map is empty unless you copy it.
Without propagation, the log line above may miss traceId, even when the caller thread had one.
Propagate MDC with TaskDecorator
Spring ThreadPoolTaskExecutor supports TaskDecorator, which is a clean way to capture and restore MDC.
This pattern prevents context leaks between tasks and ensures each async task sees the caller context.
Ensure Incoming Requests Populate Trace ID
You still need to initialize MDC at request entry. A servlet filter is a common place.
Pair this with a logging pattern that prints %X{traceId} so every log line includes it when present.
If You Use Micrometer Tracing
In newer Spring Boot stacks, Micrometer Tracing can manage trace propagation across supported async boundaries. Even then, custom executors can still need explicit configuration, especially when third-party thread pools are involved. Keep one integration test that verifies trace ID appears in logs from an @Async method, so upgrades do not silently break observability.
Add a Quick Verification Endpoint
A lightweight endpoint helps you validate propagation in local and staging environments. Trigger a request with a known trace header, then inspect both controller and async logs for the same value.
This manual check catches configuration drift quickly, especially after executor, logging, or dependency changes.
Common Pitfalls
- Adding
@Asyncbut using the default executor without context propagation. - Copying MDC into worker threads and forgetting to clear it, which can leak IDs across unrelated requests.
- Setting trace ID in business code instead of at request boundaries, causing inconsistent logs.
- Assuming all async libraries propagate context automatically. Many do not unless explicitly configured.
- Omitting log pattern fields such as
%X{traceId}, making propagation look broken even when context exists.
Summary
@Asyncswitches threads, so MDC trace data is not preserved automatically.- Use
TaskDecoratoron yourThreadPoolTaskExecutorto copy and clear MDC safely. - Initialize
traceIdat request entry, then reuse it across logs and response headers. - Keep log patterns and tests aligned so trace fields are always visible.
- Re-check propagation after framework upgrades and executor changes.

