From 4047c0877ad5995a699493453b8338f020e446fe Mon Sep 17 00:00:00 2001 From: Joshua MacDonald Date: Wed, 11 Mar 2020 11:57:57 -0700 Subject: [PATCH] Introduce metric constructor errors, `MeterMust` wrapper (#529) * Update api for Must constructors, with SDK helpers * Update for Must constructors, leaving TODOs about global errors * Add tests * Move Must methods into metric.Must * Apply the feedback * Remove interfaces * Remove more interfaces * Again... * Remove a sentence about a dead inteface --- api/global/internal/benchmark_test.go | 6 +- api/global/internal/meter.go | 85 ++++++++++++++++----- api/global/internal/meter_test.go | 63 ++++++++++++---- api/metric/api.go | 24 +++--- api/metric/api_test.go | 50 ++++++++++-- api/metric/common.go | 18 ++++- api/metric/must.go | 88 ++++++++++++++++++++++ api/metric/noop.go | 45 +++++------ api/metric/sdkhelpers.go | 20 +++-- example/basic/main.go | 7 +- example/prometheus/main.go | 6 +- exporters/metric/dogstatsd/example_test.go | 2 +- exporters/metric/stdout/example_test.go | 2 +- internal/metric/mock.go | 32 ++++---- sdk/metric/benchmark_test.go | 46 +++++------ sdk/metric/controller/push/push_test.go | 3 +- sdk/metric/correct_test.go | 14 ++-- sdk/metric/example_test.go | 2 +- sdk/metric/sdk.go | 24 +++--- sdk/metric/stress_test.go | 8 +- 20 files changed, 391 insertions(+), 154 deletions(-) create mode 100644 api/metric/must.go diff --git a/api/global/internal/benchmark_test.go b/api/global/internal/benchmark_test.go index 88d5618c8..e8f0814f2 100644 --- a/api/global/internal/benchmark_test.go +++ b/api/global/internal/benchmark_test.go @@ -18,6 +18,8 @@ import ( sdktrace "go.opentelemetry.io/otel/sdk/trace" ) +var Must = metric.Must + // benchFixture is copied from sdk/metric/benchmark_test.go. // TODO refactor to share this code. type benchFixture struct { @@ -72,7 +74,7 @@ func BenchmarkGlobalInt64CounterAddNoSDK(b *testing.B) { ctx := context.Background() sdk := global.Meter("test") labs := sdk.Labels(key.String("A", "B")) - cnt := sdk.NewInt64Counter("int64.counter") + cnt := Must(sdk).NewInt64Counter("int64.counter") b.ResetTimer() @@ -91,7 +93,7 @@ func BenchmarkGlobalInt64CounterAddWithSDK(b *testing.B) { global.SetMeterProvider(fix) labs := sdk.Labels(key.String("A", "B")) - cnt := sdk.NewInt64Counter("int64.counter") + cnt := Must(sdk).NewInt64Counter("int64.counter") b.ResetTimer() diff --git a/api/global/internal/meter.go b/api/global/internal/meter.go index b6b66b9b8..06dfc1c5a 100644 --- a/api/global/internal/meter.go +++ b/api/global/internal/meter.go @@ -2,6 +2,7 @@ package internal import ( "context" + "errors" "sync" "sync/atomic" "unsafe" @@ -80,6 +81,10 @@ type obsImpl struct { callback interface{} } +type hasImpl interface { + Impl() metric.InstrumentImpl +} + type int64ObsImpl struct { observer *obsImpl } @@ -122,6 +127,8 @@ var _ metric.Float64Observer = float64ObsImpl{} var _ observerUnregister = (metric.Int64Observer)(nil) var _ observerUnregister = (metric.Float64Observer)(nil) +var errInvalidMetricKind = errors.New("Invalid Metric kind") + // Provider interface and delegation func (p *meterProvider) setDelegate(provider metric.Provider) { @@ -174,7 +181,7 @@ func (m *meter) setDelegate(provider metric.Provider) { m.orderedObservers = nil } -func (m *meter) newInst(name string, mkind metricKind, nkind core.NumberKind, opts interface{}) metric.InstrumentImpl { +func (m *meter) newInst(name string, mkind metricKind, nkind core.NumberKind, opts interface{}) (metric.InstrumentImpl, error) { m.lock.Lock() defer m.lock.Unlock() @@ -189,23 +196,33 @@ func (m *meter) newInst(name string, mkind metricKind, nkind core.NumberKind, op opts: opts, } m.instruments = append(m.instruments, inst) - return inst + return inst, nil } -func newInstDelegate(m metric.Meter, name string, mkind metricKind, nkind core.NumberKind, opts interface{}) metric.InstrumentImpl { +func delegateCheck(has hasImpl, err error) (metric.InstrumentImpl, error) { + if has != nil { + return has.Impl(), err + } + if err == nil { + err = metric.ErrSDKReturnedNilImpl + } + return nil, err +} + +func newInstDelegate(m metric.Meter, name string, mkind metricKind, nkind core.NumberKind, opts interface{}) (metric.InstrumentImpl, error) { switch mkind { case counterKind: if nkind == core.Int64NumberKind { - return m.NewInt64Counter(name, opts.([]metric.CounterOptionApplier)...).Impl() + return delegateCheck(m.NewInt64Counter(name, opts.([]metric.CounterOptionApplier)...)) } - return m.NewFloat64Counter(name, opts.([]metric.CounterOptionApplier)...).Impl() + return delegateCheck(m.NewFloat64Counter(name, opts.([]metric.CounterOptionApplier)...)) case measureKind: if nkind == core.Int64NumberKind { - return m.NewInt64Measure(name, opts.([]metric.MeasureOptionApplier)...).Impl() + return delegateCheck(m.NewInt64Measure(name, opts.([]metric.MeasureOptionApplier)...)) } - return m.NewFloat64Measure(name, opts.([]metric.MeasureOptionApplier)...).Impl() + return delegateCheck(m.NewFloat64Measure(name, opts.([]metric.MeasureOptionApplier)...)) } - return nil + return nil, errInvalidMetricKind } // Instrument delegation @@ -213,7 +230,16 @@ func newInstDelegate(m metric.Meter, name string, mkind metricKind, nkind core.N func (inst *instImpl) setDelegate(d metric.Meter) { implPtr := new(metric.InstrumentImpl) - *implPtr = newInstDelegate(d, inst.name, inst.mkind, inst.nkind, inst.opts) + var err error + *implPtr, err = newInstDelegate(d, inst.name, inst.mkind, inst.nkind, inst.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(&inst.delegate, unsafe.Pointer(implPtr)) } @@ -281,7 +307,18 @@ func (obs *obsImpl) getUnregister() observerUnregister { func (obs *obsImpl) setInt64Delegate(d metric.Meter) { obsPtr := new(metric.Int64Observer) cb := obs.callback.(metric.Int64ObserverCallback) - *obsPtr = d.RegisterInt64Observer(obs.name, cb, obs.opts...) + + var err error + *obsPtr, err = d.RegisterInt64Observer(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)) } @@ -294,7 +331,17 @@ func (obs int64ObsImpl) Unregister() { func (obs *obsImpl) setFloat64Delegate(d metric.Meter) { obsPtr := new(metric.Float64Observer) cb := obs.callback.(metric.Float64ObserverCallback) - *obsPtr = d.RegisterFloat64Observer(obs.name, cb, obs.opts...) + + 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)) } @@ -372,23 +419,23 @@ func (labels *labelSet) Delegate() metric.LabelSet { // Constructors -func (m *meter) NewInt64Counter(name string, opts ...metric.CounterOptionApplier) metric.Int64Counter { +func (m *meter) NewInt64Counter(name string, opts ...metric.CounterOptionApplier) (metric.Int64Counter, error) { return metric.WrapInt64CounterInstrument(m.newInst(name, counterKind, core.Int64NumberKind, opts)) } -func (m *meter) NewFloat64Counter(name string, opts ...metric.CounterOptionApplier) metric.Float64Counter { +func (m *meter) NewFloat64Counter(name string, opts ...metric.CounterOptionApplier) (metric.Float64Counter, error) { return metric.WrapFloat64CounterInstrument(m.newInst(name, counterKind, core.Float64NumberKind, opts)) } -func (m *meter) NewInt64Measure(name string, opts ...metric.MeasureOptionApplier) metric.Int64Measure { +func (m *meter) NewInt64Measure(name string, opts ...metric.MeasureOptionApplier) (metric.Int64Measure, error) { return metric.WrapInt64MeasureInstrument(m.newInst(name, measureKind, core.Int64NumberKind, opts)) } -func (m *meter) NewFloat64Measure(name string, opts ...metric.MeasureOptionApplier) metric.Float64Measure { +func (m *meter) NewFloat64Measure(name string, opts ...metric.MeasureOptionApplier) (metric.Float64Measure, error) { return metric.WrapFloat64MeasureInstrument(m.newInst(name, measureKind, core.Float64NumberKind, opts)) } -func (m *meter) RegisterInt64Observer(name string, callback metric.Int64ObserverCallback, oos ...metric.ObserverOptionApplier) metric.Int64Observer { +func (m *meter) RegisterInt64Observer(name string, callback metric.Int64ObserverCallback, oos ...metric.ObserverOptionApplier) (metric.Int64Observer, error) { m.lock.Lock() defer m.lock.Unlock() @@ -406,10 +453,10 @@ func (m *meter) RegisterInt64Observer(name string, callback metric.Int64Observer m.addObserver(obs) return int64ObsImpl{ observer: obs, - } + }, nil } -func (m *meter) RegisterFloat64Observer(name string, callback metric.Float64ObserverCallback, oos ...metric.ObserverOptionApplier) metric.Float64Observer { +func (m *meter) RegisterFloat64Observer(name string, callback metric.Float64ObserverCallback, oos ...metric.ObserverOptionApplier) (metric.Float64Observer, error) { m.lock.Lock() defer m.lock.Unlock() @@ -427,7 +474,7 @@ func (m *meter) RegisterFloat64Observer(name string, callback metric.Float64Obse m.addObserver(obs) return float64ObsImpl{ observer: obs, - } + }, nil } func (m *meter) addObserver(obs *obsImpl) { diff --git a/api/global/internal/meter_test.go b/api/global/internal/meter_test.go index b57c6ef33..d456fc6c7 100644 --- a/api/global/internal/meter_test.go +++ b/api/global/internal/meter_test.go @@ -2,6 +2,7 @@ package internal_test import ( "context" + "errors" "io" "io/ioutil" "testing" @@ -30,25 +31,25 @@ func TestDirect(t *testing.T) { lvals3 := key.String("E", "F") labels3 := meter2.Labels(lvals3) - counter := meter1.NewInt64Counter("test.counter") + counter := Must(meter1).NewInt64Counter("test.counter") counter.Add(ctx, 1, labels1) counter.Add(ctx, 1, labels1) - measure := meter1.NewFloat64Measure("test.measure") + measure := Must(meter1).NewFloat64Measure("test.measure") measure.Record(ctx, 1, labels1) measure.Record(ctx, 2, labels1) - _ = meter1.RegisterFloat64Observer("test.observer.float", func(result metric.Float64ObserverResult) { + _ = Must(meter1).RegisterFloat64Observer("test.observer.float", func(result metric.Float64ObserverResult) { result.Observe(1., labels1) result.Observe(2., labels2) }) - _ = meter1.RegisterInt64Observer("test.observer.int", func(result metric.Int64ObserverResult) { + _ = Must(meter1).RegisterInt64Observer("test.observer.int", func(result metric.Int64ObserverResult) { result.Observe(1, labels1) result.Observe(2, labels2) }) - second := meter2.NewFloat64Measure("test.second") + second := Must(meter2).NewFloat64Measure("test.second") second.Record(ctx, 1, labels3) second.Record(ctx, 2, labels3) @@ -145,12 +146,12 @@ func TestBound(t *testing.T) { lvals1 := key.String("A", "B") labels1 := glob.Labels(lvals1) - counter := glob.NewFloat64Counter("test.counter") + counter := Must(glob).NewFloat64Counter("test.counter") boundC := counter.Bind(labels1) boundC.Add(ctx, 1) boundC.Add(ctx, 1) - measure := glob.NewInt64Measure("test.measure") + measure := Must(glob).NewInt64Measure("test.measure") boundM := measure.Bind(labels1) boundM.Record(ctx, 1) boundM.Record(ctx, 2) @@ -195,14 +196,14 @@ func TestUnbind(t *testing.T) { lvals1 := key.New("A").String("B") labels1 := glob.Labels(lvals1) - counter := glob.NewFloat64Counter("test.counter") + counter := Must(glob).NewFloat64Counter("test.counter") boundC := counter.Bind(labels1) - measure := glob.NewInt64Measure("test.measure") + measure := Must(glob).NewInt64Measure("test.measure") boundM := measure.Bind(labels1) - observerInt := glob.RegisterInt64Observer("test.observer.int", nil) - observerFloat := glob.RegisterFloat64Observer("test.observer.float", nil) + observerInt := Must(glob).RegisterInt64Observer("test.observer.int", nil) + observerFloat := Must(glob).RegisterFloat64Observer("test.observer.float", nil) boundC.Unbind() boundM.Unbind() @@ -218,7 +219,7 @@ func TestDefaultSDK(t *testing.T) { lvals1 := key.String("A", "B") labels1 := meter1.Labels(lvals1) - counter := meter1.NewInt64Counter("test.builtin") + counter := Must(meter1).NewInt64Counter("test.builtin") counter.Add(ctx, 1, labels1) counter.Add(ctx, 1, labels1) @@ -251,8 +252,9 @@ func TestUnbindThenRecordOne(t *testing.T) { ctx := context.Background() sdk := metrictest.NewProvider() + meter := global.Meter("test") - counter := meter.NewInt64Counter("test.counter") + counter := Must(meter).NewInt64Counter("test.counter") boundC := counter.Bind(meter.Labels()) global.SetMeterProvider(sdk) boundC.Unbind() @@ -263,3 +265,38 @@ func TestUnbindThenRecordOne(t *testing.T) { mock := global.Meter("test").(*metrictest.Meter) require.Equal(t, 0, len(mock.MeasurementBatches)) } + +type meterProviderWithConstructorError struct { + metric.Provider +} + +type meterWithConstructorError struct { + metric.Meter +} + +func (m *meterProviderWithConstructorError) Meter(name string) metric.Meter { + return &meterWithConstructorError{m.Provider.Meter(name)} +} + +func (m *meterWithConstructorError) NewInt64Counter(name string, cos ...metric.CounterOptionApplier) (metric.Int64Counter, error) { + return metric.Int64Counter{}, errors.New("constructor error") +} + +func TestErrorInDeferredConstructor(t *testing.T) { + internal.ResetForTest() + + ctx := context.Background() + meter := global.MeterProvider().Meter("builtin") + + c1 := Must(meter).NewInt64Counter("test") + c2 := Must(meter).NewInt64Counter("test") + + sdk := &meterProviderWithConstructorError{metrictest.NewProvider()} + + require.Panics(t, func() { + global.SetMeterProvider(sdk) + }) + + c1.Add(ctx, 1, meter.Labels()) + c2.Add(ctx, 2, meter.Labels()) +} diff --git a/api/metric/api.go b/api/metric/api.go index 550909795..b1b105c79 100644 --- a/api/metric/api.go +++ b/api/metric/api.go @@ -108,30 +108,36 @@ type Meter interface { // be read by the application. Labels(...core.KeyValue) LabelSet + // RecordBatch atomically records a batch of measurements. + RecordBatch(context.Context, LabelSet, ...Measurement) + + // All instrument constructors may return an error for + // conditions such as: + // `name` is an empty string + // `name` was previously registered as a different kind of instrument + // for a given named `Meter`. + // NewInt64Counter creates a new integral counter with a given // name and customized with passed options. - NewInt64Counter(name string, cos ...CounterOptionApplier) Int64Counter + NewInt64Counter(name string, cos ...CounterOptionApplier) (Int64Counter, error) // NewFloat64Counter creates a new floating point counter with // a given name and customized with passed options. - NewFloat64Counter(name string, cos ...CounterOptionApplier) Float64Counter + NewFloat64Counter(name string, cos ...CounterOptionApplier) (Float64Counter, error) // NewInt64Measure creates a new integral measure with a given // name and customized with passed options. - NewInt64Measure(name string, mos ...MeasureOptionApplier) Int64Measure + NewInt64Measure(name string, mos ...MeasureOptionApplier) (Int64Measure, error) // NewFloat64Measure creates a new floating point measure with // a given name and customized with passed options. - NewFloat64Measure(name string, mos ...MeasureOptionApplier) Float64Measure + NewFloat64Measure(name string, mos ...MeasureOptionApplier) (Float64Measure, error) // RegisterInt64Observer creates a new integral observer with a // given name, running a given callback, and customized with passed // options. Callback can be nil. - RegisterInt64Observer(name string, callback Int64ObserverCallback, oos ...ObserverOptionApplier) Int64Observer + RegisterInt64Observer(name string, callback Int64ObserverCallback, oos ...ObserverOptionApplier) (Int64Observer, error) // RegisterFloat64Observer creates a new floating point observer // with a given name, running a given callback, and customized with // passed options. Callback can be nil. - RegisterFloat64Observer(name string, callback Float64ObserverCallback, oos ...ObserverOptionApplier) Float64Observer - - // RecordBatch atomically records a batch of measurements. - RecordBatch(context.Context, LabelSet, ...Measurement) + RegisterFloat64Observer(name string, callback Float64ObserverCallback, oos ...ObserverOptionApplier) (Float64Observer, error) } // Int64ObserverResult is an interface for reporting integral diff --git a/api/metric/api_test.go b/api/metric/api_test.go index 6b93e1467..68ba16ce1 100644 --- a/api/metric/api_test.go +++ b/api/metric/api_test.go @@ -16,6 +16,7 @@ package metric_test import ( "context" + "errors" "fmt" "testing" @@ -27,8 +28,11 @@ import ( "github.com/google/go-cmp/cmp" "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" ) +var Must = metric.Must + func TestCounterOptions(t *testing.T) { type testcase struct { name string @@ -371,7 +375,7 @@ func checkOptions(t *testing.T, got *metric.Options, expected *metric.Options) { func TestCounter(t *testing.T) { { meter := mock.NewMeter() - c := meter.NewFloat64Counter("test.counter.float") + c := Must(meter).NewFloat64Counter("test.counter.float") ctx := context.Background() labels := meter.Labels() c.Add(ctx, 42, labels) @@ -383,7 +387,7 @@ func TestCounter(t *testing.T) { } { meter := mock.NewMeter() - c := meter.NewInt64Counter("test.counter.int") + c := Must(meter).NewInt64Counter("test.counter.int") ctx := context.Background() labels := meter.Labels() c.Add(ctx, 42, labels) @@ -398,7 +402,7 @@ func TestCounter(t *testing.T) { func TestMeasure(t *testing.T) { { meter := mock.NewMeter() - m := meter.NewFloat64Measure("test.measure.float") + m := Must(meter).NewFloat64Measure("test.measure.float") ctx := context.Background() labels := meter.Labels() m.Record(ctx, 42, labels) @@ -410,7 +414,7 @@ func TestMeasure(t *testing.T) { } { meter := mock.NewMeter() - m := meter.NewInt64Measure("test.measure.int") + m := Must(meter).NewInt64Measure("test.measure.int") ctx := context.Background() labels := meter.Labels() m.Record(ctx, 42, labels) @@ -426,7 +430,7 @@ func TestObserver(t *testing.T) { { meter := mock.NewMeter() labels := meter.Labels() - o := meter.RegisterFloat64Observer("test.observer.float", func(result metric.Float64ObserverResult) { + o := Must(meter).RegisterFloat64Observer("test.observer.float", func(result metric.Float64ObserverResult) { result.Observe(42, labels) }) t.Log("Testing float observer") @@ -436,7 +440,7 @@ func TestObserver(t *testing.T) { { meter := mock.NewMeter() labels := meter.Labels() - o := meter.RegisterInt64Observer("test.observer.int", func(result metric.Int64ObserverResult) { + o := Must(meter).RegisterInt64Observer("test.observer.int", func(result metric.Int64ObserverResult) { result.Observe(42, labels) }) t.Log("Testing int observer") @@ -527,3 +531,37 @@ func fortyTwo(t *testing.T, kind core.NumberKind) core.Number { t.Errorf("Invalid value kind %q", kind) return core.NewInt64Number(0) } + +type testWrappedInst struct{} + +func (*testWrappedInst) Bind(labels metric.LabelSet) metric.BoundInstrumentImpl { + panic("Not called") +} + +func (*testWrappedInst) RecordOne(ctx context.Context, number core.Number, labels metric.LabelSet) { + panic("Not called") +} + +func TestWrappedInstrumentError(t *testing.T) { + i0 := &testWrappedInst{} + e0 := errors.New("Test wrap error") + inst, err := metric.WrapInt64MeasureInstrument(i0, e0) + + // Check that error passes through w/o modifying instrument. + require.Equal(t, inst.Impl().(*testWrappedInst), i0) + require.Equal(t, err, e0) + + // Check that nil instrument is handled. + inst, err = metric.WrapInt64MeasureInstrument(nil, e0) + + require.Equal(t, err, e0) + 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, inst) + require.NotNil(t, inst.Impl()) +} diff --git a/api/metric/common.go b/api/metric/common.go index 4e5be4f3b..27d4e748b 100644 --- a/api/metric/common.go +++ b/api/metric/common.go @@ -16,6 +16,7 @@ package metric import ( "context" + "errors" "go.opentelemetry.io/otel/api/core" ) @@ -28,6 +29,8 @@ type commonBoundInstrument struct { boundInstrument BoundInstrumentImpl } +var ErrSDKReturnedNilImpl = errors.New("SDK returned a nil implementation") + func (m commonMetric) bind(labels LabelSet) commonBoundInstrument { return newCommonBoundInstrument(m.instrument.Bind(labels)) } @@ -56,10 +59,21 @@ func (h commonBoundInstrument) Unbind() { h.boundInstrument.Unbind() } -func newCommonMetric(instrument InstrumentImpl) commonMetric { +func newCommonMetric(instrument InstrumentImpl, err error) (commonMetric, error) { + if instrument == nil { + if err == nil { + err = ErrSDKReturnedNilImpl + } + // Note: an alternate behavior would be to synthesize a new name + // or group all duplicately-named instruments of a certain type + // together and use a tag for the original name, e.g., + // name = 'invalid.counter.int64' + // label = 'original-name=duplicate-counter-name' + instrument = noopInstrument{} + } return commonMetric{ instrument: instrument, - } + }, err } func newCommonBoundInstrument(boundInstrument BoundInstrumentImpl) commonBoundInstrument { diff --git a/api/metric/must.go b/api/metric/must.go new file mode 100644 index 000000000..083f023d8 --- /dev/null +++ b/api/metric/must.go @@ -0,0 +1,88 @@ +// 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 + +// MeterMust is a wrapper for Meter interfaces that panics when any +// instrument constructor encounters an error. +type MeterMust struct { + meter Meter +} + +// Must constructs a MeterMust implementation from a Meter, allowing +// the application to panic when any instrument constructor yields an +// error. +func Must(meter Meter) MeterMust { + return MeterMust{meter: meter} +} + +// NewInt64Counter calls `Meter.NewInt64Counter` and returns the +// instrument, panicking if it encounters an error. +func (mm MeterMust) NewInt64Counter(name string, cos ...CounterOptionApplier) Int64Counter { + if inst, err := mm.meter.NewInt64Counter(name, cos...); err != nil { + panic(err) + } else { + return inst + } +} + +// NewFloat64Counter calls `Meter.NewFloat64Counter` and returns the +// instrument, panicking if it encounters an error. +func (mm MeterMust) NewFloat64Counter(name string, cos ...CounterOptionApplier) Float64Counter { + if inst, err := mm.meter.NewFloat64Counter(name, cos...); err != nil { + panic(err) + } else { + return inst + } +} + +// NewInt64Measure calls `Meter.NewInt64Measure` and returns the +// instrument, panicking if it encounters an error. +func (mm MeterMust) NewInt64Measure(name string, mos ...MeasureOptionApplier) Int64Measure { + if inst, err := mm.meter.NewInt64Measure(name, mos...); err != nil { + panic(err) + } else { + return inst + } +} + +// NewFloat64Measure calls `Meter.NewFloat64Measure` and returns the +// instrument, panicking if it encounters an error. +func (mm MeterMust) NewFloat64Measure(name string, mos ...MeasureOptionApplier) Float64Measure { + if inst, err := mm.meter.NewFloat64Measure(name, mos...); err != nil { + panic(err) + } else { + return inst + } +} + +// RegisterInt64Observer calls `Meter.RegisterInt64Observer` and +// returns the instrument, panicking if it encounters an error. +func (mm MeterMust) RegisterInt64Observer(name string, callback Int64ObserverCallback, oos ...ObserverOptionApplier) Int64Observer { + if inst, err := mm.meter.RegisterInt64Observer(name, callback, oos...); err != nil { + panic(err) + } else { + return inst + } +} + +// RegisterFloat64Observer calls `Meter.RegisterFloat64Observer` and +// returns the instrument, panicking if it encounters an error. +func (mm MeterMust) RegisterFloat64Observer(name string, callback Float64ObserverCallback, oos ...ObserverOptionApplier) Float64Observer { + if inst, err := mm.meter.RegisterFloat64Observer(name, callback, oos...); err != nil { + panic(err) + } else { + return inst + } +} diff --git a/api/metric/noop.go b/api/metric/noop.go index 2a4498889..085429a51 100644 --- a/api/metric/noop.go +++ b/api/metric/noop.go @@ -7,7 +7,10 @@ import ( ) type NoopProvider struct{} -type NoopMeter struct{} + +type NoopMeter struct { +} + type noopBoundInstrument struct{} type noopLabelSet struct{} type noopInstrument struct{} @@ -53,29 +56,29 @@ func (NoopMeter) Labels(...core.KeyValue) LabelSet { return noopLabelSet{} } -func (NoopMeter) NewInt64Counter(name string, cos ...CounterOptionApplier) Int64Counter { - return WrapInt64CounterInstrument(noopInstrument{}) -} - -func (NoopMeter) NewFloat64Counter(name string, cos ...CounterOptionApplier) Float64Counter { - return WrapFloat64CounterInstrument(noopInstrument{}) -} - -func (NoopMeter) NewInt64Measure(name string, mos ...MeasureOptionApplier) Int64Measure { - return WrapInt64MeasureInstrument(noopInstrument{}) -} - -func (NoopMeter) NewFloat64Measure(name string, mos ...MeasureOptionApplier) Float64Measure { - return WrapFloat64MeasureInstrument(noopInstrument{}) -} - func (NoopMeter) RecordBatch(context.Context, LabelSet, ...Measurement) { } -func (NoopMeter) RegisterInt64Observer(name string, callback Int64ObserverCallback, oos ...ObserverOptionApplier) Int64Observer { - return noopInt64Observer{} +func (NoopMeter) NewInt64Counter(name string, cos ...CounterOptionApplier) (Int64Counter, error) { + return WrapInt64CounterInstrument(noopInstrument{}, nil) } -func (NoopMeter) RegisterFloat64Observer(name string, callback Float64ObserverCallback, oos ...ObserverOptionApplier) Float64Observer { - return noopFloat64Observer{} +func (NoopMeter) NewFloat64Counter(name string, cos ...CounterOptionApplier) (Float64Counter, error) { + return WrapFloat64CounterInstrument(noopInstrument{}, nil) +} + +func (NoopMeter) NewInt64Measure(name string, mos ...MeasureOptionApplier) (Int64Measure, error) { + return WrapInt64MeasureInstrument(noopInstrument{}, nil) +} + +func (NoopMeter) NewFloat64Measure(name string, mos ...MeasureOptionApplier) (Float64Measure, error) { + return WrapFloat64MeasureInstrument(noopInstrument{}, nil) +} + +func (NoopMeter) RegisterInt64Observer(name string, callback Int64ObserverCallback, oos ...ObserverOptionApplier) (Int64Observer, error) { + return noopInt64Observer{}, nil +} + +func (NoopMeter) RegisterFloat64Observer(name string, callback Float64ObserverCallback, oos ...ObserverOptionApplier) (Float64Observer, error) { + return noopFloat64Observer{}, nil } diff --git a/api/metric/sdkhelpers.go b/api/metric/sdkhelpers.go index d9c4eb070..885de5ebb 100644 --- a/api/metric/sdkhelpers.go +++ b/api/metric/sdkhelpers.go @@ -55,32 +55,36 @@ type BoundInstrumentImpl interface { // wrapper as an integral counter. // // It is mostly intended for SDKs. -func WrapInt64CounterInstrument(instrument InstrumentImpl) Int64Counter { - return Int64Counter{commonMetric: newCommonMetric(instrument)} +func WrapInt64CounterInstrument(instrument InstrumentImpl, err error) (Int64Counter, error) { + common, err := newCommonMetric(instrument, err) + return Int64Counter{commonMetric: common}, err } // WrapFloat64CounterInstrument wraps the instrument in the type-safe // wrapper as an floating point counter. // // It is mostly intended for SDKs. -func WrapFloat64CounterInstrument(instrument InstrumentImpl) Float64Counter { - return Float64Counter{commonMetric: newCommonMetric(instrument)} +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 // wrapper as an integral measure. // // It is mostly intended for SDKs. -func WrapInt64MeasureInstrument(instrument InstrumentImpl) Int64Measure { - return Int64Measure{commonMetric: newCommonMetric(instrument)} +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 // wrapper as an floating point measure. // // It is mostly intended for SDKs. -func WrapFloat64MeasureInstrument(instrument InstrumentImpl) Float64Measure { - return Float64Measure{commonMetric: newCommonMetric(instrument)} +func WrapFloat64MeasureInstrument(instrument InstrumentImpl, err error) (Float64Measure, error) { + common, err := newCommonMetric(instrument, err) + return Float64Measure{commonMetric: common}, err } // ApplyCounterOptions is a helper that applies all the counter diff --git a/example/basic/main.go b/example/basic/main.go index e9f89361f..b5dffdc24 100644 --- a/example/basic/main.go +++ b/example/basic/main.go @@ -67,9 +67,6 @@ func main() { defer initMeter().Stop() initTracer() - // Note: Have to get the meter and tracer after the global is - // initialized. See OTEP 0005. - tracer := global.Tracer("ex.com/basic") meter := global.Meter("ex.com/basic") @@ -78,13 +75,13 @@ func main() { oneMetricCB := func(result metric.Float64ObserverResult) { result.Observe(1, commonLabels) } - oneMetric := meter.RegisterFloat64Observer("ex.com.one", oneMetricCB, + oneMetric := metric.Must(meter).RegisterFloat64Observer("ex.com.one", oneMetricCB, metric.WithKeys(fooKey, barKey, lemonsKey), metric.WithDescription("An observer set to 1.0"), ) defer oneMetric.Unregister() - measureTwo := meter.NewFloat64Measure("ex.com.two") + measureTwo := metric.Must(meter).NewFloat64Measure("ex.com.two") ctx := context.Background() diff --git a/example/prometheus/main.go b/example/prometheus/main.go index b71316299..fe82f19c9 100644 --- a/example/prometheus/main.go +++ b/example/prometheus/main.go @@ -61,14 +61,14 @@ func main() { (*observerLock).RUnlock() result.Observe(value, labelset) } - oneMetric := meter.RegisterFloat64Observer("ex.com.one", cb, + oneMetric := metric.Must(meter).RegisterFloat64Observer("ex.com.one", cb, metric.WithKeys(fooKey, barKey, lemonsKey), metric.WithDescription("A measure set to 1.0"), ) defer oneMetric.Unregister() - measureTwo := meter.NewFloat64Measure("ex.com.two", metric.WithKeys(key.New("A"))) - measureThree := meter.NewFloat64Counter("ex.com.three") + measureTwo := metric.Must(meter).NewFloat64Measure("ex.com.two", metric.WithKeys(key.New("A"))) + measureThree := metric.Must(meter).NewFloat64Counter("ex.com.three") commonLabels := meter.Labels(lemonsKey.Int(10), key.String("A", "1"), key.String("B", "2"), key.String("C", "3")) notSoCommonLabels := meter.Labels(lemonsKey.Int(13)) diff --git a/exporters/metric/dogstatsd/example_test.go b/exporters/metric/dogstatsd/example_test.go index fcbfbc349..8222e5949 100644 --- a/exporters/metric/dogstatsd/example_test.go +++ b/exporters/metric/dogstatsd/example_test.go @@ -59,7 +59,7 @@ func ExampleNew() { meter := pusher.Meter("example") // Create and update a single counter: - counter := meter.NewInt64Counter("a.counter", metric.WithKeys(key)) + counter := metric.Must(meter).NewInt64Counter("a.counter", metric.WithKeys(key)) labels := meter.Labels(key.String("value")) counter.Add(ctx, 100, labels) diff --git a/exporters/metric/stdout/example_test.go b/exporters/metric/stdout/example_test.go index 74a61cb5f..d9405dd4b 100644 --- a/exporters/metric/stdout/example_test.go +++ b/exporters/metric/stdout/example_test.go @@ -27,7 +27,7 @@ func ExampleNewExportPipeline() { meter := pusher.Meter("example") // Create and update a single counter: - counter := meter.NewInt64Counter("a.counter", metric.WithKeys(key)) + counter := metric.Must(meter).NewInt64Counter("a.counter", metric.WithKeys(key)) labels := meter.Labels(key.String("value")) counter.Add(ctx, 100, labels) diff --git a/internal/metric/mock.go b/internal/metric/mock.go index 578fb2480..22f625823 100644 --- a/internal/metric/mock.go +++ b/internal/metric/mock.go @@ -49,7 +49,7 @@ type ( MeterProvider struct { lock sync.Mutex - registered map[string]*Meter + registered map[string]apimetric.Meter } Meter struct { @@ -154,13 +154,9 @@ func doRecordBatch(ctx context.Context, labelSet *LabelSet, instrument *Instrume }) } -func (s *LabelSet) Meter() apimetric.Meter { - return s.TheMeter -} - func NewProvider() *MeterProvider { return &MeterProvider{ - registered: map[string]*Meter{}, + registered: map[string]apimetric.Meter{}, } } @@ -191,14 +187,14 @@ func (m *Meter) Labels(labels ...core.KeyValue) apimetric.LabelSet { } } -func (m *Meter) NewInt64Counter(name string, cos ...apimetric.CounterOptionApplier) apimetric.Int64Counter { +func (m *Meter) NewInt64Counter(name string, cos ...apimetric.CounterOptionApplier) (apimetric.Int64Counter, error) { instrument := m.newCounterInstrument(name, core.Int64NumberKind, cos...) - return apimetric.WrapInt64CounterInstrument(instrument) + return apimetric.WrapInt64CounterInstrument(instrument, nil) } -func (m *Meter) NewFloat64Counter(name string, cos ...apimetric.CounterOptionApplier) apimetric.Float64Counter { +func (m *Meter) NewFloat64Counter(name string, cos ...apimetric.CounterOptionApplier) (apimetric.Float64Counter, error) { instrument := m.newCounterInstrument(name, core.Float64NumberKind, cos...) - return apimetric.WrapFloat64CounterInstrument(instrument) + return apimetric.WrapFloat64CounterInstrument(instrument, nil) } func (m *Meter) newCounterInstrument(name string, numberKind core.NumberKind, cos ...apimetric.CounterOptionApplier) *Instrument { @@ -212,14 +208,14 @@ func (m *Meter) newCounterInstrument(name string, numberKind core.NumberKind, co } } -func (m *Meter) NewInt64Measure(name string, mos ...apimetric.MeasureOptionApplier) apimetric.Int64Measure { +func (m *Meter) NewInt64Measure(name string, mos ...apimetric.MeasureOptionApplier) (apimetric.Int64Measure, error) { instrument := m.newMeasureInstrument(name, core.Int64NumberKind, mos...) - return apimetric.WrapInt64MeasureInstrument(instrument) + return apimetric.WrapInt64MeasureInstrument(instrument, nil) } -func (m *Meter) NewFloat64Measure(name string, mos ...apimetric.MeasureOptionApplier) apimetric.Float64Measure { +func (m *Meter) NewFloat64Measure(name string, mos ...apimetric.MeasureOptionApplier) (apimetric.Float64Measure, error) { instrument := m.newMeasureInstrument(name, core.Float64NumberKind, mos...) - return apimetric.WrapFloat64MeasureInstrument(instrument) + return apimetric.WrapFloat64MeasureInstrument(instrument, nil) } func (m *Meter) newMeasureInstrument(name string, numberKind core.NumberKind, mos ...apimetric.MeasureOptionApplier) *Instrument { @@ -233,9 +229,9 @@ func (m *Meter) newMeasureInstrument(name string, numberKind core.NumberKind, mo } } -func (m *Meter) RegisterInt64Observer(name string, callback apimetric.Int64ObserverCallback, oos ...apimetric.ObserverOptionApplier) apimetric.Int64Observer { +func (m *Meter) RegisterInt64Observer(name string, callback apimetric.Int64ObserverCallback, oos ...apimetric.ObserverOptionApplier) (apimetric.Int64Observer, error) { wrappedCallback := wrapInt64ObserverCallback(callback) - return m.newObserver(name, wrappedCallback, core.Int64NumberKind, oos...) + return m.newObserver(name, wrappedCallback, core.Int64NumberKind, oos...), nil } func wrapInt64ObserverCallback(callback apimetric.Int64ObserverCallback) observerCallback { @@ -250,9 +246,9 @@ func wrapInt64ObserverCallback(callback apimetric.Int64ObserverCallback) observe } } -func (m *Meter) RegisterFloat64Observer(name string, callback apimetric.Float64ObserverCallback, oos ...apimetric.ObserverOptionApplier) apimetric.Float64Observer { +func (m *Meter) RegisterFloat64Observer(name string, callback apimetric.Float64ObserverCallback, oos ...apimetric.ObserverOptionApplier) (apimetric.Float64Observer, error) { wrappedCallback := wrapFloat64ObserverCallback(callback) - return m.newObserver(name, wrappedCallback, core.Float64NumberKind, oos...) + return m.newObserver(name, wrappedCallback, core.Float64NumberKind, oos...), nil } func wrapFloat64ObserverCallback(callback apimetric.Float64ObserverCallback) observerCallback { diff --git a/sdk/metric/benchmark_test.go b/sdk/metric/benchmark_test.go index 558832aa5..772c92b84 100644 --- a/sdk/metric/benchmark_test.go +++ b/sdk/metric/benchmark_test.go @@ -33,8 +33,9 @@ import ( ) type benchFixture struct { - sdk *sdk.SDK - B *testing.B + meter metric.MeterMust + sdk *sdk.SDK + B *testing.B } func newFixture(b *testing.B) *benchFixture { @@ -43,6 +44,7 @@ func newFixture(b *testing.B) *benchFixture { B: b, } bf.sdk = sdk.New(bf, sdk.NewDefaultLabelEncoder()) + bf.meter = metric.Must(bf.sdk) return bf } @@ -140,7 +142,7 @@ func BenchmarkLabels_16(b *testing.B) { func BenchmarkAcquireNewHandle(b *testing.B) { fix := newFixture(b) labelSets := makeLabelSets(b.N) - cnt := fix.sdk.NewInt64Counter("int64.counter") + cnt := fix.meter.NewInt64Counter("int64.counter") labels := make([]metric.LabelSet, b.N) for i := 0; i < b.N; i++ { @@ -157,7 +159,7 @@ func BenchmarkAcquireNewHandle(b *testing.B) { func BenchmarkAcquireExistingHandle(b *testing.B) { fix := newFixture(b) labelSets := makeLabelSets(b.N) - cnt := fix.sdk.NewInt64Counter("int64.counter") + cnt := fix.meter.NewInt64Counter("int64.counter") labels := make([]metric.LabelSet, b.N) for i := 0; i < b.N; i++ { @@ -175,7 +177,7 @@ func BenchmarkAcquireExistingHandle(b *testing.B) { func BenchmarkAcquireReleaseExistingHandle(b *testing.B) { fix := newFixture(b) labelSets := makeLabelSets(b.N) - cnt := fix.sdk.NewInt64Counter("int64.counter") + cnt := fix.meter.NewInt64Counter("int64.counter") labels := make([]metric.LabelSet, b.N) for i := 0; i < b.N; i++ { @@ -196,7 +198,7 @@ func BenchmarkInt64CounterAdd(b *testing.B) { ctx := context.Background() fix := newFixture(b) labs := fix.sdk.Labels(makeLabels(1)...) - cnt := fix.sdk.NewInt64Counter("int64.counter") + cnt := fix.meter.NewInt64Counter("int64.counter") b.ResetTimer() @@ -209,7 +211,7 @@ func BenchmarkInt64CounterHandleAdd(b *testing.B) { ctx := context.Background() fix := newFixture(b) labs := fix.sdk.Labels(makeLabels(1)...) - cnt := fix.sdk.NewInt64Counter("int64.counter") + cnt := fix.meter.NewInt64Counter("int64.counter") handle := cnt.Bind(labs) b.ResetTimer() @@ -223,7 +225,7 @@ func BenchmarkFloat64CounterAdd(b *testing.B) { ctx := context.Background() fix := newFixture(b) labs := fix.sdk.Labels(makeLabels(1)...) - cnt := fix.sdk.NewFloat64Counter("float64.counter") + cnt := fix.meter.NewFloat64Counter("float64.counter") b.ResetTimer() @@ -236,7 +238,7 @@ func BenchmarkFloat64CounterHandleAdd(b *testing.B) { ctx := context.Background() fix := newFixture(b) labs := fix.sdk.Labels(makeLabels(1)...) - cnt := fix.sdk.NewFloat64Counter("float64.counter") + cnt := fix.meter.NewFloat64Counter("float64.counter") handle := cnt.Bind(labs) b.ResetTimer() @@ -252,7 +254,7 @@ func BenchmarkInt64LastValueAdd(b *testing.B) { ctx := context.Background() fix := newFixture(b) labs := fix.sdk.Labels(makeLabels(1)...) - mea := fix.sdk.NewInt64Measure("int64.lastvalue") + mea := fix.meter.NewInt64Measure("int64.lastvalue") b.ResetTimer() @@ -265,7 +267,7 @@ func BenchmarkInt64LastValueHandleAdd(b *testing.B) { ctx := context.Background() fix := newFixture(b) labs := fix.sdk.Labels(makeLabels(1)...) - mea := fix.sdk.NewInt64Measure("int64.lastvalue") + mea := fix.meter.NewInt64Measure("int64.lastvalue") handle := mea.Bind(labs) b.ResetTimer() @@ -279,7 +281,7 @@ func BenchmarkFloat64LastValueAdd(b *testing.B) { ctx := context.Background() fix := newFixture(b) labs := fix.sdk.Labels(makeLabels(1)...) - mea := fix.sdk.NewFloat64Measure("float64.lastvalue") + mea := fix.meter.NewFloat64Measure("float64.lastvalue") b.ResetTimer() @@ -292,7 +294,7 @@ func BenchmarkFloat64LastValueHandleAdd(b *testing.B) { ctx := context.Background() fix := newFixture(b) labs := fix.sdk.Labels(makeLabels(1)...) - mea := fix.sdk.NewFloat64Measure("float64.lastvalue") + mea := fix.meter.NewFloat64Measure("float64.lastvalue") handle := mea.Bind(labs) b.ResetTimer() @@ -308,7 +310,7 @@ func benchmarkInt64MeasureAdd(b *testing.B, name string) { ctx := context.Background() fix := newFixture(b) labs := fix.sdk.Labels(makeLabels(1)...) - mea := fix.sdk.NewInt64Measure(name) + mea := fix.meter.NewInt64Measure(name) b.ResetTimer() @@ -321,7 +323,7 @@ func benchmarkInt64MeasureHandleAdd(b *testing.B, name string) { ctx := context.Background() fix := newFixture(b) labs := fix.sdk.Labels(makeLabels(1)...) - mea := fix.sdk.NewInt64Measure(name) + mea := fix.meter.NewInt64Measure(name) handle := mea.Bind(labs) b.ResetTimer() @@ -335,7 +337,7 @@ func benchmarkFloat64MeasureAdd(b *testing.B, name string) { ctx := context.Background() fix := newFixture(b) labs := fix.sdk.Labels(makeLabels(1)...) - mea := fix.sdk.NewFloat64Measure(name) + mea := fix.meter.NewFloat64Measure(name) b.ResetTimer() @@ -348,7 +350,7 @@ func benchmarkFloat64MeasureHandleAdd(b *testing.B, name string) { ctx := context.Background() fix := newFixture(b) labs := fix.sdk.Labels(makeLabels(1)...) - mea := fix.sdk.NewFloat64Measure(name) + mea := fix.meter.NewFloat64Measure(name) handle := mea.Bind(labs) b.ResetTimer() @@ -371,7 +373,7 @@ func BenchmarkObserverRegistration(b *testing.B) { b.ResetTimer() for i := 0; i < b.N; i++ { - fix.sdk.RegisterInt64Observer(names[i], cb) + fix.meter.RegisterInt64Observer(names[i], cb) } } @@ -386,7 +388,7 @@ func BenchmarkObserverRegistrationUnregistration(b *testing.B) { b.ResetTimer() for i := 0; i < b.N; i++ { - fix.sdk.RegisterInt64Observer(names[i], cb).Unregister() + fix.meter.RegisterInt64Observer(names[i], cb).Unregister() } } @@ -402,7 +404,7 @@ func BenchmarkObserverRegistrationUnregistrationBatched(b *testing.B) { b.ResetTimer() for i := 0; i < b.N; i++ { - observers = append(observers, fix.sdk.RegisterInt64Observer(names[i], cb)) + observers = append(observers, fix.meter.RegisterInt64Observer(names[i], cb)) } for i := 0; i < b.N; i++ { observers[i].Unregister() @@ -413,7 +415,7 @@ func BenchmarkObserverObservationInt64(b *testing.B) { ctx := context.Background() fix := newFixture(b) labs := fix.sdk.Labels(makeLabels(1)...) - _ = fix.sdk.RegisterInt64Observer("test.observer", func(result metric.Int64ObserverResult) { + _ = fix.meter.RegisterInt64Observer("test.observer", func(result metric.Int64ObserverResult) { b.StartTimer() defer b.StopTimer() for i := 0; i < b.N; i++ { @@ -429,7 +431,7 @@ func BenchmarkObserverObservationFloat64(b *testing.B) { ctx := context.Background() fix := newFixture(b) labs := fix.sdk.Labels(makeLabels(1)...) - _ = fix.sdk.RegisterFloat64Observer("test.observer", func(result metric.Float64ObserverResult) { + _ = fix.meter.RegisterFloat64Observer("test.observer", func(result metric.Float64ObserverResult) { b.StartTimer() defer b.StopTimer() for i := 0; i < b.N; i++ { diff --git a/sdk/metric/controller/push/push_test.go b/sdk/metric/controller/push/push_test.go index 77e7599fa..bae1e9e8e 100644 --- a/sdk/metric/controller/push/push_test.go +++ b/sdk/metric/controller/push/push_test.go @@ -25,6 +25,7 @@ import ( "github.com/benbjohnson/clock" "github.com/stretchr/testify/require" + "go.opentelemetry.io/otel/api/metric" "go.opentelemetry.io/otel/exporters/metric/test" export "go.opentelemetry.io/otel/sdk/export/metric" "go.opentelemetry.io/otel/sdk/export/metric/aggregator" @@ -178,7 +179,7 @@ func TestPushTicker(t *testing.T) { ctx := context.Background() - counter := meter.NewInt64Counter("counter") + counter := metric.Must(meter).NewInt64Counter("counter") p.Start() diff --git a/sdk/metric/correct_test.go b/sdk/metric/correct_test.go index 31691f552..e54819699 100644 --- a/sdk/metric/correct_test.go +++ b/sdk/metric/correct_test.go @@ -33,6 +33,8 @@ import ( "go.opentelemetry.io/otel/sdk/metric/aggregator/counter" ) +var Must = metric.Must + type correctnessBatcher struct { t *testing.T @@ -82,7 +84,7 @@ func TestInputRangeTestCounter(t *testing.T) { sdkErr = handleErr }) - counter := sdk.NewInt64Counter("name.counter", metric.WithMonotonic(true)) + counter := Must(sdk).NewInt64Counter("name.counter", metric.WithMonotonic(true)) counter.Add(ctx, -1, sdk.Labels()) require.Equal(t, aggregator.ErrNegativeInput, sdkErr) @@ -116,7 +118,7 @@ func TestInputRangeTestMeasure(t *testing.T) { sdkErr = handleErr }) - measure := sdk.NewFloat64Measure("name.measure", metric.WithAbsolute(true)) + measure := Must(sdk).NewFloat64Measure("name.measure", metric.WithAbsolute(true)) measure.Record(ctx, -1, sdk.Labels()) require.Equal(t, aggregator.ErrNegativeInput, sdkErr) @@ -147,7 +149,7 @@ func TestDisabledInstrument(t *testing.T) { t: t, } sdk := sdk.New(batcher, sdk.NewDefaultLabelEncoder()) - measure := sdk.NewFloat64Measure("name.disabled", metric.WithAbsolute(true)) + measure := Must(sdk).NewFloat64Measure("name.disabled", metric.WithAbsolute(true)) measure.Record(ctx, -1, sdk.Labels()) checkpointed := sdk.Collect(ctx) @@ -167,7 +169,7 @@ func TestRecordNaN(t *testing.T) { sdk.SetErrorHandler(func(handleErr error) { sdkErr = handleErr }) - c := sdk.NewFloat64Counter("counter.name") + c := Must(sdk).NewFloat64Counter("counter.name") require.Nil(t, sdkErr) c.Add(ctx, math.NaN(), sdk.Labels()) @@ -181,7 +183,7 @@ func TestSDKAltLabelEncoder(t *testing.T) { } sdk := sdk.New(batcher, testLabelEncoder{}) - measure := sdk.NewFloat64Measure("measure") + measure := Must(sdk).NewFloat64Measure("measure") measure.Record(ctx, 1, sdk.Labels(key.String("A", "B"), key.String("C", "D"))) sdk.Collect(ctx) @@ -199,7 +201,7 @@ func TestSDKLabelsDeduplication(t *testing.T) { } sdk := sdk.New(batcher, sdk.NewDefaultLabelEncoder()) - counter := sdk.NewInt64Counter("counter") + counter := Must(sdk).NewInt64Counter("counter") const ( maxKeys = 21 diff --git a/sdk/metric/example_test.go b/sdk/metric/example_test.go index 5bb04d870..76118be8b 100644 --- a/sdk/metric/example_test.go +++ b/sdk/metric/example_test.go @@ -39,7 +39,7 @@ func ExampleNew() { key := key.New("key") meter := pusher.Meter("example") - counter := meter.NewInt64Counter("a.counter", metric.WithKeys(key)) + counter := metric.Must(meter).NewInt64Counter("a.counter", metric.WithKeys(key)) labels := meter.Labels(key.String("value")) counter.Add(ctx, 100, labels) diff --git a/sdk/metric/sdk.go b/sdk/metric/sdk.go index 5a85ac703..5100f8268 100644 --- a/sdk/metric/sdk.go +++ b/sdk/metric/sdk.go @@ -492,23 +492,23 @@ func (m *SDK) newMeasureInstrument(name string, numberKind core.NumberKind, mos return m.newInstrument(name, export.MeasureKind, numberKind, &opts) } -func (m *SDK) NewInt64Counter(name string, cos ...api.CounterOptionApplier) api.Int64Counter { - return api.WrapInt64CounterInstrument(m.newCounterInstrument(name, core.Int64NumberKind, cos...)) +func (m *SDK) NewInt64Counter(name string, cos ...api.CounterOptionApplier) (api.Int64Counter, error) { + return api.WrapInt64CounterInstrument(m.newCounterInstrument(name, core.Int64NumberKind, cos...), nil) } -func (m *SDK) NewFloat64Counter(name string, cos ...api.CounterOptionApplier) api.Float64Counter { - return api.WrapFloat64CounterInstrument(m.newCounterInstrument(name, core.Float64NumberKind, cos...)) +func (m *SDK) NewFloat64Counter(name string, cos ...api.CounterOptionApplier) (api.Float64Counter, error) { + return api.WrapFloat64CounterInstrument(m.newCounterInstrument(name, core.Float64NumberKind, cos...), nil) } -func (m *SDK) NewInt64Measure(name string, mos ...api.MeasureOptionApplier) api.Int64Measure { - return api.WrapInt64MeasureInstrument(m.newMeasureInstrument(name, core.Int64NumberKind, mos...)) +func (m *SDK) NewInt64Measure(name string, mos ...api.MeasureOptionApplier) (api.Int64Measure, error) { + return api.WrapInt64MeasureInstrument(m.newMeasureInstrument(name, core.Int64NumberKind, mos...), nil) } -func (m *SDK) NewFloat64Measure(name string, mos ...api.MeasureOptionApplier) api.Float64Measure { - return api.WrapFloat64MeasureInstrument(m.newMeasureInstrument(name, core.Float64NumberKind, mos...)) +func (m *SDK) NewFloat64Measure(name string, mos ...api.MeasureOptionApplier) (api.Float64Measure, error) { + return api.WrapFloat64MeasureInstrument(m.newMeasureInstrument(name, core.Float64NumberKind, mos...), nil) } -func (m *SDK) RegisterInt64Observer(name string, callback api.Int64ObserverCallback, oos ...api.ObserverOptionApplier) api.Int64Observer { +func (m *SDK) RegisterInt64Observer(name string, callback api.Int64ObserverCallback, oos ...api.ObserverOptionApplier) (api.Int64Observer, error) { if callback == nil { return api.NoopMeter{}.RegisterInt64Observer("", nil) } @@ -519,7 +519,7 @@ func (m *SDK) RegisterInt64Observer(name string, callback api.Int64ObserverCallb obs := m.newObserver(descriptor, cb) return int64Observer{ observer: obs, - } + }, nil } func wrapInt64ObserverCallback(callback api.Int64ObserverCallback) observerCallback { @@ -531,7 +531,7 @@ func wrapInt64ObserverCallback(callback api.Int64ObserverCallback) observerCallb } } -func (m *SDK) RegisterFloat64Observer(name string, callback api.Float64ObserverCallback, oos ...api.ObserverOptionApplier) api.Float64Observer { +func (m *SDK) RegisterFloat64Observer(name string, callback api.Float64ObserverCallback, oos ...api.ObserverOptionApplier) (api.Float64Observer, error) { if callback == nil { return api.NoopMeter{}.RegisterFloat64Observer("", nil) } @@ -542,7 +542,7 @@ func (m *SDK) RegisterFloat64Observer(name string, callback api.Float64ObserverC obs := m.newObserver(descriptor, cb) return float64Observer{ observer: obs, - } + }, nil } func wrapFloat64ObserverCallback(callback api.Float64ObserverCallback) observerCallback { diff --git a/sdk/metric/stress_test.go b/sdk/metric/stress_test.go index fd44241c5..8419e1454 100644 --- a/sdk/metric/stress_test.go +++ b/sdk/metric/stress_test.go @@ -339,7 +339,7 @@ func float64sEqual(a, b core.Number) bool { func intCounterTestImpl(nonMonotonic bool) testImpl { return testImpl{ newInstrument: func(meter api.Meter, name string) withImpl { - return meter.NewInt64Counter(name+".counter", api.WithMonotonic(!nonMonotonic)) + return Must(meter).NewInt64Counter(name+".counter", api.WithMonotonic(!nonMonotonic)) }, getUpdateValue: func() core.Number { var offset int64 @@ -385,7 +385,7 @@ func TestStressInt64CounterNonMonotonic(t *testing.T) { func floatCounterTestImpl(nonMonotonic bool) testImpl { return testImpl{ newInstrument: func(meter api.Meter, name string) withImpl { - return meter.NewFloat64Counter(name+".counter", api.WithMonotonic(!nonMonotonic)) + return Must(meter).NewFloat64Counter(name+".counter", api.WithMonotonic(!nonMonotonic)) }, getUpdateValue: func() core.Number { var offset float64 @@ -433,7 +433,7 @@ func TestStressFloat64CounterNonMonotonic(t *testing.T) { func intLastValueTestImpl() testImpl { return testImpl{ newInstrument: func(meter api.Meter, name string) withImpl { - return meter.NewInt64Measure(name+".lastvalue", metric.WithAbsolute(false)) + return Must(meter).NewInt64Measure(name+".lastvalue", metric.WithAbsolute(false)) }, getUpdateValue: func() core.Number { r1 := rand.Int63() @@ -475,7 +475,7 @@ func TestStressInt64LastValue(t *testing.T) { func floatLastValueTestImpl() testImpl { return testImpl{ newInstrument: func(meter api.Meter, name string) withImpl { - return meter.NewFloat64Measure(name+".lastvalue", metric.WithAbsolute(false)) + return Must(meter).NewFloat64Measure(name+".lastvalue", metric.WithAbsolute(false)) }, getUpdateValue: func() core.Number { return core.NewFloat64Number((-0.5 + rand.Float64()) * 100000)