Skip to content

Commit

Permalink
Add embedded private method interfaces in metric API (#3916)
Browse files Browse the repository at this point in the history
* PoC of embedded private method ifaces

* Rename embed to embedded

* Add an embedded iface for all instruments

* Fix metric/instrument tests

* Fix global and otel

* Fix SDK

* Comment the embedded pkg types

* Update the embedded pkg docs

* Update otel/metric docs about impls

* Update otel/metric type docs on impl

* Update docs in otel/metric/instrument on default

* Add changes to changelog

* Apply suggestions from code review

Co-authored-by: Robert Pająk <[email protected]>

* Apply feedback on URLs

* Reword based on feedback

* Make it clear we only recommended embedding noop

* Ignore links with godot linter

---------

Co-authored-by: Robert Pająk <[email protected]>
  • Loading branch information
MrAlias and pellared authored Apr 3, 2023
1 parent 5f13db5 commit 65ebe5e
Show file tree
Hide file tree
Showing 22 changed files with 565 additions and 50 deletions.
2 changes: 2 additions & 0 deletions .golangci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,8 @@ linters-settings:
- "**/internal/matchers/*.go"
godot:
exclude:
# Exclude links.
- '^ *\[[^]]+\]:'
# Exclude sentence fragments for lists.
- '^[ ]*[-•]'
# Exclude sentences prefixing a list.
Expand Down
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -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`

Expand Down
19 changes: 19 additions & 0 deletions internal/global/instruments.go
Original file line number Diff line number Diff line change
Expand Up @@ -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"
)

Expand All @@ -29,6 +30,7 @@ type unwrapper interface {
}

type afCounter struct {
embedded.Float64ObservableCounter
instrument.Float64Observable

name string
Expand Down Expand Up @@ -57,6 +59,7 @@ func (i *afCounter) Unwrap() instrument.Observable {
}

type afUpDownCounter struct {
embedded.Float64ObservableUpDownCounter
instrument.Float64Observable

name string
Expand Down Expand Up @@ -85,6 +88,7 @@ func (i *afUpDownCounter) Unwrap() instrument.Observable {
}

type afGauge struct {
embedded.Float64ObservableGauge
instrument.Float64Observable

name string
Expand Down Expand Up @@ -113,6 +117,7 @@ func (i *afGauge) Unwrap() instrument.Observable {
}

type aiCounter struct {
embedded.Int64ObservableCounter
instrument.Int64Observable

name string
Expand Down Expand Up @@ -141,6 +146,7 @@ func (i *aiCounter) Unwrap() instrument.Observable {
}

type aiUpDownCounter struct {
embedded.Int64ObservableUpDownCounter
instrument.Int64Observable

name string
Expand Down Expand Up @@ -169,6 +175,7 @@ func (i *aiUpDownCounter) Unwrap() instrument.Observable {
}

type aiGauge struct {
embedded.Int64ObservableGauge
instrument.Int64Observable

name string
Expand Down Expand Up @@ -198,6 +205,8 @@ func (i *aiGauge) Unwrap() instrument.Observable {

// Sync Instruments.
type sfCounter struct {
embedded.Float64Counter

name string
opts []instrument.Float64CounterOption

Expand All @@ -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

Expand All @@ -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

Expand All @@ -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

Expand All @@ -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

Expand All @@ -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

Expand Down
13 changes: 13 additions & 0 deletions internal/global/instruments_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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"
)
Expand Down Expand Up @@ -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() {
Expand All @@ -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() {
Expand Down
7 changes: 7 additions & 0 deletions internal/global/meter.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import (
"sync/atomic"

"go.opentelemetry.io/otel/metric"
"go.opentelemetry.io/otel/metric/embedded"
"go.opentelemetry.io/otel/metric/instrument"
)

Expand All @@ -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

Expand Down Expand Up @@ -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

Expand Down Expand Up @@ -308,6 +313,8 @@ func unwrapInstruments(instruments []instrument.Observable) []instrument.Observa
}

type registration struct {
embedded.Registration

instruments []instrument.Observable
function metric.Callback

Expand Down
11 changes: 10 additions & 1 deletion internal/global/meter_types_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
}

Expand All @@ -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
Expand Down Expand Up @@ -123,6 +128,8 @@ func (m *testMeter) RegisterCallback(f metric.Callback, i ...instrument.Observab
}

type testReg struct {
embedded.Registration

f func()
}

Expand All @@ -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.
Expand All @@ -145,6 +152,8 @@ func (m *testMeter) collect() {
}

type observationRecorder struct {
embedded.Observer

ctx context.Context
}

Expand Down
63 changes: 63 additions & 0 deletions metric/doc.go
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Loading

0 comments on commit 65ebe5e

Please sign in to comment.