Thread Context
Just like
Java’s ThreadLocal
,
Thread Context facilitates associating information with the executing thread and making this information accessible to the rest of the logging system.
Thread Context is one of many fish tagging capabilities provided by Log4j API.
Usage
The entry point for associating logging-related information with the executing thread is
ThreadContext
.
It offers both
-
map-structured – referred to as Thread Context Map or Mapped Diagnostic Context (MDC)
-
stack-structured – referred to as Thread Context Stack or Nested Diagnostic Context (NDC)
storage:
ThreadContext.put("ipAddress", request.getRemoteAddr()); (1)
ThreadContext.put("hostName", request.getServerName()); (1)
ThreadContext.put("loginId", session.getAttribute("loginId")); (1)
void performWork() {
ThreadContext.push("performWork()"); (2)
LOGGER.debug("Performing work"); (3)
// Perform the work
ThreadContext.pop(); (4)
}
ThreadContext.clear(); (5)
1 | Adding properties to the thread context map |
2 | Pushing properties to the thread context stack |
3 | Added properties can later on be used to, for instance, filter the log event, provide extra information in the layout, etc. |
4 | Popping the last pushed property from the thread context stack |
5 | Clearing the thread context (for both stack and map!) |
Auto-clearing thread context
When placing items on the thread context stack or map, it’s necessary to remove them again when appropriate.
To assist with this, you can use
CloseableThreadContext
(implementing
AutoCloseable
)
in a try-with-resources block:
try (CloseableThreadContext.Instance ignored = CloseableThreadContext
.put("ipAddress", request.getRemoteAddr()) (1)
.push("performWork()")) { (1)
LOGGER.debug("Performing work"); (2)
// Perform the work
}
// ... (3)
1 | Making thread context changes that will only be visible within the scope of the try-with-resources block |
2 | Added properties can later on be used to, for instance, filter the log event, provide extra information in the layout, etc. |
3 | Outside the scope of the try-with-resources block made thread context changes will not be visible |
Initializing thread context
It is a common use case that a single threaded execution fans out to multiple threads by means of a thread pool.
In such a case, you need to clone the context of the current thread to the ones in the pool.
For that purpose, you can use putAll()
and pushAll()
methods that are provided by both ThreadContext
and CloseableThreadContext
:
LOGGER.debug("Starting background thread for user");
Map<String, String> mdc = ThreadContext.getImmutableContext(); (1)
List<String> ndc = ThreadContext.getImmutableStack().asList(); (1)
executor.submit(() -> {
try (CloseableThreadContext.Instance ignored = CloseableThreadContext
.putAll(mdc) (2)
.pushAll(ndc)) { (2)
LOGGER.debug("Processing for user started");
// ...
}
});
1 | Taking a snapshot of the thread context |
2 | Initializing the thread context for the background task |
Configuration
Since the thread context is inherently linked to the logging implementation, its configuration options depend on the logging implementation used:
- Simple Logger
- Log4j Core
- Log4j API to SLF4J bridge
-
All
ThreadContext
method calls are translated into equivalentorg.slf4j.MDC
method calls. - JUL
-
All
ThreadContext
method calls are a no-op.
Extending
Certain thread context implementation details can be customized to fit your use case.
Custom thread context data provider
The ContextDataProvider
is an interface applications and libraries can use to inject additional properties into a log events' context data.
Log4j uses java.util.ServiceLoader
to locate and load ContextDataProvider
instances.
ThreadContextDataProvider
is the default implementation provided.
You can provide a custom ContextDataProvider
implementation as follows:
-
Create the following file in your class path:
META-INF/services/org.apache.logging.log4j.core.util.ContextDataProvider
-
Write the fully-qualified class name of your custom implementation (e.g.,
com.mycompany.CustomContextDataProvider
) to the file created in the previous step
Custom thread context map
Custom thread context map implementations can be provided by setting the log4j2.threadContextMap
system property to the fully-qualified class name of the custom implementation class extending from
ThreadContextMap
.
While providing a custom thread context map implementation, you are advised to also extend from
ReadOnlyThreadContextMap
too.
By this way, your custom thread context map implementation will be accessible to applications via
ThreadContext.getThreadContextMap()
.