0
# Spans and Tracing
1
2
Manual span creation and management for tracking operations, creating distributed traces, and organizing observability data hierarchically. Spans represent units of work in distributed systems and provide the foundation for distributed tracing.
3
4
## Capabilities
5
6
### Span Creation
7
8
Create spans to track operations, measure performance, and organize related events hierarchically.
9
10
```python { .api }
11
def span(msg_template: str, /, *,
12
_tags: Sequence[str] | None = None,
13
_span_name: str | None = None,
14
_level: LevelName | int | None = None,
15
_links: Sequence = (),
16
**attributes) -> LogfireSpan:
17
"""
18
Create a span context manager for tracking an operation.
19
20
Parameters:
21
- msg_template: Message template with {} placeholders for attributes
22
- _tags: Optional sequence of tags to apply to this span
23
- _span_name: Custom span name (defaults to msg_template)
24
- _level: Log level for the span
25
- _links: Sequence of links to other spans
26
- **attributes: Key-value attributes for structured data and template substitution
27
28
Returns: LogfireSpan context manager
29
"""
30
```
31
32
**Usage Examples:**
33
34
```python
35
import logfire
36
37
# Basic span usage
38
with logfire.span('Processing user data', user_id=123):
39
# Work happens here, all logs are associated with this span
40
logfire.info('Validating user input')
41
# ... processing logic
42
logfire.info('Processing complete')
43
44
# Nested spans for hierarchical tracking
45
with logfire.span('HTTP Request', method='POST', endpoint='/users'):
46
with logfire.span('Authentication', user_id=123):
47
# Auth logic
48
pass
49
50
with logfire.span('Database Query', table='users'):
51
# DB query logic
52
pass
53
54
with logfire.span('Response Generation'):
55
# Response building
56
pass
57
58
# Custom span name and level
59
with logfire.span('Calculating metrics for {user_id}',
60
_span_name='metric_calculation',
61
_level='debug',
62
user_id=456, calculation_type='advanced'):
63
# Calculation logic
64
pass
65
```
66
67
### LogfireSpan Class
68
69
The LogfireSpan class provides methods for dynamically modifying spans during execution.
70
71
```python { .api }
72
class LogfireSpan:
73
"""
74
Context manager for spans with additional functionality.
75
"""
76
77
@property
78
def message_template(self) -> str:
79
"""The message template for this span (read-only)."""
80
81
@property
82
def tags(self) -> tuple[str, ...]:
83
"""Tuple of tags for this span (read/write)."""
84
85
@tags.setter
86
def tags(self, value: Sequence[str]) -> None: ...
87
88
@property
89
def message(self) -> str:
90
"""The formatted message for this span (read/write)."""
91
92
@message.setter
93
def message(self, value: str) -> None: ...
94
95
def set_attribute(self, key: str, value: Any) -> None:
96
"""
97
Set an attribute on the span.
98
99
Parameters:
100
- key: Attribute key
101
- value: Attribute value (will be converted to string if needed)
102
"""
103
104
def set_attributes(self, attributes: dict[str, Any]) -> None:
105
"""
106
Set multiple attributes on the span.
107
108
Parameters:
109
- attributes: Dictionary of key-value attribute pairs
110
"""
111
112
def add_link(self, context: SpanContext, attributes: dict[str, Any] | None = None) -> None:
113
"""
114
Add a link to another span.
115
116
Parameters:
117
- context: SpanContext of the span to link to
118
- attributes: Optional attributes for the link
119
"""
120
121
def record_exception(self, exception: BaseException, *,
122
attributes: dict[str, Any] | None = None,
123
timestamp: int | None = None,
124
escaped: bool = False) -> None:
125
"""
126
Record an exception on the span.
127
128
Parameters:
129
- exception: Exception instance to record
130
- attributes: Optional attributes associated with the exception
131
- timestamp: Optional timestamp (nanoseconds since epoch)
132
- escaped: Whether the exception was handled/escaped
133
"""
134
135
def is_recording(self) -> bool:
136
"""
137
Check if the span is currently recording.
138
139
Returns: True if the span is recording, False otherwise
140
"""
141
142
def set_level(self, level: LevelName | int) -> None:
143
"""
144
Set the log level of the span.
145
146
Parameters:
147
- level: Log level name or integer value
148
"""
149
```
150
151
**Usage Examples:**
152
153
```python
154
import logfire
155
156
# Dynamic span modification
157
with logfire.span('Processing request') as span:
158
span.set_attribute('start_time', time.time())
159
160
try:
161
# Processing logic
162
result = process_data()
163
span.set_attributes({
164
'result_count': len(result),
165
'success': True
166
})
167
except Exception as e:
168
span.record_exception(e, escaped=True)
169
span.set_attribute('error_handled', True)
170
# Handle error
171
172
# Update span message
173
span.message = f'Processed {len(result)} items'
174
175
# Linking spans across services
176
with logfire.span('External API call') as span:
177
# Get context from current span to link from remote service
178
current_context = span.get_span_context()
179
180
# In remote service, create linked span
181
with logfire.span('Remote processing') as remote_span:
182
remote_span.add_link(current_context, {'service': 'external-api'})
183
```
184
185
### Function Instrumentation
186
187
Automatically instrument functions to create spans for their execution with configurable parameter and return value capture.
188
189
```python { .api }
190
def instrument(msg_template: str | None = None, *,
191
span_name: str | None = None,
192
extract_args: bool = True,
193
record_return: bool = False,
194
allow_generator: bool = False):
195
"""
196
Decorator to instrument functions with automatic span creation.
197
198
Parameters:
199
- msg_template: Optional message template for the span
200
- span_name: Optional custom span name
201
- extract_args: Whether to automatically log function arguments as attributes
202
- record_return: Whether to log the return value as an attribute
203
- allow_generator: Allow instrumenting generator functions
204
205
Returns: Decorator function
206
"""
207
```
208
209
**Usage Examples:**
210
211
```python
212
import logfire
213
214
# Basic function instrumentation
215
@logfire.instrument
216
def calculate_total(items):
217
return sum(item.price for item in items)
218
219
# Custom message and span name
220
@logfire.instrument(
221
msg_template='Computing metrics for {period}',
222
span_name='metric_computation'
223
)
224
def compute_metrics(period, data_source):
225
# Computation logic
226
return {'avg': 10, 'count': 100}
227
228
# Record return values
229
@logfire.instrument(record_return=True)
230
def fetch_user_data(user_id):
231
# Returns user data that will be logged as span attribute
232
return {'id': user_id, 'name': 'John', 'email': 'john@example.com'}
233
234
# Instrument without argument extraction (for sensitive data)
235
@logfire.instrument(extract_args=False)
236
def process_payment(credit_card_info):
237
# Credit card info won't be logged automatically
238
return payment_result
239
240
# Generator function instrumentation
241
@logfire.instrument(allow_generator=True)
242
def process_large_dataset():
243
for batch in large_dataset:
244
yield process_batch(batch)
245
```
246
247
### Auto-Tracing
248
249
Automatically instrument modules or functions based on patterns for comprehensive observability without manual decoration.
250
251
```python { .api }
252
def install_auto_tracing(modules: Sequence[str] | Callable[[AutoTraceModule], bool], *,
253
min_duration: float,
254
check_imported_modules: Literal['error', 'warn', 'ignore'] = 'error') -> None:
255
"""
256
Install automatic tracing for specified modules.
257
258
Parameters:
259
- modules: Module names to trace, or a predicate function
260
- min_duration: Minimum execution duration (seconds) to create spans
261
- check_imported_modules: How to handle already-imported modules
262
"""
263
264
def no_auto_trace(func):
265
"""
266
Decorator to exclude a function from auto-tracing.
267
268
Parameters:
269
- func: Function to exclude from auto-tracing
270
271
Returns: Original function with auto-trace exclusion marker
272
"""
273
```
274
275
**Usage Examples:**
276
277
```python
278
import logfire
279
280
# Auto-trace specific modules
281
logfire.install_auto_tracing(
282
modules=['myapp.services', 'myapp.models'],
283
min_duration=0.01 # Only trace functions taking > 10ms
284
)
285
286
# Auto-trace with predicate function using AutoTraceModule
287
def should_trace(module: AutoTraceModule):
288
return (module.name.startswith('myapp.') and
289
not module.name.endswith('.tests') and
290
module.filename is not None)
291
292
logfire.install_auto_tracing(
293
modules=should_trace,
294
min_duration=0.005
295
)
296
297
# Exclude specific functions from auto-tracing
298
@logfire.no_auto_trace
299
def internal_helper_function():
300
# This won't be auto-traced even if module is included
301
pass
302
```
303
304
### Async Support
305
306
Logfire spans work seamlessly with async/await code and provide utilities for monitoring async operations.
307
308
```python { .api }
309
def log_slow_async_callbacks(slow_duration: float = 0.1) -> AbstractContextManager[None]:
310
"""
311
Context manager that logs warnings for slow asyncio callbacks.
312
313
Parameters:
314
- slow_duration: Threshold in seconds for considering callbacks slow
315
316
Returns: Context manager for monitoring async callback performance
317
"""
318
```
319
320
**Usage Examples:**
321
322
```python
323
import asyncio
324
import logfire
325
326
# Async spans work automatically
327
async def async_operation():
328
with logfire.span('Async database query', query_type='SELECT'):
329
await asyncio.sleep(0.1) # Simulated async work
330
return {'results': []}
331
332
# Monitor slow async callbacks
333
async def main():
334
with logfire.log_slow_async_callbacks(slow_duration=0.05):
335
# Any callback taking > 50ms will be logged as warning
336
await async_operation()
337
await another_async_operation()
338
339
# Async function instrumentation
340
@logfire.instrument
341
async def fetch_user_async(user_id):
342
with logfire.span('Database lookup', user_id=user_id):
343
# Async database call
344
return await db.fetch_user(user_id)
345
```
346
347
### Span Scoping and Context
348
349
Control span visibility and manage OpenTelemetry contexts for fine-grained tracing control.
350
351
```python { .api }
352
def suppress_scopes(*scopes: str) -> None:
353
"""
354
Prevent span and metric creation for specified OpenTelemetry scopes.
355
356
Parameters:
357
- *scopes: OpenTelemetry scope names to suppress
358
"""
359
```
360
361
**Usage Examples:**
362
363
```python
364
import logfire
365
366
# Suppress spans from specific instrumentation
367
logfire.suppress_scopes('opentelemetry.instrumentation.requests')
368
369
# Now requests won't create spans, but other instrumentation still works
370
import requests
371
requests.get('https://api.example.com') # No span created
372
373
# Custom instrumentation still works
374
with logfire.span('Custom operation'):
375
requests.get('https://api.example.com') # Still no request span, but custom span exists
376
```
377
378
### Distributed Tracing
379
380
Support for distributed tracing across services with context propagation and baggage management.
381
382
```python { .api }
383
def get_baggage() -> dict[str, str]:
384
"""
385
Get current OpenTelemetry baggage (key-value pairs propagated across service boundaries).
386
387
Returns: Dictionary of baggage key-value pairs
388
"""
389
390
def set_baggage(baggage: dict[str, str]) -> Token:
391
"""
392
Set OpenTelemetry baggage for context propagation.
393
394
Parameters:
395
- baggage: Dictionary of key-value pairs to propagate
396
397
Returns: Token for context restoration
398
"""
399
```
400
401
**Usage Examples:**
402
403
```python
404
import logfire
405
406
# Set baggage for cross-service correlation
407
token = logfire.set_baggage({
408
'user_id': '123',
409
'session_id': 'abc-def-456',
410
'feature_flag': 'new_checkout_enabled'
411
})
412
413
# Baggage is automatically propagated in HTTP headers
414
with logfire.span('Service A operation'):
415
# Make HTTP call - baggage is automatically included in headers
416
response = requests.post('https://service-b/process')
417
418
# In Service B, retrieve baggage
419
current_baggage = logfire.get_baggage()
420
user_id = current_baggage.get('user_id')
421
feature_enabled = current_baggage.get('feature_flag') == 'new_checkout_enabled'
422
```
423
424
### Performance and Resource Management
425
426
Tools for managing span lifecycle and ensuring proper resource cleanup.
427
428
```python { .api }
429
def force_flush(timeout_millis: int = 3000) -> bool:
430
"""
431
Force flush all pending spans to configured exporters.
432
433
Parameters:
434
- timeout_millis: Maximum time to wait for flush completion
435
436
Returns: True if flush completed within timeout
437
"""
438
439
def shutdown(timeout_millis: int = 30000, flush: bool = True) -> bool:
440
"""
441
Shutdown span processors and exporters.
442
443
Parameters:
444
- timeout_millis: Maximum time to wait for shutdown
445
- flush: Whether to flush pending spans before shutdown
446
447
Returns: True if shutdown completed within timeout
448
"""
449
```
450
451
**Usage Examples:**
452
453
```python
454
import logfire
455
import atexit
456
457
# Ensure spans are flushed on application exit
458
def cleanup():
459
logfire.force_flush(timeout_millis=5000)
460
logfire.shutdown(timeout_millis=10000)
461
462
atexit.register(cleanup)
463
464
# Manual flush at critical points
465
def handle_request():
466
with logfire.span('Handle request'):
467
# Process request
468
pass
469
470
# Ensure this request's spans are sent before continuing
471
logfire.force_flush(timeout_millis=1000)
472
```
473
474
### Type Definitions
475
476
```python { .api }
477
# OpenTelemetry types used in span operations
478
from opentelemetry.trace import SpanContext, SpanKind
479
from opentelemetry.util.types import AttributeValue
480
481
# Logfire-specific types
482
LevelName = Literal['trace', 'debug', 'info', 'notice', 'warn', 'warning', 'error', 'fatal']
483
484
# Auto-tracing types
485
@dataclass
486
class AutoTraceModule:
487
"""
488
Information about a module being imported that should maybe be traced automatically.
489
490
This object will be passed to a function that should return True if the module should be traced.
491
Used with install_auto_tracing() as the modules argument.
492
"""
493
494
name: str
495
"""Fully qualified absolute name of the module being imported."""
496
497
filename: str | None
498
"""Filename of the module being imported."""
499
500
def parts_start_with(self, prefix: str | Sequence[str]) -> bool:
501
"""
502
Return True if the module name starts with any of the given prefixes, using dots as boundaries.
503
504
For example, if the module name is 'foo.bar.spam', then parts_start_with('foo') will return True,
505
but parts_start_with('bar') or parts_start_with('foo_bar') will return False.
506
507
If a prefix contains any characters other than letters, numbers, and dots,
508
then it will be treated as a regular expression.
509
510
Parameters:
511
- prefix: String or sequence of strings to match against module name
512
513
Returns: True if module name matches any prefix
514
"""
515
516
# Context management
517
from contextvars import Token
518
from typing import AbstractContextManager
519
```