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(values) (2)
            .pushAll(messages)) { (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 equivalent org.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:

  1. Create the following file in your class path:

    META-INF/services/org.apache.logging.log4j.core.util.ContextDataProvider
  2. 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().