Overview
Metrics allow you to measure and track numerical data about your application’s performance and behavior. This guide demonstrates all the core metric instruments available in OpenTelemetry Rust.
Dependencies
Add these dependencies to your Cargo.toml:
[dependencies]
opentelemetry = { version = "*", features = ["metrics"] }
opentelemetry_sdk = { version = "*", features = ["metrics"] }
opentelemetry-stdout = { version = "*", features = ["metrics"] }
tokio = { version = "1", features = ["full"] }
Complete Example
Initialize the Meter Provider
Set up the meter provider with a stdout exporter and resource attributes.use opentelemetry::{global, KeyValue};
use opentelemetry_sdk::metrics::SdkMeterProvider;
use opentelemetry_sdk::Resource;
fn init_meter_provider() -> SdkMeterProvider {
let exporter = opentelemetry_stdout::MetricExporterBuilder::default()
// Build exporter using Delta Temporality (Defaults to Temporality::Cumulative)
// .with_temporality(opentelemetry_sdk::metrics::Temporality::Delta)
.build();
let provider = SdkMeterProvider::builder()
.with_periodic_exporter(exporter)
.with_resource(
Resource::builder()
.with_service_name("metrics-basic-example")
.build(),
)
.build();
global::set_meter_provider(provider.clone());
provider
}
Create and Use Counters
Counters are used for monotonically increasing values.#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
let meter_provider = init_meter_provider();
let meter = global::meter("mylibraryname");
// Create a Counter Instrument
let counter = meter.u64_counter("my_counter").build();
// Record measurements using the Counter instrument
counter.add(
10,
&[
KeyValue::new("mykey1", "myvalue1"),
KeyValue::new("mykey2", "myvalue2"),
],
);
meter_provider.shutdown()?;
Ok(())
}
Create Observable Counters
Observable counters allow you to register a callback that reports measurements asynchronously.let _observable_counter = meter
.u64_observable_counter("my_observable_counter")
.with_description("My observable counter example description")
.with_unit("myunit")
.with_callback(|observer| {
observer.observe(
100,
&[
KeyValue::new("mykey1", "myvalue1"),
KeyValue::new("mykey2", "myvalue2"),
],
)
})
.build();
Use UpDownCounters
UpDownCounters can increase or decrease, useful for tracking values like queue sizes.// Create an UpDownCounter Instrument
let updown_counter = meter.i64_up_down_counter("my_updown_counter").build();
// Record measurements (can be negative)
updown_counter.add(
-10,
&[
KeyValue::new("mykey1", "myvalue1"),
KeyValue::new("mykey2", "myvalue2"),
],
);
// Observable version
let _observable_up_down_counter = meter
.i64_observable_up_down_counter("my_observable_updown_counter")
.with_description("My observable updown counter example description")
.with_unit("myunit")
.with_callback(|observer| {
observer.observe(
100,
&[
KeyValue::new("mykey1", "myvalue1"),
KeyValue::new("mykey2", "myvalue2"),
],
)
})
.build();
Create Histograms
Histograms measure the distribution of values, perfect for latencies and sizes.use std::vec;
// Create a Histogram Instrument
let histogram = meter
.f64_histogram("my_histogram")
.with_description("My histogram example description")
// Setting boundaries is optional. By default, the boundaries are set to
// [0.0, 5.0, 10.0, 25.0, 50.0, 75.0, 100.0, 250.0, 500.0, 750.0, 1000.0, 2500.0, 5000.0, 7500.0, 10000.0]
.with_boundaries(vec![0.0, 5.0, 10.0, 15.0, 20.0, 25.0])
.build();
// Record measurements using the histogram instrument
histogram.record(
10.5,
&[
KeyValue::new("mykey1", "myvalue1"),
KeyValue::new("mykey2", "myvalue2"),
],
);
There is no ObservableHistogram instrument in OpenTelemetry.
Use Gauges
Gauges represent a value that can arbitrarily go up and down.// Create a Gauge Instrument
let gauge = meter
.f64_gauge("my_gauge")
.with_description("A gauge set to 1.0")
.with_unit("myunit")
.build();
gauge.record(
1.0,
&[
KeyValue::new("mykey1", "myvalue1"),
KeyValue::new("mykey2", "myvalue2"),
],
);
// Create an ObservableGauge instrument
let _observable_gauge = meter
.f64_observable_gauge("my_observable_gauge")
.with_description("An observable gauge set to 1.0")
.with_unit("myunit")
.with_callback(|observer| {
observer.observe(
1.0,
&[
KeyValue::new("mykey1", "myvalue1"),
KeyValue::new("mykey2", "myvalue2"),
],
)
})
.build();
Metric Instruments Summary
| Instrument | Type | Use Case |
|---|
| Counter | Synchronous | Monotonically increasing values (requests, errors) |
| UpDownCounter | Synchronous | Values that go up and down (active connections) |
| Histogram | Synchronous | Distribution of values (latencies, response sizes) |
| Gauge | Synchronous | Current value snapshots (temperature, memory usage) |
| ObservableCounter | Asynchronous | Callback-based monotonic values |
| ObservableUpDownCounter | Asynchronous | Callback-based up/down values |
| ObservableGauge | Asynchronous | Callback-based current values |
Metrics are exported by default every 60 seconds when using the stdout exporter. Shutting down the MeterProvider instantly flushes the metrics instead of waiting for the interval.
Advanced Metrics
For advanced use cases like custom aggregations, views, and exponential histograms, check out the metrics-advanced example in the source repository which covers:
- Renaming metrics and changing units with Views
- Controlling cardinality limits
- Using exponential histograms for unpredictable value ranges
- Delta vs. Cumulative temporality
Always call shutdown() on the meter provider before your application exits to ensure all metrics are properly flushed to the exporter.