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 <seth.bunce@getcruise.com>
This commit is contained in:
Seth Bunce 2021-08-12 08:56:44 -07:00 committed by GitHub
parent 20eef74042
commit 2261d5cda1
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 38 additions and 0 deletions

View File

@ -116,6 +116,34 @@ func ExponentialBuckets(start, factor float64, count int) []float64 {
return buckets 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 // 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 // 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 // and can safely be left at their zero value, although it is strongly

View File

@ -352,6 +352,16 @@ func TestBuckets(t *testing.T) {
if !reflect.DeepEqual(got, want) { if !reflect.DeepEqual(got, want) {
t.Errorf("exponential buckets: got %v, want %v", 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) { func TestHistogramAtomicObserve(t *testing.T) {