From 2261d5cda14eb2adc5897b56996248705f9bb840 Mon Sep 17 00:00:00 2001 From: Seth Bunce Date: Thu, 12 Aug 2021 08:56:44 -0700 Subject: [PATCH] add ExponentialBucketsRange function (#899) This function calculates exponential buckets with different arguments than the existing ExponentialBuckets function. Instead of specifying the start and factor, the user can specify the min and max bucket value. We have been doing it this way internally at my company for some time. Signed-off-by: Seth Bunce --- prometheus/histogram.go | 28 ++++++++++++++++++++++++++++ prometheus/histogram_test.go | 10 ++++++++++ 2 files changed, 38 insertions(+) diff --git a/prometheus/histogram.go b/prometheus/histogram.go index 8425640b3..893802fd6 100644 --- a/prometheus/histogram.go +++ b/prometheus/histogram.go @@ -116,6 +116,34 @@ func ExponentialBuckets(start, factor float64, count int) []float64 { return buckets } +// ExponentialBucketsRange creates 'count' buckets, where the lowest bucket is +// 'min' and the highest bucket is 'max'. The final +Inf bucket is not counted +// and not included in the returned slice. The returned slice is meant to be +// used for the Buckets field of HistogramOpts. +// +// The function panics if 'count' is 0 or negative, if 'min' is 0 or negative. +func ExponentialBucketsRange(min, max float64, count int) []float64 { + if count < 1 { + panic("ExponentialBucketsRange count needs a positive count") + } + if min <= 0 { + panic("ExponentialBucketsRange min needs to be greater than 0") + } + + // Formula for exponential buckets. + // max = min*growthFactor^(bucketCount-1) + + // We know max/min and highest bucket. Solve for growthFactor. + growthFactor := math.Pow(max/min, 1.0/float64(count-1)) + + // Now that we know growthFactor, solve for each bucket. + buckets := make([]float64, count) + for i := 1; i <= count; i++ { + buckets[i-1] = min * math.Pow(growthFactor, float64(i-1)) + } + return buckets +} + // HistogramOpts bundles the options for creating a Histogram metric. It is // mandatory to set Name to a non-empty string. All other fields are optional // and can safely be left at their zero value, although it is strongly diff --git a/prometheus/histogram_test.go b/prometheus/histogram_test.go index 3514e8142..93252aa35 100644 --- a/prometheus/histogram_test.go +++ b/prometheus/histogram_test.go @@ -352,6 +352,16 @@ func TestBuckets(t *testing.T) { if !reflect.DeepEqual(got, want) { t.Errorf("exponential buckets: got %v, want %v", got, want) } + + got = ExponentialBucketsRange(1, 100, 10) + want = []float64{1.0, 1.6681005372000588, 2.782559402207125, + 4.641588833612779, 7.742636826811273, 12.915496650148842, + 21.544346900318846, 35.93813663804629, 59.94842503189414, + 100.00000000000007, + } + if !reflect.DeepEqual(got, want) { + t.Errorf("exponential buckets range: got %v, want %v", got, want) + } } func TestHistogramAtomicObserve(t *testing.T) {