mirror of
https://github.com/open-telemetry/opentelemetry-go.git
synced 2025-01-03 22:52:30 +02:00
Return noop meters once the provider has been shutdown (#4154)
This commit is contained in:
parent
b4faa3dfdb
commit
be82610b44
@ -28,6 +28,7 @@ See our [versioning policy](VERSIONING.md) for more information about these stab
|
||||
### Changed
|
||||
|
||||
- Use `strings.Cut()` instead of `string.SplitN()` for better readability and memory use. (#4049)
|
||||
- `MeterProvider` returns noop meters once it has been shutdown. (#4154)
|
||||
|
||||
### Removed
|
||||
|
||||
|
@ -16,10 +16,12 @@ package metric // import "go.opentelemetry.io/otel/sdk/metric"
|
||||
|
||||
import (
|
||||
"context"
|
||||
"sync/atomic"
|
||||
|
||||
"go.opentelemetry.io/otel/internal/global"
|
||||
"go.opentelemetry.io/otel/metric"
|
||||
"go.opentelemetry.io/otel/metric/embedded"
|
||||
"go.opentelemetry.io/otel/metric/noop"
|
||||
"go.opentelemetry.io/otel/sdk/instrumentation"
|
||||
)
|
||||
|
||||
@ -34,6 +36,7 @@ type MeterProvider struct {
|
||||
meters cache[instrumentation.Scope, *meter]
|
||||
|
||||
forceFlush, shutdown func(context.Context) error
|
||||
stopped atomic.Bool
|
||||
}
|
||||
|
||||
// Compile-time check MeterProvider implements metric.MeterProvider.
|
||||
@ -70,6 +73,10 @@ func (mp *MeterProvider) Meter(name string, options ...metric.MeterOption) metri
|
||||
global.Warn("Invalid Meter name.", "name", name)
|
||||
}
|
||||
|
||||
if mp.stopped.Load() {
|
||||
return noop.Meter{}
|
||||
}
|
||||
|
||||
c := metric.NewMeterConfig(options...)
|
||||
s := instrumentation.Scope{
|
||||
Name: name,
|
||||
@ -113,6 +120,15 @@ func (mp *MeterProvider) ForceFlush(ctx context.Context) error {
|
||||
//
|
||||
// This method is safe to call concurrently.
|
||||
func (mp *MeterProvider) Shutdown(ctx context.Context) error {
|
||||
// Even though it may seem like there is a synchronization issue between the
|
||||
// call to `Store` and checking `shutdown`, the Go concurrency model ensures
|
||||
// that is not the case, as all the atomic operations executed in a program
|
||||
// behave as though executed in some sequentially consistent order. This
|
||||
// definition provides the same semantics as C++'s sequentially consistent
|
||||
// atomics and Java's volatile variables.
|
||||
// See https://go.dev/ref/mem#atomic and https://pkg.go.dev/sync/atomic.
|
||||
|
||||
mp.stopped.Store(true)
|
||||
if mp.shutdown != nil {
|
||||
return mp.shutdown(ctx)
|
||||
}
|
||||
|
@ -22,8 +22,10 @@ import (
|
||||
|
||||
"github.com/go-logr/logr/funcr"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"go.opentelemetry.io/otel"
|
||||
"go.opentelemetry.io/otel/metric/noop"
|
||||
)
|
||||
|
||||
func TestMeterConcurrentSafe(t *testing.T) {
|
||||
@ -57,6 +59,17 @@ func TestShutdownConcurrentSafe(t *testing.T) {
|
||||
_ = mp.Shutdown(context.Background())
|
||||
}
|
||||
|
||||
func TestMeterAndShutdownConcurrentSafe(t *testing.T) {
|
||||
const name = "TestMeterAndShutdownConcurrentSafe meter"
|
||||
mp := NewMeterProvider()
|
||||
|
||||
go func() {
|
||||
_ = mp.Shutdown(context.Background())
|
||||
}()
|
||||
|
||||
_ = mp.Meter(name)
|
||||
}
|
||||
|
||||
func TestMeterDoesNotPanicForEmptyMeterProvider(t *testing.T) {
|
||||
mp := MeterProvider{}
|
||||
assert.NotPanics(t, func() { _ = mp.Meter("") })
|
||||
@ -93,3 +106,17 @@ func TestEmptyMeterName(t *testing.T) {
|
||||
|
||||
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(context.Background()))
|
||||
|
||||
m = mp.Meter("")
|
||||
_, ok = m.(noop.Meter)
|
||||
assert.Truef(t, ok, "Meter from shutdown MeterProvider is not NoOp: %T", m)
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user