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