0
# Context Management
1
2
Raven provides thread-local context management for maintaining user information, tags, extra data, and breadcrumbs that persist throughout the application lifecycle and are automatically included with events.
3
4
## Capabilities
5
6
### Context Class
7
8
Thread-local context storage for tags, extra data, and breadcrumbs.
9
10
```python { .api }
11
from raven.context import Context
12
13
class Context(local, Mapping, Iterable):
14
def __init__(self, client=None):
15
"""
16
Initialize context with empty data storage.
17
18
Parameters:
19
- client (Client): Associated Sentry client instance
20
"""
21
22
def activate(self, sticky=False):
23
"""
24
Activate context for current thread.
25
26
Parameters:
27
- sticky (bool): Keep context active after deactivation
28
"""
29
30
def deactivate(self):
31
"""Deactivate context for current thread."""
32
33
def merge(self, data, activate=True):
34
"""
35
Merge data into context.
36
37
Parameters:
38
- data (dict): Context data to merge
39
- activate (bool): Activate context after merging
40
41
Returns:
42
Context: Self for chaining
43
"""
44
45
def clear(self, deactivate=None):
46
"""
47
Clear context data.
48
49
Parameters:
50
- deactivate (bool): Whether to deactivate context
51
"""
52
53
def get(self):
54
"""
55
Get current context data.
56
57
Returns:
58
dict: Current context data
59
"""
60
61
def set(self, data):
62
"""
63
Set context data, replacing existing data.
64
65
Parameters:
66
- data (dict): Context data to set
67
"""
68
69
def __enter__(self):
70
"""Context manager entry - activates context."""
71
72
def __exit__(self, exc_type, exc_value, tb):
73
"""Context manager exit - deactivates context."""
74
75
def __getitem__(self, key):
76
"""Get context data by key (dict-like access)."""
77
78
def __iter__(self):
79
"""Iterate over context keys."""
80
81
def __len__(self):
82
"""Get number of context items."""
83
```
84
85
### Context Utilities
86
87
Functions for working with active contexts across threads.
88
89
```python { .api }
90
from raven.context import get_active_contexts
91
92
def get_active_contexts():
93
"""
94
Get all active contexts for current thread.
95
96
Returns:
97
list: List of active Context instances
98
"""
99
```
100
101
### Client Context Methods
102
103
Client methods for managing context data that gets included with events.
104
105
```python { .api }
106
# From Client class
107
def user_context(self, data):
108
"""
109
Set user context information.
110
111
Parameters:
112
- data (dict): User data including 'id', 'username', 'email', 'ip_address'
113
"""
114
115
def tags_context(self, data):
116
"""
117
Set tags for event categorization and filtering.
118
119
Parameters:
120
- data (dict): Tag key-value pairs
121
"""
122
123
def extra_context(self, data):
124
"""
125
Set extra context data for debugging.
126
127
Parameters:
128
- data (dict): Additional context information
129
"""
130
131
def http_context(self, data):
132
"""
133
Set HTTP request context.
134
135
Parameters:
136
- data (dict): HTTP context including 'url', 'method', 'headers', 'query_string'
137
"""
138
```
139
140
## Usage Examples
141
142
### Basic Context Usage
143
144
```python
145
from raven import Client
146
from raven.context import Context
147
148
client = Client('https://your-dsn@sentry.io/project-id')
149
150
# Set user context
151
client.user_context({
152
'id': 123,
153
'username': 'john_doe',
154
'email': 'john@example.com',
155
'ip_address': '192.168.1.1'
156
})
157
158
# Set tags for categorization
159
client.tags_context({
160
'environment': 'production',
161
'version': '1.2.3',
162
'feature_flag': 'new_checkout',
163
'user_type': 'premium'
164
})
165
166
# Set extra debugging information
167
client.extra_context({
168
'request_id': 'req_abc123',
169
'session_id': 'sess_xyz789',
170
'user_agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64)',
171
'referrer': 'https://example.com/products'
172
})
173
174
# This exception will include all the context above
175
try:
176
process_checkout()
177
except Exception:
178
client.captureException()
179
```
180
181
### Request-Scoped Context
182
183
```python
184
from flask import Flask, request
185
from raven.contrib.flask import Sentry
186
187
app = Flask(__name__)
188
sentry = Sentry(app)
189
190
@app.before_request
191
def set_request_context():
192
# Set context for each request
193
sentry.client.user_context({
194
'ip_address': request.remote_addr
195
})
196
197
sentry.client.tags_context({
198
'endpoint': request.endpoint,
199
'method': request.method
200
})
201
202
sentry.client.http_context({
203
'url': request.url,
204
'method': request.method,
205
'headers': dict(request.headers),
206
'query_string': request.query_string.decode()
207
})
208
209
@app.route('/api/users/<int:user_id>')
210
def get_user(user_id):
211
# Add user-specific context
212
sentry.client.user_context({'id': user_id})
213
sentry.client.tags_context({'operation': 'get_user'})
214
215
try:
216
user = User.get(user_id)
217
return jsonify(user.to_dict())
218
except UserNotFound:
219
# This will include all the request context
220
sentry.captureException()
221
return jsonify({'error': 'User not found'}), 404
222
```
223
224
### Manual Context Management
225
226
```python
227
from raven.context import Context
228
229
def process_user_request(user_id, request_data):
230
context = Context()
231
232
# Activate context for this operation
233
context.activate()
234
235
try:
236
# Merge user-specific data
237
context.merge({
238
'user': {'id': user_id},
239
'tags': {'operation': 'user_request'},
240
'extra': {'request_size': len(str(request_data))}
241
})
242
243
# Process request - any errors will include context
244
result = complex_operation(request_data)
245
246
return result
247
248
finally:
249
# Clean up context
250
context.deactivate()
251
```
252
253
### Context Inheritance in Threads
254
255
```python
256
import threading
257
from raven import Client
258
from raven.context import Context
259
260
client = Client('https://your-dsn@sentry.io/project-id')
261
262
def worker_thread(task_id, user_id):
263
# Create context for this thread
264
context = Context()
265
context.activate()
266
267
# Set thread-specific context
268
context.merge({
269
'user': {'id': user_id},
270
'tags': {
271
'task_id': task_id,
272
'thread': threading.current_thread().name
273
},
274
'extra': {'start_time': time.time()}
275
})
276
277
try:
278
# This work will have the thread context
279
result = process_task(task_id)
280
client.captureMessage(f'Task {task_id} completed successfully')
281
return result
282
283
except Exception:
284
# Exception will include thread context
285
client.captureException()
286
raise
287
finally:
288
context.deactivate()
289
290
# Main thread context
291
client.user_context({'id': 'system'})
292
client.tags_context({'component': 'task_manager'})
293
294
# Spawn worker threads
295
threads = []
296
for i in range(5):
297
t = threading.Thread(
298
target=worker_thread,
299
args=(f'task_{i}', f'user_{i}')
300
)
301
threads.append(t)
302
t.start()
303
304
for t in threads:
305
t.join()
306
```
307
308
### Context Middleware Pattern
309
310
```python
311
from raven import Client
312
313
class SentryContextMiddleware:
314
def __init__(self, client):
315
self.client = client
316
317
def __call__(self, environ, start_response):
318
# Extract request information
319
request_method = environ.get('REQUEST_METHOD')
320
path_info = environ.get('PATH_INFO')
321
query_string = environ.get('QUERY_STRING')
322
remote_addr = environ.get('REMOTE_ADDR')
323
324
# Set request context
325
self.client.http_context({
326
'url': f"{path_info}?{query_string}" if query_string else path_info,
327
'method': request_method,
328
'headers': self._get_headers(environ),
329
'query_string': query_string,
330
})
331
332
self.client.tags_context({
333
'path': path_info,
334
'method': request_method
335
})
336
337
self.client.extra_context({
338
'remote_addr': remote_addr,
339
'server_name': environ.get('SERVER_NAME'),
340
'server_port': environ.get('SERVER_PORT')
341
})
342
343
try:
344
return self.app(environ, start_response)
345
except Exception:
346
self.client.captureException()
347
raise
348
349
def _get_headers(self, environ):
350
headers = {}
351
for key, value in environ.items():
352
if key.startswith('HTTP_'):
353
header_name = key[5:].replace('_', '-').title()
354
headers[header_name] = value
355
return headers
356
357
# Use with WSGI app
358
app = get_wsgi_app()
359
client = Client('https://your-dsn@sentry.io/project-id')
360
app = SentryContextMiddleware(client)(app)
361
```
362
363
### Dynamic Context Updates
364
365
```python
366
from raven import Client
367
368
class ContextualClient:
369
def __init__(self, dsn):
370
self.client = Client(dsn)
371
self._context_stack = []
372
373
def push_context(self, **context_data):
374
"""Push new context data onto stack."""
375
self._context_stack.append(context_data)
376
self._update_client_context()
377
378
def pop_context(self):
379
"""Pop most recent context data from stack."""
380
if self._context_stack:
381
self._context_stack.pop()
382
self._update_client_context()
383
384
def _update_client_context(self):
385
"""Update client with merged context from stack."""
386
merged_context = {}
387
for context in self._context_stack:
388
for key, value in context.items():
389
if key in merged_context and isinstance(value, dict):
390
merged_context[key].update(value)
391
else:
392
merged_context[key] = value
393
394
# Apply to client
395
if 'user' in merged_context:
396
self.client.user_context(merged_context['user'])
397
if 'tags' in merged_context:
398
self.client.tags_context(merged_context['tags'])
399
if 'extra' in merged_context:
400
self.client.extra_context(merged_context['extra'])
401
402
def capture_exception(self, **kwargs):
403
return self.client.captureException(**kwargs)
404
405
def capture_message(self, message, **kwargs):
406
return self.client.captureMessage(message, **kwargs)
407
408
# Usage
409
contextual_client = ContextualClient('https://your-dsn@sentry.io/project-id')
410
411
# Set base context
412
contextual_client.push_context(
413
user={'id': 123},
414
tags={'component': 'auth'}
415
)
416
417
# Add operation-specific context
418
contextual_client.push_context(
419
tags={'operation': 'login'},
420
extra={'login_method': 'password'}
421
)
422
423
try:
424
authenticate_user()
425
except AuthError:
426
# Captures with merged context
427
contextual_client.capture_exception()
428
429
# Remove operation context, keep base context
430
contextual_client.pop_context()
431
```
432
433
### Context-Aware Decorators
434
435
```python
436
from functools import wraps
437
from raven import Client
438
439
client = Client('https://your-dsn@sentry.io/project-id')
440
441
def with_context(**context_data):
442
"""Decorator to add context to function execution."""
443
def decorator(func):
444
@wraps(func)
445
def wrapper(*args, **kwargs):
446
# Store original context
447
original_user = getattr(client.context, 'user', {})
448
original_tags = getattr(client.context, 'tags', {})
449
original_extra = getattr(client.context, 'extra', {})
450
451
try:
452
# Apply decorator context
453
if 'user' in context_data:
454
client.user_context(context_data['user'])
455
if 'tags' in context_data:
456
client.tags_context(context_data['tags'])
457
if 'extra' in context_data:
458
client.extra_context(context_data['extra'])
459
460
return func(*args, **kwargs)
461
462
except Exception:
463
client.captureException()
464
raise
465
finally:
466
# Restore original context
467
client.user_context(original_user)
468
client.tags_context(original_tags)
469
client.extra_context(original_extra)
470
471
return wrapper
472
return decorator
473
474
@with_context(
475
tags={'operation': 'payment'},
476
extra={'processor': 'stripe'}
477
)
478
def process_payment(amount, user_id):
479
# Add function-specific context
480
client.user_context({'id': user_id})
481
client.extra_context({'amount': amount})
482
483
# Any errors here will include all context
484
return stripe.charge(amount)
485
```