Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions opentelemetry-otlp/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@
- Fixed [#2777](https://github.com/open-telemetry/opentelemetry rust/issues/2777) to properly handle `shutdown_with_timeout()` when using `grpc-tonic`.
- Deprecate `tls` feature in favor of explicit `tls-ring` and `tls-aws-lc` features.
**Migration**: Replace `tls` with `tls-ring` (or `tls-aws-lc`). Users of `tls-roots` or `tls-webpki-roots` must now also enable one of these.
- Support `OTEL_EXPORTER_OTLP_METRICS_DEFAULT_HISTOGRAM_AGGREGATION` ([#3433][3433]).

## 0.31.0

Expand Down
7 changes: 6 additions & 1 deletion opentelemetry-otlp/src/exporter/http/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -355,6 +355,7 @@ impl HttpExporterBuilder {
pub fn build_metrics_exporter(
mut self,
temporality: opentelemetry_sdk::metrics::Temporality,
histogram_aggregation: opentelemetry_sdk::metrics::HistogramAggregation,
) -> Result<crate::MetricExporter, ExporterBuildError> {
use crate::{
OTEL_EXPORTER_OTLP_METRICS_COMPRESSION, OTEL_EXPORTER_OTLP_METRICS_ENDPOINT,
Expand All @@ -371,7 +372,11 @@ impl HttpExporterBuilder {
OTEL_EXPORTER_OTLP_METRICS_PROTOCOL,
)?;

Ok(crate::MetricExporter::from_http(client, temporality))
Ok(crate::MetricExporter::from_http(
client,
temporality,
histogram_aggregation,
))
}
}

Expand Down
7 changes: 6 additions & 1 deletion opentelemetry-otlp/src/exporter/tonic/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -400,6 +400,7 @@ impl TonicExporterBuilder {
pub(crate) fn build_metrics_exporter(
self,
temporality: opentelemetry_sdk::metrics::Temporality,
histogram_aggregation: opentelemetry_sdk::metrics::HistogramAggregation,
) -> Result<crate::MetricExporter, ExporterBuildError> {
use crate::MetricExporter;
use metrics::TonicMetricsClient;
Expand All @@ -416,7 +417,11 @@ impl TonicExporterBuilder {

let client = TonicMetricsClient::new(channel, interceptor, compression, retry_policy);

Ok(MetricExporter::from_tonic(client, temporality))
Ok(MetricExporter::from_tonic(
client,
temporality,
histogram_aggregation,
))
}

/// Build a new tonic span exporter
Expand Down
7 changes: 4 additions & 3 deletions opentelemetry-otlp/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -245,6 +245,7 @@
//! | `OTEL_EXPORTER_OTLP_METRICS_HEADERS` | Signal-specific headers for metrics exports. |
//! | `OTEL_EXPORTER_OTLP_METRICS_COMPRESSION` | Signal-specific compression for metrics exports. |
//! | `OTEL_EXPORTER_OTLP_METRICS_TEMPORALITY_PREFERENCE` | Temporality preference for metrics. Valid values: `cumulative`, `delta`, `lowmemory` (case-insensitive). | `cumulative` |
//! | `OTEL_EXPORTER_OTLP_METRICS_DEFAULT_HISTOGRAM_AGGREGATION` | Default histogram aggregation. Valid values: `explicit_bucket_histogram`, `base2_exponential_bucket_histogram` (case-insensitive). | `explicit_bucket_histogram` |
//!
//! ## Logs
//!
Expand Down Expand Up @@ -662,9 +663,9 @@ pub use crate::span::{
#[cfg(any(feature = "http-proto", feature = "http-json", feature = "grpc-tonic"))]
pub use crate::metric::{
MetricExporter, MetricExporterBuilder, OTEL_EXPORTER_OTLP_METRICS_COMPRESSION,
OTEL_EXPORTER_OTLP_METRICS_ENDPOINT, OTEL_EXPORTER_OTLP_METRICS_HEADERS,
OTEL_EXPORTER_OTLP_METRICS_PROTOCOL, OTEL_EXPORTER_OTLP_METRICS_TEMPORALITY_PREFERENCE,
OTEL_EXPORTER_OTLP_METRICS_TIMEOUT,
OTEL_EXPORTER_OTLP_METRICS_DEFAULT_HISTOGRAM_AGGREGATION, OTEL_EXPORTER_OTLP_METRICS_ENDPOINT,
OTEL_EXPORTER_OTLP_METRICS_HEADERS, OTEL_EXPORTER_OTLP_METRICS_PROTOCOL,
OTEL_EXPORTER_OTLP_METRICS_TEMPORALITY_PREFERENCE, OTEL_EXPORTER_OTLP_METRICS_TIMEOUT,
};

#[cfg(feature = "logs")]
Expand Down
167 changes: 164 additions & 3 deletions opentelemetry-otlp/src/metric.rs
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ use core::fmt;
use opentelemetry_sdk::error::OTelSdkResult;

use opentelemetry_sdk::metrics::{
data::ResourceMetrics, exporter::PushMetricExporter, Temporality,
data::ResourceMetrics, exporter::PushMetricExporter, HistogramAggregation, Temporality,
};
use std::fmt::{Debug, Formatter};
use std::time::Duration;
Expand All @@ -47,12 +47,18 @@ pub const OTEL_EXPORTER_OTLP_METRICS_PROTOCOL: &str = "OTEL_EXPORTER_OTLP_METRIC
/// Temporality preference for metrics, defaults to cumulative.
pub const OTEL_EXPORTER_OTLP_METRICS_TEMPORALITY_PREFERENCE: &str =
"OTEL_EXPORTER_OTLP_METRICS_TEMPORALITY_PREFERENCE";
/// Default histogram aggregation for metrics.
/// Valid values: `explicit_bucket_histogram`, `base2_exponential_bucket_histogram` (case-insensitive).
/// Defaults to `explicit_bucket_histogram`.
pub const OTEL_EXPORTER_OTLP_METRICS_DEFAULT_HISTOGRAM_AGGREGATION: &str =
"OTEL_EXPORTER_OTLP_METRICS_DEFAULT_HISTOGRAM_AGGREGATION";

/// A builder for creating a new [MetricExporter].
#[derive(Debug, Default, Clone)]
pub struct MetricExporterBuilder<C> {
client: C,
temporality: Option<Temporality>,
histogram_aggregation: Option<HistogramAggregation>,
}

impl MetricExporterBuilder<NoExporterBuilderSet> {
Expand Down Expand Up @@ -95,6 +101,7 @@ impl<C> MetricExporterBuilder<C> {
MetricExporterBuilder {
client: TonicExporterBuilderSet(TonicExporterBuilder::default()),
temporality: self.temporality,
histogram_aggregation: self.histogram_aggregation,
}
}

Expand All @@ -104,6 +111,7 @@ impl<C> MetricExporterBuilder<C> {
MetricExporterBuilder {
client: HttpExporterBuilderSet(HttpExporterBuilder::default()),
temporality: self.temporality,
histogram_aggregation: self.histogram_aggregation,
}
}

Expand All @@ -114,6 +122,25 @@ impl<C> MetricExporterBuilder<C> {
MetricExporterBuilder {
client: self.client,
temporality: Some(temporality),
histogram_aggregation: self.histogram_aggregation,
}
}

/// Set the default histogram aggregation for the metrics.
///
/// Valid values: [HistogramAggregation::ExplicitBucketHistogram] (default),
/// [HistogramAggregation::Base2ExponentialBucketHistogram].
///
/// Note: Programmatically setting this will override any value set via the
/// `OTEL_EXPORTER_OTLP_METRICS_DEFAULT_HISTOGRAM_AGGREGATION` environment variable.
pub fn with_histogram_aggregation(
self,
histogram_aggregation: HistogramAggregation,
) -> MetricExporterBuilder<C> {
MetricExporterBuilder {
client: self.client,
temporality: self.temporality,
histogram_aggregation: Some(histogram_aggregation),
}
}
}
Expand All @@ -138,12 +165,40 @@ fn resolve_temporality(provided: Option<Temporality>) -> Result<Temporality, Exp
Ok(Temporality::default())
}

/// Resolve histogram aggregation with priority:
/// 1. Provided config value
/// 2. OTEL_EXPORTER_OTLP_METRICS_DEFAULT_HISTOGRAM_AGGREGATION environment variable
/// 3. Default (ExplicitBucketHistogram)
#[cfg(any(feature = "http-proto", feature = "http-json", feature = "grpc-tonic"))]
fn resolve_histogram_aggregation(
provided: Option<HistogramAggregation>,
) -> Result<HistogramAggregation, ExporterBuildError> {
if let Some(histogram_aggregation) = provided {
return Ok(histogram_aggregation);
}
if let Ok(val) = std::env::var(OTEL_EXPORTER_OTLP_METRICS_DEFAULT_HISTOGRAM_AGGREGATION) {
return val
.parse::<HistogramAggregation>()
.map_err(|_| ExporterBuildError::InvalidConfig {
name: OTEL_EXPORTER_OTLP_METRICS_DEFAULT_HISTOGRAM_AGGREGATION.to_string(),
reason: format!(
"Invalid value '{val}'. Expected: explicit_bucket_histogram or base2_exponential_bucket_histogram"
),
});
}
Ok(HistogramAggregation::default())
}

#[cfg(feature = "grpc-tonic")]
impl MetricExporterBuilder<TonicExporterBuilderSet> {
/// Build the [MetricExporter] with the gRPC Tonic transport.
pub fn build(self) -> Result<MetricExporter, ExporterBuildError> {
let temporality = resolve_temporality(self.temporality)?;
let exporter = self.client.0.build_metrics_exporter(temporality)?;
let histogram_aggregation = resolve_histogram_aggregation(self.histogram_aggregation)?;
let exporter = self
.client
.0
.build_metrics_exporter(temporality, histogram_aggregation)?;
opentelemetry::otel_debug!(name: "MetricExporterBuilt");
Ok(exporter)
}
Expand All @@ -154,7 +209,11 @@ impl MetricExporterBuilder<HttpExporterBuilderSet> {
/// Build the [MetricExporter] with the HTTP transport.
pub fn build(self) -> Result<MetricExporter, ExporterBuildError> {
let temporality = resolve_temporality(self.temporality)?;
let exporter = self.client.0.build_metrics_exporter(temporality)?;
let histogram_aggregation = resolve_histogram_aggregation(self.histogram_aggregation)?;
let exporter = self
.client
.0
.build_metrics_exporter(temporality, histogram_aggregation)?;
Ok(exporter)
}
}
Expand Down Expand Up @@ -200,6 +259,7 @@ pub(crate) trait MetricsClient: fmt::Debug + Send + Sync + 'static {
pub struct MetricExporter {
client: SupportedTransportClient,
temporality: Temporality,
histogram_aggregation: HistogramAggregation,
}

#[derive(Debug)]
Expand Down Expand Up @@ -247,6 +307,10 @@ impl PushMetricExporter for MetricExporter {
fn temporality(&self) -> Temporality {
self.temporality
}

fn default_histogram_aggregation(&self) -> HistogramAggregation {
self.histogram_aggregation
}
}

impl MetricExporter {
Expand All @@ -259,21 +323,25 @@ impl MetricExporter {
pub(crate) fn from_tonic(
client: crate::exporter::tonic::metrics::TonicMetricsClient,
temporality: Temporality,
histogram_aggregation: HistogramAggregation,
) -> Self {
Self {
client: SupportedTransportClient::Tonic(client),
temporality,
histogram_aggregation,
}
}

#[cfg(any(feature = "http-proto", feature = "http-json"))]
pub(crate) fn from_http(
client: crate::exporter::http::OtlpHttpClient,
temporality: Temporality,
histogram_aggregation: HistogramAggregation,
) -> Self {
Self {
client: SupportedTransportClient::Http(client),
temporality,
histogram_aggregation,
}
}
}
Expand Down Expand Up @@ -379,4 +447,97 @@ mod tests {
assert_eq!(result, Temporality::Cumulative);
});
}

#[test]
fn histogram_aggregation_code_config_overrides_env_var() {
run_env_test(
vec![(
OTEL_EXPORTER_OTLP_METRICS_DEFAULT_HISTOGRAM_AGGREGATION,
"explicit_bucket_histogram",
)],
|| {
let result = resolve_histogram_aggregation(Some(
HistogramAggregation::Base2ExponentialBucketHistogram,
))
.unwrap();
assert_eq!(
result,
HistogramAggregation::Base2ExponentialBucketHistogram
);
},
);
}

#[test]
fn histogram_aggregation_env_var_sets_explicit_bucket() {
run_env_test(
vec![(
OTEL_EXPORTER_OTLP_METRICS_DEFAULT_HISTOGRAM_AGGREGATION,
"explicit_bucket_histogram",
)],
|| {
let result = resolve_histogram_aggregation(None).unwrap();
assert_eq!(result, HistogramAggregation::ExplicitBucketHistogram);
},
);
}

#[test]
fn histogram_aggregation_env_var_sets_base2_exponential() {
run_env_test(
vec![(
OTEL_EXPORTER_OTLP_METRICS_DEFAULT_HISTOGRAM_AGGREGATION,
"base2_exponential_bucket_histogram",
)],
|| {
let result = resolve_histogram_aggregation(None).unwrap();
assert_eq!(
result,
HistogramAggregation::Base2ExponentialBucketHistogram
);
},
);
}

#[test]
fn histogram_aggregation_env_var_case_insensitive() {
run_env_test(
vec![(
OTEL_EXPORTER_OTLP_METRICS_DEFAULT_HISTOGRAM_AGGREGATION,
"BASE2_EXPONENTIAL_BUCKET_HISTOGRAM",
)],
|| {
let result = resolve_histogram_aggregation(None).unwrap();
assert_eq!(
result,
HistogramAggregation::Base2ExponentialBucketHistogram
);
},
);
}

#[test]
fn histogram_aggregation_invalid_env_var_returns_error() {
run_env_test(
vec![(
OTEL_EXPORTER_OTLP_METRICS_DEFAULT_HISTOGRAM_AGGREGATION,
"invalid",
)],
|| {
let result = resolve_histogram_aggregation(None);
assert!(result.is_err());
},
);
}

#[test]
fn histogram_aggregation_default_when_nothing_set() {
temp_env::with_var_unset(
OTEL_EXPORTER_OTLP_METRICS_DEFAULT_HISTOGRAM_AGGREGATION,
|| {
let result = resolve_histogram_aggregation(None).unwrap();
assert_eq!(result, HistogramAggregation::ExplicitBucketHistogram);
},
);
}
}
1 change: 1 addition & 0 deletions opentelemetry-sdk/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@
- Updated `SpanProcessor::on_end` documentation to clarify that `Context::current()` returns the parent context, not the span's context
- Fix `traceparent` headers with unknown flags (e.g. W3C random-trace-id flag `0x02`) being incorrectly rejected. Unknown flags are now accepted and zeroed out as required by the W3C trace-context spec. [#3435][3435]
- **Breaking** `InMemoryExporterError` has been removed and replaced by `OTelSdkError`, and a new `JaegerRemoteSamplerBuildError` introduced to replace last uses of `TraceError`. [#3458][3458]
- Support `OTEL_EXPORTER_OTLP_METRICS_DEFAULT_HISTOGRAM_AGGREGATION` ([#3433][3433]).

[3227]: https://github.com/open-telemetry/opentelemetry-rust/pull/3227
[3277]: https://github.com/open-telemetry/opentelemetry-rust/pull/3277
Expand Down
7 changes: 6 additions & 1 deletion opentelemetry-sdk/src/metrics/exporter.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ use std::time::Duration;

use crate::metrics::data::ResourceMetrics;

use super::Temporality;
use super::{HistogramAggregation, Temporality};

/// Exporter handles the delivery of metric data to external receivers.
///
Expand Down Expand Up @@ -37,4 +37,9 @@ pub trait PushMetricExporter: Send + Sync + 'static {

/// Access the [Temporality] of the MetricExporter.
fn temporality(&self) -> Temporality;

/// The default aggregation to use for histogram instruments.
fn default_histogram_aggregation(&self) -> HistogramAggregation {
HistogramAggregation::default()
}
}
6 changes: 5 additions & 1 deletion opentelemetry-sdk/src/metrics/in_memory_exporter.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ use crate::metrics::data::{
ExponentialHistogram, Gauge, Histogram, MetricData, ResourceMetrics, Sum,
};
use crate::metrics::exporter::PushMetricExporter;
use crate::metrics::Temporality;
use crate::metrics::{HistogramAggregation, Temporality};
use std::collections::VecDeque;
use std::fmt;
use std::sync::{Arc, Mutex};
Expand Down Expand Up @@ -260,4 +260,8 @@ impl PushMetricExporter for InMemoryMetricExporter {
fn temporality(&self) -> Temporality {
self.temporality
}

fn default_histogram_aggregation(&self) -> HistogramAggregation {
HistogramAggregation::ExplicitBucketHistogram
}
}
Loading
Loading