Skip to content

Commit 8f13460

Browse files
authored
feat: add range based exponential buckets in histogram (#233)
Signed-off-by: Sidhant Kohli <[email protected]>
1 parent 1adc994 commit 8f13460

File tree

2 files changed

+45
-0
lines changed

2 files changed

+45
-0
lines changed

Diff for: CHANGELOG.md

+4
Original file line numberDiff line numberDiff line change
@@ -34,12 +34,16 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
3434
- Supoort `Arc<String>` for `EncodeLabelValue`.
3535
See [PR 217].
3636

37+
- Add `histogram::exponential_buckets_range`.
38+
See [PR 233].
39+
3740
- Added `get` method to `Family`.
3841
See [PR 234].
3942

4043
[PR 173]: https://github.com/prometheus/client_rust/pull/173
4144
[PR 216]: https://github.com/prometheus/client_rust/pull/216
4245
[PR 217]: https://github.com/prometheus/client_rust/pull/217
46+
[PR 233]: https://github.com/prometheus/client_rust/pull/233
4347
[PR 234]: https://github.com/prometheus/client_rust/pull/234
4448

4549
### Fixed

Diff for: src/metrics/histogram.rs

+41
Original file line numberDiff line numberDiff line change
@@ -122,6 +122,30 @@ pub fn exponential_buckets(start: f64, factor: f64, length: u16) -> impl Iterato
122122
.take(length.into())
123123
}
124124

125+
/// Exponential bucket distribution within a range
126+
///
127+
/// Creates `length` buckets, where the lowest bucket is `min` and the highest bucket is `max`.
128+
///
129+
/// If `length` is less than 1, or `min` is less than or equal to 0, an empty iterator is returned.
130+
pub fn exponential_buckets_range(min: f64, max: f64, length: u16) -> impl Iterator<Item = f64> {
131+
let mut len_observed = length;
132+
let mut min_bucket = min;
133+
// length needs a positive length and min needs to be greater than 0
134+
// set len_observed to 0 and min_bucket to 1.0
135+
// this will return an empty iterator in the result
136+
if length < 1 || min <= 0.0 {
137+
len_observed = 0;
138+
min_bucket = 1.0;
139+
}
140+
// We know max/min and highest bucket. Solve for growth_factor.
141+
let growth_factor = (max / min_bucket).powf(1.0 / (len_observed as f64 - 1.0));
142+
143+
iter::repeat(())
144+
.enumerate()
145+
.map(move |(i, _)| min_bucket * growth_factor.powf(i as f64))
146+
.take(len_observed.into())
147+
}
148+
125149
/// Linear bucket distribution.
126150
pub fn linear_buckets(start: f64, width: f64, length: u16) -> impl Iterator<Item = f64> {
127151
iter::repeat(())
@@ -166,4 +190,21 @@ mod tests {
166190
linear_buckets(0.0, 1.0, 10).collect::<Vec<_>>()
167191
);
168192
}
193+
194+
#[test]
195+
fn exponential_range() {
196+
assert_eq!(
197+
vec![1.0, 2.0, 4.0, 8.0, 16.0, 32.0],
198+
exponential_buckets_range(1.0, 32.0, 6).collect::<Vec<_>>()
199+
);
200+
}
201+
202+
#[test]
203+
fn exponential_range_incorrect() {
204+
let res = exponential_buckets_range(1.0, 32.0, 0).collect::<Vec<_>>();
205+
assert!(res.is_empty());
206+
207+
let res = exponential_buckets_range(0.0, 32.0, 6).collect::<Vec<_>>();
208+
assert!(res.is_empty());
209+
}
169210
}

0 commit comments

Comments
 (0)