From b3247f04fc47a92a71dbcdf34ce811a917cc607e Mon Sep 17 00:00:00 2001 From: Krzesimir Nowak Date: Wed, 23 Oct 2019 08:29:24 +0200 Subject: [PATCH] Update metrics API to match current spec (#228) * Update metrics API to match current spec * update options to match the spec * drop the global meter API * more docs * get rid of leftover mentions about descriptor --- api/metric/api.go | 368 +++++++++++++++++---------------- api/metric/api_test.go | 198 +++++++----------- api/metric/common.go | 64 +++--- api/metric/counter.go | 66 ++---- api/metric/doc.go | 16 +- api/metric/gauge.go | 66 ++---- api/metric/global.go | 4 +- api/metric/kind_string.go | 44 ---- api/metric/measure.go | 66 ++---- api/metric/mock_test.go | 147 ++++++++++--- api/metric/noop.go | 37 +++- api/metric/sdkhelpers.go | 87 ++++++++ api/metric/value.go | 31 ++- api/metric/valuekind_string.go | 24 +++ example/basic/main.go | 16 +- 15 files changed, 643 insertions(+), 591 deletions(-) delete mode 100644 api/metric/kind_string.go create mode 100644 api/metric/sdkhelpers.go create mode 100644 api/metric/valuekind_string.go diff --git a/api/metric/api.go b/api/metric/api.go index cdb5f5c7d..ba41f3e61 100644 --- a/api/metric/api.go +++ b/api/metric/api.go @@ -12,8 +12,6 @@ // See the License for the specific language governing permissions and // limitations under the License. -//go:generate stringer -type=Kind,ValueKind - package metric import ( @@ -23,25 +21,24 @@ import ( "go.opentelemetry.io/api/unit" ) -// Kind categorizes different kinds of metric. -type Kind int - -const ( - // Invalid describes an invalid metric. - Invalid Kind = iota - // CounterKind describes a metric that supports Add(). - CounterKind - // GaugeKind describes a metric that supports Set(). - GaugeKind - // MeasureKind describes a metric that supports Record(). - MeasureKind -) +// Instrument is the implementation-level interface Set/Add/Record +// individual metrics without precomputed labels. +type Instrument interface { + // AcquireHandle creates a Handle to record metrics with + // precomputed labels. + AcquireHandle(labels LabelSet) Handle + // RecordOne allows the SDK to observe a single metric event. + RecordOne(ctx context.Context, value MeasurementValue, labels LabelSet) +} // Handle is the implementation-level interface to Set/Add/Record -// individual metrics. +// individual metrics with precomputed labels. type Handle interface { - // RecordOne allows the SDK to observe a single metric event + // RecordOne allows the SDK to observe a single metric event. RecordOne(ctx context.Context, value MeasurementValue) + // Release frees the resources associated with this handle. It + // does not affect the metric this handle was created through. + Release() } // TODO this belongs outside the metrics API, in some sense, but that @@ -49,135 +46,115 @@ type Handle interface { // a LabelSet between metrics and tracing, even when they are the same // SDK. -// LabelSet represents a []core.KeyValue for use as pre-defined labels -// in the metrics API. +// TODO(krnowak): I wonder if this should just be: +// +// type LabelSet interface{} +// +// Not sure how the Meter function is useful. + +// LabelSet is an implementation-level interface that represents a +// []core.KeyValue for use as pre-defined labels in the metrics API. type LabelSet interface { Meter() Meter } -// WithDescriptor is an interface that all metric implement. -type WithDescriptor interface { - // Descriptor returns a descriptor of this metric. - Descriptor() *Descriptor +// Options contains some options for metrics of any kind. +type Options struct { + // Description is an optional field describing the metric + // instrument. + Description string + // Unit is an optional field describing the metric instrument. + Unit unit.Unit + // Keys are recommended keys determined in the handles + // obtained for the metric. + Keys []core.Key + // Alternate defines the property of metric value dependent on + // a metric type. + // + // - for Counter, true implies that the metric is an up-down + // Counter + // + // - for Gauge, true implies that the metric is a + // non-descending Gauge + // + // - for Measure, true implies that the metric supports + // positive and negative values + Alternate bool } -type hiddenType struct{} +// CounterOptionApplier is an interface for applying metric options +// that are valid only for counter metrics. +type CounterOptionApplier interface { + // ApplyCounterOption is used to make some general or + // counter-specific changes in the Options. + ApplyCounterOption(*Options) +} -// ExplicitReportingMetric is an interface that is implemented only by -// metrics that support getting a Handle. -type ExplicitReportingMetric interface { - WithDescriptor - // SupportHandle is a dummy function that can be only - // implemented in this package. - SupportHandle() hiddenType +// GaugeOptionApplier is an interface for applying metric options that +// are valid only for gauge metrics. +type GaugeOptionApplier interface { + // ApplyGaugeOption is used to make some general or + // gauge-specific changes in the Options. + ApplyGaugeOption(*Options) +} + +// MeasureOptionApplier is an interface for applying metric options +// that are valid only for measure metrics. +type MeasureOptionApplier interface { + // ApplyMeasureOption is used to make some general or + // measure-specific changes in the Options. + ApplyMeasureOption(*Options) +} + +// Measurement is used for reporting a batch of metric +// values. Instances of this type should be created by instruments +// (Int64Counter.Measurement()). +type Measurement struct { + instrument Instrument + value MeasurementValue +} + +// Instrument returns an instrument that created this measurement. +func (m Measurement) Instrument() Instrument { + return m.instrument +} + +// Value returns a value recorded in this measurement. +func (m Measurement) Value() MeasurementValue { + return m.value } // Meter is an interface to the metrics portion of the OpenTelemetry SDK. type Meter interface { - // DefineLabels returns a reference to a set of labels that - // cannot be read by the application. - DefineLabels(context.Context, ...core.KeyValue) LabelSet + // Labels returns a reference to a set of labels that cannot + // be read by the application. + Labels(context.Context, ...core.KeyValue) LabelSet + + // NewInt64Counter creates a new integral counter with a given + // name and customized with passed options. + NewInt64Counter(name string, cos ...CounterOptionApplier) Int64Counter + // NewFloat64Counter creates a new floating point counter with + // a given name and customized with passed options. + NewFloat64Counter(name string, cos ...CounterOptionApplier) Float64Counter + // NewInt64Gauge creates a new integral gauge with a given + // name and customized with passed options. + NewInt64Gauge(name string, gos ...GaugeOptionApplier) Int64Gauge + // NewFloat64Gauge creates a new floating point gauge with a + // given name and customized with passed options. + NewFloat64Gauge(name string, gos ...GaugeOptionApplier) Float64Gauge + // NewInt64Measure creates a new integral measure with a given + // name and customized with passed options. + NewInt64Measure(name string, mos ...MeasureOptionApplier) Int64Measure + // NewFloat64Measure creates a new floating point measure with + // a given name and customized with passed options. + NewFloat64Measure(name string, mos ...MeasureOptionApplier) Float64Measure // RecordBatch atomically records a batch of measurements. RecordBatch(context.Context, LabelSet, ...Measurement) - - // NewHandle creates a Handle that contains the passed - // key-value pairs. This should not be used directly - prefer - // using GetHandle function of a metric. - NewHandle(ExplicitReportingMetric, LabelSet) Handle - // DeleteHandle destroys the Handle and does a cleanup of the - // underlying resources. - DeleteHandle(Handle) -} - -// DescriptorID is a unique identifier of a metric. -type DescriptorID uint64 - -// ValueKind describes the data type of the measurement value the -// metric generates. -type ValueKind int8 - -const ( - // Int64ValueKind means that the metric generates values of - // type int64. - Int64ValueKind ValueKind = iota - // Float64ValueKind means that the metric generates values of - // type float64. - Float64ValueKind -) - -// Descriptor represents a named metric with recommended -// local-aggregation keys. -type Descriptor struct { - name string - kind Kind - keys []core.Key - id DescriptorID - description string - unit unit.Unit - valueKind ValueKind - alternate bool -} - -// Name is a required field describing this metric descriptor, should -// have length > 0. -func (d *Descriptor) Name() string { - return d.name -} - -// Kind is the metric kind of this descriptor. -func (d *Descriptor) Kind() Kind { - return d.kind -} - -// Keys are recommended keys determined in the handles obtained for -// this metric. -func (d *Descriptor) Keys() []core.Key { - return d.keys -} - -// ID is uniquely assigned to support per-SDK registration. -func (d *Descriptor) ID() DescriptorID { - return d.id -} - -// Description is an optional field describing this metric descriptor. -func (d *Descriptor) Description() string { - return d.description -} - -// Unit is an optional field describing this metric descriptor. -func (d *Descriptor) Unit() unit.Unit { - return d.unit -} - -// ValueKind describes the type of values the metric produces. -func (d *Descriptor) ValueKind() ValueKind { - return d.valueKind -} - -// Alternate defines the property of metric value dependent on a -// metric type. -// -// - for Counter, true implies that the metric is an up-down Counter -// -// - for Gauge, true implies that the metric is a -// non-descending Gauge -// -// - for Measure, true implies that the metric supports positive and -// negative values -func (d *Descriptor) Alternate() bool { - return d.alternate -} - -// Measurement is used for reporting a batch of metric values. -type Measurement struct { - Descriptor *Descriptor - Value MeasurementValue } // Option supports specifying the various metric options. -type Option func(*Descriptor) +type Option func(*Options) // OptionApplier is an interface for applying metric options that are // valid for all the kinds of metrics. @@ -185,37 +162,87 @@ type OptionApplier interface { CounterOptionApplier GaugeOptionApplier MeasureOptionApplier - // ApplyOption is used to make some changes in the Descriptor. - ApplyOption(*Descriptor) + // ApplyOption is used to make some general changes in the + // Options. + ApplyOption(*Options) +} + +// CounterGaugeOptionApplier is an interface for applying metric +// options that are valid for counter or gauge metrics. +type CounterGaugeOptionApplier interface { + CounterOptionApplier + GaugeOptionApplier } type optionWrapper struct { F Option } -var _ OptionApplier = optionWrapper{} - -func (o optionWrapper) ApplyCounterOption(d *Descriptor) { - o.ApplyOption(d) +type counterOptionWrapper struct { + F Option } -func (o optionWrapper) ApplyGaugeOption(d *Descriptor) { - o.ApplyOption(d) +type gaugeOptionWrapper struct { + F Option } -func (o optionWrapper) ApplyMeasureOption(d *Descriptor) { - o.ApplyOption(d) +type measureOptionWrapper struct { + F Option } -func (o optionWrapper) ApplyOption(d *Descriptor) { - o.F(d) +type counterGaugeOptionWrapper struct { + FC Option + FG Option +} + +var ( + _ OptionApplier = optionWrapper{} + _ CounterOptionApplier = counterOptionWrapper{} + _ GaugeOptionApplier = gaugeOptionWrapper{} + _ MeasureOptionApplier = measureOptionWrapper{} +) + +func (o optionWrapper) ApplyCounterOption(opts *Options) { + o.ApplyOption(opts) +} + +func (o optionWrapper) ApplyGaugeOption(opts *Options) { + o.ApplyOption(opts) +} + +func (o optionWrapper) ApplyMeasureOption(opts *Options) { + o.ApplyOption(opts) +} + +func (o optionWrapper) ApplyOption(opts *Options) { + o.F(opts) +} + +func (o counterOptionWrapper) ApplyCounterOption(opts *Options) { + o.F(opts) +} + +func (o gaugeOptionWrapper) ApplyGaugeOption(opts *Options) { + o.F(opts) +} + +func (o measureOptionWrapper) ApplyMeasureOption(opts *Options) { + o.F(opts) +} + +func (o counterGaugeOptionWrapper) ApplyCounterOption(opts *Options) { + o.FC(opts) +} + +func (o counterGaugeOptionWrapper) ApplyGaugeOption(opts *Options) { + o.FG(opts) } // WithDescription applies provided description. func WithDescription(desc string) OptionApplier { return optionWrapper{ - F: func(d *Descriptor) { - d.description = desc + F: func(opts *Options) { + opts.Description = desc }, } } @@ -223,56 +250,41 @@ func WithDescription(desc string) OptionApplier { // WithUnit applies provided unit. func WithUnit(unit unit.Unit) OptionApplier { return optionWrapper{ - F: func(d *Descriptor) { - d.unit = unit + F: func(opts *Options) { + opts.Unit = unit }, } } -// WithKeys applies required label keys. Multiple `WithKeys` options -// accumulate. +// WithKeys applies recommended label keys. Multiple `WithKeys` +// options accumulate. func WithKeys(keys ...core.Key) OptionApplier { return optionWrapper{ - F: func(d *Descriptor) { - d.keys = append(d.keys, keys...) + F: func(opts *Options) { + opts.Keys = append(opts.Keys, keys...) }, } } -// WithNonMonotonic sets whether a counter is permitted to go up AND -// down. -func WithNonMonotonic(nm bool) CounterOptionApplier { - return counterOptionWrapper{ - F: func(d *Descriptor) { - d.alternate = nm +// WithMonotonic sets whether a counter or a gauge is not permitted to +// go down. +func WithMonotonic(monotonic bool) CounterGaugeOptionApplier { + return counterGaugeOptionWrapper{ + FC: func(opts *Options) { + opts.Alternate = !monotonic + }, + FG: func(opts *Options) { + opts.Alternate = monotonic }, } } -// WithMonotonic sets whether a gauge is not permitted to go down. -func WithMonotonic(m bool) GaugeOptionApplier { - return gaugeOptionWrapper{ - F: func(d *Descriptor) { - d.alternate = m - }, - } -} - -// WithSigned sets whether a measure is permitted to be negative. -func WithSigned(s bool) MeasureOptionApplier { +// WithAbsolute sets whether a measure is not permitted to be +// negative. +func WithAbsolute(absolute bool) MeasureOptionApplier { return measureOptionWrapper{ - F: func(d *Descriptor) { - d.alternate = s + F: func(opts *Options) { + opts.Alternate = !absolute }, } } - -// Defined returns true when the descriptor has been registered. -func (d Descriptor) Defined() bool { - return len(d.name) != 0 -} - -// RecordBatch reports to the global Meter. -func RecordBatch(ctx context.Context, labels LabelSet, batch ...Measurement) { - GlobalMeter().RecordBatch(ctx, labels, batch...) -} diff --git a/api/metric/api_test.go b/api/metric/api_test.go index 9bc19a53c..d4f29da63 100644 --- a/api/metric/api_test.go +++ b/api/metric/api_test.go @@ -22,6 +22,8 @@ import ( "go.opentelemetry.io/api/core" "go.opentelemetry.io/api/key" "go.opentelemetry.io/api/unit" + + "github.com/google/go-cmp/cmp" ) func TestCounterOptions(t *testing.T) { @@ -103,7 +105,7 @@ func TestCounterOptions(t *testing.T) { { name: "nonmonotonic", opts: []CounterOptionApplier{ - WithNonMonotonic(true), + WithMonotonic(false), }, keys: nil, desc: "", @@ -113,8 +115,8 @@ func TestCounterOptions(t *testing.T) { { name: "nonmonotonic, but not really", opts: []CounterOptionApplier{ - WithNonMonotonic(true), - WithNonMonotonic(false), + WithMonotonic(false), + WithMonotonic(true), }, keys: nil, desc: "", @@ -122,24 +124,16 @@ func TestCounterOptions(t *testing.T) { alt: false, }, } - checkCounterDescriptor := func(tt testcase, vk ValueKind, d *Descriptor) { - e := descriptor{ - name: tt.name, - keys: tt.keys, - desc: tt.desc, - unit: tt.unit, - alt: tt.alt, - kind: CounterKind, - vk: vk, - } - checkDescriptor(t, e, d) - } for idx, tt := range testcases { t.Logf("Testing counter case %s (%d)", tt.name, idx) - f := NewFloat64Counter(tt.name, tt.opts...) - checkCounterDescriptor(tt, Float64ValueKind, f.Descriptor()) - i := NewInt64Counter(tt.name, tt.opts...) - checkCounterDescriptor(tt, Int64ValueKind, i.Descriptor()) + opts := &Options{} + ApplyCounterOptions(opts, tt.opts...) + checkOptions(t, opts, &Options{ + Description: tt.desc, + Unit: tt.unit, + Keys: tt.keys, + Alternate: tt.alt, + }) } } @@ -241,24 +235,16 @@ func TestGaugeOptions(t *testing.T) { alt: false, }, } - checkGaugeDescriptor := func(tt testcase, vk ValueKind, d *Descriptor) { - e := descriptor{ - name: tt.name, - keys: tt.keys, - desc: tt.desc, - unit: tt.unit, - alt: tt.alt, - kind: GaugeKind, - vk: vk, - } - checkDescriptor(t, e, d) - } for idx, tt := range testcases { t.Logf("Testing gauge case %s (%d)", tt.name, idx) - f := NewFloat64Gauge(tt.name, tt.opts...) - checkGaugeDescriptor(tt, Float64ValueKind, f.Descriptor()) - i := NewInt64Gauge(tt.name, tt.opts...) - checkGaugeDescriptor(tt, Int64ValueKind, i.Descriptor()) + opts := &Options{} + ApplyGaugeOptions(opts, tt.opts...) + checkOptions(t, opts, &Options{ + Description: tt.desc, + Unit: tt.unit, + Keys: tt.keys, + Alternate: tt.alt, + }) } } @@ -339,9 +325,9 @@ func TestMeasureOptions(t *testing.T) { alt: false, }, { - name: "signed", + name: "not absolute", opts: []MeasureOptionApplier{ - WithSigned(true), + WithAbsolute(false), }, keys: nil, desc: "", @@ -349,10 +335,10 @@ func TestMeasureOptions(t *testing.T) { alt: true, }, { - name: "signed, but not really", + name: "not absolute, but not really", opts: []MeasureOptionApplier{ - WithSigned(true), - WithSigned(false), + WithAbsolute(false), + WithAbsolute(true), }, keys: nil, desc: "", @@ -360,156 +346,112 @@ func TestMeasureOptions(t *testing.T) { alt: false, }, } - checkMeasureDescriptor := func(tt testcase, vk ValueKind, d *Descriptor) { - e := descriptor{ - name: tt.name, - keys: tt.keys, - desc: tt.desc, - unit: tt.unit, - alt: tt.alt, - kind: MeasureKind, - vk: vk, - } - checkDescriptor(t, e, d) - } for idx, tt := range testcases { t.Logf("Testing measure case %s (%d)", tt.name, idx) - f := NewFloat64Measure(tt.name, tt.opts...) - checkMeasureDescriptor(tt, Float64ValueKind, f.Descriptor()) - i := NewInt64Measure(tt.name, tt.opts...) - checkMeasureDescriptor(tt, Int64ValueKind, i.Descriptor()) + opts := &Options{} + ApplyMeasureOptions(opts, tt.opts...) + checkOptions(t, opts, &Options{ + Description: tt.desc, + Unit: tt.unit, + Keys: tt.keys, + Alternate: tt.alt, + }) } } -type descriptor struct { - name string - keys []core.Key - desc string - unit unit.Unit - alt bool - kind Kind - vk ValueKind -} - -func checkDescriptor(t *testing.T, e descriptor, d *Descriptor) { - if e.name != d.Name() { - t.Errorf("Expected name %q, got %q", e.name, d.Name()) - } - if len(e.keys) != len(d.Keys()) { - t.Errorf("Expected %d key(s), got %d", len(e.keys), len(d.Keys())) - } - minLen := len(e.keys) - if minLen > len(d.Keys()) { - minLen = len(d.Keys()) - } - for i := 0; i < minLen; i++ { - if e.keys[i] != d.Keys()[i] { - t.Errorf("Expected key %q, got %q", e.keys[i], d.Keys()[i]) - } - } - if e.desc != d.Description() { - t.Errorf("Expected description %q, got %q", e.desc, d.Description()) - } - if e.unit != d.Unit() { - t.Errorf("Expected unit %q, got %q", e.unit, d.Unit()) - } - if e.alt != d.Alternate() { - t.Errorf("Expected alternate %v, got %v", e.alt, d.Alternate()) - } - if e.vk != d.ValueKind() { - t.Errorf("Expected value kind %q, got %q", e.vk, d.ValueKind()) - } - if e.kind != d.Kind() { - t.Errorf("Expected kind %q, got %q", e.kind, d.Kind()) +func checkOptions(t *testing.T, got *Options, expected *Options) { + if diff := cmp.Diff(got, expected); diff != "" { + t.Errorf("Compare options: -got +want %s", diff) } } func TestCounter(t *testing.T) { { - c := NewFloat64Counter("ajwaj") meter := newMockMeter() + c := meter.NewFloat64Counter("ajwaj") ctx := context.Background() - labels := meter.DefineLabels(ctx) + labels := meter.Labels(ctx) c.Add(ctx, 42, labels) - handle := c.GetHandle(labels) + handle := c.AcquireHandle(labels) handle.Add(ctx, 42) meter.RecordBatch(ctx, labels, c.Measurement(42)) t.Log("Testing float counter") - checkBatches(t, ctx, labels, meter, c.Descriptor()) + checkBatches(t, ctx, labels, meter, Float64ValueKind, c.instrument) } { - c := NewInt64Counter("ajwaj") meter := newMockMeter() + c := meter.NewInt64Counter("ajwaj") ctx := context.Background() - labels := meter.DefineLabels(ctx) + labels := meter.Labels(ctx) c.Add(ctx, 42, labels) - handle := c.GetHandle(labels) + handle := c.AcquireHandle(labels) handle.Add(ctx, 42) meter.RecordBatch(ctx, labels, c.Measurement(42)) t.Log("Testing int counter") - checkBatches(t, ctx, labels, meter, c.Descriptor()) + checkBatches(t, ctx, labels, meter, Int64ValueKind, c.instrument) } } func TestGauge(t *testing.T) { { - g := NewFloat64Gauge("ajwaj") meter := newMockMeter() + g := meter.NewFloat64Gauge("ajwaj") ctx := context.Background() - labels := meter.DefineLabels(ctx) + labels := meter.Labels(ctx) g.Set(ctx, 42, labels) - handle := g.GetHandle(labels) + handle := g.AcquireHandle(labels) handle.Set(ctx, 42) meter.RecordBatch(ctx, labels, g.Measurement(42)) t.Log("Testing float gauge") - checkBatches(t, ctx, labels, meter, g.Descriptor()) + checkBatches(t, ctx, labels, meter, Float64ValueKind, g.instrument) } { - g := NewInt64Gauge("ajwaj") meter := newMockMeter() + g := meter.NewInt64Gauge("ajwaj") ctx := context.Background() - labels := meter.DefineLabels(ctx) + labels := meter.Labels(ctx) g.Set(ctx, 42, labels) - handle := g.GetHandle(labels) + handle := g.AcquireHandle(labels) handle.Set(ctx, 42) meter.RecordBatch(ctx, labels, g.Measurement(42)) t.Log("Testing int gauge") - checkBatches(t, ctx, labels, meter, g.Descriptor()) + checkBatches(t, ctx, labels, meter, Int64ValueKind, g.instrument) } } func TestMeasure(t *testing.T) { { - m := NewFloat64Measure("ajwaj") meter := newMockMeter() + m := meter.NewFloat64Measure("ajwaj") ctx := context.Background() - labels := meter.DefineLabels(ctx) + labels := meter.Labels(ctx) m.Record(ctx, 42, labels) - handle := m.GetHandle(labels) + handle := m.AcquireHandle(labels) handle.Record(ctx, 42) meter.RecordBatch(ctx, labels, m.Measurement(42)) t.Log("Testing float measure") - checkBatches(t, ctx, labels, meter, m.Descriptor()) + checkBatches(t, ctx, labels, meter, Float64ValueKind, m.instrument) } { - m := NewInt64Measure("ajwaj") meter := newMockMeter() + m := meter.NewInt64Measure("ajwaj") ctx := context.Background() - labels := meter.DefineLabels(ctx) + labels := meter.Labels(ctx) m.Record(ctx, 42, labels) - handle := m.GetHandle(labels) + handle := m.AcquireHandle(labels) handle.Record(ctx, 42) meter.RecordBatch(ctx, labels, m.Measurement(42)) t.Log("Testing int measure") - checkBatches(t, ctx, labels, meter, m.Descriptor()) + checkBatches(t, ctx, labels, meter, Int64ValueKind, m.instrument) } } -func checkBatches(t *testing.T, ctx context.Context, labels LabelSet, meter *mockMeter, descriptor *Descriptor) { +func checkBatches(t *testing.T, ctx context.Context, labels LabelSet, meter *mockMeter, kind ValueKind, instrument Instrument) { t.Helper() if len(meter.measurementBatches) != 3 { t.Errorf("Expected 3 recorded measurement batches, got %d", len(meter.measurementBatches)) } + ourInstrument := instrument.(*mockInstrument) ourLabelSet := labels.(*mockLabelSet) minLen := 3 if minLen > len(meter.measurementBatches) { @@ -538,15 +480,15 @@ func checkBatches(t *testing.T, ctx context.Context, labels LabelSet, meter *moc } for j := 0; j < minMLen; j++ { measurement := got.measurements[j] - if measurement.Descriptor != descriptor { - d := func(d *Descriptor) string { - return fmt.Sprintf("(ptr: %p, descriptor %#v)", d, d) + if measurement.instrument != ourInstrument { + d := func(i *mockInstrument) string { + return fmt.Sprintf("(ptr: %p, instrument %#v)", i, i) } - t.Errorf("Wrong recorded descriptor in measurement %d in batch %d, expected %s, got %s", j, i, d(descriptor), d(measurement.Descriptor)) + t.Errorf("Wrong recorded instrument in measurement %d in batch %d, expected %s, got %s", j, i, d(ourInstrument), d(measurement.instrument)) } - ft := fortyTwo(t, descriptor.ValueKind()) - if measurement.Value.RawCompare(ft.AsRaw(), descriptor.ValueKind()) != 0 { - t.Errorf("Wrong recorded value in measurement %d in batch %d, expected %s, got %s", j, i, ft.Emit(descriptor.ValueKind()), measurement.Value.Emit(descriptor.ValueKind())) + ft := fortyTwo(t, kind) + if measurement.value.RawCompare(ft.AsRaw(), kind) != 0 { + t.Errorf("Wrong recorded value in measurement %d in batch %d, expected %s, got %s", j, i, ft.Emit(kind), measurement.value.Emit(kind)) } } } diff --git a/api/metric/common.go b/api/metric/common.go index 1858a6e7b..1e91e3ebf 100644 --- a/api/metric/common.go +++ b/api/metric/common.go @@ -16,63 +16,55 @@ package metric import ( "context" - "sync/atomic" -) - -var ( - descriptorID uint64 ) type commonMetric struct { - d *Descriptor + instrument Instrument } -var _ ExplicitReportingMetric = commonMetric{} - -func (m commonMetric) Descriptor() *Descriptor { - return m.d +type commonHandle struct { + handle Handle } -func (m commonMetric) SupportHandle() hiddenType { - return hiddenType{} -} - -func (m commonMetric) getHandle(labels LabelSet) Handle { - return labels.Meter().NewHandle(m, labels) +func (m commonMetric) acquireCommonHandle(labels LabelSet) commonHandle { + return newCommonHandle(m.instrument.AcquireHandle(labels)) } func (m commonMetric) float64Measurement(value float64) Measurement { - return Measurement{ - Descriptor: m.d, - Value: NewFloat64MeasurementValue(value), - } + return newMeasurement(m.instrument, NewFloat64MeasurementValue(value)) } func (m commonMetric) int64Measurement(value int64) Measurement { - return Measurement{ - Descriptor: m.d, - Value: NewInt64MeasurementValue(value), - } + return newMeasurement(m.instrument, NewInt64MeasurementValue(value)) } func (m commonMetric) recordOne(ctx context.Context, value MeasurementValue, labels LabelSet) { - labels.Meter().RecordBatch(ctx, labels, Measurement{ - Descriptor: m.d, - Value: value, - }) + m.instrument.RecordOne(ctx, value, labels) } -func registerCommonMetric(name string, kind Kind, valueKind ValueKind) commonMetric { +func (h commonHandle) recordOne(ctx context.Context, value MeasurementValue) { + h.handle.RecordOne(ctx, value) +} + +func (h commonHandle) Release() { + h.handle.Release() +} + +func newCommonMetric(instrument Instrument) commonMetric { return commonMetric{ - d: registerDescriptor(name, kind, valueKind), + instrument: instrument, } } -func registerDescriptor(name string, kind Kind, valueKind ValueKind) *Descriptor { - return &Descriptor{ - name: name, - kind: kind, - valueKind: valueKind, - id: DescriptorID(atomic.AddUint64(&descriptorID, 1)), +func newCommonHandle(handle Handle) commonHandle { + return commonHandle{ + handle: handle, + } +} + +func newMeasurement(instrument Instrument, value MeasurementValue) Measurement { + return Measurement{ + instrument: instrument, + value: value, } } diff --git a/api/metric/counter.go b/api/metric/counter.go index d8b1b2a63..0f9231da7 100644 --- a/api/metric/counter.go +++ b/api/metric/counter.go @@ -29,78 +29,40 @@ type Int64Counter struct { } // Float64CounterHandle is a handle for Float64Counter. +// +// It inherits the Release function from commonHandle. type Float64CounterHandle struct { - Handle + commonHandle } // Int64CounterHandle is a handle for Int64Counter. +// +// It inherits the Release function from commonHandle. type Int64CounterHandle struct { - Handle + commonHandle } -// CounterOptionApplier is an interface for applying metric options -// that are valid only for counter metrics. -type CounterOptionApplier interface { - // ApplyCounterOption is used to make some counter-specific - // changes in the Descriptor. - ApplyCounterOption(*Descriptor) -} - -type counterOptionWrapper struct { - F Option -} - -var ( - _ CounterOptionApplier = counterOptionWrapper{} - _ ExplicitReportingMetric = Float64Counter{} - _ ExplicitReportingMetric = Int64Counter{} -) - -func (o counterOptionWrapper) ApplyCounterOption(d *Descriptor) { - o.F(d) -} - -func newCounter(name string, valueKind ValueKind, mos ...CounterOptionApplier) commonMetric { - m := registerCommonMetric(name, CounterKind, valueKind) - for _, opt := range mos { - opt.ApplyCounterOption(m.Descriptor()) - } - return m -} - -// NewFloat64Counter creates a new counter for float64. -func NewFloat64Counter(name string, mos ...CounterOptionApplier) (c Float64Counter) { - c.commonMetric = newCounter(name, Float64ValueKind, mos...) - return -} - -// NewInt64Counter creates a new counter for int64. -func NewInt64Counter(name string, mos ...CounterOptionApplier) (c Int64Counter) { - c.commonMetric = newCounter(name, Int64ValueKind, mos...) - return -} - -// GetHandle creates a handle for this counter. The labels should +// AcquireHandle creates a handle for this counter. The labels should // contain the keys and values for each key specified in the counter // with the WithKeys option. // // If the labels do not contain a value for the key specified in the // counter with the WithKeys option, then the missing value will be // treated as unspecified. -func (c *Float64Counter) GetHandle(labels LabelSet) (h Float64CounterHandle) { - h.Handle = c.getHandle(labels) +func (c *Float64Counter) AcquireHandle(labels LabelSet) (h Float64CounterHandle) { + h.commonHandle = c.acquireCommonHandle(labels) return } -// GetHandle creates a handle for this counter. The labels should +// AcquireHandle creates a handle for this counter. The labels should // contain the keys and values for each key specified in the counter // with the WithKeys option. // // If the labels do not contain a value for the key specified in the // counter with the WithKeys option, then the missing value will be // treated as unspecified. -func (c *Int64Counter) GetHandle(labels LabelSet) (h Int64CounterHandle) { - h.Handle = c.getHandle(labels) +func (c *Int64Counter) AcquireHandle(labels LabelSet) (h Int64CounterHandle) { + h.commonHandle = c.acquireCommonHandle(labels) return } @@ -140,10 +102,10 @@ func (c *Int64Counter) Add(ctx context.Context, value int64, labels LabelSet) { // Add adds the value to the counter's sum. func (h *Float64CounterHandle) Add(ctx context.Context, value float64) { - h.RecordOne(ctx, NewFloat64MeasurementValue(value)) + h.recordOne(ctx, NewFloat64MeasurementValue(value)) } // Add adds the value to the counter's sum. func (h *Int64CounterHandle) Add(ctx context.Context, value int64) { - h.RecordOne(ctx, NewInt64MeasurementValue(value)) + h.recordOne(ctx, NewInt64MeasurementValue(value)) } diff --git a/api/metric/doc.go b/api/metric/doc.go index d079cdead..f6bf44818 100644 --- a/api/metric/doc.go +++ b/api/metric/doc.go @@ -36,9 +36,10 @@ // example could be bank account balance or bytes downloaded. Counters // can be created with either NewFloat64Counter or // NewInt64Counter. Counters expect non-negative values by default to -// be reported. This can be changed with the WithNonMonotonic option -// passed to the New*Counter function - this allows reporting negative -// values. To report the new value, use an Add function. +// be reported. This can be changed with the WithMonotonic option +// (passing false as a parameter) passed to the Meter.New*Counter +// function - this allows reporting negative values. To report the new +// value, use an Add function. // // Gauges are instruments that are reporting a current state of a // value. An example could be voltage or temperature. Gauges can be @@ -55,13 +56,14 @@ // time or lines of code in the project over time. Measures can be // created with either NewFloat64Measure or NewInt64Measure. Measures // by default take only non-negative values. This can be changed with -// the WithSigned option passed to the New*Measure function - this -// allows reporting negative values too. To report a new value, use -// the Record function. +// the WithAbsolute option (passing false as a parameter) passed to +// the New*Measure function - this allows reporting negative values +// too. To report a new value, use the Record function. // // All the basic kinds of instruments also support creating handles // for a potentially more efficient reporting. The handles have the // same function names as the instruments (so counter handle has Add, // gauge handle has Set and measure handle has Record). Handles can be -// created with the GetHandle function of the respective instrument. +// created with the AcquireHandle function of the respective +// instrument. When done with the handle, call Release on it. package metric // import "go.opentelemetry.io/api/metric" diff --git a/api/metric/gauge.go b/api/metric/gauge.go index 933e51f27..0cc964a86 100644 --- a/api/metric/gauge.go +++ b/api/metric/gauge.go @@ -29,78 +29,40 @@ type Int64Gauge struct { } // Float64GaugeHandle is a handle for Float64Gauge. +// +// It inherits the Release function from commonHandle. type Float64GaugeHandle struct { - Handle + commonHandle } // Int64GaugeHandle is a handle for Int64Gauge. +// +// It inherits the Release function from commonHandle. type Int64GaugeHandle struct { - Handle + commonHandle } -// GaugeOptionApplier is an interface for applying metric options that -// are valid only for gauge metrics. -type GaugeOptionApplier interface { - // ApplyGaugeOption is used to make some gauge-specific - // changes in the Descriptor. - ApplyGaugeOption(*Descriptor) -} - -type gaugeOptionWrapper struct { - F Option -} - -var ( - _ GaugeOptionApplier = gaugeOptionWrapper{} - _ ExplicitReportingMetric = Float64Gauge{} - _ ExplicitReportingMetric = Int64Gauge{} -) - -func (o gaugeOptionWrapper) ApplyGaugeOption(d *Descriptor) { - o.F(d) -} - -func newGauge(name string, valueKind ValueKind, mos ...GaugeOptionApplier) commonMetric { - m := registerCommonMetric(name, GaugeKind, valueKind) - for _, opt := range mos { - opt.ApplyGaugeOption(m.Descriptor()) - } - return m -} - -// NewFloat64Gauge creates a new gauge for float64. -func NewFloat64Gauge(name string, mos ...GaugeOptionApplier) (g Float64Gauge) { - g.commonMetric = newGauge(name, Float64ValueKind, mos...) - return -} - -// NewInt64Gauge creates a new gauge for int64. -func NewInt64Gauge(name string, mos ...GaugeOptionApplier) (g Int64Gauge) { - g.commonMetric = newGauge(name, Int64ValueKind, mos...) - return -} - -// GetHandle creates a handle for this gauge. The labels should +// AcquireHandle creates a handle 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) GetHandle(labels LabelSet) (h Float64GaugeHandle) { - h.Handle = g.getHandle(labels) +func (g *Float64Gauge) AcquireHandle(labels LabelSet) (h Float64GaugeHandle) { + h.commonHandle = g.acquireCommonHandle(labels) return } -// GetHandle creates a handle for this gauge. The labels should +// AcquireHandle creates a handle 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) GetHandle(labels LabelSet) (h Int64GaugeHandle) { - h.Handle = g.getHandle(labels) +func (g *Int64Gauge) AcquireHandle(labels LabelSet) (h Int64GaugeHandle) { + h.commonHandle = g.acquireCommonHandle(labels) return } @@ -140,10 +102,10 @@ func (g *Int64Gauge) Set(ctx context.Context, value int64, labels LabelSet) { // Set assigns the passed value to the value of the gauge. func (h *Float64GaugeHandle) Set(ctx context.Context, value float64) { - h.RecordOne(ctx, NewFloat64MeasurementValue(value)) + h.recordOne(ctx, NewFloat64MeasurementValue(value)) } // Set assigns the passed value to the value of the gauge. func (h *Int64GaugeHandle) Set(ctx context.Context, value int64) { - h.RecordOne(ctx, NewInt64MeasurementValue(value)) + h.recordOne(ctx, NewInt64MeasurementValue(value)) } diff --git a/api/metric/global.go b/api/metric/global.go index 1840429d3..5c0d402a0 100644 --- a/api/metric/global.go +++ b/api/metric/global.go @@ -14,7 +14,9 @@ package metric -import "sync/atomic" +import ( + "sync/atomic" +) var global atomic.Value diff --git a/api/metric/kind_string.go b/api/metric/kind_string.go deleted file mode 100644 index dbadb4cf5..000000000 --- a/api/metric/kind_string.go +++ /dev/null @@ -1,44 +0,0 @@ -// Code generated by "stringer -type=Kind,ValueKind"; DO NOT EDIT. - -package metric - -import "strconv" - -func _() { - // An "invalid array index" compiler error signifies that the constant values have changed. - // Re-run the stringer command to generate them again. - var x [1]struct{} - _ = x[Invalid-0] - _ = x[CounterKind-1] - _ = x[GaugeKind-2] - _ = x[MeasureKind-3] -} - -const _Kind_name = "InvalidCounterKindGaugeKindMeasureKind" - -var _Kind_index = [...]uint8{0, 7, 18, 27, 38} - -func (i Kind) String() string { - if i < 0 || i >= Kind(len(_Kind_index)-1) { - return "Kind(" + strconv.FormatInt(int64(i), 10) + ")" - } - return _Kind_name[_Kind_index[i]:_Kind_index[i+1]] -} -func _() { - // An "invalid array index" compiler error signifies that the constant values have changed. - // Re-run the stringer command to generate them again. - var x [1]struct{} - _ = x[Int64ValueKind-0] - _ = x[Float64ValueKind-1] -} - -const _ValueKind_name = "Int64ValueKindFloat64ValueKind" - -var _ValueKind_index = [...]uint8{0, 14, 30} - -func (i ValueKind) String() string { - if i < 0 || i >= ValueKind(len(_ValueKind_index)-1) { - return "ValueKind(" + strconv.FormatInt(int64(i), 10) + ")" - } - return _ValueKind_name[_ValueKind_index[i]:_ValueKind_index[i+1]] -} diff --git a/api/metric/measure.go b/api/metric/measure.go index b0b7166c6..e3c2328a2 100644 --- a/api/metric/measure.go +++ b/api/metric/measure.go @@ -29,78 +29,40 @@ type Int64Measure struct { } // Float64MeasureHandle is a handle for Float64Measure. +// +// It inherits the Release function from commonHandle. type Float64MeasureHandle struct { - Handle + commonHandle } // Int64MeasureHandle is a handle for Int64Measure. +// +// It inherits the Release function from commonHandle. type Int64MeasureHandle struct { - Handle + commonHandle } -// MeasureOptionApplier is an interface for applying metric options -// that are valid only for measure metrics. -type MeasureOptionApplier interface { - // ApplyMeasureOption is used to make some measure-specific - // changes in the Descriptor. - ApplyMeasureOption(*Descriptor) -} - -type measureOptionWrapper struct { - F Option -} - -var ( - _ MeasureOptionApplier = measureOptionWrapper{} - _ ExplicitReportingMetric = Float64Measure{} - _ ExplicitReportingMetric = Int64Measure{} -) - -func (o measureOptionWrapper) ApplyMeasureOption(d *Descriptor) { - o.F(d) -} - -func newMeasure(name string, valueKind ValueKind, mos ...MeasureOptionApplier) commonMetric { - m := registerCommonMetric(name, MeasureKind, valueKind) - for _, opt := range mos { - opt.ApplyMeasureOption(m.Descriptor()) - } - return m -} - -// NewFloat64Measure creates a new measure for float64. -func NewFloat64Measure(name string, mos ...MeasureOptionApplier) (c Float64Measure) { - c.commonMetric = newMeasure(name, Float64ValueKind, mos...) - return -} - -// NewInt64Measure creates a new measure for int64. -func NewInt64Measure(name string, mos ...MeasureOptionApplier) (c Int64Measure) { - c.commonMetric = newMeasure(name, Int64ValueKind, mos...) - return -} - -// GetHandle creates a handle for this measure. The labels should +// AcquireHandle creates a handle for this measure. The labels should // contain the keys and values for each key specified in the measure // with the WithKeys option. // // If the labels do not contain a value for the key specified in the // measure with the WithKeys option, then the missing value will be // treated as unspecified. -func (c *Float64Measure) GetHandle(labels LabelSet) (h Float64MeasureHandle) { - h.Handle = c.getHandle(labels) +func (c *Float64Measure) AcquireHandle(labels LabelSet) (h Float64MeasureHandle) { + h.commonHandle = c.acquireCommonHandle(labels) return } -// GetHandle creates a handle for this measure. The labels should +// AcquireHandle creates a handle for this measure. The labels should // contain the keys and values for each key specified in the measure // with the WithKeys option. // // If the labels do not contain a value for the key specified in the // measure with the WithKeys option, then the missing value will be // treated as unspecified. -func (c *Int64Measure) GetHandle(labels LabelSet) (h Int64MeasureHandle) { - h.Handle = c.getHandle(labels) +func (c *Int64Measure) AcquireHandle(labels LabelSet) (h Int64MeasureHandle) { + h.commonHandle = c.acquireCommonHandle(labels) return } @@ -140,10 +102,10 @@ func (c *Int64Measure) Record(ctx context.Context, value int64, labels LabelSet) // Record adds a new value to the list of measure's records. func (h *Float64MeasureHandle) Record(ctx context.Context, value float64) { - h.RecordOne(ctx, NewFloat64MeasurementValue(value)) + h.recordOne(ctx, NewFloat64MeasurementValue(value)) } // Record adds a new value to the list of measure's records. func (h *Int64MeasureHandle) Record(ctx context.Context, value int64) { - h.RecordOne(ctx, NewInt64MeasurementValue(value)) + h.recordOne(ctx, NewInt64MeasurementValue(value)) } diff --git a/api/metric/mock_test.go b/api/metric/mock_test.go index 604067d9e..5c375c4ca 100644 --- a/api/metric/mock_test.go +++ b/api/metric/mock_test.go @@ -22,10 +22,17 @@ import ( type ( mockHandle struct { - descriptor *Descriptor + instrument *mockInstrument labelSet *mockLabelSet } + mockInstrument struct { + name string + kind mockKind + valueKind ValueKind + opts Options + } + mockLabelSet struct { meter *mockMeter labels map[core.Key]core.Value @@ -34,24 +41,56 @@ type ( batch struct { ctx context.Context labelSet *mockLabelSet - measurements []Measurement + measurements []mockMeasurement } mockMeter struct { measurementBatches []batch } + + mockKind int8 + + mockMeasurement struct { + instrument *mockInstrument + value MeasurementValue + } ) var ( - _ Handle = &mockHandle{} - _ LabelSet = &mockLabelSet{} - _ Meter = &mockMeter{} + _ Instrument = &mockInstrument{} + _ Handle = &mockHandle{} + _ LabelSet = &mockLabelSet{} + _ Meter = &mockMeter{} ) +const ( + mockKindCounter mockKind = iota + mockKindGauge + mockKindMeasure +) + +func (i *mockInstrument) AcquireHandle(labels LabelSet) Handle { + return &mockHandle{ + instrument: i, + labelSet: labels.(*mockLabelSet), + } +} + +func (i *mockInstrument) RecordOne(ctx context.Context, value MeasurementValue, labels LabelSet) { + doRecordBatch(labels.(*mockLabelSet), ctx, i, value) +} + func (h *mockHandle) RecordOne(ctx context.Context, value MeasurementValue) { - h.labelSet.meter.RecordBatch(ctx, h.labelSet, Measurement{ - Descriptor: h.descriptor, - Value: value, + doRecordBatch(h.labelSet, ctx, h.instrument, value) +} + +func (h *mockHandle) Release() { +} + +func doRecordBatch(labelSet *mockLabelSet, ctx context.Context, instrument *mockInstrument, value MeasurementValue) { + labelSet.meter.recordMockBatch(ctx, labelSet, mockMeasurement{ + instrument: instrument, + value: value, }) } @@ -63,7 +102,7 @@ func newMockMeter() *mockMeter { return &mockMeter{} } -func (m *mockMeter) DefineLabels(ctx context.Context, labels ...core.KeyValue) LabelSet { +func (m *mockMeter) Labels(ctx context.Context, labels ...core.KeyValue) LabelSet { ul := make(map[core.Key]core.Value) for _, kv := range labels { ul[kv.Key] = kv.Value @@ -74,24 +113,86 @@ func (m *mockMeter) DefineLabels(ctx context.Context, labels ...core.KeyValue) L } } -func (m *mockMeter) RecordBatch(ctx context.Context, labels LabelSet, measurements ...Measurement) { - ourLabelSet := labels.(*mockLabelSet) - m.measurementBatches = append(m.measurementBatches, batch{ - ctx: ctx, - labelSet: ourLabelSet, - measurements: measurements, - }) +func (m *mockMeter) NewInt64Counter(name string, cos ...CounterOptionApplier) Int64Counter { + instrument := m.newCounterInstrument(name, Int64ValueKind, cos...) + return WrapInt64CounterInstrument(instrument) } -func (m *mockMeter) NewHandle(erm ExplicitReportingMetric, labels LabelSet) Handle { - descriptor := erm.Descriptor() - ourLabels := labels.(*mockLabelSet) +func (m *mockMeter) NewFloat64Counter(name string, cos ...CounterOptionApplier) Float64Counter { + instrument := m.newCounterInstrument(name, Float64ValueKind, cos...) + return WrapFloat64CounterInstrument(instrument) +} - return &mockHandle{ - descriptor: descriptor, - labelSet: ourLabels, +func (m *mockMeter) newCounterInstrument(name string, valueKind ValueKind, cos ...CounterOptionApplier) *mockInstrument { + opts := Options{} + ApplyCounterOptions(&opts, cos...) + return &mockInstrument{ + name: name, + kind: mockKindCounter, + valueKind: valueKind, + opts: opts, } } -func (m *mockMeter) DeleteHandle(Handle) { +func (m *mockMeter) NewInt64Gauge(name string, gos ...GaugeOptionApplier) Int64Gauge { + instrument := m.newGaugeInstrument(name, Int64ValueKind, gos...) + return WrapInt64GaugeInstrument(instrument) +} + +func (m *mockMeter) NewFloat64Gauge(name string, gos ...GaugeOptionApplier) Float64Gauge { + instrument := m.newGaugeInstrument(name, Float64ValueKind, gos...) + return WrapFloat64GaugeInstrument(instrument) +} + +func (m *mockMeter) newGaugeInstrument(name string, valueKind ValueKind, gos ...GaugeOptionApplier) *mockInstrument { + opts := Options{} + ApplyGaugeOptions(&opts, gos...) + return &mockInstrument{ + name: name, + kind: mockKindGauge, + valueKind: valueKind, + opts: opts, + } +} + +func (m *mockMeter) NewInt64Measure(name string, mos ...MeasureOptionApplier) Int64Measure { + instrument := m.newMeasureInstrument(name, Int64ValueKind, mos...) + return WrapInt64MeasureInstrument(instrument) +} + +func (m *mockMeter) NewFloat64Measure(name string, mos ...MeasureOptionApplier) Float64Measure { + instrument := m.newMeasureInstrument(name, Float64ValueKind, mos...) + return WrapFloat64MeasureInstrument(instrument) +} + +func (m *mockMeter) newMeasureInstrument(name string, valueKind ValueKind, mos ...MeasureOptionApplier) *mockInstrument { + opts := Options{} + ApplyMeasureOptions(&opts, mos...) + return &mockInstrument{ + name: name, + kind: mockKindMeasure, + valueKind: valueKind, + opts: opts, + } +} + +func (m *mockMeter) RecordBatch(ctx context.Context, labels LabelSet, measurements ...Measurement) { + ourLabelSet := labels.(*mockLabelSet) + mm := make([]mockMeasurement, len(measurements)) + for i := 0; i < len(measurements); i++ { + m := measurements[i] + mm[i] = mockMeasurement{ + instrument: m.Instrument().(*mockInstrument), + value: m.Value(), + } + } + m.recordMockBatch(ctx, ourLabelSet, mm...) +} + +func (m *mockMeter) recordMockBatch(ctx context.Context, labelSet *mockLabelSet, measurements ...mockMeasurement) { + m.measurementBatches = append(m.measurementBatches, batch{ + ctx: ctx, + labelSet: labelSet, + measurements: measurements, + }) } diff --git a/api/metric/noop.go b/api/metric/noop.go index 487e63cfe..5741f2cd3 100644 --- a/api/metric/noop.go +++ b/api/metric/noop.go @@ -9,27 +9,56 @@ import ( type noopMeter struct{} type noopHandle struct{} type noopLabelSet struct{} +type noopInstrument struct{} var _ Meter = noopMeter{} +var _ Instrument = noopInstrument{} var _ Handle = noopHandle{} var _ LabelSet = noopLabelSet{} func (noopHandle) RecordOne(context.Context, MeasurementValue) { } +func (noopHandle) Release() { +} + +func (noopInstrument) AcquireHandle(LabelSet) Handle { + return noopHandle{} +} + +func (noopInstrument) RecordOne(context.Context, MeasurementValue, LabelSet) { +} + func (noopLabelSet) Meter() Meter { return noopMeter{} } -func (noopMeter) DefineLabels(context.Context, ...core.KeyValue) LabelSet { +func (noopMeter) Labels(context.Context, ...core.KeyValue) LabelSet { return noopLabelSet{} } -func (noopMeter) NewHandle(ExplicitReportingMetric, LabelSet) Handle { - return noopHandle{} +func (noopMeter) NewInt64Counter(name string, cos ...CounterOptionApplier) Int64Counter { + return WrapInt64CounterInstrument(noopInstrument{}) } -func (noopMeter) DeleteHandle(Handle) { +func (noopMeter) NewFloat64Counter(name string, cos ...CounterOptionApplier) Float64Counter { + return WrapFloat64CounterInstrument(noopInstrument{}) +} + +func (noopMeter) NewInt64Gauge(name string, gos ...GaugeOptionApplier) Int64Gauge { + return WrapInt64GaugeInstrument(noopInstrument{}) +} + +func (noopMeter) NewFloat64Gauge(name string, gos ...GaugeOptionApplier) Float64Gauge { + return WrapFloat64GaugeInstrument(noopInstrument{}) +} + +func (noopMeter) NewInt64Measure(name string, mos ...MeasureOptionApplier) Int64Measure { + return WrapInt64MeasureInstrument(noopInstrument{}) +} + +func (noopMeter) NewFloat64Measure(name string, mos ...MeasureOptionApplier) Float64Measure { + return WrapFloat64MeasureInstrument(noopInstrument{}) } func (noopMeter) RecordBatch(context.Context, LabelSet, ...Measurement) { diff --git a/api/metric/sdkhelpers.go b/api/metric/sdkhelpers.go new file mode 100644 index 000000000..0a31823c8 --- /dev/null +++ b/api/metric/sdkhelpers.go @@ -0,0 +1,87 @@ +// 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 + +// WrapInt64CounterInstrument wraps the instrument in the type-safe +// wrapper as an integral counter. +// +// It is mostly intended for SDKs. +func WrapInt64CounterInstrument(instrument Instrument) Int64Counter { + return Int64Counter{commonMetric: newCommonMetric(instrument)} +} + +// WrapFloat64CounterInstrument wraps the instrument in the type-safe +// wrapper as an floating point counter. +// +// It is mostly intended for SDKs. +func WrapFloat64CounterInstrument(instrument Instrument) Float64Counter { + return Float64Counter{commonMetric: newCommonMetric(instrument)} +} + +// WrapInt64GaugeInstrument wraps the instrument in the type-safe +// wrapper as an integral gauge. +// +// It is mostly intended for SDKs. +func WrapInt64GaugeInstrument(instrument Instrument) 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 Instrument) Float64Gauge { + return Float64Gauge{commonMetric: newCommonMetric(instrument)} +} + +// WrapInt64MeasureInstrument wraps the instrument in the type-safe +// wrapper as an integral measure. +// +// It is mostly intended for SDKs. +func WrapInt64MeasureInstrument(instrument Instrument) Int64Measure { + return Int64Measure{commonMetric: newCommonMetric(instrument)} +} + +// WrapFloat64MeasureInstrument wraps the instrument in the type-safe +// wrapper as an floating point measure. +// +// It is mostly intended for SDKs. +func WrapFloat64MeasureInstrument(instrument Instrument) Float64Measure { + return Float64Measure{commonMetric: newCommonMetric(instrument)} +} + +// ApplyCounterOptions is a helper that applies all the counter +// options to passed opts. +func ApplyCounterOptions(opts *Options, cos ...CounterOptionApplier) { + for _, o := range cos { + o.ApplyCounterOption(opts) + } +} + +// ApplyGaugeOptions is a helper that applies all the gauge options to +// passed opts. +func ApplyGaugeOptions(opts *Options, gos ...GaugeOptionApplier) { + for _, o := range gos { + o.ApplyGaugeOption(opts) + } +} + +// ApplyMeasureOptions is a helper that applies all the measure +// options to passed opts. +func ApplyMeasureOptions(opts *Options, mos ...MeasureOptionApplier) { + for _, o := range mos { + o.ApplyMeasureOption(opts) + } +} diff --git a/api/metric/value.go b/api/metric/value.go index b0368b27e..4550ceb50 100644 --- a/api/metric/value.go +++ b/api/metric/value.go @@ -19,10 +19,25 @@ import ( "math" ) +//go:generate stringer -type=ValueKind + +// ValueKind describes the data type of the measurement value the +// metric generates. +type ValueKind int8 + +const ( + // Int64ValueKind means that the metric generates values of + // type int64. + Int64ValueKind ValueKind = iota + // Float64ValueKind means that the metric generates values of + // type float64. + Float64ValueKind +) + // MeasurementValue represents either an integral or a floating point -// value of a measurement. It needs to be accompanied with a -// descriptor of a metric that generated this value to decide what -// type of value it represents. +// value of a measurement. It needs to be accompanied with a value +// kind or some source that provides a value kind describing this +// measurement value. type MeasurementValue uint64 // NewInt64MeasurementValue creates an integral MeasurementValue. @@ -41,15 +56,17 @@ func newFromRaw(raw uint64) MeasurementValue { } // AsInt64 assumes that the measurement value contains an int64 and -// returns it as such. Make sure that metric that generated this value -// has indeed Int64ValueKind in its descriptor. +// returns it as such. Make sure that the accompanying source of value +// kind indeed tells you its a 64 bit integral measurement value, +// otherwise the returned int64 will be wrong. func (v MeasurementValue) AsInt64() int64 { return rawToInt64(v.AsRaw()) } // AsFloat64 assumes that the measurement value contains a float64 and -// returns it as such. Make sure that metric that generated this value -// has indeed Int64ValueKind in its descriptor. +// returns it as such. Make sure that the accompanying source of value +// kind indeed tells you its a 64 bit floating point measurement +// value, otherwise the returned float64 will be wrong. func (v MeasurementValue) AsFloat64() float64 { return rawToFloat64(v.AsRaw()) } diff --git a/api/metric/valuekind_string.go b/api/metric/valuekind_string.go new file mode 100644 index 000000000..499cd3027 --- /dev/null +++ b/api/metric/valuekind_string.go @@ -0,0 +1,24 @@ +// Code generated by "stringer -type=ValueKind"; DO NOT EDIT. + +package metric + +import "strconv" + +func _() { + // An "invalid array index" compiler error signifies that the constant values have changed. + // Re-run the stringer command to generate them again. + var x [1]struct{} + _ = x[Int64ValueKind-0] + _ = x[Float64ValueKind-1] +} + +const _ValueKind_name = "Int64ValueKindFloat64ValueKind" + +var _ValueKind_index = [...]uint8{0, 14, 30} + +func (i ValueKind) String() string { + if i < 0 || i >= ValueKind(len(_ValueKind_index)-1) { + return "ValueKind(" + strconv.FormatInt(int64(i), 10) + ")" + } + return _ValueKind_name[_ValueKind_index[i]:_ValueKind_index[i+1]] +} diff --git a/example/basic/main.go b/example/basic/main.go index 3ce32b7fe..a6ed1940d 100644 --- a/example/basic/main.go +++ b/example/basic/main.go @@ -32,16 +32,16 @@ var ( barKey = key.New("ex.com/bar") lemonsKey = key.New("ex.com/lemons") anotherKey = key.New("ex.com/another") +) - oneMetric = metric.NewFloat64Gauge("ex.com/one", +func main() { + oneMetric := meter.NewFloat64Gauge("ex.com.one", metric.WithKeys(fooKey, barKey, lemonsKey), metric.WithDescription("A gauge set to 1.0"), ) - measureTwo = metric.NewFloat64Measure("ex.com/two") -) + measureTwo := meter.NewFloat64Measure("ex.com.two") -func main() { ctx := context.Background() ctx = distributedcontext.NewContext(ctx, @@ -49,11 +49,13 @@ func main() { distributedcontext.Insert(barKey.String("bar1")), ) - commonLabels := meter.DefineLabels(ctx, lemonsKey.Int(10)) + commonLabels := meter.Labels(ctx, lemonsKey.Int(10)) - gauge := oneMetric.GetHandle(commonLabels) + gauge := oneMetric.AcquireHandle(commonLabels) + defer gauge.Release() - measure := measureTwo.GetHandle(commonLabels) + measure := measureTwo.AcquireHandle(commonLabels) + defer measure.Release() err := tracer.WithSpan(ctx, "operation", func(ctx context.Context) error {