mirror of
https://github.com/open-telemetry/opentelemetry-go.git
synced 2024-12-12 10:04:29 +02:00
824 lines
26 KiB
Go
824 lines
26 KiB
Go
// Copyright The OpenTelemetry Authors
|
|
//
|
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
// you may not use this file except in compliance with the License.
|
|
// You may obtain a copy of the License at
|
|
//
|
|
// http://www.apache.org/licenses/LICENSE-2.0
|
|
//
|
|
// Unless required by applicable law or agreed to in writing, software
|
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
// See the License for the specific language governing permissions and
|
|
// limitations under the License.
|
|
|
|
package metric // import "go.opentelemetry.io/otel/sdk/metric"
|
|
|
|
import (
|
|
"context"
|
|
"sync/atomic"
|
|
"testing"
|
|
|
|
"github.com/go-logr/logr"
|
|
"github.com/go-logr/logr/testr"
|
|
"github.com/stretchr/testify/assert"
|
|
"github.com/stretchr/testify/require"
|
|
|
|
"go.opentelemetry.io/otel"
|
|
"go.opentelemetry.io/otel/attribute"
|
|
"go.opentelemetry.io/otel/sdk/metric/internal/aggregate"
|
|
"go.opentelemetry.io/otel/sdk/metric/metricdata"
|
|
"go.opentelemetry.io/otel/sdk/metric/metricdata/metricdatatest"
|
|
"go.opentelemetry.io/otel/sdk/resource"
|
|
)
|
|
|
|
var defaultView = NewView(Instrument{Name: "*"}, Stream{})
|
|
|
|
type invalidAggregation struct{}
|
|
|
|
func (invalidAggregation) copy() Aggregation {
|
|
return invalidAggregation{}
|
|
}
|
|
|
|
func (invalidAggregation) err() error {
|
|
return nil
|
|
}
|
|
|
|
func requireN[N int64 | float64](t *testing.T, n int, m []aggregate.Measure[N], comps []aggregate.ComputeAggregation, err error) {
|
|
t.Helper()
|
|
assert.NoError(t, err)
|
|
require.Len(t, m, n)
|
|
require.Len(t, comps, n)
|
|
}
|
|
|
|
func assertSum[N int64 | float64](n int, temp metricdata.Temporality, mono bool, v [2]N) func(*testing.T, []aggregate.Measure[N], []aggregate.ComputeAggregation, error) {
|
|
return func(t *testing.T, meas []aggregate.Measure[N], comps []aggregate.ComputeAggregation, err error) {
|
|
t.Helper()
|
|
requireN[N](t, n, meas, comps, err)
|
|
|
|
for m := 0; m < n; m++ {
|
|
t.Logf("input/output number: %d", m)
|
|
in, out := meas[m], comps[m]
|
|
in(context.Background(), 1, *attribute.EmptySet())
|
|
|
|
var got metricdata.Aggregation
|
|
assert.Equal(t, 1, out(&got), "1 data-point expected")
|
|
metricdatatest.AssertAggregationsEqual(t, metricdata.Sum[N]{
|
|
Temporality: temp,
|
|
IsMonotonic: mono,
|
|
DataPoints: []metricdata.DataPoint[N]{{Value: v[0]}},
|
|
}, got, metricdatatest.IgnoreTimestamp())
|
|
|
|
in(context.Background(), 3, *attribute.EmptySet())
|
|
|
|
assert.Equal(t, 1, out(&got), "1 data-point expected")
|
|
metricdatatest.AssertAggregationsEqual(t, metricdata.Sum[N]{
|
|
Temporality: temp,
|
|
IsMonotonic: mono,
|
|
DataPoints: []metricdata.DataPoint[N]{{Value: v[1]}},
|
|
}, got, metricdatatest.IgnoreTimestamp())
|
|
}
|
|
}
|
|
}
|
|
|
|
func assertHist[N int64 | float64](temp metricdata.Temporality) func(*testing.T, []aggregate.Measure[N], []aggregate.ComputeAggregation, error) {
|
|
return func(t *testing.T, meas []aggregate.Measure[N], comps []aggregate.ComputeAggregation, err error) {
|
|
t.Helper()
|
|
requireN[N](t, 1, meas, comps, err)
|
|
|
|
in, out := meas[0], comps[0]
|
|
in(context.Background(), 1, *attribute.EmptySet())
|
|
|
|
var got metricdata.Aggregation
|
|
assert.Equal(t, 1, out(&got), "1 data-point expected")
|
|
buckets := []uint64{0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}
|
|
n := 1
|
|
metricdatatest.AssertAggregationsEqual(t, metricdata.Histogram[N]{
|
|
Temporality: temp,
|
|
DataPoints: []metricdata.HistogramDataPoint[N]{{
|
|
Count: uint64(n),
|
|
Bounds: []float64{0, 5, 10, 25, 50, 75, 100, 250, 500, 750, 1000, 2500, 5000, 7500, 10000},
|
|
BucketCounts: buckets,
|
|
Min: metricdata.NewExtrema[N](1),
|
|
Max: metricdata.NewExtrema[N](1),
|
|
Sum: N(n),
|
|
}},
|
|
}, got, metricdatatest.IgnoreTimestamp())
|
|
|
|
in(context.Background(), 1, *attribute.EmptySet())
|
|
|
|
if temp == metricdata.CumulativeTemporality {
|
|
buckets[1] = 2
|
|
n = 2
|
|
}
|
|
assert.Equal(t, 1, out(&got), "1 data-point expected")
|
|
metricdatatest.AssertAggregationsEqual(t, metricdata.Histogram[N]{
|
|
Temporality: temp,
|
|
DataPoints: []metricdata.HistogramDataPoint[N]{{
|
|
Count: uint64(n),
|
|
Bounds: []float64{0, 5, 10, 25, 50, 75, 100, 250, 500, 750, 1000, 2500, 5000, 7500, 10000},
|
|
BucketCounts: buckets,
|
|
Min: metricdata.NewExtrema[N](1),
|
|
Max: metricdata.NewExtrema[N](1),
|
|
Sum: N(n),
|
|
}},
|
|
}, got, metricdatatest.IgnoreTimestamp())
|
|
}
|
|
}
|
|
|
|
func assertLastValue[N int64 | float64](t *testing.T, meas []aggregate.Measure[N], comps []aggregate.ComputeAggregation, err error) {
|
|
t.Helper()
|
|
requireN[N](t, 1, meas, comps, err)
|
|
|
|
in, out := meas[0], comps[0]
|
|
in(context.Background(), 10, *attribute.EmptySet())
|
|
in(context.Background(), 1, *attribute.EmptySet())
|
|
|
|
var got metricdata.Aggregation
|
|
assert.Equal(t, 1, out(&got), "1 data-point expected")
|
|
metricdatatest.AssertAggregationsEqual(t, metricdata.Gauge[N]{
|
|
DataPoints: []metricdata.DataPoint[N]{{Value: 1}},
|
|
}, got, metricdatatest.IgnoreTimestamp())
|
|
}
|
|
|
|
func testCreateAggregators[N int64 | float64](t *testing.T) {
|
|
changeAggView := NewView(
|
|
Instrument{Name: "foo"},
|
|
Stream{Aggregation: AggregationExplicitBucketHistogram{
|
|
Boundaries: []float64{0, 100},
|
|
NoMinMax: true,
|
|
}},
|
|
)
|
|
renameView := NewView(Instrument{Name: "foo"}, Stream{Name: "bar"})
|
|
invalidAggView := NewView(
|
|
Instrument{Name: "foo"},
|
|
Stream{Aggregation: invalidAggregation{}},
|
|
)
|
|
|
|
instruments := []Instrument{
|
|
{Name: "foo", Kind: InstrumentKind(0)}, // Unknown kind
|
|
{Name: "foo", Kind: InstrumentKindCounter},
|
|
{Name: "foo", Kind: InstrumentKindUpDownCounter},
|
|
{Name: "foo", Kind: InstrumentKindHistogram},
|
|
{Name: "foo", Kind: InstrumentKindObservableCounter},
|
|
{Name: "foo", Kind: InstrumentKindObservableUpDownCounter},
|
|
{Name: "foo", Kind: InstrumentKindObservableGauge},
|
|
}
|
|
|
|
testcases := []struct {
|
|
name string
|
|
reader Reader
|
|
views []View
|
|
inst Instrument
|
|
validate func(*testing.T, []aggregate.Measure[N], []aggregate.ComputeAggregation, error)
|
|
}{
|
|
{
|
|
name: "Default/Drop",
|
|
reader: NewManualReader(WithAggregationSelector(func(ik InstrumentKind) Aggregation { return AggregationDrop{} })),
|
|
inst: instruments[InstrumentKindCounter],
|
|
validate: func(t *testing.T, meas []aggregate.Measure[N], comps []aggregate.ComputeAggregation, err error) {
|
|
t.Helper()
|
|
assert.NoError(t, err)
|
|
assert.Len(t, meas, 0)
|
|
assert.Len(t, comps, 0)
|
|
},
|
|
},
|
|
{
|
|
name: "Default/Delta/Sum/NonMonotonic",
|
|
reader: NewManualReader(WithTemporalitySelector(deltaTemporalitySelector)),
|
|
inst: instruments[InstrumentKindUpDownCounter],
|
|
validate: assertSum[N](1, metricdata.DeltaTemporality, false, [2]N{1, 3}),
|
|
},
|
|
{
|
|
name: "Default/Delta/ExplicitBucketHistogram",
|
|
reader: NewManualReader(WithTemporalitySelector(deltaTemporalitySelector)),
|
|
inst: instruments[InstrumentKindHistogram],
|
|
validate: assertHist[N](metricdata.DeltaTemporality),
|
|
},
|
|
{
|
|
name: "Default/Delta/PrecomputedSum/Monotonic",
|
|
reader: NewManualReader(WithTemporalitySelector(deltaTemporalitySelector)),
|
|
inst: instruments[InstrumentKindObservableCounter],
|
|
validate: assertSum[N](1, metricdata.DeltaTemporality, true, [2]N{1, 2}),
|
|
},
|
|
{
|
|
name: "Default/Delta/PrecomputedSum/NonMonotonic",
|
|
reader: NewManualReader(WithTemporalitySelector(deltaTemporalitySelector)),
|
|
inst: instruments[InstrumentKindObservableUpDownCounter],
|
|
validate: assertSum[N](1, metricdata.DeltaTemporality, false, [2]N{1, 2}),
|
|
},
|
|
{
|
|
name: "Default/Delta/Gauge",
|
|
reader: NewManualReader(WithTemporalitySelector(deltaTemporalitySelector)),
|
|
inst: instruments[InstrumentKindObservableGauge],
|
|
validate: assertLastValue[N],
|
|
},
|
|
{
|
|
name: "Default/Delta/Sum/Monotonic",
|
|
reader: NewManualReader(WithTemporalitySelector(deltaTemporalitySelector)),
|
|
inst: instruments[InstrumentKindCounter],
|
|
validate: assertSum[N](1, metricdata.DeltaTemporality, true, [2]N{1, 3}),
|
|
},
|
|
{
|
|
name: "Default/Cumulative/Sum/NonMonotonic",
|
|
reader: NewManualReader(),
|
|
inst: instruments[InstrumentKindUpDownCounter],
|
|
validate: assertSum[N](1, metricdata.CumulativeTemporality, false, [2]N{1, 4}),
|
|
},
|
|
{
|
|
name: "Default/Cumulative/ExplicitBucketHistogram",
|
|
reader: NewManualReader(),
|
|
inst: instruments[InstrumentKindHistogram],
|
|
validate: assertHist[N](metricdata.CumulativeTemporality),
|
|
},
|
|
{
|
|
name: "Default/Cumulative/PrecomputedSum/Monotonic",
|
|
reader: NewManualReader(),
|
|
inst: instruments[InstrumentKindObservableCounter],
|
|
validate: assertSum[N](1, metricdata.CumulativeTemporality, true, [2]N{1, 3}),
|
|
},
|
|
{
|
|
name: "Default/Cumulative/PrecomputedSum/NonMonotonic",
|
|
reader: NewManualReader(),
|
|
inst: instruments[InstrumentKindObservableUpDownCounter],
|
|
validate: assertSum[N](1, metricdata.CumulativeTemporality, false, [2]N{1, 3}),
|
|
},
|
|
{
|
|
name: "Default/Cumulative/Gauge",
|
|
reader: NewManualReader(),
|
|
inst: instruments[InstrumentKindObservableGauge],
|
|
validate: assertLastValue[N],
|
|
},
|
|
{
|
|
name: "Default/Cumulative/Sum/Monotonic",
|
|
reader: NewManualReader(),
|
|
inst: instruments[InstrumentKindCounter],
|
|
validate: assertSum[N](1, metricdata.CumulativeTemporality, true, [2]N{1, 4}),
|
|
},
|
|
{
|
|
name: "ViewHasPrecedence",
|
|
reader: NewManualReader(),
|
|
views: []View{changeAggView},
|
|
inst: instruments[InstrumentKindCounter],
|
|
validate: func(t *testing.T, meas []aggregate.Measure[N], comps []aggregate.ComputeAggregation, err error) {
|
|
t.Helper()
|
|
requireN[N](t, 1, meas, comps, err)
|
|
|
|
in, out := meas[0], comps[0]
|
|
in(context.Background(), 1, *attribute.EmptySet())
|
|
|
|
var got metricdata.Aggregation
|
|
assert.Equal(t, 1, out(&got), "1 data-point expected")
|
|
metricdatatest.AssertAggregationsEqual(t, metricdata.Histogram[N]{
|
|
Temporality: metricdata.CumulativeTemporality,
|
|
DataPoints: []metricdata.HistogramDataPoint[N]{{
|
|
Count: 1,
|
|
Bounds: []float64{0, 100},
|
|
BucketCounts: []uint64{0, 1, 0},
|
|
Sum: 1,
|
|
}},
|
|
}, got, metricdatatest.IgnoreTimestamp())
|
|
|
|
in(context.Background(), 1, *attribute.EmptySet())
|
|
|
|
assert.Equal(t, 1, out(&got), "1 data-point expected")
|
|
metricdatatest.AssertAggregationsEqual(t, metricdata.Histogram[N]{
|
|
Temporality: metricdata.CumulativeTemporality,
|
|
DataPoints: []metricdata.HistogramDataPoint[N]{{
|
|
Count: 2,
|
|
Bounds: []float64{0, 100},
|
|
BucketCounts: []uint64{0, 2, 0},
|
|
Sum: 2,
|
|
}},
|
|
}, got, metricdatatest.IgnoreTimestamp())
|
|
},
|
|
},
|
|
{
|
|
name: "MultipleViews",
|
|
reader: NewManualReader(),
|
|
views: []View{defaultView, renameView},
|
|
inst: instruments[InstrumentKindCounter],
|
|
validate: assertSum[N](2, metricdata.CumulativeTemporality, true, [2]N{1, 4}),
|
|
},
|
|
{
|
|
name: "Reader/Default/Cumulative/Sum/Monotonic",
|
|
reader: NewManualReader(WithAggregationSelector(func(ik InstrumentKind) Aggregation { return AggregationDefault{} })),
|
|
inst: instruments[InstrumentKindCounter],
|
|
validate: assertSum[N](1, metricdata.CumulativeTemporality, true, [2]N{1, 4}),
|
|
},
|
|
{
|
|
name: "Reader/Default/Cumulative/Sum/NonMonotonic",
|
|
reader: NewManualReader(WithAggregationSelector(func(ik InstrumentKind) Aggregation { return AggregationDefault{} })),
|
|
inst: instruments[InstrumentKindUpDownCounter],
|
|
validate: assertSum[N](1, metricdata.CumulativeTemporality, false, [2]N{1, 4}),
|
|
},
|
|
{
|
|
name: "Reader/Default/Cumulative/ExplicitBucketHistogram",
|
|
reader: NewManualReader(WithAggregationSelector(func(ik InstrumentKind) Aggregation { return AggregationDefault{} })),
|
|
inst: instruments[InstrumentKindHistogram],
|
|
validate: assertHist[N](metricdata.CumulativeTemporality),
|
|
},
|
|
{
|
|
name: "Reader/Default/Cumulative/PrecomputedSum/Monotonic",
|
|
reader: NewManualReader(WithAggregationSelector(func(ik InstrumentKind) Aggregation { return AggregationDefault{} })),
|
|
inst: instruments[InstrumentKindObservableCounter],
|
|
validate: assertSum[N](1, metricdata.CumulativeTemporality, true, [2]N{1, 3}),
|
|
},
|
|
{
|
|
name: "Reader/Default/Cumulative/PrecomputedSum/NonMonotonic",
|
|
reader: NewManualReader(WithAggregationSelector(func(ik InstrumentKind) Aggregation { return AggregationDefault{} })),
|
|
inst: instruments[InstrumentKindObservableUpDownCounter],
|
|
validate: assertSum[N](1, metricdata.CumulativeTemporality, false, [2]N{1, 3}),
|
|
},
|
|
{
|
|
name: "Reader/Default/Gauge",
|
|
reader: NewManualReader(WithAggregationSelector(func(ik InstrumentKind) Aggregation { return AggregationDefault{} })),
|
|
inst: instruments[InstrumentKindObservableGauge],
|
|
validate: assertLastValue[N],
|
|
},
|
|
{
|
|
name: "InvalidAggregation",
|
|
reader: NewManualReader(),
|
|
views: []View{invalidAggView},
|
|
inst: instruments[InstrumentKindCounter],
|
|
validate: func(t *testing.T, _ []aggregate.Measure[N], _ []aggregate.ComputeAggregation, err error) {
|
|
assert.ErrorIs(t, err, errCreatingAggregators)
|
|
},
|
|
},
|
|
}
|
|
for _, tt := range testcases {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
var c cache[string, instID]
|
|
p := newPipeline(nil, tt.reader, tt.views)
|
|
i := newInserter[N](p, &c)
|
|
readerAggregation := i.readerDefaultAggregation(tt.inst.Kind)
|
|
input, err := i.Instrument(tt.inst, readerAggregation)
|
|
var comps []aggregate.ComputeAggregation
|
|
for _, instSyncs := range p.aggregations {
|
|
for _, i := range instSyncs {
|
|
comps = append(comps, i.compAgg)
|
|
}
|
|
}
|
|
tt.validate(t, input, comps, err)
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestCreateAggregators(t *testing.T) {
|
|
t.Run("Int64", testCreateAggregators[int64])
|
|
t.Run("Float64", testCreateAggregators[float64])
|
|
}
|
|
|
|
func testInvalidInstrumentShouldPanic[N int64 | float64]() {
|
|
var c cache[string, instID]
|
|
i := newInserter[N](newPipeline(nil, NewManualReader(), []View{defaultView}), &c)
|
|
inst := Instrument{
|
|
Name: "foo",
|
|
Kind: InstrumentKind(255),
|
|
}
|
|
readerAggregation := i.readerDefaultAggregation(inst.Kind)
|
|
_, _ = i.Instrument(inst, readerAggregation)
|
|
}
|
|
|
|
func TestInvalidInstrumentShouldPanic(t *testing.T) {
|
|
assert.Panics(t, testInvalidInstrumentShouldPanic[int64])
|
|
assert.Panics(t, testInvalidInstrumentShouldPanic[float64])
|
|
}
|
|
|
|
func TestPipelinesAggregatorForEachReader(t *testing.T) {
|
|
r0, r1 := NewManualReader(), NewManualReader()
|
|
pipes := newPipelines(resource.Empty(), []Reader{r0, r1}, nil)
|
|
require.Len(t, pipes, 2, "created pipelines")
|
|
|
|
inst := Instrument{Name: "foo", Kind: InstrumentKindCounter}
|
|
var c cache[string, instID]
|
|
r := newResolver[int64](pipes, &c)
|
|
aggs, err := r.Aggregators(inst)
|
|
require.NoError(t, err, "resolved Aggregators error")
|
|
require.Len(t, aggs, 2, "instrument aggregators")
|
|
|
|
for i, p := range pipes {
|
|
var aggN int
|
|
for _, is := range p.aggregations {
|
|
aggN += len(is)
|
|
}
|
|
assert.Equalf(t, 1, aggN, "pipeline %d: number of instrumentSync", i)
|
|
}
|
|
}
|
|
|
|
func TestPipelineRegistryCreateAggregators(t *testing.T) {
|
|
renameView := NewView(Instrument{Name: "foo"}, Stream{Name: "bar"})
|
|
testRdr := NewManualReader()
|
|
testRdrHistogram := NewManualReader(WithAggregationSelector(func(ik InstrumentKind) Aggregation { return AggregationExplicitBucketHistogram{} }))
|
|
|
|
testCases := []struct {
|
|
name string
|
|
readers []Reader
|
|
views []View
|
|
inst Instrument
|
|
wantCount int
|
|
}{
|
|
{
|
|
name: "No views have no aggregators",
|
|
inst: Instrument{Name: "foo"},
|
|
},
|
|
{
|
|
name: "1 reader 1 view gets 1 aggregator",
|
|
inst: Instrument{Name: "foo"},
|
|
readers: []Reader{testRdr},
|
|
wantCount: 1,
|
|
},
|
|
{
|
|
name: "1 reader 2 views gets 2 aggregator",
|
|
inst: Instrument{Name: "foo"},
|
|
readers: []Reader{testRdr},
|
|
views: []View{defaultView, renameView},
|
|
wantCount: 2,
|
|
},
|
|
{
|
|
name: "2 readers 1 view each gets 2 aggregators",
|
|
inst: Instrument{Name: "foo"},
|
|
readers: []Reader{testRdr, testRdrHistogram},
|
|
wantCount: 2,
|
|
},
|
|
{
|
|
name: "2 reader 2 views each gets 4 aggregators",
|
|
inst: Instrument{Name: "foo"},
|
|
readers: []Reader{testRdr, testRdrHistogram},
|
|
views: []View{defaultView, renameView},
|
|
wantCount: 4,
|
|
},
|
|
{
|
|
name: "An instrument is duplicated in two views share the same aggregator",
|
|
inst: Instrument{Name: "foo"},
|
|
readers: []Reader{testRdr},
|
|
views: []View{defaultView, defaultView},
|
|
wantCount: 1,
|
|
},
|
|
}
|
|
|
|
for _, tt := range testCases {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
p := newPipelines(resource.Empty(), tt.readers, tt.views)
|
|
testPipelineRegistryResolveIntAggregators(t, p, tt.wantCount)
|
|
testPipelineRegistryResolveFloatAggregators(t, p, tt.wantCount)
|
|
testPipelineRegistryResolveIntHistogramAggregators(t, p, tt.wantCount)
|
|
testPipelineRegistryResolveFloatHistogramAggregators(t, p, tt.wantCount)
|
|
})
|
|
}
|
|
}
|
|
|
|
func testPipelineRegistryResolveIntAggregators(t *testing.T, p pipelines, wantCount int) {
|
|
inst := Instrument{Name: "foo", Kind: InstrumentKindCounter}
|
|
var c cache[string, instID]
|
|
r := newResolver[int64](p, &c)
|
|
aggs, err := r.Aggregators(inst)
|
|
assert.NoError(t, err)
|
|
|
|
require.Len(t, aggs, wantCount)
|
|
}
|
|
|
|
func testPipelineRegistryResolveFloatAggregators(t *testing.T, p pipelines, wantCount int) {
|
|
inst := Instrument{Name: "foo", Kind: InstrumentKindCounter}
|
|
var c cache[string, instID]
|
|
r := newResolver[float64](p, &c)
|
|
aggs, err := r.Aggregators(inst)
|
|
assert.NoError(t, err)
|
|
|
|
require.Len(t, aggs, wantCount)
|
|
}
|
|
|
|
func testPipelineRegistryResolveIntHistogramAggregators(t *testing.T, p pipelines, wantCount int) {
|
|
inst := Instrument{Name: "foo", Kind: InstrumentKindCounter}
|
|
var c cache[string, instID]
|
|
r := newResolver[int64](p, &c)
|
|
aggs, err := r.HistogramAggregators(inst, []float64{1, 2, 3})
|
|
assert.NoError(t, err)
|
|
|
|
require.Len(t, aggs, wantCount)
|
|
}
|
|
|
|
func testPipelineRegistryResolveFloatHistogramAggregators(t *testing.T, p pipelines, wantCount int) {
|
|
inst := Instrument{Name: "foo", Kind: InstrumentKindCounter}
|
|
var c cache[string, instID]
|
|
r := newResolver[float64](p, &c)
|
|
aggs, err := r.HistogramAggregators(inst, []float64{1, 2, 3})
|
|
assert.NoError(t, err)
|
|
|
|
require.Len(t, aggs, wantCount)
|
|
}
|
|
|
|
func TestPipelineRegistryResource(t *testing.T) {
|
|
v := NewView(Instrument{Name: "bar"}, Stream{Name: "foo"})
|
|
readers := []Reader{NewManualReader()}
|
|
views := []View{defaultView, v}
|
|
res := resource.NewSchemaless(attribute.String("key", "val"))
|
|
pipes := newPipelines(res, readers, views)
|
|
for _, p := range pipes {
|
|
assert.True(t, res.Equal(p.resource), "resource not set")
|
|
}
|
|
}
|
|
|
|
func TestPipelineRegistryCreateAggregatorsIncompatibleInstrument(t *testing.T) {
|
|
testRdrHistogram := NewManualReader(WithAggregationSelector(func(ik InstrumentKind) Aggregation { return AggregationSum{} }))
|
|
|
|
readers := []Reader{testRdrHistogram}
|
|
views := []View{defaultView}
|
|
p := newPipelines(resource.Empty(), readers, views)
|
|
inst := Instrument{Name: "foo", Kind: InstrumentKindObservableGauge}
|
|
|
|
var vc cache[string, instID]
|
|
ri := newResolver[int64](p, &vc)
|
|
intAggs, err := ri.Aggregators(inst)
|
|
assert.Error(t, err)
|
|
assert.Len(t, intAggs, 0)
|
|
|
|
rf := newResolver[float64](p, &vc)
|
|
floatAggs, err := rf.Aggregators(inst)
|
|
assert.Error(t, err)
|
|
assert.Len(t, floatAggs, 0)
|
|
|
|
intAggs, err = ri.HistogramAggregators(inst, []float64{1, 2, 3})
|
|
assert.Error(t, err)
|
|
assert.Len(t, intAggs, 0)
|
|
|
|
floatAggs, err = rf.HistogramAggregators(inst, []float64{1, 2, 3})
|
|
assert.Error(t, err)
|
|
assert.Len(t, floatAggs, 0)
|
|
}
|
|
|
|
type logCounter struct {
|
|
logr.LogSink
|
|
|
|
errN uint32
|
|
infoN uint32
|
|
}
|
|
|
|
func (l *logCounter) Info(level int, msg string, keysAndValues ...interface{}) {
|
|
atomic.AddUint32(&l.infoN, 1)
|
|
l.LogSink.Info(level, msg, keysAndValues...)
|
|
}
|
|
|
|
func (l *logCounter) InfoN() int {
|
|
return int(atomic.SwapUint32(&l.infoN, 0))
|
|
}
|
|
|
|
func (l *logCounter) Error(err error, msg string, keysAndValues ...interface{}) {
|
|
atomic.AddUint32(&l.errN, 1)
|
|
l.LogSink.Error(err, msg, keysAndValues...)
|
|
}
|
|
|
|
func (l *logCounter) ErrorN() int {
|
|
return int(atomic.SwapUint32(&l.errN, 0))
|
|
}
|
|
|
|
func TestResolveAggregatorsDuplicateErrors(t *testing.T) {
|
|
tLog := testr.NewWithOptions(t, testr.Options{Verbosity: 6})
|
|
l := &logCounter{LogSink: tLog.GetSink()}
|
|
otel.SetLogger(logr.New(l))
|
|
|
|
renameView := NewView(Instrument{Name: "bar"}, Stream{Name: "foo"})
|
|
readers := []Reader{NewManualReader()}
|
|
views := []View{defaultView, renameView}
|
|
|
|
fooInst := Instrument{Name: "foo", Kind: InstrumentKindCounter}
|
|
barInst := Instrument{Name: "bar", Kind: InstrumentKindCounter}
|
|
|
|
p := newPipelines(resource.Empty(), readers, views)
|
|
|
|
var vc cache[string, instID]
|
|
ri := newResolver[int64](p, &vc)
|
|
intAggs, err := ri.Aggregators(fooInst)
|
|
assert.NoError(t, err)
|
|
assert.Equal(t, 0, l.InfoN(), "no info logging should happen")
|
|
assert.Len(t, intAggs, 1)
|
|
|
|
// The Rename view should produce the same instrument without an error, the
|
|
// default view should also cause a new aggregator to be returned.
|
|
intAggs, err = ri.Aggregators(barInst)
|
|
assert.NoError(t, err)
|
|
assert.Equal(t, 0, l.InfoN(), "no info logging should happen")
|
|
assert.Len(t, intAggs, 2)
|
|
|
|
// Creating a float foo instrument should log a warning because there is an
|
|
// int foo instrument.
|
|
rf := newResolver[float64](p, &vc)
|
|
floatAggs, err := rf.Aggregators(fooInst)
|
|
assert.NoError(t, err)
|
|
assert.Equal(t, 1, l.InfoN(), "instrument conflict not logged")
|
|
assert.Len(t, floatAggs, 1)
|
|
|
|
fooInst = Instrument{Name: "foo-float", Kind: InstrumentKindCounter}
|
|
|
|
floatAggs, err = rf.Aggregators(fooInst)
|
|
assert.NoError(t, err)
|
|
assert.Equal(t, 0, l.InfoN(), "no info logging should happen")
|
|
assert.Len(t, floatAggs, 1)
|
|
|
|
floatAggs, err = rf.Aggregators(barInst)
|
|
assert.NoError(t, err)
|
|
// Both the rename and default view aggregators created above should now
|
|
// conflict. Therefore, 2 warning messages should be logged.
|
|
assert.Equal(t, 2, l.InfoN(), "instrument conflicts not logged")
|
|
assert.Len(t, floatAggs, 2)
|
|
}
|
|
|
|
func TestIsAggregatorCompatible(t *testing.T) {
|
|
var undefinedInstrument InstrumentKind
|
|
|
|
testCases := []struct {
|
|
name string
|
|
kind InstrumentKind
|
|
agg Aggregation
|
|
want error
|
|
}{
|
|
{
|
|
name: "SyncCounter and Drop",
|
|
kind: InstrumentKindCounter,
|
|
agg: AggregationDrop{},
|
|
},
|
|
{
|
|
name: "SyncCounter and LastValue",
|
|
kind: InstrumentKindCounter,
|
|
agg: AggregationLastValue{},
|
|
want: errIncompatibleAggregation,
|
|
},
|
|
{
|
|
name: "SyncCounter and Sum",
|
|
kind: InstrumentKindCounter,
|
|
agg: AggregationSum{},
|
|
},
|
|
{
|
|
name: "SyncCounter and ExplicitBucketHistogram",
|
|
kind: InstrumentKindCounter,
|
|
agg: AggregationExplicitBucketHistogram{},
|
|
},
|
|
{
|
|
name: "SyncCounter and ExponentialHistogram",
|
|
kind: InstrumentKindCounter,
|
|
agg: AggregationBase2ExponentialHistogram{},
|
|
},
|
|
{
|
|
name: "SyncUpDownCounter and Drop",
|
|
kind: InstrumentKindUpDownCounter,
|
|
agg: AggregationDrop{},
|
|
},
|
|
{
|
|
name: "SyncUpDownCounter and LastValue",
|
|
kind: InstrumentKindUpDownCounter,
|
|
agg: AggregationLastValue{},
|
|
want: errIncompatibleAggregation,
|
|
},
|
|
{
|
|
name: "SyncUpDownCounter and Sum",
|
|
kind: InstrumentKindUpDownCounter,
|
|
agg: AggregationSum{},
|
|
},
|
|
{
|
|
name: "SyncUpDownCounter and ExplicitBucketHistogram",
|
|
kind: InstrumentKindUpDownCounter,
|
|
agg: AggregationExplicitBucketHistogram{},
|
|
},
|
|
{
|
|
name: "SyncUpDownCounter and ExponentialHistogram",
|
|
kind: InstrumentKindUpDownCounter,
|
|
agg: AggregationBase2ExponentialHistogram{},
|
|
},
|
|
{
|
|
name: "SyncHistogram and Drop",
|
|
kind: InstrumentKindHistogram,
|
|
agg: AggregationDrop{},
|
|
},
|
|
{
|
|
name: "SyncHistogram and LastValue",
|
|
kind: InstrumentKindHistogram,
|
|
agg: AggregationLastValue{},
|
|
want: errIncompatibleAggregation,
|
|
},
|
|
{
|
|
name: "SyncHistogram and Sum",
|
|
kind: InstrumentKindHistogram,
|
|
agg: AggregationSum{},
|
|
},
|
|
{
|
|
name: "SyncHistogram and ExplicitBucketHistogram",
|
|
kind: InstrumentKindHistogram,
|
|
agg: AggregationExplicitBucketHistogram{},
|
|
},
|
|
{
|
|
name: "SyncHistogram and ExponentialHistogram",
|
|
kind: InstrumentKindHistogram,
|
|
agg: AggregationBase2ExponentialHistogram{},
|
|
},
|
|
{
|
|
name: "ObservableCounter and Drop",
|
|
kind: InstrumentKindObservableCounter,
|
|
agg: AggregationDrop{},
|
|
},
|
|
{
|
|
name: "ObservableCounter and LastValue",
|
|
kind: InstrumentKindObservableCounter,
|
|
agg: AggregationLastValue{},
|
|
want: errIncompatibleAggregation,
|
|
},
|
|
{
|
|
name: "ObservableCounter and Sum",
|
|
kind: InstrumentKindObservableCounter,
|
|
agg: AggregationSum{},
|
|
},
|
|
{
|
|
name: "ObservableCounter and ExplicitBucketHistogram",
|
|
kind: InstrumentKindObservableCounter,
|
|
agg: AggregationExplicitBucketHistogram{},
|
|
},
|
|
{
|
|
name: "ObservableCounter and ExponentialHistogram",
|
|
kind: InstrumentKindObservableCounter,
|
|
agg: AggregationBase2ExponentialHistogram{},
|
|
},
|
|
{
|
|
name: "ObservableUpDownCounter and Drop",
|
|
kind: InstrumentKindObservableUpDownCounter,
|
|
agg: AggregationDrop{},
|
|
},
|
|
{
|
|
name: "ObservableUpDownCounter and LastValue",
|
|
kind: InstrumentKindObservableUpDownCounter,
|
|
agg: AggregationLastValue{},
|
|
want: errIncompatibleAggregation,
|
|
},
|
|
{
|
|
name: "ObservableUpDownCounter and Sum",
|
|
kind: InstrumentKindObservableUpDownCounter,
|
|
agg: AggregationSum{},
|
|
},
|
|
{
|
|
name: "ObservableUpDownCounter and ExplicitBucketHistogram",
|
|
kind: InstrumentKindObservableUpDownCounter,
|
|
agg: AggregationExplicitBucketHistogram{},
|
|
},
|
|
{
|
|
name: "ObservableUpDownCounter and ExponentialHistogram",
|
|
kind: InstrumentKindObservableUpDownCounter,
|
|
agg: AggregationBase2ExponentialHistogram{},
|
|
},
|
|
{
|
|
name: "ObservableGauge and Drop",
|
|
kind: InstrumentKindObservableGauge,
|
|
agg: AggregationDrop{},
|
|
},
|
|
{
|
|
name: "ObservableGauge and LastValue{}",
|
|
kind: InstrumentKindObservableGauge,
|
|
agg: AggregationLastValue{},
|
|
},
|
|
{
|
|
name: "ObservableGauge and Sum",
|
|
kind: InstrumentKindObservableGauge,
|
|
agg: AggregationSum{},
|
|
want: errIncompatibleAggregation,
|
|
},
|
|
{
|
|
name: "ObservableGauge and ExplicitBucketHistogram",
|
|
kind: InstrumentKindObservableGauge,
|
|
agg: AggregationExplicitBucketHistogram{},
|
|
},
|
|
{
|
|
name: "ObservableGauge and ExponentialHistogram",
|
|
kind: InstrumentKindObservableGauge,
|
|
agg: AggregationBase2ExponentialHistogram{},
|
|
},
|
|
{
|
|
name: "unknown kind with Sum should error",
|
|
kind: undefinedInstrument,
|
|
agg: AggregationSum{},
|
|
want: errIncompatibleAggregation,
|
|
},
|
|
{
|
|
name: "unknown kind with LastValue should error",
|
|
kind: undefinedInstrument,
|
|
agg: AggregationLastValue{},
|
|
want: errIncompatibleAggregation,
|
|
},
|
|
{
|
|
name: "unknown kind with Histogram should error",
|
|
kind: undefinedInstrument,
|
|
agg: AggregationExplicitBucketHistogram{},
|
|
want: errIncompatibleAggregation,
|
|
},
|
|
{
|
|
name: "unknown kind with Histogram should error",
|
|
kind: undefinedInstrument,
|
|
agg: AggregationBase2ExponentialHistogram{},
|
|
want: errIncompatibleAggregation,
|
|
},
|
|
}
|
|
|
|
for _, tt := range testCases {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
err := isAggregatorCompatible(tt.kind, tt.agg)
|
|
assert.ErrorIs(t, err, tt.want)
|
|
})
|
|
}
|
|
}
|