diff --git a/internal/global/instruments.go b/internal/global/instruments.go index 3a0cc42f6a4..50c4a9fbfdd 100644 --- a/internal/global/instruments.go +++ b/internal/global/instruments.go @@ -229,6 +229,13 @@ func (i *sfCounter) Add(ctx context.Context, incr float64, opts ...metric.AddOpt } } +func (i *sfCounter) IsEnabled(ctx context.Context, opts ...metric.EnabledOption) bool { + if ctr := i.delegate.Load(); ctr != nil { + ctr.(metric.Float64Counter).IsEnabled(ctx, opts...) + } + return false +} + type sfUpDownCounter struct { embedded.Float64UpDownCounter @@ -255,6 +262,13 @@ func (i *sfUpDownCounter) Add(ctx context.Context, incr float64, opts ...metric. } } +func (i *sfUpDownCounter) IsEnabled(ctx context.Context, opts ...metric.EnabledOption) bool { + if ctr := i.delegate.Load(); ctr != nil { + ctr.(metric.Float64UpDownCounter).IsEnabled(ctx, opts...) + } + return false +} + type sfHistogram struct { embedded.Float64Histogram @@ -281,6 +295,13 @@ func (i *sfHistogram) Record(ctx context.Context, x float64, opts ...metric.Reco } } +func (i *sfHistogram) IsEnabled(ctx context.Context, opts ...metric.EnabledOption) bool { + if ctr := i.delegate.Load(); ctr != nil { + ctr.(metric.Float64Histogram).IsEnabled(ctx, opts...) + } + return false +} + type sfGauge struct { embedded.Float64Gauge @@ -307,6 +328,13 @@ func (i *sfGauge) Record(ctx context.Context, x float64, opts ...metric.RecordOp } } +func (i *sfGauge) IsEnabled(ctx context.Context, opts ...metric.EnabledOption) bool { + if ctr := i.delegate.Load(); ctr != nil { + ctr.(metric.Float64Gauge).IsEnabled(ctx, opts...) + } + return false +} + type siCounter struct { embedded.Int64Counter @@ -333,6 +361,13 @@ func (i *siCounter) Add(ctx context.Context, x int64, opts ...metric.AddOption) } } +func (i *siCounter) IsEnabled(ctx context.Context, opts ...metric.EnabledOption) bool { + if ctr := i.delegate.Load(); ctr != nil { + ctr.(metric.Int64Counter).IsEnabled(ctx, opts...) + } + return false +} + type siUpDownCounter struct { embedded.Int64UpDownCounter @@ -359,6 +394,13 @@ func (i *siUpDownCounter) Add(ctx context.Context, x int64, opts ...metric.AddOp } } +func (i *siUpDownCounter) IsEnabled(ctx context.Context, opts ...metric.EnabledOption) bool { + if ctr := i.delegate.Load(); ctr != nil { + ctr.(metric.Int64UpDownCounter).IsEnabled(ctx, opts...) + } + return false +} + type siHistogram struct { embedded.Int64Histogram @@ -385,6 +427,13 @@ func (i *siHistogram) Record(ctx context.Context, x int64, opts ...metric.Record } } +func (i *siHistogram) IsEnabled(ctx context.Context, opts ...metric.EnabledOption) bool { + if ctr := i.delegate.Load(); ctr != nil { + ctr.(metric.Int64Histogram).IsEnabled(ctx, opts...) + } + return false +} + type siGauge struct { embedded.Int64Gauge @@ -410,3 +459,10 @@ func (i *siGauge) Record(ctx context.Context, x int64, opts ...metric.RecordOpti ctr.(metric.Int64Gauge).Record(ctx, x, opts...) } } + +func (i *siGauge) IsEnabled(ctx context.Context, opts ...metric.EnabledOption) bool { + if ctr := i.delegate.Load(); ctr != nil { + ctr.(metric.Int64Gauge).IsEnabled(ctx, opts...) + } + return false +} diff --git a/internal/global/instruments_test.go b/internal/global/instruments_test.go index 48c772e3b8a..c988c5cf7c1 100644 --- a/internal/global/instruments_test.go +++ b/internal/global/instruments_test.go @@ -179,6 +179,10 @@ func (i *testCountingFloatInstrument) Record(context.Context, float64, ...metric i.count++ } +func (i *testCountingFloatInstrument) IsEnabled(context.Context, ...metric.EnabledOption) bool { + return true +} + type testCountingIntInstrument struct { count int @@ -203,3 +207,7 @@ func (i *testCountingIntInstrument) Add(context.Context, int64, ...metric.AddOpt func (i *testCountingIntInstrument) Record(context.Context, int64, ...metric.RecordOption) { i.count++ } + +func (i *testCountingIntInstrument) IsEnabled(context.Context, ...metric.EnabledOption) bool { + return true +} diff --git a/metric/instrument.go b/metric/instrument.go index ea52e402331..13aa911aadf 100644 --- a/metric/instrument.go +++ b/metric/instrument.go @@ -366,3 +366,9 @@ func WithAttributes(attributes ...attribute.KeyValue) MeasurementOption { copy(cp, attributes) return attrOpt{set: attribute.NewSet(cp...)} } + +// EnabledOption applies options to IsEnabled func. +type EnabledOption interface { + // Extend this to support EnabledOptionConfig when needed. + applyEnabledOption() +} diff --git a/metric/noop/noop.go b/metric/noop/noop.go index ca6fcbdc099..c9265a0dd4f 100644 --- a/metric/noop/noop.go +++ b/metric/noop/noop.go @@ -176,6 +176,11 @@ type Int64Counter struct{ embedded.Int64Counter } // Add performs no operation. func (Int64Counter) Add(context.Context, int64, ...metric.AddOption) {} +// IsEnabled returns false always. +func (Int64Counter) IsEnabled(ctx context.Context, opts ...metric.EnabledOption) bool { + return false +} + // Float64Counter is an OpenTelemetry Counter used to record float64 // measurements. It produces no telemetry. type Float64Counter struct{ embedded.Float64Counter } @@ -183,6 +188,11 @@ type Float64Counter struct{ embedded.Float64Counter } // Add performs no operation. func (Float64Counter) Add(context.Context, float64, ...metric.AddOption) {} +// IsEnabled returns false always. +func (Float64Counter) IsEnabled(ctx context.Context, opts ...metric.EnabledOption) bool { + return false +} + // Int64UpDownCounter is an OpenTelemetry UpDownCounter used to record int64 // measurements. It produces no telemetry. type Int64UpDownCounter struct{ embedded.Int64UpDownCounter } @@ -190,6 +200,11 @@ type Int64UpDownCounter struct{ embedded.Int64UpDownCounter } // Add performs no operation. func (Int64UpDownCounter) Add(context.Context, int64, ...metric.AddOption) {} +// IsEnabled returns false always. +func (Int64UpDownCounter) IsEnabled(ctx context.Context, opts ...metric.EnabledOption) bool { + return false +} + // Float64UpDownCounter is an OpenTelemetry UpDownCounter used to record // float64 measurements. It produces no telemetry. type Float64UpDownCounter struct{ embedded.Float64UpDownCounter } @@ -197,6 +212,11 @@ type Float64UpDownCounter struct{ embedded.Float64UpDownCounter } // Add performs no operation. func (Float64UpDownCounter) Add(context.Context, float64, ...metric.AddOption) {} +// IsEnabled returns false always. +func (Float64UpDownCounter) IsEnabled(ctx context.Context, opts ...metric.EnabledOption) bool { + return false +} + // Int64Histogram is an OpenTelemetry Histogram used to record int64 // measurements. It produces no telemetry. type Int64Histogram struct{ embedded.Int64Histogram } @@ -204,6 +224,11 @@ type Int64Histogram struct{ embedded.Int64Histogram } // Record performs no operation. func (Int64Histogram) Record(context.Context, int64, ...metric.RecordOption) {} +// IsEnabled returns false always. +func (Int64Histogram) IsEnabled(ctx context.Context, opts ...metric.EnabledOption) bool { + return false +} + // Float64Histogram is an OpenTelemetry Histogram used to record float64 // measurements. It produces no telemetry. type Float64Histogram struct{ embedded.Float64Histogram } @@ -211,6 +236,11 @@ type Float64Histogram struct{ embedded.Float64Histogram } // Record performs no operation. func (Float64Histogram) Record(context.Context, float64, ...metric.RecordOption) {} +// IsEnabled returns false always. +func (Float64Histogram) IsEnabled(ctx context.Context, opts ...metric.EnabledOption) bool { + return false +} + // Int64Gauge is an OpenTelemetry Gauge used to record instantaneous int64 // measurements. It produces no telemetry. type Int64Gauge struct{ embedded.Int64Gauge } @@ -218,6 +248,11 @@ type Int64Gauge struct{ embedded.Int64Gauge } // Record performs no operation. func (Int64Gauge) Record(context.Context, int64, ...metric.RecordOption) {} +// IsEnabled returns false always. +func (Int64Gauge) IsEnabled(ctx context.Context, opts ...metric.EnabledOption) bool { + return false +} + // Float64Gauge is an OpenTelemetry Gauge used to record instantaneous float64 // measurements. It produces no telemetry. type Float64Gauge struct{ embedded.Float64Gauge } @@ -225,6 +260,11 @@ type Float64Gauge struct{ embedded.Float64Gauge } // Record performs no operation. func (Float64Gauge) Record(context.Context, float64, ...metric.RecordOption) {} +// IsEnabled returns false always. +func (Float64Gauge) IsEnabled(ctx context.Context, opts ...metric.EnabledOption) bool { + return false +} + // Int64ObservableCounter is an OpenTelemetry ObservableCounter used to record // int64 measurements. It produces no telemetry. type Int64ObservableCounter struct { diff --git a/metric/syncfloat64.go b/metric/syncfloat64.go index 8403a4bad2d..809807f8bc5 100644 --- a/metric/syncfloat64.go +++ b/metric/syncfloat64.go @@ -25,6 +25,13 @@ type Float64Counter interface { // Use the WithAttributeSet (or, if performance is not a concern, // the WithAttributes) option to include measurement attributes. Add(ctx context.Context, incr float64, options ...AddOption) + + // IsEnabled returns true if this instrument is enabled. + // + // This helps users avoid performing computationally expensive operations when + // recording measurements. The returned value cannot be cached since it + // may change over time. + IsEnabled(ctx context.Context, options ...EnabledOption) bool } // Float64CounterConfig contains options for synchronous counter instruments that @@ -78,6 +85,13 @@ type Float64UpDownCounter interface { // Use the WithAttributeSet (or, if performance is not a concern, // the WithAttributes) option to include measurement attributes. Add(ctx context.Context, incr float64, options ...AddOption) + + // IsEnabled returns true if this instrument is enabled. + // + // This helps users avoid performing computationally expensive operations when + // recording measurements. The returned value cannot be cached since it + // may change over time. + IsEnabled(ctx context.Context, options ...EnabledOption) bool } // Float64UpDownCounterConfig contains options for synchronous counter @@ -131,6 +145,13 @@ type Float64Histogram interface { // Use the WithAttributeSet (or, if performance is not a concern, // the WithAttributes) option to include measurement attributes. Record(ctx context.Context, incr float64, options ...RecordOption) + + // IsEnabled returns true if this instrument is enabled. + // + // This helps users avoid performing computationally expensive operations when + // recording measurements. The returned value cannot be cached since it + // may change over time. + IsEnabled(ctx context.Context, options ...EnabledOption) bool } // Float64HistogramConfig contains options for synchronous histogram @@ -189,6 +210,13 @@ type Float64Gauge interface { // 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) + + // IsEnabled returns true if this instrument is enabled. + // + // This helps users avoid performing computationally expensive operations when + // recording measurements. The returned value cannot be cached since it + // may change over time. + IsEnabled(ctx context.Context, options ...EnabledOption) bool } // Float64GaugeConfig contains options for synchronous gauge instruments that diff --git a/metric/syncint64.go b/metric/syncint64.go index 783fdfba773..2700a9de982 100644 --- a/metric/syncint64.go +++ b/metric/syncint64.go @@ -25,6 +25,13 @@ type Int64Counter interface { // Use the WithAttributeSet (or, if performance is not a concern, // the WithAttributes) option to include measurement attributes. Add(ctx context.Context, incr int64, options ...AddOption) + + // IsEnabled returns true if this instrument is enabled. + // + // This helps users avoid performing computationally expensive operations when + // recording measurements. The returned value cannot be cached since it + // may change over time. + IsEnabled(ctx context.Context, options ...EnabledOption) bool } // Int64CounterConfig contains options for synchronous counter instruments that @@ -78,6 +85,13 @@ type Int64UpDownCounter interface { // Use the WithAttributeSet (or, if performance is not a concern, // the WithAttributes) option to include measurement attributes. Add(ctx context.Context, incr int64, options ...AddOption) + + // IsEnabled returns true if this instrument is enabled. + // + // This helps users avoid performing computationally expensive operations when + // recording measurements. The returned value cannot be cached since it + // may change over time. + IsEnabled(ctx context.Context, options ...EnabledOption) bool } // Int64UpDownCounterConfig contains options for synchronous counter @@ -131,6 +145,13 @@ type Int64Histogram interface { // Use the WithAttributeSet (or, if performance is not a concern, // the WithAttributes) option to include measurement attributes. Record(ctx context.Context, incr int64, options ...RecordOption) + + // IsEnabled returns true if this instrument is enabled. + // + // This helps users avoid performing computationally expensive operations when + // recording measurements. The returned value cannot be cached since it + // may change over time. + IsEnabled(ctx context.Context, options ...EnabledOption) bool } // Int64HistogramConfig contains options for synchronous histogram instruments @@ -189,6 +210,13 @@ type Int64Gauge interface { // 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) + + // IsEnabled returns true if this instrument is enabled. + // + // This helps users avoid performing computationally expensive operations when + // recording measurements. The returned value cannot be cached since it + // may change over time. + IsEnabled(ctx context.Context, options ...EnabledOption) bool } // Int64GaugeConfig contains options for synchronous gauge instruments that diff --git a/sdk/metric/instrument.go b/sdk/metric/instrument.go index b52a330b3bc..8dadd63c706 100644 --- a/sdk/metric/instrument.go +++ b/sdk/metric/instrument.go @@ -196,6 +196,10 @@ func (i *int64Inst) Record(ctx context.Context, val int64, opts ...metric.Record i.aggregate(ctx, val, c.Attributes()) } +func (i *int64Inst) IsEnabled(context.Context, ...metric.EnabledOption) bool { + return len(i.measures) != 0 +} + func (i *int64Inst) aggregate(ctx context.Context, val int64, s attribute.Set) { // nolint:revive // okay to shadow pkg with method. for _, in := range i.measures { in(ctx, val, s) @@ -228,6 +232,10 @@ func (i *float64Inst) Record(ctx context.Context, val float64, opts ...metric.Re i.aggregate(ctx, val, c.Attributes()) } +func (i *float64Inst) IsEnabled(context.Context, ...metric.EnabledOption) bool { + return len(i.measures) != 0 +} + func (i *float64Inst) aggregate(ctx context.Context, val float64, s attribute.Set) { for _, in := range i.measures { in(ctx, val, s) diff --git a/sdk/metric/meter_test.go b/sdk/metric/meter_test.go index da614e62428..9f40acd079a 100644 --- a/sdk/metric/meter_test.go +++ b/sdk/metric/meter_test.go @@ -387,6 +387,7 @@ func TestMeterCreatesInstruments(t *testing.T) { ctr, err := m.Int64Counter("sint") assert.NoError(t, err) + assert.True(t, ctr.IsEnabled(ctx)) ctr.Add(ctx, 3) }, want: metricdata.Metrics{ @@ -406,6 +407,7 @@ func TestMeterCreatesInstruments(t *testing.T) { ctr, err := m.Int64UpDownCounter("sint") assert.NoError(t, err) + assert.True(t, ctr.IsEnabled(ctx)) ctr.Add(ctx, 11) }, want: metricdata.Metrics{ @@ -419,13 +421,32 @@ func TestMeterCreatesInstruments(t *testing.T) { }, }, }, + { + name: "SyncInt64Gauge", + fn: func(t *testing.T, m metric.Meter) { + gauge, err := m.Int64Gauge("sint") + assert.NoError(t, err) + + assert.True(t, gauge.IsEnabled(ctx)) + gauge.Record(ctx, 11) + }, + want: metricdata.Metrics{ + Name: "sint", + Data: metricdata.Gauge[int64]{ + DataPoints: []metricdata.DataPoint[int64]{ + {Value: 11}, + }, + }, + }, + }, { name: "SyncInt64Histogram", fn: func(t *testing.T, m metric.Meter) { - gauge, err := m.Int64Histogram("histogram") + histo, err := m.Int64Histogram("histogram") assert.NoError(t, err) - gauge.Record(ctx, 7) + assert.True(t, histo.IsEnabled(ctx)) + histo.Record(ctx, 7) }, want: metricdata.Metrics{ Name: "histogram", @@ -451,6 +472,7 @@ func TestMeterCreatesInstruments(t *testing.T) { ctr, err := m.Float64Counter("sfloat") assert.NoError(t, err) + assert.True(t, ctr.IsEnabled(ctx)) ctr.Add(ctx, 3) }, want: metricdata.Metrics{ @@ -470,6 +492,7 @@ func TestMeterCreatesInstruments(t *testing.T) { ctr, err := m.Float64UpDownCounter("sfloat") assert.NoError(t, err) + assert.True(t, ctr.IsEnabled(ctx)) ctr.Add(ctx, 11) }, want: metricdata.Metrics{ @@ -483,13 +506,32 @@ func TestMeterCreatesInstruments(t *testing.T) { }, }, }, + { + name: "SyncFloat64Gauge", + fn: func(t *testing.T, m metric.Meter) { + gauge, err := m.Float64Gauge("sfloat") + assert.NoError(t, err) + + assert.True(t, gauge.IsEnabled(ctx)) + gauge.Record(ctx, 11) + }, + want: metricdata.Metrics{ + Name: "sfloat", + Data: metricdata.Gauge[float64]{ + DataPoints: []metricdata.DataPoint[float64]{ + {Value: 11}, + }, + }, + }, + }, { name: "SyncFloat64Histogram", fn: func(t *testing.T, m metric.Meter) { - gauge, err := m.Float64Histogram("histogram") + histo, err := m.Float64Histogram("histogram") assert.NoError(t, err) - gauge.Record(ctx, 7) + assert.True(t, histo.IsEnabled(ctx)) + histo.Record(ctx, 7) }, want: metricdata.Metrics{ Name: "histogram", @@ -531,6 +573,94 @@ func TestMeterCreatesInstruments(t *testing.T) { } } +func TestMeterWithDropView(t *testing.T) { + // The synchronous measurement methods must ignore the context cancellation. + ctx, cancel := context.WithCancel(context.Background()) + cancel() + + testCases := []struct { + name string + fn func(*testing.T, metric.Meter) + }{ + { + name: "SyncInt64Count", + fn: func(t *testing.T, m metric.Meter) { + ctr, err := m.Int64Counter("sint") + assert.NoError(t, err) + assert.False(t, ctr.IsEnabled(ctx)) + }, + }, + { + name: "SyncInt64UpDownCount", + fn: func(t *testing.T, m metric.Meter) { + ctr, err := m.Int64UpDownCounter("sint") + assert.NoError(t, err) + assert.False(t, ctr.IsEnabled(ctx)) + }, + }, + { + name: "SyncInt64Gauge", + fn: func(t *testing.T, m metric.Meter) { + gauge, err := m.Int64Gauge("sint") + assert.NoError(t, err) + assert.False(t, gauge.IsEnabled(ctx)) + }, + }, + { + name: "SyncInt64Histogram", + fn: func(t *testing.T, m metric.Meter) { + histo, err := m.Int64Histogram("histogram") + assert.NoError(t, err) + assert.False(t, histo.IsEnabled(ctx)) + }, + }, + { + name: "SyncFloat64Count", + fn: func(t *testing.T, m metric.Meter) { + ctr, err := m.Float64Counter("sfloat") + assert.NoError(t, err) + assert.False(t, ctr.IsEnabled(ctx)) + }, + }, + { + name: "SyncFloat64UpDownCount", + fn: func(t *testing.T, m metric.Meter) { + ctr, err := m.Float64UpDownCounter("sfloat") + assert.NoError(t, err) + assert.False(t, ctr.IsEnabled(ctx)) + }, + }, + { + name: "SyncFloat64Gauge", + fn: func(t *testing.T, m metric.Meter) { + gauge, err := m.Float64Gauge("sfloat") + assert.NoError(t, err) + assert.False(t, gauge.IsEnabled(ctx)) + }, + }, + { + name: "SyncFloat64Histogram", + fn: func(t *testing.T, m metric.Meter) { + histo, err := m.Float64Histogram("histogram") + assert.NoError(t, err) + assert.False(t, histo.IsEnabled(ctx)) + }, + }, + } + + for _, tt := range testCases { + t.Run(tt.name, func(t *testing.T) { + rdr := NewManualReader() + dropView := NewView( + Instrument{Name: "*"}, + Stream{Aggregation: AggregationDrop{}}, + ) + m := NewMeterProvider(WithReader(rdr), WithView(dropView)).Meter("testInstruments") + tt.fn(t, m) + }) + } +} + func TestMeterCreatesInstrumentsValidations(t *testing.T) { testCases := []struct { name string