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

# Views

> Customize metric aggregation and output with Views in OpenTelemetry Rust

Views allow you to customize how metrics are aggregated and exported by the SDK. They provide powerful control over metric behavior without modifying your instrumentation code.

## What are Views?

Views are functions that match instruments and return customized `Stream` configurations. They enable you to:

* Rename metrics
* Change units
* Customize aggregation
* Filter attributes
* Set cardinality limits
* Drop unwanted metrics

## View Trait

```rust theme={null}
pub(crate) trait View: Send + Sync + 'static {
    fn match_inst(&self, inst: &Instrument) -> Option<Stream>;
}

impl<T> View for T
where
    T: Fn(&Instrument) -> Option<Stream> + Send + Sync + 'static,
{
    fn match_inst(&self, inst: &Instrument) -> Option<Stream> {
        self(inst)
    }
}
```

Views are implemented for any function that takes an `&Instrument` and returns `Option<Stream>`.

## Instrument Reference

The `Instrument` type provides information for matching:

```rust theme={null}
pub struct Instrument {
    pub(crate) name: Cow<'static, str>,
    pub(crate) description: Cow<'static, str>,
    pub(crate) kind: InstrumentKind,
    pub(crate) unit: Cow<'static, str>,
    pub(crate) scope: InstrumentationScope,
}

impl Instrument {
    pub fn name(&self) -> &str;
    pub fn kind(&self) -> InstrumentKind;
    pub fn unit(&self) -> &str;
    pub fn scope(&self) -> &InstrumentationScope;
}
```

## Stream Configuration

The `Stream` type defines how an instrument's data should be collected:

```rust theme={null}
pub struct Stream {
    name: Option<Cow<'static, str>>,
    description: Option<Cow<'static, str>>,
    unit: Option<Cow<'static, str>>,
    aggregation: Option<Aggregation>,
    allowed_attribute_keys: Option<Arc<HashSet<Key>>>,
    cardinality_limit: Option<usize>,
}
```

Create streams using the builder pattern:

```rust theme={null}
Stream::builder()
    .with_name("custom_name")
    .with_description("Custom description")
    .with_unit("ms")
    .with_aggregation(Aggregation::Sum)
    .with_cardinality_limit(100)
    .build()
```

## Registering Views

Register views when building the `MeterProvider`:

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

let my_view = |i: &Instrument| {
    if i.name() == "my_histogram" {
        Some(
            Stream::builder()
                .with_name("renamed_histogram")
                .build()
                .unwrap(),
        )
    } else {
        None
    }
};

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

## Common View Patterns

### Renaming Metrics

Change metric names without modifying code:

```rust theme={null}
let rename_view = |i: &Instrument| {
    if i.name() == "my_histogram" {
        Some(
            Stream::builder()
                .with_name("my_histogram_renamed")
                .with_unit("milliseconds")
                .build()
                .unwrap(),
        )
    } else {
        None
    }
};

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

### Changing Aggregation

Customize how metrics are aggregated:

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

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

### Setting Cardinality Limits

Limit the number of unique attribute combinations:

```rust theme={null}
let cardinality_limit_view = |i: &Instrument| {
    if i.name() == "my_second_histogram" {
        Stream::builder()
            .with_cardinality_limit(2)
            .build()
            .ok()
    } else {
        None
    }
};
```

When the limit is reached, additional attribute combinations are aggregated into an overflow bucket.

### Dropping Metrics

Exclude specific metrics from export:

```rust theme={null}
let drop_view = |i: &Instrument| {
    if i.name() == "unwanted_metric" {
        Stream::builder()
            .with_aggregation(Aggregation::Drop)
            .build()
            .ok()
    } else {
        None
    }
};
```

## Complete Example

From the `metrics-advanced` example:

```rust theme={null}
use opentelemetry::global;
use opentelemetry_sdk::metrics::{
    Aggregation, Instrument, SdkMeterProvider, Stream, Temporality
};
use opentelemetry_sdk::Resource;

fn init_meter_provider() -> SdkMeterProvider {
    // View 1: Rename and change unit
    let my_view_rename_and_unit = |i: &Instrument| {
        if i.name() == "my_histogram" {
            Some(
                Stream::builder()
                    .with_name("my_histogram_renamed")
                    .with_unit("milliseconds")
                    .build()
                    .unwrap(),
            )
        } else {
            None
        }
    };

    // View 2: Change cardinality limit
    let my_view_change_cardinality = |i: &Instrument| {
        if i.name() == "my_second_histogram" {
            Stream::builder()
                .with_cardinality_limit(2)
                .build()
                .ok()
        } else {
            None
        }
    };

    // View 3: Use exponential histogram
    let my_view_change_aggregation = |i: &Instrument| {
        if i.name() == "my_third_histogram" {
            Stream::builder()
                .with_aggregation(Aggregation::Base2ExponentialHistogram {
                    max_size: 160,
                    max_scale: 20,
                    record_min_max: true,
                })
                .build()
                .ok()
        } else {
            None
        }
    };

    let exporter = opentelemetry_stdout::MetricExporterBuilder::default()
        .with_temporality(Temporality::Delta)
        .build();

    let provider = SdkMeterProvider::builder()
        .with_periodic_exporter(exporter)
        .with_resource(
            Resource::builder()
                .with_service_name("metrics-advanced-example")
                .build(),
        )
        .with_view(my_view_rename_and_unit)
        .with_view(my_view_change_cardinality)
        .with_view(my_view_change_aggregation)
        .build();
    
    global::set_meter_provider(provider.clone());
    provider
}
```

## Aggregation Types

Views can specify different aggregation strategies:

### Default Aggregation

Use the instrument's default aggregation:

```rust theme={null}
Aggregation::Default
```

### Sum Aggregation

Sum all values:

```rust theme={null}
Aggregation::Sum
```

### LastValue Aggregation

Keep only the last value:

```rust theme={null}
Aggregation::LastValue
```

### Explicit Bucket Histogram

Histogram with custom boundaries:

```rust theme={null}
Aggregation::ExplicitBucketHistogram {
    boundaries: vec![0.0, 5.0, 10.0, 25.0, 50.0, 100.0],
    record_min_max: true,
}
```

### Exponential Histogram

Histogram with exponentially growing buckets:

```rust theme={null}
Aggregation::Base2ExponentialHistogram {
    max_size: 160,    // Maximum number of buckets
    max_scale: 20,    // Maximum resolution scale (-10 to 20)
    record_min_max: true,
}
```

<Note>
  Exponential histograms automatically adjust bucket widths, making them ideal for unpredictable value ranges (e.g., network latency with occasional outliers).
</Note>

### Drop Aggregation

Discard all data:

```rust theme={null}
Aggregation::Drop
```

## Exponential Histogram Details

Exponential histograms are useful when value ranges are unpredictable:

```rust theme={null}
let exponential_view = |i: &Instrument| {
    if i.name() == "network_latency" {
        Stream::builder()
            .with_aggregation(Aggregation::Base2ExponentialHistogram {
                max_size: 160,  // More buckets = more precision
                max_scale: 20,  // Higher scale = finer resolution
                record_min_max: true,
            })
            .build()
            .ok()
    } else {
        None
    }
};
```

**Parameters:**

* `max_size`: Maximum number of buckets (higher = more memory, better precision)
* `max_scale`: Resolution scale from -10 to 20 (higher = finer buckets)
* `record_min_max`: Whether to track min and max values

<Tip>
  Use exponential histograms for metrics like network round-trip time, where most values are small but occasional spikes occur.
</Tip>

## Pattern Matching

Match instruments by different criteria:

### By Name

```rust theme={null}
let view = |i: &Instrument| {
    if i.name() == "specific_metric" {
        Some(Stream::builder().with_name("renamed").build().unwrap())
    } else {
        None
    }
};
```

### By Name Pattern

```rust theme={null}
let view = |i: &Instrument| {
    if i.name().starts_with("http_") {
        Some(
            Stream::builder()
                .with_aggregation(Aggregation::ExplicitBucketHistogram {
                    boundaries: vec![0.01, 0.1, 1.0, 10.0],
                    record_min_max: true,
                })
                .build()
                .unwrap(),
        )
    } else {
        None
    }
};
```

### By Instrument Kind

```rust theme={null}
use opentelemetry_sdk::metrics::InstrumentKind;

let view = |i: &Instrument| {
    if i.kind() == InstrumentKind::Histogram {
        Some(
            Stream::builder()
                .with_cardinality_limit(100)
                .build()
                .unwrap(),
        )
    } else {
        None
    }
};
```

### By Scope

```rust theme={null}
let view = |i: &Instrument| {
    if i.scope().name() == "problematic-library" {
        Some(
            Stream::builder()
                .with_aggregation(Aggregation::Drop)
                .build()
                .unwrap(),
        )
    } else {
        None
    }
};
```

## Multiple Views

Register multiple views in order of precedence:

```rust theme={null}
let provider = SdkMeterProvider::builder()
    .with_view(view1)  // Checked first
    .with_view(view2)  // Checked second
    .with_view(view3)  // Checked third
    .build();
```

The first matching view is applied.

## Error Handling

Handle invalid stream configurations gracefully:

```rust theme={null}
let view = |i: &Instrument| {
    if i.name() == "my_metric" {
        // Using .ok() treats errors as non-matching
        Stream::builder()
            .with_cardinality_limit(2)
            .build()
            .ok()
    } else {
        None
    }
};
```

Alternatively, handle errors explicitly:

```rust theme={null}
let view = |i: &Instrument| {
    if i.name() == "my_metric" {
        match Stream::builder().with_cardinality_limit(2).build() {
            Ok(stream) => Some(stream),
            Err(e) => {
                eprintln!("Failed to create stream: {}", e);
                None
            }
        }
    } else {
        None
    }
};
```

## Use Cases

### Reduce Cardinality

Limit high-cardinality metrics:

```rust theme={null}
let limit_cardinality = |i: &Instrument| {
    if i.name().starts_with("user_") {
        Stream::builder()
            .with_cardinality_limit(100)
            .build()
            .ok()
    } else {
        None
    }
};
```

### Standardize Units

Convert all duration metrics to seconds:

```rust theme={null}
let standardize_units = |i: &Instrument| {
    if i.unit() == "ms" {
        Some(
            Stream::builder()
                .with_unit("s")
                .build()
                .unwrap(),
        )
    } else {
        None
    }
};
```

### Filter Noisy Metrics

Drop verbose debug metrics in production:

```rust theme={null}
let filter_debug = |i: &Instrument| {
    if i.name().starts_with("debug_") {
        Stream::builder()
            .with_aggregation(Aggregation::Drop)
            .build()
            .ok()
    } else {
        None
    }
};
```

## Best Practices

1. **Return `None` for non-matching instruments**: This allows other views to apply
2. **Use `.ok()` for graceful errors**: Treats invalid configs as non-matching
3. **Order views by specificity**: More specific views first, general views last
4. **Test view configurations**: Ensure views apply as expected
5. **Document view purposes**: Explain why each view exists

## Next Steps

<CardGroup cols={2}>
  <Card title="Instruments" href="/metrics/instruments">
    Learn about different instrument types
  </Card>

  <Card title="Histogram" href="/metrics/histogram">
    Understand histogram configuration
  </Card>

  <Card title="Meters" href="/metrics/meters">
    Create and configure Meters
  </Card>

  <Card title="Counter" href="/metrics/counter">
    Use Counter instruments
  </Card>
</CardGroup>
