0
# Metrics and Observability
1
2
ZIO provides comprehensive metrics and observability capabilities for monitoring application performance, collecting telemetry data, and gaining insights into system behavior.
3
4
## Capabilities
5
6
### Metric System
7
8
ZIO's metric system allows you to collect, aggregate, and export various types of metrics with composable metric definitions.
9
10
```scala { .api }
11
/**
12
* A Metric represents a composable way to collect measurements
13
*/
14
sealed trait Metric[In, Out] {
15
/** Contramap the input type */
16
def contramap[In2](f: In2 => In): Metric[In2, Out]
17
18
/** Map the output type */
19
def map[Out2](f: Out => Out2): Metric[In, Out2]
20
21
/** Combine with another metric */
22
def zip[In2, Out2](that: Metric[In2, Out2]): Metric[(In, In2), (Out, Out2)]
23
24
/** Add tags to the metric */
25
def tagged(tag: MetricLabel, tags: MetricLabel*): Metric[In, Out]
26
27
/** Add tags conditionally */
28
def taggedWith[A](f: A => Set[MetricLabel]): Metric[(In, A), Out]
29
}
30
31
/**
32
* Common metric types
33
*/
34
object Metric {
35
/** Counter that only increases */
36
def counter(name: String): Metric[Double, MetricState.Counter]
37
38
/** Gauge that can increase or decrease */
39
def gauge(name: String): Metric[Double, MetricState.Gauge]
40
41
/** Histogram for distribution measurements */
42
def histogram(name: String, boundaries: MetricKeyType.Histogram.Boundaries): Metric[Double, MetricState.Histogram]
43
44
/** Summary for tracking count, sum, min, max */
45
def summary(name: String): Metric[Double, MetricState.Summary]
46
47
/** Set gauge for tracking distinct values */
48
def setGauge(name: String): Metric[Set[String], MetricState.Gauge]
49
50
/** Frequency metric for counting occurrences */
51
def frequency(name: String): Metric[String, MetricState.Frequency]
52
}
53
54
/**
55
* Metric labels for categorization
56
*/
57
case class MetricLabel(key: String, value: String)
58
59
object MetricLabel {
60
def apply(key: String, value: String): MetricLabel = new MetricLabel(key, value)
61
}
62
63
/**
64
* Metric key for identification
65
*/
66
case class MetricKey[Type <: MetricKeyType](
67
name: String,
68
keyType: Type,
69
description: Option[String] = None,
70
tags: Set[MetricLabel] = Set.empty
71
)
72
```
73
74
**Usage Examples:**
75
76
```scala
77
import zio._
78
import zio.metrics._
79
80
// Define metrics
81
val requestCounter = Metric.counter("http_requests_total")
82
.tagged(MetricLabel("service", "api"))
83
84
val responseTimeHistogram = Metric.histogram(
85
"http_request_duration_seconds",
86
MetricKeyType.Histogram.Boundaries.exponential(0.001, 2.0, 10)
87
).tagged(MetricLabel("endpoint", "/users"))
88
89
val activeConnectionsGauge = Metric.gauge("active_connections")
90
91
// Use metrics in application code
92
val httpHandler = for {
93
startTime <- Clock.nanoTime
94
95
// Increment request counter
96
_ <- requestCounter.update(1.0)
97
98
// Increment active connections
99
_ <- activeConnectionsGauge.update(1.0)
100
101
// Process request
102
response <- processRequest()
103
104
// Record response time
105
endTime <- Clock.nanoTime
106
duration = (endTime - startTime) / 1e9
107
_ <- responseTimeHistogram.update(duration)
108
109
// Decrement active connections
110
_ <- activeConnectionsGauge.update(-1.0)
111
112
} yield response
113
```
114
115
### Metric Client
116
117
The metric client provides centralized metric collection and export capabilities.
118
119
```scala { .api }
120
/**
121
* Client for collecting and exporting metrics
122
*/
123
trait MetricClient {
124
/** Get current snapshot of all metrics */
125
def snapshot: UIO[Set[MetricPair.Untyped]]
126
127
/** Track a metric value */
128
def track[In, Out](metric: Metric[In, Out], value: In): UIO[Unit]
129
130
/** Get specific metric state */
131
def get[Out](key: MetricKey[_]): UIO[Option[Out]]
132
}
133
134
/**
135
* Metric pair containing key and state
136
*/
137
sealed trait MetricPair[+Type <: MetricKeyType] {
138
def metricKey: MetricKey[Type]
139
def metricState: MetricState
140
}
141
142
object MetricPair {
143
type Untyped = MetricPair[MetricKeyType]
144
}
145
146
/**
147
* Different types of metric states
148
*/
149
sealed trait MetricState
150
151
object MetricState {
152
/** Counter state */
153
final case class Counter(count: Double, startTime: java.time.Instant) extends MetricState
154
155
/** Gauge state */
156
final case class Gauge(value: Double, startTime: java.time.Instant) extends MetricState
157
158
/** Histogram state */
159
final case class Histogram(
160
buckets: Chunk[(Double, Long)],
161
count: Long,
162
min: Double,
163
max: Double,
164
sum: Double,
165
startTime: java.time.Instant
166
) extends MetricState
167
168
/** Summary state */
169
final case class Summary(
170
error: Double,
171
quantiles: Chunk[(Double, Option[Double])],
172
count: Long,
173
min: Double,
174
max: Double,
175
sum: Double,
176
startTime: java.time.Instant
177
) extends MetricState
178
179
/** Frequency state */
180
final case class Frequency(occurrences: Map[String, Long]) extends MetricState
181
}
182
```
183
184
**Usage Examples:**
185
186
```scala
187
// Create and configure metric client
188
val metricClientLayer = ZLayer.succeed(MetricClient.default)
189
190
// Collect metrics snapshot
191
val metricsReport = for {
192
client <- ZIO.service[MetricClient]
193
snapshot <- client.snapshot
194
_ <- ZIO.foreach(snapshot) { pair =>
195
Console.printLine(s"${pair.metricKey.name}: ${pair.metricState}")
196
}
197
} yield ()
198
199
// Custom metric tracking
200
val customTracking = for {
201
client <- ZIO.service[MetricClient]
202
metric = Metric.counter("custom_events")
203
_ <- client.track(metric, 1.0)
204
state <- client.get(metric.key)
205
} yield state
206
```
207
208
### Built-in Metrics
209
210
ZIO automatically collects various system and runtime metrics when enabled.
211
212
```scala { .api }
213
/**
214
* JVM and system metrics automatically collected
215
*/
216
object BuiltInMetrics {
217
/** JVM memory usage metrics */
218
val jvmMemoryUsed: Metric[Any, MetricState.Gauge]
219
val jvmMemoryCommitted: Metric[Any, MetricState.Gauge]
220
val jvmMemoryMax: Metric[Any, MetricState.Gauge]
221
222
/** JVM garbage collection metrics */
223
val jvmGcTime: Metric[Any, MetricState.Counter]
224
val jvmGcCollections: Metric[Any, MetricState.Counter]
225
226
/** Thread pool metrics */
227
val jvmThreadsActive: Metric[Any, MetricState.Gauge]
228
val jvmThreadsDaemon: Metric[Any, MetricState.Gauge]
229
val jvmThreadsPeak: Metric[Any, MetricState.Gauge]
230
231
/** ZIO fiber metrics */
232
val fiberCount: Metric[Any, MetricState.Gauge]
233
val fiberStarted: Metric[Any, MetricState.Counter]
234
val fiberCompleted: Metric[Any, MetricState.Counter]
235
236
/** System CPU and load metrics */
237
val systemCpuUsage: Metric[Any, MetricState.Gauge]
238
val systemLoadAverage: Metric[Any, MetricState.Gauge]
239
240
/** Enable built-in metrics collection */
241
def enable: ZLayer[Any, Nothing, Unit]
242
}
243
```
244
245
**Usage Examples:**
246
247
```scala
248
// Enable built-in metrics
249
val app = myApplication.provide(
250
// Other layers
251
MetricClient.default,
252
BuiltInMetrics.enable
253
)
254
255
// Monitor fiber lifecycle
256
val fiberMonitoring = for {
257
_ <- Console.printLine("Starting intensive computation")
258
259
// This will be tracked by fiber metrics
260
fibers <- ZIO.foreach(1 to 100) { i =>
261
intensiveComputation(i).fork
262
}
263
264
results <- ZIO.foreach(fibers)(_.join)
265
266
// Check current fiber count
267
client <- ZIO.service[MetricClient]
268
fiberCount <- client.get(BuiltInMetrics.fiberCount.key)
269
_ <- Console.printLine(s"Current fiber count: $fiberCount")
270
271
} yield results
272
```
273
274
### Metric Aspects
275
276
Metric aspects provide declarative ways to add metrics to ZIO effects and layers.
277
278
```scala { .api }
279
/**
280
* Aspects for adding metrics to effects
281
*/
282
object MetricAspect {
283
/** Count executions of an effect */
284
def count(metric: Metric[Long, MetricState.Counter]): ZIOAspect[Nothing, Any, Nothing, Any, Nothing, Any]
285
286
/** Time duration of effect execution */
287
def timed(metric: Metric[Duration, MetricState.Histogram]): ZIOAspect[Nothing, Any, Nothing, Any, Nothing, Any]
288
289
/** Track gauge value during effect */
290
def trackGauge(metric: Metric[Double, MetricState.Gauge], value: => Double): ZIOAspect[Nothing, Any, Nothing, Any, Nothing, Any]
291
292
/** Count successes and failures separately */
293
def countErrors(
294
successMetric: Metric[Long, MetricState.Counter],
295
errorMetric: Metric[Long, MetricState.Counter]
296
): ZIOAspect[Nothing, Any, Nothing, Any, Nothing, Any]
297
298
/** Track different metrics based on result */
299
def trackResult[E, A](
300
f: Either[E, A] => List[(Metric[Double, _], Double)]
301
): ZIOAspect[Nothing, Any, E, E, A, A]
302
}
303
```
304
305
**Usage Examples:**
306
307
```scala
308
// Add metrics to effects using aspects
309
val databaseQuery = queryDatabase(userId)
310
@@ MetricAspect.count(Metric.counter("db_queries"))
311
@@ MetricAspect.timed(Metric.histogram("db_query_duration", boundaries))
312
@@ MetricAspect.countErrors(
313
Metric.counter("db_queries_success"),
314
Metric.counter("db_queries_error")
315
)
316
317
// Track business metrics
318
val orderProcessing = processOrder(order)
319
@@ MetricAspect.trackResult {
320
case Right(order) => List(
321
(Metric.counter("orders_completed"), 1.0),
322
(Metric.gauge("order_value"), order.totalAmount)
323
)
324
case Left(error) => List(
325
(Metric.counter("orders_failed"), 1.0)
326
)
327
}
328
329
// Track resource usage
330
val resourceIntensiveTask = computeResults()
331
@@ MetricAspect.trackGauge(Metric.gauge("cpu_intensive_tasks"), 1.0)
332
```
333
334
### Metric Export and Integration
335
336
Export metrics to external monitoring systems and integrate with observability platforms.
337
338
```scala { .api }
339
/**
340
* Metric exporters for external systems
341
*/
342
trait MetricExporter {
343
/** Export metrics snapshot */
344
def export(snapshot: Set[MetricPair.Untyped]): Task[Unit]
345
}
346
347
/**
348
* Common metric export formats
349
*/
350
object MetricExporter {
351
/** Export to Prometheus format */
352
def prometheus(registry: Registry): MetricExporter
353
354
/** Export to StatsD */
355
def statsD(client: StatsDClient): MetricExporter
356
357
/** Export to console for debugging */
358
val console: MetricExporter
359
360
/** Export to JSON format */
361
def json(writer: JsonWriter): MetricExporter
362
363
/** Composite exporter for multiple destinations */
364
def composite(exporters: MetricExporter*): MetricExporter
365
}
366
367
/**
368
* Periodic metric export scheduler
369
*/
370
object MetricScheduler {
371
/** Schedule periodic metric exports */
372
def schedule(
373
client: MetricClient,
374
exporter: MetricExporter,
375
interval: Duration
376
): ZIO[Any, Nothing, Fiber.Runtime[Throwable, Nothing]]
377
}
378
```
379
380
**Usage Examples:**
381
382
```scala
383
// Set up metric export to multiple systems
384
val metricExportLayer = ZLayer.scoped {
385
for {
386
client <- ZIO.service[MetricClient]
387
388
// Create composite exporter
389
exporter = MetricExporter.composite(
390
MetricExporter.prometheus(prometheusRegistry),
391
MetricExporter.statsD(statsDClient),
392
MetricExporter.console // For debugging
393
)
394
395
// Schedule periodic export
396
scheduler <- MetricScheduler.schedule(client, exporter, 30.seconds)
397
398
// Ensure cleanup
399
_ <- ZIO.addFinalizer(scheduler.interrupt.ignore)
400
401
} yield ()
402
}
403
404
// Full application with metrics
405
val monitoredApp = myApplication.provide(
406
MetricClient.default,
407
BuiltInMetrics.enable,
408
metricExportLayer,
409
// Other application layers
410
)
411
412
// Manual metric export
413
val exportMetrics = for {
414
client <- ZIO.service[MetricClient]
415
snapshot <- client.snapshot
416
_ <- MetricExporter.console.export(snapshot)
417
} yield ()
418
```
419
420
### Performance Monitoring
421
422
Advanced performance monitoring and profiling capabilities for production applications.
423
424
```scala { .api }
425
/**
426
* Performance monitoring utilities
427
*/
428
object PerformanceMonitoring {
429
/** Monitor method execution with detailed timing */
430
def profileMethod[R, E, A](
431
methodName: String,
432
thresholds: Map[String, Duration] = Map.empty
433
)(effect: ZIO[R, E, A]): ZIO[R, E, A]
434
435
/** Monitor memory allocation during effect */
436
def trackMemory[R, E, A](effect: ZIO[R, E, A]): ZIO[R, E, (A, MemoryUsage)]
437
438
/** Monitor fiber scheduling and execution */
439
def trackFiberMetrics[R, E, A](effect: ZIO[R, E, A]): ZIO[R, E, A]
440
441
/** Create performance dashboard data */
442
def dashboardSnapshot: UIO[PerformanceDashboard]
443
}
444
445
/**
446
* Memory usage information
447
*/
448
case class MemoryUsage(
449
heapUsed: Long,
450
heapCommitted: Long,
451
heapMax: Long,
452
nonHeapUsed: Long,
453
nonHeapCommitted: Long
454
)
455
456
/**
457
* Performance dashboard data
458
*/
459
case class PerformanceDashboard(
460
uptime: Duration,
461
fiberStats: FiberStats,
462
memoryStats: MemoryUsage,
463
gcStats: GCStats,
464
threadStats: ThreadStats
465
)
466
```
467
468
**Usage Examples:**
469
470
```scala
471
// Profile critical methods
472
val criticalOperation = performCriticalTask()
473
@@ PerformanceMonitoring.profileMethod(
474
"critical_task",
475
Map(
476
"warning" -> 100.millis,
477
"critical" -> 500.millis
478
)
479
)
480
481
// Monitor memory-intensive operations
482
val memoryIntensiveTask = for {
483
(result, memUsage) <- PerformanceMonitoring.trackMemory {
484
processLargeDataset(dataset)
485
}
486
_ <- Console.printLine(s"Memory used: ${memUsage.heapUsed / 1024 / 1024} MB")
487
} yield result
488
489
// Create performance monitoring dashboard
490
val performanceDashboard = for {
491
dashboard <- PerformanceMonitoring.dashboardSnapshot
492
_ <- Console.printLine(s"Uptime: ${dashboard.uptime}")
493
_ <- Console.printLine(s"Active fibers: ${dashboard.fiberStats.active}")
494
_ <- Console.printLine(s"Heap usage: ${dashboard.memoryStats.heapUsed}")
495
} yield dashboard
496
```