Skip to main content
Instruments are objects that record measurements in OpenTelemetry. The Metrics API provides several instrument types, each designed for specific measurement patterns.

Instrument Categories

Instruments fall into two main categories:

Synchronous Instruments

Synchronous instruments record measurements inline with your application code. They implement the SyncInstrument trait:
pub trait SyncInstrument<T>: Send + Sync {
    fn measure(&self, measurement: T, attributes: &[KeyValue]);
}
Synchronous instruments include:
  • Counter: Monotonically increasing values
  • UpDownCounter: Values that can increase or decrease
  • Histogram: Distribution of values
  • Gauge: Independent point-in-time values

Asynchronous Instruments

Asynchronous instruments use callbacks that are invoked during metric collection. They implement the AsyncInstrument trait:
pub trait AsyncInstrument<T>: Send + Sync {
    fn observe(&self, measurement: T, attributes: &[KeyValue]);
}
Asynchronous instruments include:
  • ObservableCounter: Observes monotonically increasing values
  • ObservableUpDownCounter: Observes values that can increase or decrease
  • ObservableGauge: Observes current values

Instrument Types Overview

Counter

Records monotonically increasing values like request counts or bytes sent

UpDownCounter

Records values that go up and down like active connections or queue depth

Histogram

Records distributions of values like request duration or response size

Gauge

Records independent measurements like CPU usage or temperature

Choosing the Right Instrument

Use this decision tree to select the appropriate instrument:

Counter vs. UpDownCounter

Use Counter when:
  • Values only increase (e.g., requests served, errors, bytes sent)
  • You’re counting discrete events
  • Resets should only happen on restart
Use UpDownCounter when:
  • Values can increase or decrease (e.g., active connections, items in queue)
  • You’re tracking a resource that changes bidirectionally
  • Current total is meaningful

Histogram vs. Gauge

Use Histogram when:
  • You need to understand the distribution (percentiles, min, max)
  • Values represent durations, sizes, or other measurements
  • Examples: latency, request size, temperature readings
Use Gauge when:
  • You only care about the current value
  • The last value is sufficient
  • Examples: current CPU usage, current memory, cache size

Synchronous vs. Asynchronous

Use Synchronous when:
  • Recording measurements in request handlers
  • Events happen as part of application logic
  • You control when measurements occur
Use Asynchronous (Observable) when:
  • Reading from system APIs or sensors
  • Values are expensive to compute
  • Measurements should happen on a schedule
  • Multiple instruments need the same source data

Instrument Builders

All instruments are created using a builder pattern:

InstrumentBuilder (Synchronous)

pub struct InstrumentBuilder<'a, T> {
    pub instrument_provider: &'a dyn InstrumentProvider,
    pub name: Cow<'static, str>,
    pub description: Option<Cow<'static, str>>,
    pub unit: Option<Cow<'static, str>>,
}
Example:
let counter = meter.u64_counter("http_requests")
    .with_description("Total HTTP requests received")
    .with_unit("requests")
    .build();

HistogramBuilder

Histograms have additional configuration for bucket boundaries:
pub struct HistogramBuilder<'a, T> {
    pub instrument_provider: &'a dyn InstrumentProvider,
    pub name: Cow<'static, str>,
    pub description: Option<Cow<'static, str>>,
    pub unit: Option<Cow<'static, str>>,
    pub boundaries: Option<Vec<f64>>,
}
Example:
let histogram = meter.f64_histogram("http_request_duration")
    .with_description("HTTP request duration in seconds")
    .with_unit("s")
    .with_boundaries(vec![0.005, 0.01, 0.025, 0.05, 0.1, 0.25, 0.5, 1.0, 2.5, 5.0])
    .build();
Default boundaries: [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]

AsyncInstrumentBuilder (Observable)

Asynchronous instruments require callbacks:
pub struct AsyncInstrumentBuilder<'a, I, M> {
    pub instrument_provider: &'a dyn InstrumentProvider,
    pub name: Cow<'static, str>,
    pub description: Option<Cow<'static, str>>,
    pub unit: Option<Cow<'static, str>>,
    pub callbacks: Vec<Callback<M>>,
}
Example:
let _observable_counter = meter
    .u64_observable_counter("cpu_time")
    .with_description("CPU time used by the process")
    .with_unit("ms")
    .with_callback(|observer| {
        let cpu_time = get_cpu_time(); // Your function
        observer.observe(cpu_time, &[KeyValue::new("core", "0")]);
    })
    .build();

Instrument Naming

Follow these conventions when naming instruments:
  • Use lowercase letters
  • Use underscores to separate words
  • Choose descriptive, meaningful names
  • Include the unit in the name when appropriate (e.g., duration_seconds, size_bytes)
  • Use plural for counters (e.g., requests_total, errors_count)
Instrument names must:
  • Not be empty
  • Start with an alphabetic character
  • Contain only alphanumeric characters, underscores, dots, and hyphens
  • Be no longer than 255 characters

Units

Specify units using standard abbreviations:
  • Time: ms, s, min, h, d
  • Bytes: By, KiBy, MiBy
  • Percentages: %
  • Counts: {items}, {requests}, {errors}
let duration = meter.f64_histogram("request_duration").with_unit("s").build();
let size = meter.u64_counter("bytes_sent").with_unit("By").build();
let usage = meter.f64_gauge("cpu_usage").with_unit("%").build();
Units are case-sensitive: kb (kilobit) is different from kB (kilobyte).

Cloning Instruments

All synchronous instruments can be cloned:
let counter = meter.u64_counter("requests").build();
let counter_clone = counter.clone();

// Both record to the same underlying metric
counter.add(1, &[]);
counter_clone.add(1, &[]);
Clone instruments when you need to share them across threads or modules. Avoid creating duplicate instruments with the same name, as this can impact performance.

Next Steps

Counter

Learn about Counter instruments in detail

Histogram

Understand how to use Histograms

Gauge

Record independent measurements with Gauges

Observable Instruments

Use callbacks for asynchronous measurements

Build docs developers (and LLMs) love