diff --git a/opentelemetry-prometheus/CHANGELOG.md b/opentelemetry-prometheus/CHANGELOG.md index 6d03328c24..d733e2e91b 100644 --- a/opentelemetry-prometheus/CHANGELOG.md +++ b/opentelemetry-prometheus/CHANGELOG.md @@ -2,6 +2,10 @@ ## vNext +- Update `opentelemetry` dependency version to 0.30 +- Update `opentelemetry_sdk` dependency version to 0.30 +- Update `opentelemetry-semantic-conventions` dependency version to 0.30 + ## 0.29.1 Released 2025-April-11 diff --git a/opentelemetry-prometheus/Cargo.toml b/opentelemetry-prometheus/Cargo.toml index caf8f4c8bd..b50a3a014e 100644 --- a/opentelemetry-prometheus/Cargo.toml +++ b/opentelemetry-prometheus/Cargo.toml @@ -21,13 +21,13 @@ rustdoc-args = ["--cfg", "docsrs"] [dependencies] once_cell = { version = "1.13" } -opentelemetry = { version = "0.29", default-features = false, features = ["metrics"] } -opentelemetry_sdk = { version = "0.29", default-features = false, features = ["metrics"] } +opentelemetry = { version = "0.30", default-features = false, features = ["metrics"] } +opentelemetry_sdk = { version = "0.30", default-features = false, features = ["metrics", "experimental_metrics_custom_reader"] } prometheus = "0.14" tracing = { version = ">=0.1.40", default-features = false, optional = true } # optional for opentelemetry internal logging [dev-dependencies] -opentelemetry-semantic-conventions = { version = "0.29" } +opentelemetry-semantic-conventions = { version = "0.30" } http-body-util = { version = "0.1" } hyper = { version = "1.3", features = ["full"] } hyper-util = { version = "0.1", features = ["full"] } diff --git a/opentelemetry-prometheus/src/config.rs b/opentelemetry-prometheus/src/config.rs index cec975e485..1ce22ee59a 100644 --- a/opentelemetry-prometheus/src/config.rs +++ b/opentelemetry-prometheus/src/config.rs @@ -1,6 +1,6 @@ use core::fmt; use once_cell::sync::OnceCell; -use opentelemetry_sdk::metrics::{ManualReaderBuilder, MetricError, MetricResult}; +use opentelemetry_sdk::{error::OTelSdkError, metrics::ManualReaderBuilder}; use std::sync::{Arc, Mutex}; use crate::{Collector, PrometheusExporter, ResourceSelector}; @@ -115,7 +115,7 @@ impl ExporterBuilder { } /// Creates a new [PrometheusExporter] from this configuration. - pub fn build(self) -> MetricResult { + pub fn build(self) -> Result { let reader = Arc::new(self.reader.build()); let collector = Collector { @@ -134,7 +134,7 @@ impl ExporterBuilder { let registry = self.registry.unwrap_or_default(); registry .register(Box::new(collector)) - .map_err(|e| MetricError::Other(e.to_string()))?; + .map_err(|e| OTelSdkError::InternalFailure(e.to_string()))?; Ok(PrometheusExporter { reader }) } diff --git a/opentelemetry-prometheus/src/lib.rs b/opentelemetry-prometheus/src/lib.rs index 3ec3879779..75bb7a4642 100644 --- a/opentelemetry-prometheus/src/lib.rs +++ b/opentelemetry-prometheus/src/lib.rs @@ -2,9 +2,9 @@ //! //!
//! Warning: This crate is no longer recommended for use.

-//! Development of the Prometheus exporter has been discontinued. See the related +//! Development of the Prometheus exporter has been discontinued. See the related //! [issue](https://github.com/open-telemetry/opentelemetry-rust/issues/2451). -//! This crate depends on the unmaintained `protobuf` crate and has unresolved +//! This crate depends on the unmaintained `protobuf` crate and has unresolved //! security vulnerabilities. Version 0.29 will be the final release. //! //! For Prometheus integration, we strongly recommend using the [OTLP] exporter instead. @@ -106,14 +106,14 @@ )] #![cfg_attr(test, deny(warnings))] -use once_cell::sync::{Lazy, OnceCell}; +use once_cell::sync::OnceCell; use opentelemetry::{otel_error, otel_warn, InstrumentationScope, Key, Value}; use opentelemetry_sdk::{ error::OTelSdkResult, metrics::{ - data::{self, ResourceMetrics}, + data::{self, AggregatedMetrics, MetricData, ResourceMetrics}, reader::MetricReader, - InstrumentKind, ManualReader, MetricResult, Pipeline, Temporality, + InstrumentKind, ManualReader, Pipeline, Temporality, }, Resource, }; @@ -122,7 +122,6 @@ use prometheus::{ proto::{LabelPair, MetricFamily, MetricType}, }; use std::{ - any::TypeId, borrow::Cow, collections::{BTreeMap, HashMap}, sync::{Arc, Mutex}, @@ -164,7 +163,7 @@ impl MetricReader for PrometheusExporter { self.reader.register_pipeline(pipeline) } - fn collect(&self, rm: &mut ResourceMetrics) -> MetricResult<()> { + fn collect(&self, rm: &mut ResourceMetrics) -> OTelSdkResult { self.reader.collect(rm) } @@ -176,6 +175,10 @@ impl MetricReader for PrometheusExporter { self.reader.shutdown() } + fn shutdown_with_timeout(&self, timeout: std::time::Duration) -> OTelSdkResult { + self.reader.shutdown_with_timeout(timeout) + } + /// Note: Prometheus only supports cumulative temporality, so this will always be /// [Temporality::Cumulative]. fn temporality(&self, _kind: InstrumentKind) -> Temporality { @@ -202,71 +205,56 @@ struct CollectorInner { metric_families: HashMap, } -// TODO: Remove lazy and switch to pattern matching once `TypeId` is stable in -// const context: https://github.com/rust-lang/rust/issues/77125 -static HISTOGRAM_TYPES: Lazy<[TypeId; 3]> = Lazy::new(|| { - [ - TypeId::of::>(), - TypeId::of::>(), - TypeId::of::>(), - ] -}); -static SUM_TYPES: Lazy<[TypeId; 3]> = Lazy::new(|| { - [ - TypeId::of::>(), - TypeId::of::>(), - TypeId::of::>(), - ] -}); -static GAUGE_TYPES: Lazy<[TypeId; 3]> = Lazy::new(|| { - [ - TypeId::of::>(), - TypeId::of::>(), - TypeId::of::>(), - ] -}); - impl Collector { - fn metric_type_and_name(&self, m: &data::Metric) -> Option<(MetricType, Cow<'static, str>)> { - let mut name = self.get_name(m); - - let data = m.data.as_any(); - let type_id = data.type_id(); - - if HISTOGRAM_TYPES.contains(&type_id) { - Some((MetricType::HISTOGRAM, name)) - } else if GAUGE_TYPES.contains(&type_id) { - Some((MetricType::GAUGE, name)) - } else if SUM_TYPES.contains(&type_id) { - let is_monotonic = if let Some(v) = data.downcast_ref::>() { - v.is_monotonic - } else if let Some(v) = data.downcast_ref::>() { - v.is_monotonic - } else if let Some(v) = data.downcast_ref::>() { - v.is_monotonic - } else { - false - }; - + fn metric_type_and_name<'a>(&self, m: &'a data::Metric) -> Option<(MetricType, Cow<'a, str>)> { + let name = self.get_name(m); + + fn for_sum<'a, T>( + this: &Collector, + mut name: Cow<'a, str>, + sum: &data::Sum, + ) -> Option<(MetricType, Cow<'a, str>)> { + let is_monotonic = sum.is_monotonic(); if is_monotonic { - if !self.without_counter_suffixes { + if !this.without_counter_suffixes { name = format!("{name}{COUNTER_SUFFIX}").into(); } Some((MetricType::COUNTER, name)) } else { Some((MetricType::GAUGE, name)) } - } else { - None + } + + match m.data() { + AggregatedMetrics::U64(MetricData::Histogram(_)) + | AggregatedMetrics::F64(MetricData::Histogram(_)) + | AggregatedMetrics::I64(MetricData::Histogram(_)) => { + Some((MetricType::HISTOGRAM, name)) + } + + AggregatedMetrics::U64(MetricData::Gauge(_)) + | AggregatedMetrics::F64(MetricData::Gauge(_)) + | AggregatedMetrics::I64(MetricData::Gauge(_)) => Some((MetricType::GAUGE, name)), + + AggregatedMetrics::U64(MetricData::Sum(sum)) => for_sum(self, name, sum), + AggregatedMetrics::F64(MetricData::Sum(sum)) => for_sum(self, name, sum), + AggregatedMetrics::I64(MetricData::Sum(sum)) => for_sum(self, name, sum), + + AggregatedMetrics::U64(MetricData::ExponentialHistogram(_)) + | AggregatedMetrics::F64(MetricData::ExponentialHistogram(_)) + | AggregatedMetrics::I64(MetricData::ExponentialHistogram(_)) => { + // Unsupported + None + } } } - fn get_name(&self, m: &data::Metric) -> Cow<'static, str> { - let name = utils::sanitize_name(&m.name); + fn get_name<'a>(&self, m: &'a data::Metric) -> Cow<'a, str> { + let name = utils::sanitize_name(m.name()); let unit_suffixes = if self.without_units { None } else { - utils::get_unit_suffixes(&m.unit) + utils::get_unit_suffixes(m.unit()) }; match (&self.namespace, unit_suffixes) { (Some(namespace), Some(suffix)) => Cow::Owned(format!("{namespace}{name}_{suffix}")), @@ -294,10 +282,7 @@ impl prometheus::core::Collector for Collector { } }; - let mut metrics = ResourceMetrics { - resource: Resource::builder_empty().build(), - scope_metrics: vec![], - }; + let mut metrics = ResourceMetrics::default(); if let Err(err) = self.reader.collect(&mut metrics) { otel_error!( name: "MetricScrapeFailed", @@ -305,38 +290,42 @@ impl prometheus::core::Collector for Collector { ); return vec![]; } - let mut res = Vec::with_capacity(metrics.scope_metrics.len() + 1); + let mut res = Vec::with_capacity(metrics.scope_metrics().count() + 1); let target_info = self.create_target_info_once.get_or_init(|| { // Resource should be immutable, we don't need to compute again - create_info_metric(TARGET_INFO_NAME, TARGET_INFO_DESCRIPTION, &metrics.resource) + create_info_metric( + TARGET_INFO_NAME, + TARGET_INFO_DESCRIPTION, + metrics.resource(), + ) }); - if !self.disable_target_info && !metrics.resource.is_empty() { + if !self.disable_target_info && !metrics.resource().is_empty() { res.push(target_info.clone()) } let resource_labels = self .resource_labels_once - .get_or_init(|| self.resource_selector.select(&metrics.resource)); + .get_or_init(|| self.resource_selector.select(metrics.resource())); - for scope_metrics in metrics.scope_metrics { + for scope_metrics in metrics.scope_metrics() { let scope_labels = if !self.disable_scope_info { - if scope_metrics.scope.attributes().count() > 0 { + if scope_metrics.scope().attributes().count() > 0 { let scope_info = inner .scope_infos - .entry(scope_metrics.scope.clone()) + .entry(scope_metrics.scope().clone()) .or_insert_with_key(create_scope_info_metric); res.push(scope_info.clone()); } let mut labels = - Vec::with_capacity(1 + scope_metrics.scope.version().is_some() as usize); + Vec::with_capacity(1 + scope_metrics.scope().version().is_some() as usize); let mut name = LabelPair::new(); name.set_name(SCOPE_INFO_KEYS[0].into()); - name.set_value(scope_metrics.scope.name().to_string()); + name.set_value(scope_metrics.scope().name().to_string()); labels.push(name); - if let Some(version) = &scope_metrics.scope.version() { + if let Some(version) = &scope_metrics.scope().version() { let mut l_version = LabelPair::new(); l_version.set_name(SCOPE_INFO_KEYS[1].into()); l_version.set_value(version.to_string()); @@ -351,40 +340,58 @@ impl prometheus::core::Collector for Collector { Vec::new() }; - for metrics in scope_metrics.metrics { - let (metric_type, name) = match self.metric_type_and_name(&metrics) { + for metrics in scope_metrics.metrics() { + let (metric_type, name) = match self.metric_type_and_name(metrics) { Some((metric_type, name)) => (metric_type, name), _ => continue, }; let mfs = &mut inner.metric_families; - let (drop, help) = validate_metrics(&name, &metrics.description, metric_type, mfs); + let (drop, help) = validate_metrics(&name, metrics.description(), metric_type, mfs); if drop { continue; } - let description = help.unwrap_or_else(|| metrics.description.into()); - let data = metrics.data.as_any(); - - if let Some(hist) = data.downcast_ref::>() { - add_histogram_metric(&mut res, hist, description, &scope_labels, name); - } else if let Some(hist) = data.downcast_ref::>() { - add_histogram_metric(&mut res, hist, description, &scope_labels, name); - } else if let Some(hist) = data.downcast_ref::>() { - add_histogram_metric(&mut res, hist, description, &scope_labels, name); - } else if let Some(sum) = data.downcast_ref::>() { - add_sum_metric(&mut res, sum, description, &scope_labels, name); - } else if let Some(sum) = data.downcast_ref::>() { - add_sum_metric(&mut res, sum, description, &scope_labels, name); - } else if let Some(sum) = data.downcast_ref::>() { - add_sum_metric(&mut res, sum, description, &scope_labels, name); - } else if let Some(g) = data.downcast_ref::>() { - add_gauge_metric(&mut res, g, description, &scope_labels, name); - } else if let Some(g) = data.downcast_ref::>() { - add_gauge_metric(&mut res, g, description, &scope_labels, name); - } else if let Some(g) = data.downcast_ref::>() { - add_gauge_metric(&mut res, g, description, &scope_labels, name); - } + let description = help.unwrap_or_else(|| metrics.description().into()); + let data = metrics.data(); + + match data { + AggregatedMetrics::F64(MetricData::Gauge(gauge)) => { + add_gauge_metric(&mut res, gauge, description, &scope_labels, name) + } + AggregatedMetrics::U64(MetricData::Gauge(gauge)) => { + add_gauge_metric(&mut res, gauge, description, &scope_labels, name) + } + AggregatedMetrics::I64(MetricData::Gauge(gauge)) => { + add_gauge_metric(&mut res, gauge, description, &scope_labels, name) + } + + AggregatedMetrics::U64(MetricData::Sum(sum)) => { + add_sum_metric(&mut res, sum, description, &scope_labels, name) + } + AggregatedMetrics::F64(MetricData::Sum(sum)) => { + add_sum_metric(&mut res, sum, description, &scope_labels, name) + } + AggregatedMetrics::I64(MetricData::Sum(sum)) => { + add_sum_metric(&mut res, sum, description, &scope_labels, name) + } + + AggregatedMetrics::U64(MetricData::Histogram(hist)) => { + add_histogram_metric(&mut res, hist, description, &scope_labels, name) + } + AggregatedMetrics::F64(MetricData::Histogram(hist)) => { + add_histogram_metric(&mut res, hist, description, &scope_labels, name) + } + AggregatedMetrics::I64(MetricData::Histogram(hist)) => { + add_histogram_metric(&mut res, hist, description, &scope_labels, name) + } + + AggregatedMetrics::U64(MetricData::ExponentialHistogram(_)) + | AggregatedMetrics::F64(MetricData::ExponentialHistogram(_)) + | AggregatedMetrics::I64(MetricData::ExponentialHistogram(_)) => { + // Unsupported + } + }; } } @@ -459,29 +466,27 @@ fn validate_metrics( } } -fn add_histogram_metric( +fn add_histogram_metric( res: &mut Vec, histogram: &data::Histogram, description: String, extra: &[LabelPair], - name: Cow<'static, str>, + name: Cow<'_, str>, ) { // Consider supporting exemplars when `prometheus` crate has the feature // See: https://github.com/tikv/rust-prometheus/issues/393 - for dp in &histogram.data_points { - let kvs = get_attrs( - &mut dp.attributes.iter().map(|kv| (&kv.key, &kv.value)), - extra, - ); - let bounds_len = dp.bounds.len(); - let (bucket, _) = dp.bounds.iter().enumerate().fold( + for dp in histogram.data_points() { + let kvs = get_attrs(&mut dp.attributes().map(|kv| (&kv.key, &kv.value)), extra); + let bounds_len = dp.bounds().count(); + let bucket_counts: Vec<_> = dp.bucket_counts().collect(); + let (bucket, _) = dp.bounds().enumerate().fold( (Vec::with_capacity(bounds_len), 0), |(mut acc, mut count), (i, bound)| { - count += dp.bucket_counts[i]; + count += bucket_counts[i]; let mut b = prometheus::proto::Bucket::default(); - b.set_upper_bound(*bound); + b.set_upper_bound(bound); b.set_cumulative_count(count); acc.push(b); (acc, count) @@ -489,8 +494,8 @@ fn add_histogram_metric( ); let mut h = prometheus::proto::Histogram::default(); - h.set_sample_sum(dp.sum.as_f64()); - h.set_sample_count(dp.count); + h.set_sample_sum(dp.sum().as_f64()); + h.set_sample_count(dp.count()); h.set_bucket(bucket); let mut pm = prometheus::proto::Metric::default(); pm.set_label(kvs); @@ -505,35 +510,32 @@ fn add_histogram_metric( } } -fn add_sum_metric( +fn add_sum_metric( res: &mut Vec, sum: &data::Sum, description: String, extra: &[LabelPair], - name: Cow<'static, str>, + name: Cow<'_, str>, ) { - let metric_type = if sum.is_monotonic { + let metric_type = if sum.is_monotonic() { MetricType::COUNTER } else { MetricType::GAUGE }; - for dp in &sum.data_points { - let kvs = get_attrs( - &mut dp.attributes.iter().map(|kv| (&kv.key, &kv.value)), - extra, - ); + for dp in sum.data_points() { + let kvs = get_attrs(&mut dp.attributes().map(|kv| (&kv.key, &kv.value)), extra); let mut pm = prometheus::proto::Metric::default(); pm.set_label(kvs); - if sum.is_monotonic { + if sum.is_monotonic() { let mut c = prometheus::proto::Counter::default(); - c.set_value(dp.value.as_f64()); + c.set_value(dp.value().as_f64()); pm.set_counter(c); } else { let mut g = prometheus::proto::Gauge::default(); - g.set_value(dp.value.as_f64()); + g.set_value(dp.value().as_f64()); pm.set_gauge(g); } @@ -546,21 +548,18 @@ fn add_sum_metric( } } -fn add_gauge_metric( +fn add_gauge_metric( res: &mut Vec, gauge: &data::Gauge, description: String, extra: &[LabelPair], - name: Cow<'static, str>, + name: Cow<'_, str>, ) { - for dp in &gauge.data_points { - let kvs = get_attrs( - &mut dp.attributes.iter().map(|kv| (&kv.key, &kv.value)), - extra, - ); + for dp in gauge.data_points() { + let kvs = get_attrs(&mut dp.attributes().map(|kv| (&kv.key, &kv.value)), extra); let mut g = prometheus::proto::Gauge::default(); - g.set_value(dp.value.as_f64()); + g.set_value(dp.value().as_f64()); let mut pm = prometheus::proto::Metric::default(); pm.set_label(kvs); pm.set_gauge(g); @@ -583,10 +582,7 @@ fn create_info_metric( g.set_value(1.0); let mut m = prometheus::proto::Metric::default(); - m.set_label(get_attrs( - &mut resource.iter(), - &[], - )); + m.set_label(get_attrs(&mut resource.iter(), &[])); m.set_gauge(g); let mut mf = MetricFamily::default(); diff --git a/opentelemetry-prometheus/src/utils.rs b/opentelemetry-prometheus/src/utils.rs index d11c0eb973..09505ee17e 100644 --- a/opentelemetry-prometheus/src/utils.rs +++ b/opentelemetry-prometheus/src/utils.rs @@ -98,7 +98,7 @@ fn get_prom_per_unit(unit: &str) -> Option<&'static str> { } #[allow(clippy::ptr_arg)] -pub(crate) fn sanitize_name(s: &Cow<'static, str>) -> Cow<'static, str> { +pub(crate) fn sanitize_name<'a>(s: &'a str) -> Cow<'a, str> { // prefix chars to add in case name starts with number let mut prefix = ""; @@ -129,7 +129,7 @@ pub(crate) fn sanitize_name(s: &Cow<'static, str>) -> Cow<'static, str> { .collect(), ) } else { - s.clone() // no invalid chars found, return existing + Cow::Borrowed(s) // no invalid chars found, return existing } } @@ -171,7 +171,7 @@ mod tests { ]; for (input, want) in tests { - assert_eq!(want, sanitize_name(&input.into()), "input: {input}") + assert_eq!(want, sanitize_name(input), "input: {input}") } }