diff --git a/.golangci.yml b/.golangci.yml index 0f099f575..e28904f6a 100644 --- a/.golangci.yml +++ b/.golangci.yml @@ -85,6 +85,8 @@ linters-settings: - "**/internal/matchers/*.go" godot: exclude: + # Exclude links. + - '^ *\[[^]]+\]:' # Exclude sentence fragments for lists. - '^[ ]*[-•]' # Exclude sentences prefixing a list. diff --git a/CHANGELOG.md b/CHANGELOG.md index 9877807a3..201c03b2a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,11 +10,14 @@ This project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.htm ### Added +- The `go.opentelemetry.io/otel/metric/embedded` package. (#3916) - The `Version` function to `go.opentelemetry.io/otel/sdk` to return the SDK version. (#3949) ### Changed - The `Extrema` in `go.opentelemetry.io/otel/sdk/metric/metricdata` is redefined with a generic argument of `[N int64 | float64]`. (#3870) +- Update all exported interfaces from `go.opentelemetry.io/otel/metric` to embed their corresponding interface from `go.opentelemetry.io/otel/metric/embedded`. + This adds an implementation requirement to set the interface default behavior for unimplemented methods. (#3916) - Move No-Op implementation from `go.opentelemetry.io/otel/metric` into its own package `go.opentelemetry.io/otel/metric/noop`. (#3941) - `metric.NewNoopMeterProvider` is replaced with `noop.NewMeterProvider` diff --git a/internal/global/instruments.go b/internal/global/instruments.go index ef1ed650b..8a1851878 100644 --- a/internal/global/instruments.go +++ b/internal/global/instruments.go @@ -20,6 +20,7 @@ import ( "go.opentelemetry.io/otel/attribute" "go.opentelemetry.io/otel/metric" + "go.opentelemetry.io/otel/metric/embedded" "go.opentelemetry.io/otel/metric/instrument" ) @@ -29,6 +30,7 @@ type unwrapper interface { } type afCounter struct { + embedded.Float64ObservableCounter instrument.Float64Observable name string @@ -57,6 +59,7 @@ func (i *afCounter) Unwrap() instrument.Observable { } type afUpDownCounter struct { + embedded.Float64ObservableUpDownCounter instrument.Float64Observable name string @@ -85,6 +88,7 @@ func (i *afUpDownCounter) Unwrap() instrument.Observable { } type afGauge struct { + embedded.Float64ObservableGauge instrument.Float64Observable name string @@ -113,6 +117,7 @@ func (i *afGauge) Unwrap() instrument.Observable { } type aiCounter struct { + embedded.Int64ObservableCounter instrument.Int64Observable name string @@ -141,6 +146,7 @@ func (i *aiCounter) Unwrap() instrument.Observable { } type aiUpDownCounter struct { + embedded.Int64ObservableUpDownCounter instrument.Int64Observable name string @@ -169,6 +175,7 @@ func (i *aiUpDownCounter) Unwrap() instrument.Observable { } type aiGauge struct { + embedded.Int64ObservableGauge instrument.Int64Observable name string @@ -198,6 +205,8 @@ func (i *aiGauge) Unwrap() instrument.Observable { // Sync Instruments. type sfCounter struct { + embedded.Float64Counter + name string opts []instrument.Float64CounterOption @@ -222,6 +231,8 @@ func (i *sfCounter) Add(ctx context.Context, incr float64, attrs ...attribute.Ke } type sfUpDownCounter struct { + embedded.Float64UpDownCounter + name string opts []instrument.Float64UpDownCounterOption @@ -246,6 +257,8 @@ func (i *sfUpDownCounter) Add(ctx context.Context, incr float64, attrs ...attrib } type sfHistogram struct { + embedded.Float64Histogram + name string opts []instrument.Float64HistogramOption @@ -270,6 +283,8 @@ func (i *sfHistogram) Record(ctx context.Context, x float64, attrs ...attribute. } type siCounter struct { + embedded.Int64Counter + name string opts []instrument.Int64CounterOption @@ -294,6 +309,8 @@ func (i *siCounter) Add(ctx context.Context, x int64, attrs ...attribute.KeyValu } type siUpDownCounter struct { + embedded.Int64UpDownCounter + name string opts []instrument.Int64UpDownCounterOption @@ -318,6 +335,8 @@ func (i *siUpDownCounter) Add(ctx context.Context, x int64, attrs ...attribute.K } type siHistogram struct { + embedded.Int64Histogram + name string opts []instrument.Int64HistogramOption diff --git a/internal/global/instruments_test.go b/internal/global/instruments_test.go index 66fe8499c..6b5af6715 100644 --- a/internal/global/instruments_test.go +++ b/internal/global/instruments_test.go @@ -20,6 +20,7 @@ import ( "go.opentelemetry.io/otel/attribute" "go.opentelemetry.io/otel/metric" + "go.opentelemetry.io/otel/metric/embedded" "go.opentelemetry.io/otel/metric/instrument" "go.opentelemetry.io/otel/metric/noop" ) @@ -146,6 +147,12 @@ type testCountingFloatInstrument struct { count int instrument.Float64Observable + embedded.Float64Counter + embedded.Float64UpDownCounter + embedded.Float64Histogram + embedded.Float64ObservableCounter + embedded.Float64ObservableUpDownCounter + embedded.Float64ObservableGauge } func (i *testCountingFloatInstrument) observe() { @@ -162,6 +169,12 @@ type testCountingIntInstrument struct { count int instrument.Int64Observable + embedded.Int64Counter + embedded.Int64UpDownCounter + embedded.Int64Histogram + embedded.Int64ObservableCounter + embedded.Int64ObservableUpDownCounter + embedded.Int64ObservableGauge } func (i *testCountingIntInstrument) observe() { diff --git a/internal/global/meter.go b/internal/global/meter.go index 2c2d4fb4f..9449ba3ee 100644 --- a/internal/global/meter.go +++ b/internal/global/meter.go @@ -20,6 +20,7 @@ import ( "sync/atomic" "go.opentelemetry.io/otel/metric" + "go.opentelemetry.io/otel/metric/embedded" "go.opentelemetry.io/otel/metric/instrument" ) @@ -28,6 +29,8 @@ import ( // All MeterProvider functionality is forwarded to a delegate once // configured. type meterProvider struct { + embedded.MeterProvider + mtx sync.Mutex meters map[il]*meter @@ -94,6 +97,8 @@ func (p *meterProvider) Meter(name string, opts ...metric.MeterOption) metric.Me // All Meter functionality is forwarded to a delegate once configured. // Otherwise, all functionality is forwarded to a NoopMeter. type meter struct { + embedded.Meter + name string opts []metric.MeterOption @@ -308,6 +313,8 @@ func unwrapInstruments(instruments []instrument.Observable) []instrument.Observa } type registration struct { + embedded.Registration + instruments []instrument.Observable function metric.Callback diff --git a/internal/global/meter_types_test.go b/internal/global/meter_types_test.go index 038083f27..3529d92db 100644 --- a/internal/global/meter_types_test.go +++ b/internal/global/meter_types_test.go @@ -19,10 +19,13 @@ import ( "go.opentelemetry.io/otel/attribute" "go.opentelemetry.io/otel/metric" + "go.opentelemetry.io/otel/metric/embedded" "go.opentelemetry.io/otel/metric/instrument" ) type testMeterProvider struct { + embedded.MeterProvider + count int } @@ -33,6 +36,8 @@ func (p *testMeterProvider) Meter(name string, opts ...metric.MeterOption) metri } type testMeter struct { + embedded.Meter + afCount int afUDCount int afGauge int @@ -123,6 +128,8 @@ func (m *testMeter) RegisterCallback(f metric.Callback, i ...instrument.Observab } type testReg struct { + embedded.Registration + f func() } @@ -134,7 +141,7 @@ func (r testReg) Unregister() error { // This enables async collection. func (m *testMeter) collect() { ctx := context.Background() - o := observationRecorder{ctx} + o := observationRecorder{ctx: ctx} for _, f := range m.callbacks { if f == nil { // Unregister. @@ -145,6 +152,8 @@ func (m *testMeter) collect() { } type observationRecorder struct { + embedded.Observer + ctx context.Context } diff --git a/metric/doc.go b/metric/doc.go index 33de12035..b24a9efdc 100644 --- a/metric/doc.go +++ b/metric/doc.go @@ -30,6 +30,69 @@ can then be built from the [Meter]'s instruments. See [go.opentelemetry.io/otel/metric/instrument] for documentation on each instrument and its intended use. +# API Implementations + +This package does not conform to the standard Go versioning policy, all of its +interfaces may have methods added to them without a package major version bump. +This non-standard API evolution could surprise an uninformed implementation +author. They could unknowingly build their implementation in a way that would +result in a runtime panic for their users that update to the new API. + +The API is designed to help inform an instrumentation author about this +non-standard API evolution. It requires them to choose a default behavior for +unimplemented interface methods. There are three behavior choices they can +make: + + - Compilation failure + - Panic + - Default to another implementation + +All interfaces in this API embed a corresponding interface from +[go.opentelemetry.io/otel/metric/embedded]. If an author wants the default +behavior of their implementations to be a compilation failure, signaling to +their users they need to update to the latest version of that implementation, +they need to embed the corresponding interface from +[go.opentelemetry.io/otel/metric/embedded] in their implementation. For +example, + + import "go.opentelemetry.io/otel/metric/embedded" + + type MeterProvider struct { + embedded.MeterProvider + // ... + } + +If an author wants the default behavior of their implementations to a panic, +they need to embed the API interface directly. + + import "go.opentelemetry.io/otel/metric" + + type MeterProvider struct { + metric.MeterProvider + // ... + } + +This is not a recommended behavior as it could lead to publishing packages that +contain runtime panics when users update other package that use newer versions +of [go.opentelemetry.io/otel/metric]. + +Finally, an author can embed another implementation in theirs. The embedded +implementation will be used for methods not defined by the author. For example, +an author who want to default to silently dropping the call can use +[go.opentelemetry.io/otel/metric/noop]: + + import "go.opentelemetry.io/otel/metric/noop" + + type MeterProvider struct { + noop.MeterProvider + // ... + } + +It is strongly recommended that authors only embed +[go.opentelemetry.io/otel/metric/noop] if they choose this default behavior. +That implementation is the only one OpenTelemetry authors can guarantee will +fully implement all the API interfaces when a user updates their API. + [GetMeterProvider]: https://pkg.go.dev/go.opentelemetry.io/otel#GetMeterProvider */ package metric // import "go.opentelemetry.io/otel/metric" diff --git a/metric/embedded/embedded.go b/metric/embedded/embedded.go new file mode 100644 index 000000000..a4f2bdfe4 --- /dev/null +++ b/metric/embedded/embedded.go @@ -0,0 +1,233 @@ +// Copyright The 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 embedded provides interfaces embedded within the [OpenTelemetry +// metric API]. +// +// Implementers of the [OpenTelemetry metric API] can embed the relevant type +// from this package into their implementation directly. Doing so will result +// in a compilation error for users when the [OpenTelemetry metric API] is +// extended (which is something that can happen without a major version bump of +// the API package). +// +// [OpenTelemetry metric API]: go.opentelemetry.io/otel/metric +package embedded // import "go.opentelemetry.io/otel/metric/embedded" + +// MeterProvider is embedded in the OpenTelemetry metric API [MeterProvider]. +// +// Embed this interface in your implementation of the [MeterProvider] if you +// want users to experience a compilation error, signaling they need to update +// to your latest implementation, when the [MeterProvider] interface is +// extended (which is something that can happen without a major version bump of +// the API package). +// +// [MeterProvider]: go.opentelemetry.io/otel/metric.MeterProvider +type MeterProvider interface{ meterProvider() } + +// Meter is embedded in the OpenTelemetry metric API [Meter]. +// +// Embed this interface in your implementation of the [Meter] if you want users +// to experience a compilation error, signaling they need to update to your +// latest implementation, when the [Meter] interface is extended (which is +// something that can happen without a major version bump of the API package). +// +// [Meter]: go.opentelemetry.io/otel/metric.Meter +type Meter interface{ meter() } + +// Float64Observer is embedded in the OpenTelemetry metric API +// [Float64Observer]. +// +// Embed this interface in your implementation of the [Float64Observer] if you +// want users to experience a compilation error, signaling they need to update +// to your latest implementation, when the [Float64Observer] interface is +// extended (which is something that can happen without a major version bump of +// the API package). +// +// [Float64Observer]: go.opentelemetry.io/otel/metric.Float64Observer +type Float64Observer interface{ float64Observer() } + +// Int64Observer is embedded in the OpenTelemetry metric API [Int64Observer]. +// +// Embed this interface in your implementation of the [Int64Observer] if you +// want users to experience a compilation error, signaling they need to update +// to your latest implementation, when the [Int64Observer] interface is +// extended (which is something that can happen without a major version bump of +// the API package). +// +// [Int64Observer]: go.opentelemetry.io/otel/metric.Int64Observer +type Int64Observer interface{ int64Observer() } + +// Observer is embedded in the OpenTelemetry metric API [Observer]. +// +// Embed this interface in your implementation of the [Observer] if you want +// users to experience a compilation error, signaling they need to update to +// your latest implementation, when the [Observer] interface is extended (which +// is something that can happen without a major version bump of the API +// package). +// +// [Observer]: go.opentelemetry.io/otel/metric.Observer +type Observer interface{ observer() } + +// Registration is embedded in the OpenTelemetry metric API [Registration]. +// +// Embed this interface in your implementation of the [Registration] if you +// want users to experience a compilation error, signaling they need to update +// to your latest implementation, when the [Registration] interface is extended +// (which is something that can happen without a major version bump of the API +// package). +// +// [Registration]: go.opentelemetry.io/otel/metric.Registration +type Registration interface{ registration() } + +// Float64Counter is embedded in the OpenTelemetry metric API [Float64Counter]. +// +// Embed this interface in your implementation of the [Float64Counter] if you +// want users to experience a compilation error, signaling they need to update +// to your latest implementation, when the [Float64Counter] interface is +// extended (which is something that can happen without a major version bump of +// the API package). +// +// [Float64Counter]: go.opentelemetry.io/otel/metric.Float64Counter +type Float64Counter interface{ float64Counter() } + +// Float64Histogram is embedded in the OpenTelemetry metric API +// [Float64Histogram]. +// +// Embed this interface in your implementation of the [Float64Histogram] if you +// want users to experience a compilation error, signaling they need to update +// to your latest implementation, when the [Float64Histogram] interface is +// extended (which is something that can happen without a major version bump of +// the API package). +// +// [Float64Histogram]: go.opentelemetry.io/otel/metric.Float64Histogram +type Float64Histogram interface{ float64Histogram() } + +// Float64ObservableCounter is embedded in the OpenTelemetry metric API +// [Float64ObservableCounter]. +// +// Embed this interface in your implementation of the +// [Float64ObservableCounter] if you want users to experience a compilation +// error, signaling they need to update to your latest implementation, when the +// [Float64ObservableCounter] interface is extended (which is something that +// can happen without a major version bump of the API package). +// +// [Float64ObservableCounter]: go.opentelemetry.io/otel/metric.Float64ObservableCounter +type Float64ObservableCounter interface{ float64ObservableCounter() } + +// Float64ObservableGauge is embedded in the OpenTelemetry metric API +// [Float64ObservableGauge]. +// +// Embed this interface in your implementation of the [Float64ObservableGauge] +// if you want users to experience a compilation error, signaling they need to +// update to your latest implementation, when the [Float64ObservableGauge] +// interface is extended (which is something that can happen without a major +// version bump of the API package). +// +// [Float64ObservableGauge]: go.opentelemetry.io/otel/metric.Float64ObservableGauge +type Float64ObservableGauge interface{ float64ObservableGauge() } + +// Float64ObservableUpDownCounter is embedded in the OpenTelemetry metric API +// [Float64ObservableUpDownCounter]. +// +// Embed this interface in your implementation of the +// [Float64ObservableUpDownCounter] if you want users to experience a +// compilation error, signaling they need to update to your latest +// implementation, when the [Float64ObservableUpDownCounter] interface is +// extended (which is something that can happen without a major version bump of +// the API package). +// +// [Float64ObservableUpDownCounter]: go.opentelemetry.io/otel/metric.Float64ObservableUpDownCounter +type Float64ObservableUpDownCounter interface{ float64ObservableUpDownCounter() } + +// Float64UpDownCounter is embedded in the OpenTelemetry metric API +// [Float64UpDownCounter]. +// +// Embed this interface in your implementation of the [Float64UpDownCounter] if +// you want users to experience a compilation error, signaling they need to +// update to your latest implementation, when the [Float64UpDownCounter] +// interface is extended (which is something that can happen without a major +// version bump of the API package). +// +// [Float64UpDownCounter]: go.opentelemetry.io/otel/metric.Float64UpDownCounter +type Float64UpDownCounter interface{ float64UpDownCounter() } + +// Int64Counter is embedded in the OpenTelemetry metric API [Int64Counter]. +// +// Embed this interface in your implementation of the [Int64Counter] if you +// want users to experience a compilation error, signaling they need to update +// to your latest implementation, when the [Int64Counter] interface is extended +// (which is something that can happen without a major version bump of the API +// package). +// +// [Int64Counter]: go.opentelemetry.io/otel/metric.Int64Counter +type Int64Counter interface{ int64Counter() } + +// Int64Histogram is embedded in the OpenTelemetry metric API [Int64Histogram]. +// +// Embed this interface in your implementation of the [Int64Histogram] if you +// want users to experience a compilation error, signaling they need to update +// to your latest implementation, when the [Int64Histogram] interface is +// extended (which is something that can happen without a major version bump of +// the API package). +// +// [Int64Histogram]: go.opentelemetry.io/otel/metric.Int64Histogram +type Int64Histogram interface{ int64Histogram() } + +// Int64ObservableCounter is embedded in the OpenTelemetry metric API +// [Int64ObservableCounter]. +// +// Embed this interface in your implementation of the [Int64ObservableCounter] +// if you want users to experience a compilation error, signaling they need to +// update to your latest implementation, when the [Int64ObservableCounter] +// interface is extended (which is something that can happen without a major +// version bump of the API package). +// +// [Int64ObservableCounter]: go.opentelemetry.io/otel/metric.Int64ObservableCounter +type Int64ObservableCounter interface{ int64ObservableCounter() } + +// Int64ObservableGauge is embedded in the OpenTelemetry metric API +// [Int64ObservableGauge]. +// +// Embed this interface in your implementation of the [Int64ObservableGauge] if +// you want users to experience a compilation error, signaling they need to +// update to your latest implementation, when the [Int64ObservableGauge] +// interface is extended (which is something that can happen without a major +// version bump of the API package). +// +// [Int64ObservableGauge]: go.opentelemetry.io/otel/metric.Int64ObservableGauge +type Int64ObservableGauge interface{ int64ObservableGauge() } + +// Int64ObservableUpDownCounter is embedded in the OpenTelemetry metric API +// [Int64ObservableUpDownCounter]. +// +// Embed this interface in your implementation of the +// [Int64ObservableUpDownCounter] if you want users to experience a compilation +// error, signaling they need to update to your latest implementation, when the +// [Int64ObservableUpDownCounter] interface is extended (which is something +// that can happen without a major version bump of the API package). +// +// [Int64ObservableUpDownCounter]: go.opentelemetry.io/otel/metric.Int64ObservableUpDownCounter +type Int64ObservableUpDownCounter interface{ int64ObservableUpDownCounter() } + +// Int64UpDownCounter is embedded in the OpenTelemetry metric API +// [Int64UpDownCounter]. +// +// Embed this interface in your implementation of the [Int64UpDownCounter] if +// you want users to experience a compilation error, signaling they need to +// update to your latest implementation, when the [Int64UpDownCounter] +// interface is extended (which is something that can happen without a major +// version bump of the API package). +// +// [Int64UpDownCounter]: go.opentelemetry.io/otel/metric.Int64UpDownCounter +type Int64UpDownCounter interface{ int64UpDownCounter() } diff --git a/metric/instrument/asyncfloat64.go b/metric/instrument/asyncfloat64.go index cf0008e61..356c969b6 100644 --- a/metric/instrument/asyncfloat64.go +++ b/metric/instrument/asyncfloat64.go @@ -18,13 +18,14 @@ import ( "context" "go.opentelemetry.io/otel/attribute" + "go.opentelemetry.io/otel/metric/embedded" ) // Float64Observable describes a set of instruments used asynchronously to // record float64 measurements once per collection cycle. Observations of // these instruments are only made within a callback. // -// Warning: methods may be added to this interface in minor releases. +// Warning: Methods may be added to this interface in minor releases. type Float64Observable interface { Observable @@ -36,8 +37,15 @@ type Float64Observable interface { // only made within a callback for this instrument. The value observed is // assumed the to be the cumulative sum of the count. // -// Warning: methods may be added to this interface in minor releases. -type Float64ObservableCounter interface{ Float64Observable } +// Warning: Methods may be added to this interface in minor releases. See +// [go.opentelemetry.io/otel/metric] package documentation on API +// implementation for information on how to set default behavior for +// unimplemented methods. +type Float64ObservableCounter interface { + embedded.Float64ObservableCounter + + Float64Observable +} // Float64ObservableCounterConfig contains options for asynchronous counter // instruments that record int64 values. @@ -84,8 +92,15 @@ type Float64ObservableCounterOption interface { // made within a callback for this instrument. The value observed is assumed // the to be the cumulative sum of the count. // -// Warning: methods may be added to this interface in minor releases. -type Float64ObservableUpDownCounter interface{ Float64Observable } +// Warning: Methods may be added to this interface in minor releases. See +// [go.opentelemetry.io/otel/metric] package documentation on API +// implementation for information on how to set default behavior for +// unimplemented methods. +type Float64ObservableUpDownCounter interface { + embedded.Float64ObservableUpDownCounter + + Float64Observable +} // Float64ObservableUpDownCounterConfig contains options for asynchronous // counter instruments that record int64 values. @@ -132,8 +147,15 @@ type Float64ObservableUpDownCounterOption interface { // instantaneous float64 measurements once per collection cycle. Observations // are only made within a callback for this instrument. // -// Warning: methods may be added to this interface in minor releases. -type Float64ObservableGauge interface{ Float64Observable } +// Warning: Methods may be added to this interface in minor releases. See +// [go.opentelemetry.io/otel/metric] package documentation on API +// implementation for information on how to set default behavior for +// unimplemented methods. +type Float64ObservableGauge interface { + embedded.Float64ObservableGauge + + Float64Observable +} // Float64ObservableGaugeConfig contains options for asynchronous counter // instruments that record int64 values. @@ -178,8 +200,13 @@ type Float64ObservableGaugeOption interface { // Float64Observer is a recorder of float64 measurements. // -// Warning: methods may be added to this interface in minor releases. +// Warning: Methods may be added to this interface in minor releases. See +// [go.opentelemetry.io/otel/metric] package documentation on API +// implementation for information on how to set default behavior for +// unimplemented methods. type Float64Observer interface { + embedded.Float64Observer + // Observe records the float64 value with attributes. Observe(value float64, attributes ...attribute.KeyValue) } diff --git a/metric/instrument/asyncfloat64_test.go b/metric/instrument/asyncfloat64_test.go index 0613fb863..5a23c2845 100644 --- a/metric/instrument/asyncfloat64_test.go +++ b/metric/instrument/asyncfloat64_test.go @@ -22,6 +22,7 @@ import ( "github.com/stretchr/testify/require" "go.opentelemetry.io/otel/attribute" + "go.opentelemetry.io/otel/metric/embedded" ) func TestFloat64ObservableConfiguration(t *testing.T) { @@ -83,6 +84,7 @@ type float64ObservableConfig interface { } type float64Observer struct { + embedded.Float64Observer Observable got float64 } diff --git a/metric/instrument/asyncint64.go b/metric/instrument/asyncint64.go index 752dcea0a..05bfc8dbe 100644 --- a/metric/instrument/asyncint64.go +++ b/metric/instrument/asyncint64.go @@ -18,13 +18,14 @@ import ( "context" "go.opentelemetry.io/otel/attribute" + "go.opentelemetry.io/otel/metric/embedded" ) // Int64Observable describes a set of instruments used asynchronously to record // int64 measurements once per collection cycle. Observations of these // instruments are only made within a callback. // -// Warning: methods may be added to this interface in minor releases. +// Warning: Methods may be added to this interface in minor releases. type Int64Observable interface { Observable @@ -36,8 +37,15 @@ type Int64Observable interface { // only made within a callback for this instrument. The value observed is // assumed the to be the cumulative sum of the count. // -// Warning: methods may be added to this interface in minor releases. -type Int64ObservableCounter interface{ Int64Observable } +// Warning: Methods may be added to this interface in minor releases. See +// [go.opentelemetry.io/otel/metric] package documentation on API +// implementation for information on how to set default behavior for +// unimplemented methods. +type Int64ObservableCounter interface { + embedded.Int64ObservableCounter + + Int64Observable +} // Int64ObservableCounterConfig contains options for asynchronous counter // instruments that record int64 values. @@ -84,8 +92,15 @@ type Int64ObservableCounterOption interface { // within a callback for this instrument. The value observed is assumed the to // be the cumulative sum of the count. // -// Warning: methods may be added to this interface in minor releases. -type Int64ObservableUpDownCounter interface{ Int64Observable } +// Warning: Methods may be added to this interface in minor releases. See +// [go.opentelemetry.io/otel/metric] package documentation on API +// implementation for information on how to set default behavior for +// unimplemented methods. +type Int64ObservableUpDownCounter interface { + embedded.Int64ObservableUpDownCounter + + Int64Observable +} // Int64ObservableUpDownCounterConfig contains options for asynchronous counter // instruments that record int64 values. @@ -132,8 +147,15 @@ type Int64ObservableUpDownCounterOption interface { // instantaneous int64 measurements once per collection cycle. Observations are // only made within a callback for this instrument. // -// Warning: methods may be added to this interface in minor releases. -type Int64ObservableGauge interface{ Int64Observable } +// Warning: Methods may be added to this interface in minor releases. See +// [go.opentelemetry.io/otel/metric] package documentation on API +// implementation for information on how to set default behavior for +// unimplemented methods. +type Int64ObservableGauge interface { + embedded.Int64ObservableGauge + + Int64Observable +} // Int64ObservableGaugeConfig contains options for asynchronous counter // instruments that record int64 values. @@ -177,8 +199,13 @@ type Int64ObservableGaugeOption interface { // Int64Observer is a recorder of int64 measurements. // -// Warning: methods may be added to this interface in minor releases. +// Warning: Methods may be added to this interface in minor releases. See +// [go.opentelemetry.io/otel/metric] package documentation on API +// implementation for information on how to set default behavior for +// unimplemented methods. type Int64Observer interface { + embedded.Int64Observer + // Observe records the int64 value with attributes. Observe(value int64, attributes ...attribute.KeyValue) } diff --git a/metric/instrument/asyncint64_test.go b/metric/instrument/asyncint64_test.go index b8e8cba58..b52f0730d 100644 --- a/metric/instrument/asyncint64_test.go +++ b/metric/instrument/asyncint64_test.go @@ -22,6 +22,7 @@ import ( "github.com/stretchr/testify/require" "go.opentelemetry.io/otel/attribute" + "go.opentelemetry.io/otel/metric/embedded" ) func TestInt64ObservableConfiguration(t *testing.T) { @@ -83,6 +84,7 @@ type int64ObservableConfig interface { } type int64Observer struct { + embedded.Int64Observer Observable got int64 } diff --git a/metric/instrument/syncfloat64.go b/metric/instrument/syncfloat64.go index 2d55384ab..03b3bf6ac 100644 --- a/metric/instrument/syncfloat64.go +++ b/metric/instrument/syncfloat64.go @@ -18,12 +18,18 @@ import ( "context" "go.opentelemetry.io/otel/attribute" + "go.opentelemetry.io/otel/metric/embedded" ) // Float64Counter is an instrument that records increasing float64 values. // -// Warning: methods may be added to this interface in minor releases. +// Warning: Methods may be added to this interface in minor releases. See +// [go.opentelemetry.io/otel/metric] package documentation on API +// implementation for information on how to set default behavior for +// unimplemented methods. type Float64Counter interface { + embedded.Float64Counter + // Add records a change to the counter. Add(ctx context.Context, incr float64, attrs ...attribute.KeyValue) } @@ -64,8 +70,13 @@ type Float64CounterOption interface { // Float64UpDownCounter is an instrument that records increasing or decreasing // float64 values. // -// Warning: methods may be added to this interface in minor releases. +// Warning: Methods may be added to this interface in minor releases. See +// [go.opentelemetry.io/otel/metric] package documentation on API +// implementation for information on how to set default behavior for +// unimplemented methods. type Float64UpDownCounter interface { + embedded.Float64UpDownCounter + // Add records a change to the counter. Add(ctx context.Context, incr float64, attrs ...attribute.KeyValue) } @@ -107,8 +118,13 @@ type Float64UpDownCounterOption interface { // Float64Histogram is an instrument that records a distribution of float64 // values. // -// Warning: methods may be added to this interface in minor releases. +// Warning: Methods may be added to this interface in minor releases. See +// [go.opentelemetry.io/otel/metric] package documentation on API +// implementation for information on how to set default behavior for +// unimplemented methods. type Float64Histogram interface { + embedded.Float64Histogram + // Record adds an additional value to the distribution. Record(ctx context.Context, incr float64, attrs ...attribute.KeyValue) } diff --git a/metric/instrument/syncint64.go b/metric/instrument/syncint64.go index 9b6b3917f..22d0718c8 100644 --- a/metric/instrument/syncint64.go +++ b/metric/instrument/syncint64.go @@ -18,12 +18,18 @@ import ( "context" "go.opentelemetry.io/otel/attribute" + "go.opentelemetry.io/otel/metric/embedded" ) // Int64Counter is an instrument that records increasing int64 values. // -// Warning: methods may be added to this interface in minor releases. +// Warning: Methods may be added to this interface in minor releases. See +// [go.opentelemetry.io/otel/metric] package documentation on API +// implementation for information on how to set default behavior for +// unimplemented methods. type Int64Counter interface { + embedded.Int64Counter + // Add records a change to the counter. Add(ctx context.Context, incr int64, attrs ...attribute.KeyValue) } @@ -64,8 +70,13 @@ type Int64CounterOption interface { // Int64UpDownCounter is an instrument that records increasing or decreasing // int64 values. // -// Warning: methods may be added to this interface in minor releases. +// Warning: Methods may be added to this interface in minor releases. See +// [go.opentelemetry.io/otel/metric] package documentation on API +// implementation for information on how to set default behavior for +// unimplemented methods. type Int64UpDownCounter interface { + embedded.Int64UpDownCounter + // Add records a change to the counter. Add(ctx context.Context, incr int64, attrs ...attribute.KeyValue) } @@ -107,8 +118,13 @@ type Int64UpDownCounterOption interface { // Int64Histogram is an instrument that records a distribution of int64 // values. // -// Warning: methods may be added to this interface in minor releases. +// Warning: Methods may be added to this interface in minor releases. See +// [go.opentelemetry.io/otel/metric] package documentation on API +// implementation for information on how to set default behavior for +// unimplemented methods. type Int64Histogram interface { + embedded.Int64Histogram + // Record adds an additional value to the distribution. Record(ctx context.Context, incr int64, attrs ...attribute.KeyValue) } diff --git a/metric/meter.go b/metric/meter.go index 1cb556b05..5e7ca5bea 100644 --- a/metric/meter.go +++ b/metric/meter.go @@ -18,14 +18,19 @@ import ( "context" "go.opentelemetry.io/otel/attribute" + "go.opentelemetry.io/otel/metric/embedded" "go.opentelemetry.io/otel/metric/instrument" ) // MeterProvider provides access to named Meter instances, for instrumenting // an application or package. // -// Warning: methods may be added to this interface in minor releases. +// Warning: Methods may be added to this interface in minor releases. See +// package documentation on API implementation for information on how to set +// default behavior for unimplemented methods. type MeterProvider interface { + embedded.MeterProvider + // Meter returns a new Meter with the provided name and configuration. // // A Meter should be scoped at most to a single package. The name needs to @@ -40,8 +45,12 @@ type MeterProvider interface { // Meter provides access to instrument instances for recording metrics. // -// Warning: methods may be added to this interface in minor releases. +// Warning: Methods may be added to this interface in minor releases. See +// package documentation on API implementation for information on how to set +// default behavior for unimplemented methods. type Meter interface { + embedded.Meter + // Int64Counter returns a new instrument identified by name and configured // with options. The instrument is used to synchronously record increasing // int64 measurements during a computational operation. @@ -125,7 +134,13 @@ type Meter interface { type Callback func(context.Context, Observer) error // Observer records measurements for multiple instruments in a Callback. +// +// Warning: Methods may be added to this interface in minor releases. See +// package documentation on API implementation for information on how to set +// default behavior for unimplemented methods. type Observer interface { + embedded.Observer + // ObserveFloat64 records the float64 value with attributes for obsrv. ObserveFloat64(obsrv instrument.Float64Observable, value float64, attributes ...attribute.KeyValue) // ObserveInt64 records the int64 value with attributes for obsrv. @@ -134,7 +149,13 @@ type Observer interface { // Registration is an token representing the unique registration of a callback // for a set of instruments with a Meter. +// +// Warning: Methods may be added to this interface in minor releases. See +// package documentation on API implementation for information on how to set +// default behavior for unimplemented methods. type Registration interface { + embedded.Registration + // Unregister removes the callback registration from a Meter. // // This method needs to be idempotent and concurrent safe. diff --git a/metric/noop/noop.go b/metric/noop/noop.go index 0b2905da6..b4eaf4d64 100644 --- a/metric/noop/noop.go +++ b/metric/noop/noop.go @@ -28,6 +28,7 @@ import ( "go.opentelemetry.io/otel/attribute" "go.opentelemetry.io/otel/metric" + "go.opentelemetry.io/otel/metric/embedded" "go.opentelemetry.io/otel/metric/instrument" ) @@ -55,7 +56,7 @@ var ( ) // MeterProvider is an OpenTelemetry No-Op MeterProvider. -type MeterProvider struct{} +type MeterProvider struct{ embedded.MeterProvider } // NewMeterProvider returns a MeterProvider that does not record any telemetry. func NewMeterProvider() MeterProvider { @@ -68,7 +69,7 @@ func (MeterProvider) Meter(string, ...metric.MeterOption) metric.Meter { } // Meter is an OpenTelemetry No-Op Meter. -type Meter struct{} +type Meter struct{ embedded.Meter } // Int64Counter returns a Counter used to record int64 measurements that // produces no telemetry. @@ -149,7 +150,7 @@ func (Meter) RegisterCallback(metric.Callback, ...instrument.Observable) (metric // Observer acts as a recorder of measurements for multiple instruments in a // Callback, it performing no operation. -type Observer struct{} +type Observer struct{ embedded.Observer } // ObserveFloat64 performs no operation. func (Observer) ObserveFloat64(instrument.Float64Observable, float64, ...attribute.KeyValue) { @@ -160,7 +161,7 @@ func (Observer) ObserveInt64(instrument.Int64Observable, int64, ...attribute.Key } // Registration is the registration of a Callback with a No-Op Meter. -type Registration struct{} +type Registration struct{ embedded.Registration } // Unregister unregisters the Callback the Registration represents with the // No-Op Meter. This will always return nil because the No-Op Meter performs no @@ -169,79 +170,97 @@ func (Registration) Unregister() error { return nil } // Int64Counter is an OpenTelemetry Counter used to record int64 measurements. // It produces no telemetry. -type Int64Counter struct{} +type Int64Counter struct{ embedded.Int64Counter } // Add performs no operation. func (Int64Counter) Add(context.Context, int64, ...attribute.KeyValue) {} // Float64Counter is an OpenTelemetry Counter used to record float64 // measurements. It produces no telemetry. -type Float64Counter struct{} +type Float64Counter struct{ embedded.Float64Counter } // Add performs no operation. func (Float64Counter) Add(context.Context, float64, ...attribute.KeyValue) {} // Int64UpDownCounter is an OpenTelemetry UpDownCounter used to record int64 // measurements. It produces no telemetry. -type Int64UpDownCounter struct{} +type Int64UpDownCounter struct{ embedded.Int64UpDownCounter } // Add performs no operation. func (Int64UpDownCounter) Add(context.Context, int64, ...attribute.KeyValue) {} // Float64UpDownCounter is an OpenTelemetry UpDownCounter used to record // float64 measurements. It produces no telemetry. -type Float64UpDownCounter struct{} +type Float64UpDownCounter struct{ embedded.Float64UpDownCounter } // Add performs no operation. func (Float64UpDownCounter) Add(context.Context, float64, ...attribute.KeyValue) {} // Int64Histogram is an OpenTelemetry Histogram used to record int64 // measurements. It produces no telemetry. -type Int64Histogram struct{} +type Int64Histogram struct{ embedded.Int64Histogram } // Record performs no operation. func (Int64Histogram) Record(context.Context, int64, ...attribute.KeyValue) {} // Float64Histogram is an OpenTelemetry Histogram used to record float64 // measurements. It produces no telemetry. -type Float64Histogram struct{} +type Float64Histogram struct{ embedded.Float64Histogram } // Record performs no operation. func (Float64Histogram) Record(context.Context, float64, ...attribute.KeyValue) {} // Int64ObservableCounter is an OpenTelemetry ObservableCounter used to record // int64 measurements. It produces no telemetry. -type Int64ObservableCounter struct{ instrument.Int64Observable } +type Int64ObservableCounter struct { + instrument.Int64Observable + embedded.Int64ObservableCounter +} // Float64ObservableCounter is an OpenTelemetry ObservableCounter used to record // float64 measurements. It produces no telemetry. -type Float64ObservableCounter struct{ instrument.Float64Observable } +type Float64ObservableCounter struct { + instrument.Float64Observable + embedded.Float64ObservableCounter +} // Int64ObservableGauge is an OpenTelemetry ObservableGauge used to record // int64 measurements. It produces no telemetry. -type Int64ObservableGauge struct{ instrument.Int64Observable } +type Int64ObservableGauge struct { + instrument.Int64Observable + embedded.Int64ObservableGauge +} // Float64ObservableGauge is an OpenTelemetry ObservableGauge used to record // float64 measurements. It produces no telemetry. -type Float64ObservableGauge struct{ instrument.Float64Observable } +type Float64ObservableGauge struct { + instrument.Float64Observable + embedded.Float64ObservableGauge +} // Int64ObservableUpDownCounter is an OpenTelemetry ObservableUpDownCounter // used to record int64 measurements. It produces no telemetry. -type Int64ObservableUpDownCounter struct{ instrument.Int64Observable } +type Int64ObservableUpDownCounter struct { + instrument.Int64Observable + embedded.Int64ObservableUpDownCounter +} // Float64ObservableUpDownCounter is an OpenTelemetry ObservableUpDownCounter // used to record float64 measurements. It produces no telemetry. -type Float64ObservableUpDownCounter struct{ instrument.Float64Observable } +type Float64ObservableUpDownCounter struct { + instrument.Float64Observable + embedded.Float64ObservableUpDownCounter +} // Int64Observer is a recorder of int64 measurements that performs no operation. -type Int64Observer struct{} +type Int64Observer struct{ embedded.Int64Observer } // Observe performs no operation. func (Int64Observer) Observe(int64, ...attribute.KeyValue) {} // Float64Observer is a recorder of float64 measurements that performs no // operation. -type Float64Observer struct{} +type Float64Observer struct{ embedded.Float64Observer } // Observe performs no operation. func (Float64Observer) Observe(float64, ...attribute.KeyValue) {} diff --git a/metric_test.go b/metric_test.go index cc6f72d76..5e6e48fb8 100644 --- a/metric_test.go +++ b/metric_test.go @@ -20,10 +20,11 @@ import ( "github.com/stretchr/testify/assert" "go.opentelemetry.io/otel/metric" + "go.opentelemetry.io/otel/metric/embedded" "go.opentelemetry.io/otel/metric/noop" ) -type testMeterProvider struct{} +type testMeterProvider struct{ embedded.MeterProvider } var _ metric.MeterProvider = &testMeterProvider{} diff --git a/sdk/metric/instrument.go b/sdk/metric/instrument.go index 3ae2f2d7b..9e8de50cf 100644 --- a/sdk/metric/instrument.go +++ b/sdk/metric/instrument.go @@ -20,6 +20,7 @@ import ( "fmt" "go.opentelemetry.io/otel/attribute" + "go.opentelemetry.io/otel/metric/embedded" "go.opentelemetry.io/otel/metric/instrument" "go.opentelemetry.io/otel/sdk/instrumentation" "go.opentelemetry.io/otel/sdk/metric/aggregation" @@ -171,6 +172,13 @@ type streamID struct { type instrumentImpl[N int64 | float64] struct { aggregators []internal.Aggregator[N] + + embedded.Float64Counter + embedded.Float64UpDownCounter + embedded.Float64Histogram + embedded.Int64Counter + embedded.Int64UpDownCounter + embedded.Int64Histogram } var _ instrument.Float64Counter = (*instrumentImpl[float64])(nil) @@ -213,6 +221,10 @@ type observablID[N int64 | float64] struct { type float64Observable struct { instrument.Float64Observable *observable[float64] + + embedded.Float64ObservableCounter + embedded.Float64ObservableUpDownCounter + embedded.Float64ObservableGauge } var _ instrument.Float64ObservableCounter = float64Observable{} @@ -228,6 +240,10 @@ func newFloat64Observable(scope instrumentation.Scope, kind InstrumentKind, name type int64Observable struct { instrument.Int64Observable *observable[int64] + + embedded.Int64ObservableCounter + embedded.Int64ObservableUpDownCounter + embedded.Int64ObservableGauge } var _ instrument.Int64ObservableCounter = int64Observable{} diff --git a/sdk/metric/internal/aggregator_example_test.go b/sdk/metric/internal/aggregator_example_test.go index fdaf68336..297d0f511 100644 --- a/sdk/metric/internal/aggregator_example_test.go +++ b/sdk/metric/internal/aggregator_example_test.go @@ -19,6 +19,7 @@ import ( "fmt" "go.opentelemetry.io/otel/attribute" + "go.opentelemetry.io/otel/metric/embedded" "go.opentelemetry.io/otel/metric/instrument" "go.opentelemetry.io/otel/sdk/metric/aggregation" "go.opentelemetry.io/otel/sdk/metric/metricdata" @@ -87,6 +88,10 @@ func (p *meter) Int64Histogram(string, ...instrument.Int64HistogramOption) (inst // histogram used for demonstration purposes only. type inst struct { aggregateFunc func(int64, attribute.Set) + + embedded.Int64Counter + embedded.Int64UpDownCounter + embedded.Int64Histogram } func (inst) Add(context.Context, int64, ...attribute.KeyValue) {} diff --git a/sdk/metric/meter.go b/sdk/metric/meter.go index 6af74fade..72b37ea66 100644 --- a/sdk/metric/meter.go +++ b/sdk/metric/meter.go @@ -22,6 +22,7 @@ import ( "go.opentelemetry.io/otel/attribute" "go.opentelemetry.io/otel/internal/global" "go.opentelemetry.io/otel/metric" + "go.opentelemetry.io/otel/metric/embedded" "go.opentelemetry.io/otel/metric/instrument" "go.opentelemetry.io/otel/sdk/instrumentation" "go.opentelemetry.io/otel/sdk/metric/internal" @@ -32,6 +33,8 @@ import ( // produced by an instrumentation scope will use metric instruments from a // single meter. type meter struct { + embedded.Meter + scope instrumentation.Scope pipes pipelines @@ -264,6 +267,8 @@ func (m *meter) RegisterCallback(f metric.Callback, insts ...instrument.Observab } type observer struct { + embedded.Observer + float64 map[observablID[float64]]struct{} int64 map[observablID[int64]]struct{} } @@ -356,7 +361,7 @@ func (r observer) ObserveInt64(o instrument.Int64Observable, v int64, a ...attri oImpl.observe(v, a) } -type noopRegister struct{} +type noopRegister struct{ embedded.Registration } func (noopRegister) Unregister() error { return nil @@ -409,11 +414,12 @@ func (p int64ObservProvider) registerCallbacks(inst int64Observable, cBacks []in } func (p int64ObservProvider) callback(i int64Observable, f instrument.Int64Callback) func(context.Context) error { - inst := int64Observer{i} + inst := int64Observer{int64Observable: i} return func(ctx context.Context) error { return f(ctx, inst) } } type int64Observer struct { + embedded.Int64Observer int64Observable } @@ -440,11 +446,12 @@ func (p float64ObservProvider) registerCallbacks(inst float64Observable, cBacks } func (p float64ObservProvider) callback(i float64Observable, f instrument.Float64Callback) func(context.Context) error { - inst := float64Observer{i} + inst := float64Observer{float64Observable: i} return func(ctx context.Context) error { return f(ctx, inst) } } type float64Observer struct { + embedded.Float64Observer float64Observable } diff --git a/sdk/metric/pipeline.go b/sdk/metric/pipeline.go index 1d63af01b..e94cfcb14 100644 --- a/sdk/metric/pipeline.go +++ b/sdk/metric/pipeline.go @@ -24,6 +24,7 @@ import ( "go.opentelemetry.io/otel/internal/global" "go.opentelemetry.io/otel/metric" + "go.opentelemetry.io/otel/metric/embedded" "go.opentelemetry.io/otel/sdk/instrumentation" "go.opentelemetry.io/otel/sdk/metric/aggregation" "go.opentelemetry.io/otel/sdk/metric/internal" @@ -503,13 +504,16 @@ func (p pipelines) registerMultiCallback(c multiCallback) metric.Registration { for i, pipe := range p { unregs[i] = pipe.addMultiCallback(c) } - return unregisterFuncs(unregs) + return unregisterFuncs{f: unregs} } -type unregisterFuncs []func() +type unregisterFuncs struct { + embedded.Registration + f []func() +} func (u unregisterFuncs) Unregister() error { - for _, f := range u { + for _, f := range u.f { f() } return nil diff --git a/sdk/metric/provider.go b/sdk/metric/provider.go index b90ae4ff4..33daabb50 100644 --- a/sdk/metric/provider.go +++ b/sdk/metric/provider.go @@ -18,6 +18,7 @@ import ( "context" "go.opentelemetry.io/otel/metric" + "go.opentelemetry.io/otel/metric/embedded" "go.opentelemetry.io/otel/sdk/instrumentation" ) @@ -26,6 +27,8 @@ import ( // the same Views applied to them, and have their produced metric telemetry // passed to the configured Readers. type MeterProvider struct { + embedded.MeterProvider + pipes pipelines meters cache[instrumentation.Scope, *meter]