Skip to content

Commit

Permalink
feat: add range based exponential buckets in histogram (#233)
Browse files Browse the repository at this point in the history
Signed-off-by: Sidhant Kohli <[email protected]>
  • Loading branch information
kohlisid authored Jan 7, 2025
1 parent 1adc994 commit 8f13460
Show file tree
Hide file tree
Showing 2 changed files with 45 additions and 0 deletions.
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -34,12 +34,16 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- Supoort `Arc<String>` 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
Expand Down
41 changes: 41 additions & 0 deletions src/metrics/histogram.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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<Item = f64> {
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<Item = f64> {
iter::repeat(())
Expand Down Expand Up @@ -166,4 +190,21 @@ mod tests {
linear_buckets(0.0, 1.0, 10).collect::<Vec<_>>()
);
}

#[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::<Vec<_>>()
);
}

#[test]
fn exponential_range_incorrect() {
let res = exponential_buckets_range(1.0, 32.0, 0).collect::<Vec<_>>();
assert!(res.is_empty());

let res = exponential_buckets_range(0.0, 32.0, 6).collect::<Vec<_>>();
assert!(res.is_empty());
}
}

0 comments on commit 8f13460

Please sign in to comment.