From 12138c9444b72d563380fbddcfbe3aaee5452a5f Mon Sep 17 00:00:00 2001 From: Aaron Clawson <3766680+MadVikingGod@users.noreply.github.com> Date: Wed, 21 Jun 2023 13:57:49 -0500 Subject: [PATCH] Add Exponetial Histograms to otlp transforms (#4222) --- CHANGELOG.md | 1 + .../internal/transform/metricdata.go | 59 ++++++ .../internal/transform/metricdata_test.go | 176 ++++++++++++++++++ 3 files changed, 236 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index e68d9fc0534..62697c64423 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -27,6 +27,7 @@ See our [versioning policy](VERSIONING.md) for more information about these stab - The `go.opentelemetry.io/otel/semconv/v1.20.0` package. The package contains semantic conventions from the `v1.20.0` version of the OpenTelemetry specification. (#4078) - The Exponential Histogram data types in `go.opentelemetry.io/otel/sdk/metric/metricdata`. (#4165) +- OTLP metrics exporter now supports the Exponential Histogram Data Type. (#4222) ### Changed diff --git a/exporters/otlp/otlpmetric/internal/transform/metricdata.go b/exporters/otlp/otlpmetric/internal/transform/metricdata.go index 2f98115b83d..9157de6bdf0 100644 --- a/exporters/otlp/otlpmetric/internal/transform/metricdata.go +++ b/exporters/otlp/otlpmetric/internal/transform/metricdata.go @@ -99,6 +99,10 @@ func metric(m metricdata.Metrics) (*mpb.Metric, error) { out.Data, err = Histogram(a) case metricdata.Histogram[float64]: out.Data, err = Histogram(a) + case metricdata.ExponentialHistogram[int64]: + out.Data, err = ExponentialHistogram(a) + case metricdata.ExponentialHistogram[float64]: + out.Data, err = ExponentialHistogram(a) default: return out, fmt.Errorf("%w: %T", errUnknownAggregation, a) } @@ -198,6 +202,61 @@ func HistogramDataPoints[N int64 | float64](dPts []metricdata.HistogramDataPoint return out } +// ExponentialHistogram returns an OTLP Metric_ExponentialHistogram generated from h. An error is +// returned if the temporality of h is unknown. +func ExponentialHistogram[N int64 | float64](h metricdata.ExponentialHistogram[N]) (*mpb.Metric_ExponentialHistogram, error) { + t, err := Temporality(h.Temporality) + if err != nil { + return nil, err + } + return &mpb.Metric_ExponentialHistogram{ + ExponentialHistogram: &mpb.ExponentialHistogram{ + AggregationTemporality: t, + DataPoints: ExponentialHistogramDataPoints(h.DataPoints), + }, + }, nil +} + +// ExponentialHistogramDataPoints returns a slice of OTLP ExponentialHistogramDataPoint generated +// from dPts. +func ExponentialHistogramDataPoints[N int64 | float64](dPts []metricdata.ExponentialHistogramDataPoint[N]) []*mpb.ExponentialHistogramDataPoint { + out := make([]*mpb.ExponentialHistogramDataPoint, 0, len(dPts)) + for _, dPt := range dPts { + sum := float64(dPt.Sum) + ehdp := &mpb.ExponentialHistogramDataPoint{ + Attributes: AttrIter(dPt.Attributes.Iter()), + StartTimeUnixNano: uint64(dPt.StartTime.UnixNano()), + TimeUnixNano: uint64(dPt.Time.UnixNano()), + Count: dPt.Count, + Sum: &sum, + Scale: dPt.Scale, + ZeroCount: dPt.ZeroCount, + + Positive: ExponentialHistogramDataPointBuckets(dPt.PositiveBucket), + Negative: ExponentialHistogramDataPointBuckets(dPt.NegativeBucket), + } + if v, ok := dPt.Min.Value(); ok { + vF64 := float64(v) + ehdp.Min = &vF64 + } + if v, ok := dPt.Max.Value(); ok { + vF64 := float64(v) + ehdp.Max = &vF64 + } + out = append(out, ehdp) + } + return out +} + +// ExponentialHistogramDataPointBuckets returns an OTLP ExponentialHistogramDataPoint_Buckets generated +// from bucket. +func ExponentialHistogramDataPointBuckets(bucket metricdata.ExponentialBucket) *mpb.ExponentialHistogramDataPoint_Buckets { + return &mpb.ExponentialHistogramDataPoint_Buckets{ + Offset: bucket.Offset, + BucketCounts: bucket.Counts, + } +} + // Temporality returns an OTLP AggregationTemporality generated from t. If t // is unknown, an error is returned along with the invalid // AggregationTemporality_AGGREGATION_TEMPORALITY_UNSPECIFIED. diff --git a/exporters/otlp/otlpmetric/internal/transform/metricdata_test.go b/exporters/otlp/otlpmetric/internal/transform/metricdata_test.go index d9a8ddf6a7c..d6ffa7c2c06 100644 --- a/exporters/otlp/otlpmetric/internal/transform/metricdata_test.go +++ b/exporters/otlp/otlpmetric/internal/transform/metricdata_test.go @@ -95,6 +95,78 @@ var ( Sum: sumB, }} + otelEBucketA = metricdata.ExponentialBucket{ + Offset: 5, + Counts: []uint64{0, 5, 0, 5}, + } + otelEBucketB = metricdata.ExponentialBucket{ + Offset: 3, + Counts: []uint64{0, 5, 0, 5}, + } + otelEBucketsC = metricdata.ExponentialBucket{ + Offset: 5, + Counts: []uint64{0, 1}, + } + otelEBucketsD = metricdata.ExponentialBucket{ + Offset: 3, + Counts: []uint64{0, 1}, + } + + otelEHDPInt64 = []metricdata.ExponentialHistogramDataPoint[int64]{{ + Attributes: alice, + StartTime: start, + Time: end, + Count: 30, + Scale: 2, + ZeroCount: 10, + PositiveBucket: otelEBucketA, + NegativeBucket: otelEBucketB, + ZeroThreshold: .01, + Min: metricdata.NewExtrema(int64(minA)), + Max: metricdata.NewExtrema(int64(maxA)), + Sum: int64(sumA), + }, { + Attributes: bob, + StartTime: start, + Time: end, + Count: 3, + Scale: 4, + ZeroCount: 1, + PositiveBucket: otelEBucketsC, + NegativeBucket: otelEBucketsD, + ZeroThreshold: .02, + Min: metricdata.NewExtrema(int64(minB)), + Max: metricdata.NewExtrema(int64(maxB)), + Sum: int64(sumB), + }} + otelEHDPFloat64 = []metricdata.ExponentialHistogramDataPoint[float64]{{ + Attributes: alice, + StartTime: start, + Time: end, + Count: 30, + Scale: 2, + ZeroCount: 10, + PositiveBucket: otelEBucketA, + NegativeBucket: otelEBucketB, + ZeroThreshold: .01, + Min: metricdata.NewExtrema(minA), + Max: metricdata.NewExtrema(maxA), + Sum: sumA, + }, { + Attributes: bob, + StartTime: start, + Time: end, + Count: 3, + Scale: 4, + ZeroCount: 1, + PositiveBucket: otelEBucketsC, + NegativeBucket: otelEBucketsD, + ZeroThreshold: .02, + Min: metricdata.NewExtrema(minB), + Max: metricdata.NewExtrema(maxB), + Sum: sumB, + }} + pbHDP = []*mpb.HistogramDataPoint{{ Attributes: []*cpb.KeyValue{pbAlice}, StartTimeUnixNano: uint64(start.UnixNano()), @@ -117,6 +189,49 @@ var ( Max: &maxB, }} + pbEHDPBA = &mpb.ExponentialHistogramDataPoint_Buckets{ + Offset: 5, + BucketCounts: []uint64{0, 5, 0, 5}, + } + pbEHDPBB = &mpb.ExponentialHistogramDataPoint_Buckets{ + Offset: 3, + BucketCounts: []uint64{0, 5, 0, 5}, + } + pbEHDPBC = &mpb.ExponentialHistogramDataPoint_Buckets{ + Offset: 5, + BucketCounts: []uint64{0, 1}, + } + pbEHDPBD = &mpb.ExponentialHistogramDataPoint_Buckets{ + Offset: 3, + BucketCounts: []uint64{0, 1}, + } + + pbEHDP = []*mpb.ExponentialHistogramDataPoint{{ + Attributes: []*cpb.KeyValue{pbAlice}, + StartTimeUnixNano: uint64(start.UnixNano()), + TimeUnixNano: uint64(end.UnixNano()), + Count: 30, + Sum: &sumA, + Scale: 2, + ZeroCount: 10, + Positive: pbEHDPBA, + Negative: pbEHDPBB, + Min: &minA, + Max: &maxA, + }, { + Attributes: []*cpb.KeyValue{pbBob}, + StartTimeUnixNano: uint64(start.UnixNano()), + TimeUnixNano: uint64(end.UnixNano()), + Count: 3, + Sum: &sumB, + Scale: 4, + ZeroCount: 1, + Positive: pbEHDPBC, + Negative: pbEHDPBD, + Min: &minB, + Max: &maxB, + }} + otelHistInt64 = metricdata.Histogram[int64]{ Temporality: metricdata.DeltaTemporality, DataPoints: otelHDPInt64, @@ -131,11 +246,29 @@ var ( DataPoints: otelHDPInt64, } + otelExpoHistInt64 = metricdata.ExponentialHistogram[int64]{ + Temporality: metricdata.DeltaTemporality, + DataPoints: otelEHDPInt64, + } + otelExpoHistFloat64 = metricdata.ExponentialHistogram[float64]{ + Temporality: metricdata.DeltaTemporality, + DataPoints: otelEHDPFloat64, + } + otelExpoHistInvalid = metricdata.ExponentialHistogram[int64]{ + Temporality: invalidTemporality, + DataPoints: otelEHDPInt64, + } + pbHist = &mpb.Histogram{ AggregationTemporality: mpb.AggregationTemporality_AGGREGATION_TEMPORALITY_DELTA, DataPoints: pbHDP, } + pbExpoHist = &mpb.ExponentialHistogram{ + AggregationTemporality: mpb.AggregationTemporality_AGGREGATION_TEMPORALITY_DELTA, + DataPoints: pbEHDP, + } + otelDPtsInt64 = []metricdata.DataPoint[int64]{ {Attributes: alice, StartTime: start, Time: end, Value: 1}, {Attributes: bob, StartTime: start, Time: end, Value: 2}, @@ -263,6 +396,24 @@ var ( Unit: "1", Data: unknownAgg, }, + { + Name: "int64-ExponentialHistogram", + Description: "Exponential Histogram", + Unit: "1", + Data: otelExpoHistInt64, + }, + { + Name: "float64-ExponentialHistogram", + Description: "Exponential Histogram", + Unit: "1", + Data: otelExpoHistFloat64, + }, + { + Name: "invalid-ExponentialHistogram", + Description: "Invalid Exponential Histogram", + Unit: "1", + Data: otelExpoHistInvalid, + }, } pbMetrics = []*mpb.Metric{ @@ -302,6 +453,18 @@ var ( Unit: "1", Data: &mpb.Metric_Histogram{Histogram: pbHist}, }, + { + Name: "int64-ExponentialHistogram", + Description: "Exponential Histogram", + Unit: "1", + Data: &mpb.Metric_ExponentialHistogram{ExponentialHistogram: pbExpoHist}, + }, + { + Name: "float64-ExponentialHistogram", + Description: "Exponential Histogram", + Unit: "1", + Data: &mpb.Metric_ExponentialHistogram{ExponentialHistogram: pbExpoHist}, + }, } otelScopeMetrics = []metricdata.ScopeMetrics{{ @@ -368,6 +531,9 @@ func TestTransformations(t *testing.T) { assert.Equal(t, pbHDP, HistogramDataPoints(otelHDPFloat64)) assert.Equal(t, pbDPtsInt64, DataPoints[int64](otelDPtsInt64)) require.Equal(t, pbDPtsFloat64, DataPoints[float64](otelDPtsFloat64)) + assert.Equal(t, pbEHDP, ExponentialHistogramDataPoints(otelEHDPInt64)) + assert.Equal(t, pbEHDP, ExponentialHistogramDataPoints(otelEHDPFloat64)) + assert.Equal(t, pbEHDPBA, ExponentialHistogramDataPointBuckets(otelEBucketA)) // Aggregations. h, err := Histogram(otelHistInt64) @@ -393,6 +559,16 @@ func TestTransformations(t *testing.T) { assert.Equal(t, &mpb.Metric_Gauge{Gauge: pbGaugeInt64}, Gauge[int64](otelGaugeInt64)) require.Equal(t, &mpb.Metric_Gauge{Gauge: pbGaugeFloat64}, Gauge[float64](otelGaugeFloat64)) + e, err := ExponentialHistogram(otelExpoHistInt64) + assert.NoError(t, err) + assert.Equal(t, &mpb.Metric_ExponentialHistogram{ExponentialHistogram: pbExpoHist}, e) + e, err = ExponentialHistogram(otelExpoHistFloat64) + assert.NoError(t, err) + assert.Equal(t, &mpb.Metric_ExponentialHistogram{ExponentialHistogram: pbExpoHist}, e) + e, err = ExponentialHistogram(otelExpoHistInvalid) + assert.ErrorIs(t, err, errUnknownTemporality) + assert.Nil(t, e) + // Metrics. m, err := Metrics(otelMetrics) assert.ErrorIs(t, err, errUnknownTemporality)