Propagate a snapshot of one or more ThreadLocal
values into another thread.
This library enables automatic propagation of several well-known ThreadLocal contexts by capturing a snapshot, reactivating it in another thread and ensuring proper cleanup after execution finishes:
ContextAwareExecutorService
wrapping any existingExecutorService
.ContextAwareCompletableFuture
propagating context snapshots into each successiveCompletionStage
.
Abstraction containing a value in the context of a thread.
The most common implementation in Java is a ThreadLocal value.
The library provies an AbstractThreadLocalContext
base class
that features nesting values and predictable behaviour for out-of-order closing.
For each context type, there can only be one active context per thread at any time.
Manages a context. The ContextManager API can activate a new context value and provides access to the active context value.
A snapshot contains the current value from all known context managers.
These values can be reactivated in another thread.
Reactivated snapshots must be closed to avoid leaking context.
All context aware utility classes in this library are tested to make sure they reactivate and close snapshots in a safe way.
Just before creating a new thread, capture a snapshot of all ThreadLocal context values:
ContextSnapshot snapshot = ContextManagers.createContextSnapshot();
In the code of your background thread, activate the snapshot to have all ThreadLocal context values set as they were captured:
try (Context<Void> reactivation = snapshot.reactivate()) {
// All ThreadLocal values from the snapshot are available within this block
}
If your background threads are managed by an ExecutorService, you can use our context aware ExecutorService instead of your usual threadpool.
When submitting new work, this automatically takes a context snapshot
to reactivate in the background thread.
After the background thread finishes the snapshot is closed,
ensuring no ThreadLocal values leak into the thread pool.
The ContextAwareExecutorService
can wrap any ExecutorService for the actual thread execution:
private static final ExecutorService THREADPOOL =
new ContextAwareExecutorService(Executors.newCachedThreadpool());
The following ThreadLocal
-based contexts are currently supported
out of the box by this context-propagation library:
- Slf4J MDC (Mapped Diagnostic Context)
- OpenTracing Span contexts
- Spring Security Context
- Locale context
- ServletRequest contexts
- Yours? Feel free to create an issue or pull-request if you believe there's a general context that was forgotten.
Adding your own Context
type is possible
by creating your own context manager.
By default the ContextManagers
class caches the context manager instances it finds
per classloader.
Since the cache is per classloader, this should work satisfactory
for applications with simple classloader hierarchies (e.g. dropwizard)
and also for complex hierarchies (spring boot, JEE and the like).
If however, you wish to disable caching of the context manager instances, you can set either:
- the java system property
talsmasoftware.context.caching
, or - the environment variable
TALSMASOFTWARE_CONTEXT_CACHING
The values false
or 0
will disable caching.
No library is 'free' with regards to performance.
Capturing a context snapshot and reactivating it in another thread is no different.
For insight, the library tracks the overall time used creating and reactivating
context snapshots along with time spent in each individual ContextManager
.
On a development machine, you can get timing for each snapshot by turning on logging
for nl.talsmasoftware.context.Timing
at FINEST
or TRACE
level
(depending on your logger of choice).
Please do not turn this on in production as the logging overhead will most likely
have a noticable impact on your application.
The context propagation metrics module supports for the excellent
dropwizard metrics library.
Adding context propagation metrics
to your classpath will automatically
configure various timers in the default metric registry of your application.