Skip to main content

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:
Cargo.toml
[dependencies]
opentelemetry = { version = "*", features = ["metrics"] }
opentelemetry_sdk = { version = "*", features = ["metrics"] }
opentelemetry-stdout = { version = "*", features = ["metrics"] }
tokio = { version = "1", features = ["full"] }

Complete Example

1

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
}
2

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(())
}
3

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();
4

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();
5

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.
6

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

InstrumentTypeUse Case
CounterSynchronousMonotonically increasing values (requests, errors)
UpDownCounterSynchronousValues that go up and down (active connections)
HistogramSynchronousDistribution of values (latencies, response sizes)
GaugeSynchronousCurrent value snapshots (temperature, memory usage)
ObservableCounterAsynchronousCallback-based monotonic values
ObservableUpDownCounterAsynchronousCallback-based up/down values
ObservableGaugeAsynchronousCallback-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.

Build docs developers (and LLMs) love