or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

core-logging.mdindex.mdinstrumentation.mdintegrations.mdmetrics.mdspans-tracing.md

metrics.mddocs/

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

```