or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

fluent-logging.mdindex.mdmarker-logging.mdmarker-management.mdmdc-support.mdstandard-logging.md

mdc-support.mddocs/

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

```