in exporter/metric/metric.go [723:775]
func expHistToDistribution[N int64 | float64](hist metricdata.ExponentialHistogramDataPoint[N], projectID string) *distribution.Distribution {
// First calculate underflow bucket with all negatives + zeros.
underflow := hist.ZeroCount
negativeBuckets := hist.NegativeBucket.Counts
for i := 0; i < len(negativeBuckets); i++ {
underflow += negativeBuckets[i]
}
// Next, pull in remaining buckets.
counts := make([]int64, len(hist.PositiveBucket.Counts)+2)
bucketOptions := &distribution.Distribution_BucketOptions{}
counts[0] = int64(underflow)
positiveBuckets := hist.PositiveBucket.Counts
for i := 0; i < len(positiveBuckets); i++ {
counts[i+1] = int64(positiveBuckets[i])
}
// Overflow bucket is always empty
counts[len(counts)-1] = 0
if len(hist.PositiveBucket.Counts) == 0 {
// We cannot send exponential distributions with no positive buckets,
// instead we send a simple overflow/underflow histogram.
bucketOptions.Options = &distribution.Distribution_BucketOptions_ExplicitBuckets{
ExplicitBuckets: &distribution.Distribution_BucketOptions_Explicit{
Bounds: []float64{0},
},
}
} else {
// Exponential histogram
growth := math.Exp2(math.Exp2(-float64(hist.Scale)))
scale := math.Pow(growth, float64(hist.PositiveBucket.Offset))
bucketOptions.Options = &distribution.Distribution_BucketOptions_ExponentialBuckets{
ExponentialBuckets: &distribution.Distribution_BucketOptions_Exponential{
GrowthFactor: growth,
Scale: scale,
NumFiniteBuckets: int32(len(counts) - 2),
},
}
}
var mean float64
if !math.IsNaN(float64(hist.Sum)) && hist.Count > 0 { // Avoid divide-by-zero
mean = float64(hist.Sum) / float64(hist.Count)
}
return &distribution.Distribution{
Count: int64(hist.Count),
Mean: mean,
BucketCounts: counts,
BucketOptions: bucketOptions,
Exemplars: toDistributionExemplar[N](hist.Exemplars, projectID),
}
}