diff --git a/CHANGELOG.md b/CHANGELOG.md index 685c8b6..839d97e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -34,12 +34,16 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Supoort `Arc` for `EncodeLabelValue`. See [PR 217]. +- Add `histogram::exponential_buckets_range`. + See [PR 233]. + - Added `get` method to `Family`. See [PR 234]. [PR 173]: https://github.com/prometheus/client_rust/pull/173 [PR 216]: https://github.com/prometheus/client_rust/pull/216 [PR 217]: https://github.com/prometheus/client_rust/pull/217 +[PR 233]: https://github.com/prometheus/client_rust/pull/233 [PR 234]: https://github.com/prometheus/client_rust/pull/234 ### Fixed diff --git a/src/metrics/histogram.rs b/src/metrics/histogram.rs index 1e418ad..f95ca8d 100644 --- a/src/metrics/histogram.rs +++ b/src/metrics/histogram.rs @@ -122,6 +122,30 @@ pub fn exponential_buckets(start: f64, factor: f64, length: u16) -> impl Iterato .take(length.into()) } +/// Exponential bucket distribution within a range +/// +/// Creates `length` buckets, where the lowest bucket is `min` and the highest bucket is `max`. +/// +/// If `length` is less than 1, or `min` is less than or equal to 0, an empty iterator is returned. +pub fn exponential_buckets_range(min: f64, max: f64, length: u16) -> impl Iterator { + let mut len_observed = length; + let mut min_bucket = min; + // length needs a positive length and min needs to be greater than 0 + // set len_observed to 0 and min_bucket to 1.0 + // this will return an empty iterator in the result + if length < 1 || min <= 0.0 { + len_observed = 0; + min_bucket = 1.0; + } + // We know max/min and highest bucket. Solve for growth_factor. + let growth_factor = (max / min_bucket).powf(1.0 / (len_observed as f64 - 1.0)); + + iter::repeat(()) + .enumerate() + .map(move |(i, _)| min_bucket * growth_factor.powf(i as f64)) + .take(len_observed.into()) +} + /// Linear bucket distribution. pub fn linear_buckets(start: f64, width: f64, length: u16) -> impl Iterator { iter::repeat(()) @@ -166,4 +190,21 @@ mod tests { linear_buckets(0.0, 1.0, 10).collect::>() ); } + + #[test] + fn exponential_range() { + assert_eq!( + vec![1.0, 2.0, 4.0, 8.0, 16.0, 32.0], + exponential_buckets_range(1.0, 32.0, 6).collect::>() + ); + } + + #[test] + fn exponential_range_incorrect() { + let res = exponential_buckets_range(1.0, 32.0, 0).collect::>(); + assert!(res.is_empty()); + + let res = exponential_buckets_range(0.0, 32.0, 6).collect::>(); + assert!(res.is_empty()); + } }