Standardized context propagation in concurrent systems.
Provides a standardized way to create snapshots from various supported
ThreadLocal-based Context types that can be reactivated in another
thread.
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 acting as a threadpool,
you can use the ContextAwareExecutorService instead of your usual threadpool.
This automatically takes a new context snapshot when submitting new work
and reactivates this snapshot in the background threads.
The ContextAwareExecutorService can wrap any ExecutorService for the actual thread execution:
// private static final ExecutorService THREADPOOL = Executors.newCachedThreadpool();
private static final ExecutorService THREADPOOL =
new ContextAwareExecutorService(Executors.newCachedThreadpool());It will automatically create a snapshot and reactivate it in the
background thread when started.
The ThreadLocal values from the calling thread will therefore
be available in the background thread as well.
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 not difficult.
It is easy to add a custom Context type to be propagated:
- Implement the
ContextManagerinterface.
Create a class with a default constructor that implements initializeNewContext and getActiveContext methods. - Create a service file called
/META-INF/services/nl.talsmasoftware.context.ContextManagercontaining the qualified class name of yourContextManagerimplementation. - That's it. Now the result from your getActiveContext method is propagated
into each snapshot created by the
ContextManagers.createSnapshot()method. This includes all usages of theContextAwareExecutorService.
An example of a custom context implementation:
public class DummyContextManager implements ContextManager<String> {
public Context<String> initializeNewContext(String value) {
return new DummyContext(value);
}
public Context<String> getActiveContext() {
return DummyContext.current();
}
public static Optional<String> currentValue() {
return Optional.ofNullable(DummyContext.current()).map(Context::getValue);
}
private static final class DummyContext extends AbstractThreadLocalContext<String> {
private DummyContext(String newValue) {
super(newValue);
}
private static Context<String> current() {
return AbstractThreadLocalContext.current(DummyContext.class);
}
}
}By default the ContextManagers class caches the context manager instances it finds per
context classloader. Since the cache is per classloader, this should work satisfactory
for applications with simple classloader hierarchies (e.g. spring boot, dropwizard etc)
and complex hierarchies (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_CACHNG
to the values false or 0.
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 to the context management itself.
If your project happens to use dropwizard metrics, adding the context propagation metrics module to your classpath will automatically configure various timers in the default metric registry of your application.