or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

core-logging.mdindex.mdmarkers.mdmessage-system.mdperformance-features.mdspi.mdstatus-system.mdthread-context.md

thread-context.mddocs/

0

# Thread Context Management

1

2

Per-thread context functionality providing both Map (MDC) and Stack (NDC) capabilities for request correlation, user tracking, and contextual logging across application layers.

3

4

## Capabilities

5

6

### ThreadContext Map Operations (MDC)

7

8

Key-value pairs associated with the current thread for request correlation and contextual information.

9

10

```java { .api }

11

/**

12

* Thread-local Map operations for Mapped Diagnostic Context (MDC)

13

*/

14

public final class ThreadContext {

15

// Map operations

16

/** Put a key-value pair in the thread context */

17

public static void put(String key, String value);

18

19

/** Put a key-value pair only if the key doesn't exist */

20

public static void putIfNull(String key, String value);

21

22

/** Put all entries from a map into the thread context */

23

public static void putAll(Map<String, String> contextMap);

24

25

/** Get value by key from thread context */

26

public static String get(String key);

27

28

/** Remove a key from the thread context */

29

public static void remove(String key);

30

31

/** Remove multiple keys from the thread context */

32

public static void removeAll(Iterable<String> keys);

33

34

/** Clear the entire thread context map */

35

public static void clearMap();

36

37

/** Get a mutable copy of the current thread context */

38

public static Map<String, String> getContext();

39

40

/** Get an immutable view of the current thread context */

41

public static Map<String, String> getImmutableContext();

42

43

/** Check if a key exists in the thread context */

44

public static boolean containsKey(String key);

45

46

/** Check if the thread context map is empty */

47

public static boolean isEmpty();

48

49

/** Empty immutable map constant */

50

public static final Map<String, String> EMPTY_MAP;

51

}

52

```

53

54

**Usage Examples:**

55

56

```java

57

private static final Logger logger = LogManager.getLogger();

58

59

public void demonstrateThreadContextMap() {

60

// Basic key-value operations

61

ThreadContext.put("userId", "12345");

62

ThreadContext.put("sessionId", "abc-def-ghi");

63

ThreadContext.put("requestId", UUID.randomUUID().toString());

64

65

logger.info("Processing user request"); // Logs will include context

66

67

// Conditional put

68

ThreadContext.putIfNull("environment", "production");

69

70

// Bulk operations

71

Map<String, String> contextData = new HashMap<>();

72

contextData.put("correlationId", "corr-123");

73

contextData.put("operation", "userLogin");

74

ThreadContext.putAll(contextData);

75

76

// Reading context

77

String userId = ThreadContext.get("userId");

78

logger.debug("Current user ID: {}", userId);

79

80

// Context existence check

81

if (ThreadContext.containsKey("adminMode")) {

82

logger.info("Admin mode is active");

83

}

84

85

// Get full context for processing

86

Map<String, String> fullContext = ThreadContext.getContext();

87

processWithContext(fullContext);

88

89

// Cleanup

90

ThreadContext.remove("operation");

91

ThreadContext.removeAll(Arrays.asList("userId", "sessionId"));

92

ThreadContext.clearMap(); // Clear everything

93

}

94

95

// Web request example

96

@RestController

97

public class UserController {

98

private static final Logger logger = LogManager.getLogger();

99

100

@PostMapping("/users/{userId}/profile")

101

public ResponseEntity<UserProfile> updateProfile(

102

@PathVariable String userId,

103

@RequestBody ProfileRequest request,

104

HttpServletRequest httpRequest) {

105

106

// Set up context for entire request

107

ThreadContext.put("userId", userId);

108

ThreadContext.put("requestId", httpRequest.getHeader("X-Request-ID"));

109

ThreadContext.put("endpoint", "/users/{id}/profile");

110

ThreadContext.put("method", "POST");

111

112

try {

113

logger.info("Starting profile update"); // Includes all context

114

115

UserProfile profile = userService.updateProfile(userId, request);

116

117

logger.info("Profile update completed successfully");

118

return ResponseEntity.ok(profile);

119

120

} catch (Exception e) {

121

logger.error("Profile update failed", e); // Context included in error

122

return ResponseEntity.status(500).build();

123

124

} finally {

125

ThreadContext.clearMap(); // Clean up after request

126

}

127

}

128

}

129

```

130

131

### ThreadContext Stack Operations (NDC)

132

133

Stack-based Nested Diagnostic Context for tracking hierarchical execution flow.

134

135

```java { .api }

136

/**

137

* Thread-local Stack operations for Nested Diagnostic Context (NDC)

138

*/

139

public final class ThreadContext {

140

// Stack operations

141

/** Push a message onto the thread context stack */

142

public static void push(String message);

143

144

/** Push a formatted message onto the stack */

145

public static void push(String message, Object... args);

146

147

/** Pop the top message from the stack and return it */

148

public static String pop();

149

150

/** Peek at the top message without removing it */

151

public static String peek();

152

153

/** Clear the entire stack */

154

public static void clearStack();

155

156

/** Get the current stack depth */

157

public static int getDepth();

158

159

/** Trim the stack to the specified depth */

160

public static void trim(int depth);

161

162

/** Create a copy of the current stack */

163

public static ContextStack cloneStack();

164

165

/** Get an immutable view of the current stack */

166

public static ContextStack getImmutableStack();

167

168

/** Empty immutable stack constant */

169

public static final ThreadContextStack EMPTY_STACK;

170

171

/**

172

* Stack interface extending Collection<String>

173

*/

174

public interface ContextStack extends Collection<String>, Serializable {

175

String pop();

176

String peek();

177

void push(String message);

178

int getDepth();

179

List<String> asList();

180

void trim(int depth);

181

ContextStack copy();

182

}

183

}

184

```

185

186

**Usage Examples:**

187

188

```java

189

private static final Logger logger = LogManager.getLogger();

190

191

public void demonstrateThreadContextStack() {

192

// Push operation context

193

ThreadContext.push("userService");

194

logger.info("Starting user operations"); // Stack context included

195

196

try {

197

ThreadContext.push("validateUser");

198

validateUser("12345");

199

ThreadContext.pop(); // Remove validateUser

200

201

ThreadContext.push("updateProfile");

202

updateUserProfile("12345");

203

ThreadContext.pop(); // Remove updateProfile

204

205

} finally {

206

ThreadContext.pop(); // Remove userService

207

}

208

}

209

210

// Nested operation tracking

211

public class OrderProcessor {

212

private static final Logger logger = LogManager.getLogger();

213

214

public void processOrder(String orderId) {

215

ThreadContext.push("processOrder[" + orderId + "]");

216

logger.info("Starting order processing");

217

218

try {

219

validateOrder(orderId);

220

processPayment(orderId);

221

fulfillOrder(orderId);

222

223

logger.info("Order processing completed");

224

} finally {

225

ThreadContext.pop();

226

}

227

}

228

229

private void validateOrder(String orderId) {

230

ThreadContext.push("validateOrder");

231

logger.debug("Validating order");

232

233

try {

234

// Validation logic

235

checkInventory(orderId);

236

checkCustomer(orderId);

237

} finally {

238

ThreadContext.pop();

239

}

240

}

241

242

private void checkInventory(String orderId) {

243

ThreadContext.push("checkInventory");

244

logger.trace("Checking inventory availability");

245

246

try {

247

// Inventory check logic

248

} finally {

249

ThreadContext.pop();

250

}

251

}

252

}

253

254

// Stack manipulation

255

public void demonstrateStackOperations() {

256

ThreadContext.push("operation1");

257

ThreadContext.push("operation2");

258

ThreadContext.push("operation3");

259

260

logger.info("Current depth: {}", ThreadContext.getDepth()); // 3

261

logger.info("Top operation: {}", ThreadContext.peek()); // operation3

262

263

// Trim to specific depth

264

ThreadContext.trim(1); // Keeps only operation1

265

266

// Clone stack for processing

267

ContextStack stackCopy = ThreadContext.cloneStack();

268

processStackCopy(stackCopy);

269

270

ThreadContext.clearStack();

271

}

272

```

273

274

### CloseableThreadContext

275

276

Auto-closeable ThreadContext for try-with-resources usage ensuring automatic cleanup.

277

278

```java { .api }

279

/**

280

* Auto-closeable ThreadContext for automatic cleanup

281

*/

282

public class CloseableThreadContext {

283

/** Put a key-value pair with automatic cleanup */

284

public static Instance put(String key, String value);

285

286

/** Put multiple key-value pairs with automatic cleanup */

287

public static Instance putAll(Map<String, String> values);

288

289

/** Push a message with automatic cleanup */

290

public static Instance push(String message);

291

292

/** Push multiple messages with automatic cleanup */

293

public static Instance pushAll(List<String> messages);

294

295

/**

296

* Auto-closeable instance for cleanup

297

*/

298

public static class Instance implements AutoCloseable {

299

/** Restore previous thread context state */

300

@Override

301

public void close();

302

}

303

}

304

```

305

306

**Usage Examples:**

307

308

```java

309

private static final Logger logger = LogManager.getLogger();

310

311

public void demonstrateCloseableThreadContext() {

312

// Automatic cleanup with try-with-resources

313

try (CloseableThreadContext.Instance ctc =

314

CloseableThreadContext.put("operation", "userUpdate")) {

315

316

logger.info("Starting user update"); // Includes operation context

317

updateUser();

318

logger.info("User update completed");

319

320

} // Context automatically restored here

321

322

// Multiple context values

323

try (CloseableThreadContext.Instance ctc =

324

CloseableThreadContext.putAll(Map.of(

325

"userId", "12345",

326

"sessionId", "abc-def",

327

"operation", "profileUpdate"))) {

328

329

processProfileUpdate();

330

331

} // All context automatically cleaned up

332

333

// Stack operations with cleanup

334

try (CloseableThreadContext.Instance ctc =

335

CloseableThreadContext.push("batchProcessing")) {

336

337

processBatch();

338

339

} // Stack automatically popped

340

}

341

342

// Web filter example

343

@Component

344

public class RequestContextFilter implements Filter {

345

346

@Override

347

public void doFilter(ServletRequest request, ServletResponse response,

348

FilterChain chain) throws IOException, ServletException {

349

350

HttpServletRequest httpRequest = (HttpServletRequest) request;

351

352

// Set up context for entire request with automatic cleanup

353

try (CloseableThreadContext.Instance ctc =

354

CloseableThreadContext.putAll(Map.of(

355

"requestId", httpRequest.getHeader("X-Request-ID"),

356

"userAgent", httpRequest.getHeader("User-Agent"),

357

"remoteAddr", httpRequest.getRemoteAddr(),

358

"method", httpRequest.getMethod(),

359

"uri", httpRequest.getRequestURI()))) {

360

361

chain.doFilter(request, response);

362

363

} // Context automatically cleaned up after request

364

}

365

}

366

367

// Nested closeable contexts

368

public void nestedContextExample() {

369

try (CloseableThreadContext.Instance outerCtc =

370

CloseableThreadContext.put("service", "userService")) {

371

372

logger.info("Service level logging");

373

374

try (CloseableThreadContext.Instance innerCtc =

375

CloseableThreadContext.put("operation", "createUser")) {

376

377

logger.info("Operation level logging"); // Both contexts present

378

createUser();

379

380

} // operation context cleaned up

381

382

logger.info("Back to service level"); // Only service context present

383

384

} // service context cleaned up

385

}

386

```

387

388

### Context Inheritance and Threading

389

390

Understanding how ThreadContext behaves across thread boundaries and async operations.

391

392

```java { .api }

393

// ThreadContext behavior in different threading scenarios

394

public class ThreadContextBehavior {

395

private static final Logger logger = LogManager.getLogger();

396

397

public void demonstrateThreadBehavior() {

398

// Set context in main thread

399

ThreadContext.put("mainThread", "value1");

400

logger.info("Main thread logging"); // Includes context

401

402

// Context is NOT inherited by new threads

403

new Thread(() -> {

404

logger.info("New thread logging"); // NO context from main thread

405

406

// Each thread has its own context

407

ThreadContext.put("workerThread", "value2");

408

logger.info("Worker thread with context");

409

}).start();

410

411

// Main thread still has its context

412

logger.info("Back in main thread"); // Still includes mainThread context

413

}

414

415

// Manual context transfer for async operations

416

public CompletableFuture<String> asyncWithContext() {

417

// Capture context in current thread

418

Map<String, String> contextMap = ThreadContext.getContext();

419

ContextStack contextStack = ThreadContext.cloneStack();

420

421

return CompletableFuture.supplyAsync(() -> {

422

// Restore context in async thread

423

try (CloseableThreadContext.Instance ctc =

424

CloseableThreadContext.putAll(contextMap)) {

425

426

// Restore stack manually if needed

427

for (String stackItem : contextStack.asList()) {

428

ThreadContext.push(stackItem);

429

}

430

431

try {

432

logger.info("Async operation with context");

433

return performAsyncOperation();

434

} finally {

435

ThreadContext.clearStack();

436

}

437

}

438

});

439

}

440

}

441

```

442

443

**Usage Examples:**

444

445

```java

446

// Executor service with context propagation

447

public class ContextAwareExecutor {

448

private final ExecutorService executor = Executors.newFixedThreadPool(10);

449

private static final Logger logger = LogManager.getLogger();

450

451

public <T> CompletableFuture<T> executeWithContext(Supplier<T> task) {

452

// Capture current thread context

453

Map<String, String> contextMap = ThreadContext.getContext();

454

ContextStack contextStack = ThreadContext.cloneStack();

455

456

return CompletableFuture.supplyAsync(() -> {

457

try (CloseableThreadContext.Instance ctc =

458

CloseableThreadContext.putAll(contextMap)) {

459

460

// Restore stack

461

contextStack.asList().forEach(ThreadContext::push);

462

463

try {

464

return task.get();

465

} finally {

466

ThreadContext.clearStack();

467

}

468

}

469

}, executor);

470

}

471

}

472

473

// Spring async method with context

474

@Service

475

public class AsyncService {

476

private static final Logger logger = LogManager.getLogger();

477

478

@Async

479

public CompletableFuture<String> processAsync(String data) {

480

// Context must be manually propagated in @Async methods

481

logger.info("Async processing started"); // May not have context

482

483

try {

484

String result = performProcessing(data);

485

logger.info("Async processing completed");

486

return CompletableFuture.completedFuture(result);

487

} catch (Exception e) {

488

logger.error("Async processing failed", e);

489

throw e;

490

}

491

}

492

}

493

```