0
# Breadcrumb System
1
2
Raven's breadcrumb system provides automatic and manual tracking of user actions and application state leading up to errors, creating a timeline of events that helps with debugging and understanding error context.
3
4
## Capabilities
5
6
### Breadcrumb Buffer
7
8
Core breadcrumb storage and management with size limits and processing.
9
10
```python { .api }
11
from raven.breadcrumbs import BreadcrumbBuffer
12
13
class BreadcrumbBuffer:
14
def __init__(self, limit=100, message_max_length=1024):
15
"""
16
Initialize breadcrumb buffer with configurable limits.
17
18
Parameters:
19
- limit (int): Maximum number of breadcrumbs to store
20
- message_max_length (int): Maximum length of breadcrumb messages
21
"""
22
23
def record(self, timestamp=None, level=None, message=None, category=None,
24
data=None, type=None, processor=None):
25
"""
26
Record a breadcrumb.
27
28
Parameters:
29
- timestamp (datetime): Breadcrumb timestamp (defaults to now)
30
- level (str): Breadcrumb level ('debug', 'info', 'warning', 'error', 'critical')
31
- message (str): Breadcrumb message
32
- category (str): Breadcrumb category
33
- data (dict): Additional breadcrumb data
34
- type (str): Breadcrumb type
35
- processor (callable): Custom breadcrumb processor
36
"""
37
38
def clear(self):
39
"""Clear all breadcrumbs from buffer."""
40
41
def get_buffer(self):
42
"""
43
Get processed breadcrumbs for event inclusion.
44
45
Returns:
46
list: List of processed breadcrumb dictionaries
47
"""
48
49
def format(self, result):
50
"""
51
Format breadcrumb data, applying length limits.
52
53
Parameters:
54
- result (dict): Raw breadcrumb data
55
56
Returns:
57
dict: Formatted breadcrumb data
58
"""
59
```
60
61
### Breadcrumb Deduplication
62
63
Prevent duplicate consecutive breadcrumbs.
64
65
```python { .api }
66
from raven.breadcrumbs import event_payload_considered_equal
67
68
def event_payload_considered_equal(a, b):
69
"""
70
Check if two breadcrumb payloads are considered equal for deduplication.
71
72
Parameters:
73
- a (dict): First breadcrumb payload
74
- b (dict): Second breadcrumb payload
75
76
Returns:
77
bool: True if payloads are considered equal
78
"""
79
```
80
81
### Global Breadcrumb Functions
82
83
Module-level functions for recording breadcrumbs across all active clients.
84
85
```python { .api }
86
from raven.breadcrumbs import record, record_breadcrumb
87
88
def record(**kwargs):
89
"""
90
Record breadcrumb for all active clients.
91
92
Parameters:
93
- message (str): Breadcrumb message
94
- timestamp (datetime): Breadcrumb timestamp
95
- level (str): Breadcrumb level
96
- category (str): Breadcrumb category
97
- data (dict): Additional breadcrumb data
98
- type (str): Breadcrumb type
99
- processor (callable): Custom breadcrumb processor
100
"""
101
102
def record_breadcrumb(type, **kwargs):
103
"""
104
Legacy breadcrumb recording function.
105
106
Parameters:
107
- type (str): Breadcrumb type
108
- **kwargs: Breadcrumb data
109
"""
110
```
111
112
### Automatic Breadcrumb Collection
113
114
Functions for enabling automatic breadcrumb collection from various sources.
115
116
```python { .api }
117
from raven.breadcrumbs import (
118
install_logging_hook, ignore_logger, register_logging_handler, hook_libraries
119
)
120
121
def install_logging_hook():
122
"""Install logging breadcrumb hook for automatic log capture."""
123
124
def ignore_logger(name_or_logger, allow_level=None):
125
"""
126
Ignore logger for breadcrumb collection.
127
128
Parameters:
129
- name_or_logger (str|Logger): Logger name or instance to ignore
130
- allow_level (int): Optional minimum level to still capture
131
"""
132
133
def register_logging_handler(callback):
134
"""
135
Register custom logging handler for breadcrumbs.
136
137
Parameters:
138
- callback (callable): Handler function for log records
139
"""
140
141
def hook_libraries(libraries):
142
"""
143
Hook libraries for automatic breadcrumb collection.
144
145
Parameters:
146
- libraries (list): List of library names to hook
147
"""
148
```
149
150
### Client Breadcrumb Methods
151
152
Client methods for breadcrumb management and recording.
153
154
```python { .api }
155
# From Client class
156
def captureBreadcrumb(self, message=None, timestamp=None, level=None,
157
category=None, data=None, type=None, processor=None):
158
"""
159
Record breadcrumb for this client.
160
161
Parameters:
162
- message (str): Breadcrumb message
163
- timestamp (datetime): Breadcrumb timestamp
164
- level (str): Breadcrumb level
165
- category (str): Breadcrumb category
166
- data (dict): Additional breadcrumb data
167
- type (str): Breadcrumb type
168
- processor (callable): Custom breadcrumb processor
169
"""
170
```
171
172
## Usage Examples
173
174
### Manual Breadcrumb Recording
175
176
```python
177
from raven import Client
178
179
client = Client('https://your-dsn@sentry.io/project-id')
180
181
def process_user_order():
182
# Record user action
183
client.captureBreadcrumb(
184
message='User started checkout process',
185
category='ui.click',
186
level='info',
187
data={'button_id': 'checkout-btn', 'cart_items': 3}
188
)
189
190
# Record API call
191
client.captureBreadcrumb(
192
message='Calling payment API',
193
category='http',
194
level='info',
195
data={'url': '/api/payments', 'method': 'POST'}
196
)
197
198
try:
199
payment_result = process_payment()
200
201
# Record successful operation
202
client.captureBreadcrumb(
203
message='Payment processed successfully',
204
category='payment',
205
level='info',
206
data={'transaction_id': payment_result.id}
207
)
208
209
except PaymentError as e:
210
# Record failure details
211
client.captureBreadcrumb(
212
message='Payment processing failed',
213
category='payment',
214
level='error',
215
data={'error_code': e.code, 'error_message': str(e)}
216
)
217
218
# Exception will include all breadcrumbs
219
client.captureException()
220
raise
221
```
222
223
### Automatic Logging Breadcrumbs
224
225
```python
226
from raven import Client
227
from raven.breadcrumbs import install_logging_hook, ignore_logger
228
import logging
229
230
client = Client('https://your-dsn@sentry.io/project-id')
231
232
# Enable automatic logging breadcrumbs
233
install_logging_hook()
234
235
# Ignore noisy loggers
236
ignore_logger('urllib3')
237
ignore_logger('requests.packages.urllib3')
238
239
# Configure logging
240
logging.basicConfig(level=logging.INFO)
241
logger = logging.getLogger(__name__)
242
243
def user_workflow():
244
logger.info('User login started')
245
246
try:
247
authenticate_user()
248
logger.info('User authenticated successfully')
249
250
load_user_data()
251
logger.info('User data loaded')
252
253
process_request()
254
logger.info('Request processed')
255
256
except AuthenticationError:
257
logger.error('Authentication failed')
258
client.captureException() # Includes log breadcrumbs
259
except Exception:
260
logger.exception('Unexpected error in user workflow')
261
client.captureException() # Includes log breadcrumbs
262
```
263
264
### Web Request Breadcrumbs
265
266
```python
267
from flask import Flask, request
268
from raven.contrib.flask import Sentry
269
270
app = Flask(__name__)
271
sentry = Sentry(app)
272
273
@app.before_request
274
def before_request():
275
# Record request start
276
sentry.client.captureBreadcrumb(
277
message=f'{request.method} {request.path}',
278
category='http.request',
279
level='info',
280
data={
281
'method': request.method,
282
'url': request.url,
283
'headers': dict(request.headers),
284
'query_string': request.query_string.decode()
285
}
286
)
287
288
@app.after_request
289
def after_request(response):
290
# Record response
291
sentry.client.captureBreadcrumb(
292
message=f'Response {response.status_code}',
293
category='http.response',
294
level='info' if response.status_code < 400 else 'warning',
295
data={
296
'status_code': response.status_code,
297
'content_length': response.content_length
298
}
299
)
300
return response
301
302
@app.route('/api/users/<int:user_id>')
303
def get_user(user_id):
304
# Record business logic steps
305
sentry.client.captureBreadcrumb(
306
message=f'Loading user {user_id}',
307
category='db.query',
308
level='info',
309
data={'user_id': user_id}
310
)
311
312
try:
313
user = User.query.get(user_id)
314
if not user:
315
sentry.client.captureBreadcrumb(
316
message='User not found',
317
category='business.logic',
318
level='warning',
319
data={'user_id': user_id}
320
)
321
return {'error': 'User not found'}, 404
322
323
return user.to_dict()
324
325
except DatabaseError:
326
# Exception includes all request breadcrumbs
327
sentry.captureException()
328
return {'error': 'Database error'}, 500
329
```
330
331
### Custom Breadcrumb Processors
332
333
```python
334
from raven import Client
335
import json
336
337
def sanitize_breadcrumb_data(data):
338
"""Remove sensitive data from breadcrumb data."""
339
if not isinstance(data, dict):
340
return data
341
342
sanitized = {}
343
for key, value in data.items():
344
if key.lower() in ('password', 'token', 'secret', 'key'):
345
sanitized[key] = '[FILTERED]'
346
elif isinstance(value, dict):
347
sanitized[key] = sanitize_breadcrumb_data(value)
348
else:
349
sanitized[key] = value
350
351
return sanitized
352
353
client = Client('https://your-dsn@sentry.io/project-id')
354
355
def api_call_breadcrumb(url, method, payload=None, response=None):
356
"""Record API call with sanitized data."""
357
breadcrumb_data = {
358
'url': url,
359
'method': method
360
}
361
362
if payload:
363
breadcrumb_data['payload'] = sanitize_breadcrumb_data(payload)
364
365
if response:
366
breadcrumb_data['response'] = {
367
'status': response.get('status'),
368
'size': len(str(response)) if response else 0
369
}
370
371
client.captureBreadcrumb(
372
message=f'{method} {url}',
373
category='api.call',
374
level='info',
375
data=breadcrumb_data,
376
processor=sanitize_breadcrumb_data
377
)
378
379
# Usage
380
api_call_breadcrumb(
381
'/api/auth/login',
382
'POST',
383
payload={'username': 'john', 'password': 'secret123'},
384
response={'status': 200, 'token': 'jwt_token_here'}
385
)
386
```
387
388
### Database Query Breadcrumbs
389
390
```python
391
from raven import Client
392
import time
393
394
client = Client('https://your-dsn@sentry.io/project-id')
395
396
class DatabaseLogger:
397
def __init__(self, client):
398
self.client = client
399
400
def log_query(self, query, params=None, duration=None):
401
# Sanitize query parameters
402
safe_params = self._sanitize_params(params) if params else None
403
404
self.client.captureBreadcrumb(
405
message=f'Database query: {query[:50]}...' if len(query) > 50 else query,
406
category='db.sql',
407
level='info',
408
data={
409
'query': query,
410
'params': safe_params,
411
'duration_ms': duration * 1000 if duration else None
412
}
413
)
414
415
def _sanitize_params(self, params):
416
if isinstance(params, (list, tuple)):
417
return [self._sanitize_value(p) for p in params]
418
elif isinstance(params, dict):
419
return {k: self._sanitize_value(v) for k, v in params.items()}
420
return params
421
422
def _sanitize_value(self, value):
423
if isinstance(value, str) and len(value) > 100:
424
return value[:97] + '...'
425
return value
426
427
db_logger = DatabaseLogger(client)
428
429
def get_user_orders(user_id):
430
start_time = time.time()
431
432
query = "SELECT * FROM orders WHERE user_id = %s ORDER BY created_at DESC"
433
434
try:
435
cursor.execute(query, (user_id,))
436
results = cursor.fetchall()
437
438
duration = time.time() - start_time
439
db_logger.log_query(query, (user_id,), duration)
440
441
return results
442
443
except DatabaseError:
444
duration = time.time() - start_time
445
db_logger.log_query(query, (user_id,), duration)
446
client.captureException() # Includes query breadcrumb
447
raise
448
```
449
450
### Breadcrumb Performance Optimization
451
452
```python
453
from raven import Client
454
from raven.breadcrumbs import BreadcrumbBuffer
455
456
class OptimizedBreadcrumbBuffer(BreadcrumbBuffer):
457
def __init__(self, max_buffer_size=100, max_data_size=1024):
458
super().__init__(max_buffer_size)
459
self.max_data_size = max_data_size
460
461
def record(self, **kwargs):
462
# Limit data size to prevent memory issues
463
if 'data' in kwargs and kwargs['data']:
464
kwargs['data'] = self._truncate_data(kwargs['data'])
465
466
# Skip duplicate consecutive breadcrumbs
467
if self._is_duplicate(kwargs):
468
return
469
470
super().record(**kwargs)
471
472
def _truncate_data(self, data):
473
if not isinstance(data, dict):
474
return data
475
476
truncated = {}
477
total_size = 0
478
479
for key, value in data.items():
480
value_str = str(value)
481
if total_size + len(value_str) > self.max_data_size:
482
truncated[key] = value_str[:self.max_data_size - total_size - 3] + '...'
483
break
484
485
truncated[key] = value
486
total_size += len(value_str)
487
488
return truncated
489
490
def _is_duplicate(self, new_breadcrumb):
491
if not self.buffer:
492
return False
493
494
last_breadcrumb = self.buffer[-1]
495
return (
496
last_breadcrumb.get('message') == new_breadcrumb.get('message') and
497
last_breadcrumb.get('category') == new_breadcrumb.get('category')
498
)
499
500
# Use optimized buffer
501
client = Client('https://your-dsn@sentry.io/project-id')
502
client.context.breadcrumbs = OptimizedBreadcrumbBuffer(max_buffer_size=50)
503
```
504
505
### Global Breadcrumb Recording
506
507
```python
508
from raven.breadcrumbs import record
509
510
# Record breadcrumbs globally (affects all active clients)
511
def track_user_action(action, details=None):
512
record(
513
message=f'User action: {action}',
514
category='user.action',
515
level='info',
516
data=details or {}
517
)
518
519
def track_system_event(event, level='info', details=None):
520
record(
521
message=f'System event: {event}',
522
category='system',
523
level=level,
524
data=details or {}
525
)
526
527
# Usage throughout application
528
track_user_action('login', {'username': 'john_doe'})
529
track_user_action('view_product', {'product_id': 123})
530
track_system_event('cache_miss', 'warning', {'key': 'user_123'})
531
track_system_event('background_job_completed', 'info', {'job_id': 'abc123'})
532
```