mirror of
https://github.com/open-telemetry/opentelemetry-go.git
synced 2024-12-12 10:04:29 +02:00
Drop the gauge instrument (#537)
* drop gauge instrument * Restore the benchmark and stress test for lastvalue aggregator, but remove monotonic last-value support * Rename gauge->lastvalue and remove remaining uses of the word 'gauge' Co-authored-by: Krzesimir Nowak <krzesimir@kinvolk.io>
This commit is contained in:
parent
fe0099fb3d
commit
9674c81cb7
@ -14,7 +14,6 @@ import (
|
||||
sdk "go.opentelemetry.io/otel/sdk/metric"
|
||||
"go.opentelemetry.io/otel/sdk/metric/aggregator/counter"
|
||||
"go.opentelemetry.io/otel/sdk/metric/aggregator/ddsketch"
|
||||
"go.opentelemetry.io/otel/sdk/metric/aggregator/gauge"
|
||||
"go.opentelemetry.io/otel/sdk/metric/aggregator/minmaxsumcount"
|
||||
sdktrace "go.opentelemetry.io/otel/sdk/trace"
|
||||
)
|
||||
@ -41,8 +40,6 @@ func (*benchFixture) AggregatorFor(descriptor *export.Descriptor) export.Aggrega
|
||||
switch descriptor.MetricKind() {
|
||||
case export.CounterKind:
|
||||
return counter.New()
|
||||
case export.GaugeKind:
|
||||
return gauge.New()
|
||||
case export.MeasureKind:
|
||||
if strings.HasSuffix(descriptor.Name(), "minmaxsumcount") {
|
||||
return minmaxsumcount.New(descriptor)
|
||||
|
@ -35,7 +35,6 @@ type metricKind int8
|
||||
|
||||
const (
|
||||
counterKind metricKind = iota
|
||||
gaugeKind
|
||||
measureKind
|
||||
)
|
||||
|
||||
@ -200,11 +199,6 @@ func newInstDelegate(m metric.Meter, name string, mkind metricKind, nkind core.N
|
||||
return m.NewInt64Counter(name, opts.([]metric.CounterOptionApplier)...).Impl()
|
||||
}
|
||||
return m.NewFloat64Counter(name, opts.([]metric.CounterOptionApplier)...).Impl()
|
||||
case gaugeKind:
|
||||
if nkind == core.Int64NumberKind {
|
||||
return m.NewInt64Gauge(name, opts.([]metric.GaugeOptionApplier)...).Impl()
|
||||
}
|
||||
return m.NewFloat64Gauge(name, opts.([]metric.GaugeOptionApplier)...).Impl()
|
||||
case measureKind:
|
||||
if nkind == core.Int64NumberKind {
|
||||
return m.NewInt64Measure(name, opts.([]metric.MeasureOptionApplier)...).Impl()
|
||||
@ -386,14 +380,6 @@ func (m *meter) NewFloat64Counter(name string, opts ...metric.CounterOptionAppli
|
||||
return metric.WrapFloat64CounterInstrument(m.newInst(name, counterKind, core.Float64NumberKind, opts))
|
||||
}
|
||||
|
||||
func (m *meter) NewInt64Gauge(name string, opts ...metric.GaugeOptionApplier) metric.Int64Gauge {
|
||||
return metric.WrapInt64GaugeInstrument(m.newInst(name, gaugeKind, core.Int64NumberKind, opts))
|
||||
}
|
||||
|
||||
func (m *meter) NewFloat64Gauge(name string, opts ...metric.GaugeOptionApplier) metric.Float64Gauge {
|
||||
return metric.WrapFloat64GaugeInstrument(m.newInst(name, gaugeKind, core.Float64NumberKind, opts))
|
||||
}
|
||||
|
||||
func (m *meter) NewInt64Measure(name string, opts ...metric.MeasureOptionApplier) metric.Int64Measure {
|
||||
return metric.WrapInt64MeasureInstrument(m.newInst(name, measureKind, core.Int64NumberKind, opts))
|
||||
}
|
||||
|
@ -34,10 +34,6 @@ func TestDirect(t *testing.T) {
|
||||
counter.Add(ctx, 1, labels1)
|
||||
counter.Add(ctx, 1, labels1)
|
||||
|
||||
gauge := meter1.NewInt64Gauge("test.gauge")
|
||||
gauge.Set(ctx, 1, labels2)
|
||||
gauge.Set(ctx, 2, labels2)
|
||||
|
||||
measure := meter1.NewFloat64Measure("test.measure")
|
||||
measure.Record(ctx, 1, labels1)
|
||||
measure.Record(ctx, 2, labels1)
|
||||
@ -60,13 +56,12 @@ func TestDirect(t *testing.T) {
|
||||
global.SetMeterProvider(sdk)
|
||||
|
||||
counter.Add(ctx, 1, labels1)
|
||||
gauge.Set(ctx, 3, labels2)
|
||||
measure.Record(ctx, 3, labels1)
|
||||
second.Record(ctx, 3, labels3)
|
||||
|
||||
mock := sdk.Meter("test1").(*metrictest.Meter)
|
||||
mock.RunObservers()
|
||||
require.Len(t, mock.MeasurementBatches, 7)
|
||||
require.Len(t, mock.MeasurementBatches, 6)
|
||||
|
||||
require.Equal(t, map[core.Key]core.Value{
|
||||
lvals1.Key: lvals1.Value,
|
||||
@ -78,62 +73,53 @@ func TestDirect(t *testing.T) {
|
||||
mock.MeasurementBatches[0].Measurements[0].Instrument.Name)
|
||||
|
||||
require.Equal(t, map[core.Key]core.Value{
|
||||
lvals2.Key: lvals2.Value,
|
||||
lvals1.Key: lvals1.Value,
|
||||
}, mock.MeasurementBatches[1].LabelSet.Labels)
|
||||
require.Len(t, mock.MeasurementBatches[1].Measurements, 1)
|
||||
require.Equal(t, int64(3),
|
||||
mock.MeasurementBatches[1].Measurements[0].Number.AsInt64())
|
||||
require.Equal(t, "test.gauge",
|
||||
require.InDelta(t, float64(3),
|
||||
mock.MeasurementBatches[1].Measurements[0].Number.AsFloat64(),
|
||||
0.01)
|
||||
require.Equal(t, "test.measure",
|
||||
mock.MeasurementBatches[1].Measurements[0].Instrument.Name)
|
||||
|
||||
require.Equal(t, map[core.Key]core.Value{
|
||||
lvals1.Key: lvals1.Value,
|
||||
}, mock.MeasurementBatches[2].LabelSet.Labels)
|
||||
require.Len(t, mock.MeasurementBatches[2].Measurements, 1)
|
||||
require.InDelta(t, float64(3),
|
||||
require.InDelta(t, float64(1),
|
||||
mock.MeasurementBatches[2].Measurements[0].Number.AsFloat64(),
|
||||
0.01)
|
||||
require.Equal(t, "test.measure",
|
||||
require.Equal(t, "test.observer.float",
|
||||
mock.MeasurementBatches[2].Measurements[0].Instrument.Name)
|
||||
|
||||
require.Equal(t, map[core.Key]core.Value{
|
||||
lvals1.Key: lvals1.Value,
|
||||
lvals2.Key: lvals2.Value,
|
||||
}, mock.MeasurementBatches[3].LabelSet.Labels)
|
||||
require.Len(t, mock.MeasurementBatches[3].Measurements, 1)
|
||||
require.InDelta(t, float64(1),
|
||||
require.InDelta(t, float64(2),
|
||||
mock.MeasurementBatches[3].Measurements[0].Number.AsFloat64(),
|
||||
0.01)
|
||||
require.Equal(t, "test.observer.float",
|
||||
mock.MeasurementBatches[3].Measurements[0].Instrument.Name)
|
||||
|
||||
require.Equal(t, map[core.Key]core.Value{
|
||||
lvals2.Key: lvals2.Value,
|
||||
lvals1.Key: lvals1.Value,
|
||||
}, mock.MeasurementBatches[4].LabelSet.Labels)
|
||||
require.Len(t, mock.MeasurementBatches[4].Measurements, 1)
|
||||
require.InDelta(t, float64(2),
|
||||
mock.MeasurementBatches[4].Measurements[0].Number.AsFloat64(),
|
||||
0.01)
|
||||
require.Equal(t, "test.observer.float",
|
||||
require.Equal(t, int64(1),
|
||||
mock.MeasurementBatches[4].Measurements[0].Number.AsInt64())
|
||||
require.Equal(t, "test.observer.int",
|
||||
mock.MeasurementBatches[4].Measurements[0].Instrument.Name)
|
||||
|
||||
require.Equal(t, map[core.Key]core.Value{
|
||||
lvals1.Key: lvals1.Value,
|
||||
lvals2.Key: lvals2.Value,
|
||||
}, mock.MeasurementBatches[5].LabelSet.Labels)
|
||||
require.Len(t, mock.MeasurementBatches[5].Measurements, 1)
|
||||
require.Equal(t, int64(1),
|
||||
require.Equal(t, int64(2),
|
||||
mock.MeasurementBatches[5].Measurements[0].Number.AsInt64())
|
||||
require.Equal(t, "test.observer.int",
|
||||
mock.MeasurementBatches[5].Measurements[0].Instrument.Name)
|
||||
|
||||
require.Equal(t, map[core.Key]core.Value{
|
||||
lvals2.Key: lvals2.Value,
|
||||
}, mock.MeasurementBatches[6].LabelSet.Labels)
|
||||
require.Len(t, mock.MeasurementBatches[6].Measurements, 1)
|
||||
require.Equal(t, int64(2),
|
||||
mock.MeasurementBatches[6].Measurements[0].Number.AsInt64())
|
||||
require.Equal(t, "test.observer.int",
|
||||
mock.MeasurementBatches[6].Measurements[0].Instrument.Name)
|
||||
|
||||
// This tests the second Meter instance
|
||||
mock = sdk.Meter("test2").(*metrictest.Meter)
|
||||
require.Len(t, mock.MeasurementBatches, 1)
|
||||
@ -158,19 +144,12 @@ func TestBound(t *testing.T) {
|
||||
glob := global.MeterProvider().Meter("test")
|
||||
lvals1 := key.String("A", "B")
|
||||
labels1 := glob.Labels(lvals1)
|
||||
lvals2 := key.String("C", "D")
|
||||
labels2 := glob.Labels(lvals2)
|
||||
|
||||
counter := glob.NewFloat64Counter("test.counter")
|
||||
boundC := counter.Bind(labels1)
|
||||
boundC.Add(ctx, 1)
|
||||
boundC.Add(ctx, 1)
|
||||
|
||||
gauge := glob.NewFloat64Gauge("test.gauge")
|
||||
boundG := gauge.Bind(labels2)
|
||||
boundG.Set(ctx, 1)
|
||||
boundG.Set(ctx, 2)
|
||||
|
||||
measure := glob.NewInt64Measure("test.measure")
|
||||
boundM := measure.Bind(labels1)
|
||||
boundM.Record(ctx, 1)
|
||||
@ -180,16 +159,15 @@ func TestBound(t *testing.T) {
|
||||
global.SetMeterProvider(sdk)
|
||||
|
||||
boundC.Add(ctx, 1)
|
||||
boundG.Set(ctx, 3)
|
||||
boundM.Record(ctx, 3)
|
||||
|
||||
mock := sdk.Meter("test").(*metrictest.Meter)
|
||||
require.Equal(t, 3, len(mock.MeasurementBatches))
|
||||
require.Len(t, mock.MeasurementBatches, 2)
|
||||
|
||||
require.Equal(t, map[core.Key]core.Value{
|
||||
lvals1.Key: lvals1.Value,
|
||||
}, mock.MeasurementBatches[0].LabelSet.Labels)
|
||||
require.Equal(t, 1, len(mock.MeasurementBatches[0].Measurements))
|
||||
require.Len(t, mock.MeasurementBatches[0].Measurements, 1)
|
||||
require.InDelta(t, float64(1),
|
||||
mock.MeasurementBatches[0].Measurements[0].Number.AsFloat64(),
|
||||
0.01)
|
||||
@ -197,26 +175,15 @@ func TestBound(t *testing.T) {
|
||||
mock.MeasurementBatches[0].Measurements[0].Instrument.Name)
|
||||
|
||||
require.Equal(t, map[core.Key]core.Value{
|
||||
lvals2.Key: lvals2.Value,
|
||||
lvals1.Key: lvals1.Value,
|
||||
}, mock.MeasurementBatches[1].LabelSet.Labels)
|
||||
require.Equal(t, 1, len(mock.MeasurementBatches[1].Measurements))
|
||||
require.InDelta(t, float64(3),
|
||||
mock.MeasurementBatches[1].Measurements[0].Number.AsFloat64(),
|
||||
0.01)
|
||||
require.Equal(t, "test.gauge",
|
||||
require.Len(t, mock.MeasurementBatches[1].Measurements, 1)
|
||||
require.Equal(t, int64(3),
|
||||
mock.MeasurementBatches[1].Measurements[0].Number.AsInt64())
|
||||
require.Equal(t, "test.measure",
|
||||
mock.MeasurementBatches[1].Measurements[0].Instrument.Name)
|
||||
|
||||
require.Equal(t, map[core.Key]core.Value{
|
||||
lvals1.Key: lvals1.Value,
|
||||
}, mock.MeasurementBatches[2].LabelSet.Labels)
|
||||
require.Equal(t, 1, len(mock.MeasurementBatches[2].Measurements))
|
||||
require.Equal(t, int64(3),
|
||||
mock.MeasurementBatches[2].Measurements[0].Number.AsInt64())
|
||||
require.Equal(t, "test.measure",
|
||||
mock.MeasurementBatches[2].Measurements[0].Instrument.Name)
|
||||
|
||||
boundC.Unbind()
|
||||
boundG.Unbind()
|
||||
boundM.Unbind()
|
||||
}
|
||||
|
||||
@ -227,15 +194,10 @@ func TestUnbind(t *testing.T) {
|
||||
glob := global.MeterProvider().Meter("test")
|
||||
lvals1 := key.New("A").String("B")
|
||||
labels1 := glob.Labels(lvals1)
|
||||
lvals2 := key.New("C").String("D")
|
||||
labels2 := glob.Labels(lvals2)
|
||||
|
||||
counter := glob.NewFloat64Counter("test.counter")
|
||||
boundC := counter.Bind(labels1)
|
||||
|
||||
gauge := glob.NewFloat64Gauge("test.gauge")
|
||||
boundG := gauge.Bind(labels2)
|
||||
|
||||
measure := glob.NewInt64Measure("test.measure")
|
||||
boundM := measure.Bind(labels1)
|
||||
|
||||
@ -243,7 +205,6 @@ func TestUnbind(t *testing.T) {
|
||||
observerFloat := glob.RegisterFloat64Observer("test.observer.float", nil)
|
||||
|
||||
boundC.Unbind()
|
||||
boundG.Unbind()
|
||||
boundM.Unbind()
|
||||
observerInt.Unregister()
|
||||
observerFloat.Unregister()
|
||||
|
@ -49,11 +49,11 @@ type Options struct {
|
||||
// - for Counter, true implies that the metric is an up-down
|
||||
// Counter
|
||||
//
|
||||
// - for Gauge, true implies that the metric is a
|
||||
// non-descending Gauge
|
||||
//
|
||||
// - for Measure, true implies that the metric supports
|
||||
// positive and negative values
|
||||
//
|
||||
// - for Observer, true implies that the metric is a
|
||||
// non-descending Observer
|
||||
Alternate bool
|
||||
}
|
||||
|
||||
@ -65,14 +65,6 @@ type CounterOptionApplier interface {
|
||||
ApplyCounterOption(*Options)
|
||||
}
|
||||
|
||||
// GaugeOptionApplier is an interface for applying metric options that
|
||||
// are valid only for gauge metrics.
|
||||
type GaugeOptionApplier interface {
|
||||
// ApplyGaugeOption is used to make some general or
|
||||
// gauge-specific changes in the Options.
|
||||
ApplyGaugeOption(*Options)
|
||||
}
|
||||
|
||||
// MeasureOptionApplier is an interface for applying metric options
|
||||
// that are valid only for measure metrics.
|
||||
type MeasureOptionApplier interface {
|
||||
@ -122,12 +114,6 @@ type Meter interface {
|
||||
// NewFloat64Counter creates a new floating point counter with
|
||||
// a given name and customized with passed options.
|
||||
NewFloat64Counter(name string, cos ...CounterOptionApplier) Float64Counter
|
||||
// NewInt64Gauge creates a new integral gauge with a given
|
||||
// name and customized with passed options.
|
||||
NewInt64Gauge(name string, gos ...GaugeOptionApplier) Int64Gauge
|
||||
// NewFloat64Gauge creates a new floating point gauge with a
|
||||
// given name and customized with passed options.
|
||||
NewFloat64Gauge(name string, gos ...GaugeOptionApplier) Float64Gauge
|
||||
// NewInt64Measure creates a new integral measure with a given
|
||||
// name and customized with passed options.
|
||||
NewInt64Measure(name string, mos ...MeasureOptionApplier) Int64Measure
|
||||
@ -187,7 +173,6 @@ type Option func(*Options)
|
||||
// valid for all the kinds of metrics.
|
||||
type OptionApplier interface {
|
||||
CounterOptionApplier
|
||||
GaugeOptionApplier
|
||||
MeasureOptionApplier
|
||||
ObserverOptionApplier
|
||||
// ApplyOption is used to make some general changes in the
|
||||
@ -195,12 +180,10 @@ type OptionApplier interface {
|
||||
ApplyOption(*Options)
|
||||
}
|
||||
|
||||
// CounterGaugeObserverOptionApplier is an interface for applying
|
||||
// metric options that are valid for counter, gauge or observer
|
||||
// metrics.
|
||||
type CounterGaugeObserverOptionApplier interface {
|
||||
// CounterObserverOptionApplier is an interface for applying metric
|
||||
// options that are valid for counter or observer metrics.
|
||||
type CounterObserverOptionApplier interface {
|
||||
CounterOptionApplier
|
||||
GaugeOptionApplier
|
||||
ObserverOptionApplier
|
||||
}
|
||||
|
||||
@ -212,10 +195,6 @@ type counterOptionWrapper struct {
|
||||
F Option
|
||||
}
|
||||
|
||||
type gaugeOptionWrapper struct {
|
||||
F Option
|
||||
}
|
||||
|
||||
type measureOptionWrapper struct {
|
||||
F Option
|
||||
}
|
||||
@ -224,16 +203,14 @@ type observerOptionWrapper struct {
|
||||
F Option
|
||||
}
|
||||
|
||||
type counterGaugeObserverOptionWrapper struct {
|
||||
type counterObserverOptionWrapper struct {
|
||||
FC Option
|
||||
FG Option
|
||||
FO Option
|
||||
}
|
||||
|
||||
var (
|
||||
_ OptionApplier = optionWrapper{}
|
||||
_ CounterOptionApplier = counterOptionWrapper{}
|
||||
_ GaugeOptionApplier = gaugeOptionWrapper{}
|
||||
_ MeasureOptionApplier = measureOptionWrapper{}
|
||||
_ ObserverOptionApplier = observerOptionWrapper{}
|
||||
)
|
||||
@ -242,10 +219,6 @@ func (o optionWrapper) ApplyCounterOption(opts *Options) {
|
||||
o.ApplyOption(opts)
|
||||
}
|
||||
|
||||
func (o optionWrapper) ApplyGaugeOption(opts *Options) {
|
||||
o.ApplyOption(opts)
|
||||
}
|
||||
|
||||
func (o optionWrapper) ApplyMeasureOption(opts *Options) {
|
||||
o.ApplyOption(opts)
|
||||
}
|
||||
@ -262,23 +235,15 @@ func (o counterOptionWrapper) ApplyCounterOption(opts *Options) {
|
||||
o.F(opts)
|
||||
}
|
||||
|
||||
func (o gaugeOptionWrapper) ApplyGaugeOption(opts *Options) {
|
||||
o.F(opts)
|
||||
}
|
||||
|
||||
func (o measureOptionWrapper) ApplyMeasureOption(opts *Options) {
|
||||
o.F(opts)
|
||||
}
|
||||
|
||||
func (o counterGaugeObserverOptionWrapper) ApplyCounterOption(opts *Options) {
|
||||
func (o counterObserverOptionWrapper) ApplyCounterOption(opts *Options) {
|
||||
o.FC(opts)
|
||||
}
|
||||
|
||||
func (o counterGaugeObserverOptionWrapper) ApplyGaugeOption(opts *Options) {
|
||||
o.FG(opts)
|
||||
}
|
||||
|
||||
func (o counterGaugeObserverOptionWrapper) ApplyObserverOption(opts *Options) {
|
||||
func (o counterObserverOptionWrapper) ApplyObserverOption(opts *Options) {
|
||||
o.FO(opts)
|
||||
}
|
||||
|
||||
@ -314,16 +279,13 @@ func WithKeys(keys ...core.Key) OptionApplier {
|
||||
}
|
||||
}
|
||||
|
||||
// WithMonotonic sets whether a counter, a gauge or an observer is not
|
||||
// WithMonotonic sets whether a counter or an observer is not
|
||||
// permitted to go down.
|
||||
func WithMonotonic(monotonic bool) CounterGaugeObserverOptionApplier {
|
||||
return counterGaugeObserverOptionWrapper{
|
||||
func WithMonotonic(monotonic bool) CounterObserverOptionApplier {
|
||||
return counterObserverOptionWrapper{
|
||||
FC: func(opts *Options) {
|
||||
opts.Alternate = !monotonic
|
||||
},
|
||||
FG: func(opts *Options) {
|
||||
opts.Alternate = monotonic
|
||||
},
|
||||
FO: func(opts *Options) {
|
||||
opts.Alternate = monotonic
|
||||
},
|
||||
|
@ -140,117 +140,6 @@ func TestCounterOptions(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestGaugeOptions(t *testing.T) {
|
||||
type testcase struct {
|
||||
name string
|
||||
opts []metric.GaugeOptionApplier
|
||||
keys []core.Key
|
||||
desc string
|
||||
unit unit.Unit
|
||||
alt bool
|
||||
}
|
||||
testcases := []testcase{
|
||||
{
|
||||
name: "no opts",
|
||||
opts: nil,
|
||||
keys: nil,
|
||||
desc: "",
|
||||
unit: "",
|
||||
alt: false,
|
||||
},
|
||||
{
|
||||
name: "keys keys keys",
|
||||
opts: []metric.GaugeOptionApplier{
|
||||
metric.WithKeys(key.New("foo"), key.New("foo2")),
|
||||
metric.WithKeys(key.New("bar"), key.New("bar2")),
|
||||
metric.WithKeys(key.New("baz"), key.New("baz2")),
|
||||
},
|
||||
keys: []core.Key{
|
||||
key.New("foo"), key.New("foo2"),
|
||||
key.New("bar"), key.New("bar2"),
|
||||
key.New("baz"), key.New("baz2"),
|
||||
},
|
||||
desc: "",
|
||||
unit: "",
|
||||
alt: false,
|
||||
},
|
||||
{
|
||||
name: "description",
|
||||
opts: []metric.GaugeOptionApplier{
|
||||
metric.WithDescription("stuff"),
|
||||
},
|
||||
keys: nil,
|
||||
desc: "stuff",
|
||||
unit: "",
|
||||
alt: false,
|
||||
},
|
||||
{
|
||||
name: "description override",
|
||||
opts: []metric.GaugeOptionApplier{
|
||||
metric.WithDescription("stuff"),
|
||||
metric.WithDescription("things"),
|
||||
},
|
||||
keys: nil,
|
||||
desc: "things",
|
||||
unit: "",
|
||||
alt: false,
|
||||
},
|
||||
{
|
||||
name: "unit",
|
||||
opts: []metric.GaugeOptionApplier{
|
||||
metric.WithUnit("s"),
|
||||
},
|
||||
keys: nil,
|
||||
desc: "",
|
||||
unit: "s",
|
||||
alt: false,
|
||||
},
|
||||
{
|
||||
name: "unit override",
|
||||
opts: []metric.GaugeOptionApplier{
|
||||
metric.WithUnit("s"),
|
||||
metric.WithUnit("h"),
|
||||
},
|
||||
keys: nil,
|
||||
desc: "",
|
||||
unit: "h",
|
||||
alt: false,
|
||||
},
|
||||
{
|
||||
name: "monotonic",
|
||||
opts: []metric.GaugeOptionApplier{
|
||||
metric.WithMonotonic(true),
|
||||
},
|
||||
keys: nil,
|
||||
desc: "",
|
||||
unit: "",
|
||||
alt: true,
|
||||
},
|
||||
{
|
||||
name: "monotonic, but not really",
|
||||
opts: []metric.GaugeOptionApplier{
|
||||
metric.WithMonotonic(true),
|
||||
metric.WithMonotonic(false),
|
||||
},
|
||||
keys: nil,
|
||||
desc: "",
|
||||
unit: "",
|
||||
alt: false,
|
||||
},
|
||||
}
|
||||
for idx, tt := range testcases {
|
||||
t.Logf("Testing gauge case %s (%d)", tt.name, idx)
|
||||
opts := &metric.Options{}
|
||||
metric.ApplyGaugeOptions(opts, tt.opts...)
|
||||
checkOptions(t, opts, &metric.Options{
|
||||
Description: tt.desc,
|
||||
Unit: tt.unit,
|
||||
Keys: tt.keys,
|
||||
Alternate: tt.alt,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestMeasureOptions(t *testing.T) {
|
||||
type testcase struct {
|
||||
name string
|
||||
@ -506,33 +395,6 @@ func TestCounter(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestGauge(t *testing.T) {
|
||||
{
|
||||
meter := mock.NewMeter()
|
||||
g := meter.NewFloat64Gauge("test.gauge.float")
|
||||
ctx := context.Background()
|
||||
labels := meter.Labels()
|
||||
g.Set(ctx, 42, labels)
|
||||
boundInstrument := g.Bind(labels)
|
||||
boundInstrument.Set(ctx, 42)
|
||||
meter.RecordBatch(ctx, labels, g.Measurement(42))
|
||||
t.Log("Testing float gauge")
|
||||
checkBatches(t, ctx, labels, meter, core.Float64NumberKind, g.Impl())
|
||||
}
|
||||
{
|
||||
meter := mock.NewMeter()
|
||||
g := meter.NewInt64Gauge("test.gauge.int")
|
||||
ctx := context.Background()
|
||||
labels := meter.Labels()
|
||||
g.Set(ctx, 42, labels)
|
||||
boundInstrument := g.Bind(labels)
|
||||
boundInstrument.Set(ctx, 42)
|
||||
meter.RecordBatch(ctx, labels, g.Measurement(42))
|
||||
t.Log("Testing int gauge")
|
||||
checkBatches(t, ctx, labels, meter, core.Int64NumberKind, g.Impl())
|
||||
}
|
||||
}
|
||||
|
||||
func TestMeasure(t *testing.T) {
|
||||
{
|
||||
meter := mock.NewMeter()
|
||||
|
@ -15,10 +15,9 @@
|
||||
// metric package provides an API for reporting diagnostic
|
||||
// measurements using four basic kinds of instruments.
|
||||
//
|
||||
// The four basic kinds are:
|
||||
// The three basic kinds are:
|
||||
//
|
||||
// - counters
|
||||
// - gauges
|
||||
// - measures
|
||||
// - observers
|
||||
//
|
||||
@ -42,15 +41,6 @@
|
||||
// function - this allows reporting negative values. To report the new
|
||||
// value, use an Add function.
|
||||
//
|
||||
// Gauges are instruments that are reporting a current state of a
|
||||
// value. An example could be voltage or temperature. Gauges can be
|
||||
// created with either NewFloat64Gauge or NewInt64Gauge. Gauges by
|
||||
// default have no limitations about reported values - they can be
|
||||
// less or greater than the last reported value. This can be changed
|
||||
// with the WithMonotonic option passed to the New*Gauge function -
|
||||
// this permits the reported values only to go up. To report a new
|
||||
// value, use the Set function.
|
||||
//
|
||||
// Measures are instruments that are reporting values that are
|
||||
// recorded separately to figure out some statistical properties from
|
||||
// those values (like average). An example could be temperature over
|
||||
@ -74,11 +64,11 @@
|
||||
// callback can report multiple values. To unregister the observer,
|
||||
// call Unregister on it.
|
||||
//
|
||||
// Counters, gauges and measures support creating bound instruments
|
||||
// for a potentially more efficient reporting. The bound instruments
|
||||
// have the same function names as the instruments (so a Counter bound
|
||||
// instrument has Add, a Gauge bound instrument has Set, and a Measure
|
||||
// bound instrument has Record). Bound Instruments can be created
|
||||
// with the Bind function of the respective instrument. When done with
|
||||
// the bound instrument, call Unbind on it.
|
||||
// Counters and measures support creating bound instruments for a
|
||||
// potentially more efficient reporting. The bound instruments have
|
||||
// the same function names as the instruments (so a Counter bound
|
||||
// instrument has Add, and a Measure bound instrument has Record).
|
||||
// Bound Instruments can be created with the Bind function of the
|
||||
// respective instrument. When done with the bound instrument, call
|
||||
// Unbind on it.
|
||||
package metric // import "go.opentelemetry.io/otel/api/metric"
|
||||
|
@ -1,113 +0,0 @@
|
||||
// Copyright 2019, 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 metric
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"go.opentelemetry.io/otel/api/core"
|
||||
)
|
||||
|
||||
// Float64Gauge is a metric that stores the last float64 value.
|
||||
type Float64Gauge struct {
|
||||
commonMetric
|
||||
}
|
||||
|
||||
// Int64Gauge is a metric that stores the last int64 value.
|
||||
type Int64Gauge struct {
|
||||
commonMetric
|
||||
}
|
||||
|
||||
// BoundFloat64Gauge is a bound instrument for Float64Gauge.
|
||||
//
|
||||
// It inherits the Unbind function from commonBoundInstrument.
|
||||
type BoundFloat64Gauge struct {
|
||||
commonBoundInstrument
|
||||
}
|
||||
|
||||
// BoundInt64Gauge is a bound instrument for Int64Gauge.
|
||||
//
|
||||
// It inherits the Unbind function from commonBoundInstrument.
|
||||
type BoundInt64Gauge struct {
|
||||
commonBoundInstrument
|
||||
}
|
||||
|
||||
// Bind creates a bound instrument for this gauge. The labels should
|
||||
// contain the keys and values for each key specified in the gauge
|
||||
// with the WithKeys option.
|
||||
//
|
||||
// If the labels do not contain a value for the key specified in the
|
||||
// gauge with the WithKeys option, then the missing value will be
|
||||
// treated as unspecified.
|
||||
func (g Float64Gauge) Bind(labels LabelSet) (h BoundFloat64Gauge) {
|
||||
h.commonBoundInstrument = g.bind(labels)
|
||||
return
|
||||
}
|
||||
|
||||
// Bind creates a bound instrument for this gauge. The labels should
|
||||
// contain the keys and values for each key specified in the gauge
|
||||
// with the WithKeys option.
|
||||
//
|
||||
// If the labels do not contain a value for the key specified in the
|
||||
// gauge with the WithKeys option, then the missing value will be
|
||||
// treated as unspecified.
|
||||
func (g Int64Gauge) Bind(labels LabelSet) (h BoundInt64Gauge) {
|
||||
h.commonBoundInstrument = g.bind(labels)
|
||||
return
|
||||
}
|
||||
|
||||
// Measurement creates a Measurement object to use with batch
|
||||
// recording.
|
||||
func (g Float64Gauge) Measurement(value float64) Measurement {
|
||||
return g.float64Measurement(value)
|
||||
}
|
||||
|
||||
// Measurement creates a Measurement object to use with batch
|
||||
// recording.
|
||||
func (g Int64Gauge) Measurement(value int64) Measurement {
|
||||
return g.int64Measurement(value)
|
||||
}
|
||||
|
||||
// Set assigns the passed value to the value of the gauge. The labels
|
||||
// should contain the keys and values for each key specified in the
|
||||
// gauge with the WithKeys option.
|
||||
//
|
||||
// If the labels do not contain a value for the key specified in the
|
||||
// gauge with the WithKeys option, then the missing value will be
|
||||
// treated as unspecified.
|
||||
func (g Float64Gauge) Set(ctx context.Context, value float64, labels LabelSet) {
|
||||
g.directRecord(ctx, core.NewFloat64Number(value), labels)
|
||||
}
|
||||
|
||||
// Set assigns the passed value to the value of the gauge. The labels
|
||||
// should contain the keys and values for each key specified in the
|
||||
// gauge with the WithKeys option.
|
||||
//
|
||||
// If the labels do not contain a value for the key specified in the
|
||||
// gauge with the WithKeys option, then the missing value will be
|
||||
// treated as unspecified.
|
||||
func (g Int64Gauge) Set(ctx context.Context, value int64, labels LabelSet) {
|
||||
g.directRecord(ctx, core.NewInt64Number(value), labels)
|
||||
}
|
||||
|
||||
// Set assigns the passed value to the value of the gauge.
|
||||
func (b BoundFloat64Gauge) Set(ctx context.Context, value float64) {
|
||||
b.directRecord(ctx, core.NewFloat64Number(value))
|
||||
}
|
||||
|
||||
// Set assigns the passed value to the value of the gauge.
|
||||
func (b BoundInt64Gauge) Set(ctx context.Context, value int64) {
|
||||
b.directRecord(ctx, core.NewInt64Number(value))
|
||||
}
|
@ -61,14 +61,6 @@ func (NoopMeter) NewFloat64Counter(name string, cos ...CounterOptionApplier) Flo
|
||||
return WrapFloat64CounterInstrument(noopInstrument{})
|
||||
}
|
||||
|
||||
func (NoopMeter) NewInt64Gauge(name string, gos ...GaugeOptionApplier) Int64Gauge {
|
||||
return WrapInt64GaugeInstrument(noopInstrument{})
|
||||
}
|
||||
|
||||
func (NoopMeter) NewFloat64Gauge(name string, gos ...GaugeOptionApplier) Float64Gauge {
|
||||
return WrapFloat64GaugeInstrument(noopInstrument{})
|
||||
}
|
||||
|
||||
func (NoopMeter) NewInt64Measure(name string, mos ...MeasureOptionApplier) Int64Measure {
|
||||
return WrapInt64MeasureInstrument(noopInstrument{})
|
||||
}
|
||||
|
@ -67,22 +67,6 @@ func WrapFloat64CounterInstrument(instrument InstrumentImpl) Float64Counter {
|
||||
return Float64Counter{commonMetric: newCommonMetric(instrument)}
|
||||
}
|
||||
|
||||
// WrapInt64GaugeInstrument wraps the instrument in the type-safe
|
||||
// wrapper as an integral gauge.
|
||||
//
|
||||
// It is mostly intended for SDKs.
|
||||
func WrapInt64GaugeInstrument(instrument InstrumentImpl) Int64Gauge {
|
||||
return Int64Gauge{commonMetric: newCommonMetric(instrument)}
|
||||
}
|
||||
|
||||
// WrapFloat64GaugeInstrument wraps the instrument in the type-safe
|
||||
// wrapper as an floating point gauge.
|
||||
//
|
||||
// It is mostly intended for SDKs.
|
||||
func WrapFloat64GaugeInstrument(instrument InstrumentImpl) Float64Gauge {
|
||||
return Float64Gauge{commonMetric: newCommonMetric(instrument)}
|
||||
}
|
||||
|
||||
// WrapInt64MeasureInstrument wraps the instrument in the type-safe
|
||||
// wrapper as an integral measure.
|
||||
//
|
||||
@ -107,14 +91,6 @@ func ApplyCounterOptions(opts *Options, cos ...CounterOptionApplier) {
|
||||
}
|
||||
}
|
||||
|
||||
// ApplyGaugeOptions is a helper that applies all the gauge options to
|
||||
// passed opts.
|
||||
func ApplyGaugeOptions(opts *Options, gos ...GaugeOptionApplier) {
|
||||
for _, o := range gos {
|
||||
o.ApplyGaugeOption(opts)
|
||||
}
|
||||
}
|
||||
|
||||
// ApplyMeasureOptions is a helper that applies all the measure
|
||||
// options to passed opts.
|
||||
func ApplyMeasureOptions(opts *Options, mos ...MeasureOptionApplier) {
|
||||
|
@ -73,10 +73,16 @@ func main() {
|
||||
tracer := global.TraceProvider().Tracer("ex.com/basic")
|
||||
meter := global.MeterProvider().Meter("ex.com/basic")
|
||||
|
||||
oneMetric := meter.NewFloat64Gauge("ex.com.one",
|
||||
commonLabels := meter.Labels(lemonsKey.Int(10), key.String("A", "1"), key.String("B", "2"), key.String("C", "3"))
|
||||
|
||||
oneMetricCB := func(result metric.Float64ObserverResult) {
|
||||
result.Observe(1, commonLabels)
|
||||
}
|
||||
oneMetric := meter.RegisterFloat64Observer("ex.com.one", oneMetricCB,
|
||||
metric.WithKeys(fooKey, barKey, lemonsKey),
|
||||
metric.WithDescription("A gauge set to 1.0"),
|
||||
metric.WithDescription("An observer set to 1.0"),
|
||||
)
|
||||
defer oneMetric.Unregister()
|
||||
|
||||
measureTwo := meter.NewFloat64Measure("ex.com.two")
|
||||
|
||||
@ -87,11 +93,6 @@ func main() {
|
||||
barKey.String("bar1"),
|
||||
)
|
||||
|
||||
commonLabels := meter.Labels(lemonsKey.Int(10), key.String("A", "1"), key.String("B", "2"), key.String("C", "3"))
|
||||
|
||||
gauge := oneMetric.Bind(commonLabels)
|
||||
defer gauge.Unbind()
|
||||
|
||||
measure := measureTwo.Bind(commonLabels)
|
||||
defer measure.Unbind()
|
||||
|
||||
@ -101,14 +102,11 @@ func main() {
|
||||
|
||||
trace.SpanFromContext(ctx).SetAttributes(anotherKey.String("yes"))
|
||||
|
||||
gauge.Set(ctx, 1)
|
||||
|
||||
meter.RecordBatch(
|
||||
// Note: call-site variables added as context Entries:
|
||||
correlation.NewContext(ctx, anotherKey.String("xyz")),
|
||||
commonLabels,
|
||||
|
||||
oneMetric.Measurement(1.0),
|
||||
measureTwo.Measurement(2.0),
|
||||
)
|
||||
|
||||
|
@ -18,6 +18,7 @@ import (
|
||||
"context"
|
||||
"log"
|
||||
"net/http"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"go.opentelemetry.io/otel/api/global"
|
||||
@ -50,11 +51,21 @@ func main() {
|
||||
defer initMeter().Stop()
|
||||
|
||||
meter := global.MeterProvider().Meter("ex.com/basic")
|
||||
|
||||
oneMetric := meter.NewFloat64Gauge("ex.com.one",
|
||||
observerLock := new(sync.RWMutex)
|
||||
observerValueToReport := new(float64)
|
||||
observerLabelSetToReport := new(metric.LabelSet)
|
||||
cb := func(result metric.Float64ObserverResult) {
|
||||
(*observerLock).RLock()
|
||||
value := *observerValueToReport
|
||||
labelset := *observerLabelSetToReport
|
||||
(*observerLock).RUnlock()
|
||||
result.Observe(value, labelset)
|
||||
}
|
||||
oneMetric := meter.RegisterFloat64Observer("ex.com.one", cb,
|
||||
metric.WithKeys(fooKey, barKey, lemonsKey),
|
||||
metric.WithDescription("A gauge set to 1.0"),
|
||||
metric.WithDescription("A measure set to 1.0"),
|
||||
)
|
||||
defer oneMetric.Unregister()
|
||||
|
||||
measureTwo := meter.NewFloat64Measure("ex.com.two", metric.WithKeys(key.New("A")))
|
||||
measureThree := meter.NewFloat64Counter("ex.com.three")
|
||||
@ -64,28 +75,39 @@ func main() {
|
||||
|
||||
ctx := context.Background()
|
||||
|
||||
(*observerLock).Lock()
|
||||
*observerValueToReport = 1.0
|
||||
*observerLabelSetToReport = &commonLabels
|
||||
(*observerLock).Unlock()
|
||||
meter.RecordBatch(
|
||||
ctx,
|
||||
commonLabels,
|
||||
oneMetric.Measurement(1.0),
|
||||
measureTwo.Measurement(2.0),
|
||||
measureThree.Measurement(12.0),
|
||||
)
|
||||
|
||||
time.Sleep(5 * time.Second)
|
||||
|
||||
(*observerLock).Lock()
|
||||
*observerValueToReport = 1.0
|
||||
*observerLabelSetToReport = ¬SoCommonLabels
|
||||
(*observerLock).Unlock()
|
||||
meter.RecordBatch(
|
||||
ctx,
|
||||
notSoCommonLabels,
|
||||
oneMetric.Measurement(1.0),
|
||||
measureTwo.Measurement(2.0),
|
||||
measureThree.Measurement(22.0),
|
||||
)
|
||||
|
||||
time.Sleep(5 * time.Second)
|
||||
|
||||
(*observerLock).Lock()
|
||||
*observerValueToReport = 13.0
|
||||
*observerLabelSetToReport = &commonLabels
|
||||
(*observerLock).Unlock()
|
||||
meter.RecordBatch(
|
||||
ctx,
|
||||
commonLabels,
|
||||
oneMetric.Measurement(13.0),
|
||||
measureTwo.Measurement(12.0),
|
||||
measureThree.Measurement(13.0),
|
||||
)
|
||||
|
@ -92,13 +92,13 @@ func TestBasicFormat(t *testing.T) {
|
||||
for _, ao := range []adapterOutput{{
|
||||
adapter: newWithTagsAdapter(),
|
||||
expected: `counter:%s|c|#A:B,C:D
|
||||
gauge:%s|g|#A:B,C:D
|
||||
observer:%s|g|#A:B,C:D
|
||||
measure:%s|h|#A:B,C:D
|
||||
timer:%s|ms|#A:B,C:D
|
||||
`}, {
|
||||
adapter: newNoTagsAdapter(),
|
||||
expected: `counter.B.D:%s|c
|
||||
gauge.B.D:%s|g
|
||||
observer.B.D:%s|g
|
||||
measure.B.D:%s|h
|
||||
timer.B.D:%s|ms
|
||||
`},
|
||||
@ -126,7 +126,7 @@ timer.B.D:%s|ms
|
||||
cdesc := export.NewDescriptor(
|
||||
"counter", export.CounterKind, nil, "", "", nkind, false)
|
||||
gdesc := export.NewDescriptor(
|
||||
"gauge", export.GaugeKind, nil, "", "", nkind, false)
|
||||
"observer", export.ObserverKind, nil, "", "", nkind, false)
|
||||
mdesc := export.NewDescriptor(
|
||||
"measure", export.MeasureKind, nil, "", "", nkind, false)
|
||||
tdesc := export.NewDescriptor(
|
||||
@ -139,7 +139,7 @@ timer.B.D:%s|ms
|
||||
const value = 123.456
|
||||
|
||||
checkpointSet.AddCounter(cdesc, value, labels...)
|
||||
checkpointSet.AddGauge(gdesc, value, labels...)
|
||||
checkpointSet.AddLastValue(gdesc, value, labels...)
|
||||
checkpointSet.AddMeasure(mdesc, value, labels...)
|
||||
checkpointSet.AddMeasure(tdesc, value, labels...)
|
||||
|
||||
|
@ -219,20 +219,20 @@ func (c *collector) Collect(ch chan<- prometheus.Metric) {
|
||||
c.exportSummary(ch, dist, numberKind, desc, labels)
|
||||
} else if sum, ok := agg.(aggregator.Sum); ok {
|
||||
c.exportCounter(ch, sum, numberKind, desc, labels)
|
||||
} else if gauge, ok := agg.(aggregator.LastValue); ok {
|
||||
c.exportGauge(ch, gauge, numberKind, desc, labels)
|
||||
} else if lastValue, ok := agg.(aggregator.LastValue); ok {
|
||||
c.exportLastValue(ch, lastValue, numberKind, desc, labels)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func (c *collector) exportGauge(ch chan<- prometheus.Metric, gauge aggregator.LastValue, kind core.NumberKind, desc *prometheus.Desc, labels []string) {
|
||||
lastValue, _, err := gauge.LastValue()
|
||||
func (c *collector) exportLastValue(ch chan<- prometheus.Metric, lvagg aggregator.LastValue, kind core.NumberKind, desc *prometheus.Desc, labels []string) {
|
||||
lv, _, err := lvagg.LastValue()
|
||||
if err != nil {
|
||||
c.exp.onError(err)
|
||||
return
|
||||
}
|
||||
|
||||
m, err := prometheus.NewConstMetric(desc, prometheus.GaugeValue, lastValue.CoerceToFloat64(kind), labels...)
|
||||
m, err := prometheus.NewConstMetric(desc, prometheus.GaugeValue, lv.CoerceToFloat64(kind), labels...)
|
||||
if err != nil {
|
||||
c.exp.onError(err)
|
||||
return
|
||||
|
@ -31,8 +31,8 @@ func TestPrometheusExporter(t *testing.T) {
|
||||
|
||||
counter := export.NewDescriptor(
|
||||
"counter", export.CounterKind, nil, "", "", core.Float64NumberKind, false)
|
||||
gauge := export.NewDescriptor(
|
||||
"gauge", export.GaugeKind, nil, "", "", core.Float64NumberKind, false)
|
||||
lastValue := export.NewDescriptor(
|
||||
"lastvalue", export.ObserverKind, nil, "", "", core.Float64NumberKind, false)
|
||||
measure := export.NewDescriptor(
|
||||
"measure", export.MeasureKind, nil, "", "", core.Float64NumberKind, false)
|
||||
|
||||
@ -44,8 +44,8 @@ func TestPrometheusExporter(t *testing.T) {
|
||||
checkpointSet.AddCounter(counter, 15.3, labels...)
|
||||
expected = append(expected, `counter{A="B",C="D"} 15.3`)
|
||||
|
||||
checkpointSet.AddGauge(gauge, 13.2, labels...)
|
||||
expected = append(expected, `gauge{A="B",C="D"} 13.2`)
|
||||
checkpointSet.AddLastValue(lastValue, 13.2, labels...)
|
||||
expected = append(expected, `lastvalue{A="B",C="D"} 13.2`)
|
||||
|
||||
checkpointSet.AddMeasure(measure, 13, labels...)
|
||||
checkpointSet.AddMeasure(measure, 15, labels...)
|
||||
@ -64,8 +64,8 @@ func TestPrometheusExporter(t *testing.T) {
|
||||
checkpointSet.AddCounter(counter, 12, missingLabels...)
|
||||
expected = append(expected, `counter{A="E",C=""} 12`)
|
||||
|
||||
checkpointSet.AddGauge(gauge, 32, missingLabels...)
|
||||
expected = append(expected, `gauge{A="E",C=""} 32`)
|
||||
checkpointSet.AddLastValue(lastValue, 32, missingLabels...)
|
||||
expected = append(expected, `lastvalue{A="E",C=""} 32`)
|
||||
|
||||
checkpointSet.AddMeasure(measure, 19, missingLabels...)
|
||||
expected = append(expected, `measure{A="E",C="",quantile="0.5"} 19`)
|
||||
|
@ -20,7 +20,7 @@ import (
|
||||
"go.opentelemetry.io/otel/sdk/metric/aggregator/array"
|
||||
"go.opentelemetry.io/otel/sdk/metric/aggregator/counter"
|
||||
"go.opentelemetry.io/otel/sdk/metric/aggregator/ddsketch"
|
||||
"go.opentelemetry.io/otel/sdk/metric/aggregator/gauge"
|
||||
"go.opentelemetry.io/otel/sdk/metric/aggregator/lastvalue"
|
||||
"go.opentelemetry.io/otel/sdk/metric/aggregator/minmaxsumcount"
|
||||
aggtest "go.opentelemetry.io/otel/sdk/metric/aggregator/test"
|
||||
)
|
||||
@ -82,12 +82,12 @@ func TestStdoutTimestamp(t *testing.T) {
|
||||
checkpointSet := test.NewCheckpointSet(sdk.NewDefaultLabelEncoder())
|
||||
|
||||
ctx := context.Background()
|
||||
desc := export.NewDescriptor("test.name", export.GaugeKind, nil, "", "", core.Int64NumberKind, false)
|
||||
gagg := gauge.New()
|
||||
aggtest.CheckedUpdate(t, gagg, core.NewInt64Number(321), desc)
|
||||
gagg.Checkpoint(ctx, desc)
|
||||
desc := export.NewDescriptor("test.name", export.ObserverKind, nil, "", "", core.Int64NumberKind, false)
|
||||
lvagg := lastvalue.New()
|
||||
aggtest.CheckedUpdate(t, lvagg, core.NewInt64Number(321), desc)
|
||||
lvagg.Checkpoint(ctx, desc)
|
||||
|
||||
checkpointSet.Add(desc, gagg)
|
||||
checkpointSet.Add(desc, lvagg)
|
||||
|
||||
if err := exporter.Export(ctx, checkpointSet); err != nil {
|
||||
t.Fatal("Unexpected export error: ", err)
|
||||
@ -107,19 +107,19 @@ func TestStdoutTimestamp(t *testing.T) {
|
||||
t.Fatal("JSON parse error: ", updateTS, ": ", err)
|
||||
}
|
||||
|
||||
gaugeTS := printed["updates"].([]interface{})[0].(map[string]interface{})["time"].(string)
|
||||
gaugeTimestamp, err := time.Parse(time.RFC3339Nano, gaugeTS)
|
||||
lastValueTS := printed["updates"].([]interface{})[0].(map[string]interface{})["time"].(string)
|
||||
lastValueTimestamp, err := time.Parse(time.RFC3339Nano, lastValueTS)
|
||||
if err != nil {
|
||||
t.Fatal("JSON parse error: ", gaugeTS, ": ", err)
|
||||
t.Fatal("JSON parse error: ", lastValueTS, ": ", err)
|
||||
}
|
||||
|
||||
require.True(t, updateTimestamp.After(before))
|
||||
require.True(t, updateTimestamp.Before(after))
|
||||
|
||||
require.True(t, gaugeTimestamp.After(before))
|
||||
require.True(t, gaugeTimestamp.Before(after))
|
||||
require.True(t, lastValueTimestamp.After(before))
|
||||
require.True(t, lastValueTimestamp.Before(after))
|
||||
|
||||
require.True(t, gaugeTimestamp.Before(updateTimestamp))
|
||||
require.True(t, lastValueTimestamp.Before(updateTimestamp))
|
||||
}
|
||||
|
||||
func TestStdoutCounterFormat(t *testing.T) {
|
||||
@ -139,17 +139,17 @@ func TestStdoutCounterFormat(t *testing.T) {
|
||||
require.Equal(t, `{"updates":[{"name":"test.name{A=B,C=D}","sum":123}]}`, fix.Output())
|
||||
}
|
||||
|
||||
func TestStdoutGaugeFormat(t *testing.T) {
|
||||
func TestStdoutLastValueFormat(t *testing.T) {
|
||||
fix := newFixture(t, stdout.Config{})
|
||||
|
||||
checkpointSet := test.NewCheckpointSet(sdk.NewDefaultLabelEncoder())
|
||||
|
||||
desc := export.NewDescriptor("test.name", export.GaugeKind, nil, "", "", core.Float64NumberKind, false)
|
||||
gagg := gauge.New()
|
||||
aggtest.CheckedUpdate(fix.t, gagg, core.NewFloat64Number(123.456), desc)
|
||||
gagg.Checkpoint(fix.ctx, desc)
|
||||
desc := export.NewDescriptor("test.name", export.ObserverKind, nil, "", "", core.Float64NumberKind, false)
|
||||
lvagg := lastvalue.New()
|
||||
aggtest.CheckedUpdate(fix.t, lvagg, core.NewFloat64Number(123.456), desc)
|
||||
lvagg.Checkpoint(fix.ctx, desc)
|
||||
|
||||
checkpointSet.Add(desc, gagg, key.String("A", "B"), key.String("C", "D"))
|
||||
checkpointSet.Add(desc, lvagg, key.String("A", "B"), key.String("C", "D"))
|
||||
|
||||
fix.Export(checkpointSet)
|
||||
|
||||
@ -247,16 +247,16 @@ func TestStdoutEmptyDataSet(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestStdoutGaugeNotSet(t *testing.T) {
|
||||
func TestStdoutLastValueNotSet(t *testing.T) {
|
||||
fix := newFixture(t, stdout.Config{})
|
||||
|
||||
checkpointSet := test.NewCheckpointSet(sdk.NewDefaultLabelEncoder())
|
||||
|
||||
desc := export.NewDescriptor("test.name", export.GaugeKind, nil, "", "", core.Float64NumberKind, false)
|
||||
gagg := gauge.New()
|
||||
gagg.Checkpoint(fix.ctx, desc)
|
||||
desc := export.NewDescriptor("test.name", export.ObserverKind, nil, "", "", core.Float64NumberKind, false)
|
||||
lvagg := lastvalue.New()
|
||||
lvagg.Checkpoint(fix.ctx, desc)
|
||||
|
||||
checkpointSet.Add(desc, gagg, key.String("A", "B"), key.String("C", "D"))
|
||||
checkpointSet.Add(desc, lvagg, key.String("A", "B"), key.String("C", "D"))
|
||||
|
||||
fix.Export(checkpointSet)
|
||||
|
||||
|
@ -7,7 +7,7 @@ import (
|
||||
export "go.opentelemetry.io/otel/sdk/export/metric"
|
||||
"go.opentelemetry.io/otel/sdk/metric/aggregator/array"
|
||||
"go.opentelemetry.io/otel/sdk/metric/aggregator/counter"
|
||||
"go.opentelemetry.io/otel/sdk/metric/aggregator/gauge"
|
||||
"go.opentelemetry.io/otel/sdk/metric/aggregator/lastvalue"
|
||||
)
|
||||
|
||||
type CheckpointSet struct {
|
||||
@ -56,8 +56,8 @@ func createNumber(desc *export.Descriptor, v float64) core.Number {
|
||||
return core.NewInt64Number(int64(v))
|
||||
}
|
||||
|
||||
func (p *CheckpointSet) AddGauge(desc *export.Descriptor, v float64, labels ...core.KeyValue) {
|
||||
p.updateAggregator(desc, gauge.New(), v, labels...)
|
||||
func (p *CheckpointSet) AddLastValue(desc *export.Descriptor, v float64, labels ...core.KeyValue) {
|
||||
p.updateAggregator(desc, lastvalue.New(), v, labels...)
|
||||
}
|
||||
|
||||
func (p *CheckpointSet) AddCounter(desc *export.Descriptor, v float64, labels ...core.KeyValue) {
|
||||
|
@ -103,7 +103,6 @@ var (
|
||||
|
||||
const (
|
||||
KindCounter Kind = iota
|
||||
KindGauge
|
||||
KindMeasure
|
||||
KindObserver
|
||||
)
|
||||
@ -213,27 +212,6 @@ func (m *Meter) newCounterInstrument(name string, numberKind core.NumberKind, co
|
||||
}
|
||||
}
|
||||
|
||||
func (m *Meter) NewInt64Gauge(name string, gos ...apimetric.GaugeOptionApplier) apimetric.Int64Gauge {
|
||||
instrument := m.newGaugeInstrument(name, core.Int64NumberKind, gos...)
|
||||
return apimetric.WrapInt64GaugeInstrument(instrument)
|
||||
}
|
||||
|
||||
func (m *Meter) NewFloat64Gauge(name string, gos ...apimetric.GaugeOptionApplier) apimetric.Float64Gauge {
|
||||
instrument := m.newGaugeInstrument(name, core.Float64NumberKind, gos...)
|
||||
return apimetric.WrapFloat64GaugeInstrument(instrument)
|
||||
}
|
||||
|
||||
func (m *Meter) newGaugeInstrument(name string, numberKind core.NumberKind, gos ...apimetric.GaugeOptionApplier) *Instrument {
|
||||
opts := apimetric.Options{}
|
||||
apimetric.ApplyGaugeOptions(&opts, gos...)
|
||||
return &Instrument{
|
||||
Name: name,
|
||||
Kind: KindGauge,
|
||||
NumberKind: numberKind,
|
||||
Opts: opts,
|
||||
}
|
||||
}
|
||||
|
||||
func (m *Meter) NewInt64Measure(name string, mos ...apimetric.MeasureOptionApplier) apimetric.Int64Measure {
|
||||
instrument := m.newMeasureInstrument(name, core.Int64NumberKind, mos...)
|
||||
return apimetric.WrapInt64MeasureInstrument(instrument)
|
||||
|
@ -26,14 +26,14 @@ import (
|
||||
export "go.opentelemetry.io/otel/sdk/export/metric"
|
||||
"go.opentelemetry.io/otel/sdk/export/metric/aggregator"
|
||||
"go.opentelemetry.io/otel/sdk/metric/aggregator/counter"
|
||||
"go.opentelemetry.io/otel/sdk/metric/aggregator/gauge"
|
||||
"go.opentelemetry.io/otel/sdk/metric/aggregator/lastvalue"
|
||||
)
|
||||
|
||||
func TestInconsistentMergeErr(t *testing.T) {
|
||||
err := aggregator.NewInconsistentMergeError(counter.New(), gauge.New())
|
||||
err := aggregator.NewInconsistentMergeError(counter.New(), lastvalue.New())
|
||||
require.Equal(
|
||||
t,
|
||||
"cannot merge *counter.Aggregator with *gauge.Aggregator: inconsistent aggregator types",
|
||||
"cannot merge *counter.Aggregator with *lastvalue.Aggregator: inconsistent aggregator types",
|
||||
err.Error(),
|
||||
)
|
||||
require.True(t, errors.Is(err, aggregator.ErrInconsistentType))
|
||||
@ -67,7 +67,7 @@ func testRangeNegative(t *testing.T, alt bool, desc *export.Descriptor) {
|
||||
|
||||
require.Nil(t, posErr)
|
||||
|
||||
if desc.MetricKind() == export.GaugeKind {
|
||||
if desc.MetricKind() == export.ObserverKind {
|
||||
require.Nil(t, negErr)
|
||||
} else {
|
||||
require.Equal(t, negErr == nil, alt)
|
||||
@ -79,8 +79,8 @@ func TestRangeTest(t *testing.T) {
|
||||
t.Run(nkind.String(), func(t *testing.T) {
|
||||
for _, mkind := range []export.Kind{
|
||||
export.CounterKind,
|
||||
export.GaugeKind,
|
||||
export.MeasureKind,
|
||||
export.ObserverKind,
|
||||
} {
|
||||
t.Run(mkind.String(), func(t *testing.T) {
|
||||
for _, alt := range []bool{true, false} {
|
||||
|
@ -9,14 +9,13 @@ func _() {
|
||||
// Re-run the stringer command to generate them again.
|
||||
var x [1]struct{}
|
||||
_ = x[CounterKind-0]
|
||||
_ = x[GaugeKind-1]
|
||||
_ = x[MeasureKind-2]
|
||||
_ = x[ObserverKind-3]
|
||||
_ = x[MeasureKind-1]
|
||||
_ = x[ObserverKind-2]
|
||||
}
|
||||
|
||||
const _Kind_name = "CounterKindGaugeKindMeasureKindObserverKind"
|
||||
const _Kind_name = "CounterKindMeasureKindObserverKind"
|
||||
|
||||
var _Kind_index = [...]uint8{0, 11, 20, 31, 43}
|
||||
var _Kind_index = [...]uint8{0, 11, 22, 34}
|
||||
|
||||
func (i Kind) String() string {
|
||||
if i < 0 || i >= Kind(len(_Kind_index)-1) {
|
||||
|
@ -106,21 +106,21 @@ type AggregationSelector interface {
|
||||
}
|
||||
|
||||
// Aggregator implements a specific aggregation behavior, e.g., a
|
||||
// behavior to track a sequence of updates to a counter, a gauge, or a
|
||||
// measure instrument. For the most part, counter and gauge semantics
|
||||
// are fixed and the provided implementations should be used. Measure
|
||||
// metrics offer a wide range of potential tradeoffs and several
|
||||
// implementations are provided.
|
||||
// behavior to track a sequence of updates to a counter, a measure, or
|
||||
// an observer instrument. For the most part, counter semantics are
|
||||
// fixed and the provided implementation should be used. Measure and
|
||||
// observer metrics offer a wide range of potential tradeoffs and
|
||||
// several implementations are provided.
|
||||
//
|
||||
// Aggregators are meant to compute the change (i.e., delta) in state
|
||||
// from one checkpoint to the next, with the exception of gauge
|
||||
// aggregators. Gauge aggregators are required to maintain the last
|
||||
// value across checkpoints to implement montonic gauge support.
|
||||
// from one checkpoint to the next, with the exception of LastValue
|
||||
// aggregators. LastValue aggregators are required to maintain the last
|
||||
// value across checkpoints.
|
||||
//
|
||||
// Note that any Aggregator may be attached to any instrument--this is
|
||||
// the result of the OpenTelemetry API/SDK separation. It is possible
|
||||
// to attach a counter aggregator to a measure instrument (to compute
|
||||
// a simple sum) or a gauge instrument to a measure instrument (to
|
||||
// to attach a counter aggregator to a Measure instrument (to compute
|
||||
// a simple sum) or a LastValue aggregator to a measure instrument (to
|
||||
// compute the last value).
|
||||
type Aggregator interface {
|
||||
// Update receives a new measured value and incorporates it
|
||||
@ -290,9 +290,6 @@ const (
|
||||
// Counter kind indicates a counter instrument.
|
||||
CounterKind Kind = iota
|
||||
|
||||
// Gauge kind indicates a gauge instrument.
|
||||
GaugeKind
|
||||
|
||||
// Measure kind indicates a measure instrument.
|
||||
MeasureKind
|
||||
|
||||
@ -346,8 +343,8 @@ func (d *Descriptor) Name() string {
|
||||
return d.name
|
||||
}
|
||||
|
||||
// MetricKind returns the kind of instrument: counter, gauge, or
|
||||
// measure.
|
||||
// MetricKind returns the kind of instrument: counter, measure, or
|
||||
// observer.
|
||||
func (d *Descriptor) MetricKind() Kind {
|
||||
return d.metricKind
|
||||
}
|
||||
@ -381,8 +378,8 @@ func (d *Descriptor) NumberKind() core.NumberKind {
|
||||
// instrument was selected. It returns true if:
|
||||
//
|
||||
// - A counter instrument is non-monotonic
|
||||
// - A gauge instrument is monotonic
|
||||
// - A measure instrument is non-absolute
|
||||
// - An observer instrument is monotonic
|
||||
//
|
||||
// TODO: Consider renaming this method, or expanding to provide
|
||||
// kind-specific tests (e.g., Monotonic(), Absolute()).
|
||||
|
@ -12,7 +12,7 @@
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package gauge // import "go.opentelemetry.io/otel/sdk/metric/aggregator/gauge"
|
||||
package lastvalue // import "go.opentelemetry.io/otel/sdk/metric/aggregator/lastvalue"
|
||||
|
||||
import (
|
||||
"context"
|
||||
@ -25,25 +25,20 @@ import (
|
||||
"go.opentelemetry.io/otel/sdk/export/metric/aggregator"
|
||||
)
|
||||
|
||||
// Note: This aggregator enforces the behavior of monotonic gauges to
|
||||
// the best of its ability, but it will not retain any memory of
|
||||
// infrequently used gauges. Exporters may wish to enforce this, or
|
||||
// they may simply treat monotonic as a semantic hint.
|
||||
|
||||
type (
|
||||
|
||||
// Aggregator aggregates gauge events.
|
||||
// Aggregator aggregates lastValue events.
|
||||
Aggregator struct {
|
||||
// current is an atomic pointer to *gaugeData. It is never nil.
|
||||
// current is an atomic pointer to *lastValueData. It is never nil.
|
||||
current unsafe.Pointer
|
||||
|
||||
// checkpoint is a copy of the current value taken in Checkpoint()
|
||||
checkpoint unsafe.Pointer
|
||||
}
|
||||
|
||||
// gaugeData stores the current value of a gauge along with
|
||||
// lastValueData stores the current value of a lastValue along with
|
||||
// a sequence number to determine the winner of a race.
|
||||
gaugeData struct {
|
||||
lastValueData struct {
|
||||
// value is the int64- or float64-encoded Set() data
|
||||
//
|
||||
// value needs to be aligned for 64-bit atomic operations.
|
||||
@ -51,7 +46,7 @@ type (
|
||||
|
||||
// timestamp indicates when this record was submitted.
|
||||
// this can be used to pick a winner when multiple
|
||||
// records contain gauge data for the same labels due
|
||||
// records contain lastValue data for the same labels due
|
||||
// to races.
|
||||
timestamp time.Time
|
||||
}
|
||||
@ -60,25 +55,25 @@ type (
|
||||
var _ export.Aggregator = &Aggregator{}
|
||||
var _ aggregator.LastValue = &Aggregator{}
|
||||
|
||||
// An unset gauge has zero timestamp and zero value.
|
||||
var unsetGauge = &gaugeData{}
|
||||
// An unset lastValue has zero timestamp and zero value.
|
||||
var unsetLastValue = &lastValueData{}
|
||||
|
||||
// New returns a new gauge aggregator. This aggregator retains the
|
||||
// New returns a new lastValue aggregator. This aggregator retains the
|
||||
// last value and timestamp that were recorded.
|
||||
func New() *Aggregator {
|
||||
return &Aggregator{
|
||||
current: unsafe.Pointer(unsetGauge),
|
||||
checkpoint: unsafe.Pointer(unsetGauge),
|
||||
current: unsafe.Pointer(unsetLastValue),
|
||||
checkpoint: unsafe.Pointer(unsetLastValue),
|
||||
}
|
||||
}
|
||||
|
||||
// LastValue returns the last-recorded gauge value and the
|
||||
// LastValue returns the last-recorded lastValue value and the
|
||||
// corresponding timestamp. The error value aggregator.ErrNoLastValue
|
||||
// will be returned if (due to a race condition) the checkpoint was
|
||||
// computed before the first value was set.
|
||||
func (g *Aggregator) LastValue() (core.Number, time.Time, error) {
|
||||
gd := (*gaugeData)(g.checkpoint)
|
||||
if gd == unsetGauge {
|
||||
gd := (*lastValueData)(g.checkpoint)
|
||||
if gd == unsetLastValue {
|
||||
return core.Number(0), time.Time{}, aggregator.ErrNoLastValue
|
||||
}
|
||||
return gd.value.AsNumber(), gd.timestamp, nil
|
||||
@ -91,68 +86,25 @@ func (g *Aggregator) Checkpoint(ctx context.Context, _ *export.Descriptor) {
|
||||
|
||||
// Update atomically sets the current "last" value.
|
||||
func (g *Aggregator) Update(_ context.Context, number core.Number, desc *export.Descriptor) error {
|
||||
if !desc.Alternate() {
|
||||
g.updateNonMonotonic(number)
|
||||
return nil
|
||||
}
|
||||
return g.updateMonotonic(number, desc)
|
||||
}
|
||||
|
||||
func (g *Aggregator) updateNonMonotonic(number core.Number) {
|
||||
ngd := &gaugeData{
|
||||
ngd := &lastValueData{
|
||||
value: number,
|
||||
timestamp: time.Now(),
|
||||
}
|
||||
atomic.StorePointer(&g.current, unsafe.Pointer(ngd))
|
||||
return nil
|
||||
}
|
||||
|
||||
func (g *Aggregator) updateMonotonic(number core.Number, desc *export.Descriptor) error {
|
||||
ngd := &gaugeData{
|
||||
timestamp: time.Now(),
|
||||
value: number,
|
||||
}
|
||||
kind := desc.NumberKind()
|
||||
|
||||
for {
|
||||
gd := (*gaugeData)(atomic.LoadPointer(&g.current))
|
||||
|
||||
if gd.value.CompareNumber(kind, number) > 0 {
|
||||
return aggregator.ErrNonMonotoneInput
|
||||
}
|
||||
|
||||
if atomic.CompareAndSwapPointer(&g.current, unsafe.Pointer(gd), unsafe.Pointer(ngd)) {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Merge combines state from two aggregators. If the gauge is
|
||||
// declared as monotonic, the greater value is chosen. If the gauge
|
||||
// is declared as non-monotonic, the most-recently set value is
|
||||
// chosen.
|
||||
// Merge combines state from two aggregators. The most-recently set
|
||||
// value is chosen.
|
||||
func (g *Aggregator) Merge(oa export.Aggregator, desc *export.Descriptor) error {
|
||||
o, _ := oa.(*Aggregator)
|
||||
if o == nil {
|
||||
return aggregator.NewInconsistentMergeError(g, oa)
|
||||
}
|
||||
|
||||
ggd := (*gaugeData)(atomic.LoadPointer(&g.checkpoint))
|
||||
ogd := (*gaugeData)(atomic.LoadPointer(&o.checkpoint))
|
||||
ggd := (*lastValueData)(atomic.LoadPointer(&g.checkpoint))
|
||||
ogd := (*lastValueData)(atomic.LoadPointer(&o.checkpoint))
|
||||
|
||||
if desc.Alternate() {
|
||||
// Monotonic: use the greater value
|
||||
cmp := ggd.value.CompareNumber(desc.NumberKind(), ogd.value)
|
||||
|
||||
if cmp > 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
if cmp < 0 {
|
||||
g.checkpoint = unsafe.Pointer(ogd)
|
||||
return nil
|
||||
}
|
||||
}
|
||||
// Non-monotonic gauge or equal values
|
||||
if ggd.timestamp.After(ogd.timestamp) {
|
||||
return nil
|
||||
}
|
@ -12,7 +12,7 @@
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package gauge
|
||||
package lastvalue
|
||||
|
||||
import (
|
||||
"context"
|
||||
@ -38,8 +38,8 @@ var _ export.Aggregator = &Aggregator{}
|
||||
func TestMain(m *testing.M) {
|
||||
fields := []ottest.FieldOffset{
|
||||
{
|
||||
Name: "gaugeData.value",
|
||||
Offset: unsafe.Offsetof(gaugeData{}.value),
|
||||
Name: "lastValueData.value",
|
||||
Offset: unsafe.Offsetof(lastValueData{}.value),
|
||||
},
|
||||
}
|
||||
if !ottest.Aligned8Byte(fields, os.Stderr) {
|
||||
@ -49,13 +49,13 @@ func TestMain(m *testing.M) {
|
||||
os.Exit(m.Run())
|
||||
}
|
||||
|
||||
func TestGaugeNonMonotonic(t *testing.T) {
|
||||
func TestLastValueUpdate(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
|
||||
test.RunProfiles(t, func(t *testing.T, profile test.Profile) {
|
||||
agg := New()
|
||||
|
||||
record := test.NewAggregatorTest(export.GaugeKind, profile.NumberKind, false)
|
||||
record := test.NewAggregatorTest(export.ObserverKind, profile.NumberKind, false)
|
||||
|
||||
var last core.Number
|
||||
for i := 0; i < count; i++ {
|
||||
@ -72,66 +72,14 @@ func TestGaugeNonMonotonic(t *testing.T) {
|
||||
})
|
||||
}
|
||||
|
||||
func TestGaugeMonotonic(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
|
||||
test.RunProfiles(t, func(t *testing.T, profile test.Profile) {
|
||||
agg := New()
|
||||
|
||||
record := test.NewAggregatorTest(export.GaugeKind, profile.NumberKind, true)
|
||||
|
||||
small := profile.Random(+1)
|
||||
last := small
|
||||
for i := 0; i < count; i++ {
|
||||
x := profile.Random(+1)
|
||||
last.AddNumber(profile.NumberKind, x)
|
||||
test.CheckedUpdate(t, agg, last, record)
|
||||
}
|
||||
|
||||
agg.Checkpoint(ctx, record)
|
||||
|
||||
lv, _, err := agg.LastValue()
|
||||
require.Equal(t, last, lv, "Same last value - monotonic")
|
||||
require.Nil(t, err)
|
||||
})
|
||||
}
|
||||
|
||||
func TestGaugeMonotonicDescending(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
|
||||
test.RunProfiles(t, func(t *testing.T, profile test.Profile) {
|
||||
agg := New()
|
||||
|
||||
record := test.NewAggregatorTest(export.GaugeKind, profile.NumberKind, true)
|
||||
|
||||
first := profile.Random(+1)
|
||||
test.CheckedUpdate(t, agg, first, record)
|
||||
|
||||
for i := 0; i < count; i++ {
|
||||
x := profile.Random(-1)
|
||||
|
||||
err := agg.Update(ctx, x, record)
|
||||
if err != aggregator.ErrNonMonotoneInput {
|
||||
t.Error("Expected ErrNonMonotoneInput", err)
|
||||
}
|
||||
}
|
||||
|
||||
agg.Checkpoint(ctx, record)
|
||||
|
||||
lv, _, err := agg.LastValue()
|
||||
require.Equal(t, first, lv, "Same last value - monotonic")
|
||||
require.Nil(t, err)
|
||||
})
|
||||
}
|
||||
|
||||
func TestGaugeNormalMerge(t *testing.T) {
|
||||
func TestLastValueMerge(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
|
||||
test.RunProfiles(t, func(t *testing.T, profile test.Profile) {
|
||||
agg1 := New()
|
||||
agg2 := New()
|
||||
|
||||
descriptor := test.NewAggregatorTest(export.GaugeKind, profile.NumberKind, false)
|
||||
descriptor := test.NewAggregatorTest(export.ObserverKind, profile.NumberKind, false)
|
||||
|
||||
first1 := profile.Random(+1)
|
||||
first2 := profile.Random(+1)
|
||||
@ -158,39 +106,8 @@ func TestGaugeNormalMerge(t *testing.T) {
|
||||
})
|
||||
}
|
||||
|
||||
func TestGaugeMonotonicMerge(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
|
||||
test.RunProfiles(t, func(t *testing.T, profile test.Profile) {
|
||||
agg1 := New()
|
||||
agg2 := New()
|
||||
|
||||
descriptor := test.NewAggregatorTest(export.GaugeKind, profile.NumberKind, true)
|
||||
|
||||
first1 := profile.Random(+1)
|
||||
test.CheckedUpdate(t, agg1, first1, descriptor)
|
||||
|
||||
first2 := profile.Random(+1)
|
||||
first2.AddNumber(profile.NumberKind, first1)
|
||||
test.CheckedUpdate(t, agg2, first2, descriptor)
|
||||
|
||||
agg1.Checkpoint(ctx, descriptor)
|
||||
agg2.Checkpoint(ctx, descriptor)
|
||||
|
||||
test.CheckedMerge(t, agg1, agg2, descriptor)
|
||||
|
||||
_, ts2, err := agg1.LastValue()
|
||||
require.Nil(t, err)
|
||||
|
||||
lv, ts1, err := agg1.LastValue()
|
||||
require.Nil(t, err)
|
||||
require.Equal(t, first2, lv, "Merged value - monotonic")
|
||||
require.Equal(t, ts2, ts1, "Merged timestamp - monotonic")
|
||||
})
|
||||
}
|
||||
|
||||
func TestGaugeNotSet(t *testing.T) {
|
||||
descriptor := test.NewAggregatorTest(export.GaugeKind, core.Int64NumberKind, true)
|
||||
func TestLastValueNotSet(t *testing.T) {
|
||||
descriptor := test.NewAggregatorTest(export.ObserverKind, core.Int64NumberKind, true)
|
||||
|
||||
g := New()
|
||||
g.Checkpoint(context.Background(), descriptor)
|
@ -30,13 +30,13 @@ func TestGroupingStateless(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
b := defaultkeys.New(test.NewAggregationSelector(), test.GroupEncoder, false)
|
||||
|
||||
_ = b.Process(ctx, test.NewGaugeRecord(test.GaugeADesc, test.Labels1, 10))
|
||||
_ = b.Process(ctx, test.NewGaugeRecord(test.GaugeADesc, test.Labels2, 20))
|
||||
_ = b.Process(ctx, test.NewGaugeRecord(test.GaugeADesc, test.Labels3, 30))
|
||||
_ = b.Process(ctx, test.NewLastValueRecord(test.LastValueADesc, test.Labels1, 10))
|
||||
_ = b.Process(ctx, test.NewLastValueRecord(test.LastValueADesc, test.Labels2, 20))
|
||||
_ = b.Process(ctx, test.NewLastValueRecord(test.LastValueADesc, test.Labels3, 30))
|
||||
|
||||
_ = b.Process(ctx, test.NewGaugeRecord(test.GaugeBDesc, test.Labels1, 10))
|
||||
_ = b.Process(ctx, test.NewGaugeRecord(test.GaugeBDesc, test.Labels2, 20))
|
||||
_ = b.Process(ctx, test.NewGaugeRecord(test.GaugeBDesc, test.Labels3, 30))
|
||||
_ = b.Process(ctx, test.NewLastValueRecord(test.LastValueBDesc, test.Labels1, 10))
|
||||
_ = b.Process(ctx, test.NewLastValueRecord(test.LastValueBDesc, test.Labels2, 20))
|
||||
_ = b.Process(ctx, test.NewLastValueRecord(test.LastValueBDesc, test.Labels3, 30))
|
||||
|
||||
_ = b.Process(ctx, test.NewCounterRecord(test.CounterADesc, test.Labels1, 10))
|
||||
_ = b.Process(ctx, test.NewCounterRecord(test.CounterADesc, test.Labels2, 20))
|
||||
@ -52,18 +52,18 @@ func TestGroupingStateless(t *testing.T) {
|
||||
records := test.Output{}
|
||||
checkpointSet.ForEach(records.AddTo)
|
||||
|
||||
// Repeat for {counter,gauge}.{1,2}.
|
||||
// Output gauge should have only the "G=H" and "G=" keys.
|
||||
// Repeat for {counter,lastvalue}.{1,2}.
|
||||
// Output lastvalue should have only the "G=H" and "G=" keys.
|
||||
// Output counter should have only the "C=D" and "C=" keys.
|
||||
require.EqualValues(t, map[string]int64{
|
||||
"counter.a/C=D": 30, // labels1 + labels2
|
||||
"counter.a/C=": 40, // labels3
|
||||
"counter.b/C=D": 30, // labels1 + labels2
|
||||
"counter.b/C=": 40, // labels3
|
||||
"gauge.a/G=H": 10, // labels1
|
||||
"gauge.a/G=": 30, // labels3 = last value
|
||||
"gauge.b/G=H": 10, // labels1
|
||||
"gauge.b/G=": 30, // labels3 = last value
|
||||
"counter.a/C=D": 30, // labels1 + labels2
|
||||
"counter.a/C=": 40, // labels3
|
||||
"counter.b/C=D": 30, // labels1 + labels2
|
||||
"counter.b/C=": 40, // labels3
|
||||
"lastvalue.a/G=H": 10, // labels1
|
||||
"lastvalue.a/G=": 30, // labels3 = last value
|
||||
"lastvalue.b/G=H": 10, // labels1
|
||||
"lastvalue.b/G=": 30, // labels3 = last value
|
||||
}, records)
|
||||
|
||||
// Verify that state is reset by FinishedCollection()
|
||||
|
@ -24,7 +24,7 @@ import (
|
||||
export "go.opentelemetry.io/otel/sdk/export/metric"
|
||||
sdk "go.opentelemetry.io/otel/sdk/metric"
|
||||
"go.opentelemetry.io/otel/sdk/metric/aggregator/counter"
|
||||
"go.opentelemetry.io/otel/sdk/metric/aggregator/gauge"
|
||||
"go.opentelemetry.io/otel/sdk/metric/aggregator/lastvalue"
|
||||
)
|
||||
|
||||
type (
|
||||
@ -41,11 +41,11 @@ type (
|
||||
)
|
||||
|
||||
var (
|
||||
// GaugeADesc and GaugeBDesc group by "G"
|
||||
GaugeADesc = export.NewDescriptor(
|
||||
"gauge.a", export.GaugeKind, []core.Key{key.New("G")}, "", "", core.Int64NumberKind, false)
|
||||
GaugeBDesc = export.NewDescriptor(
|
||||
"gauge.b", export.GaugeKind, []core.Key{key.New("G")}, "", "", core.Int64NumberKind, false)
|
||||
// LastValueADesc and LastValueBDesc group by "G"
|
||||
LastValueADesc = export.NewDescriptor(
|
||||
"lastvalue.a", export.ObserverKind, []core.Key{key.New("G")}, "", "", core.Int64NumberKind, false)
|
||||
LastValueBDesc = export.NewDescriptor(
|
||||
"lastvalue.b", export.ObserverKind, []core.Key{key.New("G")}, "", "", core.Int64NumberKind, false)
|
||||
// CounterADesc and CounterBDesc group by "C"
|
||||
CounterADesc = export.NewDescriptor(
|
||||
"counter.a", export.CounterKind, []core.Key{key.New("C")}, "", "", core.Int64NumberKind, false)
|
||||
@ -57,7 +57,7 @@ var (
|
||||
// GroupEncoder uses the SDK default encoder
|
||||
GroupEncoder = sdk.NewDefaultLabelEncoder()
|
||||
|
||||
// Gauge groups are (labels1), (labels2+labels3)
|
||||
// LastValue groups are (labels1), (labels2+labels3)
|
||||
// Counter groups are (labels1+labels2), (labels3)
|
||||
|
||||
// Labels1 has G=H and C=D
|
||||
@ -70,7 +70,7 @@ var (
|
||||
|
||||
// NewAggregationSelector returns a policy that is consistent with the
|
||||
// test descriptors above. I.e., it returns counter.New() for counter
|
||||
// instruments and gauge.New for gauge instruments.
|
||||
// instruments and lastvalue.New for lastValue instruments.
|
||||
func NewAggregationSelector() export.AggregationSelector {
|
||||
return &testAggregationSelector{}
|
||||
}
|
||||
@ -79,8 +79,8 @@ func (*testAggregationSelector) AggregatorFor(desc *export.Descriptor) export.Ag
|
||||
switch desc.MetricKind() {
|
||||
case export.CounterKind:
|
||||
return counter.New()
|
||||
case export.GaugeKind:
|
||||
return gauge.New()
|
||||
case export.ObserverKind:
|
||||
return lastvalue.New()
|
||||
default:
|
||||
panic("Invalid descriptor MetricKind for this test")
|
||||
}
|
||||
@ -104,18 +104,18 @@ func (Encoder) Encode(labels []core.KeyValue) string {
|
||||
return sb.String()
|
||||
}
|
||||
|
||||
// GaugeAgg returns a checkpointed gauge aggregator w/ the specified descriptor and value.
|
||||
func GaugeAgg(desc *export.Descriptor, v int64) export.Aggregator {
|
||||
// LastValueAgg returns a checkpointed lastValue aggregator w/ the specified descriptor and value.
|
||||
func LastValueAgg(desc *export.Descriptor, v int64) export.Aggregator {
|
||||
ctx := context.Background()
|
||||
gagg := gauge.New()
|
||||
gagg := lastvalue.New()
|
||||
_ = gagg.Update(ctx, core.NewInt64Number(v), desc)
|
||||
gagg.Checkpoint(ctx, desc)
|
||||
return gagg
|
||||
}
|
||||
|
||||
// Convenience method for building a test exported gauge record.
|
||||
func NewGaugeRecord(desc *export.Descriptor, labels export.Labels, value int64) export.Record {
|
||||
return export.NewRecord(desc, labels, GaugeAgg(desc, value))
|
||||
// Convenience method for building a test exported lastValue record.
|
||||
func NewLastValueRecord(desc *export.Descriptor, labels export.Labels, value int64) export.Record {
|
||||
return export.NewRecord(desc, labels, LastValueAgg(desc, value))
|
||||
}
|
||||
|
||||
// Convenience method for building a test exported counter record.
|
||||
@ -132,7 +132,7 @@ func CounterAgg(desc *export.Descriptor, v int64) export.Aggregator {
|
||||
return cagg
|
||||
}
|
||||
|
||||
// AddTo adds a name/label-encoding entry with the gauge or counter
|
||||
// AddTo adds a name/label-encoding entry with the lastValue or counter
|
||||
// value to the output map.
|
||||
func (o Output) AddTo(rec export.Record) {
|
||||
labels := rec.Labels()
|
||||
@ -142,7 +142,7 @@ func (o Output) AddTo(rec export.Record) {
|
||||
case *counter.Aggregator:
|
||||
sum, _ := t.Sum()
|
||||
value = sum.AsInt64()
|
||||
case *gauge.Aggregator:
|
||||
case *lastvalue.Aggregator:
|
||||
lv, _, _ := t.LastValue()
|
||||
value = lv.AsInt64()
|
||||
}
|
||||
|
@ -32,18 +32,18 @@ func TestUngroupedStateless(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
b := ungrouped.New(test.NewAggregationSelector(), false)
|
||||
|
||||
// Set initial gauge values
|
||||
_ = b.Process(ctx, test.NewGaugeRecord(test.GaugeADesc, test.Labels1, 10))
|
||||
_ = b.Process(ctx, test.NewGaugeRecord(test.GaugeADesc, test.Labels2, 20))
|
||||
_ = b.Process(ctx, test.NewGaugeRecord(test.GaugeADesc, test.Labels3, 30))
|
||||
// Set initial lastValue values
|
||||
_ = b.Process(ctx, test.NewLastValueRecord(test.LastValueADesc, test.Labels1, 10))
|
||||
_ = b.Process(ctx, test.NewLastValueRecord(test.LastValueADesc, test.Labels2, 20))
|
||||
_ = b.Process(ctx, test.NewLastValueRecord(test.LastValueADesc, test.Labels3, 30))
|
||||
|
||||
_ = b.Process(ctx, test.NewGaugeRecord(test.GaugeBDesc, test.Labels1, 10))
|
||||
_ = b.Process(ctx, test.NewGaugeRecord(test.GaugeBDesc, test.Labels2, 20))
|
||||
_ = b.Process(ctx, test.NewGaugeRecord(test.GaugeBDesc, test.Labels3, 30))
|
||||
_ = b.Process(ctx, test.NewLastValueRecord(test.LastValueBDesc, test.Labels1, 10))
|
||||
_ = b.Process(ctx, test.NewLastValueRecord(test.LastValueBDesc, test.Labels2, 20))
|
||||
_ = b.Process(ctx, test.NewLastValueRecord(test.LastValueBDesc, test.Labels3, 30))
|
||||
|
||||
// Another gauge Set for Labels1
|
||||
_ = b.Process(ctx, test.NewGaugeRecord(test.GaugeADesc, test.Labels1, 50))
|
||||
_ = b.Process(ctx, test.NewGaugeRecord(test.GaugeBDesc, test.Labels1, 50))
|
||||
// Another lastValue Set for Labels1
|
||||
_ = b.Process(ctx, test.NewLastValueRecord(test.LastValueADesc, test.Labels1, 50))
|
||||
_ = b.Process(ctx, test.NewLastValueRecord(test.LastValueBDesc, test.Labels1, 50))
|
||||
|
||||
// Set initial counter values
|
||||
_ = b.Process(ctx, test.NewCounterRecord(test.CounterADesc, test.Labels1, 10))
|
||||
@ -64,21 +64,21 @@ func TestUngroupedStateless(t *testing.T) {
|
||||
records := test.Output{}
|
||||
checkpointSet.ForEach(records.AddTo)
|
||||
|
||||
// Output gauge should have only the "G=H" and "G=" keys.
|
||||
// Output lastvalue should have only the "G=H" and "G=" keys.
|
||||
// Output counter should have only the "C=D" and "C=" keys.
|
||||
require.EqualValues(t, map[string]int64{
|
||||
"counter.a/G~H&C~D": 60, // labels1
|
||||
"counter.a/C~D&E~F": 20, // labels2
|
||||
"counter.a/": 40, // labels3
|
||||
"counter.b/G~H&C~D": 60, // labels1
|
||||
"counter.b/C~D&E~F": 20, // labels2
|
||||
"counter.b/": 40, // labels3
|
||||
"gauge.a/G~H&C~D": 50, // labels1
|
||||
"gauge.a/C~D&E~F": 20, // labels2
|
||||
"gauge.a/": 30, // labels3
|
||||
"gauge.b/G~H&C~D": 50, // labels1
|
||||
"gauge.b/C~D&E~F": 20, // labels2
|
||||
"gauge.b/": 30, // labels3
|
||||
"counter.a/G~H&C~D": 60, // labels1
|
||||
"counter.a/C~D&E~F": 20, // labels2
|
||||
"counter.a/": 40, // labels3
|
||||
"counter.b/G~H&C~D": 60, // labels1
|
||||
"counter.b/C~D&E~F": 20, // labels2
|
||||
"counter.b/": 40, // labels3
|
||||
"lastvalue.a/G~H&C~D": 50, // labels1
|
||||
"lastvalue.a/C~D&E~F": 20, // labels2
|
||||
"lastvalue.a/": 30, // labels3
|
||||
"lastvalue.b/G~H&C~D": 50, // labels1
|
||||
"lastvalue.b/C~D&E~F": 20, // labels2
|
||||
"lastvalue.b/": 30, // labels3
|
||||
}, records)
|
||||
|
||||
// Verify that state was reset
|
||||
|
@ -28,7 +28,7 @@ import (
|
||||
sdk "go.opentelemetry.io/otel/sdk/metric"
|
||||
"go.opentelemetry.io/otel/sdk/metric/aggregator/counter"
|
||||
"go.opentelemetry.io/otel/sdk/metric/aggregator/ddsketch"
|
||||
"go.opentelemetry.io/otel/sdk/metric/aggregator/gauge"
|
||||
"go.opentelemetry.io/otel/sdk/metric/aggregator/lastvalue"
|
||||
"go.opentelemetry.io/otel/sdk/metric/aggregator/minmaxsumcount"
|
||||
)
|
||||
|
||||
@ -47,14 +47,13 @@ func newFixture(b *testing.B) *benchFixture {
|
||||
}
|
||||
|
||||
func (*benchFixture) AggregatorFor(descriptor *export.Descriptor) export.Aggregator {
|
||||
switch descriptor.MetricKind() {
|
||||
case export.CounterKind:
|
||||
name := descriptor.Name()
|
||||
switch {
|
||||
case strings.HasSuffix(name, "counter"):
|
||||
return counter.New()
|
||||
case export.GaugeKind:
|
||||
return gauge.New()
|
||||
case export.ObserverKind:
|
||||
fallthrough
|
||||
case export.MeasureKind:
|
||||
case strings.HasSuffix(name, "lastvalue"):
|
||||
return lastvalue.New()
|
||||
default:
|
||||
if strings.HasSuffix(descriptor.Name(), "minmaxsumcount") {
|
||||
return minmaxsumcount.New(descriptor)
|
||||
} else if strings.HasSuffix(descriptor.Name(), "ddsketch") {
|
||||
@ -247,59 +246,59 @@ func BenchmarkFloat64CounterHandleAdd(b *testing.B) {
|
||||
}
|
||||
}
|
||||
|
||||
// Gauges
|
||||
// LastValue
|
||||
|
||||
func BenchmarkInt64GaugeAdd(b *testing.B) {
|
||||
func BenchmarkInt64LastValueAdd(b *testing.B) {
|
||||
ctx := context.Background()
|
||||
fix := newFixture(b)
|
||||
labs := fix.sdk.Labels(makeLabels(1)...)
|
||||
gau := fix.sdk.NewInt64Gauge("int64.gauge")
|
||||
mea := fix.sdk.NewInt64Measure("int64.lastvalue")
|
||||
|
||||
b.ResetTimer()
|
||||
|
||||
for i := 0; i < b.N; i++ {
|
||||
gau.Set(ctx, int64(i), labs)
|
||||
mea.Record(ctx, int64(i), labs)
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkInt64GaugeHandleAdd(b *testing.B) {
|
||||
func BenchmarkInt64LastValueHandleAdd(b *testing.B) {
|
||||
ctx := context.Background()
|
||||
fix := newFixture(b)
|
||||
labs := fix.sdk.Labels(makeLabels(1)...)
|
||||
gau := fix.sdk.NewInt64Gauge("int64.gauge")
|
||||
handle := gau.Bind(labs)
|
||||
mea := fix.sdk.NewInt64Measure("int64.lastvalue")
|
||||
handle := mea.Bind(labs)
|
||||
|
||||
b.ResetTimer()
|
||||
|
||||
for i := 0; i < b.N; i++ {
|
||||
handle.Set(ctx, int64(i))
|
||||
handle.Record(ctx, int64(i))
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkFloat64GaugeAdd(b *testing.B) {
|
||||
func BenchmarkFloat64LastValueAdd(b *testing.B) {
|
||||
ctx := context.Background()
|
||||
fix := newFixture(b)
|
||||
labs := fix.sdk.Labels(makeLabels(1)...)
|
||||
gau := fix.sdk.NewFloat64Gauge("float64.gauge")
|
||||
mea := fix.sdk.NewFloat64Measure("float64.lastvalue")
|
||||
|
||||
b.ResetTimer()
|
||||
|
||||
for i := 0; i < b.N; i++ {
|
||||
gau.Set(ctx, float64(i), labs)
|
||||
mea.Record(ctx, float64(i), labs)
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkFloat64GaugeHandleAdd(b *testing.B) {
|
||||
func BenchmarkFloat64LastValueHandleAdd(b *testing.B) {
|
||||
ctx := context.Background()
|
||||
fix := newFixture(b)
|
||||
labs := fix.sdk.Labels(makeLabels(1)...)
|
||||
gau := fix.sdk.NewFloat64Gauge("float64.gauge")
|
||||
handle := gau.Bind(labs)
|
||||
mea := fix.sdk.NewFloat64Measure("float64.lastvalue")
|
||||
handle := mea.Bind(labs)
|
||||
|
||||
b.ResetTimer()
|
||||
|
||||
for i := 0; i < b.N; i++ {
|
||||
handle.Set(ctx, float64(i))
|
||||
handle.Record(ctx, float64(i))
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -30,7 +30,7 @@ import (
|
||||
sdk "go.opentelemetry.io/otel/sdk/metric"
|
||||
"go.opentelemetry.io/otel/sdk/metric/aggregator/array"
|
||||
"go.opentelemetry.io/otel/sdk/metric/aggregator/counter"
|
||||
"go.opentelemetry.io/otel/sdk/metric/aggregator/gauge"
|
||||
"go.opentelemetry.io/otel/sdk/metric/aggregator/lastvalue"
|
||||
)
|
||||
|
||||
type correctnessBatcher struct {
|
||||
@ -152,7 +152,7 @@ func TestRecordNaN(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
batcher := &correctnessBatcher{
|
||||
t: t,
|
||||
agg: gauge.New(),
|
||||
agg: lastvalue.New(),
|
||||
}
|
||||
sdk := sdk.New(batcher, sdk.NewDefaultLabelEncoder())
|
||||
|
||||
@ -160,10 +160,10 @@ func TestRecordNaN(t *testing.T) {
|
||||
sdk.SetErrorHandler(func(handleErr error) {
|
||||
sdkErr = handleErr
|
||||
})
|
||||
g := sdk.NewFloat64Gauge("gauge.name")
|
||||
c := sdk.NewFloat64Counter("counter.name")
|
||||
|
||||
require.Nil(t, sdkErr)
|
||||
g.Set(ctx, math.NaN(), sdk.Labels())
|
||||
c.Add(ctx, math.NaN(), sdk.Labels())
|
||||
require.Error(t, sdkErr)
|
||||
}
|
||||
|
||||
|
@ -17,11 +17,10 @@ Package metric implements the OpenTelemetry metric.Meter API. The SDK
|
||||
supports configurable metrics export behavior through a collection of
|
||||
export interfaces that support various export strategies, described below.
|
||||
|
||||
The metric.Meter API consists of methods for constructing each of the
|
||||
basic kinds of metric instrument. There are eight types of instrument
|
||||
available to the end user, comprised of four basic kinds of metric
|
||||
instrument (Counter, Gauge, Measure, Observer) crossed with two kinds
|
||||
of number (int64, float64).
|
||||
The metric.Meter API consists of methods for constructing each of the basic
|
||||
kinds of metric instrument. There are six types of instrument available to
|
||||
the end user, comprised of three basic kinds of metric instrument (Counter,
|
||||
Measure, Observer) crossed with two kinds of number (int64, float64).
|
||||
|
||||
The API assists the SDK by consolidating the variety of metric instruments
|
||||
into a narrower interface, allowing the SDK to avoid repetition of
|
||||
@ -31,25 +30,25 @@ numerical value.
|
||||
|
||||
To this end, the API uses a core.Number type to represent either an int64
|
||||
or a float64, depending on the instrument's definition. A single
|
||||
implementation interface is used for counter, gauge and measure
|
||||
instruments, metric.InstrumentImpl, and a single implementation interface
|
||||
is used for their handles, metric.HandleImpl. For observers, the API
|
||||
defines interfaces, for which the SDK provides an implementation.
|
||||
implementation interface is used for counter and measure instruments,
|
||||
metric.InstrumentImpl, and a single implementation interface is used for
|
||||
their handles, metric.HandleImpl. For observers, the API defines
|
||||
interfaces, for which the SDK provides an implementation.
|
||||
|
||||
There are four entry points for events in the Metrics API - three for
|
||||
synchronous instruments (counters, gauges and measures) and one for
|
||||
asynchronous instruments (observers). The entry points for synchronous
|
||||
instruments are: via instrument handles, via direct instrument calls, and
|
||||
via BatchRecord. The SDK is designed with handles as the primary entry
|
||||
point, the other two entry points are implemented in terms of short-lived
|
||||
handles. For example, the implementation of a direct call allocates a
|
||||
handle, operates on the handle, and releases the handle. Similarly, the
|
||||
implementation of RecordBatch uses a short-lived handle for each
|
||||
measurement in the batch. The entry point for asynchronous instruments is
|
||||
via observer callbacks. Observer callbacks behave like a set of instrument
|
||||
handles - one for each observation for a distinct label set. The observer
|
||||
handles are alive as long as they are used. If the callback stops
|
||||
reporting values for a certain label set, the associated handle is dropped.
|
||||
synchronous instruments (counters and measures) and one for asynchronous
|
||||
instruments (observers). The entry points for synchronous instruments are:
|
||||
via instrument handles, via direct instrument calls, and via BatchRecord.
|
||||
The SDK is designed with handles as the primary entry point, the other two
|
||||
entry points are implemented in terms of short-lived handles. For example,
|
||||
the implementation of a direct call allocates a handle, operates on the
|
||||
handle, and releases the handle. Similarly, the implementation of
|
||||
RecordBatch uses a short-lived handle for each measurement in the batch.
|
||||
The entry point for asynchronous instruments is via observer callbacks.
|
||||
Observer callbacks behave like a set of instrument handles - one for each
|
||||
observation for a distinct label set. The observer handles are alive as
|
||||
long as they are used. If the callback stops reporting values for a
|
||||
certain label set, the associated handle is dropped.
|
||||
|
||||
Internal Structure
|
||||
|
||||
@ -98,22 +97,21 @@ enters the SDK resulting in a new record, and collection context,
|
||||
where a system-level thread performs a collection pass through the
|
||||
SDK.
|
||||
|
||||
Descriptor is a struct that describes the metric instrument to the
|
||||
export pipeline, containing the name, recommended aggregation keys,
|
||||
units, description, metric kind (counter, gauge, or measure), number
|
||||
kind (int64 or float64), and whether the instrument has alternate
|
||||
semantics or not (i.e., monotonic=false counter, monotonic=true gauge,
|
||||
absolute=false measure). A Descriptor accompanies metric data as it
|
||||
passes through the export pipeline.
|
||||
Descriptor is a struct that describes the metric instrument to the export
|
||||
pipeline, containing the name, recommended aggregation keys, units,
|
||||
description, metric kind (counter or measure), number kind (int64 or
|
||||
float64), and whether the instrument has alternate semantics or not (i.e.,
|
||||
monotonic=false counter, absolute=false measure). A Descriptor accompanies
|
||||
metric data as it passes through the export pipeline.
|
||||
|
||||
The AggregationSelector interface supports choosing the method of
|
||||
aggregation to apply to a particular instrument. Given the
|
||||
Descriptor, this AggregatorFor method returns an implementation of
|
||||
Aggregator. If this interface returns nil, the metric will be
|
||||
disabled. The aggregator should be matched to the capabilities of the
|
||||
exporter. Selecting the aggregator for counter and gauge instruments
|
||||
is relatively straightforward, but for measure instruments there are
|
||||
numerous choices with different cost and quality tradeoffs.
|
||||
aggregation to apply to a particular instrument. Given the Descriptor,
|
||||
this AggregatorFor method returns an implementation of Aggregator. If this
|
||||
interface returns nil, the metric will be disabled. The aggregator should
|
||||
be matched to the capabilities of the exporter. Selecting the aggregator
|
||||
for counter instruments is relatively straightforward, but for measure and
|
||||
observer instruments there are numerous choices with different cost and
|
||||
quality tradeoffs.
|
||||
|
||||
Aggregator is an interface which implements a concrete strategy for
|
||||
aggregating metric updates. Several Aggregator implementations are
|
||||
|
@ -1,143 +0,0 @@
|
||||
// Copyright 2019, 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 metric_test
|
||||
|
||||
import (
|
||||
"context"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"go.opentelemetry.io/otel/api/core"
|
||||
"go.opentelemetry.io/otel/api/key"
|
||||
"go.opentelemetry.io/otel/api/metric"
|
||||
export "go.opentelemetry.io/otel/sdk/export/metric"
|
||||
"go.opentelemetry.io/otel/sdk/export/metric/aggregator"
|
||||
sdk "go.opentelemetry.io/otel/sdk/metric"
|
||||
"go.opentelemetry.io/otel/sdk/metric/aggregator/gauge"
|
||||
)
|
||||
|
||||
type monotoneBatcher struct {
|
||||
// currentValue needs to be aligned for 64-bit atomic operations.
|
||||
currentValue *core.Number
|
||||
collections int
|
||||
currentTime *time.Time
|
||||
|
||||
t *testing.T
|
||||
}
|
||||
|
||||
func (*monotoneBatcher) AggregatorFor(*export.Descriptor) export.Aggregator {
|
||||
return gauge.New()
|
||||
}
|
||||
|
||||
func (*monotoneBatcher) CheckpointSet() export.CheckpointSet {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (*monotoneBatcher) FinishedCollection() {
|
||||
}
|
||||
|
||||
func (m *monotoneBatcher) Process(_ context.Context, record export.Record) error {
|
||||
require.Equal(m.t, "my.gauge.name", record.Descriptor().Name())
|
||||
require.Equal(m.t, 1, record.Labels().Len())
|
||||
require.Equal(m.t, "a", string(record.Labels().Ordered()[0].Key))
|
||||
require.Equal(m.t, "b", record.Labels().Ordered()[0].Value.Emit())
|
||||
|
||||
gauge := record.Aggregator().(*gauge.Aggregator)
|
||||
val, ts, err := gauge.LastValue()
|
||||
require.Nil(m.t, err)
|
||||
|
||||
m.currentValue = &val
|
||||
m.currentTime = &ts
|
||||
m.collections++
|
||||
return nil
|
||||
}
|
||||
|
||||
func TestMonotoneGauge(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
batcher := &monotoneBatcher{
|
||||
t: t,
|
||||
}
|
||||
sdk := sdk.New(batcher, sdk.NewDefaultLabelEncoder())
|
||||
|
||||
sdk.SetErrorHandler(func(error) { t.Fatal("Unexpected") })
|
||||
|
||||
gauge := sdk.NewInt64Gauge("my.gauge.name", metric.WithMonotonic(true))
|
||||
|
||||
handle := gauge.Bind(sdk.Labels(key.String("a", "b")))
|
||||
|
||||
require.Nil(t, batcher.currentTime)
|
||||
require.Nil(t, batcher.currentValue)
|
||||
|
||||
before := time.Now()
|
||||
|
||||
handle.Set(ctx, 1)
|
||||
|
||||
// Until collection, expect nil.
|
||||
require.Nil(t, batcher.currentTime)
|
||||
require.Nil(t, batcher.currentValue)
|
||||
|
||||
sdk.Collect(ctx)
|
||||
|
||||
require.NotNil(t, batcher.currentValue)
|
||||
require.Equal(t, core.NewInt64Number(1), *batcher.currentValue)
|
||||
require.True(t, before.Before(*batcher.currentTime))
|
||||
|
||||
before = *batcher.currentTime
|
||||
|
||||
// Collect would ordinarily flush the record, except we're using a handle.
|
||||
sdk.Collect(ctx)
|
||||
|
||||
require.Equal(t, 2, batcher.collections)
|
||||
|
||||
// Increase the value to 2.
|
||||
handle.Set(ctx, 2)
|
||||
|
||||
sdk.Collect(ctx)
|
||||
|
||||
require.Equal(t, 3, batcher.collections)
|
||||
require.Equal(t, core.NewInt64Number(2), *batcher.currentValue)
|
||||
require.True(t, before.Before(*batcher.currentTime))
|
||||
|
||||
before = *batcher.currentTime
|
||||
|
||||
sdk.Collect(ctx)
|
||||
require.Equal(t, 4, batcher.collections)
|
||||
|
||||
// Try to lower the value to 1, it will fail.
|
||||
var err error
|
||||
sdk.SetErrorHandler(func(sdkErr error) {
|
||||
err = sdkErr
|
||||
})
|
||||
handle.Set(ctx, 1)
|
||||
require.Equal(t, aggregator.ErrNonMonotoneInput, err)
|
||||
sdk.SetErrorHandler(func(error) { t.Fatal("Unexpected") })
|
||||
|
||||
sdk.Collect(ctx)
|
||||
|
||||
// The value and timestamp are both unmodified
|
||||
require.Equal(t, 5, batcher.collections)
|
||||
require.Equal(t, core.NewInt64Number(2), *batcher.currentValue)
|
||||
require.Equal(t, before, *batcher.currentTime)
|
||||
|
||||
// Update with the same value, update the timestamp.
|
||||
handle.Set(ctx, 2)
|
||||
sdk.Collect(ctx)
|
||||
|
||||
require.Equal(t, 6, batcher.collections)
|
||||
require.Equal(t, core.NewInt64Number(2), *batcher.currentValue)
|
||||
require.True(t, before.Before(*batcher.currentTime))
|
||||
}
|
@ -408,12 +408,6 @@ func (m *SDK) newCounterInstrument(name string, numberKind core.NumberKind, cos
|
||||
return m.newInstrument(name, export.CounterKind, numberKind, &opts)
|
||||
}
|
||||
|
||||
func (m *SDK) newGaugeInstrument(name string, numberKind core.NumberKind, gos ...api.GaugeOptionApplier) *instrument {
|
||||
opts := api.Options{}
|
||||
api.ApplyGaugeOptions(&opts, gos...)
|
||||
return m.newInstrument(name, export.GaugeKind, numberKind, &opts)
|
||||
}
|
||||
|
||||
func (m *SDK) newMeasureInstrument(name string, numberKind core.NumberKind, mos ...api.MeasureOptionApplier) *instrument {
|
||||
opts := api.Options{}
|
||||
api.ApplyMeasureOptions(&opts, mos...)
|
||||
@ -428,14 +422,6 @@ func (m *SDK) NewFloat64Counter(name string, cos ...api.CounterOptionApplier) ap
|
||||
return api.WrapFloat64CounterInstrument(m.newCounterInstrument(name, core.Float64NumberKind, cos...))
|
||||
}
|
||||
|
||||
func (m *SDK) NewInt64Gauge(name string, gos ...api.GaugeOptionApplier) api.Int64Gauge {
|
||||
return api.WrapInt64GaugeInstrument(m.newGaugeInstrument(name, core.Int64NumberKind, gos...))
|
||||
}
|
||||
|
||||
func (m *SDK) NewFloat64Gauge(name string, gos ...api.GaugeOptionApplier) api.Float64Gauge {
|
||||
return api.WrapFloat64GaugeInstrument(m.newGaugeInstrument(name, core.Float64NumberKind, gos...))
|
||||
}
|
||||
|
||||
func (m *SDK) NewInt64Measure(name string, mos ...api.MeasureOptionApplier) api.Int64Measure {
|
||||
return api.WrapInt64MeasureInstrument(m.newMeasureInstrument(name, core.Int64NumberKind, mos...))
|
||||
}
|
||||
|
@ -19,7 +19,6 @@ import (
|
||||
"go.opentelemetry.io/otel/sdk/metric/aggregator/array"
|
||||
"go.opentelemetry.io/otel/sdk/metric/aggregator/counter"
|
||||
"go.opentelemetry.io/otel/sdk/metric/aggregator/ddsketch"
|
||||
"go.opentelemetry.io/otel/sdk/metric/aggregator/gauge"
|
||||
"go.opentelemetry.io/otel/sdk/metric/aggregator/minmaxsumcount"
|
||||
)
|
||||
|
||||
@ -38,17 +37,17 @@ var (
|
||||
)
|
||||
|
||||
// NewWithInexpensiveMeasure returns a simple aggregation selector
|
||||
// that uses counter, gauge, and minmaxsumcount aggregators for the
|
||||
// four kinds of metric. This selector is faster and uses less memory
|
||||
// than the others because minmaxsumcount does not aggregate quantile
|
||||
// information.
|
||||
// that uses counter, minmaxsumcount and minmaxsumcount aggregators
|
||||
// for the three kinds of metric. This selector is faster and uses
|
||||
// less memory than the others because minmaxsumcount does not
|
||||
// aggregate quantile information.
|
||||
func NewWithInexpensiveMeasure() export.AggregationSelector {
|
||||
return selectorInexpensive{}
|
||||
}
|
||||
|
||||
// NewWithSketchMeasure returns a simple aggregation selector that
|
||||
// uses counter, gauge, and ddsketch aggregators for the four kinds of
|
||||
// metric. This selector uses more cpu and memory than the
|
||||
// uses counter, ddsketch, and ddsketch aggregators for the three
|
||||
// kinds of metric. This selector uses more cpu and memory than the
|
||||
// NewWithInexpensiveMeasure because it uses one DDSketch per distinct
|
||||
// measure/observer and labelset.
|
||||
func NewWithSketchMeasure(config *ddsketch.Config) export.AggregationSelector {
|
||||
@ -58,7 +57,7 @@ func NewWithSketchMeasure(config *ddsketch.Config) export.AggregationSelector {
|
||||
}
|
||||
|
||||
// NewWithExactMeasure returns a simple aggregation selector that uses
|
||||
// counter, gauge, and array aggregators for the four kinds of metric.
|
||||
// counter, array, and array aggregators for the three kinds of metric.
|
||||
// This selector uses more memory than the NewWithSketchMeasure
|
||||
// because it aggregates an array of all values, therefore is able to
|
||||
// compute exact quantiles.
|
||||
@ -68,8 +67,6 @@ func NewWithExactMeasure() export.AggregationSelector {
|
||||
|
||||
func (selectorInexpensive) AggregatorFor(descriptor *export.Descriptor) export.Aggregator {
|
||||
switch descriptor.MetricKind() {
|
||||
case export.GaugeKind:
|
||||
return gauge.New()
|
||||
case export.ObserverKind:
|
||||
fallthrough
|
||||
case export.MeasureKind:
|
||||
@ -81,8 +78,6 @@ func (selectorInexpensive) AggregatorFor(descriptor *export.Descriptor) export.A
|
||||
|
||||
func (s selectorSketch) AggregatorFor(descriptor *export.Descriptor) export.Aggregator {
|
||||
switch descriptor.MetricKind() {
|
||||
case export.GaugeKind:
|
||||
return gauge.New()
|
||||
case export.ObserverKind:
|
||||
fallthrough
|
||||
case export.MeasureKind:
|
||||
@ -94,8 +89,6 @@ func (s selectorSketch) AggregatorFor(descriptor *export.Descriptor) export.Aggr
|
||||
|
||||
func (selectorExact) AggregatorFor(descriptor *export.Descriptor) export.Aggregator {
|
||||
switch descriptor.MetricKind() {
|
||||
case export.GaugeKind:
|
||||
return gauge.New()
|
||||
case export.ObserverKind:
|
||||
fallthrough
|
||||
case export.MeasureKind:
|
||||
|
@ -24,13 +24,11 @@ import (
|
||||
"go.opentelemetry.io/otel/sdk/metric/aggregator/array"
|
||||
"go.opentelemetry.io/otel/sdk/metric/aggregator/counter"
|
||||
"go.opentelemetry.io/otel/sdk/metric/aggregator/ddsketch"
|
||||
"go.opentelemetry.io/otel/sdk/metric/aggregator/gauge"
|
||||
"go.opentelemetry.io/otel/sdk/metric/aggregator/minmaxsumcount"
|
||||
"go.opentelemetry.io/otel/sdk/metric/selector/simple"
|
||||
)
|
||||
|
||||
var (
|
||||
testGaugeDesc = export.NewDescriptor("gauge", export.GaugeKind, nil, "", "", core.Int64NumberKind, false)
|
||||
testCounterDesc = export.NewDescriptor("counter", export.CounterKind, nil, "", "", core.Int64NumberKind, false)
|
||||
testMeasureDesc = export.NewDescriptor("measure", export.MeasureKind, nil, "", "", core.Int64NumberKind, false)
|
||||
testObserverDesc = export.NewDescriptor("observer", export.ObserverKind, nil, "", "", core.Int64NumberKind, false)
|
||||
@ -38,7 +36,6 @@ var (
|
||||
|
||||
func TestInexpensiveMeasure(t *testing.T) {
|
||||
inex := simple.NewWithInexpensiveMeasure()
|
||||
require.NotPanics(t, func() { _ = inex.AggregatorFor(testGaugeDesc).(*gauge.Aggregator) })
|
||||
require.NotPanics(t, func() { _ = inex.AggregatorFor(testCounterDesc).(*counter.Aggregator) })
|
||||
require.NotPanics(t, func() { _ = inex.AggregatorFor(testMeasureDesc).(*minmaxsumcount.Aggregator) })
|
||||
require.NotPanics(t, func() { _ = inex.AggregatorFor(testObserverDesc).(*minmaxsumcount.Aggregator) })
|
||||
@ -46,7 +43,6 @@ func TestInexpensiveMeasure(t *testing.T) {
|
||||
|
||||
func TestSketchMeasure(t *testing.T) {
|
||||
sk := simple.NewWithSketchMeasure(ddsketch.NewDefaultConfig())
|
||||
require.NotPanics(t, func() { _ = sk.AggregatorFor(testGaugeDesc).(*gauge.Aggregator) })
|
||||
require.NotPanics(t, func() { _ = sk.AggregatorFor(testCounterDesc).(*counter.Aggregator) })
|
||||
require.NotPanics(t, func() { _ = sk.AggregatorFor(testMeasureDesc).(*ddsketch.Aggregator) })
|
||||
require.NotPanics(t, func() { _ = sk.AggregatorFor(testObserverDesc).(*ddsketch.Aggregator) })
|
||||
@ -54,7 +50,6 @@ func TestSketchMeasure(t *testing.T) {
|
||||
|
||||
func TestExactMeasure(t *testing.T) {
|
||||
ex := simple.NewWithExactMeasure()
|
||||
require.NotPanics(t, func() { _ = ex.AggregatorFor(testGaugeDesc).(*gauge.Aggregator) })
|
||||
require.NotPanics(t, func() { _ = ex.AggregatorFor(testCounterDesc).(*counter.Aggregator) })
|
||||
require.NotPanics(t, func() { _ = ex.AggregatorFor(testMeasureDesc).(*array.Aggregator) })
|
||||
require.NotPanics(t, func() { _ = ex.AggregatorFor(testObserverDesc).(*array.Aggregator) })
|
||||
|
@ -39,7 +39,7 @@ import (
|
||||
"go.opentelemetry.io/otel/sdk/export/metric/aggregator"
|
||||
sdk "go.opentelemetry.io/otel/sdk/metric"
|
||||
"go.opentelemetry.io/otel/sdk/metric/aggregator/counter"
|
||||
"go.opentelemetry.io/otel/sdk/metric/aggregator/gauge"
|
||||
"go.opentelemetry.io/otel/sdk/metric/aggregator/lastvalue"
|
||||
)
|
||||
|
||||
const (
|
||||
@ -78,7 +78,7 @@ type (
|
||||
newStore func() interface{}
|
||||
|
||||
// storeCollect and storeExpect are the same for
|
||||
// counters, different for gauges, to ensure we are
|
||||
// counters, different for lastValues, to ensure we are
|
||||
// testing the timestamps correctly.
|
||||
storeCollect func(store interface{}, value core.Number, ts time.Time)
|
||||
storeExpect func(store interface{}, value core.Number)
|
||||
@ -90,10 +90,10 @@ type (
|
||||
Impl() metric.InstrumentImpl
|
||||
}
|
||||
|
||||
// gaugeState supports merging gauge values, for the case
|
||||
// lastValueState supports merging lastValue values, for the case
|
||||
// where a race condition causes duplicate records. We always
|
||||
// take the later timestamp.
|
||||
gaugeState struct {
|
||||
lastValueState struct {
|
||||
// raw has to be aligned for 64-bit atomic operations.
|
||||
raw core.Number
|
||||
ts time.Time
|
||||
@ -231,11 +231,12 @@ func (f *testFixture) preCollect() {
|
||||
}
|
||||
|
||||
func (*testFixture) AggregatorFor(descriptor *export.Descriptor) export.Aggregator {
|
||||
switch descriptor.MetricKind() {
|
||||
case export.CounterKind:
|
||||
name := descriptor.Name()
|
||||
switch {
|
||||
case strings.HasSuffix(name, "counter"):
|
||||
return counter.New()
|
||||
case export.GaugeKind:
|
||||
return gauge.New()
|
||||
case strings.HasSuffix(name, "lastvalue"):
|
||||
return lastvalue.New()
|
||||
default:
|
||||
panic("Not implemented for this test")
|
||||
}
|
||||
@ -270,9 +271,9 @@ func (f *testFixture) Process(_ context.Context, record export.Record) error {
|
||||
f.T.Fatal("Sum error: ", err)
|
||||
}
|
||||
f.impl.storeCollect(actual, sum, time.Time{})
|
||||
case export.GaugeKind:
|
||||
gauge := agg.(aggregator.LastValue)
|
||||
lv, ts, err := gauge.LastValue()
|
||||
case export.MeasureKind:
|
||||
lvagg := agg.(aggregator.LastValue)
|
||||
lv, ts, err := lvagg.LastValue()
|
||||
if err != nil && err != aggregator.ErrNoLastValue {
|
||||
f.T.Fatal("Last value error: ", err)
|
||||
}
|
||||
@ -338,7 +339,7 @@ func float64sEqual(a, b core.Number) bool {
|
||||
func intCounterTestImpl(nonMonotonic bool) testImpl {
|
||||
return testImpl{
|
||||
newInstrument: func(meter api.Meter, name string) withImpl {
|
||||
return meter.NewInt64Counter(name, api.WithMonotonic(!nonMonotonic))
|
||||
return meter.NewInt64Counter(name+".counter", api.WithMonotonic(!nonMonotonic))
|
||||
},
|
||||
getUpdateValue: func() core.Number {
|
||||
var offset int64
|
||||
@ -384,7 +385,7 @@ func TestStressInt64CounterNonMonotonic(t *testing.T) {
|
||||
func floatCounterTestImpl(nonMonotonic bool) testImpl {
|
||||
return testImpl{
|
||||
newInstrument: func(meter api.Meter, name string) withImpl {
|
||||
return meter.NewFloat64Counter(name, api.WithMonotonic(!nonMonotonic))
|
||||
return meter.NewFloat64Counter(name+".counter", api.WithMonotonic(!nonMonotonic))
|
||||
},
|
||||
getUpdateValue: func() core.Number {
|
||||
var offset float64
|
||||
@ -427,34 +428,28 @@ func TestStressFloat64CounterNonMonotonic(t *testing.T) {
|
||||
stressTest(t, floatCounterTestImpl(true))
|
||||
}
|
||||
|
||||
// Gauges
|
||||
|
||||
func intGaugeTestImpl(monotonic bool) testImpl {
|
||||
// (Now()-startTime) is used as a free monotonic source
|
||||
startTime := time.Now()
|
||||
// LastValue
|
||||
|
||||
func intLastValueTestImpl() testImpl {
|
||||
return testImpl{
|
||||
newInstrument: func(meter api.Meter, name string) withImpl {
|
||||
return meter.NewInt64Gauge(name, api.WithMonotonic(monotonic))
|
||||
return meter.NewInt64Measure(name+".lastvalue", metric.WithAbsolute(false))
|
||||
},
|
||||
getUpdateValue: func() core.Number {
|
||||
if !monotonic {
|
||||
r1 := rand.Int63()
|
||||
return core.NewInt64Number(rand.Int63() - r1)
|
||||
}
|
||||
return core.NewInt64Number(int64(time.Since(startTime)))
|
||||
r1 := rand.Int63()
|
||||
return core.NewInt64Number(rand.Int63() - r1)
|
||||
},
|
||||
operate: func(inst interface{}, ctx context.Context, value core.Number, labels api.LabelSet) {
|
||||
gauge := inst.(api.Int64Gauge)
|
||||
gauge.Set(ctx, value.AsInt64(), labels)
|
||||
measure := inst.(api.Int64Measure)
|
||||
measure.Record(ctx, value.AsInt64(), labels)
|
||||
},
|
||||
newStore: func() interface{} {
|
||||
return &gaugeState{
|
||||
return &lastValueState{
|
||||
raw: core.NewInt64Number(0),
|
||||
}
|
||||
},
|
||||
storeCollect: func(store interface{}, value core.Number, ts time.Time) {
|
||||
gs := store.(*gaugeState)
|
||||
gs := store.(*lastValueState)
|
||||
|
||||
if !ts.Before(gs.ts) {
|
||||
gs.ts = ts
|
||||
@ -462,50 +457,40 @@ func intGaugeTestImpl(monotonic bool) testImpl {
|
||||
}
|
||||
},
|
||||
storeExpect: func(store interface{}, value core.Number) {
|
||||
gs := store.(*gaugeState)
|
||||
gs := store.(*lastValueState)
|
||||
gs.raw.SetInt64Atomic(value.AsInt64())
|
||||
},
|
||||
readStore: func(store interface{}) core.Number {
|
||||
gs := store.(*gaugeState)
|
||||
gs := store.(*lastValueState)
|
||||
return gs.raw.AsNumberAtomic()
|
||||
},
|
||||
equalValues: int64sEqual,
|
||||
}
|
||||
}
|
||||
|
||||
func TestStressInt64GaugeNormal(t *testing.T) {
|
||||
stressTest(t, intGaugeTestImpl(false))
|
||||
func TestStressInt64LastValue(t *testing.T) {
|
||||
stressTest(t, intLastValueTestImpl())
|
||||
}
|
||||
|
||||
func TestStressInt64GaugeMonotonic(t *testing.T) {
|
||||
stressTest(t, intGaugeTestImpl(true))
|
||||
}
|
||||
|
||||
func floatGaugeTestImpl(monotonic bool) testImpl {
|
||||
// (Now()-startTime) is used as a free monotonic source
|
||||
startTime := time.Now()
|
||||
|
||||
func floatLastValueTestImpl() testImpl {
|
||||
return testImpl{
|
||||
newInstrument: func(meter api.Meter, name string) withImpl {
|
||||
return meter.NewFloat64Gauge(name, api.WithMonotonic(monotonic))
|
||||
return meter.NewFloat64Measure(name+".lastvalue", metric.WithAbsolute(false))
|
||||
},
|
||||
getUpdateValue: func() core.Number {
|
||||
if !monotonic {
|
||||
return core.NewFloat64Number((-0.5 + rand.Float64()) * 100000)
|
||||
}
|
||||
return core.NewFloat64Number(float64(time.Since(startTime)))
|
||||
return core.NewFloat64Number((-0.5 + rand.Float64()) * 100000)
|
||||
},
|
||||
operate: func(inst interface{}, ctx context.Context, value core.Number, labels api.LabelSet) {
|
||||
gauge := inst.(api.Float64Gauge)
|
||||
gauge.Set(ctx, value.AsFloat64(), labels)
|
||||
measure := inst.(api.Float64Measure)
|
||||
measure.Record(ctx, value.AsFloat64(), labels)
|
||||
},
|
||||
newStore: func() interface{} {
|
||||
return &gaugeState{
|
||||
return &lastValueState{
|
||||
raw: core.NewFloat64Number(0),
|
||||
}
|
||||
},
|
||||
storeCollect: func(store interface{}, value core.Number, ts time.Time) {
|
||||
gs := store.(*gaugeState)
|
||||
gs := store.(*lastValueState)
|
||||
|
||||
if !ts.Before(gs.ts) {
|
||||
gs.ts = ts
|
||||
@ -513,21 +498,17 @@ func floatGaugeTestImpl(monotonic bool) testImpl {
|
||||
}
|
||||
},
|
||||
storeExpect: func(store interface{}, value core.Number) {
|
||||
gs := store.(*gaugeState)
|
||||
gs := store.(*lastValueState)
|
||||
gs.raw.SetFloat64Atomic(value.AsFloat64())
|
||||
},
|
||||
readStore: func(store interface{}) core.Number {
|
||||
gs := store.(*gaugeState)
|
||||
gs := store.(*lastValueState)
|
||||
return gs.raw.AsNumberAtomic()
|
||||
},
|
||||
equalValues: float64sEqual,
|
||||
}
|
||||
}
|
||||
|
||||
func TestStressFloat64GaugeNormal(t *testing.T) {
|
||||
stressTest(t, floatGaugeTestImpl(false))
|
||||
}
|
||||
|
||||
func TestStressFloat64GaugeMonotonic(t *testing.T) {
|
||||
stressTest(t, floatGaugeTestImpl(true))
|
||||
func TestStressFloat64LastValue(t *testing.T) {
|
||||
stressTest(t, floatLastValueTestImpl())
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user