From 5b36b63056b96a63541141b4f1767a584dfabe75 Mon Sep 17 00:00:00 2001 From: Seth Bunce Date: Wed, 4 Aug 2021 22:31:27 +0000 Subject: [PATCH] add ExponentialBucketsRange function 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) {