You've already forked opentelemetry-go
mirror of
https://github.com/open-telemetry/opentelemetry-go.git
synced 2025-11-25 22:41:46 +02:00
Add Exemplar to metricdata package (#3849)
* Add Exemplar to metricdata pkg * Update histogram Aggregator * Update opencensus bridge * Update prometheus exporter * Update OTLP exporter * Update stdoutmetric exporter * Add changes to changelog * Update fail tests * Add tests for IgnoreExemplars * Fix merge
This commit is contained in:
@@ -11,6 +11,8 @@ This project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.htm
|
||||
### Added
|
||||
|
||||
- The `WithoutTimestamps` option to `go.opentelemetry.io/otel/exporters/stdout/stdoutmetric` to sets all timestamps to zero. (#3828)
|
||||
- The new `Exemplar` type is added to `go.opentelemetry.io/otel/sdk/metric/metricdata`.
|
||||
Both the `DataPoint` and `HistogramDataPoint` types from that package have a new field of `Exemplars` containing the sampled exemplars for their timeseries. (#3849)
|
||||
|
||||
### Changed
|
||||
|
||||
@@ -18,6 +20,7 @@ This project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.htm
|
||||
- Optimize memory allocation when creation new metric instruments in `go.opentelemetry.io/otel/sdk/metric`. (#3832)
|
||||
- Avoid creating new objects on all calls to `WithDeferredSetup` and `SkipContextSetup` in OpenTracing bridge. (#3833)
|
||||
- The `New` and `Detect` functions from `go.opentelemetry.io/otel/sdk/resource` return errors that wrap underlying errors instead of just containing the underlying error strings. (#3844)
|
||||
- Both the `Histogram` and `HistogramDataPoint` are redefined with a generic argument of `[N int64 | float64]` in `go.opentelemetry.io/otel/sdk/metric/metricdata`. (#3849)
|
||||
|
||||
### Removed
|
||||
|
||||
|
||||
@@ -127,8 +127,8 @@ func convertNumberDataPoints[N int64 | float64](labelKeys []ocmetricdata.LabelKe
|
||||
|
||||
// convertHistogram converts OpenCensus Distribution timeseries to an
|
||||
// OpenTelemetry Histogram aggregation.
|
||||
func convertHistogram(labelKeys []ocmetricdata.LabelKey, ts []*ocmetricdata.TimeSeries) (metricdata.Histogram, error) {
|
||||
points := make([]metricdata.HistogramDataPoint, 0, len(ts))
|
||||
func convertHistogram(labelKeys []ocmetricdata.LabelKey, ts []*ocmetricdata.TimeSeries) (metricdata.Histogram[float64], error) {
|
||||
points := make([]metricdata.HistogramDataPoint[float64], 0, len(ts))
|
||||
var errInfo []string
|
||||
for _, t := range ts {
|
||||
attrs, err := convertAttrs(labelKeys, t.LabelValues)
|
||||
@@ -152,7 +152,7 @@ func convertHistogram(labelKeys []ocmetricdata.LabelKey, ts []*ocmetricdata.Time
|
||||
continue
|
||||
}
|
||||
// TODO: handle exemplars
|
||||
points = append(points, metricdata.HistogramDataPoint{
|
||||
points = append(points, metricdata.HistogramDataPoint[float64]{
|
||||
Attributes: attrs,
|
||||
StartTime: t.StartTime,
|
||||
Time: p.Time,
|
||||
@@ -167,7 +167,7 @@ func convertHistogram(labelKeys []ocmetricdata.LabelKey, ts []*ocmetricdata.Time
|
||||
if len(errInfo) > 0 {
|
||||
aggregatedError = fmt.Errorf("%w: %v", errHistogramDataPoint, errInfo)
|
||||
}
|
||||
return metricdata.Histogram{DataPoints: points, Temporality: metricdata.CumulativeTemporality}, aggregatedError
|
||||
return metricdata.Histogram[float64]{DataPoints: points, Temporality: metricdata.CumulativeTemporality}, aggregatedError
|
||||
}
|
||||
|
||||
// convertBucketCounts converts from OpenCensus bucket counts to slice of uint64.
|
||||
|
||||
@@ -214,8 +214,8 @@ func TestConvertMetrics(t *testing.T) {
|
||||
Name: "foo.com/histogram-a",
|
||||
Description: "a testing histogram",
|
||||
Unit: "1",
|
||||
Data: metricdata.Histogram{
|
||||
DataPoints: []metricdata.HistogramDataPoint{
|
||||
Data: metricdata.Histogram[float64]{
|
||||
DataPoints: []metricdata.HistogramDataPoint[float64]{
|
||||
{
|
||||
Attributes: attribute.NewSet(attribute.KeyValue{
|
||||
Key: attribute.Key("a"),
|
||||
@@ -387,9 +387,9 @@ func TestConvertMetrics(t *testing.T) {
|
||||
Name: "foo.com/histogram-a",
|
||||
Description: "a testing histogram",
|
||||
Unit: "1",
|
||||
Data: metricdata.Histogram{
|
||||
Data: metricdata.Histogram[float64]{
|
||||
Temporality: metricdata.CumulativeTemporality,
|
||||
DataPoints: []metricdata.HistogramDataPoint{},
|
||||
DataPoints: []metricdata.HistogramDataPoint[float64]{},
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
@@ -95,7 +95,9 @@ func metric(m metricdata.Metrics) (*mpb.Metric, error) {
|
||||
out.Data, err = Sum[int64](a)
|
||||
case metricdata.Sum[float64]:
|
||||
out.Data, err = Sum[float64](a)
|
||||
case metricdata.Histogram:
|
||||
case metricdata.Histogram[int64]:
|
||||
out.Data, err = Histogram(a)
|
||||
case metricdata.Histogram[float64]:
|
||||
out.Data, err = Histogram(a)
|
||||
default:
|
||||
return out, fmt.Errorf("%w: %T", errUnknownAggregation, a)
|
||||
@@ -155,7 +157,7 @@ func DataPoints[N int64 | float64](dPts []metricdata.DataPoint[N]) []*mpb.Number
|
||||
// Histogram returns an OTLP Metric_Histogram generated from h. An error is
|
||||
// returned with a partial Metric_Histogram if the temporality of h is
|
||||
// unknown.
|
||||
func Histogram(h metricdata.Histogram) (*mpb.Metric_Histogram, error) {
|
||||
func Histogram[N int64 | float64](h metricdata.Histogram[N]) (*mpb.Metric_Histogram, error) {
|
||||
t, err := Temporality(h.Temporality)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@@ -170,7 +172,7 @@ func Histogram(h metricdata.Histogram) (*mpb.Metric_Histogram, error) {
|
||||
|
||||
// HistogramDataPoints returns a slice of OTLP HistogramDataPoint generated
|
||||
// from dPts.
|
||||
func HistogramDataPoints(dPts []metricdata.HistogramDataPoint) []*mpb.HistogramDataPoint {
|
||||
func HistogramDataPoints[N int64 | float64](dPts []metricdata.HistogramDataPoint[N]) []*mpb.HistogramDataPoint {
|
||||
out := make([]*mpb.HistogramDataPoint, 0, len(dPts))
|
||||
for _, dPt := range dPts {
|
||||
sum := dPt.Sum
|
||||
|
||||
@@ -52,7 +52,28 @@ var (
|
||||
|
||||
minA, maxA, sumA = 2.0, 4.0, 90.0
|
||||
minB, maxB, sumB = 4.0, 150.0, 234.0
|
||||
otelHDP = []metricdata.HistogramDataPoint{{
|
||||
otelHDPInt64 = []metricdata.HistogramDataPoint[int64]{{
|
||||
Attributes: alice,
|
||||
StartTime: start,
|
||||
Time: end,
|
||||
Count: 30,
|
||||
Bounds: []float64{1, 5},
|
||||
BucketCounts: []uint64{0, 30, 0},
|
||||
Min: metricdata.NewExtrema(minA),
|
||||
Max: metricdata.NewExtrema(maxA),
|
||||
Sum: sumA,
|
||||
}, {
|
||||
Attributes: bob,
|
||||
StartTime: start,
|
||||
Time: end,
|
||||
Count: 3,
|
||||
Bounds: []float64{1, 5},
|
||||
BucketCounts: []uint64{0, 1, 2},
|
||||
Min: metricdata.NewExtrema(minB),
|
||||
Max: metricdata.NewExtrema(maxB),
|
||||
Sum: sumB,
|
||||
}}
|
||||
otelHDPFloat64 = []metricdata.HistogramDataPoint[float64]{{
|
||||
Attributes: alice,
|
||||
StartTime: start,
|
||||
Time: end,
|
||||
@@ -96,14 +117,18 @@ var (
|
||||
Max: &maxB,
|
||||
}}
|
||||
|
||||
otelHist = metricdata.Histogram{
|
||||
otelHistInt64 = metricdata.Histogram[int64]{
|
||||
Temporality: metricdata.DeltaTemporality,
|
||||
DataPoints: otelHDP,
|
||||
DataPoints: otelHDPInt64,
|
||||
}
|
||||
otelHistFloat64 = metricdata.Histogram[float64]{
|
||||
Temporality: metricdata.DeltaTemporality,
|
||||
DataPoints: otelHDPFloat64,
|
||||
}
|
||||
invalidTemporality metricdata.Temporality
|
||||
otelHistInvalid = metricdata.Histogram{
|
||||
otelHistInvalid = metricdata.Histogram[int64]{
|
||||
Temporality: invalidTemporality,
|
||||
DataPoints: otelHDP,
|
||||
DataPoints: otelHDPInt64,
|
||||
}
|
||||
|
||||
pbHist = &mpb.Histogram{
|
||||
@@ -215,10 +240,16 @@ var (
|
||||
Data: otelSumInvalid,
|
||||
},
|
||||
{
|
||||
Name: "histogram",
|
||||
Name: "int64-histogram",
|
||||
Description: "Histogram",
|
||||
Unit: "1",
|
||||
Data: otelHist,
|
||||
Data: otelHistInt64,
|
||||
},
|
||||
{
|
||||
Name: "float64-histogram",
|
||||
Description: "Histogram",
|
||||
Unit: "1",
|
||||
Data: otelHistFloat64,
|
||||
},
|
||||
{
|
||||
Name: "invalid-histogram",
|
||||
@@ -260,7 +291,13 @@ var (
|
||||
Data: &mpb.Metric_Sum{Sum: pbSumFloat64},
|
||||
},
|
||||
{
|
||||
Name: "histogram",
|
||||
Name: "int64-histogram",
|
||||
Description: "Histogram",
|
||||
Unit: "1",
|
||||
Data: &mpb.Metric_Histogram{Histogram: pbHist},
|
||||
},
|
||||
{
|
||||
Name: "float64-histogram",
|
||||
Description: "Histogram",
|
||||
Unit: "1",
|
||||
Data: &mpb.Metric_Histogram{Histogram: pbHist},
|
||||
@@ -327,12 +364,16 @@ func TestTransformations(t *testing.T) {
|
||||
// errors deep inside the structs).
|
||||
|
||||
// DataPoint types.
|
||||
assert.Equal(t, pbHDP, HistogramDataPoints(otelHDP))
|
||||
assert.Equal(t, pbHDP, HistogramDataPoints(otelHDPInt64))
|
||||
assert.Equal(t, pbHDP, HistogramDataPoints(otelHDPFloat64))
|
||||
assert.Equal(t, pbDPtsInt64, DataPoints[int64](otelDPtsInt64))
|
||||
require.Equal(t, pbDPtsFloat64, DataPoints[float64](otelDPtsFloat64))
|
||||
|
||||
// Aggregations.
|
||||
h, err := Histogram(otelHist)
|
||||
h, err := Histogram(otelHistInt64)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, &mpb.Metric_Histogram{Histogram: pbHist}, h)
|
||||
h, err = Histogram(otelHistFloat64)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, &mpb.Metric_Histogram{Histogram: pbHist}, h)
|
||||
h, err = Histogram(otelHistInvalid)
|
||||
|
||||
@@ -155,7 +155,9 @@ func (c *collector) Collect(ch chan<- prometheus.Metric) {
|
||||
|
||||
for _, m := range scopeMetrics.Metrics {
|
||||
switch v := m.Data.(type) {
|
||||
case metricdata.Histogram:
|
||||
case metricdata.Histogram[int64]:
|
||||
addHistogramMetric(ch, v, m, keys, values, c.getName(m), c.metricFamilies)
|
||||
case metricdata.Histogram[float64]:
|
||||
addHistogramMetric(ch, v, m, keys, values, c.getName(m), c.metricFamilies)
|
||||
case metricdata.Sum[int64]:
|
||||
addSumMetric(ch, v, m, keys, values, c.getName(m), c.metricFamilies)
|
||||
@@ -170,7 +172,7 @@ func (c *collector) Collect(ch chan<- prometheus.Metric) {
|
||||
}
|
||||
}
|
||||
|
||||
func addHistogramMetric(ch chan<- prometheus.Metric, histogram metricdata.Histogram, m metricdata.Metrics, ks, vs [2]string, name string, mfs map[string]*dto.MetricFamily) {
|
||||
func addHistogramMetric[N int64 | float64](ch chan<- prometheus.Metric, histogram metricdata.Histogram[N], m metricdata.Metrics, ks, vs [2]string, name string, mfs map[string]*dto.MetricFamily) {
|
||||
// TODO(https://github.com/open-telemetry/opentelemetry-go/issues/3163): support exemplars
|
||||
drop, help := validateMetrics(name, m.Description, dto.MetricType_HISTOGRAM.Enum(), mfs)
|
||||
if drop {
|
||||
|
||||
@@ -81,9 +81,9 @@ var (
|
||||
Name: "latency",
|
||||
Description: "Time spend processing received requests",
|
||||
Unit: "ms",
|
||||
Data: metricdata.Histogram{
|
||||
Data: metricdata.Histogram[float64]{
|
||||
Temporality: metricdata.DeltaTemporality,
|
||||
DataPoints: []metricdata.HistogramDataPoint{
|
||||
DataPoints: []metricdata.HistogramDataPoint[float64]{
|
||||
{
|
||||
Attributes: attribute.NewSet(attribute.String("server", "central")),
|
||||
StartTime: now,
|
||||
|
||||
@@ -138,8 +138,13 @@ func redactAggregationTimestamps(orig metricdata.Aggregation) metricdata.Aggrega
|
||||
return metricdata.Gauge[int64]{
|
||||
DataPoints: redactDataPointTimestamps(a.DataPoints),
|
||||
}
|
||||
case metricdata.Histogram:
|
||||
return metricdata.Histogram{
|
||||
case metricdata.Histogram[int64]:
|
||||
return metricdata.Histogram[int64]{
|
||||
Temporality: a.Temporality,
|
||||
DataPoints: redactHistogramTimestamps(a.DataPoints),
|
||||
}
|
||||
case metricdata.Histogram[float64]:
|
||||
return metricdata.Histogram[float64]{
|
||||
Temporality: a.Temporality,
|
||||
DataPoints: redactHistogramTimestamps(a.DataPoints),
|
||||
}
|
||||
@@ -149,10 +154,10 @@ func redactAggregationTimestamps(orig metricdata.Aggregation) metricdata.Aggrega
|
||||
}
|
||||
}
|
||||
|
||||
func redactHistogramTimestamps(hdp []metricdata.HistogramDataPoint) []metricdata.HistogramDataPoint {
|
||||
out := make([]metricdata.HistogramDataPoint, len(hdp))
|
||||
func redactHistogramTimestamps[T int64 | float64](hdp []metricdata.HistogramDataPoint[T]) []metricdata.HistogramDataPoint[T] {
|
||||
out := make([]metricdata.HistogramDataPoint[T], len(hdp))
|
||||
for i, dp := range hdp {
|
||||
out[i] = metricdata.HistogramDataPoint{
|
||||
out[i] = metricdata.HistogramDataPoint[T]{
|
||||
Attributes: dp.Attributes,
|
||||
Count: dp.Count,
|
||||
Sum: dp.Sum,
|
||||
|
||||
@@ -141,12 +141,12 @@ func (s *deltaHistogram[N]) Aggregation() metricdata.Aggregation {
|
||||
// Do not allow modification of our copy of bounds.
|
||||
bounds := make([]float64, len(s.bounds))
|
||||
copy(bounds, s.bounds)
|
||||
h := metricdata.Histogram{
|
||||
h := metricdata.Histogram[N]{
|
||||
Temporality: metricdata.DeltaTemporality,
|
||||
DataPoints: make([]metricdata.HistogramDataPoint, 0, len(s.values)),
|
||||
DataPoints: make([]metricdata.HistogramDataPoint[N], 0, len(s.values)),
|
||||
}
|
||||
for a, b := range s.values {
|
||||
hdp := metricdata.HistogramDataPoint{
|
||||
hdp := metricdata.HistogramDataPoint[N]{
|
||||
Attributes: a,
|
||||
StartTime: s.start,
|
||||
Time: t,
|
||||
@@ -204,9 +204,9 @@ func (s *cumulativeHistogram[N]) Aggregation() metricdata.Aggregation {
|
||||
// Do not allow modification of our copy of bounds.
|
||||
bounds := make([]float64, len(s.bounds))
|
||||
copy(bounds, s.bounds)
|
||||
h := metricdata.Histogram{
|
||||
h := metricdata.Histogram[N]{
|
||||
Temporality: metricdata.CumulativeTemporality,
|
||||
DataPoints: make([]metricdata.HistogramDataPoint, 0, len(s.values)),
|
||||
DataPoints: make([]metricdata.HistogramDataPoint[N], 0, len(s.values)),
|
||||
}
|
||||
for a, b := range s.values {
|
||||
// The HistogramDataPoint field values returned need to be copies of
|
||||
@@ -217,7 +217,7 @@ func (s *cumulativeHistogram[N]) Aggregation() metricdata.Aggregation {
|
||||
counts := make([]uint64, len(b.counts))
|
||||
copy(counts, b.counts)
|
||||
|
||||
hdp := metricdata.HistogramDataPoint{
|
||||
hdp := metricdata.HistogramDataPoint[N]{
|
||||
Attributes: a,
|
||||
StartTime: s.start,
|
||||
Time: t,
|
||||
|
||||
@@ -49,31 +49,31 @@ func testHistogram[N int64 | float64](t *testing.T) {
|
||||
}
|
||||
|
||||
incr := monoIncr
|
||||
eFunc := deltaHistExpecter(incr)
|
||||
eFunc := deltaHistExpecter[N](incr)
|
||||
t.Run("Delta", tester.Run(NewDeltaHistogram[N](histConf), incr, eFunc))
|
||||
eFunc = cumuHistExpecter(incr)
|
||||
eFunc = cumuHistExpecter[N](incr)
|
||||
t.Run("Cumulative", tester.Run(NewCumulativeHistogram[N](histConf), incr, eFunc))
|
||||
}
|
||||
|
||||
func deltaHistExpecter(incr setMap) expectFunc {
|
||||
h := metricdata.Histogram{Temporality: metricdata.DeltaTemporality}
|
||||
func deltaHistExpecter[N int64 | float64](incr setMap) expectFunc {
|
||||
h := metricdata.Histogram[N]{Temporality: metricdata.DeltaTemporality}
|
||||
return func(m int) metricdata.Aggregation {
|
||||
h.DataPoints = make([]metricdata.HistogramDataPoint, 0, len(incr))
|
||||
h.DataPoints = make([]metricdata.HistogramDataPoint[N], 0, len(incr))
|
||||
for a, v := range incr {
|
||||
h.DataPoints = append(h.DataPoints, hPoint(a, float64(v), uint64(m)))
|
||||
h.DataPoints = append(h.DataPoints, hPoint[N](a, float64(v), uint64(m)))
|
||||
}
|
||||
return h
|
||||
}
|
||||
}
|
||||
|
||||
func cumuHistExpecter(incr setMap) expectFunc {
|
||||
func cumuHistExpecter[N int64 | float64](incr setMap) expectFunc {
|
||||
var cycle int
|
||||
h := metricdata.Histogram{Temporality: metricdata.CumulativeTemporality}
|
||||
h := metricdata.Histogram[N]{Temporality: metricdata.CumulativeTemporality}
|
||||
return func(m int) metricdata.Aggregation {
|
||||
cycle++
|
||||
h.DataPoints = make([]metricdata.HistogramDataPoint, 0, len(incr))
|
||||
h.DataPoints = make([]metricdata.HistogramDataPoint[N], 0, len(incr))
|
||||
for a, v := range incr {
|
||||
h.DataPoints = append(h.DataPoints, hPoint(a, float64(v), uint64(cycle*m)))
|
||||
h.DataPoints = append(h.DataPoints, hPoint[N](a, float64(v), uint64(cycle*m)))
|
||||
}
|
||||
return h
|
||||
}
|
||||
@@ -81,11 +81,11 @@ func cumuHistExpecter(incr setMap) expectFunc {
|
||||
|
||||
// hPoint returns an HistogramDataPoint that started and ended now with multi
|
||||
// number of measurements values v. It includes a min and max (set to v).
|
||||
func hPoint(a attribute.Set, v float64, multi uint64) metricdata.HistogramDataPoint {
|
||||
func hPoint[N int64 | float64](a attribute.Set, v float64, multi uint64) metricdata.HistogramDataPoint[N] {
|
||||
idx := sort.SearchFloat64s(bounds, v)
|
||||
counts := make([]uint64, len(bounds)+1)
|
||||
counts[idx] += multi
|
||||
return metricdata.HistogramDataPoint{
|
||||
return metricdata.HistogramDataPoint[N]{
|
||||
Attributes: a,
|
||||
StartTime: now(),
|
||||
Time: now(),
|
||||
@@ -128,7 +128,7 @@ func testHistImmutableBounds[N int64 | float64](newA func(aggregation.ExplicitBu
|
||||
assert.Equal(t, cpB, getBounds(a), "modifying the bounds argument should not change the bounds")
|
||||
|
||||
a.Aggregate(5, alice)
|
||||
hdp := a.Aggregation().(metricdata.Histogram).DataPoints[0]
|
||||
hdp := a.Aggregation().(metricdata.Histogram[N]).DataPoints[0]
|
||||
hdp.Bounds[1] = 10
|
||||
assert.Equal(t, cpB, getBounds(a), "modifying the Aggregation bounds should not change the bounds")
|
||||
}
|
||||
@@ -155,7 +155,7 @@ func TestHistogramImmutableBounds(t *testing.T) {
|
||||
func TestCumulativeHistogramImutableCounts(t *testing.T) {
|
||||
a := NewCumulativeHistogram[int64](histConf)
|
||||
a.Aggregate(5, alice)
|
||||
hdp := a.Aggregation().(metricdata.Histogram).DataPoints[0]
|
||||
hdp := a.Aggregation().(metricdata.Histogram[int64]).DataPoints[0]
|
||||
|
||||
cumuH := a.(*cumulativeHistogram[int64])
|
||||
require.Equal(t, hdp.BucketCounts, cumuH.values[alice].counts)
|
||||
@@ -173,8 +173,8 @@ func TestDeltaHistogramReset(t *testing.T) {
|
||||
assert.Nil(t, a.Aggregation())
|
||||
|
||||
a.Aggregate(1, alice)
|
||||
expect := metricdata.Histogram{Temporality: metricdata.DeltaTemporality}
|
||||
expect.DataPoints = []metricdata.HistogramDataPoint{hPoint(alice, 1, 1)}
|
||||
expect := metricdata.Histogram[int64]{Temporality: metricdata.DeltaTemporality}
|
||||
expect.DataPoints = []metricdata.HistogramDataPoint[int64]{hPoint[int64](alice, 1, 1)}
|
||||
metricdatatest.AssertAggregationsEqual(t, expect, a.Aggregation())
|
||||
|
||||
// The attr set should be forgotten once Aggregations is called.
|
||||
@@ -183,7 +183,7 @@ func TestDeltaHistogramReset(t *testing.T) {
|
||||
|
||||
// Aggregating another set should not affect the original (alice).
|
||||
a.Aggregate(1, bob)
|
||||
expect.DataPoints = []metricdata.HistogramDataPoint{hPoint(bob, 1, 1)}
|
||||
expect.DataPoints = []metricdata.HistogramDataPoint[int64]{hPoint[int64](bob, 1, 1)}
|
||||
metricdatatest.AssertAggregationsEqual(t, expect, a.Aggregation())
|
||||
}
|
||||
|
||||
|
||||
@@ -382,9 +382,9 @@ func TestMeterCreatesInstruments(t *testing.T) {
|
||||
},
|
||||
want: metricdata.Metrics{
|
||||
Name: "histogram",
|
||||
Data: metricdata.Histogram{
|
||||
Data: metricdata.Histogram[int64]{
|
||||
Temporality: metricdata.CumulativeTemporality,
|
||||
DataPoints: []metricdata.HistogramDataPoint{
|
||||
DataPoints: []metricdata.HistogramDataPoint[int64]{
|
||||
{
|
||||
Attributes: attribute.Set{},
|
||||
Count: 1,
|
||||
@@ -446,9 +446,9 @@ func TestMeterCreatesInstruments(t *testing.T) {
|
||||
},
|
||||
want: metricdata.Metrics{
|
||||
Name: "histogram",
|
||||
Data: metricdata.Histogram{
|
||||
Data: metricdata.Histogram[float64]{
|
||||
Temporality: metricdata.CumulativeTemporality,
|
||||
DataPoints: []metricdata.HistogramDataPoint{
|
||||
DataPoints: []metricdata.HistogramDataPoint[float64]{
|
||||
{
|
||||
Attributes: attribute.Set{},
|
||||
Count: 1,
|
||||
@@ -1124,8 +1124,8 @@ func testAttributeFilter(temporality metricdata.Temporality) func(*testing.T) {
|
||||
},
|
||||
wantMetric: metricdata.Metrics{
|
||||
Name: "sfhistogram",
|
||||
Data: metricdata.Histogram{
|
||||
DataPoints: []metricdata.HistogramDataPoint{
|
||||
Data: metricdata.Histogram[float64]{
|
||||
DataPoints: []metricdata.HistogramDataPoint[float64]{
|
||||
{
|
||||
Attributes: attribute.NewSet(attribute.String("foo", "bar")),
|
||||
Bounds: []float64{0, 5, 10, 25, 50, 75, 100, 250, 500, 750, 1000, 2500, 5000, 7500, 10000},
|
||||
@@ -1206,8 +1206,8 @@ func testAttributeFilter(temporality metricdata.Temporality) func(*testing.T) {
|
||||
},
|
||||
wantMetric: metricdata.Metrics{
|
||||
Name: "sihistogram",
|
||||
Data: metricdata.Histogram{
|
||||
DataPoints: []metricdata.HistogramDataPoint{
|
||||
Data: metricdata.Histogram[int64]{
|
||||
DataPoints: []metricdata.HistogramDataPoint[int64]{
|
||||
{
|
||||
Attributes: attribute.NewSet(attribute.String("foo", "bar")),
|
||||
Bounds: []float64{0, 5, 10, 25, 50, 75, 100, 250, 500, 750, 1000, 2500, 5000, 7500, 10000},
|
||||
|
||||
@@ -59,7 +59,8 @@ type Aggregation interface {
|
||||
|
||||
// Gauge represents a measurement of the current value of an instrument.
|
||||
type Gauge[N int64 | float64] struct {
|
||||
// DataPoints reprents individual aggregated measurements with unique Attributes.
|
||||
// DataPoints are the individual aggregated measurements with unique
|
||||
// Attributes.
|
||||
DataPoints []DataPoint[N]
|
||||
}
|
||||
|
||||
@@ -67,7 +68,8 @@ func (Gauge[N]) privateAggregation() {}
|
||||
|
||||
// Sum represents the sum of all measurements of values from an instrument.
|
||||
type Sum[N int64 | float64] struct {
|
||||
// DataPoints reprents individual aggregated measurements with unique Attributes.
|
||||
// DataPoints are the individual aggregated measurements with unique
|
||||
// Attributes.
|
||||
DataPoints []DataPoint[N]
|
||||
// Temporality describes if the aggregation is reported as the change from the
|
||||
// last report time, or the cumulative changes since a fixed start time.
|
||||
@@ -89,21 +91,25 @@ type DataPoint[N int64 | float64] struct {
|
||||
Time time.Time `json:",omitempty"`
|
||||
// Value is the value of this data point.
|
||||
Value N
|
||||
|
||||
// Exemplars is the sampled Exemplars collected during the timeseries.
|
||||
Exemplars []Exemplar[N] `json:",omitempty"`
|
||||
}
|
||||
|
||||
// Histogram represents the histogram of all measurements of values from an instrument.
|
||||
type Histogram struct {
|
||||
// DataPoints reprents individual aggregated measurements with unique Attributes.
|
||||
DataPoints []HistogramDataPoint
|
||||
type Histogram[N int64 | float64] struct {
|
||||
// DataPoints are the individual aggregated measurements with unique
|
||||
// Attributes.
|
||||
DataPoints []HistogramDataPoint[N]
|
||||
// Temporality describes if the aggregation is reported as the change from the
|
||||
// last report time, or the cumulative changes since a fixed start time.
|
||||
Temporality Temporality
|
||||
}
|
||||
|
||||
func (Histogram) privateAggregation() {}
|
||||
func (Histogram[N]) privateAggregation() {}
|
||||
|
||||
// HistogramDataPoint is a single histogram data point in a timeseries.
|
||||
type HistogramDataPoint struct {
|
||||
type HistogramDataPoint[N int64 | float64] struct {
|
||||
// Attributes is the set of key value pairs that uniquely identify the
|
||||
// timeseries.
|
||||
Attributes attribute.Set
|
||||
@@ -126,6 +132,9 @@ type HistogramDataPoint struct {
|
||||
Max Extrema
|
||||
// Sum is the sum of the values recorded.
|
||||
Sum float64
|
||||
|
||||
// Exemplars is the sampled Exemplars collected during the timeseries.
|
||||
Exemplars []Exemplar[N] `json:",omitempty"`
|
||||
}
|
||||
|
||||
// Extrema is the minimum or maximum value of a dataset.
|
||||
@@ -144,3 +153,22 @@ func NewExtrema(v float64) Extrema {
|
||||
func (e Extrema) Value() (v float64, defined bool) {
|
||||
return e.value, e.valid
|
||||
}
|
||||
|
||||
// Exemplar is a measurement sampled from a timeseries providing a typical
|
||||
// example.
|
||||
type Exemplar[N int64 | float64] struct {
|
||||
// FilteredAttributes are the attributes recorded with the measurement but
|
||||
// filtered out of the timeseries' aggregated data.
|
||||
FilteredAttributes []attribute.KeyValue
|
||||
// Time is the time when the measurement was recorded.
|
||||
Time time.Time
|
||||
// Value is the measured value.
|
||||
Value N
|
||||
// SpanID is the ID of the span that was active during the measurement. If
|
||||
// no span was active or the span was not sampled this will be empty.
|
||||
SpanID []byte `json:",omitempty"`
|
||||
// TraceID is the ID of the trace the active span belonged to during the
|
||||
// measurement. If no span was active or the span was not sampled this will
|
||||
// be empty.
|
||||
TraceID []byte `json:",omitempty"`
|
||||
}
|
||||
|
||||
@@ -30,14 +30,18 @@ type Datatypes interface {
|
||||
metricdata.DataPoint[int64] |
|
||||
metricdata.Gauge[float64] |
|
||||
metricdata.Gauge[int64] |
|
||||
metricdata.Histogram |
|
||||
metricdata.HistogramDataPoint |
|
||||
metricdata.Histogram[float64] |
|
||||
metricdata.Histogram[int64] |
|
||||
metricdata.HistogramDataPoint[float64] |
|
||||
metricdata.HistogramDataPoint[int64] |
|
||||
metricdata.Extrema |
|
||||
metricdata.Metrics |
|
||||
metricdata.ResourceMetrics |
|
||||
metricdata.ScopeMetrics |
|
||||
metricdata.Sum[float64] |
|
||||
metricdata.Sum[int64]
|
||||
metricdata.Sum[int64] |
|
||||
metricdata.Exemplar[float64] |
|
||||
metricdata.Exemplar[int64]
|
||||
|
||||
// Interface types are not allowed in union types, therefore the
|
||||
// Aggregation and Value type from metricdata are not included here.
|
||||
@@ -45,6 +49,15 @@ type Datatypes interface {
|
||||
|
||||
type config struct {
|
||||
ignoreTimestamp bool
|
||||
ignoreExemplars bool
|
||||
}
|
||||
|
||||
func newConfig(opts []Option) config {
|
||||
var cfg config
|
||||
for _, opt := range opts {
|
||||
cfg = opt.apply(cfg)
|
||||
}
|
||||
return cfg
|
||||
}
|
||||
|
||||
// Option allows for fine grain control over how AssertEqual operates.
|
||||
@@ -66,21 +79,30 @@ func IgnoreTimestamp() Option {
|
||||
})
|
||||
}
|
||||
|
||||
// IgnoreExemplars disables checking if Exemplars are different.
|
||||
func IgnoreExemplars() Option {
|
||||
return fnOption(func(cfg config) config {
|
||||
cfg.ignoreExemplars = true
|
||||
return cfg
|
||||
})
|
||||
}
|
||||
|
||||
// AssertEqual asserts that the two concrete data-types from the metricdata
|
||||
// package are equal.
|
||||
func AssertEqual[T Datatypes](t *testing.T, expected, actual T, opts ...Option) bool {
|
||||
t.Helper()
|
||||
|
||||
cfg := config{}
|
||||
for _, opt := range opts {
|
||||
cfg = opt.apply(cfg)
|
||||
}
|
||||
cfg := newConfig(opts)
|
||||
|
||||
// Generic types cannot be type asserted. Use an interface instead.
|
||||
aIface := interface{}(actual)
|
||||
|
||||
var r []string
|
||||
switch e := interface{}(expected).(type) {
|
||||
case metricdata.Exemplar[int64]:
|
||||
r = equalExemplars(e, aIface.(metricdata.Exemplar[int64]), cfg)
|
||||
case metricdata.Exemplar[float64]:
|
||||
r = equalExemplars(e, aIface.(metricdata.Exemplar[float64]), cfg)
|
||||
case metricdata.DataPoint[int64]:
|
||||
r = equalDataPoints(e, aIface.(metricdata.DataPoint[int64]), cfg)
|
||||
case metricdata.DataPoint[float64]:
|
||||
@@ -89,10 +111,14 @@ func AssertEqual[T Datatypes](t *testing.T, expected, actual T, opts ...Option)
|
||||
r = equalGauges(e, aIface.(metricdata.Gauge[int64]), cfg)
|
||||
case metricdata.Gauge[float64]:
|
||||
r = equalGauges(e, aIface.(metricdata.Gauge[float64]), cfg)
|
||||
case metricdata.Histogram:
|
||||
r = equalHistograms(e, aIface.(metricdata.Histogram), cfg)
|
||||
case metricdata.HistogramDataPoint:
|
||||
r = equalHistogramDataPoints(e, aIface.(metricdata.HistogramDataPoint), cfg)
|
||||
case metricdata.Histogram[float64]:
|
||||
r = equalHistograms(e, aIface.(metricdata.Histogram[float64]), cfg)
|
||||
case metricdata.Histogram[int64]:
|
||||
r = equalHistograms(e, aIface.(metricdata.Histogram[int64]), cfg)
|
||||
case metricdata.HistogramDataPoint[float64]:
|
||||
r = equalHistogramDataPoints(e, aIface.(metricdata.HistogramDataPoint[float64]), cfg)
|
||||
case metricdata.HistogramDataPoint[int64]:
|
||||
r = equalHistogramDataPoints(e, aIface.(metricdata.HistogramDataPoint[int64]), cfg)
|
||||
case metricdata.Extrema:
|
||||
r = equalExtrema(e, aIface.(metricdata.Extrema), cfg)
|
||||
case metricdata.Metrics:
|
||||
@@ -122,11 +148,7 @@ func AssertEqual[T Datatypes](t *testing.T, expected, actual T, opts ...Option)
|
||||
func AssertAggregationsEqual(t *testing.T, expected, actual metricdata.Aggregation, opts ...Option) bool {
|
||||
t.Helper()
|
||||
|
||||
cfg := config{}
|
||||
for _, opt := range opts {
|
||||
cfg = opt.apply(cfg)
|
||||
}
|
||||
|
||||
cfg := newConfig(opts)
|
||||
if r := equalAggregations(expected, actual, cfg); len(r) > 0 {
|
||||
t.Error(r)
|
||||
return false
|
||||
@@ -141,6 +163,10 @@ func AssertHasAttributes[T Datatypes](t *testing.T, actual T, attrs ...attribute
|
||||
var reasons []string
|
||||
|
||||
switch e := interface{}(actual).(type) {
|
||||
case metricdata.Exemplar[int64]:
|
||||
reasons = hasAttributesExemplars(e, attrs...)
|
||||
case metricdata.Exemplar[float64]:
|
||||
reasons = hasAttributesExemplars(e, attrs...)
|
||||
case metricdata.DataPoint[int64]:
|
||||
reasons = hasAttributesDataPoints(e, attrs...)
|
||||
case metricdata.DataPoint[float64]:
|
||||
@@ -153,11 +179,15 @@ func AssertHasAttributes[T Datatypes](t *testing.T, actual T, attrs ...attribute
|
||||
reasons = hasAttributesSum(e, attrs...)
|
||||
case metricdata.Sum[float64]:
|
||||
reasons = hasAttributesSum(e, attrs...)
|
||||
case metricdata.HistogramDataPoint:
|
||||
case metricdata.HistogramDataPoint[int64]:
|
||||
reasons = hasAttributesHistogramDataPoints(e, attrs...)
|
||||
case metricdata.HistogramDataPoint[float64]:
|
||||
reasons = hasAttributesHistogramDataPoints(e, attrs...)
|
||||
case metricdata.Extrema:
|
||||
// Nothing to check.
|
||||
case metricdata.Histogram:
|
||||
case metricdata.Histogram[int64]:
|
||||
reasons = hasAttributesHistogram(e, attrs...)
|
||||
case metricdata.Histogram[float64]:
|
||||
reasons = hasAttributesHistogram(e, attrs...)
|
||||
case metricdata.Metrics:
|
||||
reasons = hasAttributesMetrics(e, attrs...)
|
||||
|
||||
@@ -38,15 +38,19 @@ func TestFailAssertEqual(t *testing.T) {
|
||||
t.Run("ResourceMetrics", testFailDatatype(resourceMetricsA, resourceMetricsB))
|
||||
t.Run("ScopeMetrics", testFailDatatype(scopeMetricsA, scopeMetricsB))
|
||||
t.Run("Metrics", testFailDatatype(metricsA, metricsB))
|
||||
t.Run("Histogram", testFailDatatype(histogramA, histogramB))
|
||||
t.Run("HistogramInt64", testFailDatatype(histogramInt64A, histogramInt64B))
|
||||
t.Run("HistogramFloat64", testFailDatatype(histogramFloat64A, histogramFloat64B))
|
||||
t.Run("SumInt64", testFailDatatype(sumInt64A, sumInt64B))
|
||||
t.Run("SumFloat64", testFailDatatype(sumFloat64A, sumFloat64B))
|
||||
t.Run("GaugeInt64", testFailDatatype(gaugeInt64A, gaugeInt64B))
|
||||
t.Run("GaugeFloat64", testFailDatatype(gaugeFloat64A, gaugeFloat64B))
|
||||
t.Run("HistogramDataPoint", testFailDatatype(histogramDataPointA, histogramDataPointB))
|
||||
t.Run("HistogramDataPointInt64", testFailDatatype(histogramDataPointInt64A, histogramDataPointInt64B))
|
||||
t.Run("HistogramDataPointFloat64", testFailDatatype(histogramDataPointFloat64A, histogramDataPointFloat64B))
|
||||
t.Run("DataPointInt64", testFailDatatype(dataPointInt64A, dataPointInt64B))
|
||||
t.Run("DataPointFloat64", testFailDatatype(dataPointFloat64A, dataPointFloat64B))
|
||||
|
||||
t.Run("ExemplarInt64", testFailDatatype(exemplarInt64A, exemplarInt64B))
|
||||
t.Run("ExemplarFloat64", testFailDatatype(exemplarFloat64A, exemplarFloat64B))
|
||||
t.Run("Extrema", testFailDatatype(minA, minB))
|
||||
}
|
||||
|
||||
func TestFailAssertAggregationsEqual(t *testing.T) {
|
||||
@@ -57,20 +61,23 @@ func TestFailAssertAggregationsEqual(t *testing.T) {
|
||||
AssertAggregationsEqual(t, sumFloat64A, sumFloat64B)
|
||||
AssertAggregationsEqual(t, gaugeInt64A, gaugeInt64B)
|
||||
AssertAggregationsEqual(t, gaugeFloat64A, gaugeFloat64B)
|
||||
AssertAggregationsEqual(t, histogramA, histogramB)
|
||||
AssertAggregationsEqual(t, histogramInt64A, histogramInt64B)
|
||||
AssertAggregationsEqual(t, histogramFloat64A, histogramFloat64B)
|
||||
}
|
||||
|
||||
func TestFailAssertAttribute(t *testing.T) {
|
||||
AssertHasAttributes(t, exemplarInt64A, attribute.Bool("A", false))
|
||||
AssertHasAttributes(t, exemplarFloat64A, attribute.Bool("B", true))
|
||||
AssertHasAttributes(t, dataPointInt64A, attribute.Bool("A", false))
|
||||
AssertHasAttributes(t, dataPointFloat64A, attribute.Bool("B", true))
|
||||
AssertHasAttributes(t, gaugeInt64A, attribute.Bool("A", false))
|
||||
AssertHasAttributes(t, gaugeFloat64A, attribute.Bool("B", true))
|
||||
AssertHasAttributes(t, sumInt64A, attribute.Bool("A", false))
|
||||
AssertHasAttributes(t, sumFloat64A, attribute.Bool("B", true))
|
||||
AssertHasAttributes(t, histogramDataPointA, attribute.Bool("A", false))
|
||||
AssertHasAttributes(t, histogramDataPointA, attribute.Bool("B", true))
|
||||
AssertHasAttributes(t, histogramA, attribute.Bool("A", false))
|
||||
AssertHasAttributes(t, histogramA, attribute.Bool("B", true))
|
||||
AssertHasAttributes(t, histogramDataPointInt64A, attribute.Bool("A", false))
|
||||
AssertHasAttributes(t, histogramDataPointFloat64A, attribute.Bool("B", true))
|
||||
AssertHasAttributes(t, histogramInt64A, attribute.Bool("A", false))
|
||||
AssertHasAttributes(t, histogramFloat64A, attribute.Bool("B", true))
|
||||
AssertHasAttributes(t, metricsA, attribute.Bool("A", false))
|
||||
AssertHasAttributes(t, metricsA, attribute.Bool("B", true))
|
||||
AssertHasAttributes(t, resourceMetricsA, attribute.Bool("A", false))
|
||||
|
||||
@@ -30,53 +30,110 @@ var (
|
||||
attrA = attribute.NewSet(attribute.Bool("A", true))
|
||||
attrB = attribute.NewSet(attribute.Bool("B", true))
|
||||
|
||||
fltrAttrA = []attribute.KeyValue{attribute.Bool("filter A", true)}
|
||||
fltrAttrB = []attribute.KeyValue{attribute.Bool("filter B", true)}
|
||||
|
||||
startA = time.Now()
|
||||
startB = startA.Add(time.Millisecond)
|
||||
endA = startA.Add(time.Second)
|
||||
endB = startB.Add(time.Second)
|
||||
|
||||
spanIDA = []byte{0, 0, 0, 0, 0, 0, 0, 1}
|
||||
spanIDB = []byte{0, 0, 0, 0, 0, 0, 0, 2}
|
||||
traceIDA = []byte{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1}
|
||||
traceIDB = []byte{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2}
|
||||
|
||||
exemplarInt64A = metricdata.Exemplar[int64]{
|
||||
FilteredAttributes: fltrAttrA,
|
||||
Time: endA,
|
||||
Value: -10,
|
||||
SpanID: spanIDA,
|
||||
TraceID: traceIDA,
|
||||
}
|
||||
exemplarFloat64A = metricdata.Exemplar[float64]{
|
||||
FilteredAttributes: fltrAttrA,
|
||||
Time: endA,
|
||||
Value: -10.0,
|
||||
SpanID: spanIDA,
|
||||
TraceID: traceIDA,
|
||||
}
|
||||
exemplarInt64B = metricdata.Exemplar[int64]{
|
||||
FilteredAttributes: fltrAttrB,
|
||||
Time: endB,
|
||||
Value: 12,
|
||||
SpanID: spanIDB,
|
||||
TraceID: traceIDB,
|
||||
}
|
||||
exemplarFloat64B = metricdata.Exemplar[float64]{
|
||||
FilteredAttributes: fltrAttrB,
|
||||
Time: endB,
|
||||
Value: 12.0,
|
||||
SpanID: spanIDB,
|
||||
TraceID: traceIDB,
|
||||
}
|
||||
exemplarInt64C = metricdata.Exemplar[int64]{
|
||||
FilteredAttributes: fltrAttrA,
|
||||
Time: endB,
|
||||
Value: -10,
|
||||
SpanID: spanIDA,
|
||||
TraceID: traceIDA,
|
||||
}
|
||||
exemplarFloat64C = metricdata.Exemplar[float64]{
|
||||
FilteredAttributes: fltrAttrA,
|
||||
Time: endB,
|
||||
Value: -10.0,
|
||||
SpanID: spanIDA,
|
||||
TraceID: traceIDA,
|
||||
}
|
||||
|
||||
dataPointInt64A = metricdata.DataPoint[int64]{
|
||||
Attributes: attrA,
|
||||
StartTime: startA,
|
||||
Time: endA,
|
||||
Value: -1,
|
||||
Exemplars: []metricdata.Exemplar[int64]{exemplarInt64A},
|
||||
}
|
||||
dataPointFloat64A = metricdata.DataPoint[float64]{
|
||||
Attributes: attrA,
|
||||
StartTime: startA,
|
||||
Time: endA,
|
||||
Value: -1.0,
|
||||
Exemplars: []metricdata.Exemplar[float64]{exemplarFloat64A},
|
||||
}
|
||||
dataPointInt64B = metricdata.DataPoint[int64]{
|
||||
Attributes: attrB,
|
||||
StartTime: startB,
|
||||
Time: endB,
|
||||
Value: 2,
|
||||
Exemplars: []metricdata.Exemplar[int64]{exemplarInt64B},
|
||||
}
|
||||
dataPointFloat64B = metricdata.DataPoint[float64]{
|
||||
Attributes: attrB,
|
||||
StartTime: startB,
|
||||
Time: endB,
|
||||
Value: 2.0,
|
||||
Exemplars: []metricdata.Exemplar[float64]{exemplarFloat64B},
|
||||
}
|
||||
dataPointInt64C = metricdata.DataPoint[int64]{
|
||||
Attributes: attrA,
|
||||
StartTime: startB,
|
||||
Time: endB,
|
||||
Value: -1,
|
||||
Exemplars: []metricdata.Exemplar[int64]{exemplarInt64C},
|
||||
}
|
||||
dataPointFloat64C = metricdata.DataPoint[float64]{
|
||||
Attributes: attrA,
|
||||
StartTime: startB,
|
||||
Time: endB,
|
||||
Value: -1.0,
|
||||
Exemplars: []metricdata.Exemplar[float64]{exemplarFloat64C},
|
||||
}
|
||||
|
||||
minA = metricdata.NewExtrema(-1.)
|
||||
minB, maxB = metricdata.NewExtrema(3.), metricdata.NewExtrema(99.)
|
||||
minC = metricdata.NewExtrema(-1.)
|
||||
|
||||
histogramDataPointA = metricdata.HistogramDataPoint{
|
||||
histogramDataPointInt64A = metricdata.HistogramDataPoint[int64]{
|
||||
Attributes: attrA,
|
||||
StartTime: startA,
|
||||
Time: endA,
|
||||
@@ -85,8 +142,20 @@ var (
|
||||
BucketCounts: []uint64{1, 1},
|
||||
Min: minA,
|
||||
Sum: 2,
|
||||
Exemplars: []metricdata.Exemplar[int64]{exemplarInt64A},
|
||||
}
|
||||
histogramDataPointB = metricdata.HistogramDataPoint{
|
||||
histogramDataPointFloat64A = metricdata.HistogramDataPoint[float64]{
|
||||
Attributes: attrA,
|
||||
StartTime: startA,
|
||||
Time: endA,
|
||||
Count: 2,
|
||||
Bounds: []float64{0, 10},
|
||||
BucketCounts: []uint64{1, 1},
|
||||
Min: minA,
|
||||
Sum: 2,
|
||||
Exemplars: []metricdata.Exemplar[float64]{exemplarFloat64A},
|
||||
}
|
||||
histogramDataPointInt64B = metricdata.HistogramDataPoint[int64]{
|
||||
Attributes: attrB,
|
||||
StartTime: startB,
|
||||
Time: endB,
|
||||
@@ -96,8 +165,21 @@ var (
|
||||
Max: maxB,
|
||||
Min: minB,
|
||||
Sum: 3,
|
||||
Exemplars: []metricdata.Exemplar[int64]{exemplarInt64B},
|
||||
}
|
||||
histogramDataPointC = metricdata.HistogramDataPoint{
|
||||
histogramDataPointFloat64B = metricdata.HistogramDataPoint[float64]{
|
||||
Attributes: attrB,
|
||||
StartTime: startB,
|
||||
Time: endB,
|
||||
Count: 3,
|
||||
Bounds: []float64{0, 10, 100},
|
||||
BucketCounts: []uint64{1, 1, 1},
|
||||
Max: maxB,
|
||||
Min: minB,
|
||||
Sum: 3,
|
||||
Exemplars: []metricdata.Exemplar[float64]{exemplarFloat64B},
|
||||
}
|
||||
histogramDataPointInt64C = metricdata.HistogramDataPoint[int64]{
|
||||
Attributes: attrA,
|
||||
StartTime: startB,
|
||||
Time: endB,
|
||||
@@ -106,6 +188,18 @@ var (
|
||||
BucketCounts: []uint64{1, 1},
|
||||
Min: minC,
|
||||
Sum: 2,
|
||||
Exemplars: []metricdata.Exemplar[int64]{exemplarInt64C},
|
||||
}
|
||||
histogramDataPointFloat64C = metricdata.HistogramDataPoint[float64]{
|
||||
Attributes: attrA,
|
||||
StartTime: startB,
|
||||
Time: endB,
|
||||
Count: 2,
|
||||
Bounds: []float64{0, 10},
|
||||
BucketCounts: []uint64{1, 1},
|
||||
Min: minC,
|
||||
Sum: 2,
|
||||
Exemplars: []metricdata.Exemplar[float64]{exemplarFloat64C},
|
||||
}
|
||||
|
||||
gaugeInt64A = metricdata.Gauge[int64]{
|
||||
@@ -158,17 +252,29 @@ var (
|
||||
DataPoints: []metricdata.DataPoint[float64]{dataPointFloat64C},
|
||||
}
|
||||
|
||||
histogramA = metricdata.Histogram{
|
||||
histogramInt64A = metricdata.Histogram[int64]{
|
||||
Temporality: metricdata.CumulativeTemporality,
|
||||
DataPoints: []metricdata.HistogramDataPoint{histogramDataPointA},
|
||||
DataPoints: []metricdata.HistogramDataPoint[int64]{histogramDataPointInt64A},
|
||||
}
|
||||
histogramB = metricdata.Histogram{
|
||||
histogramFloat64A = metricdata.Histogram[float64]{
|
||||
Temporality: metricdata.CumulativeTemporality,
|
||||
DataPoints: []metricdata.HistogramDataPoint[float64]{histogramDataPointFloat64A},
|
||||
}
|
||||
histogramInt64B = metricdata.Histogram[int64]{
|
||||
Temporality: metricdata.DeltaTemporality,
|
||||
DataPoints: []metricdata.HistogramDataPoint{histogramDataPointB},
|
||||
DataPoints: []metricdata.HistogramDataPoint[int64]{histogramDataPointInt64B},
|
||||
}
|
||||
histogramC = metricdata.Histogram{
|
||||
histogramFloat64B = metricdata.Histogram[float64]{
|
||||
Temporality: metricdata.DeltaTemporality,
|
||||
DataPoints: []metricdata.HistogramDataPoint[float64]{histogramDataPointFloat64B},
|
||||
}
|
||||
histogramInt64C = metricdata.Histogram[int64]{
|
||||
Temporality: metricdata.CumulativeTemporality,
|
||||
DataPoints: []metricdata.HistogramDataPoint{histogramDataPointC},
|
||||
DataPoints: []metricdata.HistogramDataPoint[int64]{histogramDataPointInt64C},
|
||||
}
|
||||
histogramFloat64C = metricdata.Histogram[float64]{
|
||||
Temporality: metricdata.CumulativeTemporality,
|
||||
DataPoints: []metricdata.HistogramDataPoint[float64]{histogramDataPointFloat64C},
|
||||
}
|
||||
|
||||
metricsA = metricdata.Metrics{
|
||||
@@ -224,7 +330,7 @@ func testDatatype[T Datatypes](a, b T, f equalFunc[T]) func(*testing.T) {
|
||||
AssertEqual(t, a, a)
|
||||
AssertEqual(t, b, b)
|
||||
|
||||
r := f(a, b, config{})
|
||||
r := f(a, b, newConfig(nil))
|
||||
assert.Greaterf(t, len(r), 0, "%v == %v", a, b)
|
||||
}
|
||||
}
|
||||
@@ -234,8 +340,20 @@ func testDatatypeIgnoreTime[T Datatypes](a, b T, f equalFunc[T]) func(*testing.T
|
||||
AssertEqual(t, a, a)
|
||||
AssertEqual(t, b, b)
|
||||
|
||||
r := f(a, b, config{ignoreTimestamp: true})
|
||||
assert.Equalf(t, len(r), 0, "%v == %v", a, b)
|
||||
c := newConfig([]Option{IgnoreTimestamp()})
|
||||
r := f(a, b, c)
|
||||
assert.Len(t, r, 0, "unexpected inequality")
|
||||
}
|
||||
}
|
||||
|
||||
func testDatatypeIgnoreExemplars[T Datatypes](a, b T, f equalFunc[T]) func(*testing.T) {
|
||||
return func(t *testing.T) {
|
||||
AssertEqual(t, a, a)
|
||||
AssertEqual(t, b, b)
|
||||
|
||||
c := newConfig([]Option{IgnoreExemplars()})
|
||||
r := f(a, b, c)
|
||||
assert.Len(t, r, 0, "unexpected inequality")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -243,30 +361,56 @@ func TestAssertEqual(t *testing.T) {
|
||||
t.Run("ResourceMetrics", testDatatype(resourceMetricsA, resourceMetricsB, equalResourceMetrics))
|
||||
t.Run("ScopeMetrics", testDatatype(scopeMetricsA, scopeMetricsB, equalScopeMetrics))
|
||||
t.Run("Metrics", testDatatype(metricsA, metricsB, equalMetrics))
|
||||
t.Run("Histogram", testDatatype(histogramA, histogramB, equalHistograms))
|
||||
t.Run("HistogramInt64", testDatatype(histogramInt64A, histogramInt64B, equalHistograms[int64]))
|
||||
t.Run("HistogramFloat64", testDatatype(histogramFloat64A, histogramFloat64B, equalHistograms[float64]))
|
||||
t.Run("SumInt64", testDatatype(sumInt64A, sumInt64B, equalSums[int64]))
|
||||
t.Run("SumFloat64", testDatatype(sumFloat64A, sumFloat64B, equalSums[float64]))
|
||||
t.Run("GaugeInt64", testDatatype(gaugeInt64A, gaugeInt64B, equalGauges[int64]))
|
||||
t.Run("GaugeFloat64", testDatatype(gaugeFloat64A, gaugeFloat64B, equalGauges[float64]))
|
||||
t.Run("HistogramDataPoint", testDatatype(histogramDataPointA, histogramDataPointB, equalHistogramDataPoints))
|
||||
t.Run("HistogramDataPointInt64", testDatatype(histogramDataPointInt64A, histogramDataPointInt64B, equalHistogramDataPoints[int64]))
|
||||
t.Run("HistogramDataPointFloat64", testDatatype(histogramDataPointFloat64A, histogramDataPointFloat64B, equalHistogramDataPoints[float64]))
|
||||
t.Run("DataPointInt64", testDatatype(dataPointInt64A, dataPointInt64B, equalDataPoints[int64]))
|
||||
t.Run("DataPointFloat64", testDatatype(dataPointFloat64A, dataPointFloat64B, equalDataPoints[float64]))
|
||||
t.Run("Extrema", testDatatype(minA, minB, equalExtrema))
|
||||
t.Run("ExemplarInt64", testDatatype(exemplarInt64A, exemplarInt64B, equalExemplars[int64]))
|
||||
t.Run("ExemplarFloat64", testDatatype(exemplarFloat64A, exemplarFloat64B, equalExemplars[float64]))
|
||||
}
|
||||
|
||||
func TestAssertEqualIgnoreTime(t *testing.T) {
|
||||
t.Run("ResourceMetrics", testDatatypeIgnoreTime(resourceMetricsA, resourceMetricsC, equalResourceMetrics))
|
||||
t.Run("ScopeMetrics", testDatatypeIgnoreTime(scopeMetricsA, scopeMetricsC, equalScopeMetrics))
|
||||
t.Run("Metrics", testDatatypeIgnoreTime(metricsA, metricsC, equalMetrics))
|
||||
t.Run("Histogram", testDatatypeIgnoreTime(histogramA, histogramC, equalHistograms))
|
||||
t.Run("HistogramInt64", testDatatypeIgnoreTime(histogramInt64A, histogramInt64C, equalHistograms[int64]))
|
||||
t.Run("HistogramFloat64", testDatatypeIgnoreTime(histogramFloat64A, histogramFloat64C, equalHistograms[float64]))
|
||||
t.Run("SumInt64", testDatatypeIgnoreTime(sumInt64A, sumInt64C, equalSums[int64]))
|
||||
t.Run("SumFloat64", testDatatypeIgnoreTime(sumFloat64A, sumFloat64C, equalSums[float64]))
|
||||
t.Run("GaugeInt64", testDatatypeIgnoreTime(gaugeInt64A, gaugeInt64C, equalGauges[int64]))
|
||||
t.Run("GaugeFloat64", testDatatypeIgnoreTime(gaugeFloat64A, gaugeFloat64C, equalGauges[float64]))
|
||||
t.Run("HistogramDataPoint", testDatatypeIgnoreTime(histogramDataPointA, histogramDataPointC, equalHistogramDataPoints))
|
||||
t.Run("HistogramDataPointInt64", testDatatypeIgnoreTime(histogramDataPointInt64A, histogramDataPointInt64C, equalHistogramDataPoints[int64]))
|
||||
t.Run("HistogramDataPointFloat64", testDatatypeIgnoreTime(histogramDataPointFloat64A, histogramDataPointFloat64C, equalHistogramDataPoints[float64]))
|
||||
t.Run("DataPointInt64", testDatatypeIgnoreTime(dataPointInt64A, dataPointInt64C, equalDataPoints[int64]))
|
||||
t.Run("DataPointFloat64", testDatatypeIgnoreTime(dataPointFloat64A, dataPointFloat64C, equalDataPoints[float64]))
|
||||
t.Run("Extrema", testDatatypeIgnoreTime(minA, minC, equalExtrema))
|
||||
t.Run("ExemplarInt64", testDatatypeIgnoreTime(exemplarInt64A, exemplarInt64C, equalExemplars[int64]))
|
||||
t.Run("ExemplarFloat64", testDatatypeIgnoreTime(exemplarFloat64A, exemplarFloat64C, equalExemplars[float64]))
|
||||
}
|
||||
|
||||
func TestAssertEqualIgnoreExemplars(t *testing.T) {
|
||||
hdpInt64 := histogramDataPointInt64A
|
||||
hdpInt64.Exemplars = []metricdata.Exemplar[int64]{exemplarInt64B}
|
||||
t.Run("HistogramDataPointInt64", testDatatypeIgnoreExemplars(histogramDataPointInt64A, hdpInt64, equalHistogramDataPoints[int64]))
|
||||
|
||||
hdpFloat64 := histogramDataPointFloat64A
|
||||
hdpFloat64.Exemplars = []metricdata.Exemplar[float64]{exemplarFloat64B}
|
||||
t.Run("HistogramDataPointFloat64", testDatatypeIgnoreExemplars(histogramDataPointFloat64A, hdpFloat64, equalHistogramDataPoints[float64]))
|
||||
|
||||
dpInt64 := dataPointInt64A
|
||||
dpInt64.Exemplars = []metricdata.Exemplar[int64]{exemplarInt64B}
|
||||
t.Run("DataPointInt64", testDatatypeIgnoreExemplars(dataPointInt64A, dpInt64, equalDataPoints[int64]))
|
||||
|
||||
dpFloat64 := dataPointFloat64A
|
||||
dpFloat64.Exemplars = []metricdata.Exemplar[float64]{exemplarFloat64B}
|
||||
t.Run("DataPointFloat64", testDatatypeIgnoreExemplars(dataPointFloat64A, dpFloat64, equalDataPoints[float64]))
|
||||
}
|
||||
|
||||
type unknownAggregation struct {
|
||||
@@ -279,7 +423,8 @@ func TestAssertAggregationsEqual(t *testing.T) {
|
||||
AssertAggregationsEqual(t, sumFloat64A, sumFloat64A)
|
||||
AssertAggregationsEqual(t, gaugeInt64A, gaugeInt64A)
|
||||
AssertAggregationsEqual(t, gaugeFloat64A, gaugeFloat64A)
|
||||
AssertAggregationsEqual(t, histogramA, histogramA)
|
||||
AssertAggregationsEqual(t, histogramInt64A, histogramInt64A)
|
||||
AssertAggregationsEqual(t, histogramFloat64A, histogramFloat64A)
|
||||
|
||||
r := equalAggregations(sumInt64A, nil, config{})
|
||||
assert.Len(t, r, 1, "should return nil comparison mismatch only")
|
||||
@@ -291,46 +436,56 @@ func TestAssertAggregationsEqual(t *testing.T) {
|
||||
assert.Len(t, r, 1, "should return with unknown aggregation only")
|
||||
|
||||
r = equalAggregations(sumInt64A, sumInt64B, config{})
|
||||
assert.Greaterf(t, len(r), 0, "%v == %v", sumInt64A, sumInt64B)
|
||||
assert.Greaterf(t, len(r), 0, "sums should not be equal: %v == %v", sumInt64A, sumInt64B)
|
||||
|
||||
r = equalAggregations(sumInt64A, sumInt64C, config{ignoreTimestamp: true})
|
||||
assert.Equalf(t, len(r), 0, "%v == %v", sumInt64A, sumInt64C)
|
||||
assert.Len(t, r, 0, "sums should be equal: %v", r)
|
||||
|
||||
r = equalAggregations(sumFloat64A, sumFloat64B, config{})
|
||||
assert.Greaterf(t, len(r), 0, "%v == %v", sumFloat64A, sumFloat64B)
|
||||
assert.Greaterf(t, len(r), 0, "sums should not be equal: %v == %v", sumFloat64A, sumFloat64B)
|
||||
|
||||
r = equalAggregations(sumFloat64A, sumFloat64C, config{ignoreTimestamp: true})
|
||||
assert.Equalf(t, len(r), 0, "%v == %v", sumFloat64A, sumFloat64C)
|
||||
assert.Len(t, r, 0, "sums should be equal: %v", r)
|
||||
|
||||
r = equalAggregations(gaugeInt64A, gaugeInt64B, config{})
|
||||
assert.Greaterf(t, len(r), 0, "%v == %v", gaugeInt64A, gaugeInt64B)
|
||||
assert.Greaterf(t, len(r), 0, "gauges should not be equal: %v == %v", gaugeInt64A, gaugeInt64B)
|
||||
|
||||
r = equalAggregations(gaugeInt64A, gaugeInt64C, config{ignoreTimestamp: true})
|
||||
assert.Equalf(t, len(r), 0, "%v == %v", gaugeInt64A, gaugeInt64C)
|
||||
assert.Len(t, r, 0, "gauges should be equal: %v", r)
|
||||
|
||||
r = equalAggregations(gaugeFloat64A, gaugeFloat64B, config{})
|
||||
assert.Greaterf(t, len(r), 0, "%v == %v", gaugeFloat64A, gaugeFloat64B)
|
||||
assert.Greaterf(t, len(r), 0, "gauges should not be equal: %v == %v", gaugeFloat64A, gaugeFloat64B)
|
||||
|
||||
r = equalAggregations(gaugeFloat64A, gaugeFloat64C, config{ignoreTimestamp: true})
|
||||
assert.Equalf(t, len(r), 0, "%v == %v", gaugeFloat64A, gaugeFloat64C)
|
||||
assert.Len(t, r, 0, "gauges should be equal: %v", r)
|
||||
|
||||
r = equalAggregations(histogramA, histogramB, config{})
|
||||
assert.Greaterf(t, len(r), 0, "%v == %v", histogramA, histogramB)
|
||||
r = equalAggregations(histogramInt64A, histogramInt64B, config{})
|
||||
assert.Greaterf(t, len(r), 0, "histograms should not be equal: %v == %v", histogramInt64A, histogramInt64B)
|
||||
|
||||
r = equalAggregations(histogramA, histogramC, config{ignoreTimestamp: true})
|
||||
assert.Equalf(t, len(r), 0, "%v == %v", histogramA, histogramC)
|
||||
r = equalAggregations(histogramInt64A, histogramInt64C, config{ignoreTimestamp: true})
|
||||
assert.Len(t, r, 0, "histograms should be equal: %v", r)
|
||||
|
||||
r = equalAggregations(histogramFloat64A, histogramFloat64B, config{})
|
||||
assert.Greaterf(t, len(r), 0, "histograms should not be equal: %v == %v", histogramFloat64A, histogramFloat64B)
|
||||
|
||||
r = equalAggregations(histogramFloat64A, histogramFloat64C, config{ignoreTimestamp: true})
|
||||
assert.Len(t, r, 0, "histograms should be equal: %v", r)
|
||||
}
|
||||
|
||||
func TestAssertAttributes(t *testing.T) {
|
||||
AssertHasAttributes(t, minA, attribute.Bool("A", true)) // No-op, always pass.
|
||||
AssertHasAttributes(t, exemplarInt64A, attribute.Bool("filter A", true))
|
||||
AssertHasAttributes(t, exemplarFloat64A, attribute.Bool("filter A", true))
|
||||
AssertHasAttributes(t, dataPointInt64A, attribute.Bool("A", true))
|
||||
AssertHasAttributes(t, dataPointFloat64A, attribute.Bool("A", true))
|
||||
AssertHasAttributes(t, gaugeInt64A, attribute.Bool("A", true))
|
||||
AssertHasAttributes(t, gaugeFloat64A, attribute.Bool("A", true))
|
||||
AssertHasAttributes(t, sumInt64A, attribute.Bool("A", true))
|
||||
AssertHasAttributes(t, sumFloat64A, attribute.Bool("A", true))
|
||||
AssertHasAttributes(t, histogramDataPointA, attribute.Bool("A", true))
|
||||
AssertHasAttributes(t, histogramA, attribute.Bool("A", true))
|
||||
AssertHasAttributes(t, histogramDataPointInt64A, attribute.Bool("A", true))
|
||||
AssertHasAttributes(t, histogramDataPointFloat64A, attribute.Bool("A", true))
|
||||
AssertHasAttributes(t, histogramInt64A, attribute.Bool("A", true))
|
||||
AssertHasAttributes(t, histogramFloat64A, attribute.Bool("A", true))
|
||||
AssertHasAttributes(t, metricsA, attribute.Bool("A", true))
|
||||
AssertHasAttributes(t, scopeMetricsA, attribute.Bool("A", true))
|
||||
AssertHasAttributes(t, resourceMetricsA, attribute.Bool("A", true))
|
||||
@@ -343,8 +498,10 @@ func TestAssertAttributes(t *testing.T) {
|
||||
assert.Equal(t, len(r), 0, "sumInt64A has A=True")
|
||||
r = hasAttributesAggregation(sumFloat64A, attribute.Bool("A", true))
|
||||
assert.Equal(t, len(r), 0, "sumFloat64A has A=True")
|
||||
r = hasAttributesAggregation(histogramA, attribute.Bool("A", true))
|
||||
assert.Equal(t, len(r), 0, "histogramA has A=True")
|
||||
r = hasAttributesAggregation(histogramInt64A, attribute.Bool("A", true))
|
||||
assert.Equal(t, len(r), 0, "histogramInt64A has A=True")
|
||||
r = hasAttributesAggregation(histogramFloat64A, attribute.Bool("A", true))
|
||||
assert.Equal(t, len(r), 0, "histogramFloat64A has A=True")
|
||||
|
||||
r = hasAttributesAggregation(gaugeInt64A, attribute.Bool("A", false))
|
||||
assert.Greater(t, len(r), 0, "gaugeInt64A does not have A=False")
|
||||
@@ -354,8 +511,10 @@ func TestAssertAttributes(t *testing.T) {
|
||||
assert.Greater(t, len(r), 0, "sumInt64A does not have A=False")
|
||||
r = hasAttributesAggregation(sumFloat64A, attribute.Bool("A", false))
|
||||
assert.Greater(t, len(r), 0, "sumFloat64A does not have A=False")
|
||||
r = hasAttributesAggregation(histogramA, attribute.Bool("A", false))
|
||||
assert.Greater(t, len(r), 0, "histogramA does not have A=False")
|
||||
r = hasAttributesAggregation(histogramInt64A, attribute.Bool("A", false))
|
||||
assert.Greater(t, len(r), 0, "histogramInt64A does not have A=False")
|
||||
r = hasAttributesAggregation(histogramFloat64A, attribute.Bool("A", false))
|
||||
assert.Greater(t, len(r), 0, "histogramFloat64A does not have A=False")
|
||||
|
||||
r = hasAttributesAggregation(gaugeInt64A, attribute.Bool("B", true))
|
||||
assert.Greater(t, len(r), 0, "gaugeInt64A does not have Attribute B")
|
||||
@@ -365,22 +524,26 @@ func TestAssertAttributes(t *testing.T) {
|
||||
assert.Greater(t, len(r), 0, "sumInt64A does not have Attribute B")
|
||||
r = hasAttributesAggregation(sumFloat64A, attribute.Bool("B", true))
|
||||
assert.Greater(t, len(r), 0, "sumFloat64A does not have Attribute B")
|
||||
r = hasAttributesAggregation(histogramA, attribute.Bool("B", true))
|
||||
assert.Greater(t, len(r), 0, "histogramA does not have Attribute B")
|
||||
r = hasAttributesAggregation(histogramInt64A, attribute.Bool("B", true))
|
||||
assert.Greater(t, len(r), 0, "histogramIntA does not have Attribute B")
|
||||
r = hasAttributesAggregation(histogramFloat64A, attribute.Bool("B", true))
|
||||
assert.Greater(t, len(r), 0, "histogramFloatA does not have Attribute B")
|
||||
}
|
||||
|
||||
func TestAssertAttributesFail(t *testing.T) {
|
||||
fakeT := &testing.T{}
|
||||
assert.False(t, AssertHasAttributes(fakeT, dataPointInt64A, attribute.Bool("A", false)))
|
||||
assert.False(t, AssertHasAttributes(fakeT, dataPointFloat64A, attribute.Bool("B", true)))
|
||||
assert.False(t, AssertHasAttributes(fakeT, exemplarInt64A, attribute.Bool("A", false)))
|
||||
assert.False(t, AssertHasAttributes(fakeT, exemplarFloat64A, attribute.Bool("B", true)))
|
||||
assert.False(t, AssertHasAttributes(fakeT, gaugeInt64A, attribute.Bool("A", false)))
|
||||
assert.False(t, AssertHasAttributes(fakeT, gaugeFloat64A, attribute.Bool("B", true)))
|
||||
assert.False(t, AssertHasAttributes(fakeT, sumInt64A, attribute.Bool("A", false)))
|
||||
assert.False(t, AssertHasAttributes(fakeT, sumFloat64A, attribute.Bool("B", true)))
|
||||
assert.False(t, AssertHasAttributes(fakeT, histogramDataPointA, attribute.Bool("A", false)))
|
||||
assert.False(t, AssertHasAttributes(fakeT, histogramDataPointA, attribute.Bool("B", true)))
|
||||
assert.False(t, AssertHasAttributes(fakeT, histogramA, attribute.Bool("A", false)))
|
||||
assert.False(t, AssertHasAttributes(fakeT, histogramA, attribute.Bool("B", true)))
|
||||
assert.False(t, AssertHasAttributes(fakeT, histogramDataPointInt64A, attribute.Bool("A", false)))
|
||||
assert.False(t, AssertHasAttributes(fakeT, histogramDataPointFloat64A, attribute.Bool("B", true)))
|
||||
assert.False(t, AssertHasAttributes(fakeT, histogramInt64A, attribute.Bool("A", false)))
|
||||
assert.False(t, AssertHasAttributes(fakeT, histogramFloat64A, attribute.Bool("B", true)))
|
||||
assert.False(t, AssertHasAttributes(fakeT, metricsA, attribute.Bool("A", false)))
|
||||
assert.False(t, AssertHasAttributes(fakeT, metricsA, attribute.Bool("B", true)))
|
||||
assert.False(t, AssertHasAttributes(fakeT, resourceMetricsA, attribute.Bool("A", false)))
|
||||
|
||||
@@ -131,8 +131,14 @@ func equalAggregations(a, b metricdata.Aggregation, cfg config) (reasons []strin
|
||||
reasons = append(reasons, "Sum[float64] not equal:")
|
||||
reasons = append(reasons, r...)
|
||||
}
|
||||
case metricdata.Histogram:
|
||||
r := equalHistograms(v, b.(metricdata.Histogram), cfg)
|
||||
case metricdata.Histogram[int64]:
|
||||
r := equalHistograms(v, b.(metricdata.Histogram[int64]), cfg)
|
||||
if len(r) > 0 {
|
||||
reasons = append(reasons, "Histogram not equal:")
|
||||
reasons = append(reasons, r...)
|
||||
}
|
||||
case metricdata.Histogram[float64]:
|
||||
r := equalHistograms(v, b.(metricdata.Histogram[float64]), cfg)
|
||||
if len(r) > 0 {
|
||||
reasons = append(reasons, "Histogram not equal:")
|
||||
reasons = append(reasons, r...)
|
||||
@@ -195,7 +201,7 @@ func equalSums[N int64 | float64](a, b metricdata.Sum[N], cfg config) (reasons [
|
||||
//
|
||||
// The DataPoints each Histogram contains are compared based on containing the
|
||||
// same HistogramDataPoint, not the order they are stored in.
|
||||
func equalHistograms(a, b metricdata.Histogram, cfg config) (reasons []string) {
|
||||
func equalHistograms[N int64 | float64](a, b metricdata.Histogram[N], cfg config) (reasons []string) {
|
||||
if a.Temporality != b.Temporality {
|
||||
reasons = append(reasons, notEqualStr("Temporality", a.Temporality, b.Temporality))
|
||||
}
|
||||
@@ -203,7 +209,7 @@ func equalHistograms(a, b metricdata.Histogram, cfg config) (reasons []string) {
|
||||
r := compareDiff(diffSlices(
|
||||
a.DataPoints,
|
||||
b.DataPoints,
|
||||
func(a, b metricdata.HistogramDataPoint) bool {
|
||||
func(a, b metricdata.HistogramDataPoint[N]) bool {
|
||||
r := equalHistogramDataPoints(a, b, cfg)
|
||||
return len(r) == 0
|
||||
},
|
||||
@@ -237,12 +243,26 @@ func equalDataPoints[N int64 | float64](a, b metricdata.DataPoint[N], cfg config
|
||||
if a.Value != b.Value {
|
||||
reasons = append(reasons, notEqualStr("Value", a.Value, b.Value))
|
||||
}
|
||||
|
||||
if !cfg.ignoreExemplars {
|
||||
r := compareDiff(diffSlices(
|
||||
a.Exemplars,
|
||||
b.Exemplars,
|
||||
func(a, b metricdata.Exemplar[N]) bool {
|
||||
r := equalExemplars(a, b, cfg)
|
||||
return len(r) == 0
|
||||
},
|
||||
))
|
||||
if r != "" {
|
||||
reasons = append(reasons, fmt.Sprintf("Exemplars not equal:\n%s", r))
|
||||
}
|
||||
}
|
||||
return reasons
|
||||
}
|
||||
|
||||
// equalHistogramDataPoints returns reasons HistogramDataPoints are not equal.
|
||||
// If they are equal, the returned reasons will be empty.
|
||||
func equalHistogramDataPoints(a, b metricdata.HistogramDataPoint, cfg config) (reasons []string) { // nolint: revive // Intentional internal control flag
|
||||
func equalHistogramDataPoints[N int64 | float64](a, b metricdata.HistogramDataPoint[N], cfg config) (reasons []string) { // nolint: revive // Intentional internal control flag
|
||||
if !a.Attributes.Equals(&b.Attributes) {
|
||||
reasons = append(reasons, notEqualStr(
|
||||
"Attributes",
|
||||
@@ -276,6 +296,19 @@ func equalHistogramDataPoints(a, b metricdata.HistogramDataPoint, cfg config) (r
|
||||
if a.Sum != b.Sum {
|
||||
reasons = append(reasons, notEqualStr("Sum", a.Sum, b.Sum))
|
||||
}
|
||||
if !cfg.ignoreExemplars {
|
||||
r := compareDiff(diffSlices(
|
||||
a.Exemplars,
|
||||
b.Exemplars,
|
||||
func(a, b metricdata.Exemplar[N]) bool {
|
||||
r := equalExemplars(a, b, cfg)
|
||||
return len(r) == 0
|
||||
},
|
||||
))
|
||||
if r != "" {
|
||||
reasons = append(reasons, fmt.Sprintf("Exemplars not equal:\n%s", r))
|
||||
}
|
||||
}
|
||||
return reasons
|
||||
}
|
||||
|
||||
@@ -312,6 +345,82 @@ func eqExtrema(a, b metricdata.Extrema) bool {
|
||||
return aV == bV
|
||||
}
|
||||
|
||||
func equalKeyValue(a, b []attribute.KeyValue) bool {
|
||||
// Comparison of []attribute.KeyValue as a comparable requires Go >= 1.20.
|
||||
// To support Go < 1.20 use this function instead.
|
||||
if len(a) != len(b) {
|
||||
return false
|
||||
}
|
||||
for i, v := range a {
|
||||
if v.Key != b[i].Key {
|
||||
return false
|
||||
}
|
||||
if v.Value.Type() != b[i].Value.Type() {
|
||||
return false
|
||||
}
|
||||
switch v.Value.Type() {
|
||||
case attribute.BOOL:
|
||||
if v.Value.AsBool() != b[i].Value.AsBool() {
|
||||
return false
|
||||
}
|
||||
case attribute.INT64:
|
||||
if v.Value.AsInt64() != b[i].Value.AsInt64() {
|
||||
return false
|
||||
}
|
||||
case attribute.FLOAT64:
|
||||
if v.Value.AsFloat64() != b[i].Value.AsFloat64() {
|
||||
return false
|
||||
}
|
||||
case attribute.STRING:
|
||||
if v.Value.AsString() != b[i].Value.AsString() {
|
||||
return false
|
||||
}
|
||||
case attribute.BOOLSLICE:
|
||||
if ok := equalSlices(v.Value.AsBoolSlice(), b[i].Value.AsBoolSlice()); !ok {
|
||||
return false
|
||||
}
|
||||
case attribute.INT64SLICE:
|
||||
if ok := equalSlices(v.Value.AsInt64Slice(), b[i].Value.AsInt64Slice()); !ok {
|
||||
return false
|
||||
}
|
||||
case attribute.FLOAT64SLICE:
|
||||
if ok := equalSlices(v.Value.AsFloat64Slice(), b[i].Value.AsFloat64Slice()); !ok {
|
||||
return false
|
||||
}
|
||||
case attribute.STRINGSLICE:
|
||||
if ok := equalSlices(v.Value.AsStringSlice(), b[i].Value.AsStringSlice()); !ok {
|
||||
return false
|
||||
}
|
||||
default:
|
||||
// We control all types passed to this, panic to signal developers
|
||||
// early they changed things in an incompatible way.
|
||||
panic(fmt.Sprintf("unknown attribute value type: %s", v.Value.Type()))
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
func equalExemplars[N int64 | float64](a, b metricdata.Exemplar[N], cfg config) (reasons []string) {
|
||||
if !equalKeyValue(a.FilteredAttributes, b.FilteredAttributes) {
|
||||
reasons = append(reasons, notEqualStr("FilteredAttributes", a.FilteredAttributes, b.FilteredAttributes))
|
||||
}
|
||||
if !cfg.ignoreTimestamp {
|
||||
if !a.Time.Equal(b.Time) {
|
||||
reasons = append(reasons, notEqualStr("Time", a.Time.UnixNano(), b.Time.UnixNano()))
|
||||
}
|
||||
}
|
||||
if a.Value != b.Value {
|
||||
reasons = append(reasons, notEqualStr("Value", a.Value, b.Value))
|
||||
}
|
||||
if !equalSlices(a.SpanID, b.SpanID) {
|
||||
reasons = append(reasons, notEqualStr("SpanID", a.SpanID, b.SpanID))
|
||||
}
|
||||
if !equalSlices(a.TraceID, b.TraceID) {
|
||||
reasons = append(reasons, notEqualStr("TraceID", a.TraceID, b.TraceID))
|
||||
}
|
||||
return reasons
|
||||
}
|
||||
|
||||
func diffSlices[T any](a, b []T, equal func(T, T) bool) (extraA, extraB []T) {
|
||||
visited := make([]bool, len(b))
|
||||
for i := 0; i < len(a); i++ {
|
||||
@@ -372,6 +481,21 @@ func missingAttrStr(name string) string {
|
||||
return fmt.Sprintf("missing attribute %s", name)
|
||||
}
|
||||
|
||||
func hasAttributesExemplars[T int64 | float64](exemplar metricdata.Exemplar[T], attrs ...attribute.KeyValue) (reasons []string) {
|
||||
s := attribute.NewSet(exemplar.FilteredAttributes...)
|
||||
for _, attr := range attrs {
|
||||
val, ok := s.Value(attr.Key)
|
||||
if !ok {
|
||||
reasons = append(reasons, missingAttrStr(string(attr.Key)))
|
||||
continue
|
||||
}
|
||||
if val != attr.Value {
|
||||
reasons = append(reasons, notEqualStr(string(attr.Key), attr.Value.Emit(), val.Emit()))
|
||||
}
|
||||
}
|
||||
return reasons
|
||||
}
|
||||
|
||||
func hasAttributesDataPoints[T int64 | float64](dp metricdata.DataPoint[T], attrs ...attribute.KeyValue) (reasons []string) {
|
||||
for _, attr := range attrs {
|
||||
val, ok := dp.Attributes.Value(attr.Key)
|
||||
@@ -408,7 +532,7 @@ func hasAttributesSum[T int64 | float64](sum metricdata.Sum[T], attrs ...attribu
|
||||
return reasons
|
||||
}
|
||||
|
||||
func hasAttributesHistogramDataPoints(dp metricdata.HistogramDataPoint, attrs ...attribute.KeyValue) (reasons []string) {
|
||||
func hasAttributesHistogramDataPoints[T int64 | float64](dp metricdata.HistogramDataPoint[T], attrs ...attribute.KeyValue) (reasons []string) {
|
||||
for _, attr := range attrs {
|
||||
val, ok := dp.Attributes.Value(attr.Key)
|
||||
if !ok {
|
||||
@@ -422,7 +546,7 @@ func hasAttributesHistogramDataPoints(dp metricdata.HistogramDataPoint, attrs ..
|
||||
return reasons
|
||||
}
|
||||
|
||||
func hasAttributesHistogram(histogram metricdata.Histogram, attrs ...attribute.KeyValue) (reasons []string) {
|
||||
func hasAttributesHistogram[T int64 | float64](histogram metricdata.Histogram[T], attrs ...attribute.KeyValue) (reasons []string) {
|
||||
for n, dp := range histogram.DataPoints {
|
||||
reas := hasAttributesHistogramDataPoints(dp, attrs...)
|
||||
if len(reas) > 0 {
|
||||
@@ -443,7 +567,9 @@ func hasAttributesAggregation(agg metricdata.Aggregation, attrs ...attribute.Key
|
||||
reasons = hasAttributesSum(agg, attrs...)
|
||||
case metricdata.Sum[float64]:
|
||||
reasons = hasAttributesSum(agg, attrs...)
|
||||
case metricdata.Histogram:
|
||||
case metricdata.Histogram[int64]:
|
||||
reasons = hasAttributesHistogram(agg, attrs...)
|
||||
case metricdata.Histogram[float64]:
|
||||
reasons = hasAttributesHistogram(agg, attrs...)
|
||||
default:
|
||||
reasons = []string{fmt.Sprintf("unknown aggregation %T", agg)}
|
||||
|
||||
Reference in New Issue
Block a user