Skip to content

Commit

Permalink
Add the synchronous gauge to the metric API and SDK (#5304)
Browse files Browse the repository at this point in the history
Resolve #5225 

The specification has [added a synchronous gauge
instrument](open-telemetry/opentelemetry-specification#3540).
That instrument has now been
[stabilized](open-telemetry/opentelemetry-specification#4019),
and that stabilization is included in the [next
release](open-telemetry/opentelemetry-specification#4034).

This adds the new synchronous gauge instrument to the metric API and all
implementation we publish.

This change will be a breaking change for any SDK developer. The
`embedded` package is updated to ensure our compatibility guarantees are
meet.

---------

Co-authored-by: David Ashpole <[email protected]>
  • Loading branch information
MrAlias and dashpole authored May 16, 2024
1 parent 166b347 commit dafe137
Show file tree
Hide file tree
Showing 24 changed files with 416 additions and 12 deletions.
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,9 @@ This project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.htm
- Add `RecordFactory` in `go.opentelemetry.io/otel/sdk/log/logtest` to facilitate testing the exporter and processor implementations. (#5258)
- Add example for `go.opentelemetry.io/otel/exporters/stdout/stdoutlog`. (#5242)
- The count of dropped records from the `BatchProcessor` in `go.opentelemetry.io/otel/sdk/log` is logged. (#5276)
- Add the synchronous gauge instrument to `go.opentelemetry.io/otel/metric`. (#5304)
- An `int64` or `float64` synchronous gauge instrument can now be created from a `Meter`.
- All implementations of the API (`go.opentelemetry.io/otel/metric/noop`, `go.opentelemetry.io/otel/sdk/metric`) are updated to support this instrument.
- Add logs to `go.opentelemetry.io/otel/example/dice`. (#5349)

### Changed
Expand Down
52 changes: 52 additions & 0 deletions internal/global/instruments.go
Original file line number Diff line number Diff line change
Expand Up @@ -281,6 +281,32 @@ func (i *sfHistogram) Record(ctx context.Context, x float64, opts ...metric.Reco
}
}

type sfGauge struct {
embedded.Float64Gauge

name string
opts []metric.Float64GaugeOption

delegate atomic.Value // metric.Float64Gauge
}

var _ metric.Float64Gauge = (*sfGauge)(nil)

func (i *sfGauge) setDelegate(m metric.Meter) {
ctr, err := m.Float64Gauge(i.name, i.opts...)
if err != nil {
GetErrorHandler().Handle(err)
return
}
i.delegate.Store(ctr)
}

func (i *sfGauge) Record(ctx context.Context, x float64, opts ...metric.RecordOption) {
if ctr := i.delegate.Load(); ctr != nil {
ctr.(metric.Float64Gauge).Record(ctx, x, opts...)
}
}

type siCounter struct {
embedded.Int64Counter

Expand Down Expand Up @@ -358,3 +384,29 @@ func (i *siHistogram) Record(ctx context.Context, x int64, opts ...metric.Record
ctr.(metric.Int64Histogram).Record(ctx, x, opts...)
}
}

type siGauge struct {
embedded.Int64Gauge

name string
opts []metric.Int64GaugeOption

delegate atomic.Value // metric.Int64Gauge
}

var _ metric.Int64Gauge = (*siGauge)(nil)

func (i *siGauge) setDelegate(m metric.Meter) {
ctr, err := m.Int64Gauge(i.name, i.opts...)
if err != nil {
GetErrorHandler().Handle(err)
return
}
i.delegate.Store(ctr)
}

func (i *siGauge) Record(ctx context.Context, x int64, opts ...metric.RecordOption) {
if ctr := i.delegate.Load(); ctr != nil {
ctr.(metric.Int64Gauge).Record(ctx, x, opts...)
}
}
14 changes: 14 additions & 0 deletions internal/global/instruments_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -117,6 +117,12 @@ func TestSyncInstrumentSetDelegateConcurrentSafe(t *testing.T) {
f := func(v float64) { delegate.Record(context.Background(), v) }
testFloat64ConcurrentSafe(f, delegate.setDelegate)
})

t.Run("Gauge", func(t *testing.T) {
delegate := &sfGauge{}
f := func(v float64) { delegate.Record(context.Background(), v) }
testFloat64ConcurrentSafe(f, delegate.setDelegate)
})
})

// Int64 Instruments
Expand All @@ -139,6 +145,12 @@ func TestSyncInstrumentSetDelegateConcurrentSafe(t *testing.T) {
f := func(v int64) { delegate.Record(context.Background(), v) }
testInt64ConcurrentSafe(f, delegate.setDelegate)
})

t.Run("Gauge", func(t *testing.T) {
delegate := &siGauge{}
f := func(v int64) { delegate.Record(context.Background(), v) }
testInt64ConcurrentSafe(f, delegate.setDelegate)
})
})
}

Expand All @@ -149,6 +161,7 @@ type testCountingFloatInstrument struct {
embedded.Float64Counter
embedded.Float64UpDownCounter
embedded.Float64Histogram
embedded.Float64Gauge
embedded.Float64ObservableCounter
embedded.Float64ObservableUpDownCounter
embedded.Float64ObservableGauge
Expand All @@ -173,6 +186,7 @@ type testCountingIntInstrument struct {
embedded.Int64Counter
embedded.Int64UpDownCounter
embedded.Int64Histogram
embedded.Int64Gauge
embedded.Int64ObservableCounter
embedded.Int64ObservableUpDownCounter
embedded.Int64ObservableGauge
Expand Down
22 changes: 22 additions & 0 deletions internal/global/meter.go
Original file line number Diff line number Diff line change
Expand Up @@ -164,6 +164,17 @@ func (m *meter) Int64Histogram(name string, options ...metric.Int64HistogramOpti
return i, nil
}

func (m *meter) Int64Gauge(name string, options ...metric.Int64GaugeOption) (metric.Int64Gauge, error) {
if del, ok := m.delegate.Load().(metric.Meter); ok {
return del.Int64Gauge(name, options...)
}
m.mtx.Lock()
defer m.mtx.Unlock()
i := &siGauge{name: name, opts: options}
m.instruments = append(m.instruments, i)
return i, nil
}

func (m *meter) Int64ObservableCounter(name string, options ...metric.Int64ObservableCounterOption) (metric.Int64ObservableCounter, error) {
if del, ok := m.delegate.Load().(metric.Meter); ok {
return del.Int64ObservableCounter(name, options...)
Expand Down Expand Up @@ -230,6 +241,17 @@ func (m *meter) Float64Histogram(name string, options ...metric.Float64Histogram
return i, nil
}

func (m *meter) Float64Gauge(name string, options ...metric.Float64GaugeOption) (metric.Float64Gauge, error) {
if del, ok := m.delegate.Load().(metric.Meter); ok {
return del.Float64Gauge(name, options...)
}
m.mtx.Lock()
defer m.mtx.Unlock()
i := &sfGauge{name: name, opts: options}
m.instruments = append(m.instruments, i)
return i, nil
}

func (m *meter) Float64ObservableCounter(name string, options ...metric.Float64ObservableCounterOption) (metric.Float64ObservableCounter, error) {
if del, ok := m.delegate.Load().(metric.Meter); ok {
return del.Float64ObservableCounter(name, options...)
Expand Down
6 changes: 6 additions & 0 deletions internal/global/meter_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -61,9 +61,11 @@ func TestMeterConcurrentSafe(t *testing.T) {
_, _ = mtr.Float64Counter(name)
_, _ = mtr.Float64UpDownCounter(name)
_, _ = mtr.Float64Histogram(name)
_, _ = mtr.Float64Gauge(name)
_, _ = mtr.Int64Counter(name)
_, _ = mtr.Int64UpDownCounter(name)
_, _ = mtr.Int64Histogram(name)
_, _ = mtr.Int64Gauge(name)
_, _ = mtr.RegisterCallback(zeroCallback)
if !once {
wg.Done()
Expand Down Expand Up @@ -142,13 +144,17 @@ func testSetupAllInstrumentTypes(t *testing.T, m metric.Meter) (metric.Float64Co
assert.NoError(t, err)
_, err = m.Float64Histogram("test_Sync_Histogram")
assert.NoError(t, err)
_, err = m.Float64Gauge("test_Sync_Gauge")
assert.NoError(t, err)

_, err = m.Int64Counter("test_Sync_Counter")
assert.NoError(t, err)
_, err = m.Int64UpDownCounter("test_Sync_UpDownCounter")
assert.NoError(t, err)
_, err = m.Int64Histogram("test_Sync_Histogram")
assert.NoError(t, err)
_, err = m.Int64Gauge("test_Sync_Gauge")
assert.NoError(t, err)

return sfcounter, afcounter
}
Expand Down
12 changes: 12 additions & 0 deletions internal/global/meter_types_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -36,10 +36,12 @@ type testMeter struct {
sfCount int
sfUDCount int
sfHist int
sfGauge int

siCount int
siUDCount int
siHist int
siGauge int

callbacks []metric.Callback
}
Expand All @@ -59,6 +61,11 @@ func (m *testMeter) Int64Histogram(name string, options ...metric.Int64Histogram
return &testCountingIntInstrument{}, nil
}

func (m *testMeter) Int64Gauge(name string, options ...metric.Int64GaugeOption) (metric.Int64Gauge, error) {
m.siGauge++
return &testCountingIntInstrument{}, nil
}

func (m *testMeter) Int64ObservableCounter(name string, options ...metric.Int64ObservableCounterOption) (metric.Int64ObservableCounter, error) {
m.aiCount++
return &testCountingIntInstrument{}, nil
Expand Down Expand Up @@ -89,6 +96,11 @@ func (m *testMeter) Float64Histogram(name string, options ...metric.Float64Histo
return &testCountingFloatInstrument{}, nil
}

func (m *testMeter) Float64Gauge(name string, options ...metric.Float64GaugeOption) (metric.Float64Gauge, error) {
m.sfGauge++
return &testCountingFloatInstrument{}, nil
}

func (m *testMeter) Float64ObservableCounter(name string, options ...metric.Float64ObservableCounterOption) (metric.Float64ObservableCounter, error) {
m.afCount++
return &testCountingFloatInstrument{}, nil
Expand Down
20 changes: 20 additions & 0 deletions metric/embedded/embedded.go
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,16 @@ type Float64Counter interface{ float64Counter() }
// the API package).
type Float64Histogram interface{ float64Histogram() }

// Float64Gauge is embedded in [go.opentelemetry.io/otel/metric.Float64Gauge].
//
// Embed this interface in your implementation of the
// [go.opentelemetry.io/otel/metric.Float64Gauge] if you want users to
// experience a compilation error, signaling they need to update to your latest
// implementation, when the [go.opentelemetry.io/otel/metric.Float64Gauge]
// interface is extended (which is something that can happen without a major
// version bump of the API package).
type Float64Gauge interface{ float64Gauge() }

// Float64ObservableCounter is embedded in
// [go.opentelemetry.io/otel/metric.Float64ObservableCounter].
//
Expand Down Expand Up @@ -174,6 +184,16 @@ type Int64Counter interface{ int64Counter() }
// the API package).
type Int64Histogram interface{ int64Histogram() }

// Int64Gauge is embedded in [go.opentelemetry.io/otel/metric.Int64Gauge].
//
// Embed this interface in your implementation of the
// [go.opentelemetry.io/otel/metric.Int64Gauge] if you want users to experience
// a compilation error, signaling they need to update to your latest
// implementation, when the [go.opentelemetry.io/otel/metric.Int64Gauge]
// interface is extended (which is something that can happen without a major
// version bump of the API package).
type Int64Gauge interface{ int64Gauge() }

// Int64ObservableCounter is embedded in
// [go.opentelemetry.io/otel/metric.Int64ObservableCounter].
//
Expand Down
22 changes: 22 additions & 0 deletions metric/instrument.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,13 +16,15 @@ type InstrumentOption interface {
Int64CounterOption
Int64UpDownCounterOption
Int64HistogramOption
Int64GaugeOption
Int64ObservableCounterOption
Int64ObservableUpDownCounterOption
Int64ObservableGaugeOption

Float64CounterOption
Float64UpDownCounterOption
Float64HistogramOption
Float64GaugeOption
Float64ObservableCounterOption
Float64ObservableUpDownCounterOption
Float64ObservableGaugeOption
Expand Down Expand Up @@ -51,6 +53,11 @@ func (o descOpt) applyFloat64Histogram(c Float64HistogramConfig) Float64Histogra
return c
}

func (o descOpt) applyFloat64Gauge(c Float64GaugeConfig) Float64GaugeConfig {
c.description = string(o)
return c
}

func (o descOpt) applyFloat64ObservableCounter(c Float64ObservableCounterConfig) Float64ObservableCounterConfig {
c.description = string(o)
return c
Expand Down Expand Up @@ -81,6 +88,11 @@ func (o descOpt) applyInt64Histogram(c Int64HistogramConfig) Int64HistogramConfi
return c
}

func (o descOpt) applyInt64Gauge(c Int64GaugeConfig) Int64GaugeConfig {
c.description = string(o)
return c
}

func (o descOpt) applyInt64ObservableCounter(c Int64ObservableCounterConfig) Int64ObservableCounterConfig {
c.description = string(o)
return c
Expand Down Expand Up @@ -116,6 +128,11 @@ func (o unitOpt) applyFloat64Histogram(c Float64HistogramConfig) Float64Histogra
return c
}

func (o unitOpt) applyFloat64Gauge(c Float64GaugeConfig) Float64GaugeConfig {
c.unit = string(o)
return c
}

func (o unitOpt) applyFloat64ObservableCounter(c Float64ObservableCounterConfig) Float64ObservableCounterConfig {
c.unit = string(o)
return c
Expand Down Expand Up @@ -146,6 +163,11 @@ func (o unitOpt) applyInt64Histogram(c Int64HistogramConfig) Int64HistogramConfi
return c
}

func (o unitOpt) applyInt64Gauge(c Int64GaugeConfig) Int64GaugeConfig {
c.unit = string(o)
return c
}

func (o unitOpt) applyInt64ObservableCounter(c Int64ObservableCounterConfig) Int64ObservableCounterConfig {
c.unit = string(o)
return c
Expand Down
8 changes: 8 additions & 0 deletions metric/meter.go
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,10 @@ type Meter interface {
// synchronously record the distribution of int64 measurements during a
// computational operation.
Int64Histogram(name string, options ...Int64HistogramOption) (Int64Histogram, error)
// Int64Gauge returns a new Int64Gauge instrument identified by name and
// configured with options. The instrument is used to synchronously record
// instantaneous int64 measurements during a computational operation.
Int64Gauge(name string, options ...Int64GaugeOption) (Int64Gauge, error)
// Int64ObservableCounter returns a new Int64ObservableCounter identified
// by name and configured with options. The instrument is used to
// asynchronously record increasing int64 measurements once per a
Expand Down Expand Up @@ -104,6 +108,10 @@ type Meter interface {
// synchronously record the distribution of float64 measurements during a
// computational operation.
Float64Histogram(name string, options ...Float64HistogramOption) (Float64Histogram, error)
// Float64Gauge returns a new Float64Gauge instrument identified by name and
// configured with options. The instrument is used to synchronously record
// instantaneous float64 measurements during a computational operation.
Float64Gauge(name string, options ...Float64GaugeOption) (Float64Gauge, error)
// Float64ObservableCounter returns a new Float64ObservableCounter
// instrument identified by name and configured with options. The
// instrument is used to asynchronously record increasing float64
Expand Down
28 changes: 28 additions & 0 deletions metric/noop/noop.go
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,8 @@ var (
_ metric.Float64UpDownCounter = Float64UpDownCounter{}
_ metric.Int64Histogram = Int64Histogram{}
_ metric.Float64Histogram = Float64Histogram{}
_ metric.Int64Gauge = Int64Gauge{}
_ metric.Float64Gauge = Float64Gauge{}
_ metric.Int64ObservableCounter = Int64ObservableCounter{}
_ metric.Float64ObservableCounter = Float64ObservableCounter{}
_ metric.Int64ObservableGauge = Int64ObservableGauge{}
Expand Down Expand Up @@ -76,6 +78,12 @@ func (Meter) Int64Histogram(string, ...metric.Int64HistogramOption) (metric.Int6
return Int64Histogram{}, nil
}

// Int64Gauge returns a Gauge used to record int64 measurements that
// produces no telemetry.
func (Meter) Int64Gauge(string, ...metric.Int64GaugeOption) (metric.Int64Gauge, error) {
return Int64Gauge{}, nil
}

// Int64ObservableCounter returns an ObservableCounter used to record int64
// measurements that produces no telemetry.
func (Meter) Int64ObservableCounter(string, ...metric.Int64ObservableCounterOption) (metric.Int64ObservableCounter, error) {
Expand Down Expand Up @@ -112,6 +120,12 @@ func (Meter) Float64Histogram(string, ...metric.Float64HistogramOption) (metric.
return Float64Histogram{}, nil
}

// Float64Gauge returns a Gauge used to record float64 measurements that
// produces no telemetry.
func (Meter) Float64Gauge(string, ...metric.Float64GaugeOption) (metric.Float64Gauge, error) {
return Float64Gauge{}, nil
}

// Float64ObservableCounter returns an ObservableCounter used to record int64
// measurements that produces no telemetry.
func (Meter) Float64ObservableCounter(string, ...metric.Float64ObservableCounterOption) (metric.Float64ObservableCounter, error) {
Expand Down Expand Up @@ -197,6 +211,20 @@ type Float64Histogram struct{ embedded.Float64Histogram }
// Record performs no operation.
func (Float64Histogram) Record(context.Context, float64, ...metric.RecordOption) {}

// Int64Gauge is an OpenTelemetry Gauge used to record instantaneous int64
// measurements. It produces no telemetry.
type Int64Gauge struct{ embedded.Int64Gauge }

// Record performs no operation.
func (Int64Gauge) Record(context.Context, int64, ...metric.RecordOption) {}

// Float64Gauge is an OpenTelemetry Gauge used to record instantaneous float64
// measurements. It produces no telemetry.
type Float64Gauge struct{ embedded.Float64Gauge }

// Record performs no operation.
func (Float64Gauge) Record(context.Context, float64, ...metric.RecordOption) {}

// Int64ObservableCounter is an OpenTelemetry ObservableCounter used to record
// int64 measurements. It produces no telemetry.
type Int64ObservableCounter struct {
Expand Down
Loading

0 comments on commit dafe137

Please sign in to comment.