diff --git a/CHANGELOG.md b/CHANGELOG.md index 58e61d055..e50cd0623 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -20,6 +20,7 @@ This project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.htm - Add `go.opentelemetry.io/otel/log/global` to manage the global `LoggerProvider`. This package is provided with the anticipation that all functionality will be migrate to `go.opentelemetry.io/otel` when `go.opentelemetry.io/otel/log` stabilizes. At which point, users will be required to migrage their code, and this package will be deprecated then removed. (#5085) +- Add support for `Summary` metrics in the `go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp` and `go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc` exporters. (#5100) ### Changed diff --git a/exporters/otlp/otlpmetric/otlpmetricgrpc/internal/transform/metricdata.go b/exporters/otlp/otlpmetric/otlpmetricgrpc/internal/transform/metricdata.go index 2494337fe..669e25e8e 100644 --- a/exporters/otlp/otlpmetric/otlpmetricgrpc/internal/transform/metricdata.go +++ b/exporters/otlp/otlpmetric/otlpmetricgrpc/internal/transform/metricdata.go @@ -98,6 +98,8 @@ func metric(m metricdata.Metrics) (*mpb.Metric, error) { out.Data, err = ExponentialHistogram(a) case metricdata.ExponentialHistogram[float64]: out.Data, err = ExponentialHistogram(a) + case metricdata.Summary: + out.Data = Summary(a) default: return out, fmt.Errorf("%w: %T", errUnknownAggregation, a) } @@ -307,3 +309,44 @@ func Exemplars[N int64 | float64](exemplars []metricdata.Exemplar[N]) []*mpb.Exe } return out } + +// Summary returns an OTLP Metric_Summary generated from s. +func Summary(s metricdata.Summary) *mpb.Metric_Summary { + return &mpb.Metric_Summary{ + Summary: &mpb.Summary{ + DataPoints: SummaryDataPoints(s.DataPoints), + }, + } +} + +// SummaryDataPoints returns a slice of OTLP SummaryDataPoint generated from +// dPts. +func SummaryDataPoints(dPts []metricdata.SummaryDataPoint) []*mpb.SummaryDataPoint { + out := make([]*mpb.SummaryDataPoint, 0, len(dPts)) + for _, dPt := range dPts { + sdp := &mpb.SummaryDataPoint{ + Attributes: AttrIter(dPt.Attributes.Iter()), + StartTimeUnixNano: timeUnixNano(dPt.StartTime), + TimeUnixNano: timeUnixNano(dPt.Time), + Count: dPt.Count, + Sum: dPt.Sum, + QuantileValues: QuantileValues(dPt.QuantileValues), + } + out = append(out, sdp) + } + return out +} + +// QuantileValues returns a slice of OTLP SummaryDataPoint_ValueAtQuantile +// generated from quantiles. +func QuantileValues(quantiles []metricdata.QuantileValue) []*mpb.SummaryDataPoint_ValueAtQuantile { + out := make([]*mpb.SummaryDataPoint_ValueAtQuantile, 0, len(quantiles)) + for _, q := range quantiles { + quantile := &mpb.SummaryDataPoint_ValueAtQuantile{ + Quantile: q.Quantile, + Value: q.Value, + } + out = append(out, quantile) + } + return out +} diff --git a/exporters/otlp/otlpmetric/otlpmetricgrpc/internal/transform/metricdata_test.go b/exporters/otlp/otlpmetric/otlpmetricgrpc/internal/transform/metricdata_test.go index d2770158d..b0bc71e9e 100644 --- a/exporters/otlp/otlpmetric/otlpmetricgrpc/internal/transform/metricdata_test.go +++ b/exporters/otlp/otlpmetric/otlpmetricgrpc/internal/transform/metricdata_test.go @@ -432,6 +432,83 @@ var ( DataPoints: pbEHDPFloat64, } + quantileValuesA = []metricdata.QuantileValue{ + { + Quantile: 0.0, + Value: 0.1, + }, + { + Quantile: 0.5, + Value: 1.0, + }, + { + Quantile: 1.0, + Value: 10.4, + }, + } + quantileValuesB = []metricdata.QuantileValue{ + { + Quantile: 0.0, + Value: 0.5, + }, + { + Quantile: 0.5, + Value: 3.1, + }, + { + Quantile: 1.0, + Value: 8.3, + }, + } + + pbQuantileValuesA = []*mpb.SummaryDataPoint_ValueAtQuantile{ + { + Quantile: 0.0, + Value: 0.1, + }, + { + Quantile: 0.5, + Value: 1.0, + }, + { + Quantile: 1.0, + Value: 10.4, + }, + } + pbQuantileValuesB = []*mpb.SummaryDataPoint_ValueAtQuantile{ + { + Quantile: 0.0, + Value: 0.5, + }, + { + Quantile: 0.5, + Value: 3.1, + }, + { + Quantile: 1.0, + Value: 8.3, + }, + } + + otelSummaryDPts = []metricdata.SummaryDataPoint{ + { + Attributes: alice, + StartTime: start, + Time: end, + Count: 20, + Sum: sumA, + QuantileValues: quantileValuesA, + }, + { + Attributes: bob, + StartTime: start, + Time: end, + Count: 26, + Sum: sumB, + QuantileValues: quantileValuesB, + }, + } + otelDPtsInt64 = []metricdata.DataPoint[int64]{ { Attributes: alice, @@ -498,6 +575,25 @@ var ( }, } + pbDPtsSummary = []*mpb.SummaryDataPoint{ + { + Attributes: []*cpb.KeyValue{pbAlice}, + StartTimeUnixNano: uint64(start.UnixNano()), + TimeUnixNano: uint64(end.UnixNano()), + Count: 20, + Sum: sumA, + QuantileValues: pbQuantileValuesA, + }, + { + Attributes: []*cpb.KeyValue{pbBob}, + StartTimeUnixNano: uint64(start.UnixNano()), + TimeUnixNano: uint64(end.UnixNano()), + Count: 26, + Sum: sumB, + QuantileValues: pbQuantileValuesB, + }, + } + otelSumInt64 = metricdata.Sum[int64]{ Temporality: metricdata.CumulativeTemporality, IsMonotonic: true, @@ -551,6 +647,10 @@ var ( }, }} + pbSummary = &mpb.Summary{DataPoints: pbDPtsSummary} + + otelSummary = metricdata.Summary{DataPoints: otelSummaryDPts} + unknownAgg unknownAggT otelMetrics = []metricdata.Metrics{ { @@ -631,6 +731,12 @@ var ( Unit: "1", Data: otelGaugeZeroStartTime, }, + { + Name: "summary", + Description: "Summary metric", + Unit: "1", + Data: otelSummary, + }, } pbMetrics = []*mpb.Metric{ @@ -688,6 +794,12 @@ var ( Unit: "1", Data: &mpb.Metric_Gauge{Gauge: pbGaugeZeroStartTime}, }, + { + Name: "summary", + Description: "Summary metric", + Unit: "1", + Data: &mpb.Metric_Summary{Summary: pbSummary}, + }, } otelScopeMetrics = []metricdata.ScopeMetrics{ @@ -761,6 +873,7 @@ func TestTransformations(t *testing.T) { assert.Equal(t, pbEHDPInt64, ExponentialHistogramDataPoints(otelEHDPInt64)) assert.Equal(t, pbEHDPFloat64, ExponentialHistogramDataPoints(otelEHDPFloat64)) assert.Equal(t, pbEHDPBA, ExponentialHistogramDataPointBuckets(otelEBucketA)) + assert.Equal(t, pbDPtsSummary, SummaryDataPoints(otelSummaryDPts)) // Aggregations. h, err := Histogram(otelHistInt64) @@ -796,6 +909,8 @@ func TestTransformations(t *testing.T) { assert.ErrorIs(t, err, errUnknownTemporality) assert.Nil(t, e) + require.Equal(t, &mpb.Metric_Summary{Summary: pbSummary}, Summary(otelSummary)) + // Metrics. m, err := Metrics(otelMetrics) assert.ErrorIs(t, err, errUnknownTemporality) diff --git a/exporters/otlp/otlpmetric/otlpmetrichttp/internal/transform/metricdata.go b/exporters/otlp/otlpmetric/otlpmetrichttp/internal/transform/metricdata.go index d68451717..04c2ce757 100644 --- a/exporters/otlp/otlpmetric/otlpmetrichttp/internal/transform/metricdata.go +++ b/exporters/otlp/otlpmetric/otlpmetrichttp/internal/transform/metricdata.go @@ -98,6 +98,8 @@ func metric(m metricdata.Metrics) (*mpb.Metric, error) { out.Data, err = ExponentialHistogram(a) case metricdata.ExponentialHistogram[float64]: out.Data, err = ExponentialHistogram(a) + case metricdata.Summary: + out.Data = Summary(a) default: return out, fmt.Errorf("%w: %T", errUnknownAggregation, a) } @@ -307,3 +309,44 @@ func Exemplars[N int64 | float64](exemplars []metricdata.Exemplar[N]) []*mpb.Exe } return out } + +// Summary returns an OTLP Metric_Summary generated from s. +func Summary(s metricdata.Summary) *mpb.Metric_Summary { + return &mpb.Metric_Summary{ + Summary: &mpb.Summary{ + DataPoints: SummaryDataPoints(s.DataPoints), + }, + } +} + +// SummaryDataPoints returns a slice of OTLP SummaryDataPoint generated from +// dPts. +func SummaryDataPoints(dPts []metricdata.SummaryDataPoint) []*mpb.SummaryDataPoint { + out := make([]*mpb.SummaryDataPoint, 0, len(dPts)) + for _, dPt := range dPts { + sdp := &mpb.SummaryDataPoint{ + Attributes: AttrIter(dPt.Attributes.Iter()), + StartTimeUnixNano: timeUnixNano(dPt.StartTime), + TimeUnixNano: timeUnixNano(dPt.Time), + Count: dPt.Count, + Sum: dPt.Sum, + QuantileValues: QuantileValues(dPt.QuantileValues), + } + out = append(out, sdp) + } + return out +} + +// QuantileValues returns a slice of OTLP SummaryDataPoint_ValueAtQuantile +// generated from quantiles. +func QuantileValues(quantiles []metricdata.QuantileValue) []*mpb.SummaryDataPoint_ValueAtQuantile { + out := make([]*mpb.SummaryDataPoint_ValueAtQuantile, 0, len(quantiles)) + for _, q := range quantiles { + quantile := &mpb.SummaryDataPoint_ValueAtQuantile{ + Quantile: q.Quantile, + Value: q.Value, + } + out = append(out, quantile) + } + return out +} diff --git a/exporters/otlp/otlpmetric/otlpmetrichttp/internal/transform/metricdata_test.go b/exporters/otlp/otlpmetric/otlpmetrichttp/internal/transform/metricdata_test.go index d2770158d..b0bc71e9e 100644 --- a/exporters/otlp/otlpmetric/otlpmetrichttp/internal/transform/metricdata_test.go +++ b/exporters/otlp/otlpmetric/otlpmetrichttp/internal/transform/metricdata_test.go @@ -432,6 +432,83 @@ var ( DataPoints: pbEHDPFloat64, } + quantileValuesA = []metricdata.QuantileValue{ + { + Quantile: 0.0, + Value: 0.1, + }, + { + Quantile: 0.5, + Value: 1.0, + }, + { + Quantile: 1.0, + Value: 10.4, + }, + } + quantileValuesB = []metricdata.QuantileValue{ + { + Quantile: 0.0, + Value: 0.5, + }, + { + Quantile: 0.5, + Value: 3.1, + }, + { + Quantile: 1.0, + Value: 8.3, + }, + } + + pbQuantileValuesA = []*mpb.SummaryDataPoint_ValueAtQuantile{ + { + Quantile: 0.0, + Value: 0.1, + }, + { + Quantile: 0.5, + Value: 1.0, + }, + { + Quantile: 1.0, + Value: 10.4, + }, + } + pbQuantileValuesB = []*mpb.SummaryDataPoint_ValueAtQuantile{ + { + Quantile: 0.0, + Value: 0.5, + }, + { + Quantile: 0.5, + Value: 3.1, + }, + { + Quantile: 1.0, + Value: 8.3, + }, + } + + otelSummaryDPts = []metricdata.SummaryDataPoint{ + { + Attributes: alice, + StartTime: start, + Time: end, + Count: 20, + Sum: sumA, + QuantileValues: quantileValuesA, + }, + { + Attributes: bob, + StartTime: start, + Time: end, + Count: 26, + Sum: sumB, + QuantileValues: quantileValuesB, + }, + } + otelDPtsInt64 = []metricdata.DataPoint[int64]{ { Attributes: alice, @@ -498,6 +575,25 @@ var ( }, } + pbDPtsSummary = []*mpb.SummaryDataPoint{ + { + Attributes: []*cpb.KeyValue{pbAlice}, + StartTimeUnixNano: uint64(start.UnixNano()), + TimeUnixNano: uint64(end.UnixNano()), + Count: 20, + Sum: sumA, + QuantileValues: pbQuantileValuesA, + }, + { + Attributes: []*cpb.KeyValue{pbBob}, + StartTimeUnixNano: uint64(start.UnixNano()), + TimeUnixNano: uint64(end.UnixNano()), + Count: 26, + Sum: sumB, + QuantileValues: pbQuantileValuesB, + }, + } + otelSumInt64 = metricdata.Sum[int64]{ Temporality: metricdata.CumulativeTemporality, IsMonotonic: true, @@ -551,6 +647,10 @@ var ( }, }} + pbSummary = &mpb.Summary{DataPoints: pbDPtsSummary} + + otelSummary = metricdata.Summary{DataPoints: otelSummaryDPts} + unknownAgg unknownAggT otelMetrics = []metricdata.Metrics{ { @@ -631,6 +731,12 @@ var ( Unit: "1", Data: otelGaugeZeroStartTime, }, + { + Name: "summary", + Description: "Summary metric", + Unit: "1", + Data: otelSummary, + }, } pbMetrics = []*mpb.Metric{ @@ -688,6 +794,12 @@ var ( Unit: "1", Data: &mpb.Metric_Gauge{Gauge: pbGaugeZeroStartTime}, }, + { + Name: "summary", + Description: "Summary metric", + Unit: "1", + Data: &mpb.Metric_Summary{Summary: pbSummary}, + }, } otelScopeMetrics = []metricdata.ScopeMetrics{ @@ -761,6 +873,7 @@ func TestTransformations(t *testing.T) { assert.Equal(t, pbEHDPInt64, ExponentialHistogramDataPoints(otelEHDPInt64)) assert.Equal(t, pbEHDPFloat64, ExponentialHistogramDataPoints(otelEHDPFloat64)) assert.Equal(t, pbEHDPBA, ExponentialHistogramDataPointBuckets(otelEBucketA)) + assert.Equal(t, pbDPtsSummary, SummaryDataPoints(otelSummaryDPts)) // Aggregations. h, err := Histogram(otelHistInt64) @@ -796,6 +909,8 @@ func TestTransformations(t *testing.T) { assert.ErrorIs(t, err, errUnknownTemporality) assert.Nil(t, e) + require.Equal(t, &mpb.Metric_Summary{Summary: pbSummary}, Summary(otelSummary)) + // Metrics. m, err := Metrics(otelMetrics) assert.ErrorIs(t, err, errUnknownTemporality) diff --git a/internal/shared/otlp/otlpmetric/transform/metricdata.go.tmpl b/internal/shared/otlp/otlpmetric/transform/metricdata.go.tmpl index 01b259764..b6d0b76fa 100644 --- a/internal/shared/otlp/otlpmetric/transform/metricdata.go.tmpl +++ b/internal/shared/otlp/otlpmetric/transform/metricdata.go.tmpl @@ -98,6 +98,8 @@ func metric(m metricdata.Metrics) (*mpb.Metric, error) { out.Data, err = ExponentialHistogram(a) case metricdata.ExponentialHistogram[float64]: out.Data, err = ExponentialHistogram(a) + case metricdata.Summary: + out.Data = Summary(a) default: return out, fmt.Errorf("%w: %T", errUnknownAggregation, a) } @@ -307,3 +309,44 @@ func Exemplars[N int64 | float64](exemplars []metricdata.Exemplar[N]) []*mpb.Exe } return out } + +// Summary returns an OTLP Metric_Summary generated from s. +func Summary(s metricdata.Summary) *mpb.Metric_Summary { + return &mpb.Metric_Summary{ + Summary: &mpb.Summary{ + DataPoints: SummaryDataPoints(s.DataPoints), + }, + } +} + +// SummaryDataPoints returns a slice of OTLP SummaryDataPoint generated from +// dPts. +func SummaryDataPoints(dPts []metricdata.SummaryDataPoint) []*mpb.SummaryDataPoint { + out := make([]*mpb.SummaryDataPoint, 0, len(dPts)) + for _, dPt := range dPts { + sdp := &mpb.SummaryDataPoint{ + Attributes: AttrIter(dPt.Attributes.Iter()), + StartTimeUnixNano: timeUnixNano(dPt.StartTime), + TimeUnixNano: timeUnixNano(dPt.Time), + Count: dPt.Count, + Sum: dPt.Sum, + QuantileValues: QuantileValues(dPt.QuantileValues), + } + out = append(out, sdp) + } + return out +} + +// QuantileValues returns a slice of OTLP SummaryDataPoint_ValueAtQuantile +// generated from quantiles. +func QuantileValues(quantiles []metricdata.QuantileValue) []*mpb.SummaryDataPoint_ValueAtQuantile { + out := make([]*mpb.SummaryDataPoint_ValueAtQuantile, 0, len(quantiles)) + for _, q := range quantiles { + quantile := &mpb.SummaryDataPoint_ValueAtQuantile{ + Quantile: q.Quantile, + Value: q.Value, + } + out = append(out, quantile) + } + return out +} diff --git a/internal/shared/otlp/otlpmetric/transform/metricdata_test.go.tmpl b/internal/shared/otlp/otlpmetric/transform/metricdata_test.go.tmpl index d2770158d..b0bc71e9e 100644 --- a/internal/shared/otlp/otlpmetric/transform/metricdata_test.go.tmpl +++ b/internal/shared/otlp/otlpmetric/transform/metricdata_test.go.tmpl @@ -432,6 +432,83 @@ var ( DataPoints: pbEHDPFloat64, } + quantileValuesA = []metricdata.QuantileValue{ + { + Quantile: 0.0, + Value: 0.1, + }, + { + Quantile: 0.5, + Value: 1.0, + }, + { + Quantile: 1.0, + Value: 10.4, + }, + } + quantileValuesB = []metricdata.QuantileValue{ + { + Quantile: 0.0, + Value: 0.5, + }, + { + Quantile: 0.5, + Value: 3.1, + }, + { + Quantile: 1.0, + Value: 8.3, + }, + } + + pbQuantileValuesA = []*mpb.SummaryDataPoint_ValueAtQuantile{ + { + Quantile: 0.0, + Value: 0.1, + }, + { + Quantile: 0.5, + Value: 1.0, + }, + { + Quantile: 1.0, + Value: 10.4, + }, + } + pbQuantileValuesB = []*mpb.SummaryDataPoint_ValueAtQuantile{ + { + Quantile: 0.0, + Value: 0.5, + }, + { + Quantile: 0.5, + Value: 3.1, + }, + { + Quantile: 1.0, + Value: 8.3, + }, + } + + otelSummaryDPts = []metricdata.SummaryDataPoint{ + { + Attributes: alice, + StartTime: start, + Time: end, + Count: 20, + Sum: sumA, + QuantileValues: quantileValuesA, + }, + { + Attributes: bob, + StartTime: start, + Time: end, + Count: 26, + Sum: sumB, + QuantileValues: quantileValuesB, + }, + } + otelDPtsInt64 = []metricdata.DataPoint[int64]{ { Attributes: alice, @@ -498,6 +575,25 @@ var ( }, } + pbDPtsSummary = []*mpb.SummaryDataPoint{ + { + Attributes: []*cpb.KeyValue{pbAlice}, + StartTimeUnixNano: uint64(start.UnixNano()), + TimeUnixNano: uint64(end.UnixNano()), + Count: 20, + Sum: sumA, + QuantileValues: pbQuantileValuesA, + }, + { + Attributes: []*cpb.KeyValue{pbBob}, + StartTimeUnixNano: uint64(start.UnixNano()), + TimeUnixNano: uint64(end.UnixNano()), + Count: 26, + Sum: sumB, + QuantileValues: pbQuantileValuesB, + }, + } + otelSumInt64 = metricdata.Sum[int64]{ Temporality: metricdata.CumulativeTemporality, IsMonotonic: true, @@ -551,6 +647,10 @@ var ( }, }} + pbSummary = &mpb.Summary{DataPoints: pbDPtsSummary} + + otelSummary = metricdata.Summary{DataPoints: otelSummaryDPts} + unknownAgg unknownAggT otelMetrics = []metricdata.Metrics{ { @@ -631,6 +731,12 @@ var ( Unit: "1", Data: otelGaugeZeroStartTime, }, + { + Name: "summary", + Description: "Summary metric", + Unit: "1", + Data: otelSummary, + }, } pbMetrics = []*mpb.Metric{ @@ -688,6 +794,12 @@ var ( Unit: "1", Data: &mpb.Metric_Gauge{Gauge: pbGaugeZeroStartTime}, }, + { + Name: "summary", + Description: "Summary metric", + Unit: "1", + Data: &mpb.Metric_Summary{Summary: pbSummary}, + }, } otelScopeMetrics = []metricdata.ScopeMetrics{ @@ -761,6 +873,7 @@ func TestTransformations(t *testing.T) { assert.Equal(t, pbEHDPInt64, ExponentialHistogramDataPoints(otelEHDPInt64)) assert.Equal(t, pbEHDPFloat64, ExponentialHistogramDataPoints(otelEHDPFloat64)) assert.Equal(t, pbEHDPBA, ExponentialHistogramDataPointBuckets(otelEBucketA)) + assert.Equal(t, pbDPtsSummary, SummaryDataPoints(otelSummaryDPts)) // Aggregations. h, err := Histogram(otelHistInt64) @@ -796,6 +909,8 @@ func TestTransformations(t *testing.T) { assert.ErrorIs(t, err, errUnknownTemporality) assert.Nil(t, e) + require.Equal(t, &mpb.Metric_Summary{Summary: pbSummary}, Summary(otelSummary)) + // Metrics. m, err := Metrics(otelMetrics) assert.ErrorIs(t, err, errUnknownTemporality)