in exporter/collector/metrics.go [1071:1127]
func (m *metricMapper) exponentialHistogramPoint(point pmetric.ExponentialHistogramDataPoint, projectID string) *monitoringpb.TypedValue {
// First calculate underflow bucket with all negatives + zeros.
underflow := point.ZeroCount()
negativeBuckets := point.Negative().BucketCounts()
for i := 0; i < negativeBuckets.Len(); i++ {
underflow += negativeBuckets.At(i)
}
// Next, pull in remaining buckets.
counts := make([]int64, point.Positive().BucketCounts().Len()+2)
bucketOptions := &distribution.Distribution_BucketOptions{}
counts[0] = int64(underflow)
positiveBuckets := point.Positive().BucketCounts()
for i := 0; i < positiveBuckets.Len(); i++ {
counts[i+1] = int64(positiveBuckets.At(i))
}
// Overflow bucket is always empty
counts[len(counts)-1] = 0
if point.Positive().BucketCounts().Len() == 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(point.Scale())))
scale := math.Pow(growth, float64(point.Positive().Offset()))
bucketOptions.Options = &distribution.Distribution_BucketOptions_ExponentialBuckets{
ExponentialBuckets: &distribution.Distribution_BucketOptions_Exponential{
GrowthFactor: growth,
Scale: scale,
NumFiniteBuckets: int32(len(counts) - 2),
},
}
}
mean := float64(0)
if !math.IsNaN(point.Sum()) && point.Count() > 0 { // Avoid divide-by-zero
mean = float64(point.Sum() / float64(point.Count()))
}
return &monitoringpb.TypedValue{
Value: &monitoringpb.TypedValue_DistributionValue{
DistributionValue: &distribution.Distribution{
Count: int64(point.Count()),
Mean: mean,
BucketCounts: counts,
BucketOptions: bucketOptions,
Exemplars: m.exemplars(point.Exemplars(), projectID),
},
},
}
}