> ## Documentation Index
> Fetch the complete documentation index at: https://mintlify.com/open-telemetry/opentelemetry-rust/llms.txt
> Use this file to discover all available pages before exploring further.

# Histogram

> Record distributions of values with Histogram instruments in OpenTelemetry Rust

A `Histogram` is a synchronous instrument that records a distribution of values. Histograms are ideal for understanding the statistical distribution of measurements like request duration, response sizes, or temperatures.

## When to Use Histogram

Use a Histogram when:

* You need to calculate percentiles (p50, p95, p99)
* You want to understand the distribution of values
* Min, max, sum, and count are all valuable
* Values represent measurements or observations

**Common examples:**

* HTTP request duration
* Response payload size
* Database query latency
* Temperature readings
* Request processing time

<Note>
  Histograms are more expensive than counters because they record individual measurements and calculate statistics. Use them only when you need distribution data.
</Note>

## API Reference

```rust theme={null}
pub struct Histogram<T>(Arc<dyn SyncInstrument<T> + Send + Sync>);

impl<T> Histogram<T> {
    pub fn record(&self, value: T, attributes: &[KeyValue]);
}
```

The `record` method adds a value to the distribution.

## Creating a Histogram

Histograms support `u64` and `f64` data types:

```rust theme={null}
use opentelemetry::global;

let meter = global::meter("my-app");

// f64 histogram (most common)
let duration_histogram = meter.f64_histogram("http_request_duration")
    .with_description("HTTP request duration in seconds")
    .with_unit("s")
    .build();

// u64 histogram
let size_histogram = meter.u64_histogram("response_size")
    .with_description("HTTP response size in bytes")
    .with_unit("By")
    .build();
```

## Recording Measurements

Use the `record` method to add values to the histogram:

```rust theme={null}
use opentelemetry::KeyValue;

// Record a single value
duration_histogram.record(0.123, &[]);

// Record with attributes
duration_histogram.record(
    0.456,
    &[
        KeyValue::new("method", "GET"),
        KeyValue::new("endpoint", "/api/users"),
        KeyValue::new("status", "200"),
    ],
);

// Record response sizes
size_histogram.record(1024, &[KeyValue::new("content_type", "application/json")]);
```

## Configuring Bucket Boundaries

Histogram accuracy depends on bucket boundaries. You can customize them when creating the histogram:

```rust theme={null}
let histogram = meter.f64_histogram("request_duration")
    .with_description("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, 10.0
    ])
    .build();
```

### Default Boundaries

If you don't specify boundaries, the default is:

```rust theme={null}
vec![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]
```

### Boundary Requirements

<Warning>
  Boundaries must:

  * Not contain `f64::NAN`, `f64::INFINITY`, or `f64::NEG_INFINITY`
  * Be in strictly increasing order
  * Not contain duplicate values

  Invalid boundaries will cause the instrument to not report measurements.
</Warning>

### Choosing Boundaries

Choose boundaries based on your expected value range:

```rust theme={null}
// For request duration in seconds (most requests < 1s)
let latency = meter.f64_histogram("latency")
    .with_boundaries(vec![0.005, 0.01, 0.025, 0.05, 0.1, 0.25, 0.5, 1.0, 2.5, 5.0])
    .build();

// For response size in bytes
let size = meter.u64_histogram("response_size")
    .with_boundaries(vec![100.0, 500.0, 1000.0, 5000.0, 10000.0, 50000.0, 100000.0])
    .build();

// For fine-grained millisecond measurements
let ms_latency = meter.f64_histogram("db_query_duration")
    .with_unit("ms")
    .with_boundaries(vec![1.0, 2.0, 5.0, 10.0, 20.0, 50.0, 100.0, 200.0, 500.0, 1000.0])
    .build();
```

<Tip>
  Use more buckets for ranges where you expect most values. This improves percentile accuracy but increases memory and network usage.
</Tip>

## Complete Example

```rust theme={null}
use opentelemetry::{global, KeyValue};
use opentelemetry_sdk::metrics::SdkMeterProvider;
use opentelemetry_sdk::Resource;
use std::time::Instant;

fn init_meter_provider() -> SdkMeterProvider {
    let exporter = opentelemetry_stdout::MetricExporterBuilder::default().build();
    let provider = SdkMeterProvider::builder()
        .with_periodic_exporter(exporter)
        .with_resource(
            Resource::builder()
                .with_service_name("my-service")
                .build(),
        )
        .build();
    global::set_meter_provider(provider.clone());
    provider
}

fn main() -> Result<(), Box<dyn std::error::Error>> {
    let meter_provider = init_meter_provider();
    let meter = global::meter("http-server");

    // Create a histogram for request duration
    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();

    // Simulate handling a request
    let start = Instant::now();
    // ... process request ...
    let duration = start.elapsed().as_secs_f64();

    histogram.record(
        duration,
        &[
            KeyValue::new("method", "GET"),
            KeyValue::new("endpoint", "/api/users"),
            KeyValue::new("status", "200"),
        ],
    );

    meter_provider.shutdown()?;
    Ok(())
}
```

## Example from Source

From the `metrics-basic` example:

```rust theme={null}
// Create a Histogram Instrument
let histogram = meter
    .f64_histogram("my_histogram")
    .with_description("My histogram example description")
    .with_boundaries(vec![0.0, 5.0, 10.0, 15.0, 20.0, 25.0])
    .build();

// Record measurements
histogram.record(
    10.5,
    &[
        KeyValue::new("mykey1", "myvalue1"),
        KeyValue::new("mykey2", "myvalue2"),
    ],
);
```

## Exponential Histograms

For unpredictable value ranges, use exponential histograms via Views. They automatically adjust bucket widths:

```rust theme={null}
use opentelemetry_sdk::metrics::{Aggregation, Instrument, Stream};

let exponential_histogram_view = |i: &Instrument| {
    if i.name() == "my_histogram" {
        Stream::builder()
            .with_aggregation(Aggregation::Base2ExponentialHistogram {
                max_size: 160,
                max_scale: 20,
                record_min_max: true,
            })
            .build()
            .ok()
    } else {
        None
    }
};

let provider = SdkMeterProvider::builder()
    .with_view(exponential_histogram_view)
    .build();
```

See [Views](/metrics/views) for more details on customizing aggregation.

## Attributes and Cardinality

Like counters, histograms support attributes:

```rust theme={null}
histogram.record(
    0.123,
    &[
        KeyValue::new("method", "GET"),
        KeyValue::new("status", "200"),
    ],
);
```

<Warning>
  Each unique attribute combination creates a separate histogram. Keep cardinality low to avoid memory issues.
</Warning>

## Cloning Histograms

Histograms can be cloned to share across your application:

```rust theme={null}
let histogram = meter.f64_histogram("latency").build();
let histogram_clone = histogram.clone();

// Both record to the same underlying histogram
histogram.record(0.1, &[]);
histogram_clone.record(0.2, &[]);
```

<Tip>
  Clone histograms instead of creating duplicates. Multiple histograms with the same name can lower SDK performance.
</Tip>

## Histogram vs. Gauge

<CodeGroup>
  ```rust Histogram theme={null}
  // Use for distributions - captures min, max, percentiles
  let histogram = meter.f64_histogram("request_duration").build();
  histogram.record(0.123, &[]);
  histogram.record(0.456, &[]);
  histogram.record(0.789, &[]);
  // Results: min, max, p50, p95, p99, count, sum
  ```

  ```rust Gauge theme={null}
  // Use for current values - only last value matters
  let gauge = meter.f64_gauge("cpu_usage").build();
  gauge.record(45.2, &[]);
  gauge.record(67.8, &[]);
  gauge.record(52.1, &[]);
  // Results: 52.1 (last value)
  ```
</CodeGroup>

## Best Practices

1. **Choose appropriate boundaries**: Match your expected value range
2. **Use consistent units**: Seconds for duration, bytes for size
3. **Keep cardinality low**: Limit unique attribute combinations
4. **Clone when sharing**: Don't create duplicate histograms
5. **Use f64 for most cases**: Better precision for measurements
6. **Consider exponential histograms**: For unpredictable ranges

## Next Steps

<CardGroup cols={2}>
  <Card title="Counter" href="/metrics/counter">
    Record monotonically increasing values
  </Card>

  <Card title="Gauge" href="/metrics/gauge">
    Record independent point-in-time values
  </Card>

  <Card title="Views" href="/metrics/views">
    Customize histogram aggregation and boundaries
  </Card>

  <Card title="Observable Instruments" href="/metrics/observable-instruments">
    Use callbacks to report measurements
  </Card>
</CardGroup>
