0
# MDC Support
1
2
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.
3
4
## Capabilities
5
6
### Basic MDC Operations
7
8
Core MDC functionality for managing key-value pairs in the current thread's context.
9
10
```java { .api }
11
/**
12
* Put a key-value pair into the MDC
13
* @param key The key
14
* @param val The value
15
*/
16
void put(String key, String val);
17
18
/**
19
* Get a value from the MDC by key
20
* @param key The key
21
* @return The value or null if not found
22
*/
23
String get(String key);
24
25
/**
26
* Remove a key from the MDC
27
* @param key The key to remove
28
*/
29
void remove(String key);
30
31
/**
32
* Clear all entries from the MDC
33
*/
34
void clear();
35
```
36
37
**Usage Examples:**
38
39
```java
40
import org.slf4j.MDC;
41
42
// Basic MDC usage
43
MDC.put("userId", "12345");
44
MDC.put("requestId", "req-abc-123");
45
MDC.put("sessionId", "sess-xyz-789");
46
47
// Use in logging - values automatically included in log output
48
logger.info("Processing user request");
49
50
// Retrieve values
51
String userId = MDC.get("userId");
52
String requestId = MDC.get("requestId");
53
54
// Remove specific key
55
MDC.remove("sessionId");
56
57
// Clear all MDC data for current thread
58
MDC.clear();
59
```
60
61
### MDC Context Management
62
63
Manage entire MDC context maps for bulk operations and context preservation.
64
65
```java { .api }
66
/**
67
* Get a copy of the current MDC context map
68
* @return Copy of the context map (null if empty)
69
*/
70
Map<String, String> getCopyOfContextMap();
71
72
/**
73
* Set the entire MDC context map
74
* @param contextMap The context map to set (clears existing first)
75
*/
76
void setContextMap(Map<String, String> contextMap);
77
```
78
79
**Usage Examples:**
80
81
```java
82
// Save current context
83
Map<String, String> savedContext = MDC.getCopyOfContextMap();
84
85
// Set new context
86
Map<String, String> newContext = new HashMap<>();
87
newContext.put("operation", "batch-processing");
88
newContext.put("batchId", "batch-001");
89
newContext.put("worker", "worker-thread-1");
90
MDC.setContextMap(newContext);
91
92
// Perform operations with new context
93
logger.info("Starting batch processing");
94
processBatch();
95
96
// Restore previous context
97
if (savedContext != null) {
98
MDC.setContextMap(savedContext);
99
} else {
100
MDC.clear();
101
}
102
```
103
104
### Stack-Based MDC Operations
105
106
Advanced MDC functionality supporting stack-based context management for nested operations.
107
108
```java { .api }
109
/**
110
* Push a value onto a keyed stack in the MDC
111
* @param key The stack key (null for default stack)
112
* @param value The value to push
113
*/
114
void pushByKey(String key, String value);
115
116
/**
117
* Pop a value from a keyed stack in the MDC
118
* @param key The stack key (null for default stack)
119
* @return The popped value or null if stack is empty
120
*/
121
String popByKey(String key);
122
123
/**
124
* Get a copy of the deque for a specific key
125
* @param key The stack key (null for default stack)
126
* @return Copy of the deque or null if not found
127
*/
128
Deque<String> getCopyOfDequeByKey(String key);
129
130
/**
131
* Clear the deque for a specific key
132
* @param key The stack key (null for default stack)
133
*/
134
void clearDequeByKey(String key);
135
```
136
137
**Usage Examples:**
138
139
```java
140
// Stack-based context for nested operations
141
MDC.pushByKey("operation", "user-service");
142
MDC.pushByKey("operation", "authenticate");
143
MDC.pushByKey("operation", "validate-token");
144
145
logger.info("Validating authentication token");
146
147
// Pop back through the operation stack
148
MDC.popByKey("operation"); // removes "validate-token"
149
logger.info("Token validated, checking permissions");
150
151
MDC.popByKey("operation"); // removes "authenticate"
152
logger.info("Authentication completed");
153
154
MDC.popByKey("operation"); // removes "user-service"
155
156
// Default stack operations (key = null)
157
MDC.pushByKey(null, "context1");
158
MDC.pushByKey(null, "context2");
159
String current = MDC.popByKey(null); // returns "context2"
160
161
// Inspect stack contents
162
Deque<String> operationStack = MDC.getCopyOfDequeByKey("operation");
163
if (operationStack != null) {
164
logger.debug("Current operation stack depth: {}", operationStack.size());
165
}
166
167
// Clear specific stack
168
MDC.clearDequeByKey("operation");
169
```
170
171
## Common MDC Patterns
172
173
### Request Context Pattern
174
175
```java
176
// Web request processing
177
public void processRequest(HttpServletRequest request) {
178
try {
179
// Set request context
180
MDC.put("requestId", generateRequestId());
181
MDC.put("userId", extractUserId(request));
182
MDC.put("userAgent", request.getHeader("User-Agent"));
183
MDC.put("remoteAddr", request.getRemoteAddr());
184
MDC.put("method", request.getMethod());
185
MDC.put("uri", request.getRequestURI());
186
187
logger.info("Processing request");
188
189
// All subsequent logging in this thread includes context
190
handleRequest(request);
191
192
logger.info("Request completed successfully");
193
194
} catch (Exception e) {
195
logger.error("Request failed", e);
196
} finally {
197
// Always clean up MDC
198
MDC.clear();
199
}
200
}
201
```
202
203
### Transaction Context Pattern
204
205
```java
206
// Database transaction context
207
public void executeInTransaction(String transactionType, Runnable operation) {
208
String transactionId = generateTransactionId();
209
210
try {
211
MDC.put("transactionId", transactionId);
212
MDC.put("transactionType", transactionType);
213
MDC.put("threadName", Thread.currentThread().getName());
214
215
logger.info("Starting transaction");
216
217
operation.run();
218
219
logger.info("Transaction completed");
220
221
} catch (Exception e) {
222
logger.error("Transaction failed", e);
223
throw e;
224
} finally {
225
MDC.remove("transactionId");
226
MDC.remove("transactionType");
227
MDC.remove("threadName");
228
}
229
}
230
```
231
232
### Nested Operation Context
233
234
```java
235
// Service layer with nested operations
236
public class UserService {
237
238
public void createUser(User user) {
239
MDC.put("operation", "createUser");
240
MDC.put("userId", user.getId());
241
242
try {
243
logger.info("Creating user");
244
245
validateUser(user);
246
saveUser(user);
247
sendWelcomeEmail(user);
248
249
logger.info("User created successfully");
250
251
} finally {
252
MDC.remove("operation");
253
MDC.remove("userId");
254
}
255
}
256
257
private void validateUser(User user) {
258
// Push nested operation context
259
MDC.pushByKey("subOperation", "validation");
260
261
try {
262
logger.debug("Validating user data");
263
// validation logic
264
} finally {
265
MDC.popByKey("subOperation");
266
}
267
}
268
269
private void saveUser(User user) {
270
MDC.pushByKey("subOperation", "database-save");
271
272
try {
273
logger.debug("Saving user to database");
274
// save logic
275
} finally {
276
MDC.popByKey("subOperation");
277
}
278
}
279
}
280
```
281
282
### Async Processing Context
283
284
```java
285
// Preserving context across async boundaries
286
public CompletableFuture<String> processAsync(String data) {
287
// Capture current MDC context
288
Map<String, String> currentContext = MDC.getCopyOfContextMap();
289
290
return CompletableFuture.supplyAsync(() -> {
291
try {
292
// Restore context in async thread
293
if (currentContext != null) {
294
MDC.setContextMap(currentContext);
295
}
296
297
MDC.put("asyncOperation", "data-processing");
298
299
logger.info("Processing data asynchronously");
300
301
// Process data
302
String result = performDataProcessing(data);
303
304
logger.info("Async processing completed");
305
306
return result;
307
308
} finally {
309
// Clean up context in async thread
310
MDC.clear();
311
}
312
});
313
}
314
```
315
316
### Context Inheritance Pattern
317
318
```java
319
// Parent-child context inheritance
320
public void parentOperation() {
321
MDC.put("parentOperation", "batch-job");
322
MDC.put("batchId", "batch-12345");
323
324
try {
325
logger.info("Starting batch job");
326
327
for (int i = 0; i < items.size(); i++) {
328
processItem(items.get(i), i);
329
}
330
331
logger.info("Batch job completed");
332
333
} finally {
334
MDC.clear();
335
}
336
}
337
338
private void processItem(Item item, int index) {
339
// Inherit parent context and add item-specific context
340
MDC.put("itemIndex", String.valueOf(index));
341
MDC.put("itemId", item.getId());
342
343
try {
344
logger.debug("Processing item");
345
346
// Item processing logic
347
performItemProcessing(item);
348
349
logger.debug("Item processed successfully");
350
351
} catch (Exception e) {
352
logger.error("Item processing failed", e);
353
} finally {
354
// Remove item-specific context but keep parent context
355
MDC.remove("itemIndex");
356
MDC.remove("itemId");
357
}
358
}
359
```
360
361
## Integration with Log4j Configuration
362
363
MDC values can be referenced in Log4j configuration patterns:
364
365
```xml
366
<!-- log4j2.xml pattern configuration -->
367
<Configuration>
368
<Appenders>
369
<Console name="Console">
370
<PatternLayout pattern="%d{HH:mm:ss.SSS} [%t] %-5level [%X{requestId}] [%X{userId}] %logger{36} - %msg%n"/>
371
</Console>
372
<File name="FileAppender" fileName="app.log">
373
<PatternLayout pattern="%d{yyyy-MM-dd HH:mm:ss.SSS} [%t] %-5level [%X] %logger{36} - %msg%n"/>
374
</File>
375
</Appenders>
376
</Configuration>
377
```
378
379
## Performance Considerations
380
381
- MDC operations are thread-local and generally very fast
382
- Consider the overhead of frequent put/remove operations in tight loops
383
- Use try-finally blocks to ensure cleanup
384
- Be mindful of memory usage with long-running threads
385
386
```java
387
// Efficient MDC usage
388
public void efficientLogging() {
389
// Set context once for multiple operations
390
MDC.put("operation", "bulk-processing");
391
392
try {
393
for (Item item : items) {
394
// Only update changing context
395
MDC.put("itemId", item.getId());
396
397
processItem(item);
398
399
// Remove only the changing context
400
MDC.remove("itemId");
401
}
402
} finally {
403
// Clean up operation context
404
MDC.remove("operation");
405
}
406
}
407
```