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
ContextManager
interface.
Create a class with a default constructor that implements initializeNewContext and getActiveContext methods. - Create a service file called
/META-INF/services/nl.talsmasoftware.context.ContextManager
containing the qualified class name of yourContextManager
implementation. - 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.