SLF4J 2 provider that bridges SLF4J 2 logging calls to the Apache Log4j API
—
Mapped Diagnostic Context (MDC) provides thread-local context management for adding contextual information to log events. The implementation uses Log4j's ThreadContext as the backing store, providing both map-based and stack-based context storage.
Core MDC functionality for managing key-value pairs in the current thread's context.
/**
* Put a key-value pair into the MDC
* @param key The key
* @param val The value
*/
void put(String key, String val);
/**
* Get a value from the MDC by key
* @param key The key
* @return The value or null if not found
*/
String get(String key);
/**
* Remove a key from the MDC
* @param key The key to remove
*/
void remove(String key);
/**
* Clear all entries from the MDC
*/
void clear();Usage Examples:
import org.slf4j.MDC;
// Basic MDC usage
MDC.put("userId", "12345");
MDC.put("requestId", "req-abc-123");
MDC.put("sessionId", "sess-xyz-789");
// Use in logging - values automatically included in log output
logger.info("Processing user request");
// Retrieve values
String userId = MDC.get("userId");
String requestId = MDC.get("requestId");
// Remove specific key
MDC.remove("sessionId");
// Clear all MDC data for current thread
MDC.clear();Manage entire MDC context maps for bulk operations and context preservation.
/**
* Get a copy of the current MDC context map
* @return Copy of the context map (null if empty)
*/
Map<String, String> getCopyOfContextMap();
/**
* Set the entire MDC context map
* @param contextMap The context map to set (clears existing first)
*/
void setContextMap(Map<String, String> contextMap);Usage Examples:
// Save current context
Map<String, String> savedContext = MDC.getCopyOfContextMap();
// Set new context
Map<String, String> newContext = new HashMap<>();
newContext.put("operation", "batch-processing");
newContext.put("batchId", "batch-001");
newContext.put("worker", "worker-thread-1");
MDC.setContextMap(newContext);
// Perform operations with new context
logger.info("Starting batch processing");
processBatch();
// Restore previous context
if (savedContext != null) {
MDC.setContextMap(savedContext);
} else {
MDC.clear();
}Advanced MDC functionality supporting stack-based context management for nested operations.
/**
* Push a value onto a keyed stack in the MDC
* @param key The stack key (null for default stack)
* @param value The value to push
*/
void pushByKey(String key, String value);
/**
* Pop a value from a keyed stack in the MDC
* @param key The stack key (null for default stack)
* @return The popped value or null if stack is empty
*/
String popByKey(String key);
/**
* Get a copy of the deque for a specific key
* @param key The stack key (null for default stack)
* @return Copy of the deque or null if not found
*/
Deque<String> getCopyOfDequeByKey(String key);
/**
* Clear the deque for a specific key
* @param key The stack key (null for default stack)
*/
void clearDequeByKey(String key);Usage Examples:
// Stack-based context for nested operations
MDC.pushByKey("operation", "user-service");
MDC.pushByKey("operation", "authenticate");
MDC.pushByKey("operation", "validate-token");
logger.info("Validating authentication token");
// Pop back through the operation stack
MDC.popByKey("operation"); // removes "validate-token"
logger.info("Token validated, checking permissions");
MDC.popByKey("operation"); // removes "authenticate"
logger.info("Authentication completed");
MDC.popByKey("operation"); // removes "user-service"
// Default stack operations (key = null)
MDC.pushByKey(null, "context1");
MDC.pushByKey(null, "context2");
String current = MDC.popByKey(null); // returns "context2"
// Inspect stack contents
Deque<String> operationStack = MDC.getCopyOfDequeByKey("operation");
if (operationStack != null) {
logger.debug("Current operation stack depth: {}", operationStack.size());
}
// Clear specific stack
MDC.clearDequeByKey("operation");// Web request processing
public void processRequest(HttpServletRequest request) {
try {
// Set request context
MDC.put("requestId", generateRequestId());
MDC.put("userId", extractUserId(request));
MDC.put("userAgent", request.getHeader("User-Agent"));
MDC.put("remoteAddr", request.getRemoteAddr());
MDC.put("method", request.getMethod());
MDC.put("uri", request.getRequestURI());
logger.info("Processing request");
// All subsequent logging in this thread includes context
handleRequest(request);
logger.info("Request completed successfully");
} catch (Exception e) {
logger.error("Request failed", e);
} finally {
// Always clean up MDC
MDC.clear();
}
}// Database transaction context
public void executeInTransaction(String transactionType, Runnable operation) {
String transactionId = generateTransactionId();
try {
MDC.put("transactionId", transactionId);
MDC.put("transactionType", transactionType);
MDC.put("threadName", Thread.currentThread().getName());
logger.info("Starting transaction");
operation.run();
logger.info("Transaction completed");
} catch (Exception e) {
logger.error("Transaction failed", e);
throw e;
} finally {
MDC.remove("transactionId");
MDC.remove("transactionType");
MDC.remove("threadName");
}
}// Service layer with nested operations
public class UserService {
public void createUser(User user) {
MDC.put("operation", "createUser");
MDC.put("userId", user.getId());
try {
logger.info("Creating user");
validateUser(user);
saveUser(user);
sendWelcomeEmail(user);
logger.info("User created successfully");
} finally {
MDC.remove("operation");
MDC.remove("userId");
}
}
private void validateUser(User user) {
// Push nested operation context
MDC.pushByKey("subOperation", "validation");
try {
logger.debug("Validating user data");
// validation logic
} finally {
MDC.popByKey("subOperation");
}
}
private void saveUser(User user) {
MDC.pushByKey("subOperation", "database-save");
try {
logger.debug("Saving user to database");
// save logic
} finally {
MDC.popByKey("subOperation");
}
}
}// Preserving context across async boundaries
public CompletableFuture<String> processAsync(String data) {
// Capture current MDC context
Map<String, String> currentContext = MDC.getCopyOfContextMap();
return CompletableFuture.supplyAsync(() -> {
try {
// Restore context in async thread
if (currentContext != null) {
MDC.setContextMap(currentContext);
}
MDC.put("asyncOperation", "data-processing");
logger.info("Processing data asynchronously");
// Process data
String result = performDataProcessing(data);
logger.info("Async processing completed");
return result;
} finally {
// Clean up context in async thread
MDC.clear();
}
});
}// Parent-child context inheritance
public void parentOperation() {
MDC.put("parentOperation", "batch-job");
MDC.put("batchId", "batch-12345");
try {
logger.info("Starting batch job");
for (int i = 0; i < items.size(); i++) {
processItem(items.get(i), i);
}
logger.info("Batch job completed");
} finally {
MDC.clear();
}
}
private void processItem(Item item, int index) {
// Inherit parent context and add item-specific context
MDC.put("itemIndex", String.valueOf(index));
MDC.put("itemId", item.getId());
try {
logger.debug("Processing item");
// Item processing logic
performItemProcessing(item);
logger.debug("Item processed successfully");
} catch (Exception e) {
logger.error("Item processing failed", e);
} finally {
// Remove item-specific context but keep parent context
MDC.remove("itemIndex");
MDC.remove("itemId");
}
}MDC values can be referenced in Log4j configuration patterns:
<!-- log4j2.xml pattern configuration -->
<Configuration>
<Appenders>
<Console name="Console">
<PatternLayout pattern="%d{HH:mm:ss.SSS} [%t] %-5level [%X{requestId}] [%X{userId}] %logger{36} - %msg%n"/>
</Console>
<File name="FileAppender" fileName="app.log">
<PatternLayout pattern="%d{yyyy-MM-dd HH:mm:ss.SSS} [%t] %-5level [%X] %logger{36} - %msg%n"/>
</File>
</Appenders>
</Configuration>// Efficient MDC usage
public void efficientLogging() {
// Set context once for multiple operations
MDC.put("operation", "bulk-processing");
try {
for (Item item : items) {
// Only update changing context
MDC.put("itemId", item.getId());
processItem(item);
// Remove only the changing context
MDC.remove("itemId");
}
} finally {
// Clean up operation context
MDC.remove("operation");
}
}Install with Tessl CLI
npx tessl i tessl/maven-org-apache-logging-log4j--log4j-slf4j2-impl