1
0
mirror of https://github.com/open-telemetry/opentelemetry-go.git synced 2025-11-29 23:07:45 +02:00

Handle duplicate Aggregators and log instrument conflicts (#3251)

* Add the cache type

* Add cache unit tests

* Test cache concurrency

* Add the instrumentCache

* Use the instrumentCache to deduplicate creation

* Drop unique check from addAggregator

* Fix aggregatorCache* docs

* Update cachedAggregator and aggregator method docs

* Remove unnecessary type constraint

* Remove unused errAlreadyRegistered

* Rename to not shadow imports

* Add changes to changelog

* Fix changelog English

* Store resolvers in the meter instead of caches

* Test all Aggregator[N] impls are comparable

* Fix lint

* Add documentation that Aggregators need to be comparable

* Update sdk/metric/internal/aggregator.go

Co-authored-by: Anthony Mirabella <a9@aneurysm9.com>

* Update sdk/metric/instrument.go

Co-authored-by: Anthony Mirabella <a9@aneurysm9.com>

* Update sdk/metric/instrument.go

Co-authored-by: Anthony Mirabella <a9@aneurysm9.com>

* Update sdk/metric/internal/aggregator_test.go

Co-authored-by: Anthony Mirabella <a9@aneurysm9.com>

* Fix pipeline_test.go use of newInstrumentCache

Co-authored-by: Anthony Mirabella <a9@aneurysm9.com>
This commit is contained in:
Tyler Yahn
2022-10-11 12:41:47 -07:00
committed by GitHub
parent ffa94ca529
commit b5292b8459
10 changed files with 505 additions and 242 deletions

View File

@@ -15,11 +15,15 @@
package metric // import "go.opentelemetry.io/otel/sdk/metric"
import (
"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/metric/unit"
"go.opentelemetry.io/otel/sdk/metric/aggregation"
@@ -211,7 +215,8 @@ func testCreateAggregators[N int64 | float64](t *testing.T) {
}
for _, tt := range testcases {
t.Run(tt.name, func(t *testing.T) {
i := newInserter[N](newPipeline(nil, tt.reader, tt.views))
c := newInstrumentCache[N](nil, nil)
i := newInserter(newPipeline(nil, tt.reader, tt.views), c)
got, err := i.Instrument(tt.inst, unit.Dimensionless)
assert.ErrorIs(t, err, tt.wantErr)
require.Len(t, got, tt.wantLen)
@@ -223,7 +228,8 @@ func testCreateAggregators[N int64 | float64](t *testing.T) {
}
func testInvalidInstrumentShouldPanic[N int64 | float64]() {
i := newInserter[N](newPipeline(nil, NewManualReader(), []view.View{{}}))
c := newInstrumentCache[N](nil, nil)
i := newInserter(newPipeline(nil, NewManualReader(), []view.View{{}}), c)
inst := view.Instrument{
Name: "foo",
Kind: view.InstrumentKind(255),
@@ -334,7 +340,8 @@ func TestPipelineRegistryCreateAggregators(t *testing.T) {
func testPipelineRegistryResolveIntAggregators(t *testing.T, p pipelines, wantCount int) {
inst := view.Instrument{Name: "foo", Kind: view.SyncCounter}
r := newResolver[int64](p)
c := newInstrumentCache[int64](nil, nil)
r := newResolver(p, c)
aggs, err := r.Aggregators(inst, unit.Dimensionless)
assert.NoError(t, err)
@@ -344,7 +351,8 @@ func testPipelineRegistryResolveIntAggregators(t *testing.T, p pipelines, wantCo
func testPipelineRegistryResolveFloatAggregators(t *testing.T, p pipelines, wantCount int) {
inst := view.Instrument{Name: "foo", Kind: view.SyncCounter}
r := newResolver[float64](p)
c := newInstrumentCache[float64](nil, nil)
r := newResolver(p, c)
aggs, err := r.Aggregators(inst, unit.Dimensionless)
assert.NoError(t, err)
@@ -375,20 +383,40 @@ func TestPipelineRegistryCreateAggregatorsIncompatibleInstrument(t *testing.T) {
p := newPipelines(resource.Empty(), views)
inst := view.Instrument{Name: "foo", Kind: view.AsyncGauge}
ri := newResolver[int64](p)
vc := cache[string, instrumentID]{}
ri := newResolver(p, newInstrumentCache[int64](nil, &vc))
intAggs, err := ri.Aggregators(inst, unit.Dimensionless)
assert.Error(t, err)
assert.Len(t, intAggs, 0)
p = newPipelines(resource.Empty(), views)
rf := newResolver[float64](p)
rf := newResolver(p, newInstrumentCache[float64](nil, &vc))
floatAggs, err := rf.Aggregators(inst, unit.Dimensionless)
assert.Error(t, err)
assert.Len(t, floatAggs, 0)
}
func TestPipelineRegistryCreateAggregatorsDuplicateErrors(t *testing.T) {
type logCounter struct {
logr.LogSink
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 TestResolveAggregatorsDuplicateErrors(t *testing.T) {
tLog := testr.NewWithOptions(t, testr.Options{Verbosity: 6})
l := &logCounter{LogSink: tLog.GetSink()}
otel.SetLogger(logr.New(l))
renameView, _ := view.New(
view.MatchInstrumentName("bar"),
view.WithRename("foo"),
@@ -405,29 +433,40 @@ func TestPipelineRegistryCreateAggregatorsDuplicateErrors(t *testing.T) {
p := newPipelines(resource.Empty(), views)
ri := newResolver[int64](p)
vc := cache[string, instrumentID]{}
ri := newResolver(p, newInstrumentCache[int64](nil, &vc))
intAggs, err := ri.Aggregators(fooInst, unit.Dimensionless)
assert.NoError(t, err)
assert.Equal(t, 0, l.InfoN(), "no info logging should happen")
assert.Len(t, intAggs, 1)
// The Rename view should error, because it creates a foo instrument.
// 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, unit.Dimensionless)
assert.Error(t, err)
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 error because there is an int foo instrument.
rf := newResolver[float64](p)
// Creating a float foo instrument should log a warning because there is an
// int foo instrument.
rf := newResolver(p, newInstrumentCache[float64](nil, &vc))
floatAggs, err := rf.Aggregators(fooInst, unit.Dimensionless)
assert.Error(t, err)
assert.NoError(t, err)
assert.Equal(t, 1, l.InfoN(), "instrument conflict not logged")
assert.Len(t, floatAggs, 1)
fooInst = view.Instrument{Name: "foo-float", Kind: view.SyncCounter}
_, err = rf.Aggregators(fooInst, unit.Dimensionless)
floatAggs, err = rf.Aggregators(fooInst, unit.Dimensionless)
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, unit.Dimensionless)
assert.Error(t, err)
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)
}