You've already forked opentelemetry-go
mirror of
https://github.com/open-telemetry/opentelemetry-go.git
synced 2025-07-17 01:12:45 +02:00
Allow multi-instrument callbacks to be unregistered (#3522)
* Update Meter RegisterCallback method Return a Registration from the method that can be used by the caller to unregister their callback. Update documentation of the method to better explain expectations of use and implementation. * Update noop impl * Update global impl * Test global Unregister concurrent safe * Use a map to track reg in global impl * Update sdk impl * Use a list for global impl * Fix prom example * Lint metric/meter.go * Fix metric example * Placeholder for changelog * Update PR number in changelog * Update sdk/metric/pipeline.go Co-authored-by: Aaron Clawson <3766680+MadVikingGod@users.noreply.github.com> * Add test unregistered callback is not called Co-authored-by: Aaron Clawson <3766680+MadVikingGod@users.noreply.github.com>
This commit is contained in:
10
CHANGELOG.md
10
CHANGELOG.md
@ -8,14 +8,16 @@ This project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.htm
|
|||||||
|
|
||||||
## [Unreleased]
|
## [Unreleased]
|
||||||
|
|
||||||
|
### Added
|
||||||
|
|
||||||
|
- Return a `Registration` from the `RegisterCallback` method of a `Meter` in the `go.opentelemetry.io/otel/metric` package.
|
||||||
|
This `Registration` can be used to unregister callbacks. (#3522)
|
||||||
|
- Add `Producer` interface and `Reader.RegisterProducer(Producer)` to `go.opentelemetry.io/otel/sdk/metric` to enable external metric Producers. (#3524)
|
||||||
|
|
||||||
### Removed
|
### Removed
|
||||||
|
|
||||||
- The deprecated `go.opentelemetry.io/otel/sdk/metric/view` package is removed. (#3520)
|
- The deprecated `go.opentelemetry.io/otel/sdk/metric/view` package is removed. (#3520)
|
||||||
|
|
||||||
### Added
|
|
||||||
|
|
||||||
- Add `Producer` interface and `Reader.RegisterProducer(Producer)` to `go.opentelemetry.io/otel/sdk/metric` to enable external metric Producers. (#3524)
|
|
||||||
|
|
||||||
### Changed
|
### Changed
|
||||||
|
|
||||||
- Global error handler uses an atomic value instead of a mutex. (#3543)
|
- Global error handler uses an atomic value instead of a mutex. (#3543)
|
||||||
|
@ -68,7 +68,7 @@ func main() {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatal(err)
|
log.Fatal(err)
|
||||||
}
|
}
|
||||||
err = meter.RegisterCallback([]instrument.Asynchronous{gauge}, func(ctx context.Context) {
|
_, err = meter.RegisterCallback([]instrument.Asynchronous{gauge}, func(ctx context.Context) {
|
||||||
n := -10. + rand.Float64()*(90.) // [-10, 100)
|
n := -10. + rand.Float64()*(90.) // [-10, 100)
|
||||||
gauge.Observe(ctx, n, attrs...)
|
gauge.Observe(ctx, n, attrs...)
|
||||||
})
|
})
|
||||||
|
@ -61,7 +61,7 @@ func ExampleMeter_asynchronous_single() {
|
|||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
err = meter.RegisterCallback([]instrument.Asynchronous{memoryUsage},
|
_, err = meter.RegisterCallback([]instrument.Asynchronous{memoryUsage},
|
||||||
func(ctx context.Context) {
|
func(ctx context.Context) {
|
||||||
// instrument.WithCallbackFunc(func(ctx context.Context) {
|
// instrument.WithCallbackFunc(func(ctx context.Context) {
|
||||||
//Do Work to get the real memoryUsage
|
//Do Work to get the real memoryUsage
|
||||||
@ -86,7 +86,7 @@ func ExampleMeter_asynchronous_multiple() {
|
|||||||
gcCount, _ := meter.AsyncInt64().Counter("gcCount")
|
gcCount, _ := meter.AsyncInt64().Counter("gcCount")
|
||||||
gcPause, _ := meter.SyncFloat64().Histogram("gcPause")
|
gcPause, _ := meter.SyncFloat64().Histogram("gcPause")
|
||||||
|
|
||||||
err := meter.RegisterCallback([]instrument.Asynchronous{
|
_, err := meter.RegisterCallback([]instrument.Asynchronous{
|
||||||
heapAlloc,
|
heapAlloc,
|
||||||
gcCount,
|
gcCount,
|
||||||
},
|
},
|
||||||
|
@ -15,6 +15,7 @@
|
|||||||
package global // import "go.opentelemetry.io/otel/metric/internal/global"
|
package global // import "go.opentelemetry.io/otel/metric/internal/global"
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"container/list"
|
||||||
"context"
|
"context"
|
||||||
"sync"
|
"sync"
|
||||||
"sync/atomic"
|
"sync/atomic"
|
||||||
@ -109,7 +110,8 @@ type meter struct {
|
|||||||
|
|
||||||
mtx sync.Mutex
|
mtx sync.Mutex
|
||||||
instruments []delegatedInstrument
|
instruments []delegatedInstrument
|
||||||
callbacks []delegatedCallback
|
|
||||||
|
registry list.List
|
||||||
|
|
||||||
delegate atomic.Value // metric.Meter
|
delegate atomic.Value // metric.Meter
|
||||||
}
|
}
|
||||||
@ -135,12 +137,14 @@ func (m *meter) setDelegate(provider metric.MeterProvider) {
|
|||||||
inst.setDelegate(meter)
|
inst.setDelegate(meter)
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, callback := range m.callbacks {
|
for e := m.registry.Front(); e != nil; e = e.Next() {
|
||||||
callback.setDelegate(meter)
|
r := e.Value.(*registration)
|
||||||
|
r.setDelegate(meter)
|
||||||
|
m.registry.Remove(e)
|
||||||
}
|
}
|
||||||
|
|
||||||
m.instruments = nil
|
m.instruments = nil
|
||||||
m.callbacks = nil
|
m.registry.Init()
|
||||||
}
|
}
|
||||||
|
|
||||||
// AsyncInt64 is the namespace for the Asynchronous Integer instruments.
|
// AsyncInt64 is the namespace for the Asynchronous Integer instruments.
|
||||||
@ -167,21 +171,25 @@ func (m *meter) AsyncFloat64() asyncfloat64.InstrumentProvider {
|
|||||||
//
|
//
|
||||||
// It is only valid to call Observe within the scope of the passed function,
|
// It is only valid to call Observe within the scope of the passed function,
|
||||||
// and only on the instruments that were registered with this call.
|
// and only on the instruments that were registered with this call.
|
||||||
func (m *meter) RegisterCallback(insts []instrument.Asynchronous, function func(context.Context)) error {
|
func (m *meter) RegisterCallback(insts []instrument.Asynchronous, f func(context.Context)) (metric.Registration, error) {
|
||||||
if del, ok := m.delegate.Load().(metric.Meter); ok {
|
if del, ok := m.delegate.Load().(metric.Meter); ok {
|
||||||
insts = unwrapInstruments(insts)
|
insts = unwrapInstruments(insts)
|
||||||
return del.RegisterCallback(insts, function)
|
return del.RegisterCallback(insts, f)
|
||||||
}
|
}
|
||||||
|
|
||||||
m.mtx.Lock()
|
m.mtx.Lock()
|
||||||
defer m.mtx.Unlock()
|
defer m.mtx.Unlock()
|
||||||
m.callbacks = append(m.callbacks, delegatedCallback{
|
|
||||||
instruments: insts,
|
|
||||||
function: function,
|
|
||||||
})
|
|
||||||
|
|
||||||
|
reg := ®istration{instruments: insts, function: f}
|
||||||
|
e := m.registry.PushBack(reg)
|
||||||
|
reg.unreg = func() error {
|
||||||
|
m.mtx.Lock()
|
||||||
|
_ = m.registry.Remove(e)
|
||||||
|
m.mtx.Unlock()
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
return reg, nil
|
||||||
|
}
|
||||||
|
|
||||||
type wrapped interface {
|
type wrapped interface {
|
||||||
unwrap() instrument.Asynchronous
|
unwrap() instrument.Asynchronous
|
||||||
@ -217,17 +225,44 @@ func (m *meter) SyncFloat64() syncfloat64.InstrumentProvider {
|
|||||||
return (*sfInstProvider)(m)
|
return (*sfInstProvider)(m)
|
||||||
}
|
}
|
||||||
|
|
||||||
type delegatedCallback struct {
|
type registration struct {
|
||||||
instruments []instrument.Asynchronous
|
instruments []instrument.Asynchronous
|
||||||
function func(context.Context)
|
function func(context.Context)
|
||||||
|
|
||||||
|
unreg func() error
|
||||||
|
unregMu sync.Mutex
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *delegatedCallback) setDelegate(m metric.Meter) {
|
func (c *registration) setDelegate(m metric.Meter) {
|
||||||
insts := unwrapInstruments(c.instruments)
|
insts := unwrapInstruments(c.instruments)
|
||||||
err := m.RegisterCallback(insts, c.function)
|
|
||||||
|
c.unregMu.Lock()
|
||||||
|
defer c.unregMu.Unlock()
|
||||||
|
|
||||||
|
if c.unreg == nil {
|
||||||
|
// Unregister already called.
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
reg, err := m.RegisterCallback(insts, c.function)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
otel.Handle(err)
|
otel.Handle(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
c.unreg = reg.Unregister
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *registration) Unregister() error {
|
||||||
|
c.unregMu.Lock()
|
||||||
|
defer c.unregMu.Unlock()
|
||||||
|
if c.unreg == nil {
|
||||||
|
// Unregister already called.
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
var err error
|
||||||
|
err, c.unreg = c.unreg(), nil
|
||||||
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
type afInstProvider meter
|
type afInstProvider meter
|
||||||
|
@ -68,7 +68,7 @@ func TestMeterRace(t *testing.T) {
|
|||||||
_, _ = mtr.SyncInt64().Counter(name)
|
_, _ = mtr.SyncInt64().Counter(name)
|
||||||
_, _ = mtr.SyncInt64().UpDownCounter(name)
|
_, _ = mtr.SyncInt64().UpDownCounter(name)
|
||||||
_, _ = mtr.SyncInt64().Histogram(name)
|
_, _ = mtr.SyncInt64().Histogram(name)
|
||||||
_ = mtr.RegisterCallback(nil, func(ctx context.Context) {})
|
_, _ = mtr.RegisterCallback(nil, func(ctx context.Context) {})
|
||||||
if !once {
|
if !once {
|
||||||
wg.Done()
|
wg.Done()
|
||||||
once = true
|
once = true
|
||||||
@ -86,6 +86,35 @@ func TestMeterRace(t *testing.T) {
|
|||||||
close(finish)
|
close(finish)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestUnregisterRace(t *testing.T) {
|
||||||
|
mtr := &meter{}
|
||||||
|
reg, err := mtr.RegisterCallback(nil, func(ctx context.Context) {})
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
wg := &sync.WaitGroup{}
|
||||||
|
wg.Add(1)
|
||||||
|
finish := make(chan struct{})
|
||||||
|
go func() {
|
||||||
|
for i, once := 0, false; ; i++ {
|
||||||
|
_ = reg.Unregister()
|
||||||
|
if !once {
|
||||||
|
wg.Done()
|
||||||
|
once = true
|
||||||
|
}
|
||||||
|
select {
|
||||||
|
case <-finish:
|
||||||
|
return
|
||||||
|
default:
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
_ = reg.Unregister()
|
||||||
|
|
||||||
|
wg.Wait()
|
||||||
|
mtr.setDelegate(metric.NewNoopMeterProvider())
|
||||||
|
close(finish)
|
||||||
|
}
|
||||||
|
|
||||||
func testSetupAllInstrumentTypes(t *testing.T, m metric.Meter) (syncfloat64.Counter, asyncfloat64.Counter) {
|
func testSetupAllInstrumentTypes(t *testing.T, m metric.Meter) (syncfloat64.Counter, asyncfloat64.Counter) {
|
||||||
afcounter, err := m.AsyncFloat64().Counter("test_Async_Counter")
|
afcounter, err := m.AsyncFloat64().Counter("test_Async_Counter")
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
@ -101,9 +130,10 @@ func testSetupAllInstrumentTypes(t *testing.T, m metric.Meter) (syncfloat64.Coun
|
|||||||
_, err = m.AsyncInt64().Gauge("test_Async_Gauge")
|
_, err = m.AsyncInt64().Gauge("test_Async_Gauge")
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
|
|
||||||
require.NoError(t, m.RegisterCallback([]instrument.Asynchronous{afcounter}, func(ctx context.Context) {
|
_, err = m.RegisterCallback([]instrument.Asynchronous{afcounter}, func(ctx context.Context) {
|
||||||
afcounter.Observe(ctx, 3)
|
afcounter.Observe(ctx, 3)
|
||||||
}))
|
})
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
sfcounter, err := m.SyncFloat64().Counter("test_Async_Counter")
|
sfcounter, err := m.SyncFloat64().Counter("test_Async_Counter")
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
@ -257,3 +287,51 @@ func TestMeterDefersDelegations(t *testing.T) {
|
|||||||
assert.IsType(t, &afCounter{}, actr)
|
assert.IsType(t, &afCounter{}, actr)
|
||||||
assert.Equal(t, 1, mp.count)
|
assert.Equal(t, 1, mp.count)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestRegistrationDelegation(t *testing.T) {
|
||||||
|
// globalMeterProvider := otel.GetMeterProvider
|
||||||
|
globalMeterProvider := &meterProvider{}
|
||||||
|
|
||||||
|
m := globalMeterProvider.Meter("go.opentelemetry.io/otel/metric/internal/global/meter_test")
|
||||||
|
require.IsType(t, &meter{}, m)
|
||||||
|
mImpl := m.(*meter)
|
||||||
|
|
||||||
|
actr, err := m.AsyncFloat64().Counter("test_Async_Counter")
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
var called0 bool
|
||||||
|
reg0, err := m.RegisterCallback([]instrument.Asynchronous{actr}, func(context.Context) {
|
||||||
|
called0 = true
|
||||||
|
})
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.Equal(t, 1, mImpl.registry.Len(), "callback not registered")
|
||||||
|
// This means reg0 should not be delegated.
|
||||||
|
assert.NoError(t, reg0.Unregister())
|
||||||
|
assert.Equal(t, 0, mImpl.registry.Len(), "callback not unregistered")
|
||||||
|
|
||||||
|
var called1 bool
|
||||||
|
reg1, err := m.RegisterCallback([]instrument.Asynchronous{actr}, func(context.Context) {
|
||||||
|
called1 = true
|
||||||
|
})
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.Equal(t, 1, mImpl.registry.Len(), "second callback not registered")
|
||||||
|
|
||||||
|
mp := &testMeterProvider{}
|
||||||
|
|
||||||
|
// otel.SetMeterProvider(mp)
|
||||||
|
globalMeterProvider.setDelegate(mp)
|
||||||
|
|
||||||
|
testCollect(t, m) // This is a hacky way to emulate a read from an exporter
|
||||||
|
require.False(t, called0, "pre-delegation unregistered callback called")
|
||||||
|
require.True(t, called1, "callback not called")
|
||||||
|
|
||||||
|
called1 = false
|
||||||
|
assert.NoError(t, reg1.Unregister(), "unregister second callback")
|
||||||
|
|
||||||
|
testCollect(t, m) // This is a hacky way to emulate a read from an exporter
|
||||||
|
assert.False(t, called1, "unregistered callback called")
|
||||||
|
|
||||||
|
assert.NotPanics(t, func() {
|
||||||
|
assert.NoError(t, reg1.Unregister(), "duplicate unregister calls")
|
||||||
|
})
|
||||||
|
}
|
||||||
|
@ -64,8 +64,21 @@ func (m *testMeter) AsyncFloat64() asyncfloat64.InstrumentProvider {
|
|||||||
//
|
//
|
||||||
// It is only valid to call Observe within the scope of the passed function,
|
// It is only valid to call Observe within the scope of the passed function,
|
||||||
// and only on the instruments that were registered with this call.
|
// and only on the instruments that were registered with this call.
|
||||||
func (m *testMeter) RegisterCallback(insts []instrument.Asynchronous, function func(context.Context)) error {
|
func (m *testMeter) RegisterCallback(i []instrument.Asynchronous, f func(context.Context)) (metric.Registration, error) {
|
||||||
m.callbacks = append(m.callbacks, function)
|
m.callbacks = append(m.callbacks, f)
|
||||||
|
return testReg{
|
||||||
|
f: func(idx int) func() {
|
||||||
|
return func() { m.callbacks[idx] = nil }
|
||||||
|
}(len(m.callbacks) - 1),
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
type testReg struct {
|
||||||
|
f func()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r testReg) Unregister() error {
|
||||||
|
r.f()
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -85,6 +98,10 @@ func (m *testMeter) SyncFloat64() syncfloat64.InstrumentProvider {
|
|||||||
func (m *testMeter) collect() {
|
func (m *testMeter) collect() {
|
||||||
ctx := context.Background()
|
ctx := context.Background()
|
||||||
for _, f := range m.callbacks {
|
for _, f := range m.callbacks {
|
||||||
|
if f == nil {
|
||||||
|
// Unregister.
|
||||||
|
continue
|
||||||
|
}
|
||||||
f(ctx)
|
f(ctx)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -51,14 +51,30 @@ type Meter interface {
|
|||||||
// To Observe data with instruments it must be registered in a callback.
|
// To Observe data with instruments it must be registered in a callback.
|
||||||
AsyncFloat64() asyncfloat64.InstrumentProvider
|
AsyncFloat64() asyncfloat64.InstrumentProvider
|
||||||
|
|
||||||
// RegisterCallback captures the function that will be called during Collect.
|
|
||||||
//
|
|
||||||
// It is only valid to call Observe within the scope of the passed function,
|
|
||||||
// and only on the instruments that were registered with this call.
|
|
||||||
RegisterCallback(insts []instrument.Asynchronous, function func(context.Context)) error
|
|
||||||
|
|
||||||
// SyncInt64 is the namespace for the Synchronous Integer instruments
|
// SyncInt64 is the namespace for the Synchronous Integer instruments
|
||||||
SyncInt64() syncint64.InstrumentProvider
|
SyncInt64() syncint64.InstrumentProvider
|
||||||
// SyncFloat64 is the namespace for the Synchronous Float instruments
|
// SyncFloat64 is the namespace for the Synchronous Float instruments
|
||||||
SyncFloat64() syncfloat64.InstrumentProvider
|
SyncFloat64() syncfloat64.InstrumentProvider
|
||||||
|
|
||||||
|
// RegisterCallback registers f to be called during the collection of a
|
||||||
|
// measurement cycle.
|
||||||
|
//
|
||||||
|
// If Unregister of the returned Registration is called, f needs to be
|
||||||
|
// unregistered and not called during collection.
|
||||||
|
//
|
||||||
|
// The instruments f is registered with are the only instruments that f may
|
||||||
|
// observe values for.
|
||||||
|
//
|
||||||
|
// If no instruments are passed, f should not be registered nor called
|
||||||
|
// during collection.
|
||||||
|
RegisterCallback(instruments []instrument.Asynchronous, f func(context.Context)) (Registration, error)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Registration is an token representing the unique registration of a callback
|
||||||
|
// for a set of instruments with a Meter.
|
||||||
|
type Registration interface {
|
||||||
|
// Unregister removes the callback registration from a Meter.
|
||||||
|
//
|
||||||
|
// This method needs to be idempotent and concurrent safe.
|
||||||
|
Unregister() error
|
||||||
}
|
}
|
||||||
|
@ -64,10 +64,14 @@ func (noopMeter) SyncFloat64() syncfloat64.InstrumentProvider {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// RegisterCallback creates a register callback that does not record any metrics.
|
// RegisterCallback creates a register callback that does not record any metrics.
|
||||||
func (noopMeter) RegisterCallback([]instrument.Asynchronous, func(context.Context)) error {
|
func (noopMeter) RegisterCallback([]instrument.Asynchronous, func(context.Context)) (Registration, error) {
|
||||||
return nil
|
return noopReg{}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type noopReg struct{}
|
||||||
|
|
||||||
|
func (noopReg) Unregister() error { return nil }
|
||||||
|
|
||||||
type nonrecordingAsyncFloat64Instrument struct {
|
type nonrecordingAsyncFloat64Instrument struct {
|
||||||
instrument.Asynchronous
|
instrument.Asynchronous
|
||||||
}
|
}
|
||||||
|
@ -69,7 +69,7 @@ func (m *meter) AsyncFloat64() asyncfloat64.InstrumentProvider {
|
|||||||
|
|
||||||
// RegisterCallback registers the function f to be called when any of the
|
// RegisterCallback registers the function f to be called when any of the
|
||||||
// insts Collect method is called.
|
// insts Collect method is called.
|
||||||
func (m *meter) RegisterCallback(insts []instrument.Asynchronous, f func(context.Context)) error {
|
func (m *meter) RegisterCallback(insts []instrument.Asynchronous, f func(context.Context)) (metric.Registration, error) {
|
||||||
for _, inst := range insts {
|
for _, inst := range insts {
|
||||||
// Only register if at least one instrument has a non-drop aggregation.
|
// Only register if at least one instrument has a non-drop aggregation.
|
||||||
// Otherwise, calling f during collection will be wasted computation.
|
// Otherwise, calling f during collection will be wasted computation.
|
||||||
@ -91,12 +91,19 @@ func (m *meter) RegisterCallback(insts []instrument.Asynchronous, f func(context
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
// All insts use drop aggregation.
|
// All insts use drop aggregation.
|
||||||
|
return noopRegister{}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
type noopRegister struct{}
|
||||||
|
|
||||||
|
func (noopRegister) Unregister() error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *meter) registerCallback(f func(context.Context)) error {
|
type callback func(context.Context)
|
||||||
m.pipes.registerCallback(f)
|
|
||||||
return nil
|
func (m *meter) registerCallback(c callback) (metric.Registration, error) {
|
||||||
|
return m.pipes.registerCallback(c), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// SyncInt64 returns the synchronous integer instrument provider.
|
// SyncInt64 returns the synchronous integer instrument provider.
|
||||||
|
@ -103,11 +103,63 @@ func TestMeterCallbackCreationConcurrency(t *testing.T) {
|
|||||||
m := NewMeterProvider().Meter("callback-concurrency")
|
m := NewMeterProvider().Meter("callback-concurrency")
|
||||||
|
|
||||||
go func() {
|
go func() {
|
||||||
_ = m.RegisterCallback([]instrument.Asynchronous{}, func(ctx context.Context) {})
|
_, _ = m.RegisterCallback([]instrument.Asynchronous{}, func(ctx context.Context) {})
|
||||||
wg.Done()
|
wg.Done()
|
||||||
}()
|
}()
|
||||||
go func() {
|
go func() {
|
||||||
_ = m.RegisterCallback([]instrument.Asynchronous{}, func(ctx context.Context) {})
|
_, _ = m.RegisterCallback([]instrument.Asynchronous{}, func(ctx context.Context) {})
|
||||||
|
wg.Done()
|
||||||
|
}()
|
||||||
|
wg.Wait()
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestNoopCallbackUnregisterConcurrency(t *testing.T) {
|
||||||
|
m := NewMeterProvider().Meter("noop-unregister-concurrency")
|
||||||
|
reg, err := m.RegisterCallback(nil, func(ctx context.Context) {})
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
wg := &sync.WaitGroup{}
|
||||||
|
wg.Add(2)
|
||||||
|
go func() {
|
||||||
|
_ = reg.Unregister()
|
||||||
|
wg.Done()
|
||||||
|
}()
|
||||||
|
go func() {
|
||||||
|
_ = reg.Unregister()
|
||||||
|
wg.Done()
|
||||||
|
}()
|
||||||
|
wg.Wait()
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestCallbackUnregisterConcurrency(t *testing.T) {
|
||||||
|
reader := NewManualReader()
|
||||||
|
provider := NewMeterProvider(WithReader(reader))
|
||||||
|
meter := provider.Meter("unregister-concurrency")
|
||||||
|
|
||||||
|
actr, err := meter.AsyncFloat64().Counter("counter")
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
ag, err := meter.AsyncInt64().Gauge("gauge")
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
i := []instrument.Asynchronous{actr}
|
||||||
|
regCtr, err := meter.RegisterCallback(i, func(ctx context.Context) {})
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
i = []instrument.Asynchronous{ag}
|
||||||
|
regG, err := meter.RegisterCallback(i, func(ctx context.Context) {})
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
wg := &sync.WaitGroup{}
|
||||||
|
wg.Add(2)
|
||||||
|
go func() {
|
||||||
|
_ = regCtr.Unregister()
|
||||||
|
_ = regG.Unregister()
|
||||||
|
wg.Done()
|
||||||
|
}()
|
||||||
|
go func() {
|
||||||
|
_ = regCtr.Unregister()
|
||||||
|
_ = regG.Unregister()
|
||||||
wg.Done()
|
wg.Done()
|
||||||
}()
|
}()
|
||||||
wg.Wait()
|
wg.Wait()
|
||||||
@ -126,7 +178,7 @@ func TestMeterCreatesInstruments(t *testing.T) {
|
|||||||
fn: func(t *testing.T, m metric.Meter) {
|
fn: func(t *testing.T, m metric.Meter) {
|
||||||
ctr, err := m.AsyncInt64().Counter("aint")
|
ctr, err := m.AsyncInt64().Counter("aint")
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
err = m.RegisterCallback([]instrument.Asynchronous{ctr}, func(ctx context.Context) {
|
_, err = m.RegisterCallback([]instrument.Asynchronous{ctr}, func(ctx context.Context) {
|
||||||
ctr.Observe(ctx, 3)
|
ctr.Observe(ctx, 3)
|
||||||
})
|
})
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
@ -150,7 +202,7 @@ func TestMeterCreatesInstruments(t *testing.T) {
|
|||||||
fn: func(t *testing.T, m metric.Meter) {
|
fn: func(t *testing.T, m metric.Meter) {
|
||||||
ctr, err := m.AsyncInt64().UpDownCounter("aint")
|
ctr, err := m.AsyncInt64().UpDownCounter("aint")
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
err = m.RegisterCallback([]instrument.Asynchronous{ctr}, func(ctx context.Context) {
|
_, err = m.RegisterCallback([]instrument.Asynchronous{ctr}, func(ctx context.Context) {
|
||||||
ctr.Observe(ctx, 11)
|
ctr.Observe(ctx, 11)
|
||||||
})
|
})
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
@ -174,7 +226,7 @@ func TestMeterCreatesInstruments(t *testing.T) {
|
|||||||
fn: func(t *testing.T, m metric.Meter) {
|
fn: func(t *testing.T, m metric.Meter) {
|
||||||
gauge, err := m.AsyncInt64().Gauge("agauge")
|
gauge, err := m.AsyncInt64().Gauge("agauge")
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
err = m.RegisterCallback([]instrument.Asynchronous{gauge}, func(ctx context.Context) {
|
_, err = m.RegisterCallback([]instrument.Asynchronous{gauge}, func(ctx context.Context) {
|
||||||
gauge.Observe(ctx, 11)
|
gauge.Observe(ctx, 11)
|
||||||
})
|
})
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
@ -196,7 +248,7 @@ func TestMeterCreatesInstruments(t *testing.T) {
|
|||||||
fn: func(t *testing.T, m metric.Meter) {
|
fn: func(t *testing.T, m metric.Meter) {
|
||||||
ctr, err := m.AsyncFloat64().Counter("afloat")
|
ctr, err := m.AsyncFloat64().Counter("afloat")
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
err = m.RegisterCallback([]instrument.Asynchronous{ctr}, func(ctx context.Context) {
|
_, err = m.RegisterCallback([]instrument.Asynchronous{ctr}, func(ctx context.Context) {
|
||||||
ctr.Observe(ctx, 3)
|
ctr.Observe(ctx, 3)
|
||||||
})
|
})
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
@ -220,7 +272,7 @@ func TestMeterCreatesInstruments(t *testing.T) {
|
|||||||
fn: func(t *testing.T, m metric.Meter) {
|
fn: func(t *testing.T, m metric.Meter) {
|
||||||
ctr, err := m.AsyncFloat64().UpDownCounter("afloat")
|
ctr, err := m.AsyncFloat64().UpDownCounter("afloat")
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
err = m.RegisterCallback([]instrument.Asynchronous{ctr}, func(ctx context.Context) {
|
_, err = m.RegisterCallback([]instrument.Asynchronous{ctr}, func(ctx context.Context) {
|
||||||
ctr.Observe(ctx, 11)
|
ctr.Observe(ctx, 11)
|
||||||
})
|
})
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
@ -244,7 +296,7 @@ func TestMeterCreatesInstruments(t *testing.T) {
|
|||||||
fn: func(t *testing.T, m metric.Meter) {
|
fn: func(t *testing.T, m metric.Meter) {
|
||||||
gauge, err := m.AsyncFloat64().Gauge("agauge")
|
gauge, err := m.AsyncFloat64().Gauge("agauge")
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
err = m.RegisterCallback([]instrument.Asynchronous{gauge}, func(ctx context.Context) {
|
_, err = m.RegisterCallback([]instrument.Asynchronous{gauge}, func(ctx context.Context) {
|
||||||
gauge.Observe(ctx, 11)
|
gauge.Observe(ctx, 11)
|
||||||
})
|
})
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
@ -418,7 +470,7 @@ func TestMetersProvideScope(t *testing.T) {
|
|||||||
m1 := mp.Meter("scope1")
|
m1 := mp.Meter("scope1")
|
||||||
ctr1, err := m1.AsyncFloat64().Counter("ctr1")
|
ctr1, err := m1.AsyncFloat64().Counter("ctr1")
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
err = m1.RegisterCallback([]instrument.Asynchronous{ctr1}, func(ctx context.Context) {
|
_, err = m1.RegisterCallback([]instrument.Asynchronous{ctr1}, func(ctx context.Context) {
|
||||||
ctr1.Observe(ctx, 5)
|
ctr1.Observe(ctx, 5)
|
||||||
})
|
})
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
@ -426,7 +478,7 @@ func TestMetersProvideScope(t *testing.T) {
|
|||||||
m2 := mp.Meter("scope2")
|
m2 := mp.Meter("scope2")
|
||||||
ctr2, err := m2.AsyncInt64().Counter("ctr2")
|
ctr2, err := m2.AsyncInt64().Counter("ctr2")
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
err = m1.RegisterCallback([]instrument.Asynchronous{ctr2}, func(ctx context.Context) {
|
_, err = m1.RegisterCallback([]instrument.Asynchronous{ctr2}, func(ctx context.Context) {
|
||||||
ctr2.Observe(ctx, 7)
|
ctr2.Observe(ctx, 7)
|
||||||
})
|
})
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
@ -480,6 +532,53 @@ func TestMetersProvideScope(t *testing.T) {
|
|||||||
metricdatatest.AssertEqual(t, want, got, metricdatatest.IgnoreTimestamp())
|
metricdatatest.AssertEqual(t, want, got, metricdatatest.IgnoreTimestamp())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestUnregisterUnregisters(t *testing.T) {
|
||||||
|
r := NewManualReader()
|
||||||
|
mp := NewMeterProvider(WithReader(r))
|
||||||
|
m := mp.Meter("TestUnregisterUnregisters")
|
||||||
|
|
||||||
|
int64Counter, err := m.AsyncInt64().Counter("int64.counter")
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
int64UpDownCounter, err := m.AsyncInt64().UpDownCounter("int64.up_down_counter")
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
int64Gauge, err := m.AsyncInt64().Gauge("int64.gauge")
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
floag64Counter, err := m.AsyncFloat64().Counter("floag64.counter")
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
floag64UpDownCounter, err := m.AsyncFloat64().UpDownCounter("floag64.up_down_counter")
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
floag64Gauge, err := m.AsyncFloat64().Gauge("floag64.gauge")
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
var called bool
|
||||||
|
reg, err := m.RegisterCallback([]instrument.Asynchronous{
|
||||||
|
int64Counter,
|
||||||
|
int64UpDownCounter,
|
||||||
|
int64Gauge,
|
||||||
|
floag64Counter,
|
||||||
|
floag64UpDownCounter,
|
||||||
|
floag64Gauge,
|
||||||
|
}, func(context.Context) { called = true })
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
ctx := context.Background()
|
||||||
|
_, err = r.Collect(ctx)
|
||||||
|
require.NoError(t, err)
|
||||||
|
assert.True(t, called, "callback not called for registered callback")
|
||||||
|
|
||||||
|
called = false
|
||||||
|
require.NoError(t, reg.Unregister(), "unregister")
|
||||||
|
|
||||||
|
_, err = r.Collect(ctx)
|
||||||
|
require.NoError(t, err)
|
||||||
|
assert.False(t, called, "callback called for unregistered callback")
|
||||||
|
}
|
||||||
|
|
||||||
func TestRegisterCallbackDropAggregations(t *testing.T) {
|
func TestRegisterCallbackDropAggregations(t *testing.T) {
|
||||||
aggFn := func(InstrumentKind) aggregation.Aggregation {
|
aggFn := func(InstrumentKind) aggregation.Aggregation {
|
||||||
return aggregation.Drop{}
|
return aggregation.Drop{}
|
||||||
@ -507,14 +606,15 @@ func TestRegisterCallbackDropAggregations(t *testing.T) {
|
|||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
var called bool
|
var called bool
|
||||||
require.NoError(t, m.RegisterCallback([]instrument.Asynchronous{
|
_, err = m.RegisterCallback([]instrument.Asynchronous{
|
||||||
int64Counter,
|
int64Counter,
|
||||||
int64UpDownCounter,
|
int64UpDownCounter,
|
||||||
int64Gauge,
|
int64Gauge,
|
||||||
floag64Counter,
|
floag64Counter,
|
||||||
floag64UpDownCounter,
|
floag64UpDownCounter,
|
||||||
floag64Gauge,
|
floag64Gauge,
|
||||||
}, func(context.Context) { called = true }))
|
}, func(context.Context) { called = true })
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
data, err := r.Collect(context.Background())
|
data, err := r.Collect(context.Background())
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
@ -538,10 +638,11 @@ func TestAttributeFilter(t *testing.T) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
return mtr.RegisterCallback([]instrument.Asynchronous{ctr}, func(ctx context.Context) {
|
_, err = mtr.RegisterCallback([]instrument.Asynchronous{ctr}, func(ctx context.Context) {
|
||||||
ctr.Observe(ctx, 1.0, attribute.String("foo", "bar"), attribute.Int("version", 1))
|
ctr.Observe(ctx, 1.0, attribute.String("foo", "bar"), attribute.Int("version", 1))
|
||||||
ctr.Observe(ctx, 2.0, attribute.String("foo", "bar"), attribute.Int("version", 2))
|
ctr.Observe(ctx, 2.0, attribute.String("foo", "bar"), attribute.Int("version", 2))
|
||||||
})
|
})
|
||||||
|
return err
|
||||||
},
|
},
|
||||||
wantMetric: metricdata.Metrics{
|
wantMetric: metricdata.Metrics{
|
||||||
Name: "afcounter",
|
Name: "afcounter",
|
||||||
@ -564,10 +665,11 @@ func TestAttributeFilter(t *testing.T) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
return mtr.RegisterCallback([]instrument.Asynchronous{ctr}, func(ctx context.Context) {
|
_, err = mtr.RegisterCallback([]instrument.Asynchronous{ctr}, func(ctx context.Context) {
|
||||||
ctr.Observe(ctx, 1.0, attribute.String("foo", "bar"), attribute.Int("version", 1))
|
ctr.Observe(ctx, 1.0, attribute.String("foo", "bar"), attribute.Int("version", 1))
|
||||||
ctr.Observe(ctx, 2.0, attribute.String("foo", "bar"), attribute.Int("version", 2))
|
ctr.Observe(ctx, 2.0, attribute.String("foo", "bar"), attribute.Int("version", 2))
|
||||||
})
|
})
|
||||||
|
return err
|
||||||
},
|
},
|
||||||
wantMetric: metricdata.Metrics{
|
wantMetric: metricdata.Metrics{
|
||||||
Name: "afupdowncounter",
|
Name: "afupdowncounter",
|
||||||
@ -590,10 +692,11 @@ func TestAttributeFilter(t *testing.T) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
return mtr.RegisterCallback([]instrument.Asynchronous{ctr}, func(ctx context.Context) {
|
_, err = mtr.RegisterCallback([]instrument.Asynchronous{ctr}, func(ctx context.Context) {
|
||||||
ctr.Observe(ctx, 1.0, attribute.String("foo", "bar"), attribute.Int("version", 1))
|
ctr.Observe(ctx, 1.0, attribute.String("foo", "bar"), attribute.Int("version", 1))
|
||||||
ctr.Observe(ctx, 2.0, attribute.String("foo", "bar"), attribute.Int("version", 2))
|
ctr.Observe(ctx, 2.0, attribute.String("foo", "bar"), attribute.Int("version", 2))
|
||||||
})
|
})
|
||||||
|
return err
|
||||||
},
|
},
|
||||||
wantMetric: metricdata.Metrics{
|
wantMetric: metricdata.Metrics{
|
||||||
Name: "afgauge",
|
Name: "afgauge",
|
||||||
@ -614,10 +717,11 @@ func TestAttributeFilter(t *testing.T) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
return mtr.RegisterCallback([]instrument.Asynchronous{ctr}, func(ctx context.Context) {
|
_, err = mtr.RegisterCallback([]instrument.Asynchronous{ctr}, func(ctx context.Context) {
|
||||||
ctr.Observe(ctx, 10, attribute.String("foo", "bar"), attribute.Int("version", 1))
|
ctr.Observe(ctx, 10, attribute.String("foo", "bar"), attribute.Int("version", 1))
|
||||||
ctr.Observe(ctx, 20, attribute.String("foo", "bar"), attribute.Int("version", 2))
|
ctr.Observe(ctx, 20, attribute.String("foo", "bar"), attribute.Int("version", 2))
|
||||||
})
|
})
|
||||||
|
return err
|
||||||
},
|
},
|
||||||
wantMetric: metricdata.Metrics{
|
wantMetric: metricdata.Metrics{
|
||||||
Name: "aicounter",
|
Name: "aicounter",
|
||||||
@ -640,10 +744,11 @@ func TestAttributeFilter(t *testing.T) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
return mtr.RegisterCallback([]instrument.Asynchronous{ctr}, func(ctx context.Context) {
|
_, err = mtr.RegisterCallback([]instrument.Asynchronous{ctr}, func(ctx context.Context) {
|
||||||
ctr.Observe(ctx, 10, attribute.String("foo", "bar"), attribute.Int("version", 1))
|
ctr.Observe(ctx, 10, attribute.String("foo", "bar"), attribute.Int("version", 1))
|
||||||
ctr.Observe(ctx, 20, attribute.String("foo", "bar"), attribute.Int("version", 2))
|
ctr.Observe(ctx, 20, attribute.String("foo", "bar"), attribute.Int("version", 2))
|
||||||
})
|
})
|
||||||
|
return err
|
||||||
},
|
},
|
||||||
wantMetric: metricdata.Metrics{
|
wantMetric: metricdata.Metrics{
|
||||||
Name: "aiupdowncounter",
|
Name: "aiupdowncounter",
|
||||||
@ -666,10 +771,11 @@ func TestAttributeFilter(t *testing.T) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
return mtr.RegisterCallback([]instrument.Asynchronous{ctr}, func(ctx context.Context) {
|
_, err = mtr.RegisterCallback([]instrument.Asynchronous{ctr}, func(ctx context.Context) {
|
||||||
ctr.Observe(ctx, 10, attribute.String("foo", "bar"), attribute.Int("version", 1))
|
ctr.Observe(ctx, 10, attribute.String("foo", "bar"), attribute.Int("version", 1))
|
||||||
ctr.Observe(ctx, 20, attribute.String("foo", "bar"), attribute.Int("version", 2))
|
ctr.Observe(ctx, 20, attribute.String("foo", "bar"), attribute.Int("version", 2))
|
||||||
})
|
})
|
||||||
|
return err
|
||||||
},
|
},
|
||||||
wantMetric: metricdata.Metrics{
|
wantMetric: metricdata.Metrics{
|
||||||
Name: "aigauge",
|
Name: "aigauge",
|
||||||
|
@ -15,6 +15,7 @@
|
|||||||
package metric // import "go.opentelemetry.io/otel/sdk/metric"
|
package metric // import "go.opentelemetry.io/otel/sdk/metric"
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"container/list"
|
||||||
"context"
|
"context"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
@ -22,6 +23,7 @@ import (
|
|||||||
"sync"
|
"sync"
|
||||||
|
|
||||||
"go.opentelemetry.io/otel/internal/global"
|
"go.opentelemetry.io/otel/internal/global"
|
||||||
|
"go.opentelemetry.io/otel/metric"
|
||||||
"go.opentelemetry.io/otel/metric/unit"
|
"go.opentelemetry.io/otel/metric/unit"
|
||||||
"go.opentelemetry.io/otel/sdk/instrumentation"
|
"go.opentelemetry.io/otel/sdk/instrumentation"
|
||||||
"go.opentelemetry.io/otel/sdk/metric/aggregation"
|
"go.opentelemetry.io/otel/sdk/metric/aggregation"
|
||||||
@ -75,7 +77,7 @@ type pipeline struct {
|
|||||||
|
|
||||||
sync.Mutex
|
sync.Mutex
|
||||||
aggregations map[instrumentation.Scope][]instrumentSync
|
aggregations map[instrumentation.Scope][]instrumentSync
|
||||||
callbacks []func(context.Context)
|
callbacks list.List
|
||||||
}
|
}
|
||||||
|
|
||||||
// addSync adds the instrumentSync to pipeline p with scope. This method is not
|
// addSync adds the instrumentSync to pipeline p with scope. This method is not
|
||||||
@ -94,10 +96,15 @@ func (p *pipeline) addSync(scope instrumentation.Scope, iSync instrumentSync) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// addCallback registers a callback to be run when `produce()` is called.
|
// addCallback registers a callback to be run when `produce()` is called.
|
||||||
func (p *pipeline) addCallback(callback func(context.Context)) {
|
func (p *pipeline) addCallback(c callback) (unregister func()) {
|
||||||
p.Lock()
|
p.Lock()
|
||||||
defer p.Unlock()
|
defer p.Unlock()
|
||||||
p.callbacks = append(p.callbacks, callback)
|
e := p.callbacks.PushBack(c)
|
||||||
|
return func() {
|
||||||
|
p.Lock()
|
||||||
|
p.callbacks.Remove(e)
|
||||||
|
p.Unlock()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// callbackKey is a context key type used to identify context that came from the SDK.
|
// callbackKey is a context key type used to identify context that came from the SDK.
|
||||||
@ -112,14 +119,15 @@ const produceKey callbackKey = 0
|
|||||||
//
|
//
|
||||||
// This method is safe to call concurrently.
|
// This method is safe to call concurrently.
|
||||||
func (p *pipeline) produce(ctx context.Context) (metricdata.ResourceMetrics, error) {
|
func (p *pipeline) produce(ctx context.Context) (metricdata.ResourceMetrics, error) {
|
||||||
|
ctx = context.WithValue(ctx, produceKey, struct{}{})
|
||||||
|
|
||||||
p.Lock()
|
p.Lock()
|
||||||
defer p.Unlock()
|
defer p.Unlock()
|
||||||
|
|
||||||
ctx = context.WithValue(ctx, produceKey, struct{}{})
|
for e := p.callbacks.Front(); e != nil; e = e.Next() {
|
||||||
|
|
||||||
for _, callback := range p.callbacks {
|
|
||||||
// TODO make the callbacks parallel. ( #3034 )
|
// TODO make the callbacks parallel. ( #3034 )
|
||||||
callback(ctx)
|
f := e.Value.(callback)
|
||||||
|
f(ctx)
|
||||||
if err := ctx.Err(); err != nil {
|
if err := ctx.Err(); err != nil {
|
||||||
// This means the context expired before we finished running callbacks.
|
// This means the context expired before we finished running callbacks.
|
||||||
return metricdata.ResourceMetrics{}, err
|
return metricdata.ResourceMetrics{}, err
|
||||||
@ -439,10 +447,21 @@ func newPipelines(res *resource.Resource, readers []Reader, views []View) pipeli
|
|||||||
return pipes
|
return pipes
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p pipelines) registerCallback(fn func(context.Context)) {
|
func (p pipelines) registerCallback(c callback) metric.Registration {
|
||||||
for _, pipe := range p {
|
unregs := make([]func(), len(p))
|
||||||
pipe.addCallback(fn)
|
for i, pipe := range p {
|
||||||
|
unregs[i] = pipe.addCallback(c)
|
||||||
}
|
}
|
||||||
|
return unregisterFuncs(unregs)
|
||||||
|
}
|
||||||
|
|
||||||
|
type unregisterFuncs []func()
|
||||||
|
|
||||||
|
func (u unregisterFuncs) Unregister() error {
|
||||||
|
for _, f := range u {
|
||||||
|
f()
|
||||||
|
}
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// resolver facilitates resolving Aggregators an instrument needs to aggregate
|
// resolver facilitates resolving Aggregators an instrument needs to aggregate
|
||||||
|
Reference in New Issue
Block a user