pkg/pdatatest/pmetrictest/options.go (802 lines of code) (raw):

// Copyright The OpenTelemetry Authors // SPDX-License-Identifier: Apache-2.0 package pmetrictest // import "github.com/open-telemetry/opentelemetry-collector-contrib/pkg/pdatatest/pmetrictest" import ( "bytes" "fmt" "math" "regexp" "time" "go.opentelemetry.io/collector/pdata/pcommon" "go.opentelemetry.io/collector/pdata/pmetric" "github.com/open-telemetry/opentelemetry-collector-contrib/pkg/pdatatest/internal" "github.com/open-telemetry/opentelemetry-collector-contrib/pkg/pdatautil" ) // CompareMetricsOption can be used to mutate expected and/or actual metrics before comparing. type CompareMetricsOption interface { applyOnMetrics(expected, actual pmetric.Metrics) } type compareMetricsOptionFunc func(expected, actual pmetric.Metrics) func (f compareMetricsOptionFunc) applyOnMetrics(expected, actual pmetric.Metrics) { f(expected, actual) } // IgnoreMetricValues is a CompareMetricsOption that clears all metric values. func IgnoreMetricValues(metricNames ...string) CompareMetricsOption { return compareMetricsOptionFunc(func(expected, actual pmetric.Metrics) { maskMetricValues(expected, metricNames...) maskMetricValues(actual, metricNames...) }) } func maskMetricValues(metrics pmetric.Metrics, metricNames ...string) { rms := metrics.ResourceMetrics() for i := 0; i < rms.Len(); i++ { ilms := rms.At(i).ScopeMetrics() for j := 0; j < ilms.Len(); j++ { maskMetricSliceValues(ilms.At(j).Metrics(), metricNames...) } } } // maskMetricSliceValues sets all data point values to zero. func maskMetricSliceValues(metrics pmetric.MetricSlice, metricNames ...string) { metricNameSet := make(map[string]bool, len(metricNames)) for _, metricName := range metricNames { metricNameSet[metricName] = true } for i := 0; i < metrics.Len(); i++ { if len(metricNames) == 0 || metricNameSet[metrics.At(i).Name()] { switch metrics.At(i).Type() { case pmetric.MetricTypeEmpty, pmetric.MetricTypeSum, pmetric.MetricTypeGauge: maskDataPointSliceValues(getDataPointSlice(metrics.At(i))) case pmetric.MetricTypeHistogram: maskHistogramDataPointSliceValues(metrics.At(i).Histogram().DataPoints()) default: panic(fmt.Sprintf("data type not supported: %s", metrics.At(i).Type())) } } } } func getDataPointSlice(metric pmetric.Metric) pmetric.NumberDataPointSlice { var dataPointSlice pmetric.NumberDataPointSlice //exhaustive:enforce switch metric.Type() { case pmetric.MetricTypeGauge: dataPointSlice = metric.Gauge().DataPoints() case pmetric.MetricTypeSum: dataPointSlice = metric.Sum().DataPoints() case pmetric.MetricTypeEmpty: dataPointSlice = pmetric.NewNumberDataPointSlice() case pmetric.MetricTypeHistogram, pmetric.MetricTypeExponentialHistogram, pmetric.MetricTypeSummary: fallthrough default: panic(fmt.Sprintf("data type not supported: %s", metric.Type())) } return dataPointSlice } // maskDataPointSliceValues sets all data point values to zero. func maskDataPointSliceValues(dataPoints pmetric.NumberDataPointSlice) { for i := 0; i < dataPoints.Len(); i++ { dataPoint := dataPoints.At(i) dataPoint.SetIntValue(0) dataPoint.SetDoubleValue(0) } } // maskHistogramDataPointSliceValues sets all data point values to zero. func maskHistogramDataPointSliceValues(dataPoints pmetric.HistogramDataPointSlice) { for i := 0; i < dataPoints.Len(); i++ { dataPoint := dataPoints.At(i) dataPoint.SetCount(0) dataPoint.SetSum(0) dataPoint.SetMin(0) dataPoint.SetMax(0) dataPoint.BucketCounts().FromRaw([]uint64{}) dataPoint.Exemplars().RemoveIf(func(pmetric.Exemplar) bool { return true }) dataPoint.ExplicitBounds().FromRaw([]float64{}) } } // IgnoreMetricFloatPrecision is a CompareMetricsOption that rounds away float precision discrepancies in metric values. func IgnoreMetricFloatPrecision(precision int, metricNames ...string) CompareMetricsOption { return compareMetricsOptionFunc(func(expected, actual pmetric.Metrics) { floatMetricValues(precision, expected, metricNames...) floatMetricValues(precision, actual, metricNames...) }) } func floatMetricValues(precision int, metrics pmetric.Metrics, metricNames ...string) { rms := metrics.ResourceMetrics() for i := 0; i < rms.Len(); i++ { ilms := rms.At(i).ScopeMetrics() for j := 0; j < ilms.Len(); j++ { floatMetricSliceValues(precision, ilms.At(j).Metrics(), metricNames...) } } } // floatMetricSliceValues sets all data point values to zero. func floatMetricSliceValues(precision int, metrics pmetric.MetricSlice, metricNames ...string) { metricNameSet := make(map[string]bool, len(metricNames)) for _, metricName := range metricNames { metricNameSet[metricName] = true } for i := 0; i < metrics.Len(); i++ { if len(metricNames) == 0 || metricNameSet[metrics.At(i).Name()] { switch metrics.At(i).Type() { case pmetric.MetricTypeEmpty, pmetric.MetricTypeSum, pmetric.MetricTypeGauge: roundDataPointSliceValues(getDataPointSlice(metrics.At(i)), precision) default: panic(fmt.Sprintf("data type not supported: %s", metrics.At(i).Type())) } } } } // maskDataPointSliceValues rounds all data point values at a given decimal. func roundDataPointSliceValues(dataPoints pmetric.NumberDataPointSlice, precision int) { for i := 0; i < dataPoints.Len(); i++ { dataPoint := dataPoints.At(i) factor := math.Pow(10, float64(precision)) switch { case dataPoint.DoubleValue() != 0.0: dataPoint.SetDoubleValue(math.Round(dataPoint.DoubleValue()*factor) / factor) case dataPoint.IntValue() != 0: panic(fmt.Sprintf("integers can not have float precision ignored: %v", dataPoints.At(i))) } } } // IgnoreExemplars is a CompareMetricsOption that clears exemplar fields on all data points. func IgnoreExemplars() CompareMetricsOption { return compareMetricsOptionFunc(func(expected, actual pmetric.Metrics) { maskExemplars(expected) maskExemplars(actual) }) } func maskExemplars(metrics pmetric.Metrics) { emptyExemplar := pmetric.NewExemplar() for i := 0; i < metrics.ResourceMetrics().Len(); i++ { for j := 0; j < metrics.ResourceMetrics().At(i).ScopeMetrics().Len(); j++ { for g := 0; g < metrics.ResourceMetrics().At(i).ScopeMetrics().At(j).Metrics().Len(); g++ { m := metrics.ResourceMetrics().At(i).ScopeMetrics().At(j).Metrics().At(g) switch m.Type() { case pmetric.MetricTypeGauge: datapoints := m.Gauge().DataPoints() for k := 0; k < datapoints.Len(); k++ { for f := 0; f < datapoints.At(k).Exemplars().Len(); f++ { emptyExemplar.CopyTo(datapoints.At(k).Exemplars().At(f)) } } case pmetric.MetricTypeSum: datapoints := m.Sum().DataPoints() for k := 0; k < datapoints.Len(); k++ { for f := 0; f < datapoints.At(k).Exemplars().Len(); f++ { emptyExemplar.CopyTo(datapoints.At(k).Exemplars().At(f)) } } case pmetric.MetricTypeHistogram: datapoints := m.Histogram().DataPoints() for k := 0; k < datapoints.Len(); k++ { for f := 0; f < datapoints.At(k).Exemplars().Len(); f++ { emptyExemplar.CopyTo(datapoints.At(k).Exemplars().At(f)) } } case pmetric.MetricTypeExponentialHistogram: datapoints := m.ExponentialHistogram().DataPoints() for k := 0; k < datapoints.Len(); k++ { for f := 0; f < datapoints.At(k).Exemplars().Len(); f++ { emptyExemplar.CopyTo(datapoints.At(k).Exemplars().At(f)) } } } } } } } // IgnoreExemplarSlice is a CompareMetricsOption that clears exemplars slice on all data points. func IgnoreExemplarSlice() CompareMetricsOption { return compareMetricsOptionFunc(func(expected, actual pmetric.Metrics) { maskExemplarSlice(expected) maskExemplarSlice(actual) }) } func maskExemplarSlice(metrics pmetric.Metrics) { emptyExemplarSlice := pmetric.NewExemplarSlice() for i := 0; i < metrics.ResourceMetrics().Len(); i++ { for j := 0; j < metrics.ResourceMetrics().At(i).ScopeMetrics().Len(); j++ { for g := 0; g < metrics.ResourceMetrics().At(i).ScopeMetrics().At(j).Metrics().Len(); g++ { m := metrics.ResourceMetrics().At(i).ScopeMetrics().At(j).Metrics().At(g) switch m.Type() { case pmetric.MetricTypeGauge: datapoints := m.Gauge().DataPoints() for k := 0; k < datapoints.Len(); k++ { emptyExemplarSlice.CopyTo(datapoints.At(k).Exemplars()) } case pmetric.MetricTypeSum: datapoints := m.Sum().DataPoints() for k := 0; k < datapoints.Len(); k++ { emptyExemplarSlice.CopyTo(datapoints.At(k).Exemplars()) } case pmetric.MetricTypeHistogram: datapoints := m.Histogram().DataPoints() for k := 0; k < datapoints.Len(); k++ { emptyExemplarSlice.CopyTo(datapoints.At(k).Exemplars()) } case pmetric.MetricTypeExponentialHistogram: datapoints := m.ExponentialHistogram().DataPoints() for k := 0; k < datapoints.Len(); k++ { emptyExemplarSlice.CopyTo(datapoints.At(k).Exemplars()) } } } } } } // IgnoreTimestamp is a CompareMetricsOption that clears Timestamp fields on all the data points. func IgnoreTimestamp() CompareMetricsOption { return compareMetricsOptionFunc(func(expected, actual pmetric.Metrics) { now := pcommon.NewTimestampFromTime(time.Now()) maskTimestamp(expected, now) maskTimestamp(actual, now) }) } func maskTimestamp(metrics pmetric.Metrics, ts pcommon.Timestamp) { rms := metrics.ResourceMetrics() for i := 0; i < rms.Len(); i++ { for j := 0; j < rms.At(i).ScopeMetrics().Len(); j++ { for k := 0; k < rms.At(i).ScopeMetrics().At(j).Metrics().Len(); k++ { m := rms.At(i).ScopeMetrics().At(j).Metrics().At(k) //exhaustive:enforce switch m.Type() { case pmetric.MetricTypeGauge: for l := 0; l < m.Gauge().DataPoints().Len(); l++ { m.Gauge().DataPoints().At(l).SetTimestamp(ts) } case pmetric.MetricTypeSum: for l := 0; l < m.Sum().DataPoints().Len(); l++ { m.Sum().DataPoints().At(l).SetTimestamp(ts) } case pmetric.MetricTypeHistogram: for l := 0; l < m.Histogram().DataPoints().Len(); l++ { m.Histogram().DataPoints().At(l).SetTimestamp(ts) } case pmetric.MetricTypeExponentialHistogram: for l := 0; l < m.ExponentialHistogram().DataPoints().Len(); l++ { m.ExponentialHistogram().DataPoints().At(l).SetTimestamp(ts) } case pmetric.MetricTypeSummary: for l := 0; l < m.Summary().DataPoints().Len(); l++ { m.Summary().DataPoints().At(l).SetTimestamp(ts) } case pmetric.MetricTypeEmpty: } } } } } // IgnoreStartTimestamp is a CompareMetricsOption that clears StartTimestamp fields on all the data points. func IgnoreStartTimestamp() CompareMetricsOption { return compareMetricsOptionFunc(func(expected, actual pmetric.Metrics) { now := pcommon.NewTimestampFromTime(time.Now()) maskStartTimestamp(expected, now) maskStartTimestamp(actual, now) }) } func maskStartTimestamp(metrics pmetric.Metrics, ts pcommon.Timestamp) { rms := metrics.ResourceMetrics() for i := 0; i < rms.Len(); i++ { for j := 0; j < rms.At(i).ScopeMetrics().Len(); j++ { for k := 0; k < rms.At(i).ScopeMetrics().At(j).Metrics().Len(); k++ { m := rms.At(i).ScopeMetrics().At(j).Metrics().At(k) //exhaustive:enforce switch m.Type() { case pmetric.MetricTypeGauge: for l := 0; l < m.Gauge().DataPoints().Len(); l++ { m.Gauge().DataPoints().At(l).SetStartTimestamp(ts) } case pmetric.MetricTypeSum: for l := 0; l < m.Sum().DataPoints().Len(); l++ { m.Sum().DataPoints().At(l).SetStartTimestamp(ts) } case pmetric.MetricTypeHistogram: for l := 0; l < m.Histogram().DataPoints().Len(); l++ { m.Histogram().DataPoints().At(l).SetStartTimestamp(ts) } case pmetric.MetricTypeExponentialHistogram: for l := 0; l < m.ExponentialHistogram().DataPoints().Len(); l++ { m.ExponentialHistogram().DataPoints().At(l).SetStartTimestamp(ts) } case pmetric.MetricTypeSummary: for l := 0; l < m.Summary().DataPoints().Len(); l++ { m.Summary().DataPoints().At(l).SetStartTimestamp(ts) } case pmetric.MetricTypeEmpty: } } } } } // IgnoreMetricAttributeValue is a CompareMetricsOption that clears value of the metric attribute. func IgnoreMetricAttributeValue(attributeName string, metricNames ...string) CompareMetricsOption { return compareMetricsOptionFunc(func(expected, actual pmetric.Metrics) { maskMetricAttributeValue(expected, attributeName, metricNames) maskMetricAttributeValue(actual, attributeName, metricNames) }) } // IgnoreDatapointAttributesOrder is a CompareMetricsOption that ignores the order of datapoint attributes. func IgnoreDatapointAttributesOrder() CompareMetricsOption { return compareMetricsOptionFunc(func(expected, actual pmetric.Metrics) { orderDatapointAttributes(expected) orderDatapointAttributes(actual) }) } func orderDatapointAttributes(metrics pmetric.Metrics) { rms := metrics.ResourceMetrics() for i := 0; i < rms.Len(); i++ { ilms := rms.At(i).ScopeMetrics() for j := 0; j < ilms.Len(); j++ { msl := ilms.At(j).Metrics() for g := 0; g < msl.Len(); g++ { msl.At(g) switch msl.At(g).Type() { case pmetric.MetricTypeGauge: for k := 0; k < msl.At(g).Gauge().DataPoints().Len(); k++ { rawOrdered := internal.OrderMapByKey(msl.At(g).Gauge().DataPoints().At(k).Attributes().AsRaw()) _ = msl.At(g).Gauge().DataPoints().At(k).Attributes().FromRaw(rawOrdered) } case pmetric.MetricTypeSum: for k := 0; k < msl.At(g).Sum().DataPoints().Len(); k++ { rawOrdered := internal.OrderMapByKey(msl.At(g).Sum().DataPoints().At(k).Attributes().AsRaw()) _ = msl.At(g).Sum().DataPoints().At(k).Attributes().FromRaw(rawOrdered) } case pmetric.MetricTypeHistogram: for k := 0; k < msl.At(g).Histogram().DataPoints().Len(); k++ { rawOrdered := internal.OrderMapByKey(msl.At(g).Histogram().DataPoints().At(k).Attributes().AsRaw()) _ = msl.At(g).Histogram().DataPoints().At(k).Attributes().FromRaw(rawOrdered) } case pmetric.MetricTypeExponentialHistogram: for k := 0; k < msl.At(g).ExponentialHistogram().DataPoints().Len(); k++ { rawOrdered := internal.OrderMapByKey(msl.At(g).ExponentialHistogram().DataPoints().At(k).Attributes().AsRaw()) _ = msl.At(g).ExponentialHistogram().DataPoints().At(k).Attributes().FromRaw(rawOrdered) } case pmetric.MetricTypeSummary: for k := 0; k < msl.At(g).Summary().DataPoints().Len(); k++ { rawOrdered := internal.OrderMapByKey(msl.At(g).Summary().DataPoints().At(k).Attributes().AsRaw()) _ = msl.At(g).Summary().DataPoints().At(k).Attributes().FromRaw(rawOrdered) } case pmetric.MetricTypeEmpty: } } } } } func maskMetricAttributeValue(metrics pmetric.Metrics, attributeName string, metricNames []string) { rms := metrics.ResourceMetrics() for i := 0; i < rms.Len(); i++ { ilms := rms.At(i).ScopeMetrics() for j := 0; j < ilms.Len(); j++ { maskMetricSliceAttributeValues(ilms.At(j).Metrics(), attributeName, metricNames) } } } // maskMetricSliceAttributeValues sets the value of the specified attribute to // the zero value associated with the attribute data type. // If metric names are specified, only the data points within those metrics will be masked. // Otherwise, all data points with the attribute will be masked. func maskMetricSliceAttributeValues(metrics pmetric.MetricSlice, attributeName string, metricNames []string) { metricNameSet := make(map[string]bool, len(metricNames)) for _, metricName := range metricNames { metricNameSet[metricName] = true } for i := 0; i < metrics.Len(); i++ { if len(metricNames) == 0 || metricNameSet[metrics.At(i).Name()] { switch metrics.At(i).Type() { case pmetric.MetricTypeHistogram: dps := metrics.At(i).Histogram().DataPoints() maskHistogramSliceAttributeValues(dps, attributeName) // If attribute values are ignored, some data points may become // indistinguishable from each other, but sorting by value allows // for a reasonably thorough comparison and a deterministic outcome. dps.Sort(func(a, b pmetric.HistogramDataPoint) bool { if a.Sum() < b.Sum() { return true } if a.Min() < b.Min() { return true } if a.Max() < b.Max() { return true } if a.Count() < b.Count() { return true } if a.BucketCounts().Len() < b.BucketCounts().Len() { return true } if a.ExplicitBounds().Len() < b.ExplicitBounds().Len() { return true } return false }) default: dps := getDataPointSlice(metrics.At(i)) maskDataPointSliceAttributeValues(dps, attributeName) // If attribute values are ignored, some data points may become // indistinguishable from each other, but sorting by value allows // for a reasonably thorough comparison and a deterministic outcome. dps.Sort(func(a, b pmetric.NumberDataPoint) bool { if a.IntValue() < b.IntValue() { return true } if a.DoubleValue() < b.DoubleValue() { return true } return false }) } } } } // maskDataPointSliceAttributeValues sets the value of the specified attribute to // the zero value associated with the attribute data type. func maskDataPointSliceAttributeValues(dataPoints pmetric.NumberDataPointSlice, attributeName string) { for i := 0; i < dataPoints.Len(); i++ { attributes := dataPoints.At(i).Attributes() attribute, ok := attributes.Get(attributeName) if ok { switch attribute.Type() { case pcommon.ValueTypeStr: attribute.SetStr("") case pcommon.ValueTypeBool: attribute.SetBool(false) case pcommon.ValueTypeInt: attribute.SetInt(0) case pcommon.ValueTypeEmpty, pcommon.ValueTypeDouble, pcommon.ValueTypeMap, pcommon.ValueTypeSlice, pcommon.ValueTypeBytes: fallthrough default: panic(fmt.Sprintf("data type not supported: %s", attribute.Type())) } } } } // maskHistogramSliceAttributeValues sets the value of the specified attribute to // the zero value associated with the attribute data type. func maskHistogramSliceAttributeValues(dataPoints pmetric.HistogramDataPointSlice, attributeName string) { for i := 0; i < dataPoints.Len(); i++ { attributes := dataPoints.At(i).Attributes() attribute, ok := attributes.Get(attributeName) if ok { switch attribute.Type() { case pcommon.ValueTypeStr: attribute.SetStr("") case pcommon.ValueTypeBool: attribute.SetBool(false) case pcommon.ValueTypeInt: attribute.SetInt(0) case pcommon.ValueTypeEmpty, pcommon.ValueTypeDouble, pcommon.ValueTypeMap, pcommon.ValueTypeSlice, pcommon.ValueTypeBytes: fallthrough default: panic(fmt.Sprintf("data type not supported: %s", attribute.Type())) } } } } // MatchMetricAttributeValue is a CompareMetricsOption that transforms a metric attribute value based on a regular expression. func MatchMetricAttributeValue(attributeName string, pattern string, metricNames ...string) CompareMetricsOption { re := regexp.MustCompile(pattern) return compareMetricsOptionFunc(func(expected, actual pmetric.Metrics) { matchMetricAttributeValue(expected, attributeName, re, metricNames) matchMetricAttributeValue(actual, attributeName, re, metricNames) }) } func matchMetricAttributeValue(metrics pmetric.Metrics, attributeName string, re *regexp.Regexp, metricNames []string) { rms := metrics.ResourceMetrics() for i := 0; i < rms.Len(); i++ { ilms := rms.At(i).ScopeMetrics() for j := 0; j < ilms.Len(); j++ { matchMetricSliceAttributeValues(ilms.At(j).Metrics(), attributeName, re, metricNames) } } } func matchMetricSliceAttributeValues(metrics pmetric.MetricSlice, attributeName string, re *regexp.Regexp, metricNames []string) { metricNameSet := make(map[string]bool, len(metricNames)) for _, metricName := range metricNames { metricNameSet[metricName] = true } for i := 0; i < metrics.Len(); i++ { if len(metricNames) == 0 || metricNameSet[metrics.At(i).Name()] { switch metrics.At(i).Type() { case pmetric.MetricTypeHistogram: dps := metrics.At(i).Histogram().DataPoints() matchHistogramDataPointSliceAttributeValues(dps, attributeName, re) // If attribute values are ignored, some data points may become // indistinguishable from each other, but sorting by value allows // for a reasonably thorough comparison and a deterministic outcome. dps.Sort(func(a, b pmetric.HistogramDataPoint) bool { if a.Sum() < b.Sum() { return true } if a.Min() < b.Min() { return true } if a.Max() < b.Max() { return true } if a.Count() < b.Count() { return true } if a.BucketCounts().Len() < b.BucketCounts().Len() { return true } if a.ExplicitBounds().Len() < b.ExplicitBounds().Len() { return true } return false }) default: dps := getDataPointSlice(metrics.At(i)) matchDataPointSliceAttributeValues(dps, attributeName, re) // If attribute values are ignored, some data points may become // indistinguishable from each other, but sorting by value allows // for a reasonably thorough comparison and a deterministic outcome. dps.Sort(func(a, b pmetric.NumberDataPoint) bool { if a.IntValue() < b.IntValue() { return true } if a.DoubleValue() < b.DoubleValue() { return true } return false }) } } } } func matchDataPointSliceAttributeValues(dataPoints pmetric.NumberDataPointSlice, attributeName string, re *regexp.Regexp) { for i := 0; i < dataPoints.Len(); i++ { attributes := dataPoints.At(i).Attributes() attribute, ok := attributes.Get(attributeName) if ok { results := re.FindStringSubmatch(attribute.Str()) if len(results) > 0 { attribute.SetStr(results[0]) } } } } func matchHistogramDataPointSliceAttributeValues(dataPoints pmetric.HistogramDataPointSlice, attributeName string, re *regexp.Regexp) { for i := 0; i < dataPoints.Len(); i++ { attributes := dataPoints.At(i).Attributes() attribute, ok := attributes.Get(attributeName) if ok { results := re.FindStringSubmatch(attribute.Str()) if len(results) > 0 { attribute.SetStr(results[0]) } } } } // MatchResourceAttributeValue is a CompareMetricsOption that transforms a resource attribute value based on a regular expression. func MatchResourceAttributeValue(attributeName string, pattern string) CompareMetricsOption { re := regexp.MustCompile(pattern) return compareMetricsOptionFunc(func(expected, actual pmetric.Metrics) { matchResourceAttributeValue(expected, attributeName, re) matchResourceAttributeValue(actual, attributeName, re) }) } func matchResourceAttributeValue(metrics pmetric.Metrics, attributeName string, re *regexp.Regexp) { rms := metrics.ResourceMetrics() for i := 0; i < rms.Len(); i++ { internal.MatchResourceAttributeValue(rms.At(i).Resource(), attributeName, re) } } // IgnoreResourceAttributeValue is a CompareMetricsOption that removes a resource attribute // from all resources. func IgnoreResourceAttributeValue(attributeName string) CompareMetricsOption { return compareMetricsOptionFunc(func(expected, actual pmetric.Metrics) { maskMetricsResourceAttributeValue(expected, attributeName) maskMetricsResourceAttributeValue(actual, attributeName) }) } func maskMetricsResourceAttributeValue(metrics pmetric.Metrics, attributeName string) { rms := metrics.ResourceMetrics() for i := 0; i < rms.Len(); i++ { internal.MaskResourceAttributeValue(rms.At(i).Resource(), attributeName) } } func ChangeResourceAttributeValue(attributeName string, changeFn func(string) string) CompareMetricsOption { return compareMetricsOptionFunc(func(expected, actual pmetric.Metrics) { changeMetricsResourceAttributeValue(expected, attributeName, changeFn) changeMetricsResourceAttributeValue(actual, attributeName, changeFn) }) } func changeMetricsResourceAttributeValue(metrics pmetric.Metrics, attributeName string, changeFn func(string) string) { rms := metrics.ResourceMetrics() for i := 0; i < rms.Len(); i++ { internal.ChangeResourceAttributeValue(rms.At(i).Resource(), attributeName, changeFn) } } // ChangeDatapointAttributeValue changes the metric datapoint value with the specified key func ChangeDatapointAttributeValue(attributeName string, changeFn func(string) string) CompareMetricsOption { return compareMetricsOptionFunc(func(expected, actual pmetric.Metrics) { changeMetricsDatapointAttributeValue(expected, attributeName, changeFn) changeMetricsDatapointAttributeValue(actual, attributeName, changeFn) }) } func changeMetricsDatapointAttributeValue(metrics pmetric.Metrics, attributeName string, changeFn func(string) string) { rms := metrics.ResourceMetrics() for i := 0; i < rms.Len(); i++ { internal.ChangeResourceAttributeValue(rms.At(i).Resource(), attributeName, changeFn) } for i := 0; i < metrics.ResourceMetrics().Len(); i++ { for j := 0; j < metrics.ResourceMetrics().At(i).ScopeMetrics().Len(); j++ { for g := 0; g < metrics.ResourceMetrics().At(i).ScopeMetrics().At(j).Metrics().Len(); g++ { m := metrics.ResourceMetrics().At(i).ScopeMetrics().At(j).Metrics().At(g) switch m.Type() { case pmetric.MetricTypeGauge: datapoints := m.Gauge().DataPoints() for k := 0; k < datapoints.Len(); k++ { if v, ok := datapoints.At(k).Attributes().Get(attributeName); ok { datapoints.At(k).Attributes().PutStr(attributeName, changeFn(v.Str())) } } case pmetric.MetricTypeSum: datapoints := m.Sum().DataPoints() for k := 0; k < datapoints.Len(); k++ { if v, ok := datapoints.At(k).Attributes().Get(attributeName); ok { datapoints.At(k).Attributes().PutStr(attributeName, changeFn(v.Str())) } } case pmetric.MetricTypeHistogram: datapoints := m.Histogram().DataPoints() for k := 0; k < datapoints.Len(); k++ { if v, ok := datapoints.At(k).Attributes().Get(attributeName); ok { datapoints.At(k).Attributes().PutStr(attributeName, changeFn(v.Str())) } } case pmetric.MetricTypeExponentialHistogram: datapoints := m.ExponentialHistogram().DataPoints() for k := 0; k < datapoints.Len(); k++ { if v, ok := datapoints.At(k).Attributes().Get(attributeName); ok { datapoints.At(k).Attributes().PutStr(attributeName, changeFn(v.Str())) } } } } } } } // IgnoreSubsequentDataPoints is a CompareMetricsOption that ignores data points after the first. func IgnoreSubsequentDataPoints(metricNames ...string) CompareMetricsOption { return compareMetricsOptionFunc(func(expected, actual pmetric.Metrics) { maskSubsequentDataPoints(expected, metricNames) maskSubsequentDataPoints(actual, metricNames) }) } func maskSubsequentDataPoints(metrics pmetric.Metrics, metricNames []string) { metricNameSet := make(map[string]bool, len(metricNames)) for _, metricName := range metricNames { metricNameSet[metricName] = true } rms := metrics.ResourceMetrics() for i := 0; i < rms.Len(); i++ { sms := rms.At(i).ScopeMetrics() for j := 0; j < sms.Len(); j++ { ms := sms.At(j).Metrics() for k := 0; k < ms.Len(); k++ { if len(metricNames) == 0 || metricNameSet[ms.At(k).Name()] { switch ms.At(k).Type() { case pmetric.MetricTypeHistogram: dps := ms.At(k).Histogram().DataPoints() n := 0 dps.RemoveIf(func(pmetric.HistogramDataPoint) bool { n++ return n > 1 }) default: dps := getDataPointSlice(ms.At(k)) n := 0 dps.RemoveIf(func(pmetric.NumberDataPoint) bool { n++ return n > 1 }) } } } } } } // IgnoreResourceMetricsOrder is a CompareMetricsOption that ignores the order of resource traces/metrics/logs. func IgnoreResourceMetricsOrder() CompareMetricsOption { return compareMetricsOptionFunc(func(expected, actual pmetric.Metrics) { sortResourceMetricsSlice(expected.ResourceMetrics()) sortResourceMetricsSlice(actual.ResourceMetrics()) }) } func sortResourceMetricsSlice(rms pmetric.ResourceMetricsSlice) { rms.Sort(func(a, b pmetric.ResourceMetrics) bool { if a.SchemaUrl() != b.SchemaUrl() { return a.SchemaUrl() < b.SchemaUrl() } aAttrs := pdatautil.MapHash(a.Resource().Attributes()) bAttrs := pdatautil.MapHash(b.Resource().Attributes()) return bytes.Compare(aAttrs[:], bAttrs[:]) < 0 }) } func IgnoreScopeVersion() CompareMetricsOption { return compareMetricsOptionFunc(func(expected, actual pmetric.Metrics) { maskScopeVersion(expected) maskScopeVersion(actual) }) } func maskScopeVersion(metrics pmetric.Metrics) { rms := metrics.ResourceMetrics() for i := 0; i < rms.Len(); i++ { rm := rms.At(i) for j := 0; j < rm.ScopeMetrics().Len(); j++ { sm := rm.ScopeMetrics().At(j) sm.Scope().SetVersion("") } } } // IgnoreScopeMetricsOrder is a CompareMetricsOption that ignores the order of instrumentation scope traces/metrics/logs. func IgnoreScopeMetricsOrder() CompareMetricsOption { return compareMetricsOptionFunc(func(expected, actual pmetric.Metrics) { sortScopeMetricsSlices(expected) sortScopeMetricsSlices(actual) }) } func sortScopeMetricsSlices(ms pmetric.Metrics) { for i := 0; i < ms.ResourceMetrics().Len(); i++ { ms.ResourceMetrics().At(i).ScopeMetrics().Sort(func(a, b pmetric.ScopeMetrics) bool { if a.SchemaUrl() != b.SchemaUrl() { return a.SchemaUrl() < b.SchemaUrl() } if a.Scope().Name() != b.Scope().Name() { return a.Scope().Name() < b.Scope().Name() } return a.Scope().Version() < b.Scope().Version() }) } } // IgnoreMetricsOrder is a CompareMetricsOption that ignores the order of metrics. func IgnoreMetricsOrder() CompareMetricsOption { return compareMetricsOptionFunc(func(expected, actual pmetric.Metrics) { sortMetricSlices(expected) sortMetricSlices(actual) }) } func sortMetricSlices(ms pmetric.Metrics) { for i := 0; i < ms.ResourceMetrics().Len(); i++ { for j := 0; j < ms.ResourceMetrics().At(i).ScopeMetrics().Len(); j++ { ms.ResourceMetrics().At(i).ScopeMetrics().At(j).Metrics().Sort(func(a, b pmetric.Metric) bool { return a.Name() < b.Name() }) } } } // IgnoreMetricDataPointsOrder is a CompareMetricsOption that ignores the order of metrics. func IgnoreMetricDataPointsOrder() CompareMetricsOption { return compareMetricsOptionFunc(func(expected, actual pmetric.Metrics) { sortMetricDataPointSlices(expected) sortMetricDataPointSlices(actual) }) } func sortMetricDataPointSlices(ms pmetric.Metrics) { for i := 0; i < ms.ResourceMetrics().Len(); i++ { for j := 0; j < ms.ResourceMetrics().At(i).ScopeMetrics().Len(); j++ { for k := 0; k < ms.ResourceMetrics().At(i).ScopeMetrics().At(j).Metrics().Len(); k++ { m := ms.ResourceMetrics().At(i).ScopeMetrics().At(j).Metrics().At(k) //exhaustive:enforce switch m.Type() { case pmetric.MetricTypeGauge: sortNumberDataPointSlice(m.Gauge().DataPoints()) case pmetric.MetricTypeSum: sortNumberDataPointSlice(m.Sum().DataPoints()) case pmetric.MetricTypeHistogram: sortHistogramDataPointSlice(m.Histogram().DataPoints()) case pmetric.MetricTypeExponentialHistogram: sortExponentialHistogramDataPointSlice(m.ExponentialHistogram().DataPoints()) case pmetric.MetricTypeSummary: sortSummaryDataPointSlice(m.Summary().DataPoints()) case pmetric.MetricTypeEmpty: } } } } } func sortNumberDataPointSlice(ndps pmetric.NumberDataPointSlice) { ndps.Sort(func(a, b pmetric.NumberDataPoint) bool { aAttrs := pdatautil.MapHash(a.Attributes()) bAttrs := pdatautil.MapHash(b.Attributes()) return bytes.Compare(aAttrs[:], bAttrs[:]) < 0 }) } func sortHistogramDataPointSlice(hdps pmetric.HistogramDataPointSlice) { hdps.Sort(func(a, b pmetric.HistogramDataPoint) bool { aAttrs := pdatautil.MapHash(a.Attributes()) bAttrs := pdatautil.MapHash(b.Attributes()) return bytes.Compare(aAttrs[:], bAttrs[:]) < 0 }) } func sortExponentialHistogramDataPointSlice(hdps pmetric.ExponentialHistogramDataPointSlice) { hdps.Sort(func(a, b pmetric.ExponentialHistogramDataPoint) bool { aAttrs := pdatautil.MapHash(a.Attributes()) bAttrs := pdatautil.MapHash(b.Attributes()) return bytes.Compare(aAttrs[:], bAttrs[:]) < 0 }) } func sortSummaryDataPointSlice(sds pmetric.SummaryDataPointSlice) { sds.Sort(func(a, b pmetric.SummaryDataPoint) bool { aAttrs := pdatautil.MapHash(a.Attributes()) bAttrs := pdatautil.MapHash(b.Attributes()) return bytes.Compare(aAttrs[:], bAttrs[:]) < 0 }) } // IgnoreSummaryDataPointValueAtQuantileSliceOrder is a CompareMetricsOption that ignores the order of summary data point quantile slice. func IgnoreSummaryDataPointValueAtQuantileSliceOrder() CompareMetricsOption { return compareMetricsOptionFunc(func(expected, actual pmetric.Metrics) { sortSummaryDataPointValueAtQuantileSlices(expected) sortSummaryDataPointValueAtQuantileSlices(actual) }) } func sortSummaryDataPointValueAtQuantileSlices(ms pmetric.Metrics) { for i := 0; i < ms.ResourceMetrics().Len(); i++ { for j := 0; j < ms.ResourceMetrics().At(i).ScopeMetrics().Len(); j++ { for k := 0; k < ms.ResourceMetrics().At(i).ScopeMetrics().At(j).Metrics().Len(); k++ { m := ms.ResourceMetrics().At(i).ScopeMetrics().At(j).Metrics().At(k) if m.Type() == pmetric.MetricTypeSummary { for l := 0; l < m.Summary().DataPoints().Len(); l++ { m.Summary().DataPoints().At(l).QuantileValues().Sort(func(a, b pmetric.SummaryDataPointValueAtQuantile) bool { return a.Quantile() < b.Quantile() }) } } } } } }