or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

bound-loggers.mdconfiguration.mdcontext-management.mddevelopment-tools.mdexception-handling.mdindex.mdlogger-creation.mdoutput-loggers.mdprocessors.mdstdlib-integration.mdtesting.md

context-management.mddocs/

0

# Context Management

1

2

Thread-safe context binding using contextvars and deprecated thread-local storage, enabling global context that persists across function calls and async boundaries. Context management allows you to maintain structured data that automatically appears in all log entries within a given scope.

3

4

## Capabilities

5

6

### Context Variables (Recommended)

7

8

Modern context management using Python's contextvars module for thread-safe and async-safe context storage.

9

10

```python { .api }

11

def get_contextvars() -> dict[str, Any]:

12

"""

13

Return copy of structlog-specific context-local context.

14

15

Returns:

16

dict: Current context variables as key-value pairs

17

"""

18

19

def get_merged_contextvars(bound_logger) -> dict[str, Any]:

20

"""

21

Return merged context-local and bound logger context.

22

23

Args:

24

bound_logger: Bound logger instance

25

26

Returns:

27

dict: Merged context from contextvars and bound logger

28

"""

29

30

def merge_contextvars(logger, method_name, event_dict) -> EventDict:

31

"""

32

Processor that merges global context-local context into event dict.

33

34

Use this processor to automatically include context variables

35

in all log entries.

36

37

Args:

38

logger: Logger instance

39

method_name (str): Logger method name

40

event_dict (dict): Event dictionary

41

42

Returns:

43

dict: Event dictionary with context variables merged in

44

"""

45

46

def clear_contextvars() -> None:

47

"""Clear all structlog context-local context variables."""

48

49

def bind_contextvars(**kw) -> Mapping[str, contextvars.Token[Any]]:

50

"""

51

Put keys and values into context-local context.

52

53

Args:

54

**kw: Key-value pairs to bind to context

55

56

Returns:

57

dict: Mapping of keys to context variable tokens for later reset

58

"""

59

60

def reset_contextvars(**kw) -> None:

61

"""

62

Reset contextvars using tokens returned from bind_contextvars.

63

64

Args:

65

**kw: Mapping of keys to tokens from previous bind_contextvars call

66

"""

67

68

def unbind_contextvars(*keys) -> None:

69

"""

70

Remove keys from context-local context.

71

72

Args:

73

*keys: Context variable keys to remove

74

"""

75

76

def bound_contextvars(**kw) -> Generator[None, None, None]:

77

"""

78

Context manager for temporary context binding.

79

80

Binds context variables for the duration of the with block,

81

then automatically restores the previous state.

82

83

Args:

84

**kw: Key-value pairs to bind temporarily

85

86

Yields:

87

None

88

"""

89

```

90

91

### Context Variable Constants

92

93

```python { .api }

94

STRUCTLOG_KEY_PREFIX: str

95

"""Prefix used for structlog context variable names."""

96

```

97

98

### Thread-Local Context (Deprecated)

99

100

Legacy thread-local context management. Use contextvars instead for new code.

101

102

```python { .api }

103

def wrap_dict(dict_class) -> type[Context]:

104

"""

105

Wrap a dict class to be used for thread-local storage.

106

107

Args:

108

dict_class: Dictionary class to wrap

109

110

Returns:

111

type: Wrapped dictionary class with thread-local behavior

112

"""

113

114

def as_immutable(logger) -> logger:

115

"""

116

Extract context from thread-local storage into an immutable logger.

117

118

Args:

119

logger: Logger instance

120

121

Returns:

122

Logger with context extracted from thread-local storage

123

"""

124

125

def tmp_bind(logger, **tmp_values) -> Generator[logger, None, None]:

126

"""

127

Context manager for temporarily binding thread-local context.

128

129

Args:

130

logger: Logger instance

131

**tmp_values: Temporary values to bind

132

133

Yields:

134

Logger: Logger with temporary context bound

135

"""

136

137

def get_threadlocal() -> Context:

138

"""Get copy of current thread-local context."""

139

140

def get_merged_threadlocal(bound_logger) -> Context:

141

"""

142

Get merged thread-local and bound logger context.

143

144

Args:

145

bound_logger: Bound logger instance

146

147

Returns:

148

dict: Merged context

149

"""

150

151

def merge_threadlocal(logger, method_name, event_dict) -> EventDict:

152

"""

153

Processor for merging thread-local context into event dict.

154

155

Args:

156

logger: Logger instance

157

method_name (str): Logger method name

158

event_dict (dict): Event dictionary

159

160

Returns:

161

dict: Event dictionary with thread-local context merged

162

"""

163

164

def clear_threadlocal() -> None:

165

"""Clear all thread-local context."""

166

167

def bind_threadlocal(**kw) -> None:

168

"""

169

Bind key-value pairs to thread-local context.

170

171

Args:

172

**kw: Key-value pairs to bind

173

"""

174

175

def unbind_threadlocal(*keys) -> None:

176

"""

177

Remove keys from thread-local context.

178

179

Args:

180

*keys: Keys to remove

181

"""

182

183

def bound_threadlocal(**kw) -> Generator[None, None, None]:

184

"""

185

Context manager for temporary thread-local context binding.

186

187

Args:

188

**kw: Key-value pairs to bind temporarily

189

190

Yields:

191

None

192

"""

193

```

194

195

## Usage Examples

196

197

### Basic Context Variables Usage

198

199

```python

200

import structlog

201

from structlog import contextvars, processors

202

203

# Configure structlog with context variables

204

structlog.configure(

205

processors=[

206

contextvars.merge_contextvars, # Include context vars in logs

207

processors.TimeStamper(),

208

processors.JSONRenderer()

209

],

210

wrapper_class=structlog.BoundLogger,

211

)

212

213

logger = structlog.get_logger()

214

215

# Bind context variables globally

216

contextvars.bind_contextvars(

217

service="api",

218

version="1.0.0"

219

)

220

221

# All subsequent logs include the context

222

logger.info("Service started") # Includes service and version

223

logger.info("Processing request", request_id="req-123")

224

```

225

226

### Temporary Context Binding

227

228

```python

229

import structlog

230

from structlog import contextvars

231

232

structlog.configure(

233

processors=[

234

contextvars.merge_contextvars,

235

structlog.processors.JSONRenderer()

236

],

237

wrapper_class=structlog.BoundLogger,

238

)

239

240

logger = structlog.get_logger()

241

242

# Set some global context

243

contextvars.bind_contextvars(service="user-service")

244

245

# Temporarily add context for a block

246

with contextvars.bound_contextvars(user_id=123, operation="update"):

247

logger.info("Starting user update") # Includes user_id and operation

248

logger.info("Validation passed") # Includes user_id and operation

249

250

# Outside the block, temporary context is gone

251

logger.info("Update completed") # Only includes service

252

```

253

254

### Request-Scoped Context

255

256

```python

257

import structlog

258

from structlog import contextvars

259

import uuid

260

261

def process_request(request_data):

262

"""Process a request with request-scoped context."""

263

264

# Generate request ID and bind context

265

request_id = str(uuid.uuid4())

266

267

with contextvars.bound_contextvars(

268

request_id=request_id,

269

user_id=request_data.get("user_id"),

270

endpoint=request_data.get("endpoint")

271

):

272

logger = structlog.get_logger()

273

274

logger.info("Request started")

275

276

# All nested function calls will include the context

277

validate_request(request_data)

278

result = perform_operation(request_data)

279

280

logger.info("Request completed", result_count=len(result))

281

return result

282

283

def validate_request(data):

284

"""Validation function - automatically gets request context."""

285

logger = structlog.get_logger()

286

logger.info("Validating request", fields=list(data.keys()))

287

288

def perform_operation(data):

289

"""Business logic - automatically gets request context."""

290

logger = structlog.get_logger()

291

logger.info("Performing operation", operation_type=data.get("type"))

292

return ["result1", "result2"]

293

```

294

295

### Context Management Across Async Calls

296

297

```python

298

import asyncio

299

import structlog

300

from structlog import contextvars

301

302

async def handle_async_request(user_id, task_type):

303

"""Handle async request with context that spans async boundaries."""

304

305

# Bind context for this async task

306

with contextvars.bound_contextvars(

307

user_id=user_id,

308

task_type=task_type,

309

correlation_id=str(uuid.uuid4())

310

):

311

logger = structlog.get_logger()

312

313

logger.info("Async task started")

314

315

# Context is preserved across await calls

316

result = await perform_async_operation()

317

318

logger.info("Async task completed", result=result)

319

return result

320

321

async def perform_async_operation():

322

"""Async operation that inherits context."""

323

logger = structlog.get_logger()

324

325

logger.info("Starting async operation")

326

await asyncio.sleep(0.1) # Simulate async work

327

logger.info("Async operation completed")

328

329

return "success"

330

331

# Usage

332

async def main():

333

await handle_async_request(user_id=123, task_type="data_processing")

334

335

asyncio.run(main())

336

```

337

338

### Context Inspection and Management

339

340

```python

341

import structlog

342

from structlog import contextvars

343

344

structlog.configure(

345

processors=[

346

contextvars.merge_contextvars,

347

structlog.processors.JSONRenderer()

348

],

349

wrapper_class=structlog.BoundLogger,

350

)

351

352

# Bind some context

353

contextvars.bind_contextvars(

354

service="auth",

355

environment="production",

356

debug_mode=False

357

)

358

359

# Inspect current context

360

current_context = contextvars.get_contextvars()

361

print(f"Current context: {current_context}")

362

363

# Conditionally add more context

364

if current_context.get("environment") == "production":

365

contextvars.bind_contextvars(monitoring_enabled=True)

366

367

# Remove specific context

368

contextvars.unbind_contextvars("debug_mode")

369

370

# Clear all context

371

# contextvars.clear_contextvars()

372

```

373

374

### Token-Based Context Reset

375

376

```python

377

import structlog

378

from structlog import contextvars

379

380

# Bind context and get tokens

381

tokens = contextvars.bind_contextvars(

382

transaction_id="tx-123",

383

user_id=456

384

)

385

386

logger = structlog.get_logger()

387

logger.info("Transaction started")

388

389

# Later, reset specific context using tokens

390

contextvars.reset_contextvars(user_id=tokens["user_id"])

391

392

logger.info("User context reset")

393

```

394

395

### Mixed Context Sources

396

397

```python

398

import structlog

399

from structlog import contextvars

400

401

structlog.configure(

402

processors=[

403

contextvars.merge_contextvars,

404

structlog.processors.JSONRenderer()

405

],

406

wrapper_class=structlog.BoundLogger,

407

)

408

409

# Global context via contextvars

410

contextvars.bind_contextvars(service="api", version="2.0")

411

412

# Logger-specific context via bind()

413

logger = structlog.get_logger().bind(component="auth")

414

415

# Both contexts will be merged

416

logger.info("Authentication attempt", username="alice")

417

# Output includes: service, version, component, and username

418

419

# Get merged context from logger

420

bound_logger = structlog.get_logger().bind(session="abc-123")

421

merged = contextvars.get_merged_contextvars(bound_logger)

422

print(f"Merged context: {merged}")

423

```

424

425

### Error Handling with Context

426

427

```python

428

import structlog

429

from structlog import contextvars

430

431

def risky_operation():

432

"""Operation that might fail - context helps with debugging."""

433

434

with contextvars.bound_contextvars(

435

operation="data_processing",

436

batch_id="batch-456"

437

):

438

logger = structlog.get_logger()

439

440

try:

441

logger.info("Starting risky operation")

442

443

# Simulate error

444

raise ValueError("Something went wrong")

445

446

except Exception as e:

447

logger.error(

448

"Operation failed",

449

error_type=type(e).__name__,

450

error_message=str(e)

451

)

452

# Context variables (operation, batch_id) automatically included

453

raise

454

455

# The error log will include operation and batch_id for easier debugging

456

```