You've already forked opentelemetry-go
mirror of
https://github.com/open-telemetry/opentelemetry-go.git
synced 2025-11-23 22:34:47 +02:00
Based on the Go version we currently use, the dependency already supports 1.24+, which allows using `t.Context()` and `b.Context()` in unit tests and benchmarks respectively. - Enable `context-background` and `context-todo` in [`usetesting`](https://golangci-lint.run/docs/linters/configuration/#usetesting) - Adjust the code to support linter detection --------- Co-authored-by: Tyler Yahn <MrAlias@users.noreply.github.com> Co-authored-by: Tyler Yahn <codingalias@gmail.com> Co-authored-by: Damien Mathieu <42@dmathieu.com>
247 lines
5.8 KiB
Go
247 lines
5.8 KiB
Go
// Copyright The OpenTelemetry Authors
|
|
// SPDX-License-Identifier: Apache-2.0
|
|
|
|
package metric
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"strings"
|
|
"testing"
|
|
|
|
"github.com/go-logr/logr/funcr"
|
|
"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"
|
|
api "go.opentelemetry.io/otel/metric"
|
|
"go.opentelemetry.io/otel/metric/noop"
|
|
"go.opentelemetry.io/otel/sdk/metric/metricdata"
|
|
)
|
|
|
|
func TestMeterConcurrentSafe(*testing.T) {
|
|
const name = "TestMeterConcurrentSafe meter"
|
|
mp := NewMeterProvider()
|
|
|
|
done := make(chan struct{})
|
|
go func() {
|
|
defer close(done)
|
|
_ = mp.Meter(name)
|
|
}()
|
|
|
|
_ = mp.Meter(name)
|
|
<-done
|
|
}
|
|
|
|
func TestForceFlushConcurrentSafe(t *testing.T) {
|
|
mp := NewMeterProvider()
|
|
|
|
done := make(chan struct{})
|
|
go func() {
|
|
defer close(done)
|
|
_ = mp.ForceFlush(t.Context())
|
|
}()
|
|
|
|
_ = mp.ForceFlush(t.Context())
|
|
<-done
|
|
}
|
|
|
|
func TestShutdownConcurrentSafe(t *testing.T) {
|
|
mp := NewMeterProvider()
|
|
|
|
done := make(chan struct{})
|
|
go func() {
|
|
defer close(done)
|
|
_ = mp.Shutdown(t.Context())
|
|
}()
|
|
|
|
_ = mp.Shutdown(t.Context())
|
|
<-done
|
|
}
|
|
|
|
func TestMeterAndShutdownConcurrentSafe(t *testing.T) {
|
|
const name = "TestMeterAndShutdownConcurrentSafe meter"
|
|
mp := NewMeterProvider()
|
|
|
|
done := make(chan struct{})
|
|
go func() {
|
|
defer close(done)
|
|
_ = mp.Shutdown(t.Context())
|
|
}()
|
|
|
|
_ = mp.Meter(name)
|
|
<-done
|
|
}
|
|
|
|
func TestMeterDoesNotPanicForEmptyMeterProvider(t *testing.T) {
|
|
mp := MeterProvider{}
|
|
assert.NotPanics(t, func() { _ = mp.Meter("") })
|
|
}
|
|
|
|
func TestForceFlushDoesNotPanicForEmptyMeterProvider(t *testing.T) {
|
|
mp := MeterProvider{}
|
|
assert.NotPanics(t, func() { _ = mp.ForceFlush(t.Context()) })
|
|
}
|
|
|
|
func TestShutdownDoesNotPanicForEmptyMeterProvider(t *testing.T) {
|
|
mp := MeterProvider{}
|
|
assert.NotPanics(t, func() { _ = mp.Shutdown(t.Context()) })
|
|
}
|
|
|
|
func TestMeterProviderReturnsSameMeter(t *testing.T) {
|
|
mp := MeterProvider{}
|
|
mtr := mp.Meter("")
|
|
|
|
assert.Same(t, mtr, mp.Meter(""))
|
|
assert.NotSame(t, mtr, mp.Meter("diff"))
|
|
assert.NotSame(t, mtr, mp.Meter("", api.WithInstrumentationAttributes(attribute.String("k", "v"))))
|
|
}
|
|
|
|
func TestEmptyMeterName(t *testing.T) {
|
|
var buf strings.Builder
|
|
warnLevel := 1
|
|
l := funcr.New(func(prefix, args string) {
|
|
_, _ = buf.WriteString(fmt.Sprint(prefix, args))
|
|
}, funcr.Options{Verbosity: warnLevel})
|
|
otel.SetLogger(l)
|
|
mp := NewMeterProvider()
|
|
|
|
mp.Meter("")
|
|
|
|
assert.Contains(t, buf.String(), `"level"=1 "msg"="Invalid Meter name." "name"=""`)
|
|
}
|
|
|
|
func TestMeterProviderReturnsNoopMeterAfterShutdown(t *testing.T) {
|
|
mp := NewMeterProvider()
|
|
|
|
m := mp.Meter("")
|
|
_, ok := m.(noop.Meter)
|
|
assert.False(t, ok, "Meter from running MeterProvider is NoOp")
|
|
|
|
require.NoError(t, mp.Shutdown(t.Context()))
|
|
|
|
m = mp.Meter("")
|
|
_, ok = m.(noop.Meter)
|
|
assert.Truef(t, ok, "Meter from shutdown MeterProvider is not NoOp: %T", m)
|
|
}
|
|
|
|
func TestMeterProviderMixingOnRegisterErrors(t *testing.T) {
|
|
otel.SetLogger(testr.New(t))
|
|
|
|
rdr0 := NewManualReader()
|
|
mp0 := NewMeterProvider(WithReader(rdr0))
|
|
|
|
rdr1 := NewManualReader()
|
|
mp1 := NewMeterProvider(WithReader(rdr1))
|
|
|
|
// Meters with the same scope but different MeterProviders.
|
|
m0 := mp0.Meter("TestMeterProviderMixingOnRegisterErrors")
|
|
m1 := mp1.Meter("TestMeterProviderMixingOnRegisterErrors")
|
|
|
|
m0Gauge, err := m0.Float64ObservableGauge("float64Gauge")
|
|
require.NoError(t, err)
|
|
|
|
m1Gauge, err := m1.Int64ObservableGauge("int64Gauge")
|
|
require.NoError(t, err)
|
|
|
|
_, err = m0.RegisterCallback(
|
|
func(_ context.Context, o api.Observer) error {
|
|
o.ObserveFloat64(m0Gauge, 2)
|
|
// Observe an instrument from a different MeterProvider.
|
|
o.ObserveInt64(m1Gauge, 1)
|
|
|
|
return nil
|
|
},
|
|
m0Gauge, m1Gauge,
|
|
)
|
|
assert.Error(
|
|
t,
|
|
err,
|
|
"Instrument registered with Meter from different MeterProvider",
|
|
)
|
|
|
|
var data metricdata.ResourceMetrics
|
|
_ = rdr0.Collect(t.Context(), &data)
|
|
// Only the metrics from mp0 should be produced.
|
|
assert.Len(t, data.ScopeMetrics, 1)
|
|
|
|
err = rdr1.Collect(t.Context(), &data)
|
|
assert.NoError(t, err, "Errored when collect should be a noop")
|
|
assert.Empty(
|
|
t, data.ScopeMetrics,
|
|
"Metrics produced for instrument collected by different MeterProvider",
|
|
)
|
|
}
|
|
|
|
func TestMeterProviderCardinalityLimit(t *testing.T) {
|
|
const uniqueAttributesCount = 10
|
|
|
|
tests := []struct {
|
|
name string
|
|
options []Option
|
|
wantDataPoints int
|
|
}{
|
|
{
|
|
name: "no limit (default)",
|
|
options: nil,
|
|
wantDataPoints: uniqueAttributesCount,
|
|
},
|
|
{
|
|
name: "no limit (limit=0)",
|
|
options: []Option{WithCardinalityLimit(0)},
|
|
wantDataPoints: uniqueAttributesCount,
|
|
},
|
|
{
|
|
name: "no limit (negative)",
|
|
options: []Option{WithCardinalityLimit(-5)},
|
|
wantDataPoints: uniqueAttributesCount,
|
|
},
|
|
{
|
|
name: "limit=5",
|
|
options: []Option{WithCardinalityLimit(5)},
|
|
wantDataPoints: 5,
|
|
},
|
|
}
|
|
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
reader := NewManualReader()
|
|
|
|
opts := append(tt.options, WithReader(reader))
|
|
mp := NewMeterProvider(opts...)
|
|
|
|
meter := mp.Meter("test-meter")
|
|
counter, err := meter.Int64Counter("metric")
|
|
require.NoError(t, err, "failed to create counter")
|
|
|
|
for i := range uniqueAttributesCount {
|
|
counter.Add(
|
|
t.Context(),
|
|
1,
|
|
api.WithAttributes(attribute.Int("key", i)),
|
|
)
|
|
}
|
|
|
|
var rm metricdata.ResourceMetrics
|
|
err = reader.Collect(t.Context(), &rm)
|
|
require.NoError(t, err, "failed to collect metrics")
|
|
|
|
require.Len(t, rm.ScopeMetrics, 1, "expected 1 ScopeMetrics")
|
|
require.Len(t, rm.ScopeMetrics[0].Metrics, 1, "expected 1 Metric")
|
|
|
|
data := rm.ScopeMetrics[0].Metrics[0].Data
|
|
require.IsType(t, metricdata.Sum[int64]{}, data, "expected metricdata.Sum[int64]")
|
|
|
|
sumData := data.(metricdata.Sum[int64])
|
|
assert.Len(
|
|
t,
|
|
sumData.DataPoints,
|
|
tt.wantDataPoints,
|
|
"unexpected number of data points",
|
|
)
|
|
})
|
|
}
|
|
}
|