The Apache Log4j 1.x Compatibility API providing a bridge to Log4j 2.x implementations
—
Thread context provides thread-local diagnostic information storage through MDC (Mapped Diagnostic Context) and NDC (Nested Diagnostic Context). This enables context-aware logging across application threads.
public final class MDC {
// Context management
public static void put(String key, String value);
public static Object get(String key);
public static String get(String key, String defaultValue);
public static void remove(String key);
public static void clear();
// Context retrieval
public static Hashtable<String, Object> getContext();
public static Map<String, String> getCopyOfContextMap();
public static void setContextMap(Map<String, String> contextMap);
// Utility methods
public static Set<String> getKeys();
public static boolean isEmpty();
}Parameters:
key - String key for the context valuevalue - String value to storedefaultValue - String default value if key not foundcontextMap - Map of context key-value pairsReturns:
Object or String value for the specified keyHashtable<String, Object> current contextMap<String, String> copy of context mapSet<String> set of all keysboolean indicating if context is emptypublic final class NDC {
// Stack operations
public static void push(String message);
public static String pop();
public static String peek();
public static void clear();
public static void remove();
// Stack information
public static int getDepth();
public static Stack cloneStack();
public static void inherit(Stack stack);
// Formatting
public static String get();
}Parameters:
message - String message to push onto the stackstack - Stack to inherit from another threadReturns:
String popped or peeked messageint current stack depthStack cloned copy of current stackString formatted NDC stringimport org.apache.log4j.MDC;
import org.apache.log4j.Logger;
public class MDCExample {
private static final Logger logger = Logger.getLogger(MDCExample.class);
public void processUserRequest(String userId, String sessionId) {
// Set context information
MDC.put("userId", userId);
MDC.put("sessionId", sessionId);
MDC.put("operation", "processRequest");
try {
logger.info("Starting user request processing");
// Business logic
processBusinessLogic();
logger.info("User request completed successfully");
} catch (Exception e) {
logger.error("User request failed", e);
} finally {
// Clean up context
MDC.clear();
}
}
private void processBusinessLogic() {
// This method can access MDC context
String userId = (String) MDC.get("userId");
logger.debug("Processing business logic for user: " + userId);
// Add more context
MDC.put("step", "validation");
validateUserData();
MDC.put("step", "processing");
performProcessing();
MDC.remove("step");
}
private void validateUserData() {
logger.debug("Validating user data");
}
private void performProcessing() {
logger.debug("Performing main processing");
}
}import org.apache.log4j.MDC;
import javax.servlet.*;
import javax.servlet.http.HttpServletRequest;
import java.io.IOException;
import java.util.UUID;
public class MDCFilter implements Filter {
@Override
public void doFilter(ServletRequest request, ServletResponse response,
FilterChain chain) throws IOException, ServletException {
HttpServletRequest httpRequest = (HttpServletRequest) request;
try {
// Set request-specific context
MDC.put("requestId", UUID.randomUUID().toString());
MDC.put("remoteAddr", request.getRemoteAddr());
MDC.put("requestURI", httpRequest.getRequestURI());
MDC.put("method", httpRequest.getMethod());
// Add user context if available
String userId = httpRequest.getRemoteUser();
if (userId != null) {
MDC.put("userId", userId);
}
// Continue filter chain
chain.doFilter(request, response);
} finally {
// Always clean up MDC
MDC.clear();
}
}
@Override
public void init(FilterConfig filterConfig) throws ServletException {
// Filter initialization
}
@Override
public void destroy() {
// Filter cleanup
}
}import org.apache.log4j.MDC;
import java.util.Map;
import java.util.HashMap;
public class MDCContextExample {
public void demonstrateContextOperations() {
// Set individual values
MDC.put("user", "john");
MDC.put("session", "abc123");
MDC.put("operation", "login");
// Get context copy
Map<String, String> contextCopy = MDC.getCopyOfContextMap();
System.out.println("Current context: " + contextCopy);
// Check if context is empty
boolean isEmpty = MDC.isEmpty();
System.out.println("Context empty: " + isEmpty);
// Get all keys
Set<String> keys = MDC.getKeys();
System.out.println("Context keys: " + keys);
// Create new context map
Map<String, String> newContext = new HashMap<>();
newContext.put("service", "authentication");
newContext.put("version", "1.0");
// Replace entire context
MDC.setContextMap(newContext);
// Verify change
System.out.println("New context: " + MDC.getCopyOfContextMap());
// Clear all
MDC.clear();
}
}import org.apache.log4j.NDC;
import org.apache.log4j.Logger;
public class NDCExample {
private static final Logger logger = Logger.getLogger(NDCExample.class);
public void processOrder(String orderId) {
NDC.push("OrderService");
NDC.push("orderId=" + orderId);
try {
logger.info("Starting order processing");
validateOrder();
processPayment();
fulfillOrder();
logger.info("Order processing completed");
} catch (Exception e) {
logger.error("Order processing failed", e);
} finally {
// Pop all contexts for this operation
NDC.pop(); // Remove orderId
NDC.pop(); // Remove OrderService
}
}
private void validateOrder() {
NDC.push("validation");
try {
logger.debug("Validating order details");
// Validation logic
} finally {
NDC.pop();
}
}
private void processPayment() {
NDC.push("payment");
try {
logger.debug("Processing payment");
// Payment logic
} finally {
NDC.pop();
}
}
private void fulfillOrder() {
NDC.push("fulfillment");
try {
logger.debug("Fulfilling order");
// Fulfillment logic
} finally {
NDC.pop();
}
}
}import org.apache.log4j.NDC;
import org.apache.log4j.Logger;
import java.util.Stack;
public class NDCStackExample {
private static final Logger logger = Logger.getLogger(NDCStackExample.class);
public void demonstrateStackOperations() {
// Build up context stack
NDC.push("Application");
NDC.push("UserService");
NDC.push("createUser");
// Check stack depth
int depth = NDC.getDepth();
logger.info("Current NDC depth: " + depth);
// Peek at top without removing
String top = NDC.peek();
logger.info("Top of stack: " + top);
// Get full NDC string
String fullContext = NDC.get();
logger.info("Full NDC context: " + fullContext);
// Clone stack for another thread
Stack<String> stackCopy = NDC.cloneStack();
// Pop one level
String popped = NDC.pop();
logger.info("Popped: " + popped);
logger.info("Remaining context: " + NDC.get());
// Clear all
NDC.clear();
logger.info("After clear, depth: " + NDC.getDepth());
// Restore from clone
NDC.inherit(stackCopy);
logger.info("After inherit: " + NDC.get());
// Final cleanup
NDC.remove();
}
}import org.apache.log4j.MDC;
import org.apache.log4j.NDC;
import org.apache.log4j.Logger;
import java.util.Map;
import java.util.Stack;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class ThreadContextInheritance {
private static final Logger logger = Logger.getLogger(ThreadContextInheritance.class);
public void processWithThreads() {
// Set up context in main thread
MDC.put("mainThread", "true");
MDC.put("operation", "multiThreadProcess");
NDC.push("MainProcessor");
// Capture context for inheritance
Map<String, String> mdcContext = MDC.getCopyOfContextMap();
Stack ndcStack = NDC.cloneStack();
ExecutorService executor = Executors.newFixedThreadPool(3);
for (int i = 0; i < 3; i++) {
final int taskId = i;
executor.submit(() -> {
try {
// Inherit context from parent thread
MDC.setContextMap(mdcContext);
NDC.inherit(ndcStack);
// Add thread-specific context
MDC.put("threadId", String.valueOf(taskId));
NDC.push("Worker-" + taskId);
logger.info("Processing task in worker thread");
// Simulate work
Thread.sleep(1000);
logger.info("Task completed in worker thread");
} catch (InterruptedException e) {
logger.warn("Thread interrupted", e);
} finally {
// Clean up thread context
MDC.clear();
NDC.remove();
}
});
}
executor.shutdown();
// Clean up main thread context
MDC.clear();
NDC.clear();
}
}import org.apache.log4j.*;
public class ContextPatternExample {
public void setupContextLogging() {
// Pattern that includes MDC values
PatternLayout mdcPattern = new PatternLayout(
"%d{ISO8601} [%t] %-5p %c - [%X{userId}:%X{sessionId}] %m%n"
);
// Pattern that includes specific MDC keys
PatternLayout specificMDC = new PatternLayout(
"%d %-5p %c - User:%X{userId} Session:%X{sessionId} - %m%n"
);
// Pattern that includes NDC
PatternLayout ndcPattern = new PatternLayout(
"%d %-5p %c %x - %m%n"
);
// Pattern with both MDC and NDC
PatternLayout combinedPattern = new PatternLayout(
"%d [%t] %-5p %c - %X{userId} %x - %m%n"
);
// Create appenders
ConsoleAppender mdcAppender = new ConsoleAppender(mdcPattern);
ConsoleAppender ndcAppender = new ConsoleAppender(ndcPattern);
// Configure loggers
Logger mdcLogger = Logger.getLogger("mdc-logger");
mdcLogger.addAppender(mdcAppender);
Logger ndcLogger = Logger.getLogger("ndc-logger");
ndcLogger.addAppender(ndcAppender);
}
}import org.apache.log4j.MDC;
import org.apache.log4j.NDC;
import org.apache.log4j.Logger;
public class ContextCleanupExample {
private static final Logger logger = Logger.getLogger(ContextCleanupExample.class);
// Using try-with-resources pattern for MDC
public static class MDCCloseable implements AutoCloseable {
public MDCCloseable(String key, String value) {
MDC.put(key, value);
}
@Override
public void close() {
MDC.clear();
}
}
// Using try-with-resources pattern for NDC
public static class NDCCloseable implements AutoCloseable {
public NDCCloseable(String context) {
NDC.push(context);
}
@Override
public void close() {
NDC.pop();
}
}
public void processWithAutoCleanup(String userId, String operation) {
// MDC with automatic cleanup
try (MDCCloseable mdcContext = new MDCCloseable("userId", userId)) {
MDC.put("operation", operation);
// NDC with automatic cleanup
try (NDCCloseable ndcContext = new NDCCloseable("ProcessorService")) {
logger.info("Starting processing with auto-cleanup");
// Business logic here
performBusinessLogic();
logger.info("Processing completed");
} // NDC automatically popped here
} // MDC automatically cleared here
}
// Manual cleanup with finally blocks
public void processWithManualCleanup(String userId, String operation) {
// Set up MDC
MDC.put("userId", userId);
MDC.put("operation", operation);
// Set up NDC
NDC.push("ManualService");
try {
logger.info("Starting processing with manual cleanup");
performBusinessLogic();
logger.info("Processing completed");
} catch (Exception e) {
logger.error("Processing failed", e);
throw e;
} finally {
// Always clean up in reverse order
NDC.pop(); // Remove ManualService
MDC.clear(); // Clear all MDC values
}
}
private void performBusinessLogic() {
// This method inherits the context
logger.debug("Performing business logic");
// Can add temporary context
MDC.put("step", "validation");
NDC.push("validator");
try {
logger.debug("Validating input");
// Validation logic
} finally {
// Clean up temporary context
NDC.pop();
MDC.remove("step");
}
}
}import org.apache.log4j.MDC;
import org.apache.log4j.Logger;
public class ContextPerformanceExample {
private static final Logger logger = Logger.getLogger(ContextPerformanceExample.class);
public void efficientContextUsage() {
// Avoid expensive operations in context values
String userId = getCurrentUserId(); // Expensive call
MDC.put("userId", userId); // Store result, don't call repeatedly
// Use conditional logging to avoid context lookups
if (logger.isDebugEnabled()) {
String contextInfo = buildContextInfo(); // Only when needed
logger.debug("Context info: " + contextInfo);
}
// Prefer specific key access over full context maps
String user = (String) MDC.get("userId"); // Efficient
// Map<String, String> fullContext = MDC.getCopyOfContextMap(); // Less efficient
// Clean up promptly to avoid memory leaks
MDC.clear();
}
private String getCurrentUserId() {
// Simulate expensive operation
return "user123";
}
private String buildContextInfo() {
// Build debug info only when needed
return "User: " + MDC.get("userId") + ", Session: " + MDC.get("sessionId");
}
}Install with Tessl CLI
npx tessl i tessl/maven-org-apache-logging-log4j--log4j-1-2-api