diff --git a/Cargo.toml b/Cargo.toml index e8727293..958cb7fa 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -11,6 +11,7 @@ keywords = ["openmetrics", "prometheus", "metrics", "instrumentation", "monitori owning_ref = "0.4" itoa = "0.4" dtoa = "0.4" +generic-array = "0.14" [dev-dependencies] pyo3 = "0.13" diff --git a/benches/encoding/text.rs b/benches/encoding/text.rs index 640b0783..91741543 100644 --- a/benches/encoding/text.rs +++ b/benches/encoding/text.rs @@ -8,6 +8,7 @@ use open_metrics_client::metrics::histogram::{exponential_series, Histogram}; use open_metrics_client::registry::Registry; use std::io::Write; use std::sync::atomic::AtomicU64; +use generic_array::typenum::U10; pub fn text(c: &mut Criterion) { c.bench_function("encode", |b| { @@ -59,8 +60,8 @@ pub fn text(c: &mut Criterion) { for i in 0..100 { let counter_family = Family::>::default(); - let histogram_family = Family::::new_with_constructor(|| { - Histogram::new(exponential_series(1.0, 2.0, 10)) + let histogram_family = Family::>::new_with_constructor(|| { + Histogram::new(exponential_series(1.0, 2.0)) }); registry.register( diff --git a/src/encoding/text.rs b/src/encoding/text.rs index 3bfe9648..3a3c19d9 100644 --- a/src/encoding/text.rs +++ b/src/encoding/text.rs @@ -32,6 +32,7 @@ use crate::metrics::histogram::Histogram; use crate::metrics::{MetricType, TypedMetric}; use crate::registry::{Registry, Unit}; +use generic_array::ArrayLength; use std::io::Write; use std::ops::Deref; @@ -360,7 +361,7 @@ where } } -impl EncodeMetric for Histogram { +impl> EncodeMetric for Histogram { fn encode(&self, mut encoder: Encoder) -> Result<(), std::io::Error> { let (sum, count, buckets) = self.get(); encoder @@ -395,6 +396,7 @@ mod tests { use crate::metrics::histogram::exponential_series; use pyo3::{prelude::*, types::PyModule}; use std::sync::atomic::AtomicU64; + use generic_array::typenum::U10; #[test] fn encode_counter() { @@ -461,7 +463,7 @@ mod tests { #[test] fn encode_histogram() { let mut registry = Registry::default(); - let histogram = Histogram::new(exponential_series(1.0, 2.0, 10)); + let histogram = Histogram::::new(exponential_series(1.0, 2.0)); registry.register("my_histogram", "My histogram", histogram.clone()); histogram.observe(1.0); diff --git a/src/metrics/family.rs b/src/metrics/family.rs index 1b2e3fd4..372363aa 100644 --- a/src/metrics/family.rs +++ b/src/metrics/family.rs @@ -150,7 +150,7 @@ impl Family { /// # use open_metrics_client::metrics::family::Family; /// # use open_metrics_client::metrics::histogram::{exponential_series, Histogram}; /// Family::, Histogram>::new_with_constructor(|| { - /// Histogram::new(exponential_series(1.0, 2.0, 10)) + /// Histogram::new(exponential_series(1.0, 2.0)) /// }); /// ``` pub fn new_with_constructor(constructor: fn() -> M) -> Self { @@ -245,7 +245,7 @@ mod tests { #[test] fn histogram_family() { Family::<(), Histogram>::new_with_constructor(|| { - Histogram::new(exponential_series(1.0, 2.0, 10)) + Histogram::new(exponential_series(1.0, 2.0)) }); } } diff --git a/src/metrics/histogram.rs b/src/metrics/histogram.rs index 384996ee..c3014516 100644 --- a/src/metrics/histogram.rs +++ b/src/metrics/histogram.rs @@ -3,24 +3,26 @@ //! See [`Histogram`] for details. use super::{MetricType, TypedMetric}; +use generic_array::{ArrayLength, GenericArray}; +use generic_array::sequence::GenericSequence; +use generic_array::typenum::U10; use owning_ref::OwningRef; -use std::iter::once; use std::sync::{Arc, Mutex, MutexGuard}; /// Open Metrics [`Histogram`] to measure distributions of discrete events. /// /// ``` /// # use open_metrics_client::metrics::histogram::{Histogram, exponential_series}; -/// let histogram = Histogram::new(exponential_series(1.0, 2.0, 10)); +/// let histogram = Histogram::new(exponential_series(1.0, 2.0)); /// histogram.observe(4.2); /// ``` // TODO: Consider using atomics. See // https://github.com/tikv/rust-prometheus/pull/314. -pub struct Histogram { - inner: Arc>, +pub struct Histogram = U10> { + inner: Arc>>, } -impl Clone for Histogram { +impl> Clone for Histogram { fn clone(&self) -> Self { Histogram { inner: self.inner.clone(), @@ -28,25 +30,21 @@ impl Clone for Histogram { } } -pub(crate) struct Inner { +pub(crate) struct Inner> { // TODO: Consider allowing integer observe values. sum: f64, count: u64, // TODO: Consider being generic over the bucket length. - buckets: Vec<(f64, u64)>, + buckets: GenericArray<(f64, u64), NumBuckets>, } -impl Histogram { - pub fn new(buckets: impl Iterator) -> Self { +impl> Histogram { + pub fn new(buckets: GenericArray<(f64, u64), NumBuckets>) -> Self { Self { inner: Arc::new(Mutex::new(Inner { sum: Default::default(), count: Default::default(), - buckets: buckets - .into_iter() - .chain(once(f64::MAX)) - .map(|upper_bound| (upper_bound, 0)) - .collect(), + buckets, })), } } @@ -62,7 +60,7 @@ impl Histogram { } } - pub(crate) fn get(&self) -> (f64, u64, MutexGuardedBuckets) { + pub(crate) fn get(&self) -> (f64, u64, MutexGuardedBuckets) { let inner = self.inner.lock().unwrap(); let sum = inner.sum; let count = inner.count; @@ -71,20 +69,19 @@ impl Histogram { } } -type MutexGuardedBuckets<'a> = OwningRef, Vec<(f64, u64)>>; +type MutexGuardedBuckets<'a, NumBuckets> = + OwningRef>, GenericArray<(f64, u64), NumBuckets>>; -impl TypedMetric for Histogram { +impl> TypedMetric for Histogram { const TYPE: MetricType = MetricType::Histogram; } // TODO: consider renaming to exponential_buckets -pub fn exponential_series(start: f64, factor: f64, length: u16) -> impl Iterator { - let mut current = start; - (0..length).map(move |_| { - let to_return = current; - current *= factor; - to_return - }) +pub fn exponential_series>( + start: f64, + factor: f64, +) -> GenericArray<(f64, u64), NumBuckets> { + GenericArray::generate(|i: usize| (start * factor.powf(i as f64), 0)) } #[cfg(test)] @@ -93,7 +90,7 @@ mod tests { #[test] fn histogram() { - let histogram = Histogram::new(exponential_series(1.0, 2.0, 10)); + let histogram = Histogram::::new(exponential_series(1.0, 2.0)); histogram.observe(1.0); } }