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) {