You've already forked opentelemetry-go
mirror of
https://github.com/open-telemetry/opentelemetry-go.git
synced 2025-11-27 22:49:15 +02:00
Add the synchronous gauge to the metric API and SDK (#5304)
Resolve #5225 The specification has [added a synchronous gauge instrument](https://github.com/open-telemetry/opentelemetry-specification/pull/3540). That instrument has now been [stabilized](https://github.com/open-telemetry/opentelemetry-specification/pull/4019), and that stabilization is included in the [next release](https://github.com/open-telemetry/opentelemetry-specification/pull/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 <dashpole@google.com>
This commit is contained in:
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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...)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
@@ -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)
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
@@ -149,6 +161,7 @@ type testCountingFloatInstrument struct {
|
||||
embedded.Float64Counter
|
||||
embedded.Float64UpDownCounter
|
||||
embedded.Float64Histogram
|
||||
embedded.Float64Gauge
|
||||
embedded.Float64ObservableCounter
|
||||
embedded.Float64ObservableUpDownCounter
|
||||
embedded.Float64ObservableGauge
|
||||
@@ -173,6 +186,7 @@ type testCountingIntInstrument struct {
|
||||
embedded.Int64Counter
|
||||
embedded.Int64UpDownCounter
|
||||
embedded.Int64Histogram
|
||||
embedded.Int64Gauge
|
||||
embedded.Int64ObservableCounter
|
||||
embedded.Int64ObservableUpDownCounter
|
||||
embedded.Int64ObservableGauge
|
||||
|
||||
@@ -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...)
|
||||
@@ -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...)
|
||||
|
||||
@@ -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()
|
||||
@@ -142,6 +144,8 @@ 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)
|
||||
@@ -149,6 +153,8 @@ func testSetupAllInstrumentTypes(t *testing.T, m metric.Meter) (metric.Float64Co
|
||||
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
|
||||
}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
@@ -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
|
||||
@@ -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
|
||||
|
||||
@@ -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].
|
||||
//
|
||||
@@ -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].
|
||||
//
|
||||
|
||||
@@ -16,6 +16,7 @@ type InstrumentOption interface {
|
||||
Int64CounterOption
|
||||
Int64UpDownCounterOption
|
||||
Int64HistogramOption
|
||||
Int64GaugeOption
|
||||
Int64ObservableCounterOption
|
||||
Int64ObservableUpDownCounterOption
|
||||
Int64ObservableGaugeOption
|
||||
@@ -23,6 +24,7 @@ type InstrumentOption interface {
|
||||
Float64CounterOption
|
||||
Float64UpDownCounterOption
|
||||
Float64HistogramOption
|
||||
Float64GaugeOption
|
||||
Float64ObservableCounterOption
|
||||
Float64ObservableUpDownCounterOption
|
||||
Float64ObservableGaugeOption
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
@@ -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
|
||||
|
||||
@@ -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{}
|
||||
@@ -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) {
|
||||
@@ -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) {
|
||||
@@ -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 {
|
||||
|
||||
@@ -55,6 +55,14 @@ func TestImplementationNoPanics(t *testing.T) {
|
||||
reflect.ValueOf(Float64Histogram{}),
|
||||
reflect.TypeOf((*metric.Float64Histogram)(nil)).Elem(),
|
||||
))
|
||||
t.Run("Int64Gauge", assertAllExportedMethodNoPanic(
|
||||
reflect.ValueOf(Int64Gauge{}),
|
||||
reflect.TypeOf((*metric.Int64Gauge)(nil)).Elem(),
|
||||
))
|
||||
t.Run("Float64Gauge", assertAllExportedMethodNoPanic(
|
||||
reflect.ValueOf(Float64Gauge{}),
|
||||
reflect.TypeOf((*metric.Float64Gauge)(nil)).Elem(),
|
||||
))
|
||||
t.Run("Int64ObservableCounter", assertAllExportedMethodNoPanic(
|
||||
reflect.ValueOf(Int64ObservableCounter{}),
|
||||
reflect.TypeOf((*metric.Int64ObservableCounter)(nil)).Elem(),
|
||||
|
||||
@@ -172,3 +172,55 @@ func (c Float64HistogramConfig) ExplicitBucketBoundaries() []float64 {
|
||||
type Float64HistogramOption interface {
|
||||
applyFloat64Histogram(Float64HistogramConfig) Float64HistogramConfig
|
||||
}
|
||||
|
||||
// Float64Gauge is an instrument that records instantaneous float64 values.
|
||||
//
|
||||
// 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 Float64Gauge interface {
|
||||
// Users of the interface can ignore this. This embedded type is only used
|
||||
// by implementations of this interface. See the "API Implementations"
|
||||
// section of the package documentation for more information.
|
||||
embedded.Float64Gauge
|
||||
|
||||
// Record records the instantaneous value.
|
||||
//
|
||||
// Use the WithAttributeSet (or, if performance is not a concern,
|
||||
// the WithAttributes) option to include measurement attributes.
|
||||
Record(ctx context.Context, value float64, options ...RecordOption)
|
||||
}
|
||||
|
||||
// Float64GaugeConfig contains options for synchronous gauge instruments that
|
||||
// record float64 values.
|
||||
type Float64GaugeConfig struct {
|
||||
description string
|
||||
unit string
|
||||
}
|
||||
|
||||
// NewFloat64GaugeConfig returns a new [Float64GaugeConfig] with all opts
|
||||
// applied.
|
||||
func NewFloat64GaugeConfig(opts ...Float64GaugeOption) Float64GaugeConfig {
|
||||
var config Float64GaugeConfig
|
||||
for _, o := range opts {
|
||||
config = o.applyFloat64Gauge(config)
|
||||
}
|
||||
return config
|
||||
}
|
||||
|
||||
// Description returns the configured description.
|
||||
func (c Float64GaugeConfig) Description() string {
|
||||
return c.description
|
||||
}
|
||||
|
||||
// Unit returns the configured unit.
|
||||
func (c Float64GaugeConfig) Unit() string {
|
||||
return c.unit
|
||||
}
|
||||
|
||||
// Float64GaugeOption applies options to a [Float64GaugeConfig]. See
|
||||
// [InstrumentOption] for other options that can be used as a
|
||||
// Float64GaugeOption.
|
||||
type Float64GaugeOption interface {
|
||||
applyFloat64Gauge(Float64GaugeConfig) Float64GaugeConfig
|
||||
}
|
||||
|
||||
@@ -34,6 +34,10 @@ func TestFloat64Configuration(t *testing.T) {
|
||||
t.Run("Float64Histogram", run(
|
||||
NewFloat64HistogramConfig(WithDescription(desc), WithUnit(uBytes)),
|
||||
))
|
||||
|
||||
t.Run("Float64Gauge", run(
|
||||
NewFloat64GaugeConfig(WithDescription(desc), WithUnit(uBytes)),
|
||||
))
|
||||
}
|
||||
|
||||
type float64Config interface {
|
||||
|
||||
@@ -172,3 +172,55 @@ func (c Int64HistogramConfig) ExplicitBucketBoundaries() []float64 {
|
||||
type Int64HistogramOption interface {
|
||||
applyInt64Histogram(Int64HistogramConfig) Int64HistogramConfig
|
||||
}
|
||||
|
||||
// Int64Gauge is an instrument that records instantaneous int64 values.
|
||||
//
|
||||
// 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 Int64Gauge interface {
|
||||
// Users of the interface can ignore this. This embedded type is only used
|
||||
// by implementations of this interface. See the "API Implementations"
|
||||
// section of the package documentation for more information.
|
||||
embedded.Int64Gauge
|
||||
|
||||
// Record records the instantaneous value.
|
||||
//
|
||||
// Use the WithAttributeSet (or, if performance is not a concern,
|
||||
// the WithAttributes) option to include measurement attributes.
|
||||
Record(ctx context.Context, value int64, options ...RecordOption)
|
||||
}
|
||||
|
||||
// Int64GaugeConfig contains options for synchronous gauge instruments that
|
||||
// record int64 values.
|
||||
type Int64GaugeConfig struct {
|
||||
description string
|
||||
unit string
|
||||
}
|
||||
|
||||
// NewInt64GaugeConfig returns a new [Int64GaugeConfig] with all opts
|
||||
// applied.
|
||||
func NewInt64GaugeConfig(opts ...Int64GaugeOption) Int64GaugeConfig {
|
||||
var config Int64GaugeConfig
|
||||
for _, o := range opts {
|
||||
config = o.applyInt64Gauge(config)
|
||||
}
|
||||
return config
|
||||
}
|
||||
|
||||
// Description returns the configured description.
|
||||
func (c Int64GaugeConfig) Description() string {
|
||||
return c.description
|
||||
}
|
||||
|
||||
// Unit returns the configured unit.
|
||||
func (c Int64GaugeConfig) Unit() string {
|
||||
return c.unit
|
||||
}
|
||||
|
||||
// Int64GaugeOption applies options to a [Int64GaugeConfig]. See
|
||||
// [InstrumentOption] for other options that can be used as a
|
||||
// Int64GaugeOption.
|
||||
type Int64GaugeOption interface {
|
||||
applyInt64Gauge(Int64GaugeConfig) Int64GaugeConfig
|
||||
}
|
||||
|
||||
@@ -34,6 +34,10 @@ func TestInt64Configuration(t *testing.T) {
|
||||
t.Run("Int64Histogram", run(
|
||||
NewInt64HistogramConfig(WithDescription(desc), WithUnit(uBytes)),
|
||||
))
|
||||
|
||||
t.Run("Int64Gauge", run(
|
||||
NewInt64GaugeConfig(WithDescription(desc), WithUnit(uBytes)),
|
||||
))
|
||||
}
|
||||
|
||||
type int64Config interface {
|
||||
|
||||
@@ -42,6 +42,10 @@ const (
|
||||
// distribution of values synchronously with the code path they are
|
||||
// measuring.
|
||||
InstrumentKindHistogram
|
||||
// InstrumentKindGauge identifies a group of instruments that record
|
||||
// instantaneous values synchronously with the code path they are
|
||||
// measuring.
|
||||
InstrumentKindGauge
|
||||
// InstrumentKindObservableCounter identifies a group of instruments that
|
||||
// record increasing values in an asynchronous callback.
|
||||
InstrumentKindObservableCounter
|
||||
@@ -175,12 +179,14 @@ type int64Inst struct {
|
||||
embedded.Int64Counter
|
||||
embedded.Int64UpDownCounter
|
||||
embedded.Int64Histogram
|
||||
embedded.Int64Gauge
|
||||
}
|
||||
|
||||
var (
|
||||
_ metric.Int64Counter = (*int64Inst)(nil)
|
||||
_ metric.Int64UpDownCounter = (*int64Inst)(nil)
|
||||
_ metric.Int64Histogram = (*int64Inst)(nil)
|
||||
_ metric.Int64Gauge = (*int64Inst)(nil)
|
||||
)
|
||||
|
||||
func (i *int64Inst) Add(ctx context.Context, val int64, opts ...metric.AddOption) {
|
||||
@@ -205,12 +211,14 @@ type float64Inst struct {
|
||||
embedded.Float64Counter
|
||||
embedded.Float64UpDownCounter
|
||||
embedded.Float64Histogram
|
||||
embedded.Float64Gauge
|
||||
}
|
||||
|
||||
var (
|
||||
_ metric.Float64Counter = (*float64Inst)(nil)
|
||||
_ metric.Float64UpDownCounter = (*float64Inst)(nil)
|
||||
_ metric.Float64Histogram = (*float64Inst)(nil)
|
||||
_ metric.Float64Gauge = (*float64Inst)(nil)
|
||||
)
|
||||
|
||||
func (i *float64Inst) Add(ctx context.Context, val float64, opts ...metric.AddOption) {
|
||||
|
||||
@@ -25,7 +25,12 @@ func BenchmarkInstrument(b *testing.B) {
|
||||
build := aggregate.Builder[int64]{}
|
||||
var meas []aggregate.Measure[int64]
|
||||
|
||||
in, _ := build.PrecomputedLastValue()
|
||||
build.Temporality = metricdata.CumulativeTemporality
|
||||
in, _ := build.LastValue()
|
||||
meas = append(meas, in)
|
||||
|
||||
build.Temporality = metricdata.DeltaTemporality
|
||||
in, _ = build.LastValue()
|
||||
meas = append(meas, in)
|
||||
|
||||
build.Temporality = metricdata.CumulativeTemporality
|
||||
|
||||
@@ -12,14 +12,15 @@ func _() {
|
||||
_ = x[InstrumentKindCounter-1]
|
||||
_ = x[InstrumentKindUpDownCounter-2]
|
||||
_ = x[InstrumentKindHistogram-3]
|
||||
_ = x[InstrumentKindObservableCounter-4]
|
||||
_ = x[InstrumentKindObservableUpDownCounter-5]
|
||||
_ = x[InstrumentKindObservableGauge-6]
|
||||
_ = x[InstrumentKindGauge-4]
|
||||
_ = x[InstrumentKindObservableCounter-5]
|
||||
_ = x[InstrumentKindObservableUpDownCounter-6]
|
||||
_ = x[InstrumentKindObservableGauge-7]
|
||||
}
|
||||
|
||||
const _InstrumentKind_name = "instrumentKindUndefinedCounterUpDownCounterHistogramObservableCounterObservableUpDownCounterObservableGauge"
|
||||
const _InstrumentKind_name = "instrumentKindUndefinedCounterUpDownCounterHistogramGaugeObservableCounterObservableUpDownCounterObservableGauge"
|
||||
|
||||
var _InstrumentKind_index = [...]uint8{0, 23, 30, 43, 52, 69, 92, 107}
|
||||
var _InstrumentKind_index = [...]uint8{0, 23, 30, 43, 52, 57, 74, 97, 112}
|
||||
|
||||
func (i InstrumentKind) String() string {
|
||||
if i >= InstrumentKind(len(_InstrumentKind_index)-1) {
|
||||
|
||||
@@ -108,6 +108,21 @@ func (m *meter) Int64Histogram(name string, options ...metric.Int64HistogramOpti
|
||||
return i, validateInstrumentName(name)
|
||||
}
|
||||
|
||||
// Int64Gauge returns a new instrument identified by name and configured
|
||||
// with options. The instrument is used to synchronously record the
|
||||
// distribution of int64 measurements during a computational operation.
|
||||
func (m *meter) Int64Gauge(name string, options ...metric.Int64GaugeOption) (metric.Int64Gauge, error) {
|
||||
cfg := metric.NewInt64GaugeConfig(options...)
|
||||
const kind = InstrumentKindGauge
|
||||
p := int64InstProvider{m}
|
||||
i, err := p.lookup(kind, name, cfg.Description(), cfg.Unit())
|
||||
if err != nil {
|
||||
return i, err
|
||||
}
|
||||
|
||||
return i, validateInstrumentName(name)
|
||||
}
|
||||
|
||||
// int64ObservableInstrument returns a new observable identified by the Instrument.
|
||||
// It registers callbacks for each reader's pipeline.
|
||||
func (m *meter) int64ObservableInstrument(id Instrument, callbacks []metric.Int64Callback) (int64Observable, error) {
|
||||
@@ -242,6 +257,21 @@ func (m *meter) Float64Histogram(name string, options ...metric.Float64Histogram
|
||||
return i, validateInstrumentName(name)
|
||||
}
|
||||
|
||||
// Float64Gauge returns a new instrument identified by name and configured
|
||||
// with options. The instrument is used to synchronously record the
|
||||
// distribution of float64 measurements during a computational operation.
|
||||
func (m *meter) Float64Gauge(name string, options ...metric.Float64GaugeOption) (metric.Float64Gauge, error) {
|
||||
cfg := metric.NewFloat64GaugeConfig(options...)
|
||||
const kind = InstrumentKindGauge
|
||||
p := float64InstProvider{m}
|
||||
i, err := p.lookup(kind, name, cfg.Description(), cfg.Unit())
|
||||
if err != nil {
|
||||
return i, err
|
||||
}
|
||||
|
||||
return i, validateInstrumentName(name)
|
||||
}
|
||||
|
||||
// float64ObservableInstrument returns a new observable identified by the Instrument.
|
||||
// It registers callbacks for each reader's pipeline.
|
||||
func (m *meter) float64ObservableInstrument(id Instrument, callbacks []metric.Float64Callback) (float64Observable, error) {
|
||||
|
||||
@@ -447,10 +447,12 @@ func (i *inserter[N]) aggregateFunc(b aggregate.Builder[N], agg Aggregation, kin
|
||||
case AggregationDrop:
|
||||
// Return nil in and out to signify the drop aggregator.
|
||||
case AggregationLastValue:
|
||||
if kind == InstrumentKindObservableGauge {
|
||||
switch kind {
|
||||
case InstrumentKindGauge:
|
||||
meas, comp = b.LastValue()
|
||||
case InstrumentKindObservableGauge:
|
||||
meas, comp = b.PrecomputedLastValue()
|
||||
}
|
||||
// TODO (#5304): Support synchronous gauges.
|
||||
case AggregationSum:
|
||||
switch kind {
|
||||
case InstrumentKindObservableCounter:
|
||||
@@ -467,7 +469,7 @@ func (i *inserter[N]) aggregateFunc(b aggregate.Builder[N], agg Aggregation, kin
|
||||
case AggregationExplicitBucketHistogram:
|
||||
var noSum bool
|
||||
switch kind {
|
||||
case InstrumentKindUpDownCounter, InstrumentKindObservableUpDownCounter, InstrumentKindObservableGauge:
|
||||
case InstrumentKindUpDownCounter, InstrumentKindObservableUpDownCounter, InstrumentKindObservableGauge, InstrumentKindGauge:
|
||||
// The sum should not be collected for any instrument that can make
|
||||
// negative measurements:
|
||||
// https://github.com/open-telemetry/opentelemetry-specification/blob/v1.21.0/specification/metrics/sdk.md#histogram-aggregations
|
||||
@@ -477,7 +479,7 @@ func (i *inserter[N]) aggregateFunc(b aggregate.Builder[N], agg Aggregation, kin
|
||||
case AggregationBase2ExponentialHistogram:
|
||||
var noSum bool
|
||||
switch kind {
|
||||
case InstrumentKindUpDownCounter, InstrumentKindObservableUpDownCounter, InstrumentKindObservableGauge:
|
||||
case InstrumentKindUpDownCounter, InstrumentKindObservableUpDownCounter, InstrumentKindObservableGauge, InstrumentKindGauge:
|
||||
// The sum should not be collected for any instrument that can make
|
||||
// negative measurements:
|
||||
// https://github.com/open-telemetry/opentelemetry-specification/blob/v1.21.0/specification/metrics/sdk.md#histogram-aggregations
|
||||
@@ -500,6 +502,7 @@ func (i *inserter[N]) aggregateFunc(b aggregate.Builder[N], agg Aggregation, kin
|
||||
// | Counter | ✓ | | ✓ | ✓ | ✓ |
|
||||
// | UpDownCounter | ✓ | | ✓ | ✓ | ✓ |
|
||||
// | Histogram | ✓ | | ✓ | ✓ | ✓ |
|
||||
// | Gauge | ✓ | ✓ | | ✓ | ✓ |
|
||||
// | Observable Counter | ✓ | | ✓ | ✓ | ✓ |
|
||||
// | Observable UpDownCounter | ✓ | | ✓ | ✓ | ✓ |
|
||||
// | Observable Gauge | ✓ | ✓ | | ✓ | ✓ |.
|
||||
@@ -512,6 +515,7 @@ func isAggregatorCompatible(kind InstrumentKind, agg Aggregation) error {
|
||||
case InstrumentKindCounter,
|
||||
InstrumentKindUpDownCounter,
|
||||
InstrumentKindHistogram,
|
||||
InstrumentKindGauge,
|
||||
InstrumentKindObservableCounter,
|
||||
InstrumentKindObservableUpDownCounter,
|
||||
InstrumentKindObservableGauge:
|
||||
@@ -529,7 +533,8 @@ func isAggregatorCompatible(kind InstrumentKind, agg Aggregation) error {
|
||||
return errIncompatibleAggregation
|
||||
}
|
||||
case AggregationLastValue:
|
||||
if kind == InstrumentKindObservableGauge {
|
||||
switch kind {
|
||||
case InstrumentKindObservableGauge, InstrumentKindGauge:
|
||||
return nil
|
||||
}
|
||||
// TODO: review need for aggregation check after
|
||||
|
||||
@@ -149,6 +149,7 @@ func testCreateAggregators[N int64 | float64](t *testing.T) {
|
||||
{Name: "foo", Kind: InstrumentKindCounter},
|
||||
{Name: "foo", Kind: InstrumentKindUpDownCounter},
|
||||
{Name: "foo", Kind: InstrumentKindHistogram},
|
||||
{Name: "foo", Kind: InstrumentKindGauge},
|
||||
{Name: "foo", Kind: InstrumentKindObservableCounter},
|
||||
{Name: "foo", Kind: InstrumentKindObservableUpDownCounter},
|
||||
{Name: "foo", Kind: InstrumentKindObservableGauge},
|
||||
@@ -184,6 +185,12 @@ func testCreateAggregators[N int64 | float64](t *testing.T) {
|
||||
inst: instruments[InstrumentKindHistogram],
|
||||
validate: assertHist[N](metricdata.DeltaTemporality),
|
||||
},
|
||||
{
|
||||
name: "Default/Delta/Gauge",
|
||||
reader: NewManualReader(WithTemporalitySelector(deltaTemporalitySelector)),
|
||||
inst: instruments[InstrumentKindGauge],
|
||||
validate: assertLastValue[N],
|
||||
},
|
||||
{
|
||||
name: "Default/Delta/PrecomputedSum/Monotonic",
|
||||
reader: NewManualReader(WithTemporalitySelector(deltaTemporalitySelector)),
|
||||
@@ -220,6 +227,12 @@ func testCreateAggregators[N int64 | float64](t *testing.T) {
|
||||
inst: instruments[InstrumentKindHistogram],
|
||||
validate: assertHist[N](metricdata.CumulativeTemporality),
|
||||
},
|
||||
{
|
||||
name: "Default/Cumulative/Gauge",
|
||||
reader: NewManualReader(),
|
||||
inst: instruments[InstrumentKindGauge],
|
||||
validate: assertLastValue[N],
|
||||
},
|
||||
{
|
||||
name: "Default/Cumulative/PrecomputedSum/Monotonic",
|
||||
reader: NewManualReader(),
|
||||
@@ -307,6 +320,12 @@ func testCreateAggregators[N int64 | float64](t *testing.T) {
|
||||
inst: instruments[InstrumentKindHistogram],
|
||||
validate: assertHist[N](metricdata.CumulativeTemporality),
|
||||
},
|
||||
{
|
||||
name: "Reader/Default/Cumulative/Gauge",
|
||||
reader: NewManualReader(WithAggregationSelector(func(ik InstrumentKind) Aggregation { return AggregationDefault{} })),
|
||||
inst: instruments[InstrumentKindGauge],
|
||||
validate: assertLastValue[N],
|
||||
},
|
||||
{
|
||||
name: "Reader/Default/Cumulative/PrecomputedSum/Monotonic",
|
||||
reader: NewManualReader(WithAggregationSelector(func(ik InstrumentKind) Aggregation { return AggregationDefault{} })),
|
||||
@@ -699,6 +718,32 @@ func TestIsAggregatorCompatible(t *testing.T) {
|
||||
kind: InstrumentKindHistogram,
|
||||
agg: AggregationBase2ExponentialHistogram{},
|
||||
},
|
||||
{
|
||||
name: "SyncGauge and Drop",
|
||||
kind: InstrumentKindGauge,
|
||||
agg: AggregationDrop{},
|
||||
},
|
||||
{
|
||||
name: "SyncGauge and LastValue",
|
||||
kind: InstrumentKindGauge,
|
||||
agg: AggregationLastValue{},
|
||||
},
|
||||
{
|
||||
name: "SyncGauge and Sum",
|
||||
kind: InstrumentKindGauge,
|
||||
agg: AggregationSum{},
|
||||
want: errIncompatibleAggregation,
|
||||
},
|
||||
{
|
||||
name: "SyncGauge and ExplicitBucketHistogram",
|
||||
kind: InstrumentKindGauge,
|
||||
agg: AggregationExplicitBucketHistogram{},
|
||||
},
|
||||
{
|
||||
name: "SyncGauge and ExponentialHistogram",
|
||||
kind: InstrumentKindGauge,
|
||||
agg: AggregationBase2ExponentialHistogram{},
|
||||
},
|
||||
{
|
||||
name: "ObservableCounter and Drop",
|
||||
kind: InstrumentKindObservableCounter,
|
||||
|
||||
@@ -148,7 +148,7 @@ func DefaultAggregationSelector(ik InstrumentKind) Aggregation {
|
||||
switch ik {
|
||||
case InstrumentKindCounter, InstrumentKindUpDownCounter, InstrumentKindObservableCounter, InstrumentKindObservableUpDownCounter:
|
||||
return AggregationSum{}
|
||||
case InstrumentKindObservableGauge:
|
||||
case InstrumentKindObservableGauge, InstrumentKindGauge:
|
||||
return AggregationLastValue{}
|
||||
case InstrumentKindHistogram:
|
||||
return AggregationExplicitBucketHistogram{
|
||||
|
||||
@@ -300,6 +300,7 @@ func TestDefaultAggregationSelector(t *testing.T) {
|
||||
InstrumentKindCounter,
|
||||
InstrumentKindUpDownCounter,
|
||||
InstrumentKindHistogram,
|
||||
InstrumentKindGauge,
|
||||
InstrumentKindObservableCounter,
|
||||
InstrumentKindObservableUpDownCounter,
|
||||
InstrumentKindObservableGauge,
|
||||
@@ -317,6 +318,7 @@ func TestDefaultTemporalitySelector(t *testing.T) {
|
||||
InstrumentKindCounter,
|
||||
InstrumentKindUpDownCounter,
|
||||
InstrumentKindHistogram,
|
||||
InstrumentKindGauge,
|
||||
InstrumentKindObservableCounter,
|
||||
InstrumentKindObservableUpDownCounter,
|
||||
InstrumentKindObservableGauge,
|
||||
|
||||
@@ -185,6 +185,7 @@ func TestNewViewMatch(t *testing.T) {
|
||||
{},
|
||||
{Kind: InstrumentKindUpDownCounter},
|
||||
{Kind: InstrumentKindHistogram},
|
||||
{Kind: InstrumentKindGauge},
|
||||
{Kind: InstrumentKindObservableCounter},
|
||||
{Kind: InstrumentKindObservableUpDownCounter},
|
||||
{Kind: InstrumentKindObservableGauge},
|
||||
|
||||
Reference in New Issue
Block a user