0
# Metrics
1
2
Metrics creation and management for quantitative monitoring including counters, histograms, gauges, and callback-based metrics. Logfire provides comprehensive metrics capabilities built on OpenTelemetry standards for performance monitoring and system observability.
3
4
## Capabilities
5
6
### Counter Metrics
7
8
Counters track cumulative values that only increase over time, such as request counts, error counts, or bytes processed.
9
10
```python { .api }
11
def metric_counter(name: str, *,
12
unit: str = '',
13
description: str = '') -> Counter:
14
"""
15
Create a counter metric for tracking cumulative increasing values.
16
17
Parameters:
18
- name: Unique metric name
19
- unit: Unit of measurement (e.g., 'requests', 'bytes', 'ms')
20
- description: Human-readable description of what the metric measures
21
22
Returns: Counter instance for recording values
23
"""
24
25
class Counter:
26
"""Counter metric for tracking cumulative increasing values."""
27
28
def add(self, amount: int | float, attributes: dict[str, str] | None = None) -> None:
29
"""
30
Add a value to the counter.
31
32
Parameters:
33
- amount: Positive number to add to the counter
34
- attributes: Optional attributes for labeling/filtering
35
"""
36
```
37
38
**Usage Examples:**
39
40
```python
41
import logfire
42
43
logfire.configure()
44
45
# Create counters for different metrics
46
request_counter = logfire.metric_counter(
47
'http_requests_total',
48
unit='requests',
49
description='Total number of HTTP requests'
50
)
51
52
error_counter = logfire.metric_counter(
53
'http_errors_total',
54
unit='errors',
55
description='Total number of HTTP errors'
56
)
57
58
bytes_processed = logfire.metric_counter(
59
'bytes_processed_total',
60
unit='bytes',
61
description='Total bytes processed by the application'
62
)
63
64
# Record counter values
65
request_counter.add(1, {'method': 'GET', 'endpoint': '/users'})
66
request_counter.add(1, {'method': 'POST', 'endpoint': '/users'})
67
68
# Record errors with context
69
error_counter.add(1, {'status_code': '404', 'endpoint': '/users/999'})
70
71
# Record data processing
72
bytes_processed.add(1024, {'operation': 'upload', 'file_type': 'json'})
73
```
74
75
### Histogram Metrics
76
77
Histograms track distributions of values, automatically creating buckets for analyzing percentiles, averages, and value distributions.
78
79
```python { .api }
80
def metric_histogram(name: str, *,
81
unit: str = '',
82
description: str = '') -> Histogram:
83
"""
84
Create a histogram metric for tracking value distributions.
85
86
Parameters:
87
- name: Unique metric name
88
- unit: Unit of measurement (e.g., 'ms', 'seconds', 'bytes')
89
- description: Human-readable description of what the metric measures
90
91
Returns: Histogram instance for recording values
92
"""
93
94
class Histogram:
95
"""Histogram metric for tracking value distributions."""
96
97
def record(self, amount: int | float, attributes: dict[str, str] | None = None) -> None:
98
"""
99
Record a value in the histogram.
100
101
Parameters:
102
- amount: Value to record
103
- attributes: Optional attributes for labeling/filtering
104
"""
105
```
106
107
**Usage Examples:**
108
109
```python
110
import logfire
111
import time
112
113
logfire.configure()
114
115
# Create histograms for performance monitoring
116
response_time = logfire.metric_histogram(
117
'http_request_duration_ms',
118
unit='ms',
119
description='HTTP request duration in milliseconds'
120
)
121
122
payload_size = logfire.metric_histogram(
123
'request_payload_size_bytes',
124
unit='bytes',
125
description='Size of request payloads'
126
)
127
128
query_duration = logfire.metric_histogram(
129
'database_query_duration_ms',
130
unit='ms',
131
description='Database query execution time'
132
)
133
134
# Record response times
135
def handle_request():
136
start_time = time.time()
137
138
# Process request
139
time.sleep(0.1) # Simulated work
140
141
duration_ms = (time.time() - start_time) * 1000
142
response_time.record(duration_ms, {
143
'method': 'GET',
144
'endpoint': '/api/users',
145
'status_code': '200'
146
})
147
148
# Record payload sizes
149
payload_size.record(2048, {'content_type': 'application/json'})
150
151
# Record query performance
152
query_duration.record(15.5, {'table': 'users', 'operation': 'SELECT'})
153
```
154
155
### Gauge Metrics
156
157
Gauges track current values that can go up or down, such as memory usage, queue length, or temperature readings.
158
159
```python { .api }
160
def metric_gauge(name: str, *,
161
unit: str = '',
162
description: str = '') -> Gauge:
163
"""
164
Create a gauge metric for tracking current values that can increase or decrease.
165
166
Parameters:
167
- name: Unique metric name
168
- unit: Unit of measurement (e.g., 'bytes', 'percent', 'count')
169
- description: Human-readable description of what the metric measures
170
171
Returns: Gauge instance for recording values
172
"""
173
174
class Gauge:
175
"""Gauge metric for tracking current values."""
176
177
def set(self, amount: int | float, attributes: dict[str, str] | None = None) -> None:
178
"""
179
Set the current value of the gauge.
180
181
Parameters:
182
- amount: Current value to set
183
- attributes: Optional attributes for labeling/filtering
184
"""
185
```
186
187
**Usage Examples:**
188
189
```python
190
import logfire
191
import psutil
192
193
logfire.configure()
194
195
# Create gauges for system monitoring
196
memory_usage = logfire.metric_gauge(
197
'memory_usage_bytes',
198
unit='bytes',
199
description='Current memory usage'
200
)
201
202
cpu_utilization = logfire.metric_gauge(
203
'cpu_utilization_percent',
204
unit='percent',
205
description='Current CPU utilization percentage'
206
)
207
208
queue_size = logfire.metric_gauge(
209
'task_queue_size',
210
unit='tasks',
211
description='Current number of tasks in queue'
212
)
213
214
# Update gauge values
215
def update_system_metrics():
216
# Memory usage
217
memory_usage.set(psutil.virtual_memory().used, {'type': 'virtual'})
218
219
# CPU utilization
220
cpu_percent = psutil.cpu_percent()
221
cpu_utilization.set(cpu_percent, {'core': 'all'})
222
223
# Application-specific metrics
224
queue_size.set(len(task_queue), {'queue_type': 'background_jobs'})
225
226
# Call periodically to update gauges
227
update_system_metrics()
228
```
229
230
### Up-Down Counter Metrics
231
232
Up-down counters track values that can increase or decrease, similar to gauges but for cumulative values like active connections or items in inventory.
233
234
```python { .api }
235
def metric_up_down_counter(name: str, *,
236
unit: str = '',
237
description: str = '') -> UpDownCounter:
238
"""
239
Create an up-down counter metric for values that can increase or decrease.
240
241
Parameters:
242
- name: Unique metric name
243
- unit: Unit of measurement (e.g., 'connections', 'items')
244
- description: Human-readable description of what the metric measures
245
246
Returns: UpDownCounter instance for recording changes
247
"""
248
249
class UpDownCounter:
250
"""Up-down counter metric for tracking values that can increase or decrease."""
251
252
def add(self, amount: int | float, attributes: dict[str, str] | None = None) -> None:
253
"""
254
Add a value to the up-down counter (can be positive or negative).
255
256
Parameters:
257
- amount: Value to add (positive to increase, negative to decrease)
258
- attributes: Optional attributes for labeling/filtering
259
"""
260
```
261
262
**Usage Examples:**
263
264
```python
265
import logfire
266
267
logfire.configure()
268
269
# Create up-down counters for resource tracking
270
active_connections = logfire.metric_up_down_counter(
271
'active_connections',
272
unit='connections',
273
description='Number of active database connections'
274
)
275
276
items_in_cart = logfire.metric_up_down_counter(
277
'shopping_cart_items',
278
unit='items',
279
description='Number of items in shopping carts'
280
)
281
282
# Track connection lifecycle
283
def on_connection_open():
284
active_connections.add(1, {'database': 'users', 'pool': 'primary'})
285
286
def on_connection_close():
287
active_connections.add(-1, {'database': 'users', 'pool': 'primary'})
288
289
# Track shopping cart changes
290
def add_item_to_cart(user_id, item_id):
291
items_in_cart.add(1, {'user_category': 'premium'})
292
293
def remove_item_from_cart(user_id, item_id):
294
items_in_cart.add(-1, {'user_category': 'premium'})
295
```
296
297
### Callback-Based Metrics
298
299
Callback metrics allow you to define functions that are called periodically to collect metric values, useful for metrics that are expensive to calculate or need to be collected on a schedule.
300
301
```python { .api }
302
def metric_counter_callback(name: str, *,
303
callbacks: Sequence[Callable[[], Iterable[Measurement]]],
304
unit: str = '',
305
description: str = '') -> None:
306
"""
307
Create a counter metric with callback-based collection.
308
309
Parameters:
310
- name: Unique metric name
311
- callbacks: Functions that return measurement values when called
312
- unit: Unit of measurement
313
- description: Human-readable description
314
"""
315
316
def metric_gauge_callback(name: str,
317
callbacks: Sequence[Callable[[], Iterable[Measurement]]], *,
318
unit: str = '',
319
description: str = '') -> None:
320
"""
321
Create a gauge metric with callback-based collection.
322
323
Parameters:
324
- name: Unique metric name
325
- callbacks: Functions that return measurement values when called
326
- unit: Unit of measurement
327
- description: Human-readable description
328
"""
329
330
def metric_up_down_counter_callback(name: str,
331
callbacks: Sequence[Callable[[], Iterable[Measurement]]], *,
332
unit: str = '',
333
description: str = '') -> None:
334
"""
335
Create an up-down counter metric with callback-based collection.
336
337
Parameters:
338
- name: Unique metric name
339
- callbacks: Functions that return measurement values when called
340
- unit: Unit of measurement
341
- description: Human-readable description
342
"""
343
```
344
345
**Usage Examples:**
346
347
```python
348
import logfire
349
import psutil
350
from typing import Iterable
351
from opentelemetry.metrics import Measurement
352
353
logfire.configure()
354
355
# Callback functions for system metrics
356
def get_memory_measurements() -> Iterable[Measurement]:
357
memory = psutil.virtual_memory()
358
return [
359
Measurement(value=memory.total, attributes={'type': 'total'}),
360
Measurement(value=memory.available, attributes={'type': 'available'}),
361
Measurement(value=memory.used, attributes={'type': 'used'})
362
]
363
364
def get_cpu_measurements() -> Iterable[Measurement]:
365
cpu_percentages = psutil.cpu_percent(percpu=True)
366
measurements = []
367
for i, percent in enumerate(cpu_percentages):
368
measurements.append(
369
Measurement(value=percent, attributes={'core': str(i)})
370
)
371
return measurements
372
373
def get_connection_count() -> Iterable[Measurement]:
374
# Example: count active connections from connection pool
375
active_count = connection_pool.active_connections()
376
return [Measurement(value=active_count, attributes={'pool': 'primary'})]
377
378
# Register callback metrics
379
logfire.metric_gauge_callback(
380
'system_memory_bytes',
381
callbacks=[get_memory_measurements],
382
unit='bytes',
383
description='System memory statistics'
384
)
385
386
logfire.metric_gauge_callback(
387
'cpu_usage_percent',
388
callbacks=[get_cpu_measurements],
389
unit='percent',
390
description='Per-core CPU usage percentage'
391
)
392
393
logfire.metric_up_down_counter_callback(
394
'database_connections_active',
395
callbacks=[get_connection_count],
396
unit='connections',
397
description='Active database connections'
398
)
399
```
400
401
### Metrics Configuration
402
403
Configure how metrics are collected, processed, and exported.
404
405
```python { .api }
406
class MetricsOptions:
407
"""Configuration options for metrics collection."""
408
409
additional_readers: Sequence[MetricReader] = ()
410
"""Additional metric readers for custom export destinations."""
411
412
collect_in_spans: bool = True
413
"""Whether to collect metrics data within span context."""
414
```
415
416
**Usage Example:**
417
418
```python
419
import logfire
420
from opentelemetry.exporter.prometheus import PrometheusMetricReader
421
422
# Configure metrics with Prometheus export
423
prometheus_reader = PrometheusMetricReader()
424
425
logfire.configure(
426
metrics=logfire.MetricsOptions(
427
additional_readers=[prometheus_reader],
428
collect_in_spans=True
429
)
430
)
431
```
432
433
### Metrics Integration with Spans
434
435
Metrics can be associated with spans to provide rich context about when and where measurements were taken.
436
437
**Usage Examples:**
438
439
```python
440
import logfire
441
442
logfire.configure()
443
444
request_duration = logfire.metric_histogram(
445
'request_duration_ms',
446
unit='ms',
447
description='Request processing duration'
448
)
449
450
def handle_request():
451
with logfire.span('Handle HTTP request', endpoint='/api/users') as span:
452
start_time = time.time()
453
454
# Process request
455
result = process_request()
456
457
# Record metric within span context
458
duration_ms = (time.time() - start_time) * 1000
459
request_duration.record(duration_ms, {
460
'endpoint': '/api/users',
461
'status': '200'
462
})
463
464
span.set_attribute('duration_ms', duration_ms)
465
return result
466
```
467
468
### Type Definitions
469
470
```python { .api }
471
# OpenTelemetry metric types
472
from opentelemetry.metrics import Counter, Histogram, Gauge, UpDownCounter
473
from opentelemetry.metrics import Measurement, MetricReader
474
from typing import Callable, Sequence, Iterable
475
476
# Measurement creation for callbacks
477
class Measurement:
478
"""A single measurement value with optional attributes."""
479
def __init__(self, value: int | float, attributes: dict[str, str] | None = None): ...
480
481
# Callback function signature
482
MetricCallback = Callable[[], Iterable[Measurement]]
483
```
484
485
### Best Practices
486
487
**Naming Conventions:**
488
- Use descriptive names with units: `http_request_duration_seconds`
489
- Include totals for counters: `requests_total`, `errors_total`
490
- Use consistent naming across related metrics
491
492
**Attribute Management:**
493
- Keep attribute cardinality low (< 1000 unique combinations)
494
- Use consistent attribute names across metrics
495
- Avoid high-cardinality attributes like user IDs or request IDs
496
497
**Performance Considerations:**
498
- Use callback metrics for expensive calculations
499
- Batch metric updates when possible
500
- Consider sampling for high-frequency metrics
501
502
**Usage Example with Best Practices:**
503
504
```python
505
import logfire
506
507
logfire.configure()
508
509
# Well-named metrics with appropriate units
510
http_requests_total = logfire.metric_counter(
511
'http_requests_total',
512
unit='requests',
513
description='Total number of HTTP requests received'
514
)
515
516
http_request_duration_seconds = logfire.metric_histogram(
517
'http_request_duration_seconds',
518
unit='seconds',
519
description='HTTP request duration in seconds'
520
)
521
522
# Consistent, low-cardinality attributes
523
def record_request_metrics(method, endpoint, status_code, duration):
524
# Good: low cardinality attributes
525
attributes = {
526
'method': method,
527
'endpoint': normalize_endpoint(endpoint), # /users/{id} not /users/123
528
'status_code': str(status_code)
529
}
530
531
http_requests_total.add(1, attributes)
532
http_request_duration_seconds.record(duration, attributes)
533
```