1
0
mirror of https://github.com/open-telemetry/opentelemetry-go.git synced 2025-07-15 01:04:25 +02:00

Refactor the SDK helpers, create MeterImpl (#560)

* Create MeterImpl interface

* Checkpoint w/ sdk.go building

* Checkpoint working on global

* api/global builds (test fails)

* Test fix

* All tests pass

* Comments

* Add two tests

* Comments and uncomment tests

* Precommit part 1

* Still working on tests

* Lint

* Add a test and a TODO

* Cleanup

* Lint

* Interface()->Implementation()

* Apply some feedback

* From feedback

* (A)Synchronous -> (A)Sync

* Add a missing comment

* Apply suggestions from code review

Co-Authored-By: Krzesimir Nowak <qdlacz@gmail.com>

* Rename a variable

Co-authored-by: Krzesimir Nowak <qdlacz@gmail.com>
This commit is contained in:
Joshua MacDonald
2020-03-19 12:02:46 -07:00
committed by GitHub
parent e0406dd3eb
commit d8682c1999
55 changed files with 1314 additions and 1299 deletions

View File

@ -23,8 +23,9 @@ var Must = metric.Must
// benchFixture is copied from sdk/metric/benchmark_test.go. // benchFixture is copied from sdk/metric/benchmark_test.go.
// TODO refactor to share this code. // TODO refactor to share this code.
type benchFixture struct { type benchFixture struct {
sdk *sdk.SDK sdk *sdk.SDK
B *testing.B meter metric.Meter
B *testing.B
} }
var _ metric.Provider = &benchFixture{} var _ metric.Provider = &benchFixture{}
@ -35,14 +36,15 @@ func newFixture(b *testing.B) *benchFixture {
B: b, B: b,
} }
bf.sdk = sdk.New(bf, sdk.NewDefaultLabelEncoder()) bf.sdk = sdk.New(bf, sdk.NewDefaultLabelEncoder())
bf.meter = metric.WrapMeterImpl(bf.sdk)
return bf return bf
} }
func (*benchFixture) AggregatorFor(descriptor *export.Descriptor) export.Aggregator { func (*benchFixture) AggregatorFor(descriptor *metric.Descriptor) export.Aggregator {
switch descriptor.MetricKind() { switch descriptor.MetricKind() {
case export.CounterKind: case metric.CounterKind:
return sum.New() return sum.New()
case export.MeasureKind: case metric.MeasureKind:
if strings.HasSuffix(descriptor.Name(), "minmaxsumcount") { if strings.HasSuffix(descriptor.Name(), "minmaxsumcount") {
return minmaxsumcount.New(descriptor) return minmaxsumcount.New(descriptor)
} else if strings.HasSuffix(descriptor.Name(), "ddsketch") { } else if strings.HasSuffix(descriptor.Name(), "ddsketch") {
@ -66,7 +68,7 @@ func (*benchFixture) FinishedCollection() {
} }
func (fix *benchFixture) Meter(name string) metric.Meter { func (fix *benchFixture) Meter(name string) metric.Meter {
return fix.sdk return fix.meter
} }
func BenchmarkGlobalInt64CounterAddNoSDK(b *testing.B) { func BenchmarkGlobalInt64CounterAddNoSDK(b *testing.B) {

View File

@ -2,7 +2,6 @@ package internal
import ( import (
"context" "context"
"errors"
"sync" "sync"
"sync/atomic" "sync/atomic"
"unsafe" "unsafe"
@ -32,13 +31,6 @@ import (
// instrument after it is registered, with a sync.Once initializer to // instrument after it is registered, with a sync.Once initializer to
// protect against races with Release(). // protect against races with Release().
type metricKind int8
const (
counterKind metricKind = iota
measureKind
)
type meterProvider struct { type meterProvider struct {
delegate metric.Provider delegate metric.Provider
@ -52,50 +44,41 @@ type meter struct {
provider *meterProvider provider *meterProvider
name string name string
lock sync.Mutex lock sync.Mutex
instruments []*instImpl syncInsts []*syncImpl
liveObservers map[*obsImpl]struct{} asyncInsts []*obsImpl
// orderedObservers slice contains observers in their order of
// registration. It may also contain unregistered
// observers. The liveObservers map should be consulted to
// check if the observer is registered or not.
orderedObservers []*obsImpl
} }
type instImpl struct { type instrument struct {
delegate unsafe.Pointer // (*metric.InstrumentImpl) descriptor metric.Descriptor
}
name string type syncImpl struct {
mkind metricKind delegate unsafe.Pointer // (*metric.SyncImpl)
nkind core.NumberKind
opts []metric.Option instrument
constructor func(metric.Meter) (metric.SyncImpl, error)
} }
type obsImpl struct { type obsImpl struct {
delegate unsafe.Pointer // (*metric.Int64Observer or *metric.Float64Observer) delegate unsafe.Pointer // (*metric.AsyncImpl)
name string instrument
nkind core.NumberKind
opts []metric.Option constructor func(metric.Meter) (metric.AsyncImpl, error)
meter *meter
callback interface{}
} }
type hasImpl interface { // SyncImpler is implemented by all of the sync metric
Impl() metric.InstrumentImpl // instruments.
type SyncImpler interface {
SyncImpl() metric.SyncImpl
} }
type int64ObsImpl struct { // AsyncImpler is implemented by all of the async
observer *obsImpl // metric instruments.
} type AsyncImpler interface {
AsyncImpl() metric.AsyncImpl
type float64ObsImpl struct {
observer *obsImpl
}
// this is a common subset of the metric observers interfaces
type observerUnregister interface {
Unregister()
} }
type labelSet struct { type labelSet struct {
@ -107,10 +90,10 @@ type labelSet struct {
initialize sync.Once initialize sync.Once
} }
type instHandle struct { type syncHandle struct {
delegate unsafe.Pointer // (*metric.HandleImpl) delegate unsafe.Pointer // (*metric.HandleImpl)
inst *instImpl inst *syncImpl
labels metric.LabelSet labels metric.LabelSet
initialize sync.Once initialize sync.Once
@ -120,14 +103,13 @@ var _ metric.Provider = &meterProvider{}
var _ metric.Meter = &meter{} var _ metric.Meter = &meter{}
var _ metric.LabelSet = &labelSet{} var _ metric.LabelSet = &labelSet{}
var _ metric.LabelSetDelegate = &labelSet{} var _ metric.LabelSetDelegate = &labelSet{}
var _ metric.InstrumentImpl = &instImpl{} var _ metric.InstrumentImpl = &syncImpl{}
var _ metric.BoundInstrumentImpl = &instHandle{} var _ metric.BoundSyncImpl = &syncHandle{}
var _ metric.Int64Observer = int64ObsImpl{} var _ metric.AsyncImpl = &obsImpl{}
var _ metric.Float64Observer = float64ObsImpl{}
var _ observerUnregister = (metric.Int64Observer)(nil)
var _ observerUnregister = (metric.Float64Observer)(nil)
var errInvalidMetricKind = errors.New("Invalid Metric kind") func (inst *instrument) Descriptor() metric.Descriptor {
return inst.descriptor
}
// Provider interface and delegation // Provider interface and delegation
@ -168,40 +150,37 @@ func (m *meter) setDelegate(provider metric.Provider) {
*d = provider.Meter(m.name) *d = provider.Meter(m.name)
m.delegate = unsafe.Pointer(d) m.delegate = unsafe.Pointer(d)
for _, inst := range m.instruments { for _, inst := range m.syncInsts {
inst.setDelegate(*d) inst.setDelegate(*d)
} }
m.instruments = nil m.syncInsts = nil
for _, obs := range m.orderedObservers { for _, obs := range m.asyncInsts {
if _, ok := m.liveObservers[obs]; ok { obs.setDelegate(*d)
obs.setDelegate(*d)
}
} }
m.liveObservers = nil m.asyncInsts = nil
m.orderedObservers = nil
} }
func (m *meter) newInst(name string, mkind metricKind, nkind core.NumberKind, opts []metric.Option) (metric.InstrumentImpl, error) { func (m *meter) newSync(desc metric.Descriptor, constructor func(metric.Meter) (metric.SyncImpl, error)) (metric.SyncImpl, error) {
m.lock.Lock() m.lock.Lock()
defer m.lock.Unlock() defer m.lock.Unlock()
if meterPtr := (*metric.Meter)(atomic.LoadPointer(&m.delegate)); meterPtr != nil { if meterPtr := (*metric.Meter)(atomic.LoadPointer(&m.delegate)); meterPtr != nil {
return newInstDelegate(*meterPtr, name, mkind, nkind, opts) return constructor(*meterPtr)
} }
inst := &instImpl{ inst := &syncImpl{
name: name, instrument: instrument{
mkind: mkind, descriptor: desc,
nkind: nkind, },
opts: opts, constructor: constructor,
} }
m.instruments = append(m.instruments, inst) m.syncInsts = append(m.syncInsts, inst)
return inst, nil return inst, nil
} }
func delegateCheck(has hasImpl, err error) (metric.InstrumentImpl, error) { func syncCheck(has SyncImpler, err error) (metric.SyncImpl, error) {
if has != nil { if has != nil {
return has.Impl(), err return has.SyncImpl(), err
} }
if err == nil { if err == nil {
err = metric.ErrSDKReturnedNilImpl err = metric.ErrSDKReturnedNilImpl
@ -209,29 +188,13 @@ func delegateCheck(has hasImpl, err error) (metric.InstrumentImpl, error) {
return nil, err return nil, err
} }
func newInstDelegate(m metric.Meter, name string, mkind metricKind, nkind core.NumberKind, opts []metric.Option) (metric.InstrumentImpl, error) { // Synchronous delegation
switch mkind {
case counterKind:
if nkind == core.Int64NumberKind {
return delegateCheck(m.NewInt64Counter(name, opts...))
}
return delegateCheck(m.NewFloat64Counter(name, opts...))
case measureKind:
if nkind == core.Int64NumberKind {
return delegateCheck(m.NewInt64Measure(name, opts...))
}
return delegateCheck(m.NewFloat64Measure(name, opts...))
}
return nil, errInvalidMetricKind
}
// Instrument delegation func (inst *syncImpl) setDelegate(d metric.Meter) {
implPtr := new(metric.SyncImpl)
func (inst *instImpl) setDelegate(d metric.Meter) {
implPtr := new(metric.InstrumentImpl)
var err error var err error
*implPtr, err = newInstDelegate(d, inst.name, inst.mkind, inst.nkind, inst.opts) *implPtr, err = inst.constructor(d)
if err != nil { if err != nil {
// TODO: There is no standard way to deliver this error to the user. // TODO: There is no standard way to deliver this error to the user.
@ -244,20 +207,27 @@ func (inst *instImpl) setDelegate(d metric.Meter) {
atomic.StorePointer(&inst.delegate, unsafe.Pointer(implPtr)) atomic.StorePointer(&inst.delegate, unsafe.Pointer(implPtr))
} }
func (inst *instImpl) Bind(labels metric.LabelSet) metric.BoundInstrumentImpl { func (inst *syncImpl) Implementation() interface{} {
if implPtr := (*metric.InstrumentImpl)(atomic.LoadPointer(&inst.delegate)); implPtr != nil { if implPtr := (*metric.SyncImpl)(atomic.LoadPointer(&inst.delegate)); implPtr != nil {
return (*implPtr).Implementation()
}
return inst
}
func (inst *syncImpl) Bind(labels metric.LabelSet) metric.BoundSyncImpl {
if implPtr := (*metric.SyncImpl)(atomic.LoadPointer(&inst.delegate)); implPtr != nil {
return (*implPtr).Bind(labels) return (*implPtr).Bind(labels)
} }
return &instHandle{ return &syncHandle{
inst: inst, inst: inst,
labels: labels, labels: labels,
} }
} }
func (bound *instHandle) Unbind() { func (bound *syncHandle) Unbind() {
bound.initialize.Do(func() {}) bound.initialize.Do(func() {})
implPtr := (*metric.BoundInstrumentImpl)(atomic.LoadPointer(&bound.delegate)) implPtr := (*metric.BoundSyncImpl)(atomic.LoadPointer(&bound.delegate))
if implPtr == nil { if implPtr == nil {
return return
@ -266,50 +236,48 @@ func (bound *instHandle) Unbind() {
(*implPtr).Unbind() (*implPtr).Unbind()
} }
// Any Observer delegation // Async delegation
func (m *meter) newAsync(desc metric.Descriptor, constructor func(metric.Meter) (metric.AsyncImpl, error)) (metric.AsyncImpl, error) {
m.lock.Lock()
defer m.lock.Unlock()
if meterPtr := (*metric.Meter)(atomic.LoadPointer(&m.delegate)); meterPtr != nil {
return constructor(*meterPtr)
}
inst := &obsImpl{
instrument: instrument{
descriptor: desc,
},
constructor: constructor,
}
m.asyncInsts = append(m.asyncInsts, inst)
return inst, nil
}
func (obs *obsImpl) Implementation() interface{} {
if implPtr := (*metric.AsyncImpl)(atomic.LoadPointer(&obs.delegate)); implPtr != nil {
return (*implPtr).Implementation()
}
return obs
}
func asyncCheck(has AsyncImpler, err error) (metric.AsyncImpl, error) {
if has != nil {
return has.AsyncImpl(), err
}
if err == nil {
err = metric.ErrSDKReturnedNilImpl
}
return nil, err
}
func (obs *obsImpl) setDelegate(d metric.Meter) { func (obs *obsImpl) setDelegate(d metric.Meter) {
if obs.nkind == core.Int64NumberKind { implPtr := new(metric.AsyncImpl)
obs.setInt64Delegate(d)
} else {
obs.setFloat64Delegate(d)
}
}
func (obs *obsImpl) unregister() {
unreg := obs.getUnregister()
if unreg != nil {
unreg.Unregister()
return
}
obs.meter.lock.Lock()
defer obs.meter.lock.Unlock()
delete(obs.meter.liveObservers, obs)
if len(obs.meter.liveObservers) == 0 {
obs.meter.liveObservers = nil
obs.meter.orderedObservers = nil
}
}
func (obs *obsImpl) getUnregister() observerUnregister {
ptr := atomic.LoadPointer(&obs.delegate)
if ptr == nil {
return nil
}
if obs.nkind == core.Int64NumberKind {
return *(*metric.Int64Observer)(ptr)
}
return *(*metric.Float64Observer)(ptr)
}
// Int64Observer delegation
func (obs *obsImpl) setInt64Delegate(d metric.Meter) {
obsPtr := new(metric.Int64Observer)
cb := obs.callback.(metric.Int64ObserverCallback)
var err error var err error
*obsPtr, err = d.RegisterInt64Observer(obs.name, cb, obs.opts...) *implPtr, err = obs.constructor(d)
if err != nil { if err != nil {
// TODO: There is no standard way to deliver this error to the user. // TODO: There is no standard way to deliver this error to the user.
@ -319,34 +287,7 @@ func (obs *obsImpl) setInt64Delegate(d metric.Meter) {
panic(err) panic(err)
} }
atomic.StorePointer(&obs.delegate, unsafe.Pointer(obsPtr)) atomic.StorePointer(&obs.delegate, unsafe.Pointer(implPtr))
}
func (obs int64ObsImpl) Unregister() {
obs.observer.unregister()
}
// Float64Observer delegation
func (obs *obsImpl) setFloat64Delegate(d metric.Meter) {
obsPtr := new(metric.Float64Observer)
cb := obs.callback.(metric.Float64ObserverCallback)
var err error
*obsPtr, err = d.RegisterFloat64Observer(obs.name, cb, obs.opts...)
if err != nil {
// TODO: There is no standard way to deliver this error to the user.
// See https://github.com/open-telemetry/opentelemetry-go/issues/514
// Note that the default SDK will not generate any errors yet, this is
// only for added safety.
panic(err)
}
atomic.StorePointer(&obs.delegate, unsafe.Pointer(obsPtr))
}
func (obs float64ObsImpl) Unregister() {
obs.observer.unregister()
} }
// Metric updates // Metric updates
@ -357,27 +298,27 @@ func (m *meter) RecordBatch(ctx context.Context, labels metric.LabelSet, measure
} }
} }
func (inst *instImpl) RecordOne(ctx context.Context, number core.Number, labels metric.LabelSet) { func (inst *syncImpl) RecordOne(ctx context.Context, number core.Number, labels metric.LabelSet) {
if instPtr := (*metric.InstrumentImpl)(atomic.LoadPointer(&inst.delegate)); instPtr != nil { if instPtr := (*metric.SyncImpl)(atomic.LoadPointer(&inst.delegate)); instPtr != nil {
(*instPtr).RecordOne(ctx, number, labels) (*instPtr).RecordOne(ctx, number, labels)
} }
} }
// Bound instrument initialization // Bound instrument initialization
func (bound *instHandle) RecordOne(ctx context.Context, number core.Number) { func (bound *syncHandle) RecordOne(ctx context.Context, number core.Number) {
instPtr := (*metric.InstrumentImpl)(atomic.LoadPointer(&bound.inst.delegate)) instPtr := (*metric.SyncImpl)(atomic.LoadPointer(&bound.inst.delegate))
if instPtr == nil { if instPtr == nil {
return return
} }
var implPtr *metric.BoundInstrumentImpl var implPtr *metric.BoundSyncImpl
bound.initialize.Do(func() { bound.initialize.Do(func() {
implPtr = new(metric.BoundInstrumentImpl) implPtr = new(metric.BoundSyncImpl)
*implPtr = (*instPtr).Bind(bound.labels) *implPtr = (*instPtr).Bind(bound.labels)
atomic.StorePointer(&bound.delegate, unsafe.Pointer(implPtr)) atomic.StorePointer(&bound.delegate, unsafe.Pointer(implPtr))
}) })
if implPtr == nil { if implPtr == nil {
implPtr = (*metric.BoundInstrumentImpl)(atomic.LoadPointer(&bound.delegate)) implPtr = (*metric.BoundSyncImpl)(atomic.LoadPointer(&bound.delegate))
} }
// This may still be nil if instrument was created and bound // This may still be nil if instrument was created and bound
// without a delegate, then the instrument was set to have a // without a delegate, then the instrument was set to have a
@ -420,78 +361,60 @@ func (labels *labelSet) Delegate() metric.LabelSet {
// Constructors // Constructors
func (m *meter) NewInt64Counter(name string, opts ...metric.Option) (metric.Int64Counter, error) { func (m *meter) NewInt64Counter(name string, opts ...metric.Option) (metric.Int64Counter, error) {
return metric.WrapInt64CounterInstrument(m.newInst(name, counterKind, core.Int64NumberKind, opts)) return metric.WrapInt64CounterInstrument(m.newSync(
metric.NewDescriptor(name, metric.CounterKind, core.Int64NumberKind, opts...),
func(other metric.Meter) (metric.SyncImpl, error) {
return syncCheck(other.NewInt64Counter(name, opts...))
}))
} }
func (m *meter) NewFloat64Counter(name string, opts ...metric.Option) (metric.Float64Counter, error) { func (m *meter) NewFloat64Counter(name string, opts ...metric.Option) (metric.Float64Counter, error) {
return metric.WrapFloat64CounterInstrument(m.newInst(name, counterKind, core.Float64NumberKind, opts)) return metric.WrapFloat64CounterInstrument(m.newSync(
metric.NewDescriptor(name, metric.CounterKind, core.Float64NumberKind, opts...),
func(other metric.Meter) (metric.SyncImpl, error) {
return syncCheck(other.NewFloat64Counter(name, opts...))
}))
} }
func (m *meter) NewInt64Measure(name string, opts ...metric.Option) (metric.Int64Measure, error) { func (m *meter) NewInt64Measure(name string, opts ...metric.Option) (metric.Int64Measure, error) {
return metric.WrapInt64MeasureInstrument(m.newInst(name, measureKind, core.Int64NumberKind, opts)) return metric.WrapInt64MeasureInstrument(m.newSync(
metric.NewDescriptor(name, metric.MeasureKind, core.Int64NumberKind, opts...),
func(other metric.Meter) (metric.SyncImpl, error) {
return syncCheck(other.NewInt64Measure(name, opts...))
}))
} }
func (m *meter) NewFloat64Measure(name string, opts ...metric.Option) (metric.Float64Measure, error) { func (m *meter) NewFloat64Measure(name string, opts ...metric.Option) (metric.Float64Measure, error) {
return metric.WrapFloat64MeasureInstrument(m.newInst(name, measureKind, core.Float64NumberKind, opts)) return metric.WrapFloat64MeasureInstrument(m.newSync(
metric.NewDescriptor(name, metric.MeasureKind, core.Float64NumberKind, opts...),
func(other metric.Meter) (metric.SyncImpl, error) {
return syncCheck(other.NewFloat64Measure(name, opts...))
}))
} }
func (m *meter) RegisterInt64Observer(name string, callback metric.Int64ObserverCallback, opts ...metric.Option) (metric.Int64Observer, error) { func (m *meter) RegisterInt64Observer(name string, callback metric.Int64ObserverCallback, opts ...metric.Option) (metric.Int64Observer, error) {
m.lock.Lock() return metric.WrapInt64ObserverInstrument(m.newAsync(
defer m.lock.Unlock() metric.NewDescriptor(name, metric.ObserverKind, core.Int64NumberKind, opts...),
func(other metric.Meter) (metric.AsyncImpl, error) {
if meterPtr := (*metric.Meter)(atomic.LoadPointer(&m.delegate)); meterPtr != nil { return asyncCheck(other.RegisterInt64Observer(name, callback, opts...))
return (*meterPtr).RegisterInt64Observer(name, callback, opts...) }))
}
obs := &obsImpl{
name: name,
nkind: core.Int64NumberKind,
opts: opts,
meter: m,
callback: callback,
}
m.addObserver(obs)
return int64ObsImpl{
observer: obs,
}, nil
} }
func (m *meter) RegisterFloat64Observer(name string, callback metric.Float64ObserverCallback, opts ...metric.Option) (metric.Float64Observer, error) { func (m *meter) RegisterFloat64Observer(name string, callback metric.Float64ObserverCallback, opts ...metric.Option) (metric.Float64Observer, error) {
m.lock.Lock() return metric.WrapFloat64ObserverInstrument(m.newAsync(
defer m.lock.Unlock() metric.NewDescriptor(name, metric.ObserverKind, core.Float64NumberKind, opts...),
func(other metric.Meter) (metric.AsyncImpl, error) {
if meterPtr := (*metric.Meter)(atomic.LoadPointer(&m.delegate)); meterPtr != nil { return asyncCheck(other.RegisterFloat64Observer(name, callback, opts...))
return (*meterPtr).RegisterFloat64Observer(name, callback, opts...) }))
}
obs := &obsImpl{
name: name,
nkind: core.Float64NumberKind,
opts: opts,
meter: m,
callback: callback,
}
m.addObserver(obs)
return float64ObsImpl{
observer: obs,
}, nil
}
func (m *meter) addObserver(obs *obsImpl) {
if m.liveObservers == nil {
m.liveObservers = make(map[*obsImpl]struct{})
}
m.liveObservers[obs] = struct{}{}
m.orderedObservers = append(m.orderedObservers, obs)
} }
func AtomicFieldOffsets() map[string]uintptr { func AtomicFieldOffsets() map[string]uintptr {
return map[string]uintptr{ return map[string]uintptr{
"meterProvider.delegate": unsafe.Offsetof(meterProvider{}.delegate), "meterProvider.delegate": unsafe.Offsetof(meterProvider{}.delegate),
"meter.delegate": unsafe.Offsetof(meter{}.delegate), "meter.delegate": unsafe.Offsetof(meter{}.delegate),
"instImpl.delegate": unsafe.Offsetof(instImpl{}.delegate), "syncImpl.delegate": unsafe.Offsetof(syncImpl{}.delegate),
"obsImpl.delegate": unsafe.Offsetof(obsImpl{}.delegate), "obsImpl.delegate": unsafe.Offsetof(obsImpl{}.delegate),
"labelSet.delegate": unsafe.Offsetof(labelSet{}.delegate), "labelSet.delegate": unsafe.Offsetof(labelSet{}.delegate),
"instHandle.delegate": unsafe.Offsetof(instHandle{}.delegate), "syncHandle.delegate": unsafe.Offsetof(syncHandle{}.delegate),
} }
} }

View File

@ -60,8 +60,9 @@ func TestDirect(t *testing.T) {
measure.Record(ctx, 3, labels1) measure.Record(ctx, 3, labels1)
second.Record(ctx, 3, labels3) second.Record(ctx, 3, labels3)
mock := sdk.Meter("test1").(*metrictest.Meter) mockImpl, _ := metric.UnwrapImpl(sdk.Meter("test1"))
mock.RunObservers() mock := mockImpl.(*metrictest.Meter)
mock.RunAsyncInstruments()
require.Len(t, mock.MeasurementBatches, 6) require.Len(t, mock.MeasurementBatches, 6)
require.Equal(t, map[core.Key]core.Value{ require.Equal(t, map[core.Key]core.Value{
@ -71,7 +72,7 @@ func TestDirect(t *testing.T) {
require.Equal(t, int64(1), require.Equal(t, int64(1),
mock.MeasurementBatches[0].Measurements[0].Number.AsInt64()) mock.MeasurementBatches[0].Measurements[0].Number.AsInt64())
require.Equal(t, "test.counter", require.Equal(t, "test.counter",
mock.MeasurementBatches[0].Measurements[0].Instrument.Name) mock.MeasurementBatches[0].Measurements[0].Instrument.Descriptor().Name())
require.Equal(t, map[core.Key]core.Value{ require.Equal(t, map[core.Key]core.Value{
lvals1.Key: lvals1.Value, lvals1.Key: lvals1.Value,
@ -81,7 +82,7 @@ func TestDirect(t *testing.T) {
mock.MeasurementBatches[1].Measurements[0].Number.AsFloat64(), mock.MeasurementBatches[1].Measurements[0].Number.AsFloat64(),
0.01) 0.01)
require.Equal(t, "test.measure", require.Equal(t, "test.measure",
mock.MeasurementBatches[1].Measurements[0].Instrument.Name) mock.MeasurementBatches[1].Measurements[0].Instrument.Descriptor().Name())
require.Equal(t, map[core.Key]core.Value{ require.Equal(t, map[core.Key]core.Value{
lvals1.Key: lvals1.Value, lvals1.Key: lvals1.Value,
@ -91,7 +92,7 @@ func TestDirect(t *testing.T) {
mock.MeasurementBatches[2].Measurements[0].Number.AsFloat64(), mock.MeasurementBatches[2].Measurements[0].Number.AsFloat64(),
0.01) 0.01)
require.Equal(t, "test.observer.float", require.Equal(t, "test.observer.float",
mock.MeasurementBatches[2].Measurements[0].Instrument.Name) mock.MeasurementBatches[2].Measurements[0].Instrument.Descriptor().Name())
require.Equal(t, map[core.Key]core.Value{ require.Equal(t, map[core.Key]core.Value{
lvals2.Key: lvals2.Value, lvals2.Key: lvals2.Value,
@ -101,7 +102,7 @@ func TestDirect(t *testing.T) {
mock.MeasurementBatches[3].Measurements[0].Number.AsFloat64(), mock.MeasurementBatches[3].Measurements[0].Number.AsFloat64(),
0.01) 0.01)
require.Equal(t, "test.observer.float", require.Equal(t, "test.observer.float",
mock.MeasurementBatches[3].Measurements[0].Instrument.Name) mock.MeasurementBatches[3].Measurements[0].Instrument.Descriptor().Name())
require.Equal(t, map[core.Key]core.Value{ require.Equal(t, map[core.Key]core.Value{
lvals1.Key: lvals1.Value, lvals1.Key: lvals1.Value,
@ -110,7 +111,7 @@ func TestDirect(t *testing.T) {
require.Equal(t, int64(1), require.Equal(t, int64(1),
mock.MeasurementBatches[4].Measurements[0].Number.AsInt64()) mock.MeasurementBatches[4].Measurements[0].Number.AsInt64())
require.Equal(t, "test.observer.int", require.Equal(t, "test.observer.int",
mock.MeasurementBatches[4].Measurements[0].Instrument.Name) mock.MeasurementBatches[4].Measurements[0].Instrument.Descriptor().Name())
require.Equal(t, map[core.Key]core.Value{ require.Equal(t, map[core.Key]core.Value{
lvals2.Key: lvals2.Value, lvals2.Key: lvals2.Value,
@ -119,10 +120,11 @@ func TestDirect(t *testing.T) {
require.Equal(t, int64(2), require.Equal(t, int64(2),
mock.MeasurementBatches[5].Measurements[0].Number.AsInt64()) mock.MeasurementBatches[5].Measurements[0].Number.AsInt64())
require.Equal(t, "test.observer.int", require.Equal(t, "test.observer.int",
mock.MeasurementBatches[5].Measurements[0].Instrument.Name) mock.MeasurementBatches[5].Measurements[0].Instrument.Descriptor().Name())
// This tests the second Meter instance // This tests the second Meter instance
mock = sdk.Meter("test2").(*metrictest.Meter) mockImpl, _ = metric.UnwrapImpl(sdk.Meter("test2"))
mock = mockImpl.(*metrictest.Meter)
require.Len(t, mock.MeasurementBatches, 1) require.Len(t, mock.MeasurementBatches, 1)
require.Equal(t, map[core.Key]core.Value{ require.Equal(t, map[core.Key]core.Value{
@ -133,7 +135,7 @@ func TestDirect(t *testing.T) {
mock.MeasurementBatches[0].Measurements[0].Number.AsFloat64(), mock.MeasurementBatches[0].Measurements[0].Number.AsFloat64(),
0.01) 0.01)
require.Equal(t, "test.second", require.Equal(t, "test.second",
mock.MeasurementBatches[0].Measurements[0].Instrument.Name) mock.MeasurementBatches[0].Measurements[0].Instrument.Descriptor().Name())
} }
func TestBound(t *testing.T) { func TestBound(t *testing.T) {
@ -162,7 +164,8 @@ func TestBound(t *testing.T) {
boundC.Add(ctx, 1) boundC.Add(ctx, 1)
boundM.Record(ctx, 3) boundM.Record(ctx, 3)
mock := sdk.Meter("test").(*metrictest.Meter) mockImpl, _ := metric.UnwrapImpl(sdk.Meter("test"))
mock := mockImpl.(*metrictest.Meter)
require.Len(t, mock.MeasurementBatches, 2) require.Len(t, mock.MeasurementBatches, 2)
require.Equal(t, map[core.Key]core.Value{ require.Equal(t, map[core.Key]core.Value{
@ -173,7 +176,7 @@ func TestBound(t *testing.T) {
mock.MeasurementBatches[0].Measurements[0].Number.AsFloat64(), mock.MeasurementBatches[0].Measurements[0].Number.AsFloat64(),
0.01) 0.01)
require.Equal(t, "test.counter", require.Equal(t, "test.counter",
mock.MeasurementBatches[0].Measurements[0].Instrument.Name) mock.MeasurementBatches[0].Measurements[0].Instrument.Descriptor().Name())
require.Equal(t, map[core.Key]core.Value{ require.Equal(t, map[core.Key]core.Value{
lvals1.Key: lvals1.Value, lvals1.Key: lvals1.Value,
@ -182,7 +185,7 @@ func TestBound(t *testing.T) {
require.Equal(t, int64(3), require.Equal(t, int64(3),
mock.MeasurementBatches[1].Measurements[0].Number.AsInt64()) mock.MeasurementBatches[1].Measurements[0].Number.AsInt64())
require.Equal(t, "test.measure", require.Equal(t, "test.measure",
mock.MeasurementBatches[1].Measurements[0].Instrument.Name) mock.MeasurementBatches[1].Measurements[0].Instrument.Descriptor().Name())
boundC.Unbind() boundC.Unbind()
boundM.Unbind() boundM.Unbind()
@ -202,13 +205,8 @@ func TestUnbind(t *testing.T) {
measure := Must(glob).NewInt64Measure("test.measure") measure := Must(glob).NewInt64Measure("test.measure")
boundM := measure.Bind(labels1) boundM := measure.Bind(labels1)
observerInt := Must(glob).RegisterInt64Observer("test.observer.int", nil)
observerFloat := Must(glob).RegisterFloat64Observer("test.observer.float", nil)
boundC.Unbind() boundC.Unbind()
boundM.Unbind() boundM.Unbind()
observerInt.Unregister()
observerFloat.Unregister()
} }
func TestDefaultSDK(t *testing.T) { func TestDefaultSDK(t *testing.T) {
@ -262,7 +260,8 @@ func TestUnbindThenRecordOne(t *testing.T) {
require.NotPanics(t, func() { require.NotPanics(t, func() {
boundC.Add(ctx, 1) boundC.Add(ctx, 1)
}) })
mock := global.Meter("test").(*metrictest.Meter) mockImpl, _ := metric.UnwrapImpl(global.Meter("test"))
mock := mockImpl.(*metrictest.Meter)
require.Equal(t, 0, len(mock.MeasurementBatches)) require.Equal(t, 0, len(mock.MeasurementBatches))
} }
@ -300,3 +299,54 @@ func TestErrorInDeferredConstructor(t *testing.T) {
c1.Add(ctx, 1, meter.Labels()) c1.Add(ctx, 1, meter.Labels())
c2.Add(ctx, 2, meter.Labels()) c2.Add(ctx, 2, meter.Labels())
} }
func TestImplementationIndirection(t *testing.T) {
internal.ResetForTest()
// Test that Implementation() does the proper indirection, i.e.,
// returns the implementation interface not the global, after
// registered.
meter1 := global.Meter("test1")
// Sync: no SDK yet
counter := Must(meter1).NewInt64Counter("interface.counter")
ival := counter.Measurement(1).SyncImpl().Implementation()
require.NotNil(t, ival)
_, ok := ival.(*metrictest.Sync)
require.False(t, ok)
// Async: no SDK yet
observer := Must(meter1).RegisterFloat64Observer(
"interface.observer",
func(result metric.Float64ObserverResult) {},
)
ival = observer.AsyncImpl().Implementation()
require.NotNil(t, ival)
_, ok = ival.(*metrictest.Async)
require.False(t, ok)
// Register the SDK
sdk := metrictest.NewProvider()
global.SetMeterProvider(sdk)
// Repeat the above tests
// Sync
ival = counter.Measurement(1).SyncImpl().Implementation()
require.NotNil(t, ival)
_, ok = ival.(*metrictest.Sync)
require.True(t, ok)
// Async
ival = observer.AsyncImpl().Implementation()
require.NotNil(t, ival)
_, ok = ival.(*metrictest.Async)
require.True(t, ok)
}

View File

@ -12,6 +12,8 @@
// See the License for the specific language governing permissions and // See the License for the specific language governing permissions and
// limitations under the License. // limitations under the License.
//go:generate stringer -type=Kind
package metric package metric
import ( import (
@ -57,13 +59,13 @@ type Option interface {
type Measurement struct { type Measurement struct {
// number needs to be aligned for 64-bit atomic operations. // number needs to be aligned for 64-bit atomic operations.
number core.Number number core.Number
instrument InstrumentImpl instrument SyncImpl
} }
// Instrument returns the instrument that created this measurement. // SyncImpl returns the instrument that created this measurement.
// This returns an implementation-level object for use by the SDK, // This returns an implementation-level object for use by the SDK,
// users should not refer to this. // users should not refer to this.
func (m Measurement) InstrumentImpl() InstrumentImpl { func (m Measurement) SyncImpl() SyncImpl {
return m.instrument return m.instrument
} }
@ -72,6 +74,73 @@ func (m Measurement) Number() core.Number {
return m.number return m.number
} }
// Kind describes the kind of instrument.
type Kind int8
const (
// MeasureKind indicates a Measure instrument.
MeasureKind Kind = iota
// ObserverKind indicates an Observer instrument.
ObserverKind
// CounterKind indicates a Counter instrument.
CounterKind
)
// Descriptor contains all the settings that describe an instrument,
// including its name, metric kind, number kind, and the configurable
// options.
type Descriptor struct {
name string
kind Kind
numberKind core.NumberKind
config Config
}
// NewDescriptor returns a Descriptor with the given contents.
func NewDescriptor(name string, mkind Kind, nkind core.NumberKind, opts ...Option) Descriptor {
return Descriptor{
name: name,
kind: mkind,
numberKind: nkind,
config: Configure(opts),
}
}
// Name returns the metric instrument's name.
func (d Descriptor) Name() string {
return d.name
}
// MetricKind returns the specific kind of instrument.
func (d Descriptor) MetricKind() Kind {
return d.kind
}
// Keys returns the recommended keys included in the metric
// definition. These keys may be used by a Batcher as a default set
// of grouping keys for the metric instrument.
func (d Descriptor) Keys() []core.Key {
return d.config.Keys
}
// Description provides a human-readable description of the metric
// instrument.
func (d Descriptor) Description() string {
return d.config.Description
}
// Unit describes the units of the metric instrument. Unitless
// metrics return the empty string.
func (d Descriptor) Unit() unit.Unit {
return d.config.Unit
}
// NumberKind returns whether this instrument is declared over int64,
// float64, or uint64 values.
func (d Descriptor) NumberKind() core.NumberKind {
return d.numberKind
}
// Meter is an interface to the metrics portion of the OpenTelemetry SDK. // Meter is an interface to the metrics portion of the OpenTelemetry SDK.
type Meter interface { type Meter interface {
// Labels returns a reference to a set of labels that cannot // Labels returns a reference to a set of labels that cannot
@ -110,38 +179,6 @@ type Meter interface {
RegisterFloat64Observer(name string, callback Float64ObserverCallback, opts ...Option) (Float64Observer, error) RegisterFloat64Observer(name string, callback Float64ObserverCallback, opts ...Option) (Float64Observer, error)
} }
// Int64ObserverResult is an interface for reporting integral
// observations.
type Int64ObserverResult interface {
Observe(value int64, labels LabelSet)
}
// Float64ObserverResult is an interface for reporting floating point
// observations.
type Float64ObserverResult interface {
Observe(value float64, labels LabelSet)
}
// Int64ObserverCallback is a type of callback that integral
// observers run.
type Int64ObserverCallback func(result Int64ObserverResult)
// Float64ObserverCallback is a type of callback that floating point
// observers run.
type Float64ObserverCallback func(result Float64ObserverResult)
// Int64Observer is a metric that captures a set of int64 values at a
// point in time.
type Int64Observer interface {
Unregister()
}
// Float64Observer is a metric that captures a set of float64 values
// at a point in time.
type Float64Observer interface {
Unregister()
}
// WithDescription applies provided description. // WithDescription applies provided description.
func WithDescription(desc string) Option { func WithDescription(desc string) Option {
return descriptionOption(desc) return descriptionOption(desc)

View File

@ -24,7 +24,7 @@ import (
"go.opentelemetry.io/otel/api/key" "go.opentelemetry.io/otel/api/key"
"go.opentelemetry.io/otel/api/metric" "go.opentelemetry.io/otel/api/metric"
"go.opentelemetry.io/otel/api/unit" "go.opentelemetry.io/otel/api/unit"
mock "go.opentelemetry.io/otel/internal/metric" mockTest "go.opentelemetry.io/otel/internal/metric"
"github.com/google/go-cmp/cmp" "github.com/google/go-cmp/cmp"
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
@ -117,7 +117,7 @@ func TestOptions(t *testing.T) {
func TestCounter(t *testing.T) { func TestCounter(t *testing.T) {
{ {
meter := mock.NewMeter() mockSDK, meter := mockTest.NewMeter()
c := Must(meter).NewFloat64Counter("test.counter.float") c := Must(meter).NewFloat64Counter("test.counter.float")
ctx := context.Background() ctx := context.Background()
labels := meter.Labels() labels := meter.Labels()
@ -126,10 +126,10 @@ func TestCounter(t *testing.T) {
boundInstrument.Add(ctx, 42) boundInstrument.Add(ctx, 42)
meter.RecordBatch(ctx, labels, c.Measurement(42)) meter.RecordBatch(ctx, labels, c.Measurement(42))
t.Log("Testing float counter") t.Log("Testing float counter")
checkBatches(t, ctx, labels, meter, core.Float64NumberKind, c.Impl()) checkBatches(t, ctx, labels, mockSDK, core.Float64NumberKind, c.SyncImpl())
} }
{ {
meter := mock.NewMeter() mockSDK, meter := mockTest.NewMeter()
c := Must(meter).NewInt64Counter("test.counter.int") c := Must(meter).NewInt64Counter("test.counter.int")
ctx := context.Background() ctx := context.Background()
labels := meter.Labels() labels := meter.Labels()
@ -138,13 +138,13 @@ func TestCounter(t *testing.T) {
boundInstrument.Add(ctx, 42) boundInstrument.Add(ctx, 42)
meter.RecordBatch(ctx, labels, c.Measurement(42)) meter.RecordBatch(ctx, labels, c.Measurement(42))
t.Log("Testing int counter") t.Log("Testing int counter")
checkBatches(t, ctx, labels, meter, core.Int64NumberKind, c.Impl()) checkBatches(t, ctx, labels, mockSDK, core.Int64NumberKind, c.SyncImpl())
} }
} }
func TestMeasure(t *testing.T) { func TestMeasure(t *testing.T) {
{ {
meter := mock.NewMeter() mockSDK, meter := mockTest.NewMeter()
m := Must(meter).NewFloat64Measure("test.measure.float") m := Must(meter).NewFloat64Measure("test.measure.float")
ctx := context.Background() ctx := context.Background()
labels := meter.Labels() labels := meter.Labels()
@ -153,10 +153,10 @@ func TestMeasure(t *testing.T) {
boundInstrument.Record(ctx, 42) boundInstrument.Record(ctx, 42)
meter.RecordBatch(ctx, labels, m.Measurement(42)) meter.RecordBatch(ctx, labels, m.Measurement(42))
t.Log("Testing float measure") t.Log("Testing float measure")
checkBatches(t, ctx, labels, meter, core.Float64NumberKind, m.Impl()) checkBatches(t, ctx, labels, mockSDK, core.Float64NumberKind, m.SyncImpl())
} }
{ {
meter := mock.NewMeter() mockSDK, meter := mockTest.NewMeter()
m := Must(meter).NewInt64Measure("test.measure.int") m := Must(meter).NewInt64Measure("test.measure.int")
ctx := context.Background() ctx := context.Background()
labels := meter.Labels() labels := meter.Labels()
@ -165,46 +165,47 @@ func TestMeasure(t *testing.T) {
boundInstrument.Record(ctx, 42) boundInstrument.Record(ctx, 42)
meter.RecordBatch(ctx, labels, m.Measurement(42)) meter.RecordBatch(ctx, labels, m.Measurement(42))
t.Log("Testing int measure") t.Log("Testing int measure")
checkBatches(t, ctx, labels, meter, core.Int64NumberKind, m.Impl()) checkBatches(t, ctx, labels, mockSDK, core.Int64NumberKind, m.SyncImpl())
} }
} }
func TestObserver(t *testing.T) { func TestObserver(t *testing.T) {
{ {
meter := mock.NewMeter() mockSDK, meter := mockTest.NewMeter()
labels := meter.Labels() labels := meter.Labels()
o := Must(meter).RegisterFloat64Observer("test.observer.float", func(result metric.Float64ObserverResult) { o := Must(meter).RegisterFloat64Observer("test.observer.float", func(result metric.Float64ObserverResult) {
result.Observe(42, labels) result.Observe(42, labels)
}) })
t.Log("Testing float observer") t.Log("Testing float observer")
meter.RunObservers()
checkObserverBatch(t, labels, meter, core.Float64NumberKind, o) mockSDK.RunAsyncInstruments()
checkObserverBatch(t, labels, mockSDK, core.Float64NumberKind, o.AsyncImpl())
} }
{ {
meter := mock.NewMeter() mockSDK, meter := mockTest.NewMeter()
labels := meter.Labels() labels := meter.Labels()
o := Must(meter).RegisterInt64Observer("test.observer.int", func(result metric.Int64ObserverResult) { o := Must(meter).RegisterInt64Observer("test.observer.int", func(result metric.Int64ObserverResult) {
result.Observe(42, labels) result.Observe(42, labels)
}) })
t.Log("Testing int observer") t.Log("Testing int observer")
meter.RunObservers() mockSDK.RunAsyncInstruments()
checkObserverBatch(t, labels, meter, core.Int64NumberKind, o) checkObserverBatch(t, labels, mockSDK, core.Int64NumberKind, o.AsyncImpl())
} }
} }
func checkBatches(t *testing.T, ctx context.Context, labels metric.LabelSet, meter *mock.Meter, kind core.NumberKind, instrument metric.InstrumentImpl) { func checkBatches(t *testing.T, ctx context.Context, labels metric.LabelSet, mock *mockTest.Meter, kind core.NumberKind, instrument metric.InstrumentImpl) {
t.Helper() t.Helper()
if len(meter.MeasurementBatches) != 3 { if len(mock.MeasurementBatches) != 3 {
t.Errorf("Expected 3 recorded measurement batches, got %d", len(meter.MeasurementBatches)) t.Errorf("Expected 3 recorded measurement batches, got %d", len(mock.MeasurementBatches))
} }
ourInstrument := instrument.(*mock.Instrument) ourInstrument := instrument.Implementation().(*mockTest.Sync)
ourLabelSet := labels.(*mock.LabelSet) ourLabelSet := labels.(*mockTest.LabelSet)
minLen := 3 minLen := 3
if minLen > len(meter.MeasurementBatches) { if minLen > len(mock.MeasurementBatches) {
minLen = len(meter.MeasurementBatches) minLen = len(mock.MeasurementBatches)
} }
for i := 0; i < minLen; i++ { for i := 0; i < minLen; i++ {
got := meter.MeasurementBatches[i] got := mock.MeasurementBatches[i]
if got.Ctx != ctx { if got.Ctx != ctx {
d := func(c context.Context) string { d := func(c context.Context) string {
return fmt.Sprintf("(ptr: %p, ctx %#v)", c, c) return fmt.Sprintf("(ptr: %p, ctx %#v)", c, c)
@ -212,7 +213,7 @@ func checkBatches(t *testing.T, ctx context.Context, labels metric.LabelSet, met
t.Errorf("Wrong recorded context in batch %d, expected %s, got %s", i, d(ctx), d(got.Ctx)) t.Errorf("Wrong recorded context in batch %d, expected %s, got %s", i, d(ctx), d(got.Ctx))
} }
if got.LabelSet != ourLabelSet { if got.LabelSet != ourLabelSet {
d := func(l *mock.LabelSet) string { d := func(l *mockTest.LabelSet) string {
return fmt.Sprintf("(ptr: %p, labels %#v)", l, l.Labels) return fmt.Sprintf("(ptr: %p, labels %#v)", l, l.Labels)
} }
t.Errorf("Wrong recorded label set in batch %d, expected %s, got %s", i, d(ourLabelSet), d(got.LabelSet)) t.Errorf("Wrong recorded label set in batch %d, expected %s, got %s", i, d(ourLabelSet), d(got.LabelSet))
@ -226,11 +227,12 @@ func checkBatches(t *testing.T, ctx context.Context, labels metric.LabelSet, met
} }
for j := 0; j < minMLen; j++ { for j := 0; j < minMLen; j++ {
measurement := got.Measurements[j] measurement := got.Measurements[j]
if measurement.Instrument != ourInstrument { if measurement.Instrument.Implementation() != ourInstrument {
d := func(i *mock.Instrument) string { d := func(iface interface{}) string {
i := iface.(*mockTest.Instrument)
return fmt.Sprintf("(ptr: %p, instrument %#v)", i, i) return fmt.Sprintf("(ptr: %p, instrument %#v)", i, i)
} }
t.Errorf("Wrong recorded instrument in measurement %d in batch %d, expected %s, got %s", j, i, d(ourInstrument), d(measurement.Instrument)) t.Errorf("Wrong recorded instrument in measurement %d in batch %d, expected %s, got %s", j, i, d(ourInstrument), d(measurement.Instrument.Implementation()))
} }
ft := fortyTwo(t, kind) ft := fortyTwo(t, kind)
if measurement.Number.CompareNumber(kind, ft) != 0 { if measurement.Number.CompareNumber(kind, ft) != 0 {
@ -240,25 +242,25 @@ func checkBatches(t *testing.T, ctx context.Context, labels metric.LabelSet, met
} }
} }
func checkObserverBatch(t *testing.T, labels metric.LabelSet, meter *mock.Meter, kind core.NumberKind, observer interface{}) { func checkObserverBatch(t *testing.T, labels metric.LabelSet, mock *mockTest.Meter, kind core.NumberKind, observer metric.AsyncImpl) {
t.Helper() t.Helper()
assert.Len(t, meter.MeasurementBatches, 1) assert.Len(t, mock.MeasurementBatches, 1)
if len(meter.MeasurementBatches) < 1 { if len(mock.MeasurementBatches) < 1 {
return return
} }
o := observer.(*mock.Observer) o := observer.Implementation().(*mockTest.Async)
if !assert.NotNil(t, o) { if !assert.NotNil(t, o) {
return return
} }
ourLabelSet := labels.(*mock.LabelSet) ourLabelSet := labels.(*mockTest.LabelSet)
got := meter.MeasurementBatches[0] got := mock.MeasurementBatches[0]
assert.Equal(t, ourLabelSet, got.LabelSet) assert.Equal(t, ourLabelSet, got.LabelSet)
assert.Len(t, got.Measurements, 1) assert.Len(t, got.Measurements, 1)
if len(got.Measurements) < 1 { if len(got.Measurements) < 1 {
return return
} }
measurement := got.Measurements[0] measurement := got.Measurements[0]
assert.Equal(t, o.Instrument, measurement.Instrument) assert.Equal(t, o, measurement.Instrument.Implementation().(*mockTest.Async))
ft := fortyTwo(t, kind) ft := fortyTwo(t, kind)
assert.Equal(t, 0, measurement.Number.CompareNumber(kind, ft)) assert.Equal(t, 0, measurement.Number.CompareNumber(kind, ft))
} }
@ -275,36 +277,47 @@ func fortyTwo(t *testing.T, kind core.NumberKind) core.Number {
return core.NewInt64Number(0) return core.NewInt64Number(0)
} }
type testWrappedInst struct{} type testWrappedMeter struct {
func (*testWrappedInst) Bind(labels metric.LabelSet) metric.BoundInstrumentImpl {
panic("Not called")
} }
func (*testWrappedInst) RecordOne(ctx context.Context, number core.Number, labels metric.LabelSet) { var _ metric.MeterImpl = testWrappedMeter{}
panic("Not called")
func (testWrappedMeter) Labels(...core.KeyValue) metric.LabelSet {
return nil
}
func (testWrappedMeter) RecordBatch(context.Context, metric.LabelSet, ...metric.Measurement) {
}
func (testWrappedMeter) NewSyncInstrument(_ metric.Descriptor) (metric.SyncImpl, error) {
return nil, nil
}
func (testWrappedMeter) NewAsyncInstrument(_ metric.Descriptor, _ func(func(core.Number, metric.LabelSet))) (metric.AsyncImpl, error) {
return nil, errors.New("Test wrap error")
} }
func TestWrappedInstrumentError(t *testing.T) { func TestWrappedInstrumentError(t *testing.T) {
i0 := &testWrappedInst{} impl := &testWrappedMeter{}
e0 := errors.New("Test wrap error") meter := metric.WrapMeterImpl(impl)
inst, err := metric.WrapInt64MeasureInstrument(i0, e0)
// Check that error passes through w/o modifying instrument. measure, err := meter.NewInt64Measure("test.measure")
require.Equal(t, inst.Impl().(*testWrappedInst), i0)
require.Equal(t, err, e0)
// Check that nil instrument is handled. require.Equal(t, err, metric.ErrSDKReturnedNilImpl)
inst, err = metric.WrapInt64MeasureInstrument(nil, e0) require.NotNil(t, measure.SyncImpl())
require.Equal(t, err, e0) observer, err := meter.RegisterInt64Observer("test.observer", func(result metric.Int64ObserverResult) {})
require.NotNil(t, inst)
require.NotNil(t, inst.Impl())
// Check that nil instrument generates an error.
inst, err = metric.WrapInt64MeasureInstrument(nil, nil)
require.NotNil(t, err) require.NotNil(t, err)
require.NotNil(t, inst) require.NotNil(t, observer.AsyncImpl())
require.NotNil(t, inst.Impl()) }
func TestNilCallbackObserverNoop(t *testing.T) {
// Tests that a nil callback yields a no-op observer without error.
_, meter := mockTest.NewMeter()
observer := Must(meter).RegisterInt64Observer("test.observer", nil)
_, ok := observer.AsyncImpl().(metric.NoopAsync)
require.True(t, ok)
} }

View File

@ -21,45 +21,56 @@ import (
"go.opentelemetry.io/otel/api/core" "go.opentelemetry.io/otel/api/core"
) )
type commonMetric struct { type syncInstrument struct {
instrument InstrumentImpl instrument SyncImpl
} }
type commonBoundInstrument struct { type syncBoundInstrument struct {
boundInstrument BoundInstrumentImpl boundInstrument BoundSyncImpl
}
type asyncInstrument struct {
instrument AsyncImpl
} }
var ErrSDKReturnedNilImpl = errors.New("SDK returned a nil implementation") var ErrSDKReturnedNilImpl = errors.New("SDK returned a nil implementation")
func (m commonMetric) bind(labels LabelSet) commonBoundInstrument { func (s syncInstrument) bind(labels LabelSet) syncBoundInstrument {
return newCommonBoundInstrument(m.instrument.Bind(labels)) return newSyncBoundInstrument(s.instrument.Bind(labels))
} }
func (m commonMetric) float64Measurement(value float64) Measurement { func (s syncInstrument) float64Measurement(value float64) Measurement {
return newMeasurement(m.instrument, core.NewFloat64Number(value)) return newMeasurement(s.instrument, core.NewFloat64Number(value))
} }
func (m commonMetric) int64Measurement(value int64) Measurement { func (s syncInstrument) int64Measurement(value int64) Measurement {
return newMeasurement(m.instrument, core.NewInt64Number(value)) return newMeasurement(s.instrument, core.NewInt64Number(value))
} }
func (m commonMetric) directRecord(ctx context.Context, number core.Number, labels LabelSet) { func (s syncInstrument) directRecord(ctx context.Context, number core.Number, labels LabelSet) {
m.instrument.RecordOne(ctx, number, labels) s.instrument.RecordOne(ctx, number, labels)
} }
func (m commonMetric) Impl() InstrumentImpl { func (s syncInstrument) SyncImpl() SyncImpl {
return m.instrument return s.instrument
} }
func (h commonBoundInstrument) directRecord(ctx context.Context, number core.Number) { func (h syncBoundInstrument) directRecord(ctx context.Context, number core.Number) {
h.boundInstrument.RecordOne(ctx, number) h.boundInstrument.RecordOne(ctx, number)
} }
func (h commonBoundInstrument) Unbind() { func (h syncBoundInstrument) Unbind() {
h.boundInstrument.Unbind() h.boundInstrument.Unbind()
} }
func newCommonMetric(instrument InstrumentImpl, err error) (commonMetric, error) { func (a asyncInstrument) AsyncImpl() AsyncImpl {
return a.instrument
}
// checkNewSync receives an SyncImpl and potential
// error, and returns the same types, checking for and ensuring that
// the returned interface is not nil.
func checkNewSync(instrument SyncImpl, err error) (syncInstrument, error) {
if instrument == nil { if instrument == nil {
if err == nil { if err == nil {
err = ErrSDKReturnedNilImpl err = ErrSDKReturnedNilImpl
@ -69,22 +80,37 @@ func newCommonMetric(instrument InstrumentImpl, err error) (commonMetric, error)
// together and use a tag for the original name, e.g., // together and use a tag for the original name, e.g.,
// name = 'invalid.counter.int64' // name = 'invalid.counter.int64'
// label = 'original-name=duplicate-counter-name' // label = 'original-name=duplicate-counter-name'
instrument = noopInstrument{} instrument = NoopSync{}
} }
return commonMetric{ return syncInstrument{
instrument: instrument, instrument: instrument,
}, err }, err
} }
func newCommonBoundInstrument(boundInstrument BoundInstrumentImpl) commonBoundInstrument { func newSyncBoundInstrument(boundInstrument BoundSyncImpl) syncBoundInstrument {
return commonBoundInstrument{ return syncBoundInstrument{
boundInstrument: boundInstrument, boundInstrument: boundInstrument,
} }
} }
func newMeasurement(instrument InstrumentImpl, number core.Number) Measurement { func newMeasurement(instrument SyncImpl, number core.Number) Measurement {
return Measurement{ return Measurement{
instrument: instrument, instrument: instrument,
number: number, number: number,
} }
} }
// checkNewAsync receives an AsyncImpl and potential
// error, and returns the same types, checking for and ensuring that
// the returned interface is not nil.
func checkNewAsync(instrument AsyncImpl, err error) (asyncInstrument, error) {
if instrument == nil {
if err == nil {
err = ErrSDKReturnedNilImpl
}
instrument = NoopAsync{}
}
return asyncInstrument{
instrument: instrument,
}, err
}

View File

@ -22,26 +22,26 @@ import (
// Float64Counter is a metric that accumulates float64 values. // Float64Counter is a metric that accumulates float64 values.
type Float64Counter struct { type Float64Counter struct {
commonMetric syncInstrument
} }
// Int64Counter is a metric that accumulates int64 values. // Int64Counter is a metric that accumulates int64 values.
type Int64Counter struct { type Int64Counter struct {
commonMetric syncInstrument
} }
// BoundFloat64Counter is a bound instrument for Float64Counter. // BoundFloat64Counter is a bound instrument for Float64Counter.
// //
// It inherits the Unbind function from commonBoundInstrument. // It inherits the Unbind function from syncBoundInstrument.
type BoundFloat64Counter struct { type BoundFloat64Counter struct {
commonBoundInstrument syncBoundInstrument
} }
// BoundInt64Counter is a boundInstrument for Int64Counter. // BoundInt64Counter is a boundInstrument for Int64Counter.
// //
// It inherits the Unbind function from commonBoundInstrument. // It inherits the Unbind function from syncBoundInstrument.
type BoundInt64Counter struct { type BoundInt64Counter struct {
commonBoundInstrument syncBoundInstrument
} }
// Bind creates a bound instrument for this counter. The labels should // Bind creates a bound instrument for this counter. The labels should
@ -52,7 +52,7 @@ type BoundInt64Counter struct {
// counter with the WithKeys option, then the missing value will be // counter with the WithKeys option, then the missing value will be
// treated as unspecified. // treated as unspecified.
func (c Float64Counter) Bind(labels LabelSet) (h BoundFloat64Counter) { func (c Float64Counter) Bind(labels LabelSet) (h BoundFloat64Counter) {
h.commonBoundInstrument = c.bind(labels) h.syncBoundInstrument = c.bind(labels)
return return
} }
@ -64,7 +64,7 @@ func (c Float64Counter) Bind(labels LabelSet) (h BoundFloat64Counter) {
// counter with the WithKeys option, then the missing value will be // counter with the WithKeys option, then the missing value will be
// treated as unspecified. // treated as unspecified.
func (c Int64Counter) Bind(labels LabelSet) (h BoundInt64Counter) { func (c Int64Counter) Bind(labels LabelSet) (h BoundInt64Counter) {
h.commonBoundInstrument = c.bind(labels) h.syncBoundInstrument = c.bind(labels)
return return
} }

View File

@ -61,8 +61,7 @@
// function - this permits the reported values only to go // function - this permits the reported values only to go
// up. Reporting of the new values happens asynchronously, with the // up. Reporting of the new values happens asynchronously, with the
// use of a callback passed to the Register*Observer function. The // use of a callback passed to the Register*Observer function. The
// callback can report multiple values. To unregister the observer, // callback can report multiple values. There is no unregister function.
// call Unregister on it.
// //
// Counters and measures support creating bound instruments for a // Counters and measures support creating bound instruments for a
// potentially more efficient reporting. The bound instruments have // potentially more efficient reporting. The bound instruments have

View File

@ -8,14 +8,14 @@ func _() {
// An "invalid array index" compiler error signifies that the constant values have changed. // An "invalid array index" compiler error signifies that the constant values have changed.
// Re-run the stringer command to generate them again. // Re-run the stringer command to generate them again.
var x [1]struct{} var x [1]struct{}
_ = x[CounterKind-0] _ = x[MeasureKind-0]
_ = x[MeasureKind-1] _ = x[ObserverKind-1]
_ = x[ObserverKind-2] _ = x[CounterKind-2]
} }
const _Kind_name = "CounterKindMeasureKindObserverKind" const _Kind_name = "MeasureKindObserverKindCounterKind"
var _Kind_index = [...]uint8{0, 11, 22, 34} var _Kind_index = [...]uint8{0, 11, 23, 34}
func (i Kind) String() string { func (i Kind) String() string {
if i < 0 || i >= Kind(len(_Kind_index)-1) { if i < 0 || i >= Kind(len(_Kind_index)-1) {

View File

@ -22,26 +22,26 @@ import (
// Float64Measure is a metric that records float64 values. // Float64Measure is a metric that records float64 values.
type Float64Measure struct { type Float64Measure struct {
commonMetric syncInstrument
} }
// Int64Measure is a metric that records int64 values. // Int64Measure is a metric that records int64 values.
type Int64Measure struct { type Int64Measure struct {
commonMetric syncInstrument
} }
// BoundFloat64Measure is a bound instrument for Float64Measure. // BoundFloat64Measure is a bound instrument for Float64Measure.
// //
// It inherits the Unbind function from commonBoundInstrument. // It inherits the Unbind function from syncBoundInstrument.
type BoundFloat64Measure struct { type BoundFloat64Measure struct {
commonBoundInstrument syncBoundInstrument
} }
// BoundInt64Measure is a bound instrument for Int64Measure. // BoundInt64Measure is a bound instrument for Int64Measure.
// //
// It inherits the Unbind function from commonBoundInstrument. // It inherits the Unbind function from syncBoundInstrument.
type BoundInt64Measure struct { type BoundInt64Measure struct {
commonBoundInstrument syncBoundInstrument
} }
// Bind creates a bound instrument for this measure. The labels should // Bind creates a bound instrument for this measure. The labels should
@ -52,7 +52,7 @@ type BoundInt64Measure struct {
// measure with the WithKeys option, then the missing value will be // measure with the WithKeys option, then the missing value will be
// treated as unspecified. // treated as unspecified.
func (c Float64Measure) Bind(labels LabelSet) (h BoundFloat64Measure) { func (c Float64Measure) Bind(labels LabelSet) (h BoundFloat64Measure) {
h.commonBoundInstrument = c.bind(labels) h.syncBoundInstrument = c.bind(labels)
return return
} }
@ -64,7 +64,7 @@ func (c Float64Measure) Bind(labels LabelSet) (h BoundFloat64Measure) {
// measure with the WithKeys option, then the missing value will be // measure with the WithKeys option, then the missing value will be
// treated as unspecified. // treated as unspecified.
func (c Int64Measure) Bind(labels LabelSet) (h BoundInt64Measure) { func (c Int64Measure) Bind(labels LabelSet) (h BoundInt64Measure) {
h.commonBoundInstrument = c.bind(labels) h.syncBoundInstrument = c.bind(labels)
return return
} }

View File

@ -7,49 +7,44 @@ import (
) )
type NoopProvider struct{} type NoopProvider struct{}
type NoopMeter struct{}
type NoopMeter struct {
}
type noopBoundInstrument struct{}
type noopLabelSet struct{} type noopLabelSet struct{}
type noopInstrument struct{} type noopInstrument struct{}
type noopInt64Observer struct{} type noopBoundInstrument struct{}
type noopFloat64Observer struct{} type NoopSync struct{ noopInstrument }
type NoopAsync struct{ noopInstrument }
var _ Provider = NoopProvider{} var _ Provider = NoopProvider{}
var _ Meter = NoopMeter{} var _ Meter = NoopMeter{}
var _ InstrumentImpl = noopInstrument{} var _ SyncImpl = NoopSync{}
var _ BoundInstrumentImpl = noopBoundInstrument{} var _ BoundSyncImpl = noopBoundInstrument{}
var _ LabelSet = noopLabelSet{} var _ LabelSet = noopLabelSet{}
var _ Int64Observer = noopInt64Observer{} var _ AsyncImpl = NoopAsync{}
var _ Float64Observer = noopFloat64Observer{}
func (NoopProvider) Meter(name string) Meter { func (NoopProvider) Meter(name string) Meter {
return NoopMeter{} return NoopMeter{}
} }
func (noopInstrument) Implementation() interface{} {
return nil
}
func (noopInstrument) Descriptor() Descriptor {
return Descriptor{}
}
func (noopBoundInstrument) RecordOne(context.Context, core.Number) { func (noopBoundInstrument) RecordOne(context.Context, core.Number) {
} }
func (noopBoundInstrument) Unbind() { func (noopBoundInstrument) Unbind() {
} }
func (noopInstrument) Bind(LabelSet) BoundInstrumentImpl { func (NoopSync) Bind(LabelSet) BoundSyncImpl {
return noopBoundInstrument{} return noopBoundInstrument{}
} }
func (noopInstrument) RecordOne(context.Context, core.Number, LabelSet) { func (NoopSync) RecordOne(context.Context, core.Number, LabelSet) {
}
func (noopInstrument) Meter() Meter {
return NoopMeter{}
}
func (noopInt64Observer) Unregister() {
}
func (noopFloat64Observer) Unregister() {
} }
func (NoopMeter) Labels(...core.KeyValue) LabelSet { func (NoopMeter) Labels(...core.KeyValue) LabelSet {
@ -59,26 +54,26 @@ func (NoopMeter) Labels(...core.KeyValue) LabelSet {
func (NoopMeter) RecordBatch(context.Context, LabelSet, ...Measurement) { func (NoopMeter) RecordBatch(context.Context, LabelSet, ...Measurement) {
} }
func (NoopMeter) NewInt64Counter(name string, cos ...Option) (Int64Counter, error) { func (NoopMeter) NewInt64Counter(string, ...Option) (Int64Counter, error) {
return WrapInt64CounterInstrument(noopInstrument{}, nil) return Int64Counter{syncInstrument{NoopSync{}}}, nil
} }
func (NoopMeter) NewFloat64Counter(name string, cos ...Option) (Float64Counter, error) { func (NoopMeter) NewFloat64Counter(string, ...Option) (Float64Counter, error) {
return WrapFloat64CounterInstrument(noopInstrument{}, nil) return Float64Counter{syncInstrument{NoopSync{}}}, nil
} }
func (NoopMeter) NewInt64Measure(name string, mos ...Option) (Int64Measure, error) { func (NoopMeter) NewInt64Measure(string, ...Option) (Int64Measure, error) {
return WrapInt64MeasureInstrument(noopInstrument{}, nil) return Int64Measure{syncInstrument{NoopSync{}}}, nil
} }
func (NoopMeter) NewFloat64Measure(name string, mos ...Option) (Float64Measure, error) { func (NoopMeter) NewFloat64Measure(string, ...Option) (Float64Measure, error) {
return WrapFloat64MeasureInstrument(noopInstrument{}, nil) return Float64Measure{syncInstrument{NoopSync{}}}, nil
} }
func (NoopMeter) RegisterInt64Observer(name string, callback Int64ObserverCallback, oos ...Option) (Int64Observer, error) { func (NoopMeter) RegisterInt64Observer(string, Int64ObserverCallback, ...Option) (Int64Observer, error) {
return noopInt64Observer{}, nil return Int64Observer{asyncInstrument{NoopAsync{}}}, nil
} }
func (NoopMeter) RegisterFloat64Observer(name string, callback Float64ObserverCallback, oos ...Option) (Float64Observer, error) { func (NoopMeter) RegisterFloat64Observer(string, Float64ObserverCallback, ...Option) (Float64Observer, error) {
return noopFloat64Observer{}, nil return Float64Observer{asyncInstrument{NoopAsync{}}}, nil
} }

47
api/metric/observer.go Normal file
View File

@ -0,0 +1,47 @@
// Copyright 2020, 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
// Int64ObserverResult is an interface for reporting integral
// observations.
type Int64ObserverResult interface {
Observe(value int64, labels LabelSet)
}
// Float64ObserverResult is an interface for reporting floating point
// observations.
type Float64ObserverResult interface {
Observe(value float64, labels LabelSet)
}
// Int64ObserverCallback is a type of callback that integral
// observers run.
type Int64ObserverCallback func(result Int64ObserverResult)
// Float64ObserverCallback is a type of callback that floating point
// observers run.
type Float64ObserverCallback func(result Float64ObserverResult)
// Int64Observer is a metric that captures a set of int64 values at a
// point in time.
type Int64Observer struct {
asyncInstrument
}
// Float64Observer is a metric that captures a set of float64 values
// at a point in time.
type Float64Observer struct {
asyncInstrument
}

View File

@ -20,6 +20,32 @@ import (
"go.opentelemetry.io/otel/api/core" "go.opentelemetry.io/otel/api/core"
) )
// MeterImpl is a convenient interface for SDK and test
// implementations that would provide a `Meter` but do not wish to
// re-implement the API's type-safe interfaces. Helpers provided in
// this package will construct a `Meter` given a `MeterImpl`.
type MeterImpl interface {
// Labels returns a reference to a set of labels that cannot
// be read by the application.
Labels(...core.KeyValue) LabelSet
// RecordBatch atomically records a batch of measurements.
RecordBatch(context.Context, LabelSet, ...Measurement)
// NewSyncInstrument returns a newly constructed
// synchronous instrument implementation or an error, should
// one occur.
NewSyncInstrument(descriptor Descriptor) (SyncImpl, error)
// NewAsyncInstrument returns a newly constructed
// asynchronous instrument implementation or an error, should
// one occur.
NewAsyncInstrument(
descriptor Descriptor,
callback func(func(core.Number, LabelSet)),
) (AsyncImpl, error)
}
// LabelSetDelegate is a general-purpose delegating implementation of // LabelSetDelegate is a general-purpose delegating implementation of
// the LabelSet interface. This is implemented by the default // the LabelSet interface. This is implemented by the default
// Provider returned by api/global.SetMeterProvider(), and should be // Provider returned by api/global.SetMeterProvider(), and should be
@ -29,21 +55,36 @@ type LabelSetDelegate interface {
Delegate() LabelSet Delegate() LabelSet
} }
// InstrumentImpl is the implementation-level interface Set/Add/Record // InstrumentImpl is a common interface for synchronous and
// individual metrics without precomputed labels. // asynchronous instruments.
type InstrumentImpl interface { type InstrumentImpl interface {
// Bind creates a Bound Instrument to record metrics with // Implementation returns the underlying implementation of the
// precomputed labels. // instrument, which allows the implementation to gain access
Bind(labels LabelSet) BoundInstrumentImpl // to its own representation especially from a `Measurement`.
Implementation() interface{}
// RecordOne allows the SDK to observe a single metric event. // Descriptor returns a copy of the instrument's Descriptor.
Descriptor() Descriptor
}
// SyncImpl is the implementation-level interface to a generic
// synchronous instrument (e.g., Measure and Counter instruments).
type SyncImpl interface {
InstrumentImpl
// Bind creates an implementation-level bound instrument,
// binding a label set with this instrument implementation.
Bind(labels LabelSet) BoundSyncImpl
// RecordOne captures a single synchronous metric event.
RecordOne(ctx context.Context, number core.Number, labels LabelSet) RecordOne(ctx context.Context, number core.Number, labels LabelSet)
} }
// BoundInstrumentImpl is the implementation-level interface to Set/Add/Record // BoundSyncImpl is the implementation-level interface to a
// individual metrics with precomputed labels. // generic bound synchronous instrument
type BoundInstrumentImpl interface { type BoundSyncImpl interface {
// RecordOne allows the SDK to observe a single metric event.
// RecordOne captures a single synchronous metric event.
RecordOne(ctx context.Context, number core.Number) RecordOne(ctx context.Context, number core.Number)
// Unbind frees the resources associated with this bound instrument. It // Unbind frees the resources associated with this bound instrument. It
@ -51,42 +92,38 @@ type BoundInstrumentImpl interface {
Unbind() Unbind()
} }
// WrapInt64CounterInstrument wraps the instrument in the type-safe // AsyncImpl is an implementation-level interface to an
// wrapper as an integral counter. // asynchronous instrument (e.g., Observer instruments).
// type AsyncImpl interface {
// It is mostly intended for SDKs. InstrumentImpl
func WrapInt64CounterInstrument(instrument InstrumentImpl, err error) (Int64Counter, error) {
common, err := newCommonMetric(instrument, err) // Note: An `Unregister()` API could be supported here.
return Int64Counter{commonMetric: common}, err
} }
// WrapFloat64CounterInstrument wraps the instrument in the type-safe // wrappedMeterImpl implements the `Meter` interface given a
// wrapper as an floating point counter. // `MeterImpl` implementation.
// type wrappedMeterImpl struct {
// It is mostly intended for SDKs. impl MeterImpl
func WrapFloat64CounterInstrument(instrument InstrumentImpl, err error) (Float64Counter, error) {
common, err := newCommonMetric(instrument, err)
return Float64Counter{commonMetric: common}, err
} }
// WrapInt64MeasureInstrument wraps the instrument in the type-safe // int64ObserverResult is an adapter for int64-valued asynchronous
// wrapper as an integral measure. // callbacks.
// type int64ObserverResult struct {
// It is mostly intended for SDKs. observe func(core.Number, LabelSet)
func WrapInt64MeasureInstrument(instrument InstrumentImpl, err error) (Int64Measure, error) {
common, err := newCommonMetric(instrument, err)
return Int64Measure{commonMetric: common}, err
} }
// WrapFloat64MeasureInstrument wraps the instrument in the type-safe // float64ObserverResult is an adapter for float64-valued asynchronous
// wrapper as an floating point measure. // callbacks.
// type float64ObserverResult struct {
// It is mostly intended for SDKs. observe func(core.Number, LabelSet)
func WrapFloat64MeasureInstrument(instrument InstrumentImpl, err error) (Float64Measure, error) {
common, err := newCommonMetric(instrument, err)
return Float64Measure{commonMetric: common}, err
} }
var (
_ Meter = (*wrappedMeterImpl)(nil)
_ Int64ObserverResult = int64ObserverResult{}
_ Float64ObserverResult = float64ObserverResult{}
)
// Configure is a helper that applies all the options to a Config. // Configure is a helper that applies all the options to a Config.
func Configure(opts []Option) Config { func Configure(opts []Option) Config {
var config Config var config Config
@ -95,3 +132,145 @@ func Configure(opts []Option) Config {
} }
return config return config
} }
// WrapMeterImpl constructs a `Meter` implementation from a
// `MeterImpl` implementation.
func WrapMeterImpl(impl MeterImpl) Meter {
return &wrappedMeterImpl{
impl: impl,
}
}
// UnwrapImpl returns a `MeterImpl` given a `Meter` that was
// constructed using `WrapMeterImpl`.
func UnwrapImpl(meter Meter) (MeterImpl, bool) {
if wrap, ok := meter.(*wrappedMeterImpl); ok {
return wrap.impl, true
}
return nil, false
}
func (m *wrappedMeterImpl) Labels(labels ...core.KeyValue) LabelSet {
return m.impl.Labels(labels...)
}
func (m *wrappedMeterImpl) RecordBatch(ctx context.Context, ls LabelSet, ms ...Measurement) {
m.impl.RecordBatch(ctx, ls, ms...)
}
func (m *wrappedMeterImpl) newSync(name string, metricKind Kind, numberKind core.NumberKind, opts []Option) (SyncImpl, error) {
return m.impl.NewSyncInstrument(NewDescriptor(name, metricKind, numberKind, opts...))
}
func (m *wrappedMeterImpl) NewInt64Counter(name string, opts ...Option) (Int64Counter, error) {
return WrapInt64CounterInstrument(
m.newSync(name, CounterKind, core.Int64NumberKind, opts))
}
// WrapInt64CounterInstrument returns an `Int64Counter` from a
// `SyncImpl`. An error will be generated if the
// `SyncImpl` is nil (in which case a No-op is substituted),
// otherwise the error passes through.
func WrapInt64CounterInstrument(syncInst SyncImpl, err error) (Int64Counter, error) {
common, err := checkNewSync(syncInst, err)
return Int64Counter{syncInstrument: common}, err
}
func (m *wrappedMeterImpl) NewFloat64Counter(name string, opts ...Option) (Float64Counter, error) {
return WrapFloat64CounterInstrument(
m.newSync(name, CounterKind, core.Float64NumberKind, opts))
}
// WrapFloat64CounterInstrument returns an `Float64Counter` from a
// `SyncImpl`. An error will be generated if the
// `SyncImpl` is nil (in which case a No-op is substituted),
// otherwise the error passes through.
func WrapFloat64CounterInstrument(syncInst SyncImpl, err error) (Float64Counter, error) {
common, err := checkNewSync(syncInst, err)
return Float64Counter{syncInstrument: common}, err
}
func (m *wrappedMeterImpl) NewInt64Measure(name string, opts ...Option) (Int64Measure, error) {
return WrapInt64MeasureInstrument(
m.newSync(name, MeasureKind, core.Int64NumberKind, opts))
}
// WrapInt64MeasureInstrument returns an `Int64Measure` from a
// `SyncImpl`. An error will be generated if the
// `SyncImpl` is nil (in which case a No-op is substituted),
// otherwise the error passes through.
func WrapInt64MeasureInstrument(syncInst SyncImpl, err error) (Int64Measure, error) {
common, err := checkNewSync(syncInst, err)
return Int64Measure{syncInstrument: common}, err
}
func (m *wrappedMeterImpl) NewFloat64Measure(name string, opts ...Option) (Float64Measure, error) {
return WrapFloat64MeasureInstrument(
m.newSync(name, MeasureKind, core.Float64NumberKind, opts))
}
// WrapFloat64MeasureInstrument returns an `Float64Measure` from a
// `SyncImpl`. An error will be generated if the
// `SyncImpl` is nil (in which case a No-op is substituted),
// otherwise the error passes through.
func WrapFloat64MeasureInstrument(syncInst SyncImpl, err error) (Float64Measure, error) {
common, err := checkNewSync(syncInst, err)
return Float64Measure{syncInstrument: common}, err
}
func (m *wrappedMeterImpl) newAsync(name string, mkind Kind, nkind core.NumberKind, opts []Option, callback func(func(core.Number, LabelSet))) (AsyncImpl, error) {
return m.impl.NewAsyncInstrument(
NewDescriptor(name, mkind, nkind, opts...),
callback)
}
func (m *wrappedMeterImpl) RegisterInt64Observer(name string, callback Int64ObserverCallback, opts ...Option) (Int64Observer, error) {
if callback == nil {
return NoopMeter{}.RegisterInt64Observer("", nil)
}
return WrapInt64ObserverInstrument(
m.newAsync(name, ObserverKind, core.Int64NumberKind, opts,
func(observe func(core.Number, LabelSet)) {
// Note: this memory allocation could be avoided by
// using a pointer to this object and mutating it
// on each collection interval.
callback(int64ObserverResult{observe})
}))
}
// WrapInt64ObserverInstrument returns an `Int64Observer` from a
// `AsyncImpl`. An error will be generated if the
// `AsyncImpl` is nil (in which case a No-op is substituted),
// otherwise the error passes through.
func WrapInt64ObserverInstrument(asyncInst AsyncImpl, err error) (Int64Observer, error) {
common, err := checkNewAsync(asyncInst, err)
return Int64Observer{asyncInstrument: common}, err
}
func (m *wrappedMeterImpl) RegisterFloat64Observer(name string, callback Float64ObserverCallback, opts ...Option) (Float64Observer, error) {
if callback == nil {
return NoopMeter{}.RegisterFloat64Observer("", nil)
}
return WrapFloat64ObserverInstrument(
m.newAsync(name, ObserverKind, core.Float64NumberKind, opts,
func(observe func(core.Number, LabelSet)) {
callback(float64ObserverResult{observe})
}))
}
// WrapFloat64ObserverInstrument returns an `Float64Observer` from a
// `AsyncImpl`. An error will be generated if the
// `AsyncImpl` is nil (in which case a No-op is substituted),
// otherwise the error passes through.
func WrapFloat64ObserverInstrument(asyncInst AsyncImpl, err error) (Float64Observer, error) {
common, err := checkNewAsync(asyncInst, err)
return Float64Observer{asyncInstrument: common}, err
}
func (io int64ObserverResult) Observe(value int64, labels LabelSet) {
io.observe(core.NewInt64Number(value), labels)
}
func (fo float64ObserverResult) Observe(value float64, labels LabelSet) {
fo.observe(core.NewFloat64Number(value), labels)
}

View File

@ -77,11 +77,10 @@ func main() {
oneMetricCB := func(result metric.Float64ObserverResult) { oneMetricCB := func(result metric.Float64ObserverResult) {
result.Observe(1, commonLabels) result.Observe(1, commonLabels)
} }
oneMetric := metric.Must(meter).RegisterFloat64Observer("ex.com.one", oneMetricCB, _ = metric.Must(meter).RegisterFloat64Observer("ex.com.one", oneMetricCB,
metric.WithKeys(fooKey, barKey, lemonsKey), metric.WithKeys(fooKey, barKey, lemonsKey),
metric.WithDescription("An observer set to 1.0"), metric.WithDescription("An observer set to 1.0"),
) )
defer oneMetric.Unregister()
measureTwo := metric.Must(meter).NewFloat64Measure("ex.com.two") measureTwo := metric.Must(meter).NewFloat64Measure("ex.com.two")

View File

@ -61,11 +61,10 @@ func main() {
(*observerLock).RUnlock() (*observerLock).RUnlock()
result.Observe(value, labelset) result.Observe(value, labelset)
} }
oneMetric := metric.Must(meter).RegisterFloat64Observer("ex.com.one", cb, _ = metric.Must(meter).RegisterFloat64Observer("ex.com.one", cb,
metric.WithKeys(fooKey, barKey, lemonsKey), metric.WithKeys(fooKey, barKey, lemonsKey),
metric.WithDescription("A measure set to 1.0"), metric.WithDescription("A measure set to 1.0"),
) )
defer oneMetric.Unregister()
measureTwo := metric.Must(meter).NewFloat64Measure("ex.com.two", metric.WithKeys(key.New("A"))) measureTwo := metric.Must(meter).NewFloat64Measure("ex.com.two", metric.WithKeys(key.New("A")))
measureThree := metric.Must(meter).NewFloat64Counter("ex.com.three") measureThree := metric.Must(meter).NewFloat64Counter("ex.com.three")

View File

@ -24,6 +24,7 @@ import (
"go.opentelemetry.io/otel/api/core" "go.opentelemetry.io/otel/api/core"
"go.opentelemetry.io/otel/api/key" "go.opentelemetry.io/otel/api/key"
"go.opentelemetry.io/otel/api/metric"
"go.opentelemetry.io/otel/exporters/metric/dogstatsd" "go.opentelemetry.io/otel/exporters/metric/dogstatsd"
"go.opentelemetry.io/otel/exporters/metric/internal/statsd" "go.opentelemetry.io/otel/exporters/metric/internal/statsd"
"go.opentelemetry.io/otel/exporters/metric/test" "go.opentelemetry.io/otel/exporters/metric/test"
@ -44,12 +45,12 @@ func TestDogstatsLabels(t *testing.T) {
ctx := context.Background() ctx := context.Background()
checkpointSet := test.NewCheckpointSet(encoder) checkpointSet := test.NewCheckpointSet(encoder)
desc := export.NewDescriptor("test.name", export.CounterKind, nil, "", "", core.Int64NumberKind) desc := metric.NewDescriptor("test.name", metric.CounterKind, core.Int64NumberKind)
cagg := sum.New() cagg := sum.New()
_ = cagg.Update(ctx, core.NewInt64Number(123), desc) _ = cagg.Update(ctx, core.NewInt64Number(123), &desc)
cagg.Checkpoint(ctx, desc) cagg.Checkpoint(ctx, &desc)
checkpointSet.Add(desc, cagg, key.New("A").String("B")) checkpointSet.Add(&desc, cagg, key.New("A").String("B"))
var buf bytes.Buffer var buf bytes.Buffer
exp, err := dogstatsd.NewRawExporter(dogstatsd.Config{ exp, err := dogstatsd.NewRawExporter(dogstatsd.Config{

View File

@ -26,6 +26,7 @@ import (
"go.opentelemetry.io/otel/api/core" "go.opentelemetry.io/otel/api/core"
"go.opentelemetry.io/otel/api/key" "go.opentelemetry.io/otel/api/key"
"go.opentelemetry.io/otel/api/metric"
"go.opentelemetry.io/otel/api/unit" "go.opentelemetry.io/otel/api/unit"
"go.opentelemetry.io/otel/exporters/metric/internal/statsd" "go.opentelemetry.io/otel/exporters/metric/internal/statsd"
"go.opentelemetry.io/otel/exporters/metric/test" "go.opentelemetry.io/otel/exporters/metric/test"
@ -123,14 +124,14 @@ timer.B.D:%s|ms
} }
checkpointSet := test.NewCheckpointSet(sdk.NewDefaultLabelEncoder()) checkpointSet := test.NewCheckpointSet(sdk.NewDefaultLabelEncoder())
cdesc := export.NewDescriptor( cdesc := metric.NewDescriptor(
"counter", export.CounterKind, nil, "", "", nkind) "counter", metric.CounterKind, nkind)
gdesc := export.NewDescriptor( gdesc := metric.NewDescriptor(
"observer", export.ObserverKind, nil, "", "", nkind) "observer", metric.ObserverKind, nkind)
mdesc := export.NewDescriptor( mdesc := metric.NewDescriptor(
"measure", export.MeasureKind, nil, "", "", nkind) "measure", metric.MeasureKind, nkind)
tdesc := export.NewDescriptor( tdesc := metric.NewDescriptor(
"timer", export.MeasureKind, nil, "", unit.Milliseconds, nkind) "timer", metric.MeasureKind, nkind, metric.WithUnit(unit.Milliseconds))
labels := []core.KeyValue{ labels := []core.KeyValue{
key.New("A").String("B"), key.New("A").String("B"),
@ -138,10 +139,10 @@ timer.B.D:%s|ms
} }
const value = 123.456 const value = 123.456
checkpointSet.AddCounter(cdesc, value, labels...) checkpointSet.AddCounter(&cdesc, value, labels...)
checkpointSet.AddLastValue(gdesc, value, labels...) checkpointSet.AddLastValue(&gdesc, value, labels...)
checkpointSet.AddMeasure(mdesc, value, labels...) checkpointSet.AddMeasure(&mdesc, value, labels...)
checkpointSet.AddMeasure(tdesc, value, labels...) checkpointSet.AddMeasure(&tdesc, value, labels...)
err = exp.Export(ctx, checkpointSet) err = exp.Export(ctx, checkpointSet)
require.Nil(t, err) require.Nil(t, err)
@ -285,7 +286,7 @@ func TestPacketSplit(t *testing.T) {
} }
checkpointSet := test.NewCheckpointSet(adapter.LabelEncoder) checkpointSet := test.NewCheckpointSet(adapter.LabelEncoder)
desc := export.NewDescriptor("counter", export.CounterKind, nil, "", "", core.Int64NumberKind) desc := metric.NewDescriptor("counter", metric.CounterKind, core.Int64NumberKind)
var expected []string var expected []string
@ -295,7 +296,7 @@ func TestPacketSplit(t *testing.T) {
offset += nkeys offset += nkeys
expect := fmt.Sprint("counter:100|c", adapter.LabelEncoder.Encode(labels), "\n") expect := fmt.Sprint("counter:100|c", adapter.LabelEncoder.Encode(labels), "\n")
expected = append(expected, expect) expected = append(expected, expect)
checkpointSet.AddCounter(desc, 100, labels...) checkpointSet.AddCounter(&desc, 100, labels...)
}) })
err = exp.Export(ctx, checkpointSet) err = exp.Export(ctx, checkpointSet)

View File

@ -12,10 +12,10 @@ import (
"go.opentelemetry.io/otel/api/core" "go.opentelemetry.io/otel/api/core"
"go.opentelemetry.io/otel/api/key" "go.opentelemetry.io/otel/api/key"
"go.opentelemetry.io/otel/api/metric"
"go.opentelemetry.io/otel/exporters/metric/prometheus" "go.opentelemetry.io/otel/exporters/metric/prometheus"
"go.opentelemetry.io/otel/exporters/metric/test" "go.opentelemetry.io/otel/exporters/metric/test"
export "go.opentelemetry.io/otel/sdk/export/metric" sdk "go.opentelemetry.io/otel/sdk/metric"
"go.opentelemetry.io/otel/sdk/metric"
) )
func TestPrometheusExporter(t *testing.T) { func TestPrometheusExporter(t *testing.T) {
@ -27,29 +27,29 @@ func TestPrometheusExporter(t *testing.T) {
} }
var expected []string var expected []string
checkpointSet := test.NewCheckpointSet(metric.NewDefaultLabelEncoder()) checkpointSet := test.NewCheckpointSet(sdk.NewDefaultLabelEncoder())
counter := export.NewDescriptor( counter := metric.NewDescriptor(
"counter", export.CounterKind, nil, "", "", core.Float64NumberKind) "counter", metric.CounterKind, core.Float64NumberKind)
lastValue := export.NewDescriptor( lastValue := metric.NewDescriptor(
"lastvalue", export.ObserverKind, nil, "", "", core.Float64NumberKind) "lastvalue", metric.ObserverKind, core.Float64NumberKind)
measure := export.NewDescriptor( measure := metric.NewDescriptor(
"measure", export.MeasureKind, nil, "", "", core.Float64NumberKind) "measure", metric.MeasureKind, core.Float64NumberKind)
labels := []core.KeyValue{ labels := []core.KeyValue{
key.New("A").String("B"), key.New("A").String("B"),
key.New("C").String("D"), key.New("C").String("D"),
} }
checkpointSet.AddCounter(counter, 15.3, labels...) checkpointSet.AddCounter(&counter, 15.3, labels...)
expected = append(expected, `counter{A="B",C="D"} 15.3`) expected = append(expected, `counter{A="B",C="D"} 15.3`)
checkpointSet.AddLastValue(lastValue, 13.2, labels...) checkpointSet.AddLastValue(&lastValue, 13.2, labels...)
expected = append(expected, `lastvalue{A="B",C="D"} 13.2`) expected = append(expected, `lastvalue{A="B",C="D"} 13.2`)
checkpointSet.AddMeasure(measure, 13, labels...) checkpointSet.AddMeasure(&measure, 13, labels...)
checkpointSet.AddMeasure(measure, 15, labels...) checkpointSet.AddMeasure(&measure, 15, labels...)
checkpointSet.AddMeasure(measure, 17, labels...) checkpointSet.AddMeasure(&measure, 17, labels...)
expected = append(expected, `measure{A="B",C="D",quantile="0.5"} 15`) expected = append(expected, `measure{A="B",C="D",quantile="0.5"} 15`)
expected = append(expected, `measure{A="B",C="D",quantile="0.9"} 17`) expected = append(expected, `measure{A="B",C="D",quantile="0.9"} 17`)
expected = append(expected, `measure{A="B",C="D",quantile="0.99"} 17`) expected = append(expected, `measure{A="B",C="D",quantile="0.99"} 17`)
@ -61,13 +61,13 @@ func TestPrometheusExporter(t *testing.T) {
key.New("C").String(""), key.New("C").String(""),
} }
checkpointSet.AddCounter(counter, 12, missingLabels...) checkpointSet.AddCounter(&counter, 12, missingLabels...)
expected = append(expected, `counter{A="E",C=""} 12`) expected = append(expected, `counter{A="E",C=""} 12`)
checkpointSet.AddLastValue(lastValue, 32, missingLabels...) checkpointSet.AddLastValue(&lastValue, 32, missingLabels...)
expected = append(expected, `lastvalue{A="E",C=""} 32`) expected = append(expected, `lastvalue{A="E",C=""} 32`)
checkpointSet.AddMeasure(measure, 19, missingLabels...) checkpointSet.AddMeasure(&measure, 19, missingLabels...)
expected = append(expected, `measure{A="E",C="",quantile="0.5"} 19`) expected = append(expected, `measure{A="E",C="",quantile="0.5"} 19`)
expected = append(expected, `measure{A="E",C="",quantile="0.9"} 19`) expected = append(expected, `measure{A="E",C="",quantile="0.9"} 19`)
expected = append(expected, `measure{A="E",C="",quantile="0.99"} 19`) expected = append(expected, `measure{A="E",C="",quantile="0.99"} 19`)

View File

@ -12,6 +12,7 @@ import (
"go.opentelemetry.io/otel/api/core" "go.opentelemetry.io/otel/api/core"
"go.opentelemetry.io/otel/api/key" "go.opentelemetry.io/otel/api/key"
"go.opentelemetry.io/otel/api/metric"
"go.opentelemetry.io/otel/exporters/metric/stdout" "go.opentelemetry.io/otel/exporters/metric/stdout"
"go.opentelemetry.io/otel/exporters/metric/test" "go.opentelemetry.io/otel/exporters/metric/test"
export "go.opentelemetry.io/otel/sdk/export/metric" export "go.opentelemetry.io/otel/sdk/export/metric"
@ -82,12 +83,12 @@ func TestStdoutTimestamp(t *testing.T) {
checkpointSet := test.NewCheckpointSet(sdk.NewDefaultLabelEncoder()) checkpointSet := test.NewCheckpointSet(sdk.NewDefaultLabelEncoder())
ctx := context.Background() ctx := context.Background()
desc := export.NewDescriptor("test.name", export.ObserverKind, nil, "", "", core.Int64NumberKind) desc := metric.NewDescriptor("test.name", metric.ObserverKind, core.Int64NumberKind)
lvagg := lastvalue.New() lvagg := lastvalue.New()
aggtest.CheckedUpdate(t, lvagg, core.NewInt64Number(321), desc) aggtest.CheckedUpdate(t, lvagg, core.NewInt64Number(321), &desc)
lvagg.Checkpoint(ctx, desc) lvagg.Checkpoint(ctx, &desc)
checkpointSet.Add(desc, lvagg) checkpointSet.Add(&desc, lvagg)
if err := exporter.Export(ctx, checkpointSet); err != nil { if err := exporter.Export(ctx, checkpointSet); err != nil {
t.Fatal("Unexpected export error: ", err) t.Fatal("Unexpected export error: ", err)
@ -127,12 +128,12 @@ func TestStdoutCounterFormat(t *testing.T) {
checkpointSet := test.NewCheckpointSet(sdk.NewDefaultLabelEncoder()) checkpointSet := test.NewCheckpointSet(sdk.NewDefaultLabelEncoder())
desc := export.NewDescriptor("test.name", export.CounterKind, nil, "", "", core.Int64NumberKind) desc := metric.NewDescriptor("test.name", metric.CounterKind, core.Int64NumberKind)
cagg := sum.New() cagg := sum.New()
aggtest.CheckedUpdate(fix.t, cagg, core.NewInt64Number(123), desc) aggtest.CheckedUpdate(fix.t, cagg, core.NewInt64Number(123), &desc)
cagg.Checkpoint(fix.ctx, desc) cagg.Checkpoint(fix.ctx, &desc)
checkpointSet.Add(desc, cagg, key.String("A", "B"), key.String("C", "D")) checkpointSet.Add(&desc, cagg, key.String("A", "B"), key.String("C", "D"))
fix.Export(checkpointSet) fix.Export(checkpointSet)
@ -144,12 +145,12 @@ func TestStdoutLastValueFormat(t *testing.T) {
checkpointSet := test.NewCheckpointSet(sdk.NewDefaultLabelEncoder()) checkpointSet := test.NewCheckpointSet(sdk.NewDefaultLabelEncoder())
desc := export.NewDescriptor("test.name", export.ObserverKind, nil, "", "", core.Float64NumberKind) desc := metric.NewDescriptor("test.name", metric.ObserverKind, core.Float64NumberKind)
lvagg := lastvalue.New() lvagg := lastvalue.New()
aggtest.CheckedUpdate(fix.t, lvagg, core.NewFloat64Number(123.456), desc) aggtest.CheckedUpdate(fix.t, lvagg, core.NewFloat64Number(123.456), &desc)
lvagg.Checkpoint(fix.ctx, desc) lvagg.Checkpoint(fix.ctx, &desc)
checkpointSet.Add(desc, lvagg, key.String("A", "B"), key.String("C", "D")) checkpointSet.Add(&desc, lvagg, key.String("A", "B"), key.String("C", "D"))
fix.Export(checkpointSet) fix.Export(checkpointSet)
@ -161,13 +162,13 @@ func TestStdoutMinMaxSumCount(t *testing.T) {
checkpointSet := test.NewCheckpointSet(sdk.NewDefaultLabelEncoder()) checkpointSet := test.NewCheckpointSet(sdk.NewDefaultLabelEncoder())
desc := export.NewDescriptor("test.name", export.MeasureKind, nil, "", "", core.Float64NumberKind) desc := metric.NewDescriptor("test.name", metric.MeasureKind, core.Float64NumberKind)
magg := minmaxsumcount.New(desc) magg := minmaxsumcount.New(&desc)
aggtest.CheckedUpdate(fix.t, magg, core.NewFloat64Number(123.456), desc) aggtest.CheckedUpdate(fix.t, magg, core.NewFloat64Number(123.456), &desc)
aggtest.CheckedUpdate(fix.t, magg, core.NewFloat64Number(876.543), desc) aggtest.CheckedUpdate(fix.t, magg, core.NewFloat64Number(876.543), &desc)
magg.Checkpoint(fix.ctx, desc) magg.Checkpoint(fix.ctx, &desc)
checkpointSet.Add(desc, magg, key.String("A", "B"), key.String("C", "D")) checkpointSet.Add(&desc, magg, key.String("A", "B"), key.String("C", "D"))
fix.Export(checkpointSet) fix.Export(checkpointSet)
@ -181,16 +182,16 @@ func TestStdoutMeasureFormat(t *testing.T) {
checkpointSet := test.NewCheckpointSet(sdk.NewDefaultLabelEncoder()) checkpointSet := test.NewCheckpointSet(sdk.NewDefaultLabelEncoder())
desc := export.NewDescriptor("test.name", export.MeasureKind, nil, "", "", core.Float64NumberKind) desc := metric.NewDescriptor("test.name", metric.MeasureKind, core.Float64NumberKind)
magg := array.New() magg := array.New()
for i := 0; i < 1000; i++ { for i := 0; i < 1000; i++ {
aggtest.CheckedUpdate(fix.t, magg, core.NewFloat64Number(float64(i)+0.5), desc) aggtest.CheckedUpdate(fix.t, magg, core.NewFloat64Number(float64(i)+0.5), &desc)
} }
magg.Checkpoint(fix.ctx, desc) magg.Checkpoint(fix.ctx, &desc)
checkpointSet.Add(desc, magg, key.String("A", "B"), key.String("C", "D")) checkpointSet.Add(&desc, magg, key.String("A", "B"), key.String("C", "D"))
fix.Export(checkpointSet) fix.Export(checkpointSet)
@ -222,10 +223,10 @@ func TestStdoutMeasureFormat(t *testing.T) {
} }
func TestStdoutNoData(t *testing.T) { func TestStdoutNoData(t *testing.T) {
desc := export.NewDescriptor("test.name", export.MeasureKind, nil, "", "", core.Float64NumberKind) desc := metric.NewDescriptor("test.name", metric.MeasureKind, core.Float64NumberKind)
for name, tc := range map[string]export.Aggregator{ for name, tc := range map[string]export.Aggregator{
"ddsketch": ddsketch.New(ddsketch.NewDefaultConfig(), desc), "ddsketch": ddsketch.New(ddsketch.NewDefaultConfig(), &desc),
"minmaxsumcount": minmaxsumcount.New(desc), "minmaxsumcount": minmaxsumcount.New(&desc),
} { } {
tc := tc tc := tc
t.Run(name, func(t *testing.T) { t.Run(name, func(t *testing.T) {
@ -236,9 +237,9 @@ func TestStdoutNoData(t *testing.T) {
checkpointSet := test.NewCheckpointSet(sdk.NewDefaultLabelEncoder()) checkpointSet := test.NewCheckpointSet(sdk.NewDefaultLabelEncoder())
magg := tc magg := tc
magg.Checkpoint(fix.ctx, desc) magg.Checkpoint(fix.ctx, &desc)
checkpointSet.Add(desc, magg) checkpointSet.Add(&desc, magg)
fix.Export(checkpointSet) fix.Export(checkpointSet)
@ -252,11 +253,11 @@ func TestStdoutLastValueNotSet(t *testing.T) {
checkpointSet := test.NewCheckpointSet(sdk.NewDefaultLabelEncoder()) checkpointSet := test.NewCheckpointSet(sdk.NewDefaultLabelEncoder())
desc := export.NewDescriptor("test.name", export.ObserverKind, nil, "", "", core.Float64NumberKind) desc := metric.NewDescriptor("test.name", metric.ObserverKind, core.Float64NumberKind)
lvagg := lastvalue.New() lvagg := lastvalue.New()
lvagg.Checkpoint(fix.ctx, desc) lvagg.Checkpoint(fix.ctx, &desc)
checkpointSet.Add(desc, lvagg, key.String("A", "B"), key.String("C", "D")) checkpointSet.Add(&desc, lvagg, key.String("A", "B"), key.String("C", "D"))
fix.Export(checkpointSet) fix.Export(checkpointSet)
@ -270,12 +271,12 @@ func TestStdoutCounterWithUnspecifiedKeys(t *testing.T) {
keys := []core.Key{key.New("C"), key.New("D")} keys := []core.Key{key.New("C"), key.New("D")}
desc := export.NewDescriptor("test.name", export.CounterKind, keys, "", "", core.Int64NumberKind) desc := metric.NewDescriptor("test.name", metric.CounterKind, core.Int64NumberKind, metric.WithKeys(keys...))
cagg := sum.New() cagg := sum.New()
aggtest.CheckedUpdate(fix.t, cagg, core.NewInt64Number(10), desc) aggtest.CheckedUpdate(fix.t, cagg, core.NewInt64Number(10), &desc)
cagg.Checkpoint(fix.ctx, desc) cagg.Checkpoint(fix.ctx, &desc)
checkpointSet.Add(desc, cagg, key.String("A", "B")) checkpointSet.Add(&desc, cagg, key.String("A", "B"))
fix.Export(checkpointSet) fix.Export(checkpointSet)

View File

@ -5,6 +5,7 @@ import (
"errors" "errors"
"go.opentelemetry.io/otel/api/core" "go.opentelemetry.io/otel/api/core"
"go.opentelemetry.io/otel/api/metric"
export "go.opentelemetry.io/otel/sdk/export/metric" export "go.opentelemetry.io/otel/sdk/export/metric"
"go.opentelemetry.io/otel/sdk/export/metric/aggregator" "go.opentelemetry.io/otel/sdk/export/metric/aggregator"
"go.opentelemetry.io/otel/sdk/metric/aggregator/array" "go.opentelemetry.io/otel/sdk/metric/aggregator/array"
@ -36,7 +37,7 @@ func (p *CheckpointSet) Reset() {
// //
// If there is an existing record with the same descriptor and LabelSet // If there is an existing record with the same descriptor and LabelSet
// the stored aggregator will be returned and should be merged. // the stored aggregator will be returned and should be merged.
func (p *CheckpointSet) Add(desc *export.Descriptor, newAgg export.Aggregator, labels ...core.KeyValue) (agg export.Aggregator, added bool) { func (p *CheckpointSet) Add(desc *metric.Descriptor, newAgg export.Aggregator, labels ...core.KeyValue) (agg export.Aggregator, added bool) {
encoded := p.encoder.Encode(labels) encoded := p.encoder.Encode(labels)
elabels := export.NewLabels(labels, encoded, p.encoder) elabels := export.NewLabels(labels, encoded, p.encoder)
@ -51,26 +52,26 @@ func (p *CheckpointSet) Add(desc *export.Descriptor, newAgg export.Aggregator, l
return newAgg, true return newAgg, true
} }
func createNumber(desc *export.Descriptor, v float64) core.Number { func createNumber(desc *metric.Descriptor, v float64) core.Number {
if desc.NumberKind() == core.Float64NumberKind { if desc.NumberKind() == core.Float64NumberKind {
return core.NewFloat64Number(v) return core.NewFloat64Number(v)
} }
return core.NewInt64Number(int64(v)) return core.NewInt64Number(int64(v))
} }
func (p *CheckpointSet) AddLastValue(desc *export.Descriptor, v float64, labels ...core.KeyValue) { func (p *CheckpointSet) AddLastValue(desc *metric.Descriptor, v float64, labels ...core.KeyValue) {
p.updateAggregator(desc, lastvalue.New(), v, labels...) p.updateAggregator(desc, lastvalue.New(), v, labels...)
} }
func (p *CheckpointSet) AddCounter(desc *export.Descriptor, v float64, labels ...core.KeyValue) { func (p *CheckpointSet) AddCounter(desc *metric.Descriptor, v float64, labels ...core.KeyValue) {
p.updateAggregator(desc, sum.New(), v, labels...) p.updateAggregator(desc, sum.New(), v, labels...)
} }
func (p *CheckpointSet) AddMeasure(desc *export.Descriptor, v float64, labels ...core.KeyValue) { func (p *CheckpointSet) AddMeasure(desc *metric.Descriptor, v float64, labels ...core.KeyValue) {
p.updateAggregator(desc, array.New(), v, labels...) p.updateAggregator(desc, array.New(), v, labels...)
} }
func (p *CheckpointSet) updateAggregator(desc *export.Descriptor, newAgg export.Aggregator, v float64, labels ...core.KeyValue) { func (p *CheckpointSet) updateAggregator(desc *metric.Descriptor, newAgg export.Aggregator, v float64, labels ...core.KeyValue) {
ctx := context.Background() ctx := context.Background()
// Updates and checkpoint the new aggregator // Updates and checkpoint the new aggregator
_ = newAgg.Update(ctx, createNumber(desc, v), desc) _ = newAgg.Update(ctx, createNumber(desc, v), desc)

View File

@ -23,7 +23,8 @@ import (
metricpb "github.com/open-telemetry/opentelemetry-proto/gen/go/metrics/v1" metricpb "github.com/open-telemetry/opentelemetry-proto/gen/go/metrics/v1"
"go.opentelemetry.io/otel/api/core" "go.opentelemetry.io/otel/api/core"
metricsdk "go.opentelemetry.io/otel/sdk/export/metric" "go.opentelemetry.io/otel/api/metric"
export "go.opentelemetry.io/otel/sdk/export/metric"
"go.opentelemetry.io/otel/sdk/export/metric/aggregator" "go.opentelemetry.io/otel/sdk/export/metric/aggregator"
) )
@ -33,7 +34,7 @@ var ErrUnimplementedAgg = errors.New("unimplemented aggregator")
// Record transforms a Record into an OTLP Metric. An ErrUnimplementedAgg // Record transforms a Record into an OTLP Metric. An ErrUnimplementedAgg
// error is returned if the Record Aggregator is not supported. // error is returned if the Record Aggregator is not supported.
func Record(r metricsdk.Record) (*metricpb.Metric, error) { func Record(r export.Record) (*metricpb.Metric, error) {
d := r.Descriptor() d := r.Descriptor()
l := r.Labels() l := r.Labels()
switch a := r.Aggregator().(type) { switch a := r.Aggregator().(type) {
@ -46,7 +47,7 @@ func Record(r metricsdk.Record) (*metricpb.Metric, error) {
} }
// sum transforms a Sum Aggregator into an OTLP Metric. // sum transforms a Sum Aggregator into an OTLP Metric.
func sum(desc *metricsdk.Descriptor, labels metricsdk.Labels, a aggregator.Sum) (*metricpb.Metric, error) { func sum(desc *metric.Descriptor, labels export.Labels, a aggregator.Sum) (*metricpb.Metric, error) {
sum, err := a.Sum() sum, err := a.Sum()
if err != nil { if err != nil {
return nil, err return nil, err
@ -96,7 +97,7 @@ func minMaxSumCountValues(a aggregator.MinMaxSumCount) (min, max, sum core.Numbe
} }
// minMaxSumCount transforms a MinMaxSumCount Aggregator into an OTLP Metric. // minMaxSumCount transforms a MinMaxSumCount Aggregator into an OTLP Metric.
func minMaxSumCount(desc *metricsdk.Descriptor, labels metricsdk.Labels, a aggregator.MinMaxSumCount) (*metricpb.Metric, error) { func minMaxSumCount(desc *metric.Descriptor, labels export.Labels, a aggregator.MinMaxSumCount) (*metricpb.Metric, error) {
min, max, sum, count, err := minMaxSumCountValues(a) min, max, sum, count, err := minMaxSumCountValues(a)
if err != nil { if err != nil {
return nil, err return nil, err

View File

@ -23,8 +23,9 @@ import (
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
"go.opentelemetry.io/otel/api/core" "go.opentelemetry.io/otel/api/core"
"go.opentelemetry.io/otel/api/metric"
"go.opentelemetry.io/otel/api/unit" "go.opentelemetry.io/otel/api/unit"
metricsdk "go.opentelemetry.io/otel/sdk/export/metric" export "go.opentelemetry.io/otel/sdk/export/metric"
"go.opentelemetry.io/otel/sdk/export/metric/aggregator" "go.opentelemetry.io/otel/sdk/export/metric/aggregator"
"go.opentelemetry.io/otel/sdk/metric/aggregator/minmaxsumcount" "go.opentelemetry.io/otel/sdk/metric/aggregator/minmaxsumcount"
sumAgg "go.opentelemetry.io/otel/sdk/metric/aggregator/sum" sumAgg "go.opentelemetry.io/otel/sdk/metric/aggregator/sum"
@ -73,16 +74,16 @@ func TestStringKeyValues(t *testing.T) {
} }
func TestMinMaxSumCountValue(t *testing.T) { func TestMinMaxSumCountValue(t *testing.T) {
mmsc := minmaxsumcount.New(&metricsdk.Descriptor{}) mmsc := minmaxsumcount.New(&metric.Descriptor{})
assert.NoError(t, mmsc.Update(context.Background(), 1, &metricsdk.Descriptor{})) assert.NoError(t, mmsc.Update(context.Background(), 1, &metric.Descriptor{}))
assert.NoError(t, mmsc.Update(context.Background(), 10, &metricsdk.Descriptor{})) assert.NoError(t, mmsc.Update(context.Background(), 10, &metric.Descriptor{}))
// Prior to checkpointing ErrNoData should be returned. // Prior to checkpointing ErrNoData should be returned.
_, _, _, _, err := minMaxSumCountValues(mmsc) _, _, _, _, err := minMaxSumCountValues(mmsc)
assert.EqualError(t, err, aggregator.ErrNoData.Error()) assert.EqualError(t, err, aggregator.ErrNoData.Error())
// Checkpoint to set non-zero values // Checkpoint to set non-zero values
mmsc.Checkpoint(context.Background(), &metricsdk.Descriptor{}) mmsc.Checkpoint(context.Background(), &metric.Descriptor{})
min, max, sum, count, err := minMaxSumCountValues(mmsc) min, max, sum, count, err := minMaxSumCountValues(mmsc)
if assert.NoError(t, err) { if assert.NoError(t, err) {
assert.Equal(t, min, core.NewInt64Number(1)) assert.Equal(t, min, core.NewInt64Number(1))
@ -95,7 +96,7 @@ func TestMinMaxSumCountValue(t *testing.T) {
func TestMinMaxSumCountMetricDescriptor(t *testing.T) { func TestMinMaxSumCountMetricDescriptor(t *testing.T) {
tests := []struct { tests := []struct {
name string name string
metricKind metricsdk.Kind metricKind metric.Kind
keys []core.Key keys []core.Key
description string description string
unit unit.Unit unit unit.Unit
@ -105,7 +106,7 @@ func TestMinMaxSumCountMetricDescriptor(t *testing.T) {
}{ }{
{ {
"mmsc-test-a", "mmsc-test-a",
metricsdk.MeasureKind, metric.MeasureKind,
[]core.Key{}, []core.Key{},
"test-a-description", "test-a-description",
unit.Dimensionless, unit.Dimensionless,
@ -121,8 +122,8 @@ func TestMinMaxSumCountMetricDescriptor(t *testing.T) {
}, },
{ {
"mmsc-test-b", "mmsc-test-b",
metricsdk.CounterKind, // This shouldn't change anything. metric.CounterKind, // This shouldn't change anything.
[]core.Key{"test"}, // This shouldn't change anything. []core.Key{"test"}, // This shouldn't change anything.
"test-b-description", "test-b-description",
unit.Bytes, unit.Bytes,
core.Float64NumberKind, // This shouldn't change anything. core.Float64NumberKind, // This shouldn't change anything.
@ -138,15 +139,18 @@ func TestMinMaxSumCountMetricDescriptor(t *testing.T) {
} }
ctx := context.Background() ctx := context.Background()
mmsc := minmaxsumcount.New(&metricsdk.Descriptor{}) mmsc := minmaxsumcount.New(&metric.Descriptor{})
if !assert.NoError(t, mmsc.Update(ctx, 1, &metricsdk.Descriptor{})) { if !assert.NoError(t, mmsc.Update(ctx, 1, &metric.Descriptor{})) {
return return
} }
mmsc.Checkpoint(ctx, &metricsdk.Descriptor{}) mmsc.Checkpoint(ctx, &metric.Descriptor{})
for _, test := range tests { for _, test := range tests {
desc := metricsdk.NewDescriptor(test.name, test.metricKind, test.keys, test.description, test.unit, test.numberKind) desc := metric.NewDescriptor(test.name, test.metricKind, test.numberKind,
labels := metricsdk.NewLabels(test.labels, "", nil) metric.WithKeys(test.keys...),
got, err := minMaxSumCount(desc, labels, mmsc) metric.WithDescription(test.description),
metric.WithUnit(test.unit))
labels := export.NewLabels(test.labels, "", nil)
got, err := minMaxSumCount(&desc, labels, mmsc)
if assert.NoError(t, err) { if assert.NoError(t, err) {
assert.Equal(t, test.expected, got.MetricDescriptor) assert.Equal(t, test.expected, got.MetricDescriptor)
} }
@ -154,12 +158,12 @@ func TestMinMaxSumCountMetricDescriptor(t *testing.T) {
} }
func TestMinMaxSumCountDatapoints(t *testing.T) { func TestMinMaxSumCountDatapoints(t *testing.T) {
desc := metricsdk.NewDescriptor("", metricsdk.MeasureKind, []core.Key{}, "", unit.Dimensionless, core.Int64NumberKind) desc := metric.NewDescriptor("", metric.MeasureKind, core.Int64NumberKind)
labels := metricsdk.NewLabels([]core.KeyValue{}, "", nil) labels := export.NewLabels([]core.KeyValue{}, "", nil)
mmsc := minmaxsumcount.New(desc) mmsc := minmaxsumcount.New(&desc)
assert.NoError(t, mmsc.Update(context.Background(), 1, desc)) assert.NoError(t, mmsc.Update(context.Background(), 1, &desc))
assert.NoError(t, mmsc.Update(context.Background(), 10, desc)) assert.NoError(t, mmsc.Update(context.Background(), 10, &desc))
mmsc.Checkpoint(context.Background(), desc) mmsc.Checkpoint(context.Background(), &desc)
expected := []*metricpb.SummaryDataPoint{ expected := []*metricpb.SummaryDataPoint{
{ {
Count: 2, Count: 2,
@ -176,7 +180,7 @@ func TestMinMaxSumCountDatapoints(t *testing.T) {
}, },
}, },
} }
m, err := minMaxSumCount(desc, labels, mmsc) m, err := minMaxSumCount(&desc, labels, mmsc)
if assert.NoError(t, err) { if assert.NoError(t, err) {
assert.Equal(t, []*metricpb.Int64DataPoint(nil), m.Int64DataPoints) assert.Equal(t, []*metricpb.Int64DataPoint(nil), m.Int64DataPoints)
assert.Equal(t, []*metricpb.DoubleDataPoint(nil), m.DoubleDataPoints) assert.Equal(t, []*metricpb.DoubleDataPoint(nil), m.DoubleDataPoints)
@ -189,7 +193,7 @@ func TestMinMaxSumCountPropagatesErrors(t *testing.T) {
// ErrNoData should be returned by both the Min and Max values of // ErrNoData should be returned by both the Min and Max values of
// a MinMaxSumCount Aggregator. Use this fact to check the error is // a MinMaxSumCount Aggregator. Use this fact to check the error is
// correctly returned. // correctly returned.
mmsc := minmaxsumcount.New(&metricsdk.Descriptor{}) mmsc := minmaxsumcount.New(&metric.Descriptor{})
_, _, _, _, err := minMaxSumCountValues(mmsc) _, _, _, _, err := minMaxSumCountValues(mmsc)
assert.Error(t, err) assert.Error(t, err)
assert.Equal(t, aggregator.ErrNoData, err) assert.Equal(t, aggregator.ErrNoData, err)
@ -198,7 +202,7 @@ func TestMinMaxSumCountPropagatesErrors(t *testing.T) {
func TestSumMetricDescriptor(t *testing.T) { func TestSumMetricDescriptor(t *testing.T) {
tests := []struct { tests := []struct {
name string name string
metricKind metricsdk.Kind metricKind metric.Kind
keys []core.Key keys []core.Key
description string description string
unit unit.Unit unit unit.Unit
@ -208,7 +212,7 @@ func TestSumMetricDescriptor(t *testing.T) {
}{ }{
{ {
"sum-test-a", "sum-test-a",
metricsdk.CounterKind, metric.CounterKind,
[]core.Key{}, []core.Key{},
"test-a-description", "test-a-description",
unit.Dimensionless, unit.Dimensionless,
@ -224,8 +228,8 @@ func TestSumMetricDescriptor(t *testing.T) {
}, },
{ {
"sum-test-b", "sum-test-b",
metricsdk.MeasureKind, // This shouldn't change anything. metric.MeasureKind, // This shouldn't change anything.
[]core.Key{"test"}, // This shouldn't change anything. []core.Key{"test"}, // This shouldn't change anything.
"test-b-description", "test-b-description",
unit.Milliseconds, unit.Milliseconds,
core.Float64NumberKind, core.Float64NumberKind,
@ -241,22 +245,26 @@ func TestSumMetricDescriptor(t *testing.T) {
} }
for _, test := range tests { for _, test := range tests {
desc := metricsdk.NewDescriptor(test.name, test.metricKind, test.keys, test.description, test.unit, test.numberKind) desc := metric.NewDescriptor(test.name, test.metricKind, test.numberKind,
labels := metricsdk.NewLabels(test.labels, "", nil) metric.WithKeys(test.keys...),
got, err := sum(desc, labels, sumAgg.New()) metric.WithDescription(test.description),
metric.WithUnit(test.unit),
)
labels := export.NewLabels(test.labels, "", nil)
got, err := sum(&desc, labels, sumAgg.New())
if assert.NoError(t, err) { if assert.NoError(t, err) {
assert.Equal(t, test.expected, got.MetricDescriptor) assert.Equal(t, test.expected, got.MetricDescriptor)
} }
} }
} }
func TestSumInt64Datapoints(t *testing.T) { func TestSumInt64DataPoints(t *testing.T) {
desc := metricsdk.NewDescriptor("", metricsdk.MeasureKind, []core.Key{}, "", unit.Dimensionless, core.Int64NumberKind) desc := metric.NewDescriptor("", metric.MeasureKind, core.Int64NumberKind)
labels := metricsdk.NewLabels([]core.KeyValue{}, "", nil) labels := export.NewLabels([]core.KeyValue{}, "", nil)
s := sumAgg.New() s := sumAgg.New()
assert.NoError(t, s.Update(context.Background(), core.Number(1), desc)) assert.NoError(t, s.Update(context.Background(), core.Number(1), &desc))
s.Checkpoint(context.Background(), desc) s.Checkpoint(context.Background(), &desc)
if m, err := sum(desc, labels, s); assert.NoError(t, err) { if m, err := sum(&desc, labels, s); assert.NoError(t, err) {
assert.Equal(t, []*metricpb.Int64DataPoint{{Value: 1}}, m.Int64DataPoints) assert.Equal(t, []*metricpb.Int64DataPoint{{Value: 1}}, m.Int64DataPoints)
assert.Equal(t, []*metricpb.DoubleDataPoint(nil), m.DoubleDataPoints) assert.Equal(t, []*metricpb.DoubleDataPoint(nil), m.DoubleDataPoints)
assert.Equal(t, []*metricpb.HistogramDataPoint(nil), m.HistogramDataPoints) assert.Equal(t, []*metricpb.HistogramDataPoint(nil), m.HistogramDataPoints)
@ -264,13 +272,13 @@ func TestSumInt64Datapoints(t *testing.T) {
} }
} }
func TestSumFloat64Datapoints(t *testing.T) { func TestSumFloat64DataPoints(t *testing.T) {
desc := metricsdk.NewDescriptor("", metricsdk.MeasureKind, []core.Key{}, "", unit.Dimensionless, core.Float64NumberKind) desc := metric.NewDescriptor("", metric.MeasureKind, core.Float64NumberKind)
labels := metricsdk.NewLabels([]core.KeyValue{}, "", nil) labels := export.NewLabels([]core.KeyValue{}, "", nil)
s := sumAgg.New() s := sumAgg.New()
assert.NoError(t, s.Update(context.Background(), core.NewFloat64Number(1), desc)) assert.NoError(t, s.Update(context.Background(), core.NewFloat64Number(1), &desc))
s.Checkpoint(context.Background(), desc) s.Checkpoint(context.Background(), &desc)
if m, err := sum(desc, labels, s); assert.NoError(t, err) { if m, err := sum(&desc, labels, s); assert.NoError(t, err) {
assert.Equal(t, []*metricpb.Int64DataPoint(nil), m.Int64DataPoints) assert.Equal(t, []*metricpb.Int64DataPoint(nil), m.Int64DataPoints)
assert.Equal(t, []*metricpb.DoubleDataPoint{{Value: 1}}, m.DoubleDataPoints) assert.Equal(t, []*metricpb.DoubleDataPoint{{Value: 1}}, m.DoubleDataPoints)
assert.Equal(t, []*metricpb.HistogramDataPoint(nil), m.HistogramDataPoints) assert.Equal(t, []*metricpb.HistogramDataPoint(nil), m.HistogramDataPoints)

View File

@ -27,9 +27,9 @@ import (
metricpb "github.com/open-telemetry/opentelemetry-proto/gen/go/metrics/v1" metricpb "github.com/open-telemetry/opentelemetry-proto/gen/go/metrics/v1"
"go.opentelemetry.io/otel/api/core" "go.opentelemetry.io/otel/api/core"
"go.opentelemetry.io/otel/api/metric"
metricapi "go.opentelemetry.io/otel/api/metric" metricapi "go.opentelemetry.io/otel/api/metric"
"go.opentelemetry.io/otel/exporters/otlp" "go.opentelemetry.io/otel/exporters/otlp"
metricsdk "go.opentelemetry.io/otel/sdk/export/metric"
export "go.opentelemetry.io/otel/sdk/export/trace" export "go.opentelemetry.io/otel/sdk/export/trace"
"go.opentelemetry.io/otel/sdk/metric/batcher/ungrouped" "go.opentelemetry.io/otel/sdk/metric/batcher/ungrouped"
"go.opentelemetry.io/otel/sdk/metric/controller/push" "go.opentelemetry.io/otel/sdk/metric/controller/push"
@ -119,21 +119,21 @@ func newExporterEndToEndTest(t *testing.T, additionalOpts []otlp.ExporterOption)
labels := meter.Labels(core.Key("test").Bool(true)) labels := meter.Labels(core.Key("test").Bool(true))
type data struct { type data struct {
iKind metricsdk.Kind iKind metric.Kind
nKind core.NumberKind nKind core.NumberKind
val int64 val int64
} }
instruments := map[string]data{ instruments := map[string]data{
"test-int64-counter": {metricsdk.CounterKind, core.Int64NumberKind, 1}, "test-int64-counter": {metric.CounterKind, core.Int64NumberKind, 1},
"test-float64-counter": {metricsdk.CounterKind, core.Float64NumberKind, 1}, "test-float64-counter": {metric.CounterKind, core.Float64NumberKind, 1},
"test-int64-measure": {metricsdk.MeasureKind, core.Int64NumberKind, 2}, "test-int64-measure": {metric.MeasureKind, core.Int64NumberKind, 2},
"test-float64-measure": {metricsdk.MeasureKind, core.Float64NumberKind, 2}, "test-float64-measure": {metric.MeasureKind, core.Float64NumberKind, 2},
"test-int64-observer": {metricsdk.ObserverKind, core.Int64NumberKind, 3}, "test-int64-observer": {metric.ObserverKind, core.Int64NumberKind, 3},
"test-float64-observer": {metricsdk.ObserverKind, core.Float64NumberKind, 3}, "test-float64-observer": {metric.ObserverKind, core.Float64NumberKind, 3},
} }
for name, data := range instruments { for name, data := range instruments {
switch data.iKind { switch data.iKind {
case metricsdk.CounterKind: case metric.CounterKind:
switch data.nKind { switch data.nKind {
case core.Int64NumberKind: case core.Int64NumberKind:
metricapi.Must(meter).NewInt64Counter(name).Add(ctx, data.val, labels) metricapi.Must(meter).NewInt64Counter(name).Add(ctx, data.val, labels)
@ -142,7 +142,7 @@ func newExporterEndToEndTest(t *testing.T, additionalOpts []otlp.ExporterOption)
default: default:
assert.Failf(t, "unsupported number testing kind", data.nKind.String()) assert.Failf(t, "unsupported number testing kind", data.nKind.String())
} }
case metricsdk.MeasureKind: case metric.MeasureKind:
switch data.nKind { switch data.nKind {
case core.Int64NumberKind: case core.Int64NumberKind:
metricapi.Must(meter).NewInt64Measure(name).Record(ctx, data.val, labels) metricapi.Must(meter).NewInt64Measure(name).Record(ctx, data.val, labels)
@ -151,7 +151,7 @@ func newExporterEndToEndTest(t *testing.T, additionalOpts []otlp.ExporterOption)
default: default:
assert.Failf(t, "unsupported number testing kind", data.nKind.String()) assert.Failf(t, "unsupported number testing kind", data.nKind.String())
} }
case metricsdk.ObserverKind: case metric.ObserverKind:
switch data.nKind { switch data.nKind {
case core.Int64NumberKind: case core.Int64NumberKind:
callback := func(v int64) metricapi.Int64ObserverCallback { callback := func(v int64) metricapi.Int64ObserverCallback {
@ -228,7 +228,7 @@ func newExporterEndToEndTest(t *testing.T, additionalOpts []otlp.ExporterOption)
seen[desc.Name] = struct{}{} seen[desc.Name] = struct{}{}
switch data.iKind { switch data.iKind {
case metricsdk.CounterKind: case metric.CounterKind:
switch data.nKind { switch data.nKind {
case core.Int64NumberKind: case core.Int64NumberKind:
assert.Equal(t, metricpb.MetricDescriptor_COUNTER_INT64.String(), desc.GetType().String()) assert.Equal(t, metricpb.MetricDescriptor_COUNTER_INT64.String(), desc.GetType().String())
@ -243,7 +243,7 @@ func newExporterEndToEndTest(t *testing.T, additionalOpts []otlp.ExporterOption)
default: default:
assert.Failf(t, "invalid number kind", data.nKind.String()) assert.Failf(t, "invalid number kind", data.nKind.String())
} }
case metricsdk.MeasureKind, metricsdk.ObserverKind: case metric.MeasureKind, metric.ObserverKind:
assert.Equal(t, metricpb.MetricDescriptor_SUMMARY.String(), desc.GetType().String()) assert.Equal(t, metricpb.MetricDescriptor_SUMMARY.String(), desc.GetType().String())
m.GetSummaryDataPoints() m.GetSummaryDataPoints()
if dp := m.GetSummaryDataPoints(); assert.Len(t, dp, 1) { if dp := m.GetSummaryDataPoints(); assert.Len(t, dp, 1) {

View File

@ -19,22 +19,16 @@ import (
"sync" "sync"
"go.opentelemetry.io/otel/api/core" "go.opentelemetry.io/otel/api/core"
"go.opentelemetry.io/otel/api/metric"
apimetric "go.opentelemetry.io/otel/api/metric" apimetric "go.opentelemetry.io/otel/api/metric"
) )
type ( type (
Handle struct { Handle struct {
Instrument *Instrument Instrument *Sync
LabelSet *LabelSet LabelSet *LabelSet
} }
Instrument struct {
Name string
Kind Kind
NumberKind core.NumberKind
Config apimetric.Config
}
LabelSet struct { LabelSet struct {
TheMeter *Meter TheMeter *Meter
Labels map[core.Key]core.Value Labels map[core.Key]core.Value
@ -54,101 +48,77 @@ type (
Meter struct { Meter struct {
MeasurementBatches []Batch MeasurementBatches []Batch
// Observers contains also unregistered AsyncInstruments []*Async
// observers. Check the Dead field of the Observer to
// figure out its status.
Observers []*Observer
} }
Kind int8
Measurement struct { Measurement struct {
// Number needs to be aligned for 64-bit atomic operations. // Number needs to be aligned for 64-bit atomic operations.
Number core.Number Number core.Number
Instrument *Instrument Instrument apimetric.InstrumentImpl
} }
observerResult struct { Instrument struct {
instrument *Instrument meter *Meter
descriptor apimetric.Descriptor
} }
int64ObserverResult struct { Async struct {
result observerResult Instrument
callback func(func(core.Number, apimetric.LabelSet))
} }
float64ObserverResult struct { Sync struct {
result observerResult Instrument
}
observerCallback func(observerResult)
Observer struct {
Instrument *Instrument
Meter *Meter
Dead bool
callback observerCallback
} }
) )
var ( var (
_ apimetric.InstrumentImpl = &Instrument{} _ apimetric.SyncImpl = &Sync{}
_ apimetric.BoundInstrumentImpl = &Handle{} _ apimetric.BoundSyncImpl = &Handle{}
_ apimetric.LabelSet = &LabelSet{} _ apimetric.LabelSet = &LabelSet{}
_ apimetric.Meter = &Meter{} _ apimetric.MeterImpl = &Meter{}
_ apimetric.Int64Observer = &Observer{} _ apimetric.AsyncImpl = &Async{}
_ apimetric.Float64Observer = &Observer{}
_ apimetric.Int64ObserverResult = int64ObserverResult{}
_ apimetric.Float64ObserverResult = float64ObserverResult{}
) )
const ( func (i Instrument) Descriptor() apimetric.Descriptor {
KindCounter Kind = iota return i.descriptor
KindMeasure
KindObserver
)
func (o *Observer) Unregister() {
o.Dead = true
} }
func (r int64ObserverResult) Observe(value int64, labels apimetric.LabelSet) { func (a *Async) Implementation() interface{} {
r.result.observe(core.NewInt64Number(value), labels) return a
} }
func (r float64ObserverResult) Observe(value float64, labels apimetric.LabelSet) { func (s *Sync) Implementation() interface{} {
r.result.observe(core.NewFloat64Number(value), labels) return s
} }
func (r observerResult) observe(number core.Number, labels apimetric.LabelSet) { func (s *Sync) Bind(labels apimetric.LabelSet) apimetric.BoundSyncImpl {
r.instrument.RecordOne(context.Background(), number, labels)
}
func (i *Instrument) Bind(labels apimetric.LabelSet) apimetric.BoundInstrumentImpl {
if ld, ok := labels.(apimetric.LabelSetDelegate); ok { if ld, ok := labels.(apimetric.LabelSetDelegate); ok {
labels = ld.Delegate() labels = ld.Delegate()
} }
return &Handle{ return &Handle{
Instrument: i, Instrument: s,
LabelSet: labels.(*LabelSet), LabelSet: labels.(*LabelSet),
} }
} }
func (i *Instrument) RecordOne(ctx context.Context, number core.Number, labels apimetric.LabelSet) { func (s *Sync) RecordOne(ctx context.Context, number core.Number, labels apimetric.LabelSet) {
if ld, ok := labels.(apimetric.LabelSetDelegate); ok { if ld, ok := labels.(apimetric.LabelSetDelegate); ok {
labels = ld.Delegate() labels = ld.Delegate()
} }
doRecordBatch(ctx, labels.(*LabelSet), i, number) s.meter.doRecordSingle(ctx, labels.(*LabelSet), s, number)
} }
func (h *Handle) RecordOne(ctx context.Context, number core.Number) { func (h *Handle) RecordOne(ctx context.Context, number core.Number) {
doRecordBatch(ctx, h.LabelSet, h.Instrument, number) h.Instrument.meter.doRecordSingle(ctx, h.LabelSet, h.Instrument, number)
} }
func (h *Handle) Unbind() { func (h *Handle) Unbind() {
} }
func doRecordBatch(ctx context.Context, labelSet *LabelSet, instrument *Instrument, number core.Number) { func (m *Meter) doRecordSingle(ctx context.Context, labelSet *LabelSet, instrument apimetric.InstrumentImpl, number core.Number) {
labelSet.TheMeter.recordMockBatch(ctx, labelSet, Measurement{ m.recordMockBatch(ctx, labelSet, Measurement{
Instrument: instrument, Instrument: instrument,
Number: number, Number: number,
}) })
@ -167,13 +137,14 @@ func (p *MeterProvider) Meter(name string) apimetric.Meter {
if lookup, ok := p.registered[name]; ok { if lookup, ok := p.registered[name]; ok {
return lookup return lookup
} }
m := NewMeter() _, m := NewMeter()
p.registered[name] = m p.registered[name] = m
return m return m
} }
func NewMeter() *Meter { func NewMeter() (*Meter, apimetric.Meter) {
return &Meter{} mock := &Meter{}
return mock, apimetric.WrapMeterImpl(mock)
} }
func (m *Meter) Labels(labels ...core.KeyValue) apimetric.LabelSet { func (m *Meter) Labels(labels ...core.KeyValue) apimetric.LabelSet {
@ -187,92 +158,25 @@ func (m *Meter) Labels(labels ...core.KeyValue) apimetric.LabelSet {
} }
} }
func (m *Meter) NewInt64Counter(name string, opts ...apimetric.Option) (apimetric.Int64Counter, error) { func (m *Meter) NewSyncInstrument(descriptor metric.Descriptor) (apimetric.SyncImpl, error) {
instrument := m.newCounterInstrument(name, core.Int64NumberKind, opts) return &Sync{
return apimetric.WrapInt64CounterInstrument(instrument, nil) Instrument{
} descriptor: descriptor,
meter: m,
func (m *Meter) NewFloat64Counter(name string, opts ...apimetric.Option) (apimetric.Float64Counter, error) { },
instrument := m.newCounterInstrument(name, core.Float64NumberKind, opts) }, nil
return apimetric.WrapFloat64CounterInstrument(instrument, nil) }
}
func (m *Meter) NewAsyncInstrument(descriptor metric.Descriptor, callback func(func(core.Number, apimetric.LabelSet))) (apimetric.AsyncImpl, error) {
func (m *Meter) newCounterInstrument(name string, numberKind core.NumberKind, opts []apimetric.Option) *Instrument { a := &Async{
return &Instrument{ Instrument: Instrument{
Name: name, descriptor: descriptor,
Kind: KindCounter, meter: m,
NumberKind: numberKind,
Config: apimetric.Configure(opts),
}
}
func (m *Meter) NewInt64Measure(name string, opts ...apimetric.Option) (apimetric.Int64Measure, error) {
instrument := m.newMeasureInstrument(name, core.Int64NumberKind, opts)
return apimetric.WrapInt64MeasureInstrument(instrument, nil)
}
func (m *Meter) NewFloat64Measure(name string, opts ...apimetric.Option) (apimetric.Float64Measure, error) {
instrument := m.newMeasureInstrument(name, core.Float64NumberKind, opts)
return apimetric.WrapFloat64MeasureInstrument(instrument, nil)
}
func (m *Meter) newMeasureInstrument(name string, numberKind core.NumberKind, opts []apimetric.Option) *Instrument {
return &Instrument{
Name: name,
Kind: KindMeasure,
NumberKind: numberKind,
Config: apimetric.Configure(opts),
}
}
func (m *Meter) RegisterInt64Observer(name string, callback apimetric.Int64ObserverCallback, opts ...apimetric.Option) (apimetric.Int64Observer, error) {
wrappedCallback := wrapInt64ObserverCallback(callback)
return m.newObserver(name, wrappedCallback, core.Int64NumberKind, opts), nil
}
func wrapInt64ObserverCallback(callback apimetric.Int64ObserverCallback) observerCallback {
if callback == nil {
return func(result observerResult) {}
}
return func(result observerResult) {
typeSafeResult := int64ObserverResult{
result: result,
}
callback(typeSafeResult)
}
}
func (m *Meter) RegisterFloat64Observer(name string, callback apimetric.Float64ObserverCallback, opts ...apimetric.Option) (apimetric.Float64Observer, error) {
wrappedCallback := wrapFloat64ObserverCallback(callback)
return m.newObserver(name, wrappedCallback, core.Float64NumberKind, opts), nil
}
func wrapFloat64ObserverCallback(callback apimetric.Float64ObserverCallback) observerCallback {
if callback == nil {
return func(result observerResult) {}
}
return func(result observerResult) {
typeSafeResult := float64ObserverResult{
result: result,
}
callback(typeSafeResult)
}
}
func (m *Meter) newObserver(name string, callback observerCallback, numberKind core.NumberKind, opts []apimetric.Option) *Observer {
obs := &Observer{
Instrument: &Instrument{
Name: name,
Kind: KindObserver,
NumberKind: numberKind,
Config: apimetric.Configure(opts),
}, },
Meter: m,
Dead: false,
callback: callback, callback: callback,
} }
m.Observers = append(m.Observers, obs) m.AsyncInstruments = append(m.AsyncInstruments, a)
return obs return a, nil
} }
func (m *Meter) RecordBatch(ctx context.Context, labels apimetric.LabelSet, measurements ...apimetric.Measurement) { func (m *Meter) RecordBatch(ctx context.Context, labels apimetric.LabelSet, measurements ...apimetric.Measurement) {
@ -281,7 +185,7 @@ func (m *Meter) RecordBatch(ctx context.Context, labels apimetric.LabelSet, meas
for i := 0; i < len(measurements); i++ { for i := 0; i < len(measurements); i++ {
m := measurements[i] m := measurements[i]
mm[i] = Measurement{ mm[i] = Measurement{
Instrument: m.InstrumentImpl().(*Instrument), Instrument: m.SyncImpl().(*Sync),
Number: m.Number(), Number: m.Number(),
} }
} }
@ -296,13 +200,15 @@ func (m *Meter) recordMockBatch(ctx context.Context, labelSet *LabelSet, measure
}) })
} }
func (m *Meter) RunObservers() { func (m *Meter) RunAsyncInstruments() {
for _, observer := range m.Observers { for _, observer := range m.AsyncInstruments {
if observer.Dead { observer.callback(func(n core.Number, labels apimetric.LabelSet) {
continue
} if ld, ok := labels.(apimetric.LabelSetDelegate); ok {
observer.callback(observerResult{ labels = ld.Delegate()
instrument: observer.Instrument, }
m.doRecordSingle(context.Background(), labels.(*LabelSet), observer, n)
}) })
} }
} }

View File

@ -20,6 +20,7 @@ import (
"time" "time"
"go.opentelemetry.io/otel/api/core" "go.opentelemetry.io/otel/api/core"
"go.opentelemetry.io/otel/api/metric"
export "go.opentelemetry.io/otel/sdk/export/metric" export "go.opentelemetry.io/otel/sdk/export/metric"
) )
@ -116,7 +117,7 @@ func NewInconsistentMergeError(a1, a2 export.Aggregator) error {
// This rejects NaN values. This rejects negative values when the // This rejects NaN values. This rejects negative values when the
// metric instrument does not support negative values, including // metric instrument does not support negative values, including
// monotonic counter metrics and absolute measure metrics. // monotonic counter metrics and absolute measure metrics.
func RangeTest(number core.Number, descriptor *export.Descriptor) error { func RangeTest(number core.Number, descriptor *metric.Descriptor) error {
numberKind := descriptor.NumberKind() numberKind := descriptor.NumberKind()
if numberKind == core.Float64NumberKind && math.IsNaN(number.AsFloat64()) { if numberKind == core.Float64NumberKind && math.IsNaN(number.AsFloat64()) {
@ -124,7 +125,7 @@ func RangeTest(number core.Number, descriptor *export.Descriptor) error {
} }
switch descriptor.MetricKind() { switch descriptor.MetricKind() {
case export.CounterKind: case metric.CounterKind:
if number.IsNegative(numberKind) { if number.IsNegative(numberKind) {
return ErrNegativeInput return ErrNegativeInput
} }

View File

@ -22,7 +22,7 @@ import (
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
"go.opentelemetry.io/otel/api/core" "go.opentelemetry.io/otel/api/core"
export "go.opentelemetry.io/otel/sdk/export/metric" "go.opentelemetry.io/otel/api/metric"
"go.opentelemetry.io/otel/sdk/export/metric/aggregator" "go.opentelemetry.io/otel/sdk/export/metric/aggregator"
"go.opentelemetry.io/otel/sdk/metric/aggregator/lastvalue" "go.opentelemetry.io/otel/sdk/metric/aggregator/lastvalue"
"go.opentelemetry.io/otel/sdk/metric/aggregator/sum" "go.opentelemetry.io/otel/sdk/metric/aggregator/sum"
@ -38,7 +38,7 @@ func TestInconsistentMergeErr(t *testing.T) {
require.True(t, errors.Is(err, aggregator.ErrInconsistentType)) require.True(t, errors.Is(err, aggregator.ErrInconsistentType))
} }
func testRangeNaN(t *testing.T, desc *export.Descriptor) { func testRangeNaN(t *testing.T, desc *metric.Descriptor) {
// If the descriptor uses int64 numbers, this won't register as NaN // If the descriptor uses int64 numbers, this won't register as NaN
nan := core.NewFloat64Number(math.NaN()) nan := core.NewFloat64Number(math.NaN())
err := aggregator.RangeTest(nan, desc) err := aggregator.RangeTest(nan, desc)
@ -50,7 +50,7 @@ func testRangeNaN(t *testing.T, desc *export.Descriptor) {
} }
} }
func testRangeNegative(t *testing.T, desc *export.Descriptor) { func testRangeNegative(t *testing.T, desc *metric.Descriptor) {
var neg, pos core.Number var neg, pos core.Number
if desc.NumberKind() == core.Float64NumberKind { if desc.NumberKind() == core.Float64NumberKind {
@ -72,15 +72,12 @@ func TestRangeTest(t *testing.T) {
// Only Counters implement a range test. // Only Counters implement a range test.
for _, nkind := range []core.NumberKind{core.Float64NumberKind, core.Int64NumberKind} { for _, nkind := range []core.NumberKind{core.Float64NumberKind, core.Int64NumberKind} {
t.Run(nkind.String(), func(t *testing.T) { t.Run(nkind.String(), func(t *testing.T) {
desc := export.NewDescriptor( desc := metric.NewDescriptor(
"name", "name",
export.CounterKind, metric.CounterKind,
nil,
"",
"",
nkind, nkind,
) )
testRangeNegative(t, desc) testRangeNegative(t, &desc)
}) })
} }
} }
@ -88,20 +85,17 @@ func TestRangeTest(t *testing.T) {
func TestNaNTest(t *testing.T) { func TestNaNTest(t *testing.T) {
for _, nkind := range []core.NumberKind{core.Float64NumberKind, core.Int64NumberKind} { for _, nkind := range []core.NumberKind{core.Float64NumberKind, core.Int64NumberKind} {
t.Run(nkind.String(), func(t *testing.T) { t.Run(nkind.String(), func(t *testing.T) {
for _, mkind := range []export.Kind{ for _, mkind := range []metric.Kind{
export.CounterKind, metric.CounterKind,
export.MeasureKind, metric.MeasureKind,
export.ObserverKind, metric.ObserverKind,
} { } {
desc := export.NewDescriptor( desc := metric.NewDescriptor(
"name", "name",
mkind, mkind,
nil,
"",
"",
nkind, nkind,
) )
testRangeNaN(t, desc) testRangeNaN(t, &desc)
} }
}) })
} }

View File

@ -14,13 +14,11 @@
package metric package metric
//go:generate stringer -type=Kind
import ( import (
"context" "context"
"go.opentelemetry.io/otel/api/core" "go.opentelemetry.io/otel/api/core"
"go.opentelemetry.io/otel/api/unit" "go.opentelemetry.io/otel/api/metric"
) )
// Batcher is responsible for deciding which kind of aggregation to // Batcher is responsible for deciding which kind of aggregation to
@ -102,7 +100,7 @@ type AggregationSelector interface {
// Note: This is context-free because the aggregator should // Note: This is context-free because the aggregator should
// not relate to the incoming context. This call should not // not relate to the incoming context. This call should not
// block. // block.
AggregatorFor(*Descriptor) Aggregator AggregatorFor(*metric.Descriptor) Aggregator
} }
// Aggregator implements a specific aggregation behavior, e.g., a // Aggregator implements a specific aggregation behavior, e.g., a
@ -132,7 +130,7 @@ type Aggregator interface {
// //
// The Context argument comes from user-level code and could be // The Context argument comes from user-level code and could be
// inspected for distributed or span context. // inspected for distributed or span context.
Update(context.Context, core.Number, *Descriptor) error Update(context.Context, core.Number, *metric.Descriptor) error
// Checkpoint is called during collection to finish one period // Checkpoint is called during collection to finish one period
// of aggregation by atomically saving the current value. // of aggregation by atomically saving the current value.
@ -147,13 +145,13 @@ type Aggregator interface {
// //
// The Context argument originates from the controller that // The Context argument originates from the controller that
// orchestrates collection. // orchestrates collection.
Checkpoint(context.Context, *Descriptor) Checkpoint(context.Context, *metric.Descriptor)
// Merge combines the checkpointed state from the argument // Merge combines the checkpointed state from the argument
// aggregator into this aggregator's checkpointed state. // aggregator into this aggregator's checkpointed state.
// Merge() is called in a single-threaded context, no locking // Merge() is called in a single-threaded context, no locking
// is required. // is required.
Merge(Aggregator, *Descriptor) error Merge(Aggregator, *metric.Descriptor) error
} }
// Exporter handles presentation of the checkpoint of aggregate // Exporter handles presentation of the checkpoint of aggregate
@ -213,7 +211,7 @@ type CheckpointSet interface {
// Record contains the exported data for a single metric instrument // Record contains the exported data for a single metric instrument
// and label set. // and label set.
type Record struct { type Record struct {
descriptor *Descriptor descriptor *metric.Descriptor
labels Labels labels Labels
aggregator Aggregator aggregator Aggregator
} }
@ -263,7 +261,7 @@ func (l Labels) Len() int {
// NewRecord allows Batcher implementations to construct export // NewRecord allows Batcher implementations to construct export
// records. The Descriptor, Labels, and Aggregator represent // records. The Descriptor, Labels, and Aggregator represent
// aggregate metric events received over a single collection period. // aggregate metric events received over a single collection period.
func NewRecord(descriptor *Descriptor, labels Labels, aggregator Aggregator) Record { func NewRecord(descriptor *metric.Descriptor, labels Labels, aggregator Aggregator) Record {
return Record{ return Record{
descriptor: descriptor, descriptor: descriptor,
labels: labels, labels: labels,
@ -278,7 +276,7 @@ func (r Record) Aggregator() Aggregator {
} }
// Descriptor describes the metric instrument being exported. // Descriptor describes the metric instrument being exported.
func (r Record) Descriptor() *Descriptor { func (r Record) Descriptor() *metric.Descriptor {
return r.descriptor return r.descriptor
} }
@ -287,91 +285,3 @@ func (r Record) Descriptor() *Descriptor {
func (r Record) Labels() Labels { func (r Record) Labels() Labels {
return r.labels return r.labels
} }
// Kind describes the kind of instrument.
type Kind int8
const (
// Counter kind indicates a counter instrument.
CounterKind Kind = iota
// Measure kind indicates a measure instrument.
MeasureKind
// Observer kind indicates an observer instrument
ObserverKind
)
// Descriptor describes a metric instrument to the exporter.
//
// Descriptors are created once per instrument and a pointer to the
// descriptor may be used to uniquely identify the instrument in an
// exporter.
type Descriptor struct {
name string
metricKind Kind
keys []core.Key
description string
unit unit.Unit
numberKind core.NumberKind
}
// NewDescriptor builds a new descriptor, for use by `Meter`
// implementations in constructing new metric instruments.
//
// Descriptors are created once per instrument and a pointer to the
// descriptor may be used to uniquely identify the instrument in an
// exporter.
func NewDescriptor(
name string,
metricKind Kind,
keys []core.Key,
description string,
unit unit.Unit,
numberKind core.NumberKind,
) *Descriptor {
return &Descriptor{
name: name,
metricKind: metricKind,
keys: keys,
description: description,
unit: unit,
numberKind: numberKind,
}
}
// Name returns the metric instrument's name.
func (d *Descriptor) Name() string {
return d.name
}
// MetricKind returns the kind of instrument: counter, measure, or
// observer.
func (d *Descriptor) MetricKind() Kind {
return d.metricKind
}
// Keys returns the recommended keys included in the metric
// definition. These keys may be used by a Batcher as a default set
// of grouping keys for the metric instrument.
func (d *Descriptor) Keys() []core.Key {
return d.keys
}
// Description provides a human-readable description of the metric
// instrument.
func (d *Descriptor) Description() string {
return d.description
}
// Unit describes the units of the metric instrument. Unitless
// metrics return the empty string.
func (d *Descriptor) Unit() unit.Unit {
return d.unit
}
// NumberKind returns whether this instrument is declared over int64
// or a float64 values.
func (d *Descriptor) NumberKind() core.NumberKind {
return d.numberKind
}

View File

@ -22,6 +22,7 @@ import (
"unsafe" "unsafe"
"go.opentelemetry.io/otel/api/core" "go.opentelemetry.io/otel/api/core"
"go.opentelemetry.io/otel/api/metric"
export "go.opentelemetry.io/otel/sdk/export/metric" export "go.opentelemetry.io/otel/sdk/export/metric"
"go.opentelemetry.io/otel/sdk/export/metric/aggregator" "go.opentelemetry.io/otel/sdk/export/metric/aggregator"
) )
@ -83,7 +84,7 @@ func (c *Aggregator) Points() ([]core.Number, error) {
// Checkpoint saves the current state and resets the current state to // Checkpoint saves the current state and resets the current state to
// the empty set, taking a lock to prevent concurrent Update() calls. // the empty set, taking a lock to prevent concurrent Update() calls.
func (c *Aggregator) Checkpoint(ctx context.Context, desc *export.Descriptor) { func (c *Aggregator) Checkpoint(ctx context.Context, desc *metric.Descriptor) {
c.lock.Lock() c.lock.Lock()
c.checkpoint, c.current = c.current, nil c.checkpoint, c.current = c.current, nil
c.lock.Unlock() c.lock.Unlock()
@ -106,7 +107,7 @@ func (c *Aggregator) Checkpoint(ctx context.Context, desc *export.Descriptor) {
// Update adds the recorded measurement to the current data set. // Update adds the recorded measurement to the current data set.
// Update takes a lock to prevent concurrent Update() and Checkpoint() // Update takes a lock to prevent concurrent Update() and Checkpoint()
// calls. // calls.
func (c *Aggregator) Update(_ context.Context, number core.Number, desc *export.Descriptor) error { func (c *Aggregator) Update(_ context.Context, number core.Number, desc *metric.Descriptor) error {
c.lock.Lock() c.lock.Lock()
c.current = append(c.current, number) c.current = append(c.current, number)
c.lock.Unlock() c.lock.Unlock()
@ -114,7 +115,7 @@ func (c *Aggregator) Update(_ context.Context, number core.Number, desc *export.
} }
// Merge combines two data sets into one. // Merge combines two data sets into one.
func (c *Aggregator) Merge(oa export.Aggregator, desc *export.Descriptor) error { func (c *Aggregator) Merge(oa export.Aggregator, desc *metric.Descriptor) error {
o, _ := oa.(*Aggregator) o, _ := oa.(*Aggregator)
if o == nil { if o == nil {
return aggregator.NewInconsistentMergeError(c, oa) return aggregator.NewInconsistentMergeError(c, oa)

View File

@ -25,8 +25,8 @@ import (
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
"go.opentelemetry.io/otel/api/core" "go.opentelemetry.io/otel/api/core"
"go.opentelemetry.io/otel/api/metric"
ottest "go.opentelemetry.io/otel/internal/testing" ottest "go.opentelemetry.io/otel/internal/testing"
export "go.opentelemetry.io/otel/sdk/export/metric"
"go.opentelemetry.io/otel/sdk/export/metric/aggregator" "go.opentelemetry.io/otel/sdk/export/metric/aggregator"
"go.opentelemetry.io/otel/sdk/metric/aggregator/test" "go.opentelemetry.io/otel/sdk/metric/aggregator/test"
) )
@ -51,7 +51,7 @@ type updateTest struct {
} }
func (ut *updateTest) run(t *testing.T, profile test.Profile) { func (ut *updateTest) run(t *testing.T, profile test.Profile) {
descriptor := test.NewAggregatorTest(export.MeasureKind, profile.NumberKind) descriptor := test.NewAggregatorTest(metric.MeasureKind, profile.NumberKind)
agg := New() agg := New()
@ -119,7 +119,7 @@ type mergeTest struct {
func (mt *mergeTest) run(t *testing.T, profile test.Profile) { func (mt *mergeTest) run(t *testing.T, profile test.Profile) {
ctx := context.Background() ctx := context.Background()
descriptor := test.NewAggregatorTest(export.MeasureKind, profile.NumberKind) descriptor := test.NewAggregatorTest(metric.MeasureKind, profile.NumberKind)
agg1 := New() agg1 := New()
agg2 := New() agg2 := New()
@ -216,7 +216,7 @@ func TestArrayErrors(t *testing.T) {
ctx := context.Background() ctx := context.Background()
descriptor := test.NewAggregatorTest(export.MeasureKind, profile.NumberKind) descriptor := test.NewAggregatorTest(metric.MeasureKind, profile.NumberKind)
test.CheckedUpdate(t, agg, core.Number(0), descriptor) test.CheckedUpdate(t, agg, core.Number(0), descriptor)
@ -244,7 +244,7 @@ func TestArrayErrors(t *testing.T) {
} }
func TestArrayFloat64(t *testing.T) { func TestArrayFloat64(t *testing.T) {
descriptor := test.NewAggregatorTest(export.MeasureKind, core.Float64NumberKind) descriptor := test.NewAggregatorTest(metric.MeasureKind, core.Float64NumberKind)
fpsf := func(sign int) []float64 { fpsf := func(sign int) []float64 {
// Check behavior of a bunch of odd floating // Check behavior of a bunch of odd floating

View File

@ -21,6 +21,7 @@ import (
sdk "github.com/DataDog/sketches-go/ddsketch" sdk "github.com/DataDog/sketches-go/ddsketch"
"go.opentelemetry.io/otel/api/core" "go.opentelemetry.io/otel/api/core"
"go.opentelemetry.io/otel/api/metric"
export "go.opentelemetry.io/otel/sdk/export/metric" export "go.opentelemetry.io/otel/sdk/export/metric"
"go.opentelemetry.io/otel/sdk/export/metric/aggregator" "go.opentelemetry.io/otel/sdk/export/metric/aggregator"
@ -43,7 +44,7 @@ var _ aggregator.MinMaxSumCount = &Aggregator{}
var _ aggregator.Distribution = &Aggregator{} var _ aggregator.Distribution = &Aggregator{}
// New returns a new DDSketch aggregator. // New returns a new DDSketch aggregator.
func New(cfg *Config, desc *export.Descriptor) *Aggregator { func New(cfg *Config, desc *metric.Descriptor) *Aggregator {
return &Aggregator{ return &Aggregator{
cfg: cfg, cfg: cfg,
kind: desc.NumberKind(), kind: desc.NumberKind(),
@ -103,7 +104,7 @@ func (c *Aggregator) toNumber(f float64) core.Number {
// Checkpoint saves the current state and resets the current state to // Checkpoint saves the current state and resets the current state to
// the empty set, taking a lock to prevent concurrent Update() calls. // the empty set, taking a lock to prevent concurrent Update() calls.
func (c *Aggregator) Checkpoint(ctx context.Context, _ *export.Descriptor) { func (c *Aggregator) Checkpoint(ctx context.Context, _ *metric.Descriptor) {
replace := sdk.NewDDSketch(c.cfg) replace := sdk.NewDDSketch(c.cfg)
c.lock.Lock() c.lock.Lock()
@ -115,7 +116,7 @@ func (c *Aggregator) Checkpoint(ctx context.Context, _ *export.Descriptor) {
// Update adds the recorded measurement to the current data set. // Update adds the recorded measurement to the current data set.
// Update takes a lock to prevent concurrent Update() and Checkpoint() // Update takes a lock to prevent concurrent Update() and Checkpoint()
// calls. // calls.
func (c *Aggregator) Update(_ context.Context, number core.Number, desc *export.Descriptor) error { func (c *Aggregator) Update(_ context.Context, number core.Number, desc *metric.Descriptor) error {
c.lock.Lock() c.lock.Lock()
defer c.lock.Unlock() defer c.lock.Unlock()
c.current.Add(number.CoerceToFloat64(desc.NumberKind())) c.current.Add(number.CoerceToFloat64(desc.NumberKind()))
@ -123,7 +124,7 @@ func (c *Aggregator) Update(_ context.Context, number core.Number, desc *export.
} }
// Merge combines two sketches into one. // Merge combines two sketches into one.
func (c *Aggregator) Merge(oa export.Aggregator, d *export.Descriptor) error { func (c *Aggregator) Merge(oa export.Aggregator, d *metric.Descriptor) error {
o, _ := oa.(*Aggregator) o, _ := oa.(*Aggregator)
if o == nil { if o == nil {
return aggregator.NewInconsistentMergeError(c, oa) return aggregator.NewInconsistentMergeError(c, oa)

View File

@ -21,7 +21,7 @@ import (
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
export "go.opentelemetry.io/otel/sdk/export/metric" "go.opentelemetry.io/otel/api/metric"
"go.opentelemetry.io/otel/sdk/metric/aggregator/test" "go.opentelemetry.io/otel/sdk/metric/aggregator/test"
) )
@ -33,7 +33,7 @@ type updateTest struct {
func (ut *updateTest) run(t *testing.T, profile test.Profile) { func (ut *updateTest) run(t *testing.T, profile test.Profile) {
ctx := context.Background() ctx := context.Background()
descriptor := test.NewAggregatorTest(export.MeasureKind, profile.NumberKind) descriptor := test.NewAggregatorTest(metric.MeasureKind, profile.NumberKind)
agg := New(NewDefaultConfig(), descriptor) agg := New(NewDefaultConfig(), descriptor)
all := test.NewNumbers(profile.NumberKind) all := test.NewNumbers(profile.NumberKind)
@ -92,7 +92,7 @@ type mergeTest struct {
func (mt *mergeTest) run(t *testing.T, profile test.Profile) { func (mt *mergeTest) run(t *testing.T, profile test.Profile) {
ctx := context.Background() ctx := context.Background()
descriptor := test.NewAggregatorTest(export.MeasureKind, profile.NumberKind) descriptor := test.NewAggregatorTest(metric.MeasureKind, profile.NumberKind)
agg1 := New(NewDefaultConfig(), descriptor) agg1 := New(NewDefaultConfig(), descriptor)
agg2 := New(NewDefaultConfig(), descriptor) agg2 := New(NewDefaultConfig(), descriptor)

View File

@ -19,6 +19,7 @@ import (
"sort" "sort"
"go.opentelemetry.io/otel/api/core" "go.opentelemetry.io/otel/api/core"
"go.opentelemetry.io/otel/api/metric"
export "go.opentelemetry.io/otel/sdk/export/metric" export "go.opentelemetry.io/otel/sdk/export/metric"
"go.opentelemetry.io/otel/sdk/export/metric/aggregator" "go.opentelemetry.io/otel/sdk/export/metric/aggregator"
"go.opentelemetry.io/otel/sdk/internal" "go.opentelemetry.io/otel/sdk/internal"
@ -68,7 +69,7 @@ var _ aggregator.Histogram = &Aggregator{}
// Note that this aggregator maintains each value using independent // Note that this aggregator maintains each value using independent
// atomic operations, which introduces the possibility that // atomic operations, which introduces the possibility that
// checkpoints are inconsistent. // checkpoints are inconsistent.
func New(desc *export.Descriptor, boundaries []core.Number) *Aggregator { func New(desc *metric.Descriptor, boundaries []core.Number) *Aggregator {
// Boundaries MUST be ordered otherwise the histogram could not // Boundaries MUST be ordered otherwise the histogram could not
// be properly computed. // be properly computed.
sortedBoundaries := numbers{ sortedBoundaries := numbers{
@ -126,7 +127,7 @@ func (c *Aggregator) Histogram() (aggregator.Buckets, error) {
// the empty set. Since no locks are taken, there is a chance that // the empty set. Since no locks are taken, there is a chance that
// the independent Sum, Count and Bucket Count are not consistent with each // the independent Sum, Count and Bucket Count are not consistent with each
// other. // other.
func (c *Aggregator) Checkpoint(ctx context.Context, desc *export.Descriptor) { func (c *Aggregator) Checkpoint(ctx context.Context, desc *metric.Descriptor) {
c.lock.SwapActiveState(c.resetCheckpoint) c.lock.SwapActiveState(c.resetCheckpoint)
} }
@ -144,7 +145,7 @@ func (c *Aggregator) resetCheckpoint() {
} }
// Update adds the recorded measurement to the current data set. // Update adds the recorded measurement to the current data set.
func (c *Aggregator) Update(_ context.Context, number core.Number, desc *export.Descriptor) error { func (c *Aggregator) Update(_ context.Context, number core.Number, desc *metric.Descriptor) error {
kind := desc.NumberKind() kind := desc.NumberKind()
cIdx := c.lock.Start() cIdx := c.lock.Start()
@ -168,7 +169,7 @@ func (c *Aggregator) Update(_ context.Context, number core.Number, desc *export.
} }
// Merge combines two histograms that have the same buckets into a single one. // Merge combines two histograms that have the same buckets into a single one.
func (c *Aggregator) Merge(oa export.Aggregator, desc *export.Descriptor) error { func (c *Aggregator) Merge(oa export.Aggregator, desc *metric.Descriptor) error {
o, _ := oa.(*Aggregator) o, _ := oa.(*Aggregator)
if o == nil { if o == nil {
return aggregator.NewInconsistentMergeError(c, oa) return aggregator.NewInconsistentMergeError(c, oa)

View File

@ -26,8 +26,8 @@ import (
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
"go.opentelemetry.io/otel/api/core" "go.opentelemetry.io/otel/api/core"
"go.opentelemetry.io/otel/api/metric"
ottest "go.opentelemetry.io/otel/internal/testing" ottest "go.opentelemetry.io/otel/internal/testing"
export "go.opentelemetry.io/otel/sdk/export/metric"
"go.opentelemetry.io/otel/sdk/metric/aggregator/test" "go.opentelemetry.io/otel/sdk/metric/aggregator/test"
) )
@ -116,7 +116,7 @@ func TestHistogramPositiveAndNegative(t *testing.T) {
// Validates count, sum and buckets for a given profile and policy // Validates count, sum and buckets for a given profile and policy
func histogram(t *testing.T, profile test.Profile, policy policy) { func histogram(t *testing.T, profile test.Profile, policy policy) {
ctx := context.Background() ctx := context.Background()
descriptor := test.NewAggregatorTest(export.MeasureKind, profile.NumberKind) descriptor := test.NewAggregatorTest(metric.MeasureKind, profile.NumberKind)
agg := New(descriptor, boundaries[profile.NumberKind]) agg := New(descriptor, boundaries[profile.NumberKind])
@ -158,7 +158,7 @@ func TestHistogramMerge(t *testing.T) {
ctx := context.Background() ctx := context.Background()
test.RunProfiles(t, func(t *testing.T, profile test.Profile) { test.RunProfiles(t, func(t *testing.T, profile test.Profile) {
descriptor := test.NewAggregatorTest(export.MeasureKind, profile.NumberKind) descriptor := test.NewAggregatorTest(metric.MeasureKind, profile.NumberKind)
agg1 := New(descriptor, boundaries[profile.NumberKind]) agg1 := New(descriptor, boundaries[profile.NumberKind])
agg2 := New(descriptor, boundaries[profile.NumberKind]) agg2 := New(descriptor, boundaries[profile.NumberKind])
@ -210,7 +210,7 @@ func TestHistogramNotSet(t *testing.T) {
ctx := context.Background() ctx := context.Background()
test.RunProfiles(t, func(t *testing.T, profile test.Profile) { test.RunProfiles(t, func(t *testing.T, profile test.Profile) {
descriptor := test.NewAggregatorTest(export.MeasureKind, profile.NumberKind) descriptor := test.NewAggregatorTest(metric.MeasureKind, profile.NumberKind)
agg := New(descriptor, boundaries[profile.NumberKind]) agg := New(descriptor, boundaries[profile.NumberKind])
agg.Checkpoint(ctx, descriptor) agg.Checkpoint(ctx, descriptor)

View File

@ -21,6 +21,7 @@ import (
"unsafe" "unsafe"
"go.opentelemetry.io/otel/api/core" "go.opentelemetry.io/otel/api/core"
"go.opentelemetry.io/otel/api/metric"
export "go.opentelemetry.io/otel/sdk/export/metric" export "go.opentelemetry.io/otel/sdk/export/metric"
"go.opentelemetry.io/otel/sdk/export/metric/aggregator" "go.opentelemetry.io/otel/sdk/export/metric/aggregator"
) )
@ -80,12 +81,12 @@ func (g *Aggregator) LastValue() (core.Number, time.Time, error) {
} }
// Checkpoint atomically saves the current value. // Checkpoint atomically saves the current value.
func (g *Aggregator) Checkpoint(ctx context.Context, _ *export.Descriptor) { func (g *Aggregator) Checkpoint(ctx context.Context, _ *metric.Descriptor) {
g.checkpoint = atomic.LoadPointer(&g.current) g.checkpoint = atomic.LoadPointer(&g.current)
} }
// Update atomically sets the current "last" value. // Update atomically sets the current "last" value.
func (g *Aggregator) Update(_ context.Context, number core.Number, desc *export.Descriptor) error { func (g *Aggregator) Update(_ context.Context, number core.Number, desc *metric.Descriptor) error {
ngd := &lastValueData{ ngd := &lastValueData{
value: number, value: number,
timestamp: time.Now(), timestamp: time.Now(),
@ -96,7 +97,7 @@ func (g *Aggregator) Update(_ context.Context, number core.Number, desc *export.
// Merge combines state from two aggregators. The most-recently set // Merge combines state from two aggregators. The most-recently set
// value is chosen. // value is chosen.
func (g *Aggregator) Merge(oa export.Aggregator, desc *export.Descriptor) error { func (g *Aggregator) Merge(oa export.Aggregator, desc *metric.Descriptor) error {
o, _ := oa.(*Aggregator) o, _ := oa.(*Aggregator)
if o == nil { if o == nil {
return aggregator.NewInconsistentMergeError(g, oa) return aggregator.NewInconsistentMergeError(g, oa)

View File

@ -24,6 +24,7 @@ import (
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
"go.opentelemetry.io/otel/api/core" "go.opentelemetry.io/otel/api/core"
"go.opentelemetry.io/otel/api/metric"
ottest "go.opentelemetry.io/otel/internal/testing" ottest "go.opentelemetry.io/otel/internal/testing"
export "go.opentelemetry.io/otel/sdk/export/metric" export "go.opentelemetry.io/otel/sdk/export/metric"
"go.opentelemetry.io/otel/sdk/export/metric/aggregator" "go.opentelemetry.io/otel/sdk/export/metric/aggregator"
@ -55,7 +56,7 @@ func TestLastValueUpdate(t *testing.T) {
test.RunProfiles(t, func(t *testing.T, profile test.Profile) { test.RunProfiles(t, func(t *testing.T, profile test.Profile) {
agg := New() agg := New()
record := test.NewAggregatorTest(export.ObserverKind, profile.NumberKind) record := test.NewAggregatorTest(metric.ObserverKind, profile.NumberKind)
var last core.Number var last core.Number
for i := 0; i < count; i++ { for i := 0; i < count; i++ {
@ -79,7 +80,7 @@ func TestLastValueMerge(t *testing.T) {
agg1 := New() agg1 := New()
agg2 := New() agg2 := New()
descriptor := test.NewAggregatorTest(export.ObserverKind, profile.NumberKind) descriptor := test.NewAggregatorTest(metric.ObserverKind, profile.NumberKind)
first1 := profile.Random(+1) first1 := profile.Random(+1)
first2 := profile.Random(+1) first2 := profile.Random(+1)
@ -107,7 +108,7 @@ func TestLastValueMerge(t *testing.T) {
} }
func TestLastValueNotSet(t *testing.T) { func TestLastValueNotSet(t *testing.T) {
descriptor := test.NewAggregatorTest(export.ObserverKind, core.Int64NumberKind) descriptor := test.NewAggregatorTest(metric.ObserverKind, core.Int64NumberKind)
g := New() g := New()
g.Checkpoint(context.Background(), descriptor) g.Checkpoint(context.Background(), descriptor)

View File

@ -18,6 +18,7 @@ import (
"context" "context"
"go.opentelemetry.io/otel/api/core" "go.opentelemetry.io/otel/api/core"
"go.opentelemetry.io/otel/api/metric"
export "go.opentelemetry.io/otel/sdk/export/metric" export "go.opentelemetry.io/otel/sdk/export/metric"
"go.opentelemetry.io/otel/sdk/export/metric/aggregator" "go.opentelemetry.io/otel/sdk/export/metric/aggregator"
"go.opentelemetry.io/otel/sdk/internal" "go.opentelemetry.io/otel/sdk/internal"
@ -50,7 +51,7 @@ var _ aggregator.MinMaxSumCount = &Aggregator{}
// //
// This aggregator uses the StateLocker pattern to guarantee // This aggregator uses the StateLocker pattern to guarantee
// the count, sum, min and max are consistent within a checkpoint // the count, sum, min and max are consistent within a checkpoint
func New(desc *export.Descriptor) *Aggregator { func New(desc *metric.Descriptor) *Aggregator {
kind := desc.NumberKind() kind := desc.NumberKind()
return &Aggregator{ return &Aggregator{
kind: kind, kind: kind,
@ -111,7 +112,7 @@ func (c *Aggregator) Max() (core.Number, error) {
// Checkpoint saves the current state and resets the current state to // Checkpoint saves the current state and resets the current state to
// the empty set. // the empty set.
func (c *Aggregator) Checkpoint(ctx context.Context, desc *export.Descriptor) { func (c *Aggregator) Checkpoint(ctx context.Context, desc *metric.Descriptor) {
c.lock.SwapActiveState(c.resetCheckpoint) c.lock.SwapActiveState(c.resetCheckpoint)
} }
@ -131,7 +132,7 @@ func (c *Aggregator) resetCheckpoint() {
} }
// Update adds the recorded measurement to the current data set. // Update adds the recorded measurement to the current data set.
func (c *Aggregator) Update(_ context.Context, number core.Number, desc *export.Descriptor) error { func (c *Aggregator) Update(_ context.Context, number core.Number, desc *metric.Descriptor) error {
kind := desc.NumberKind() kind := desc.NumberKind()
cIdx := c.lock.Start() cIdx := c.lock.Start()
@ -165,7 +166,7 @@ func (c *Aggregator) Update(_ context.Context, number core.Number, desc *export.
} }
// Merge combines two data sets into one. // Merge combines two data sets into one.
func (c *Aggregator) Merge(oa export.Aggregator, desc *export.Descriptor) error { func (c *Aggregator) Merge(oa export.Aggregator, desc *metric.Descriptor) error {
o, _ := oa.(*Aggregator) o, _ := oa.(*Aggregator)
if o == nil { if o == nil {
return aggregator.NewInconsistentMergeError(c, oa) return aggregator.NewInconsistentMergeError(c, oa)

View File

@ -25,8 +25,8 @@ import (
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
"go.opentelemetry.io/otel/api/core" "go.opentelemetry.io/otel/api/core"
"go.opentelemetry.io/otel/api/metric"
ottest "go.opentelemetry.io/otel/internal/testing" ottest "go.opentelemetry.io/otel/internal/testing"
export "go.opentelemetry.io/otel/sdk/export/metric"
"go.opentelemetry.io/otel/sdk/export/metric/aggregator" "go.opentelemetry.io/otel/sdk/export/metric/aggregator"
"go.opentelemetry.io/otel/sdk/metric/aggregator/test" "go.opentelemetry.io/otel/sdk/metric/aggregator/test"
) )
@ -114,7 +114,7 @@ func TestMinMaxSumCountPositiveAndNegative(t *testing.T) {
// Validates min, max, sum and count for a given profile and policy // Validates min, max, sum and count for a given profile and policy
func minMaxSumCount(t *testing.T, profile test.Profile, policy policy) { func minMaxSumCount(t *testing.T, profile test.Profile, policy policy) {
ctx := context.Background() ctx := context.Background()
descriptor := test.NewAggregatorTest(export.MeasureKind, profile.NumberKind) descriptor := test.NewAggregatorTest(metric.MeasureKind, profile.NumberKind)
agg := New(descriptor) agg := New(descriptor)
@ -162,7 +162,7 @@ func TestMinMaxSumCountMerge(t *testing.T) {
ctx := context.Background() ctx := context.Background()
test.RunProfiles(t, func(t *testing.T, profile test.Profile) { test.RunProfiles(t, func(t *testing.T, profile test.Profile) {
descriptor := test.NewAggregatorTest(export.MeasureKind, profile.NumberKind) descriptor := test.NewAggregatorTest(metric.MeasureKind, profile.NumberKind)
agg1 := New(descriptor) agg1 := New(descriptor)
agg2 := New(descriptor) agg2 := New(descriptor)
@ -220,7 +220,7 @@ func TestMaxSumCountNotSet(t *testing.T) {
ctx := context.Background() ctx := context.Background()
test.RunProfiles(t, func(t *testing.T, profile test.Profile) { test.RunProfiles(t, func(t *testing.T, profile test.Profile) {
descriptor := test.NewAggregatorTest(export.MeasureKind, profile.NumberKind) descriptor := test.NewAggregatorTest(metric.MeasureKind, profile.NumberKind)
agg := New(descriptor) agg := New(descriptor)
agg.Checkpoint(ctx, descriptor) agg.Checkpoint(ctx, descriptor)

View File

@ -18,6 +18,7 @@ import (
"context" "context"
"go.opentelemetry.io/otel/api/core" "go.opentelemetry.io/otel/api/core"
"go.opentelemetry.io/otel/api/metric"
export "go.opentelemetry.io/otel/sdk/export/metric" export "go.opentelemetry.io/otel/sdk/export/metric"
"go.opentelemetry.io/otel/sdk/export/metric/aggregator" "go.opentelemetry.io/otel/sdk/export/metric/aggregator"
) )
@ -51,18 +52,18 @@ func (c *Aggregator) Sum() (core.Number, error) {
// Checkpoint atomically saves the current value and resets the // Checkpoint atomically saves the current value and resets the
// current sum to zero. // current sum to zero.
func (c *Aggregator) Checkpoint(ctx context.Context, _ *export.Descriptor) { func (c *Aggregator) Checkpoint(ctx context.Context, _ *metric.Descriptor) {
c.checkpoint = c.current.SwapNumberAtomic(core.Number(0)) c.checkpoint = c.current.SwapNumberAtomic(core.Number(0))
} }
// Update atomically adds to the current value. // Update atomically adds to the current value.
func (c *Aggregator) Update(_ context.Context, number core.Number, desc *export.Descriptor) error { func (c *Aggregator) Update(_ context.Context, number core.Number, desc *metric.Descriptor) error {
c.current.AddNumberAtomic(desc.NumberKind(), number) c.current.AddNumberAtomic(desc.NumberKind(), number)
return nil return nil
} }
// Merge combines two counters by adding their sums. // Merge combines two counters by adding their sums.
func (c *Aggregator) Merge(oa export.Aggregator, desc *export.Descriptor) error { func (c *Aggregator) Merge(oa export.Aggregator, desc *metric.Descriptor) error {
o, _ := oa.(*Aggregator) o, _ := oa.(*Aggregator)
if o == nil { if o == nil {
return aggregator.NewInconsistentMergeError(c, oa) return aggregator.NewInconsistentMergeError(c, oa)

View File

@ -23,8 +23,8 @@ import (
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
"go.opentelemetry.io/otel/api/core" "go.opentelemetry.io/otel/api/core"
"go.opentelemetry.io/otel/api/metric"
ottest "go.opentelemetry.io/otel/internal/testing" ottest "go.opentelemetry.io/otel/internal/testing"
export "go.opentelemetry.io/otel/sdk/export/metric"
"go.opentelemetry.io/otel/sdk/metric/aggregator/test" "go.opentelemetry.io/otel/sdk/metric/aggregator/test"
) )
@ -55,7 +55,7 @@ func TestCounterSum(t *testing.T) {
test.RunProfiles(t, func(t *testing.T, profile test.Profile) { test.RunProfiles(t, func(t *testing.T, profile test.Profile) {
agg := New() agg := New()
descriptor := test.NewAggregatorTest(export.CounterKind, profile.NumberKind) descriptor := test.NewAggregatorTest(metric.CounterKind, profile.NumberKind)
sum := core.Number(0) sum := core.Number(0)
for i := 0; i < count; i++ { for i := 0; i < count; i++ {
@ -78,7 +78,7 @@ func TestMeasureSum(t *testing.T) {
test.RunProfiles(t, func(t *testing.T, profile test.Profile) { test.RunProfiles(t, func(t *testing.T, profile test.Profile) {
agg := New() agg := New()
descriptor := test.NewAggregatorTest(export.MeasureKind, profile.NumberKind) descriptor := test.NewAggregatorTest(metric.MeasureKind, profile.NumberKind)
sum := core.Number(0) sum := core.Number(0)
@ -106,7 +106,7 @@ func TestCounterMerge(t *testing.T) {
agg1 := New() agg1 := New()
agg2 := New() agg2 := New()
descriptor := test.NewAggregatorTest(export.CounterKind, profile.NumberKind) descriptor := test.NewAggregatorTest(metric.CounterKind, profile.NumberKind)
sum := core.Number(0) sum := core.Number(0)
for i := 0; i < count; i++ { for i := 0; i < count; i++ {

View File

@ -23,6 +23,7 @@ import (
"unsafe" "unsafe"
"go.opentelemetry.io/otel/api/core" "go.opentelemetry.io/otel/api/core"
"go.opentelemetry.io/otel/api/metric"
ottest "go.opentelemetry.io/otel/internal/testing" ottest "go.opentelemetry.io/otel/internal/testing"
export "go.opentelemetry.io/otel/sdk/export/metric" export "go.opentelemetry.io/otel/sdk/export/metric"
"go.opentelemetry.io/otel/sdk/export/metric/aggregator" "go.opentelemetry.io/otel/sdk/export/metric/aggregator"
@ -53,8 +54,9 @@ func newProfiles() []Profile {
} }
} }
func NewAggregatorTest(mkind export.Kind, nkind core.NumberKind) *export.Descriptor { func NewAggregatorTest(mkind metric.Kind, nkind core.NumberKind) *metric.Descriptor {
return export.NewDescriptor("test.name", mkind, nil, "", "", nkind) desc := metric.NewDescriptor("test.name", mkind, nkind)
return &desc
} }
func RunProfiles(t *testing.T, f func(*testing.T, Profile)) { func RunProfiles(t *testing.T, f func(*testing.T, Profile)) {
@ -147,7 +149,7 @@ func (n *Numbers) Points() []core.Number {
} }
// Performs the same range test the SDK does on behalf of the aggregator. // Performs the same range test the SDK does on behalf of the aggregator.
func CheckedUpdate(t *testing.T, agg export.Aggregator, number core.Number, descriptor *export.Descriptor) { func CheckedUpdate(t *testing.T, agg export.Aggregator, number core.Number, descriptor *metric.Descriptor) {
ctx := context.Background() ctx := context.Background()
// Note: Aggregator tests are written assuming that the SDK // Note: Aggregator tests are written assuming that the SDK
@ -163,7 +165,7 @@ func CheckedUpdate(t *testing.T, agg export.Aggregator, number core.Number, desc
} }
} }
func CheckedMerge(t *testing.T, aggInto, aggFrom export.Aggregator, descriptor *export.Descriptor) { func CheckedMerge(t *testing.T, aggInto, aggFrom export.Aggregator, descriptor *metric.Descriptor) {
if err := aggInto.Merge(aggFrom, descriptor); err != nil { if err := aggInto.Merge(aggFrom, descriptor); err != nil {
t.Error("Unexpected Merge failure", err) t.Error("Unexpected Merge failure", err)
} }

View File

@ -19,6 +19,7 @@ import (
"errors" "errors"
"go.opentelemetry.io/otel/api/core" "go.opentelemetry.io/otel/api/core"
"go.opentelemetry.io/otel/api/metric"
export "go.opentelemetry.io/otel/sdk/export/metric" export "go.opentelemetry.io/otel/sdk/export/metric"
"go.opentelemetry.io/otel/sdk/export/metric/aggregator" "go.opentelemetry.io/otel/sdk/export/metric/aggregator"
) )
@ -34,11 +35,11 @@ type (
// descKeyIndexMap is a mapping, for each Descriptor, from the // descKeyIndexMap is a mapping, for each Descriptor, from the
// Key to the position in the descriptor's recommended keys. // Key to the position in the descriptor's recommended keys.
descKeyIndexMap map[*export.Descriptor]map[core.Key]int descKeyIndexMap map[*metric.Descriptor]map[core.Key]int
// batchKey describes a unique metric descriptor and encoded label set. // batchKey describes a unique metric descriptor and encoded label set.
batchKey struct { batchKey struct {
descriptor *export.Descriptor descriptor *metric.Descriptor
encoded string encoded string
} }
@ -66,7 +67,7 @@ func New(selector export.AggregationSelector, labelEncoder export.LabelEncoder,
} }
} }
func (b *Batcher) AggregatorFor(descriptor *export.Descriptor) export.Aggregator { func (b *Batcher) AggregatorFor(descriptor *metric.Descriptor) export.Aggregator {
return b.selector.AggregatorFor(descriptor) return b.selector.AggregatorFor(descriptor)
} }

View File

@ -30,21 +30,21 @@ func TestGroupingStateless(t *testing.T) {
ctx := context.Background() ctx := context.Background()
b := defaultkeys.New(test.NewAggregationSelector(), test.GroupEncoder, false) b := defaultkeys.New(test.NewAggregationSelector(), test.GroupEncoder, false)
_ = b.Process(ctx, test.NewLastValueRecord(test.LastValueADesc, test.Labels1, 10)) _ = b.Process(ctx, test.NewLastValueRecord(&test.LastValueADesc, test.Labels1, 10))
_ = b.Process(ctx, test.NewLastValueRecord(test.LastValueADesc, test.Labels2, 20)) _ = b.Process(ctx, test.NewLastValueRecord(&test.LastValueADesc, test.Labels2, 20))
_ = b.Process(ctx, test.NewLastValueRecord(test.LastValueADesc, test.Labels3, 30)) _ = b.Process(ctx, test.NewLastValueRecord(&test.LastValueADesc, test.Labels3, 30))
_ = b.Process(ctx, test.NewLastValueRecord(test.LastValueBDesc, test.Labels1, 10)) _ = b.Process(ctx, test.NewLastValueRecord(&test.LastValueBDesc, test.Labels1, 10))
_ = b.Process(ctx, test.NewLastValueRecord(test.LastValueBDesc, test.Labels2, 20)) _ = b.Process(ctx, test.NewLastValueRecord(&test.LastValueBDesc, test.Labels2, 20))
_ = b.Process(ctx, test.NewLastValueRecord(test.LastValueBDesc, test.Labels3, 30)) _ = b.Process(ctx, test.NewLastValueRecord(&test.LastValueBDesc, test.Labels3, 30))
_ = b.Process(ctx, test.NewCounterRecord(test.CounterADesc, test.Labels1, 10)) _ = b.Process(ctx, test.NewCounterRecord(&test.CounterADesc, test.Labels1, 10))
_ = b.Process(ctx, test.NewCounterRecord(test.CounterADesc, test.Labels2, 20)) _ = b.Process(ctx, test.NewCounterRecord(&test.CounterADesc, test.Labels2, 20))
_ = b.Process(ctx, test.NewCounterRecord(test.CounterADesc, test.Labels3, 40)) _ = b.Process(ctx, test.NewCounterRecord(&test.CounterADesc, test.Labels3, 40))
_ = b.Process(ctx, test.NewCounterRecord(test.CounterBDesc, test.Labels1, 10)) _ = b.Process(ctx, test.NewCounterRecord(&test.CounterBDesc, test.Labels1, 10))
_ = b.Process(ctx, test.NewCounterRecord(test.CounterBDesc, test.Labels2, 20)) _ = b.Process(ctx, test.NewCounterRecord(&test.CounterBDesc, test.Labels2, 20))
_ = b.Process(ctx, test.NewCounterRecord(test.CounterBDesc, test.Labels3, 40)) _ = b.Process(ctx, test.NewCounterRecord(&test.CounterBDesc, test.Labels3, 40))
checkpointSet := b.CheckpointSet() checkpointSet := b.CheckpointSet()
b.FinishedCollection() b.FinishedCollection()
@ -56,7 +56,7 @@ func TestGroupingStateless(t *testing.T) {
// Repeat for {counter,lastvalue}.{1,2}. // Repeat for {counter,lastvalue}.{1,2}.
// Output lastvalue should have only the "G=H" and "G=" keys. // Output lastvalue should have only the "G=H" and "G=" keys.
// Output counter should have only the "C=D" and "C=" keys. // Output counter should have only the "C=D" and "C=" keys.
require.EqualValues(t, map[string]int64{ require.EqualValues(t, map[string]float64{
"sum.a/C=D": 30, // labels1 + labels2 "sum.a/C=D": 30, // labels1 + labels2
"sum.a/C=": 40, // labels3 "sum.a/C=": 40, // labels3
"sum.b/C=D": 30, // labels1 + labels2 "sum.b/C=D": 30, // labels1 + labels2
@ -80,11 +80,11 @@ func TestGroupingStateful(t *testing.T) {
ctx := context.Background() ctx := context.Background()
b := defaultkeys.New(test.NewAggregationSelector(), test.GroupEncoder, true) b := defaultkeys.New(test.NewAggregationSelector(), test.GroupEncoder, true)
counterA := test.NewCounterRecord(test.CounterADesc, test.Labels1, 10) counterA := test.NewCounterRecord(&test.CounterADesc, test.Labels1, 10)
caggA := counterA.Aggregator() caggA := counterA.Aggregator()
_ = b.Process(ctx, counterA) _ = b.Process(ctx, counterA)
counterB := test.NewCounterRecord(test.CounterBDesc, test.Labels1, 10) counterB := test.NewCounterRecord(&test.CounterBDesc, test.Labels1, 10)
caggB := counterB.Aggregator() caggB := counterB.Aggregator()
_ = b.Process(ctx, counterB) _ = b.Process(ctx, counterB)
@ -95,7 +95,7 @@ func TestGroupingStateful(t *testing.T) {
err := checkpointSet.ForEach(records1.AddTo) err := checkpointSet.ForEach(records1.AddTo)
require.NoError(t, err) require.NoError(t, err)
require.EqualValues(t, map[string]int64{ require.EqualValues(t, map[string]float64{
"sum.a/C=D": 10, // labels1 "sum.a/C=D": 10, // labels1
"sum.b/C=D": 10, // labels1 "sum.b/C=D": 10, // labels1
}, records1) }, records1)
@ -111,10 +111,10 @@ func TestGroupingStateful(t *testing.T) {
require.EqualValues(t, records1, records2) require.EqualValues(t, records1, records2)
// Update and re-checkpoint the original record. // Update and re-checkpoint the original record.
_ = caggA.Update(ctx, core.NewInt64Number(20), test.CounterADesc) _ = caggA.Update(ctx, core.NewInt64Number(20), &test.CounterADesc)
_ = caggB.Update(ctx, core.NewInt64Number(20), test.CounterBDesc) _ = caggB.Update(ctx, core.NewInt64Number(20), &test.CounterBDesc)
caggA.Checkpoint(ctx, test.CounterADesc) caggA.Checkpoint(ctx, &test.CounterADesc)
caggB.Checkpoint(ctx, test.CounterBDesc) caggB.Checkpoint(ctx, &test.CounterBDesc)
// As yet cagg has not been passed to Batcher.Process. Should // As yet cagg has not been passed to Batcher.Process. Should
// not see an update. // not see an update.
@ -128,8 +128,8 @@ func TestGroupingStateful(t *testing.T) {
require.EqualValues(t, records1, records3) require.EqualValues(t, records1, records3)
// Now process the second update // Now process the second update
_ = b.Process(ctx, export.NewRecord(test.CounterADesc, test.Labels1, caggA)) _ = b.Process(ctx, export.NewRecord(&test.CounterADesc, test.Labels1, caggA))
_ = b.Process(ctx, export.NewRecord(test.CounterBDesc, test.Labels1, caggB)) _ = b.Process(ctx, export.NewRecord(&test.CounterBDesc, test.Labels1, caggB))
checkpointSet = b.CheckpointSet() checkpointSet = b.CheckpointSet()
b.FinishedCollection() b.FinishedCollection()
@ -138,7 +138,7 @@ func TestGroupingStateful(t *testing.T) {
err = checkpointSet.ForEach(records4.AddTo) err = checkpointSet.ForEach(records4.AddTo)
require.NoError(t, err) require.NoError(t, err)
require.EqualValues(t, map[string]int64{ require.EqualValues(t, map[string]float64{
"sum.a/C=D": 30, "sum.a/C=D": 30,
"sum.b/C=D": 30, "sum.b/C=D": 30,
}, records4) }, records4)

View File

@ -21,7 +21,9 @@ import (
"go.opentelemetry.io/otel/api/core" "go.opentelemetry.io/otel/api/core"
"go.opentelemetry.io/otel/api/key" "go.opentelemetry.io/otel/api/key"
"go.opentelemetry.io/otel/api/metric"
export "go.opentelemetry.io/otel/sdk/export/metric" export "go.opentelemetry.io/otel/sdk/export/metric"
"go.opentelemetry.io/otel/sdk/export/metric/aggregator"
sdk "go.opentelemetry.io/otel/sdk/metric" sdk "go.opentelemetry.io/otel/sdk/metric"
"go.opentelemetry.io/otel/sdk/metric/aggregator/lastvalue" "go.opentelemetry.io/otel/sdk/metric/aggregator/lastvalue"
"go.opentelemetry.io/otel/sdk/metric/aggregator/sum" "go.opentelemetry.io/otel/sdk/metric/aggregator/sum"
@ -32,7 +34,7 @@ type (
Encoder struct{} Encoder struct{}
// Output collects distinct metric/label set outputs. // Output collects distinct metric/label set outputs.
Output map[string]int64 Output map[string]float64
// testAggregationSelector returns aggregators consistent with // testAggregationSelector returns aggregators consistent with
// the test variables below, needed for testing stateful // the test variables below, needed for testing stateful
@ -42,15 +44,15 @@ type (
var ( var (
// LastValueADesc and LastValueBDesc group by "G" // LastValueADesc and LastValueBDesc group by "G"
LastValueADesc = export.NewDescriptor( LastValueADesc = metric.NewDescriptor(
"lastvalue.a", export.ObserverKind, []core.Key{key.New("G")}, "", "", core.Int64NumberKind) "lastvalue.a", metric.ObserverKind, core.Int64NumberKind, metric.WithKeys(key.New("G")))
LastValueBDesc = export.NewDescriptor( LastValueBDesc = metric.NewDescriptor(
"lastvalue.b", export.ObserverKind, []core.Key{key.New("G")}, "", "", core.Int64NumberKind) "lastvalue.b", metric.ObserverKind, core.Int64NumberKind, metric.WithKeys(key.New("G")))
// CounterADesc and CounterBDesc group by "C" // CounterADesc and CounterBDesc group by "C"
CounterADesc = export.NewDescriptor( CounterADesc = metric.NewDescriptor(
"sum.a", export.CounterKind, []core.Key{key.New("C")}, "", "", core.Int64NumberKind) "sum.a", metric.CounterKind, core.Int64NumberKind, metric.WithKeys(key.New("C")))
CounterBDesc = export.NewDescriptor( CounterBDesc = metric.NewDescriptor(
"sum.b", export.CounterKind, []core.Key{key.New("C")}, "", "", core.Int64NumberKind) "sum.b", metric.CounterKind, core.Int64NumberKind, metric.WithKeys(key.New("C")))
// SdkEncoder uses a non-standard encoder like K1~V1&K2~V2 // SdkEncoder uses a non-standard encoder like K1~V1&K2~V2
SdkEncoder = &Encoder{} SdkEncoder = &Encoder{}
@ -75,11 +77,11 @@ func NewAggregationSelector() export.AggregationSelector {
return &testAggregationSelector{} return &testAggregationSelector{}
} }
func (*testAggregationSelector) AggregatorFor(desc *export.Descriptor) export.Aggregator { func (*testAggregationSelector) AggregatorFor(desc *metric.Descriptor) export.Aggregator {
switch desc.MetricKind() { switch desc.MetricKind() {
case export.CounterKind: case metric.CounterKind:
return sum.New() return sum.New()
case export.ObserverKind: case metric.ObserverKind:
return lastvalue.New() return lastvalue.New()
default: default:
panic("Invalid descriptor MetricKind for this test") panic("Invalid descriptor MetricKind for this test")
@ -105,7 +107,7 @@ func (Encoder) Encode(labels []core.KeyValue) string {
} }
// LastValueAgg returns a checkpointed lastValue aggregator w/ the specified descriptor and value. // LastValueAgg returns a checkpointed lastValue aggregator w/ the specified descriptor and value.
func LastValueAgg(desc *export.Descriptor, v int64) export.Aggregator { func LastValueAgg(desc *metric.Descriptor, v int64) export.Aggregator {
ctx := context.Background() ctx := context.Background()
gagg := lastvalue.New() gagg := lastvalue.New()
_ = gagg.Update(ctx, core.NewInt64Number(v), desc) _ = gagg.Update(ctx, core.NewInt64Number(v), desc)
@ -114,17 +116,17 @@ func LastValueAgg(desc *export.Descriptor, v int64) export.Aggregator {
} }
// Convenience method for building a test exported lastValue record. // Convenience method for building a test exported lastValue record.
func NewLastValueRecord(desc *export.Descriptor, labels export.Labels, value int64) export.Record { func NewLastValueRecord(desc *metric.Descriptor, labels export.Labels, value int64) export.Record {
return export.NewRecord(desc, labels, LastValueAgg(desc, value)) return export.NewRecord(desc, labels, LastValueAgg(desc, value))
} }
// Convenience method for building a test exported counter record. // Convenience method for building a test exported counter record.
func NewCounterRecord(desc *export.Descriptor, labels export.Labels, value int64) export.Record { func NewCounterRecord(desc *metric.Descriptor, labels export.Labels, value int64) export.Record {
return export.NewRecord(desc, labels, CounterAgg(desc, value)) return export.NewRecord(desc, labels, CounterAgg(desc, value))
} }
// CounterAgg returns a checkpointed counter aggregator w/ the specified descriptor and value. // CounterAgg returns a checkpointed counter aggregator w/ the specified descriptor and value.
func CounterAgg(desc *export.Descriptor, v int64) export.Aggregator { func CounterAgg(desc *metric.Descriptor, v int64) export.Aggregator {
ctx := context.Background() ctx := context.Background()
cagg := sum.New() cagg := sum.New()
_ = cagg.Update(ctx, core.NewInt64Number(v), desc) _ = cagg.Update(ctx, core.NewInt64Number(v), desc)
@ -137,14 +139,16 @@ func CounterAgg(desc *export.Descriptor, v int64) export.Aggregator {
func (o Output) AddTo(rec export.Record) error { func (o Output) AddTo(rec export.Record) error {
labels := rec.Labels() labels := rec.Labels()
key := fmt.Sprint(rec.Descriptor().Name(), "/", labels.Encoded()) key := fmt.Sprint(rec.Descriptor().Name(), "/", labels.Encoded())
var value int64 var value float64
switch t := rec.Aggregator().(type) {
case *sum.Aggregator: if s, ok := rec.Aggregator().(aggregator.Sum); ok {
sum, _ := t.Sum() sum, _ := s.Sum()
value = sum.AsInt64() value = sum.CoerceToFloat64(rec.Descriptor().NumberKind())
case *lastvalue.Aggregator: } else if l, ok := rec.Aggregator().(aggregator.LastValue); ok {
lv, _, _ := t.LastValue() last, _, _ := l.LastValue()
value = lv.AsInt64() value = last.CoerceToFloat64(rec.Descriptor().NumberKind())
} else {
panic(fmt.Sprintf("Unhandled aggregator type: %T", rec.Aggregator()))
} }
o[key] = value o[key] = value
return nil return nil

View File

@ -18,6 +18,7 @@ import (
"context" "context"
"errors" "errors"
"go.opentelemetry.io/otel/api/metric"
export "go.opentelemetry.io/otel/sdk/export/metric" export "go.opentelemetry.io/otel/sdk/export/metric"
"go.opentelemetry.io/otel/sdk/export/metric/aggregator" "go.opentelemetry.io/otel/sdk/export/metric/aggregator"
) )
@ -30,7 +31,7 @@ type (
} }
batchKey struct { batchKey struct {
descriptor *export.Descriptor descriptor *metric.Descriptor
encoded string encoded string
} }
@ -53,7 +54,7 @@ func New(selector export.AggregationSelector, stateful bool) *Batcher {
} }
} }
func (b *Batcher) AggregatorFor(descriptor *export.Descriptor) export.Aggregator { func (b *Batcher) AggregatorFor(descriptor *metric.Descriptor) export.Aggregator {
return b.selector.AggregatorFor(descriptor) return b.selector.AggregatorFor(descriptor)
} }

View File

@ -33,30 +33,30 @@ func TestUngroupedStateless(t *testing.T) {
b := ungrouped.New(test.NewAggregationSelector(), false) b := ungrouped.New(test.NewAggregationSelector(), false)
// Set initial lastValue values // Set initial lastValue values
_ = b.Process(ctx, test.NewLastValueRecord(test.LastValueADesc, test.Labels1, 10)) _ = b.Process(ctx, test.NewLastValueRecord(&test.LastValueADesc, test.Labels1, 10))
_ = b.Process(ctx, test.NewLastValueRecord(test.LastValueADesc, test.Labels2, 20)) _ = b.Process(ctx, test.NewLastValueRecord(&test.LastValueADesc, test.Labels2, 20))
_ = b.Process(ctx, test.NewLastValueRecord(test.LastValueADesc, test.Labels3, 30)) _ = b.Process(ctx, test.NewLastValueRecord(&test.LastValueADesc, test.Labels3, 30))
_ = b.Process(ctx, test.NewLastValueRecord(test.LastValueBDesc, test.Labels1, 10)) _ = b.Process(ctx, test.NewLastValueRecord(&test.LastValueBDesc, test.Labels1, 10))
_ = b.Process(ctx, test.NewLastValueRecord(test.LastValueBDesc, test.Labels2, 20)) _ = b.Process(ctx, test.NewLastValueRecord(&test.LastValueBDesc, test.Labels2, 20))
_ = b.Process(ctx, test.NewLastValueRecord(test.LastValueBDesc, test.Labels3, 30)) _ = b.Process(ctx, test.NewLastValueRecord(&test.LastValueBDesc, test.Labels3, 30))
// Another lastValue Set for Labels1 // Another lastValue Set for Labels1
_ = b.Process(ctx, test.NewLastValueRecord(test.LastValueADesc, test.Labels1, 50)) _ = b.Process(ctx, test.NewLastValueRecord(&test.LastValueADesc, test.Labels1, 50))
_ = b.Process(ctx, test.NewLastValueRecord(test.LastValueBDesc, test.Labels1, 50)) _ = b.Process(ctx, test.NewLastValueRecord(&test.LastValueBDesc, test.Labels1, 50))
// Set initial counter values // Set initial counter values
_ = b.Process(ctx, test.NewCounterRecord(test.CounterADesc, test.Labels1, 10)) _ = b.Process(ctx, test.NewCounterRecord(&test.CounterADesc, test.Labels1, 10))
_ = b.Process(ctx, test.NewCounterRecord(test.CounterADesc, test.Labels2, 20)) _ = b.Process(ctx, test.NewCounterRecord(&test.CounterADesc, test.Labels2, 20))
_ = b.Process(ctx, test.NewCounterRecord(test.CounterADesc, test.Labels3, 40)) _ = b.Process(ctx, test.NewCounterRecord(&test.CounterADesc, test.Labels3, 40))
_ = b.Process(ctx, test.NewCounterRecord(test.CounterBDesc, test.Labels1, 10)) _ = b.Process(ctx, test.NewCounterRecord(&test.CounterBDesc, test.Labels1, 10))
_ = b.Process(ctx, test.NewCounterRecord(test.CounterBDesc, test.Labels2, 20)) _ = b.Process(ctx, test.NewCounterRecord(&test.CounterBDesc, test.Labels2, 20))
_ = b.Process(ctx, test.NewCounterRecord(test.CounterBDesc, test.Labels3, 40)) _ = b.Process(ctx, test.NewCounterRecord(&test.CounterBDesc, test.Labels3, 40))
// Another counter Add for Labels1 // Another counter Add for Labels1
_ = b.Process(ctx, test.NewCounterRecord(test.CounterADesc, test.Labels1, 50)) _ = b.Process(ctx, test.NewCounterRecord(&test.CounterADesc, test.Labels1, 50))
_ = b.Process(ctx, test.NewCounterRecord(test.CounterBDesc, test.Labels1, 50)) _ = b.Process(ctx, test.NewCounterRecord(&test.CounterBDesc, test.Labels1, 50))
checkpointSet := b.CheckpointSet() checkpointSet := b.CheckpointSet()
b.FinishedCollection() b.FinishedCollection()
@ -66,7 +66,7 @@ func TestUngroupedStateless(t *testing.T) {
// Output lastvalue should have only the "G=H" and "G=" keys. // Output lastvalue should have only the "G=H" and "G=" keys.
// Output counter should have only the "C=D" and "C=" keys. // Output counter should have only the "C=D" and "C=" keys.
require.EqualValues(t, map[string]int64{ require.EqualValues(t, map[string]float64{
"sum.a/G~H&C~D": 60, // labels1 "sum.a/G~H&C~D": 60, // labels1
"sum.a/C~D&E~F": 20, // labels2 "sum.a/C~D&E~F": 20, // labels2
"sum.a/": 40, // labels3 "sum.a/": 40, // labels3
@ -94,11 +94,11 @@ func TestUngroupedStateful(t *testing.T) {
ctx := context.Background() ctx := context.Background()
b := ungrouped.New(test.NewAggregationSelector(), true) b := ungrouped.New(test.NewAggregationSelector(), true)
counterA := test.NewCounterRecord(test.CounterADesc, test.Labels1, 10) counterA := test.NewCounterRecord(&test.CounterADesc, test.Labels1, 10)
caggA := counterA.Aggregator() caggA := counterA.Aggregator()
_ = b.Process(ctx, counterA) _ = b.Process(ctx, counterA)
counterB := test.NewCounterRecord(test.CounterBDesc, test.Labels1, 10) counterB := test.NewCounterRecord(&test.CounterBDesc, test.Labels1, 10)
caggB := counterB.Aggregator() caggB := counterB.Aggregator()
_ = b.Process(ctx, counterB) _ = b.Process(ctx, counterB)
@ -108,7 +108,7 @@ func TestUngroupedStateful(t *testing.T) {
records1 := test.Output{} records1 := test.Output{}
_ = checkpointSet.ForEach(records1.AddTo) _ = checkpointSet.ForEach(records1.AddTo)
require.EqualValues(t, map[string]int64{ require.EqualValues(t, map[string]float64{
"sum.a/G~H&C~D": 10, // labels1 "sum.a/G~H&C~D": 10, // labels1
"sum.b/G~H&C~D": 10, // labels1 "sum.b/G~H&C~D": 10, // labels1
}, records1) }, records1)
@ -123,10 +123,10 @@ func TestUngroupedStateful(t *testing.T) {
require.EqualValues(t, records1, records2) require.EqualValues(t, records1, records2)
// Update and re-checkpoint the original record. // Update and re-checkpoint the original record.
_ = caggA.Update(ctx, core.NewInt64Number(20), test.CounterADesc) _ = caggA.Update(ctx, core.NewInt64Number(20), &test.CounterADesc)
_ = caggB.Update(ctx, core.NewInt64Number(20), test.CounterBDesc) _ = caggB.Update(ctx, core.NewInt64Number(20), &test.CounterBDesc)
caggA.Checkpoint(ctx, test.CounterADesc) caggA.Checkpoint(ctx, &test.CounterADesc)
caggB.Checkpoint(ctx, test.CounterBDesc) caggB.Checkpoint(ctx, &test.CounterBDesc)
// As yet cagg has not been passed to Batcher.Process. Should // As yet cagg has not been passed to Batcher.Process. Should
// not see an update. // not see an update.
@ -139,8 +139,8 @@ func TestUngroupedStateful(t *testing.T) {
require.EqualValues(t, records1, records3) require.EqualValues(t, records1, records3)
// Now process the second update // Now process the second update
_ = b.Process(ctx, export.NewRecord(test.CounterADesc, test.Labels1, caggA)) _ = b.Process(ctx, export.NewRecord(&test.CounterADesc, test.Labels1, caggA))
_ = b.Process(ctx, export.NewRecord(test.CounterBDesc, test.Labels1, caggB)) _ = b.Process(ctx, export.NewRecord(&test.CounterBDesc, test.Labels1, caggB))
checkpointSet = b.CheckpointSet() checkpointSet = b.CheckpointSet()
b.FinishedCollection() b.FinishedCollection()
@ -148,7 +148,7 @@ func TestUngroupedStateful(t *testing.T) {
records4 := test.Output{} records4 := test.Output{}
_ = checkpointSet.ForEach(records4.AddTo) _ = checkpointSet.ForEach(records4.AddTo)
require.EqualValues(t, map[string]int64{ require.EqualValues(t, map[string]float64{
"sum.a/G~H&C~D": 30, "sum.a/G~H&C~D": 30,
"sum.b/G~H&C~D": 30, "sum.b/G~H&C~D": 30,
}, records4) }, records4)

View File

@ -44,11 +44,11 @@ func newFixture(b *testing.B) *benchFixture {
B: b, B: b,
} }
bf.sdk = sdk.New(bf, sdk.NewDefaultLabelEncoder()) bf.sdk = sdk.New(bf, sdk.NewDefaultLabelEncoder())
bf.meter = metric.Must(bf.sdk) bf.meter = metric.Must(metric.WrapMeterImpl(bf.sdk))
return bf return bf
} }
func (*benchFixture) AggregatorFor(descriptor *export.Descriptor) export.Aggregator { func (*benchFixture) AggregatorFor(descriptor *metric.Descriptor) export.Aggregator {
name := descriptor.Name() name := descriptor.Name()
switch { switch {
case strings.HasSuffix(name, "counter"): case strings.HasSuffix(name, "counter"):
@ -377,40 +377,6 @@ func BenchmarkObserverRegistration(b *testing.B) {
} }
} }
func BenchmarkObserverRegistrationUnregistration(b *testing.B) {
fix := newFixture(b)
names := make([]string, 0, b.N)
for i := 0; i < b.N; i++ {
names = append(names, fmt.Sprintf("test.observer.%d", i))
}
cb := func(result metric.Int64ObserverResult) {}
b.ResetTimer()
for i := 0; i < b.N; i++ {
fix.meter.RegisterInt64Observer(names[i], cb).Unregister()
}
}
func BenchmarkObserverRegistrationUnregistrationBatched(b *testing.B) {
fix := newFixture(b)
names := make([]string, 0, b.N)
for i := 0; i < b.N; i++ {
names = append(names, fmt.Sprintf("test.observer.%d", i))
}
observers := make([]metric.Int64Observer, 0, b.N)
cb := func(result metric.Int64ObserverResult) {}
b.ResetTimer()
for i := 0; i < b.N; i++ {
observers = append(observers, fix.meter.RegisterInt64Observer(names[i], cb))
}
for i := 0; i < b.N; i++ {
observers[i].Unregister()
}
}
func BenchmarkObserverObservationInt64(b *testing.B) { func BenchmarkObserverObservationInt64(b *testing.B) {
ctx := context.Background() ctx := context.Background()
fix := newFixture(b) fix := newFixture(b)

View File

@ -29,6 +29,7 @@ type Controller struct {
lock sync.Mutex lock sync.Mutex
collectLock sync.Mutex collectLock sync.Mutex
sdk *sdk.SDK sdk *sdk.SDK
meter metric.Meter
errorHandler sdk.ErrorHandler errorHandler sdk.ErrorHandler
batcher export.Batcher batcher export.Batcher
exporter export.Exporter exporter export.Exporter
@ -80,8 +81,10 @@ func New(batcher export.Batcher, exporter export.Exporter, period time.Duration)
lencoder = sdk.NewDefaultLabelEncoder() lencoder = sdk.NewDefaultLabelEncoder()
} }
impl := sdk.New(batcher, lencoder)
return &Controller{ return &Controller{
sdk: sdk.New(batcher, lencoder), sdk: impl,
meter: metric.WrapMeterImpl(impl),
errorHandler: sdk.DefaultErrorHandler, errorHandler: sdk.DefaultErrorHandler,
batcher: batcher, batcher: batcher,
exporter: exporter, exporter: exporter,
@ -109,7 +112,7 @@ func (c *Controller) SetErrorHandler(errorHandler sdk.ErrorHandler) {
// Meter returns a named Meter, satisifying the metric.Provider // Meter returns a named Meter, satisifying the metric.Provider
// interface. // interface.
func (c *Controller) Meter(_ string) metric.Meter { func (c *Controller) Meter(_ string) metric.Meter {
return c.sdk return c.meter
} }
// Start begins a ticker that periodically collects and exports // Start begins a ticker that periodically collects and exports

View File

@ -84,7 +84,7 @@ func newFixture(t *testing.T) testFixture {
} }
} }
func (b *testBatcher) AggregatorFor(*export.Descriptor) export.Aggregator { func (b *testBatcher) AggregatorFor(*metric.Descriptor) export.Aggregator {
return sum.New() return sum.New()
} }

View File

@ -31,6 +31,7 @@ import (
sdk "go.opentelemetry.io/otel/sdk/metric" sdk "go.opentelemetry.io/otel/sdk/metric"
"go.opentelemetry.io/otel/sdk/metric/aggregator/array" "go.opentelemetry.io/otel/sdk/metric/aggregator/array"
"go.opentelemetry.io/otel/sdk/metric/aggregator/sum" "go.opentelemetry.io/otel/sdk/metric/aggregator/sum"
batchTest "go.opentelemetry.io/otel/sdk/metric/batcher/test"
) )
var Must = metric.Must var Must = metric.Must
@ -43,7 +44,7 @@ type correctnessBatcher struct {
type testLabelEncoder struct{} type testLabelEncoder struct{}
func (cb *correctnessBatcher) AggregatorFor(descriptor *export.Descriptor) export.Aggregator { func (cb *correctnessBatcher) AggregatorFor(descriptor *metric.Descriptor) export.Aggregator {
name := descriptor.Name() name := descriptor.Name()
switch { switch {
case strings.HasSuffix(name, ".counter"): case strings.HasSuffix(name, ".counter"):
@ -78,13 +79,14 @@ func TestInputRangeTestCounter(t *testing.T) {
t: t, t: t,
} }
sdk := sdk.New(batcher, sdk.NewDefaultLabelEncoder()) sdk := sdk.New(batcher, sdk.NewDefaultLabelEncoder())
meter := metric.WrapMeterImpl(sdk)
var sdkErr error var sdkErr error
sdk.SetErrorHandler(func(handleErr error) { sdk.SetErrorHandler(func(handleErr error) {
sdkErr = handleErr sdkErr = handleErr
}) })
counter := Must(sdk).NewInt64Counter("name.counter") counter := Must(meter).NewInt64Counter("name.counter")
counter.Add(ctx, -1, sdk.Labels()) counter.Add(ctx, -1, sdk.Labels())
require.Equal(t, aggregator.ErrNegativeInput, sdkErr) require.Equal(t, aggregator.ErrNegativeInput, sdkErr)
@ -112,13 +114,14 @@ func TestInputRangeTestMeasure(t *testing.T) {
t: t, t: t,
} }
sdk := sdk.New(batcher, sdk.NewDefaultLabelEncoder()) sdk := sdk.New(batcher, sdk.NewDefaultLabelEncoder())
meter := metric.WrapMeterImpl(sdk)
var sdkErr error var sdkErr error
sdk.SetErrorHandler(func(handleErr error) { sdk.SetErrorHandler(func(handleErr error) {
sdkErr = handleErr sdkErr = handleErr
}) })
measure := Must(sdk).NewFloat64Measure("name.measure") measure := Must(meter).NewFloat64Measure("name.measure")
measure.Record(ctx, math.NaN(), sdk.Labels()) measure.Record(ctx, math.NaN(), sdk.Labels())
require.Equal(t, aggregator.ErrNaNInput, sdkErr) require.Equal(t, aggregator.ErrNaNInput, sdkErr)
@ -149,7 +152,8 @@ func TestDisabledInstrument(t *testing.T) {
t: t, t: t,
} }
sdk := sdk.New(batcher, sdk.NewDefaultLabelEncoder()) sdk := sdk.New(batcher, sdk.NewDefaultLabelEncoder())
measure := Must(sdk).NewFloat64Measure("name.disabled") meter := metric.WrapMeterImpl(sdk)
measure := Must(meter).NewFloat64Measure("name.disabled")
measure.Record(ctx, -1, sdk.Labels()) measure.Record(ctx, -1, sdk.Labels())
checkpointed := sdk.Collect(ctx) checkpointed := sdk.Collect(ctx)
@ -164,12 +168,13 @@ func TestRecordNaN(t *testing.T) {
t: t, t: t,
} }
sdk := sdk.New(batcher, sdk.NewDefaultLabelEncoder()) sdk := sdk.New(batcher, sdk.NewDefaultLabelEncoder())
meter := metric.WrapMeterImpl(sdk)
var sdkErr error var sdkErr error
sdk.SetErrorHandler(func(handleErr error) { sdk.SetErrorHandler(func(handleErr error) {
sdkErr = handleErr sdkErr = handleErr
}) })
c := Must(sdk).NewFloat64Counter("sum.name") c := Must(meter).NewFloat64Counter("sum.name")
require.Nil(t, sdkErr) require.Nil(t, sdkErr)
c.Add(ctx, math.NaN(), sdk.Labels()) c.Add(ctx, math.NaN(), sdk.Labels())
@ -182,8 +187,9 @@ func TestSDKAltLabelEncoder(t *testing.T) {
t: t, t: t,
} }
sdk := sdk.New(batcher, testLabelEncoder{}) sdk := sdk.New(batcher, testLabelEncoder{})
meter := metric.WrapMeterImpl(sdk)
measure := Must(sdk).NewFloat64Measure("measure") measure := Must(meter).NewFloat64Measure("measure")
measure.Record(ctx, 1, sdk.Labels(key.String("A", "B"), key.String("C", "D"))) measure.Record(ctx, 1, sdk.Labels(key.String("A", "B"), key.String("C", "D")))
sdk.Collect(ctx) sdk.Collect(ctx)
@ -200,8 +206,9 @@ func TestSDKLabelsDeduplication(t *testing.T) {
t: t, t: t,
} }
sdk := sdk.New(batcher, sdk.NewDefaultLabelEncoder()) sdk := sdk.New(batcher, sdk.NewDefaultLabelEncoder())
meter := metric.WrapMeterImpl(sdk)
counter := Must(sdk).NewInt64Counter("counter") counter := Must(meter).NewInt64Counter("counter")
const ( const (
maxKeys = 21 maxKeys = 21
@ -289,3 +296,45 @@ func TestDefaultLabelEncoder(t *testing.T) {
}) })
require.Equal(t, "I=1,U=1,I32=1,U32=1,I64=1,U64=1,F64=1,F64=1,S=1,B=true", encoded) require.Equal(t, "I=1,U=1,I32=1,U32=1,I64=1,U64=1,F64=1,F64=1,S=1,B=true", encoded)
} }
func TestObserverCollection(t *testing.T) {
ctx := context.Background()
batcher := &correctnessBatcher{
t: t,
}
sdk := sdk.New(batcher, sdk.NewDefaultLabelEncoder())
meter := metric.WrapMeterImpl(sdk)
_ = Must(meter).RegisterFloat64Observer("float.observer", func(result metric.Float64ObserverResult) {
// TODO: The spec says the last-value wins in observer
// instruments, but it is not implemented yet, i.e., with the
// following line we get 1-1==0 instead of -1:
// result.Observe(1, meter.Labels(key.String("A", "B")))
result.Observe(-1, meter.Labels(key.String("A", "B")))
result.Observe(-1, meter.Labels(key.String("C", "D")))
})
_ = Must(meter).RegisterInt64Observer("int.observer", func(result metric.Int64ObserverResult) {
result.Observe(1, meter.Labels(key.String("A", "B")))
result.Observe(1, meter.Labels())
})
_ = Must(meter).RegisterInt64Observer("empty.observer", func(result metric.Int64ObserverResult) {
})
collected := sdk.Collect(ctx)
require.Equal(t, 4, collected)
require.Equal(t, 4, len(batcher.records))
out := batchTest.Output{}
for _, rec := range batcher.records {
_ = out.AddTo(rec)
}
require.EqualValues(t, map[string]float64{
"float.observer/A=B": -1,
"float.observer/C=D": -1,
"int.observer/": 1,
"int.observer/A=B": 1,
}, out)
}

View File

@ -25,13 +25,13 @@ import (
"time" "time"
"go.opentelemetry.io/otel/api/core" "go.opentelemetry.io/otel/api/core"
"go.opentelemetry.io/otel/sdk/export/metric" "go.opentelemetry.io/otel/api/metric"
"go.opentelemetry.io/otel/sdk/metric/aggregator/histogram" "go.opentelemetry.io/otel/sdk/metric/aggregator/histogram"
) )
func TestStressInt64Histogram(t *testing.T) { func TestStressInt64Histogram(t *testing.T) {
desc := metric.NewDescriptor("some_metric", metric.MeasureKind, nil, "", "", core.Int64NumberKind) desc := metric.NewDescriptor("some_metric", metric.MeasureKind, core.Int64NumberKind)
h := histogram.New(desc, []core.Number{core.NewInt64Number(25), core.NewInt64Number(50), core.NewInt64Number(75)}) h := histogram.New(&desc, []core.Number{core.NewInt64Number(25), core.NewInt64Number(50), core.NewInt64Number(75)})
ctx, cancelFunc := context.WithCancel(context.Background()) ctx, cancelFunc := context.WithCancel(context.Background())
defer cancelFunc() defer cancelFunc()
@ -42,14 +42,14 @@ func TestStressInt64Histogram(t *testing.T) {
case <-ctx.Done(): case <-ctx.Done():
return return
default: default:
_ = h.Update(ctx, core.NewInt64Number(rnd.Int63()%100), desc) _ = h.Update(ctx, core.NewInt64Number(rnd.Int63()%100), &desc)
} }
} }
}() }()
startTime := time.Now() startTime := time.Now()
for time.Since(startTime) < time.Second { for time.Since(startTime) < time.Second {
h.Checkpoint(context.Background(), desc) h.Checkpoint(context.Background(), &desc)
b, _ := h.Histogram() b, _ := h.Histogram()
c, _ := h.Count() c, _ := h.Count()

View File

@ -25,13 +25,13 @@ import (
"time" "time"
"go.opentelemetry.io/otel/api/core" "go.opentelemetry.io/otel/api/core"
"go.opentelemetry.io/otel/sdk/export/metric" "go.opentelemetry.io/otel/api/metric"
"go.opentelemetry.io/otel/sdk/metric/aggregator/minmaxsumcount" "go.opentelemetry.io/otel/sdk/metric/aggregator/minmaxsumcount"
) )
func TestStressInt64MinMaxSumCount(t *testing.T) { func TestStressInt64MinMaxSumCount(t *testing.T) {
desc := metric.NewDescriptor("some_metric", metric.MeasureKind, nil, "", "", core.Int64NumberKind) desc := metric.NewDescriptor("some_metric", metric.MeasureKind, core.Int64NumberKind)
mmsc := minmaxsumcount.New(desc) mmsc := minmaxsumcount.New(&desc)
ctx, cancel := context.WithCancel(context.Background()) ctx, cancel := context.WithCancel(context.Background())
defer cancel() defer cancel()
@ -43,7 +43,7 @@ func TestStressInt64MinMaxSumCount(t *testing.T) {
case <-ctx.Done(): case <-ctx.Done():
return return
default: default:
_ = mmsc.Update(ctx, core.NewInt64Number(v), desc) _ = mmsc.Update(ctx, core.NewInt64Number(v), &desc)
} }
v++ v++
} }
@ -51,7 +51,7 @@ func TestStressInt64MinMaxSumCount(t *testing.T) {
startTime := time.Now() startTime := time.Now()
for time.Since(startTime) < time.Second { for time.Since(startTime) < time.Second {
mmsc.Checkpoint(context.Background(), desc) mmsc.Checkpoint(context.Background(), &desc)
s, _ := mmsc.Sum() s, _ := mmsc.Sum()
c, _ := mmsc.Count() c, _ := mmsc.Count()

View File

@ -44,8 +44,9 @@ type (
// current maps `mapkey` to *record. // current maps `mapkey` to *record.
current sync.Map current sync.Map
// observers is a set of `*observer` instances // asyncInstruments is a set of
observers sync.Map // `*asyncInstrument` instances
asyncInstruments sync.Map
// empty is the (singleton) result of Labels() // empty is the (singleton) result of Labels()
// w/ zero arguments. // w/ zero arguments.
@ -68,9 +69,8 @@ type (
errorHandler ErrorHandler errorHandler ErrorHandler
} }
instrument struct { syncInstrument struct {
descriptor *export.Descriptor instrument
meter *SDK
} }
// orderedLabels is a variable-size array of core.KeyValue // orderedLabels is a variable-size array of core.KeyValue
@ -95,7 +95,7 @@ type (
// mapkey uniquely describes a metric instrument in terms of // mapkey uniquely describes a metric instrument in terms of
// its InstrumentID and the encoded form of its LabelSet. // its InstrumentID and the encoded form of its LabelSet.
mapkey struct { mapkey struct {
descriptor *export.Descriptor descriptor *metric.Descriptor
ordered orderedLabels ordered orderedLabels
} }
@ -117,8 +117,8 @@ type (
// labels is the LabelSet passed by the user. // labels is the LabelSet passed by the user.
labels *labels labels *labels
// descriptor describes the metric instrument. // inst is a pointer to the corresponding instrument.
descriptor *export.Descriptor inst *syncInstrument
// recorder implements the actual RecordOne() API, // recorder implements the actual RecordOne() API,
// depending on the type of aggregation. If nil, the // depending on the type of aggregation. If nil, the
@ -126,27 +126,18 @@ type (
recorder export.Aggregator recorder export.Aggregator
} }
observerResult struct { instrument struct {
observer *observer
}
int64ObserverResult struct {
result observerResult
}
float64ObserverResult struct {
result observerResult
}
observerCallback func(result observerResult)
observer struct {
meter *SDK meter *SDK
descriptor *export.Descriptor descriptor metric.Descriptor
}
asyncInstrument struct {
instrument
// recorders maps ordered labels to the pair of // recorders maps ordered labels to the pair of
// labelset and recorder // labelset and recorder
recorders map[orderedLabels]labeledRecorder recorders map[orderedLabels]labeledRecorder
callback observerCallback
callback func(func(core.Number, api.LabelSet))
} }
labeledRecorder struct { labeledRecorder struct {
@ -155,110 +146,83 @@ type (
modifiedEpoch int64 modifiedEpoch int64
} }
int64Observer struct {
observer *observer
}
float64Observer struct {
observer *observer
}
ErrorHandler func(error) ErrorHandler func(error)
) )
var ( var (
_ api.Meter = &SDK{} _ api.MeterImpl = &SDK{}
_ api.LabelSet = &labels{} _ api.LabelSet = &labels{}
_ api.InstrumentImpl = &instrument{} _ api.AsyncImpl = &asyncInstrument{}
_ api.BoundInstrumentImpl = &record{} _ api.SyncImpl = &syncInstrument{}
_ api.Int64Observer = int64Observer{} _ api.BoundSyncImpl = &record{}
_ api.Float64Observer = float64Observer{}
_ api.Int64ObserverResult = int64ObserverResult{}
_ api.Float64ObserverResult = float64ObserverResult{}
kvType = reflect.TypeOf(core.KeyValue{}) kvType = reflect.TypeOf(core.KeyValue{})
) )
func (r observerResult) observe(number core.Number, ls api.LabelSet) { func (inst *instrument) Descriptor() api.Descriptor {
r.observer.recordOne(number, ls) return inst.descriptor
} }
func (o *observer) recordOne(number core.Number, ls api.LabelSet) { func (a *asyncInstrument) Implementation() interface{} {
if err := aggregator.RangeTest(number, o.descriptor); err != nil { return a
o.meter.errorHandler(err) }
func (s *syncInstrument) Implementation() interface{} {
return s
}
func (a *asyncInstrument) observe(number core.Number, ls api.LabelSet) {
if err := aggregator.RangeTest(number, &a.descriptor); err != nil {
a.meter.errorHandler(err)
return return
} }
recorder := o.getRecorder(ls) recorder := a.getRecorder(ls)
if recorder == nil { if recorder == nil {
// The instrument is disabled according to the // The instrument is disabled according to the
// AggregationSelector. // AggregationSelector.
return return
} }
if err := recorder.Update(context.Background(), number, o.descriptor); err != nil { if err := recorder.Update(context.Background(), number, &a.descriptor); err != nil {
o.meter.errorHandler(err) a.meter.errorHandler(err)
return return
} }
} }
func (o *observer) getRecorder(ls api.LabelSet) export.Aggregator { func (a *asyncInstrument) getRecorder(ls api.LabelSet) export.Aggregator {
labels := o.meter.labsFor(ls) labels := a.meter.labsFor(ls)
lrec, ok := o.recorders[labels.ordered] lrec, ok := a.recorders[labels.ordered]
if ok { if ok {
lrec.modifiedEpoch = o.meter.currentEpoch lrec.modifiedEpoch = a.meter.currentEpoch
o.recorders[labels.ordered] = lrec a.recorders[labels.ordered] = lrec
return lrec.recorder return lrec.recorder
} }
rec := o.meter.batcher.AggregatorFor(o.descriptor) rec := a.meter.batcher.AggregatorFor(&a.descriptor)
if o.recorders == nil { if a.recorders == nil {
o.recorders = make(map[orderedLabels]labeledRecorder) a.recorders = make(map[orderedLabels]labeledRecorder)
} }
// This may store nil recorder in the map, thus disabling the // This may store nil recorder in the map, thus disabling the
// observer for the labelset for good. This is intentional, // asyncInstrument for the labelset for good. This is intentional,
// but will be revisited later. // but will be revisited later.
o.recorders[labels.ordered] = labeledRecorder{ a.recorders[labels.ordered] = labeledRecorder{
recorder: rec, recorder: rec,
labels: labels, labels: labels,
modifiedEpoch: o.meter.currentEpoch, modifiedEpoch: a.meter.currentEpoch,
} }
return rec return rec
} }
func (o *observer) unregister() {
o.meter.observers.Delete(o)
}
func (r int64ObserverResult) Observe(value int64, labels api.LabelSet) {
r.result.observe(core.NewInt64Number(value), labels)
}
func (r float64ObserverResult) Observe(value float64, labels api.LabelSet) {
r.result.observe(core.NewFloat64Number(value), labels)
}
func (o int64Observer) Unregister() {
o.observer.unregister()
}
func (o float64Observer) Unregister() {
o.observer.unregister()
}
func (i *instrument) Meter() api.Meter {
return i.meter
}
func (m *SDK) SetErrorHandler(f ErrorHandler) { func (m *SDK) SetErrorHandler(f ErrorHandler) {
m.errorHandler = f m.errorHandler = f
} }
func (i *instrument) acquireHandle(ls *labels) *record { func (s *syncInstrument) acquireHandle(ls *labels) *record {
// Create lookup key for sync.Map (one allocation) // Create lookup key for sync.Map (one allocation)
mk := mapkey{ mk := mapkey{
descriptor: i.descriptor, descriptor: &s.descriptor,
ordered: ls.ordered, ordered: ls.ordered,
} }
if actual, ok := i.meter.current.Load(mk); ok { if actual, ok := s.meter.current.Load(mk); ok {
// Existing record case, only one allocation so far. // Existing record case, only one allocation so far.
rec := actual.(*record) rec := actual.(*record)
if rec.refMapped.ref() { if rec.refMapped.ref() {
@ -271,17 +235,17 @@ func (i *instrument) acquireHandle(ls *labels) *record {
// There's a memory allocation here. // There's a memory allocation here.
rec := &record{ rec := &record{
labels: ls, labels: ls,
descriptor: i.descriptor, inst: s,
refMapped: refcountMapped{value: 2}, refMapped: refcountMapped{value: 2},
modified: 0, modified: 0,
recorder: i.meter.batcher.AggregatorFor(i.descriptor), recorder: s.meter.batcher.AggregatorFor(&s.descriptor),
} }
for { for {
// Load/Store: there's a memory allocation to place `mk` into // Load/Store: there's a memory allocation to place `mk` into
// an interface here. // an interface here.
if actual, loaded := i.meter.current.LoadOrStore(mk, rec); loaded { if actual, loaded := s.meter.current.LoadOrStore(mk, rec); loaded {
// Existing record case. Cannot change rec here because if fail // Existing record case. Cannot change rec here because if fail
// will try to add rec again to avoid new allocations. // will try to add rec again to avoid new allocations.
oldRec := actual.(*record) oldRec := actual.(*record)
@ -308,14 +272,14 @@ func (i *instrument) acquireHandle(ls *labels) *record {
} }
} }
func (i *instrument) Bind(ls api.LabelSet) api.BoundInstrumentImpl { func (s *syncInstrument) Bind(ls api.LabelSet) api.BoundSyncImpl {
labs := i.meter.labsFor(ls) labs := s.meter.labsFor(ls)
return i.acquireHandle(labs) return s.acquireHandle(labs)
} }
func (i *instrument) RecordOne(ctx context.Context, number core.Number, ls api.LabelSet) { func (s *syncInstrument) RecordOne(ctx context.Context, number core.Number, ls api.LabelSet) {
ourLs := i.meter.labsFor(ls) ourLs := s.meter.labsFor(ls)
h := i.acquireHandle(ourLs) h := s.acquireHandle(ourLs)
defer h.Unbind() defer h.Unbind()
h.RecordOne(ctx, number) h.RecordOne(ctx, number)
} }
@ -461,100 +425,25 @@ func (m *SDK) labsFor(ls api.LabelSet) *labels {
return &m.empty return &m.empty
} }
func newDescriptor(name string, metricKind export.Kind, numberKind core.NumberKind, opts []api.Option) *export.Descriptor { func (m *SDK) NewSyncInstrument(descriptor api.Descriptor) (api.SyncImpl, error) {
config := api.Configure(opts) return &syncInstrument{
return export.NewDescriptor( instrument: instrument{
name, descriptor: descriptor,
metricKind, meter: m,
config.Keys, },
config.Description,
config.Unit,
numberKind)
}
func (m *SDK) newInstrument(name string, metricKind export.Kind, numberKind core.NumberKind, opts []api.Option) *instrument {
descriptor := newDescriptor(name, metricKind, numberKind, opts)
return &instrument{
descriptor: descriptor,
meter: m,
}
}
func (m *SDK) newCounterInstrument(name string, numberKind core.NumberKind, opts []api.Option) *instrument {
return m.newInstrument(name, export.CounterKind, numberKind, opts)
}
func (m *SDK) newMeasureInstrument(name string, numberKind core.NumberKind, opts []api.Option) *instrument {
return m.newInstrument(name, export.MeasureKind, numberKind, opts)
}
func (m *SDK) NewInt64Counter(name string, opts ...api.Option) (api.Int64Counter, error) {
return api.WrapInt64CounterInstrument(m.newCounterInstrument(name, core.Int64NumberKind, opts), nil)
}
func (m *SDK) NewFloat64Counter(name string, opts ...api.Option) (api.Float64Counter, error) {
return api.WrapFloat64CounterInstrument(m.newCounterInstrument(name, core.Float64NumberKind, opts), nil)
}
func (m *SDK) NewInt64Measure(name string, opts ...api.Option) (api.Int64Measure, error) {
return api.WrapInt64MeasureInstrument(m.newMeasureInstrument(name, core.Int64NumberKind, opts), nil)
}
func (m *SDK) NewFloat64Measure(name string, opts ...api.Option) (api.Float64Measure, error) {
return api.WrapFloat64MeasureInstrument(m.newMeasureInstrument(name, core.Float64NumberKind, opts), nil)
}
func (m *SDK) RegisterInt64Observer(name string, callback api.Int64ObserverCallback, opts ...api.Option) (api.Int64Observer, error) {
if callback == nil {
return api.NoopMeter{}.RegisterInt64Observer("", nil)
}
descriptor := newDescriptor(name, export.ObserverKind, core.Int64NumberKind, opts)
cb := wrapInt64ObserverCallback(callback)
obs := m.newObserver(descriptor, cb)
return int64Observer{
observer: obs,
}, nil }, nil
} }
func wrapInt64ObserverCallback(callback api.Int64ObserverCallback) observerCallback { func (m *SDK) NewAsyncInstrument(descriptor api.Descriptor, callback func(func(core.Number, api.LabelSet))) (api.AsyncImpl, error) {
return func(result observerResult) { a := &asyncInstrument{
typeSafeResult := int64ObserverResult{ instrument: instrument{
result: result, descriptor: descriptor,
} meter: m,
callback(typeSafeResult) },
callback: callback,
} }
} m.asyncInstruments.Store(a, nil)
return a, nil
func (m *SDK) RegisterFloat64Observer(name string, callback api.Float64ObserverCallback, opts ...api.Option) (api.Float64Observer, error) {
if callback == nil {
return api.NoopMeter{}.RegisterFloat64Observer("", nil)
}
descriptor := newDescriptor(name, export.ObserverKind, core.Float64NumberKind, opts)
cb := wrapFloat64ObserverCallback(callback)
obs := m.newObserver(descriptor, cb)
return float64Observer{
observer: obs,
}, nil
}
func wrapFloat64ObserverCallback(callback api.Float64ObserverCallback) observerCallback {
return func(result observerResult) {
typeSafeResult := float64ObserverResult{
result: result,
}
callback(typeSafeResult)
}
}
func (m *SDK) newObserver(descriptor *export.Descriptor, callback observerCallback) *observer {
obs := &observer{
meter: m,
descriptor: descriptor,
recorders: nil,
callback: callback,
}
m.observers.Store(obs, nil)
return obs
} }
// Collect traverses the list of active records and observers and // Collect traverses the list of active records and observers and
@ -570,7 +459,7 @@ func (m *SDK) Collect(ctx context.Context) int {
defer m.collectLock.Unlock() defer m.collectLock.Unlock()
checkpointed := m.collectRecords(ctx) checkpointed := m.collectRecords(ctx)
checkpointed += m.collectObservers(ctx) checkpointed += m.collectAsync(ctx)
m.currentEpoch++ m.currentEpoch++
return checkpointed return checkpointed
} }
@ -601,16 +490,13 @@ func (m *SDK) collectRecords(ctx context.Context) int {
return checkpointed return checkpointed
} }
func (m *SDK) collectObservers(ctx context.Context) int { func (m *SDK) collectAsync(ctx context.Context) int {
checkpointed := 0 checkpointed := 0
m.observers.Range(func(key, value interface{}) bool { m.asyncInstruments.Range(func(key, value interface{}) bool {
obs := key.(*observer) a := key.(*asyncInstrument)
result := observerResult{ a.callback(a.observe)
observer: obs, checkpointed += m.checkpointAsync(ctx, a)
}
obs.callback(result)
checkpointed += m.checkpointObserver(ctx, obs)
return true return true
}) })
@ -618,32 +504,32 @@ func (m *SDK) collectObservers(ctx context.Context) int {
} }
func (m *SDK) checkpointRecord(ctx context.Context, r *record) int { func (m *SDK) checkpointRecord(ctx context.Context, r *record) int {
return m.checkpoint(ctx, r.descriptor, r.recorder, r.labels) return m.checkpoint(ctx, &r.inst.descriptor, r.recorder, r.labels)
} }
func (m *SDK) checkpointObserver(ctx context.Context, obs *observer) int { func (m *SDK) checkpointAsync(ctx context.Context, a *asyncInstrument) int {
if len(obs.recorders) == 0 { if len(a.recorders) == 0 {
return 0 return 0
} }
checkpointed := 0 checkpointed := 0
for encodedLabels, lrec := range obs.recorders { for encodedLabels, lrec := range a.recorders {
epochDiff := m.currentEpoch - lrec.modifiedEpoch epochDiff := m.currentEpoch - lrec.modifiedEpoch
if epochDiff == 0 { if epochDiff == 0 {
checkpointed += m.checkpoint(ctx, obs.descriptor, lrec.recorder, lrec.labels) checkpointed += m.checkpoint(ctx, &a.descriptor, lrec.recorder, lrec.labels)
} else if epochDiff > 1 { } else if epochDiff > 1 {
// This is second collection cycle with no // This is second collection cycle with no
// observations for this labelset. Remove the // observations for this labelset. Remove the
// recorder. // recorder.
delete(obs.recorders, encodedLabels) delete(a.recorders, encodedLabels)
} }
} }
if len(obs.recorders) == 0 { if len(a.recorders) == 0 {
obs.recorders = nil a.recorders = nil
} }
return checkpointed return checkpointed
} }
func (m *SDK) checkpoint(ctx context.Context, descriptor *export.Descriptor, recorder export.Aggregator, labels *labels) int { func (m *SDK) checkpoint(ctx context.Context, descriptor *metric.Descriptor, recorder export.Aggregator, labels *labels) int {
if recorder == nil { if recorder == nil {
return 0 return 0
} }
@ -665,15 +551,16 @@ func (m *SDK) checkpoint(ctx context.Context, descriptor *export.Descriptor, rec
// RecordBatch enters a batch of metric events. // RecordBatch enters a batch of metric events.
func (m *SDK) RecordBatch(ctx context.Context, ls api.LabelSet, measurements ...api.Measurement) { func (m *SDK) RecordBatch(ctx context.Context, ls api.LabelSet, measurements ...api.Measurement) {
for _, meas := range measurements { for _, meas := range measurements {
meas.InstrumentImpl().RecordOne(ctx, meas.Number(), ls) meas.SyncImpl().RecordOne(ctx, meas.Number(), ls)
} }
} }
// GetDescriptor returns the descriptor of an instrument, which is not // GetDescriptor returns a pointer to the descriptor of an instrument,
// part of the public metric API. // which is not part of the public metric API. This is for testing. Use
func (m *SDK) GetDescriptor(inst metric.InstrumentImpl) *export.Descriptor { // SyncImpl().Descriptor() to get a copy of the descriptor.
if ii, ok := inst.(*instrument); ok { func (m *SDK) GetDescriptor(inst api.SyncImpl) *metric.Descriptor {
return ii.descriptor if ii, ok := inst.(*syncInstrument); ok {
return &ii.descriptor
} }
return nil return nil
} }
@ -683,11 +570,11 @@ func (r *record) RecordOne(ctx context.Context, number core.Number) {
// The instrument is disabled according to the AggregationSelector. // The instrument is disabled according to the AggregationSelector.
return return
} }
if err := aggregator.RangeTest(number, r.descriptor); err != nil { if err := aggregator.RangeTest(number, &r.inst.descriptor); err != nil {
r.labels.meter.errorHandler(err) r.labels.meter.errorHandler(err)
return return
} }
if err := r.recorder.Update(ctx, number, r.descriptor); err != nil { if err := r.recorder.Update(ctx, number, &r.inst.descriptor); err != nil {
r.labels.meter.errorHandler(err) r.labels.meter.errorHandler(err)
return return
} }
@ -703,7 +590,7 @@ func (r *record) Unbind() {
func (r *record) mapkey() mapkey { func (r *record) mapkey() mapkey {
return mapkey{ return mapkey{
descriptor: r.descriptor, descriptor: &r.inst.descriptor,
ordered: r.labels.ordered, ordered: r.labels.ordered,
} }
} }

View File

@ -15,6 +15,7 @@
package simple // import "go.opentelemetry.io/otel/sdk/metric/selector/simple" package simple // import "go.opentelemetry.io/otel/sdk/metric/selector/simple"
import ( import (
"go.opentelemetry.io/otel/api/metric"
export "go.opentelemetry.io/otel/sdk/export/metric" export "go.opentelemetry.io/otel/sdk/export/metric"
"go.opentelemetry.io/otel/sdk/metric/aggregator/array" "go.opentelemetry.io/otel/sdk/metric/aggregator/array"
"go.opentelemetry.io/otel/sdk/metric/aggregator/ddsketch" "go.opentelemetry.io/otel/sdk/metric/aggregator/ddsketch"
@ -65,33 +66,33 @@ func NewWithExactMeasure() export.AggregationSelector {
return selectorExact{} return selectorExact{}
} }
func (selectorInexpensive) AggregatorFor(descriptor *export.Descriptor) export.Aggregator { func (selectorInexpensive) AggregatorFor(descriptor *metric.Descriptor) export.Aggregator {
switch descriptor.MetricKind() { switch descriptor.MetricKind() {
case export.ObserverKind: case metric.ObserverKind:
fallthrough fallthrough
case export.MeasureKind: case metric.MeasureKind:
return minmaxsumcount.New(descriptor) return minmaxsumcount.New(descriptor)
default: default:
return sum.New() return sum.New()
} }
} }
func (s selectorSketch) AggregatorFor(descriptor *export.Descriptor) export.Aggregator { func (s selectorSketch) AggregatorFor(descriptor *metric.Descriptor) export.Aggregator {
switch descriptor.MetricKind() { switch descriptor.MetricKind() {
case export.ObserverKind: case metric.ObserverKind:
fallthrough fallthrough
case export.MeasureKind: case metric.MeasureKind:
return ddsketch.New(s.config, descriptor) return ddsketch.New(s.config, descriptor)
default: default:
return sum.New() return sum.New()
} }
} }
func (selectorExact) AggregatorFor(descriptor *export.Descriptor) export.Aggregator { func (selectorExact) AggregatorFor(descriptor *metric.Descriptor) export.Aggregator {
switch descriptor.MetricKind() { switch descriptor.MetricKind() {
case export.ObserverKind: case metric.ObserverKind:
fallthrough fallthrough
case export.MeasureKind: case metric.MeasureKind:
return array.New() return array.New()
default: default:
return sum.New() return sum.New()

View File

@ -20,7 +20,7 @@ import (
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
"go.opentelemetry.io/otel/api/core" "go.opentelemetry.io/otel/api/core"
export "go.opentelemetry.io/otel/sdk/export/metric" "go.opentelemetry.io/otel/api/metric"
"go.opentelemetry.io/otel/sdk/metric/aggregator/array" "go.opentelemetry.io/otel/sdk/metric/aggregator/array"
"go.opentelemetry.io/otel/sdk/metric/aggregator/ddsketch" "go.opentelemetry.io/otel/sdk/metric/aggregator/ddsketch"
"go.opentelemetry.io/otel/sdk/metric/aggregator/minmaxsumcount" "go.opentelemetry.io/otel/sdk/metric/aggregator/minmaxsumcount"
@ -29,28 +29,28 @@ import (
) )
var ( var (
testCounterDesc = export.NewDescriptor("counter", export.CounterKind, nil, "", "", core.Int64NumberKind) testCounterDesc = metric.NewDescriptor("counter", metric.CounterKind, core.Int64NumberKind)
testMeasureDesc = export.NewDescriptor("measure", export.MeasureKind, nil, "", "", core.Int64NumberKind) testMeasureDesc = metric.NewDescriptor("measure", metric.MeasureKind, core.Int64NumberKind)
testObserverDesc = export.NewDescriptor("observer", export.ObserverKind, nil, "", "", core.Int64NumberKind) testObserverDesc = metric.NewDescriptor("observer", metric.ObserverKind, core.Int64NumberKind)
) )
func TestInexpensiveMeasure(t *testing.T) { func TestInexpensiveMeasure(t *testing.T) {
inex := simple.NewWithInexpensiveMeasure() inex := simple.NewWithInexpensiveMeasure()
require.NotPanics(t, func() { _ = inex.AggregatorFor(testCounterDesc).(*sum.Aggregator) }) require.NotPanics(t, func() { _ = inex.AggregatorFor(&testCounterDesc).(*sum.Aggregator) })
require.NotPanics(t, func() { _ = inex.AggregatorFor(testMeasureDesc).(*minmaxsumcount.Aggregator) }) require.NotPanics(t, func() { _ = inex.AggregatorFor(&testMeasureDesc).(*minmaxsumcount.Aggregator) })
require.NotPanics(t, func() { _ = inex.AggregatorFor(testObserverDesc).(*minmaxsumcount.Aggregator) }) require.NotPanics(t, func() { _ = inex.AggregatorFor(&testObserverDesc).(*minmaxsumcount.Aggregator) })
} }
func TestSketchMeasure(t *testing.T) { func TestSketchMeasure(t *testing.T) {
sk := simple.NewWithSketchMeasure(ddsketch.NewDefaultConfig()) sk := simple.NewWithSketchMeasure(ddsketch.NewDefaultConfig())
require.NotPanics(t, func() { _ = sk.AggregatorFor(testCounterDesc).(*sum.Aggregator) }) require.NotPanics(t, func() { _ = sk.AggregatorFor(&testCounterDesc).(*sum.Aggregator) })
require.NotPanics(t, func() { _ = sk.AggregatorFor(testMeasureDesc).(*ddsketch.Aggregator) }) require.NotPanics(t, func() { _ = sk.AggregatorFor(&testMeasureDesc).(*ddsketch.Aggregator) })
require.NotPanics(t, func() { _ = sk.AggregatorFor(testObserverDesc).(*ddsketch.Aggregator) }) require.NotPanics(t, func() { _ = sk.AggregatorFor(&testObserverDesc).(*ddsketch.Aggregator) })
} }
func TestExactMeasure(t *testing.T) { func TestExactMeasure(t *testing.T) {
ex := simple.NewWithExactMeasure() ex := simple.NewWithExactMeasure()
require.NotPanics(t, func() { _ = ex.AggregatorFor(testCounterDesc).(*sum.Aggregator) }) require.NotPanics(t, func() { _ = ex.AggregatorFor(&testCounterDesc).(*sum.Aggregator) })
require.NotPanics(t, func() { _ = ex.AggregatorFor(testMeasureDesc).(*array.Aggregator) }) require.NotPanics(t, func() { _ = ex.AggregatorFor(&testMeasureDesc).(*array.Aggregator) })
require.NotPanics(t, func() { _ = ex.AggregatorFor(testObserverDesc).(*array.Aggregator) }) require.NotPanics(t, func() { _ = ex.AggregatorFor(&testObserverDesc).(*array.Aggregator) })
} }

View File

@ -68,11 +68,11 @@ type (
testKey struct { testKey struct {
labels string labels string
descriptor *export.Descriptor descriptor *metric.Descriptor
} }
testImpl struct { testImpl struct {
newInstrument func(meter api.Meter, name string) withImpl newInstrument func(meter api.Meter, name string) SyncImpler
getUpdateValue func() core.Number getUpdateValue func() core.Number
operate func(interface{}, context.Context, core.Number, api.LabelSet) operate func(interface{}, context.Context, core.Number, api.LabelSet)
newStore func() interface{} newStore func() interface{}
@ -86,8 +86,8 @@ type (
equalValues func(a, b core.Number) bool equalValues func(a, b core.Number) bool
} }
withImpl interface { SyncImpler interface {
Impl() metric.InstrumentImpl SyncImpl() metric.SyncImpl
} }
// lastValueState supports merging lastValue values, for the case // lastValueState supports merging lastValue values, for the case
@ -156,14 +156,14 @@ func (f *testFixture) someLabels() []core.KeyValue {
} }
} }
func (f *testFixture) startWorker(sdk *sdk.SDK, wg *sync.WaitGroup, i int) { func (f *testFixture) startWorker(impl *sdk.SDK, meter api.Meter, wg *sync.WaitGroup, i int) {
ctx := context.Background() ctx := context.Background()
name := fmt.Sprint("test_", i) name := fmt.Sprint("test_", i)
instrument := f.impl.newInstrument(sdk, name) instrument := f.impl.newInstrument(meter, name)
descriptor := sdk.GetDescriptor(instrument.Impl()) descriptor := impl.GetDescriptor(instrument.SyncImpl())
kvs := f.someLabels() kvs := f.someLabels()
clabs := canonicalizeLabels(kvs) clabs := canonicalizeLabels(kvs)
labs := sdk.Labels(kvs...) labs := meter.Labels(kvs...)
dur := getPeriod() dur := getPeriod()
key := testKey{ key := testKey{
labels: clabs, labels: clabs,
@ -230,7 +230,7 @@ func (f *testFixture) preCollect() {
f.dupCheck = map[testKey]int{} f.dupCheck = map[testKey]int{}
} }
func (*testFixture) AggregatorFor(descriptor *export.Descriptor) export.Aggregator { func (*testFixture) AggregatorFor(descriptor *metric.Descriptor) export.Aggregator {
name := descriptor.Name() name := descriptor.Name()
switch { switch {
case strings.HasSuffix(name, "counter"): case strings.HasSuffix(name, "counter"):
@ -264,13 +264,13 @@ func (f *testFixture) Process(_ context.Context, record export.Record) error {
agg := record.Aggregator() agg := record.Aggregator()
switch record.Descriptor().MetricKind() { switch record.Descriptor().MetricKind() {
case export.CounterKind: case metric.CounterKind:
sum, err := agg.(aggregator.Sum).Sum() sum, err := agg.(aggregator.Sum).Sum()
if err != nil { if err != nil {
f.T.Fatal("Sum error: ", err) f.T.Fatal("Sum error: ", err)
} }
f.impl.storeCollect(actual, sum, time.Time{}) f.impl.storeCollect(actual, sum, time.Time{})
case export.MeasureKind: case metric.MeasureKind:
lv, ts, err := agg.(aggregator.LastValue).LastValue() lv, ts, err := agg.(aggregator.LastValue).LastValue()
if err != nil && err != aggregator.ErrNoData { if err != nil && err != aggregator.ErrNoData {
f.T.Fatal("Last value error: ", err) f.T.Fatal("Last value error: ", err)
@ -292,10 +292,11 @@ func stressTest(t *testing.T, impl testImpl) {
} }
cc := concurrency() cc := concurrency()
sdk := sdk.New(fixture, sdk.NewDefaultLabelEncoder()) sdk := sdk.New(fixture, sdk.NewDefaultLabelEncoder())
meter := metric.WrapMeterImpl(sdk)
fixture.wg.Add(cc + 1) fixture.wg.Add(cc + 1)
for i := 0; i < cc; i++ { for i := 0; i < cc; i++ {
go fixture.startWorker(sdk, &fixture.wg, i) go fixture.startWorker(sdk, meter, &fixture.wg, i)
} }
numCollect := 0 numCollect := 0
@ -336,7 +337,7 @@ func float64sEqual(a, b core.Number) bool {
func intCounterTestImpl() testImpl { func intCounterTestImpl() testImpl {
return testImpl{ return testImpl{
newInstrument: func(meter api.Meter, name string) withImpl { newInstrument: func(meter api.Meter, name string) SyncImpler {
return Must(meter).NewInt64Counter(name + ".counter") return Must(meter).NewInt64Counter(name + ".counter")
}, },
getUpdateValue: func() core.Number { getUpdateValue: func() core.Number {
@ -374,7 +375,7 @@ func TestStressInt64Counter(t *testing.T) {
func floatCounterTestImpl() testImpl { func floatCounterTestImpl() testImpl {
return testImpl{ return testImpl{
newInstrument: func(meter api.Meter, name string) withImpl { newInstrument: func(meter api.Meter, name string) SyncImpler {
return Must(meter).NewFloat64Counter(name + ".counter") return Must(meter).NewFloat64Counter(name + ".counter")
}, },
getUpdateValue: func() core.Number { getUpdateValue: func() core.Number {
@ -414,7 +415,7 @@ func TestStressFloat64Counter(t *testing.T) {
func intLastValueTestImpl() testImpl { func intLastValueTestImpl() testImpl {
return testImpl{ return testImpl{
newInstrument: func(meter api.Meter, name string) withImpl { newInstrument: func(meter api.Meter, name string) SyncImpler {
return Must(meter).NewInt64Measure(name + ".lastvalue") return Must(meter).NewInt64Measure(name + ".lastvalue")
}, },
getUpdateValue: func() core.Number { getUpdateValue: func() core.Number {
@ -456,7 +457,7 @@ func TestStressInt64LastValue(t *testing.T) {
func floatLastValueTestImpl() testImpl { func floatLastValueTestImpl() testImpl {
return testImpl{ return testImpl{
newInstrument: func(meter api.Meter, name string) withImpl { newInstrument: func(meter api.Meter, name string) SyncImpler {
return Must(meter).NewFloat64Measure(name + ".lastvalue") return Must(meter).NewFloat64Measure(name + ".lastvalue")
}, },
getUpdateValue: func() core.Number { getUpdateValue: func() core.Number {