From c888fc18ed998af70f6f834d3b8aa1265eeba4f0 Mon Sep 17 00:00:00 2001 From: jmacd Date: Thu, 19 Sep 2019 15:04:32 +0200 Subject: [PATCH 01/35] initial metrics work --- api/core/key.go | 1 + api/metric/api.go | 164 ++++++++++++++---- api/metric/common.go | 17 +- api/metric/cumulative.go | 77 ++++++++ api/metric/gauge.go | 60 ++++++- api/metric/global.go | 2 +- api/metric/kind_string.go | 26 +++ api/metric/measure.go | 77 ++++++++ api/metric/noop_meter.go | 28 ++- api/stats/stats.go | 106 ----------- example/basic/main.go | 32 +++- experimental/streaming/example/basic/main.go | 39 +++-- .../streaming/exporter/eventtype_string.go | 15 +- experimental/streaming/exporter/exporter.go | 24 ++- .../exporter/reader/format/format.go | 58 ++++--- .../streaming/exporter/reader/reader.go | 110 +++++------- .../streaming/exporter/spandata/spandata.go | 13 +- experimental/streaming/sdk/metric.go | 96 ++++++++++ experimental/streaming/sdk/package.go | 19 ++ experimental/streaming/sdk/span.go | 20 +-- experimental/streaming/sdk/trace.go | 25 ++- 21 files changed, 703 insertions(+), 306 deletions(-) create mode 100644 api/metric/cumulative.go create mode 100644 api/metric/kind_string.go create mode 100644 api/metric/measure.go delete mode 100644 api/stats/stats.go create mode 100644 experimental/streaming/sdk/metric.go create mode 100644 experimental/streaming/sdk/package.go diff --git a/api/core/key.go b/api/core/key.go index 9b09b3cee5b..e6c78f3a8c4 100644 --- a/api/core/key.go +++ b/api/core/key.go @@ -25,6 +25,7 @@ type Value struct { String string Bytes []byte + // TODO See how segmentio/stats handles this type, it's much smaller. // TODO Lazy value type? } diff --git a/api/metric/api.go b/api/metric/api.go index 37e56eae509..9321072b20c 100644 --- a/api/metric/api.go +++ b/api/metric/api.go @@ -21,62 +21,166 @@ import ( "go.opentelemetry.io/api/unit" ) -type MetricType int +// Kind categorizes different kinds of metric. +type Kind int +//go:generate stringer -type=Kind const ( - Invalid MetricType = iota - Gauge // Supports Set() - Cumulative // Supports Inc() + Invalid Kind = iota + CumulativeKind // Supports Add() + GaugeKind // Supports Set() + MeasureKind // Supports Record() ) -type Meter interface { - // TODO more Metric types - GetFloat64Gauge(ctx context.Context, gauge *Float64GaugeHandle, labels ...core.KeyValue) Float64Gauge +// Recorder is the implementation-level interface to Set/Add/Record individual metrics. +type Recorder interface { + // Record allows the SDK to observe a single metric event + Record(ctx context.Context, value float64) } -type Float64Gauge interface { - Set(ctx context.Context, value float64, labels ...core.KeyValue) +// LabelSet represents a []core.KeyValue for use as pre-defined labels +// in the metrics API. +// +// TODO this belongs outside the metrics API, in some sense, but that +// might create a dependency. Putting this here means we can't re-use +// a LabelSet between metrics and tracing, even when they are the same +// SDK. +type LabelSet interface { + Meter() Meter } -type Handle struct { - Name string +// 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 + + // RecorderFor returns a handle for observing single measurements. + RecorderFor(context.Context, LabelSet, Instrument) Recorder + + // RecordSingle records a single measurement without computing a handle. + RecordSingle(context.Context, LabelSet, Measurement) + + // RecordBatch atomically records a batch of measurements. An + // implementation may elect to call `RecordSingle` on each + // measurement, or it could choose a more-optimized approach. + RecordBatch(context.Context, LabelSet, ...Measurement) +} + +type InstrumentID uint64 + +// Instrument represents a named metric with recommended local-aggregation keys. +type Instrument struct { + // Name is a required field describing this metric instrument, + // should have length > 0. + Name string + + // ID is uniquely assigned to support per-SDK registration. + ID InstrumentID + + // Description is an optional field describing this metric instrument. Description string - Unit unit.Unit - Type MetricType + // Unit is an optional field describing this metric instrument. + Unit unit.Unit + + // Kind is the metric kind of this instrument. + Kind Kind + + // Bidirectional implies this is an up-down Cumulative. + Bidirectional bool + + // Unidirectional implies this is a non-descending Gauge. + Unidirectional bool + + // NonNegative implies this is a non-negative Measure. + NonNegative bool + + // Disabled implies this instrument is disabled by default. + Disabled bool + + // Keys are required keys determined in the handles + // obtained for this metric. Keys []core.Key } -type Option func(*Handle) +// Handle contains a Recorder to support the implementation-defined +// behavior of reporting a single metric with pre-determined label +// values. +type Handle struct { + Recorder +} + +// Measurement is used for reporting a batch of metric values. +type Measurement struct { + Instrument Instrument + Value float64 +} + +// Option supports specifying the various metric options. +type Option func(*Instrument) // WithDescription applies provided description. func WithDescription(desc string) Option { - return func(m *Handle) { - m.Description = desc + return func(inst *Instrument) { + inst.Description = desc } } // WithUnit applies provided unit. func WithUnit(unit unit.Unit) Option { - return func(m *Handle) { - m.Unit = unit + return func(inst *Instrument) { + inst.Unit = unit } } -// WithKeys applies the provided dimension keys. -func WithKeys(keys ...core.Key) Option { - return func(m *Handle) { - m.Keys = keys +// WithBidirectional sets whether a cumulative is permitted to go up AND down. +func WithBidirectional(bi bool) Option { + return func(inst *Instrument) { + inst.Bidirectional = bi + } +} + +// WithUnidirectional sets whether a gauge is not permitted to go down. +func WithUnidirectional(uni bool) Option { + return func(inst *Instrument) { + inst.Unidirectional = uni + } +} + +// WithNonNegative sets whether a measure is not permitted to be negative. +func WithNonNegative(non bool) Option { + return func(inst *Instrument) { + inst.NonNegative = non } } -func (mtype MetricType) String() string { - switch mtype { - case Gauge: - return "gauge" - case Cumulative: - return "cumulative" - default: - return "unknown" +// WithDisabled sets whether a measure is disabled by default +func WithDisabled(dis bool) Option { + return func(inst *Instrument) { + inst.Disabled = dis } } + +// WithKeys applies required label keys. Multiple `WithKeys` +// options accumulate. +func WithKeys(keys ...core.Key) Option { + return func(m *Instrument) { + m.Keys = append(m.Keys, keys...) + } +} + +// Defined returns true when the instrument has been registered. +func (inst Instrument) Defined() bool { + return len(inst.Name) != 0 +} + +// RecordSingle reports to the global Meter. +func RecordSingle(ctx context.Context, labels LabelSet, measurement Measurement) { + GlobalMeter().RecordSingle(ctx, labels, measurement) +} + +// 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/common.go b/api/metric/common.go index 93d85ad7cd8..99e4370daed 100644 --- a/api/metric/common.go +++ b/api/metric/common.go @@ -14,11 +14,18 @@ package metric -func registerMetric(name string, mtype MetricType, opts []Option, metric *Handle) { +import "sync/atomic" + +var ( + instrumentID uint64 +) + +func registerInstrument(name string, kind Kind, opts []Option, inst *Instrument) { + inst.Name = name + inst.Kind = kind + inst.ID = InstrumentID(atomic.AddUint64(&instrumentID, 1)) + for _, opt := range opts { - opt(metric) + opt(inst) } - - metric.Name = name - metric.Type = mtype } diff --git a/api/metric/cumulative.go b/api/metric/cumulative.go new file mode 100644 index 00000000000..8357bbebc7c --- /dev/null +++ b/api/metric/cumulative.go @@ -0,0 +1,77 @@ +// 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" +) + +type Float64Cumulative struct { + Instrument +} + +type Int64Cumulative struct { + Instrument +} + +type Float64CumulativeHandle struct { + Handle +} + +type Int64CumulativeHandle struct { + Handle +} + +func NewFloat64Cumulative(name string, mos ...Option) (c Float64Cumulative) { + registerInstrument(name, CumulativeKind, mos, &c.Instrument) + return +} + +func NewInt64Cumulative(name string, mos ...Option) (c Int64Cumulative) { + registerInstrument(name, CumulativeKind, mos, &c.Instrument) + return +} + +func (c *Float64Cumulative) GetHandle(ctx context.Context, labels LabelSet) (h Float64CumulativeHandle) { + h.Recorder = labels.Meter().RecorderFor(ctx, labels, c.Instrument) + return +} + +func (c *Int64Cumulative) GetHandle(ctx context.Context, labels LabelSet) (h Int64CumulativeHandle) { + h.Recorder = labels.Meter().RecorderFor(ctx, labels, c.Instrument) + return +} + +func (g *Float64Cumulative) Add(ctx context.Context, value float64, labels LabelSet) { + labels.Meter().RecordSingle(ctx, labels, Measurement{ + Instrument: g.Instrument, + Value: value, + }) +} + +func (g *Int64Cumulative) Add(ctx context.Context, value int64, labels LabelSet) { + labels.Meter().RecordSingle(ctx, labels, Measurement{ + Instrument: g.Instrument, + Value: float64(value), + }) +} + +func (g *Float64CumulativeHandle) Add(ctx context.Context, value float64) { + g.Recorder.Record(ctx, value) +} + +func (g *Int64CumulativeHandle) Add(ctx context.Context, value int64) { + g.Recorder.Record(ctx, float64(value)) +} diff --git a/api/metric/gauge.go b/api/metric/gauge.go index 34a895480d0..29f5385f6de 100644 --- a/api/metric/gauge.go +++ b/api/metric/gauge.go @@ -14,12 +14,64 @@ package metric +import ( + "context" +) + +type Float64Gauge struct { + Instrument +} + +type Int64Gauge struct { + Instrument +} + type Float64GaugeHandle struct { Handle } -func NewFloat64Gauge(name string, mos ...Option) *Float64GaugeHandle { - g := &Float64GaugeHandle{} - registerMetric(name, Gauge, mos, &g.Handle) - return g +type Int64GaugeHandle struct { + Handle +} + +func NewFloat64Gauge(name string, mos ...Option) (g Float64Gauge) { + registerInstrument(name, GaugeKind, mos, &g.Instrument) + return +} + +func NewInt64Gauge(name string, mos ...Option) (g Int64Gauge) { + registerInstrument(name, GaugeKind, mos, &g.Instrument) + return +} + +func (g *Float64Gauge) GetHandle(ctx context.Context, labels LabelSet) (h Float64GaugeHandle) { + h.Recorder = labels.Meter().RecorderFor(ctx, labels, g.Instrument) + return +} + +func (g *Int64Gauge) GetHandle(ctx context.Context, labels LabelSet) (h Int64GaugeHandle) { + h.Recorder = labels.Meter().RecorderFor(ctx, labels, g.Instrument) + return +} + +func (g *Float64Gauge) Set(ctx context.Context, value float64, labels LabelSet) { + labels.Meter().RecordSingle(ctx, labels, Measurement{ + Instrument: g.Instrument, + Value: value, + }) +} + +func (g *Int64Gauge) Set(ctx context.Context, value int64, labels LabelSet) { + labels.Meter().RecordSingle(ctx, labels, Measurement{ + Instrument: g.Instrument, + Value: float64(value), + }) +} + +func (g *Float64GaugeHandle) Set(ctx context.Context, value float64) { + g.Recorder.Record(ctx, value) +} + +func (g *Int64GaugeHandle) Set(ctx context.Context, value int64) { + g.Recorder.Record(ctx, float64(value)) } diff --git a/api/metric/global.go b/api/metric/global.go index 804689d7c0c..f5527b75c77 100644 --- a/api/metric/global.go +++ b/api/metric/global.go @@ -24,7 +24,7 @@ func GlobalMeter() Meter { if t := global.Load(); t != nil { return t.(Meter) } - return NoopMeter{} + return noopMeter{} } // SetGlobalMeter sets provided meter as a global meter. diff --git a/api/metric/kind_string.go b/api/metric/kind_string.go new file mode 100644 index 00000000000..78a067e64ec --- /dev/null +++ b/api/metric/kind_string.go @@ -0,0 +1,26 @@ +// Code generated by "stringer -type=Kind"; 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[CumulativeKind-1] + _ = x[GaugeKind-2] + _ = x[MeasureKind-3] +} + +const _Kind_name = "InvalidCumulativeKindGaugeKindMeasureKind" + +var _Kind_index = [...]uint8{0, 7, 21, 30, 41} + +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]] +} diff --git a/api/metric/measure.go b/api/metric/measure.go new file mode 100644 index 00000000000..68f911c8b08 --- /dev/null +++ b/api/metric/measure.go @@ -0,0 +1,77 @@ +// 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" +) + +type Float64Measure struct { + Instrument +} + +type Int64Measure struct { + Instrument +} + +type Float64MeasureHandle struct { + Handle +} + +type Int64MeasureHandle struct { + Handle +} + +func NewFloat64Measure(name string, mos ...Option) (m Float64Measure) { + registerInstrument(name, MeasureKind, mos, &m.Instrument) + return +} + +func NewInt64Measure(name string, mos ...Option) (m Int64Measure) { + registerInstrument(name, MeasureKind, mos, &m.Instrument) + return +} + +func (m *Float64Measure) GetHandle(ctx context.Context, labels LabelSet) (h Float64MeasureHandle) { + h.Recorder = labels.Meter().RecorderFor(ctx, labels, m.Instrument) + return +} + +func (m *Int64Measure) GetHandle(ctx context.Context, labels LabelSet) (h Int64MeasureHandle) { + h.Recorder = labels.Meter().RecorderFor(ctx, labels, m.Instrument) + return +} + +func (g *Float64Measure) Record(ctx context.Context, value float64, labels LabelSet) { + labels.Meter().RecordSingle(ctx, labels, Measurement{ + Instrument: g.Instrument, + Value: value, + }) +} + +func (g *Int64Measure) Record(ctx context.Context, value int64, labels LabelSet) { + labels.Meter().RecordSingle(ctx, labels, Measurement{ + Instrument: g.Instrument, + Value: float64(value), + }) +} + +func (g *Float64MeasureHandle) Record(ctx context.Context, value float64) { + g.Recorder.Record(ctx, value) +} + +func (g *Int64MeasureHandle) Record(ctx context.Context, value int64) { + g.Recorder.Record(ctx, float64(value)) +} diff --git a/api/metric/noop_meter.go b/api/metric/noop_meter.go index e361862b42b..4c4aca6481b 100644 --- a/api/metric/noop_meter.go +++ b/api/metric/noop_meter.go @@ -6,17 +6,31 @@ import ( "go.opentelemetry.io/api/core" ) -type NoopMeter struct{} +type noopMeter struct{} +type noopRecorder struct{} +type noopLabelSet struct{} -type noopMetric struct{} +var _ Meter = noopMeter{} +var _ Recorder = noopRecorder{} +var _ LabelSet = noopLabelSet{} -var _ Meter = NoopMeter{} +func (noopRecorder) Record(ctx context.Context, value float64) { +} + +func (noopLabelSet) Meter() Meter { + return noopMeter{} +} -var _ Float64Gauge = noopMetric{} +func (noopMeter) DefineLabels(ctx context.Context, labels ...core.KeyValue) LabelSet { + return noopLabelSet{} +} + +func (noopMeter) RecordSingle(context.Context, LabelSet, Measurement) { +} -func (NoopMeter) GetFloat64Gauge(ctx context.Context, gauge *Float64GaugeHandle, labels ...core.KeyValue) Float64Gauge { - return noopMetric{} +func (noopMeter) RecordBatch(context.Context, LabelSet, ...Measurement) { } -func (noopMetric) Set(ctx context.Context, value float64, labels ...core.KeyValue) { +func (noopMeter) RecorderFor(context.Context, LabelSet, Instrument) Recorder { + return noopRecorder{} } diff --git a/api/stats/stats.go b/api/stats/stats.go deleted file mode 100644 index df7f0de2f44..00000000000 --- a/api/stats/stats.go +++ /dev/null @@ -1,106 +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 stats - -import ( - "context" - "sync/atomic" - - "go.opentelemetry.io/api/core" -) - -type MeasureHandle struct { - Name string -} - -type Measure interface { - N() string - M(value float64) Measurement -} - -type Measurement struct { - Measure Measure - Value float64 -} - -type Recorder interface { - // TODO: Note as in rfc 0001, allow raw Measures to have pre-defined labels: - GetMeasure(ctx context.Context, measure *MeasureHandle, labels ...core.KeyValue) Measure - - Record(ctx context.Context, m ...Measurement) - RecordSingle(ctx context.Context, m Measurement) -} - -type noopRecorder struct{} -type noopMeasure struct{} - -var global atomic.Value - -// GlobalRecorder return meter registered with global registry. -// If no meter is registered then an instance of noop Recorder is returned. -func GlobalRecorder() Recorder { - if t := global.Load(); t != nil { - return t.(Recorder) - } - return noopRecorder{} -} - -// SetGlobalRecorder sets provided meter as a global meter. -func SetGlobalRecorder(t Recorder) { - global.Store(t) -} - -func Record(ctx context.Context, m ...Measurement) { - GlobalRecorder().Record(ctx, m...) -} - -func RecordSingle(ctx context.Context, m Measurement) { - GlobalRecorder().RecordSingle(ctx, m) -} - -func NewMeasure(name string) *MeasureHandle { - return &MeasureHandle{ - Name: name, - } -} - -func (m *MeasureHandle) M(value float64) Measurement { - return Measurement{ - Measure: m, - Value: value, - } -} - -func (m *MeasureHandle) N() string { - return m.Name -} - -func (noopRecorder) Record(ctx context.Context, m ...Measurement) { -} - -func (noopRecorder) RecordSingle(ctx context.Context, m Measurement) { -} - -func (noopRecorder) GetMeasure(ctx context.Context, handle *MeasureHandle, labels ...core.KeyValue) Measure { - return noopMeasure{} -} - -func (noopMeasure) M(float64) Measurement { - return Measurement{} -} - -func (noopMeasure) N() string { - return "" -} diff --git a/example/basic/main.go b/example/basic/main.go index cabc9f8eb12..f71a128b67e 100644 --- a/example/basic/main.go +++ b/example/basic/main.go @@ -19,7 +19,6 @@ import ( "go.opentelemetry.io/api/key" "go.opentelemetry.io/api/metric" - "go.opentelemetry.io/api/stats" "go.opentelemetry.io/api/tag" "go.opentelemetry.io/api/trace" ) @@ -39,7 +38,7 @@ var ( metric.WithDescription("A gauge set to 1.0"), ) - measureTwo = stats.NewMeasure("ex.com/two") + measureTwo = metric.NewFloat64Measure("ex.com/two") ) func main() { @@ -50,11 +49,11 @@ func main() { tag.Insert(barKey.String("bar1")), ) - gauge := meter.GetFloat64Gauge( - ctx, - oneMetric, - lemonsKey.Int(10), - ) + commonLabels := meter.DefineLabels(ctx, lemonsKey.Int(10)) + + gauge := oneMetric.GetHandle(ctx, commonLabels) + + measure := measureTwo.GetHandle(ctx, commonLabels) err := tracer.WithSpan(ctx, "operation", func(ctx context.Context) error { @@ -64,6 +63,23 @@ func main() { gauge.Set(ctx, 1) + meter.RecordBatch( + // Note: call-site variables added as context tags: + tag.NewContext(ctx, + tag.Insert(anotherKey.String("xyz"))), + commonLabels, + + metric.Measurement{ + Instrument: oneMetric.Instrument, + Value: 1.0, + }, + + metric.Measurement{ + Instrument: measureTwo.Instrument, + Value: 2.0, + }, + ) + return tracer.WithSpan( ctx, "Sub operation...", @@ -72,7 +88,7 @@ func main() { trace.CurrentSpan(ctx).AddEvent(ctx, "Sub span event") - stats.Record(ctx, measureTwo.M(1.3)) + measure.Record(ctx, 1.3) return nil }, diff --git a/experimental/streaming/example/basic/main.go b/experimental/streaming/example/basic/main.go index 0e32d7a2638..4425244f488 100644 --- a/experimental/streaming/example/basic/main.go +++ b/experimental/streaming/example/basic/main.go @@ -19,7 +19,6 @@ import ( "go.opentelemetry.io/api/key" "go.opentelemetry.io/api/metric" - "go.opentelemetry.io/api/stats" "go.opentelemetry.io/api/tag" "go.opentelemetry.io/api/trace" @@ -28,10 +27,9 @@ import ( ) var ( - streaming = sdk.New(spanlog.New()) - - tracer trace.Tracer = streaming - meter metric.Meter = metric.NoopMeter{} + streaming = sdk.New(spanlog.New()) + tracer trace.Tracer = streaming + meter metric.Meter = streaming fooKey = key.New("ex.com/foo") barKey = key.New("ex.com/bar") @@ -43,7 +41,7 @@ var ( metric.WithDescription("A gauge set to 1.0"), ) - measureTwo = stats.NewMeasure("ex.com/two") + measureTwo = metric.NewFloat64Measure("ex.com/two") ) func main() { @@ -54,11 +52,11 @@ func main() { tag.Insert(barKey.String("bar1")), ) - gauge := meter.GetFloat64Gauge( - ctx, - oneMetric, - lemonsKey.Int(10), - ) + commonLabels := meter.DefineLabels(ctx, lemonsKey.Int(10)) + + gauge := oneMetric.GetHandle(ctx, commonLabels) + + measure := measureTwo.GetHandle(ctx, commonLabels) err := tracer.WithSpan(ctx, "operation", func(ctx context.Context) error { @@ -68,6 +66,23 @@ func main() { gauge.Set(ctx, 1) + meter.RecordBatch( + // Note: call-site variables added as context tags: + tag.NewContext(ctx, + tag.Insert(anotherKey.String("xyz"))), + commonLabels, + + metric.Measurement{ + Instrument: oneMetric.Instrument, + Value: 1.0, + }, + + metric.Measurement{ + Instrument: measureTwo.Instrument, + Value: 2.0, + }, + ) + return tracer.WithSpan( ctx, "Sub operation...", @@ -76,7 +91,7 @@ func main() { trace.CurrentSpan(ctx).AddEvent(ctx, "Sub span event") - stats.Record(ctx, measureTwo.M(1.3)) + measure.Record(ctx, 1.3) return nil }, diff --git a/experimental/streaming/exporter/eventtype_string.go b/experimental/streaming/exporter/eventtype_string.go index 3a780fe9b87..55ebe17ade7 100644 --- a/experimental/streaming/exporter/eventtype_string.go +++ b/experimental/streaming/exporter/eventtype_string.go @@ -13,17 +13,16 @@ func _() { _ = x[END_SPAN-2] _ = x[ADD_EVENT-3] _ = x[NEW_SCOPE-4] - _ = x[NEW_MEASURE-5] - _ = x[NEW_METRIC-6] - _ = x[MODIFY_ATTR-7] - _ = x[RECORD_STATS-8] - _ = x[SET_STATUS-9] - _ = x[SET_NAME-10] + _ = x[MODIFY_ATTR-5] + _ = x[SET_STATUS-6] + _ = x[SET_NAME-7] + _ = x[SINGLE_METRIC-8] + _ = x[BATCH_METRIC-9] } -const _EventType_name = "INVALIDSTART_SPANEND_SPANADD_EVENTNEW_SCOPENEW_MEASURENEW_METRICMODIFY_ATTRRECORD_STATSSET_STATUSSET_NAME" +const _EventType_name = "INVALIDSTART_SPANEND_SPANADD_EVENTNEW_SCOPEMODIFY_ATTRSET_STATUSSET_NAMESINGLE_METRICBATCH_METRIC" -var _EventType_index = [...]uint8{0, 7, 17, 25, 34, 43, 54, 64, 75, 87, 97, 105} +var _EventType_index = [...]uint8{0, 7, 17, 25, 34, 43, 54, 64, 72, 85, 97} func (i EventType) String() string { if i < 0 || i >= EventType(len(_EventType_index)-1) { diff --git a/experimental/streaming/exporter/exporter.go b/experimental/streaming/exporter/exporter.go index 91186e86cd3..568f9b5269a 100644 --- a/experimental/streaming/exporter/exporter.go +++ b/experimental/streaming/exporter/exporter.go @@ -8,7 +8,7 @@ import ( "google.golang.org/grpc/codes" "go.opentelemetry.io/api/core" - "go.opentelemetry.io/api/stats" + "go.opentelemetry.io/api/metric" "go.opentelemetry.io/api/tag" ) @@ -21,6 +21,11 @@ type ScopeID struct { core.SpanContext } +type Measurement struct { + Instrument metric.Instrument + Value float64 +} + type Event struct { // Automatic fields Sequence EventID // Auto-filled @@ -40,11 +45,10 @@ type Event struct { Status codes.Code // SET_STATUS // Values - String string // START_SPAN, EVENT, SET_NAME, ... - Float64 float64 - Parent ScopeID // START_SPAN - Stats []stats.Measurement - Stat stats.Measurement + String string // START_SPAN, EVENT, SET_NAME, ... + Parent ScopeID // START_SPAN + Measurement Measurement + Measurements []Measurement } type Observer interface { @@ -58,12 +62,11 @@ const ( END_SPAN ADD_EVENT NEW_SCOPE - NEW_MEASURE - NEW_METRIC MODIFY_ATTR - RECORD_STATS SET_STATUS SET_NAME + SINGLE_METRIC // A metric Set(), Add(), Record(), or RecordSingle() + BATCH_METRIC // A RecordBatch() ) type Exporter struct { @@ -101,6 +104,9 @@ func (e *Exporter) Foreach(f func(Observer)) { } func (e *Exporter) NewScope(parent ScopeID, attributes ...core.KeyValue) ScopeID { + if len(attributes) == 0 { + return parent + } eventID := e.Record(Event{ Type: NEW_SCOPE, Scope: parent, diff --git a/experimental/streaming/exporter/reader/format/format.go b/experimental/streaming/exporter/reader/format/format.go index 20bdd0dc52c..8d75a832fd2 100644 --- a/experimental/streaming/exporter/reader/format/format.go +++ b/experimental/streaming/exporter/reader/format/format.go @@ -20,6 +20,8 @@ import ( "go.opentelemetry.io/api/core" "go.opentelemetry.io/api/key" + "go.opentelemetry.io/api/metric" + "go.opentelemetry.io/api/tag" "go.opentelemetry.io/experimental/streaming/exporter" "go.opentelemetry.io/experimental/streaming/exporter/reader" @@ -89,27 +91,17 @@ func AppendEvent(buf *strings.Builder, data reader.Event) { case exporter.MODIFY_ATTR: buf.WriteString("modify attr ") buf.WriteString(data.Type.String()) - case exporter.RECORD_STATS: - buf.WriteString("record") - - for _, s := range data.Stats { - f(false)(core.Key{ - Name: s.Measure.N(), - }.Float64(s.Value)) - - buf.WriteString(" {") - i := 0 - s.Tags.Foreach(func(kv core.KeyValue) bool { - if i != 0 { - buf.WriteString(",") - } - i++ - buf.WriteString(kv.Key.Name) - buf.WriteString("=") - buf.WriteString(kv.Value.Emit()) - return true - }) - buf.WriteString("}") + + case exporter.SINGLE_METRIC: + formatMetricUpdate(buf, data.Measurement) + formatMetricLabels(buf, data.Attributes) + + case exporter.BATCH_METRIC: + buf.WriteString("BATCH") + formatMetricLabels(buf, data.Attributes) + for _, m := range data.Measurements { + formatMetricUpdate(buf, m) + buf.WriteString(" ") } case exporter.SET_STATUS: @@ -142,6 +134,30 @@ func AppendEvent(buf *strings.Builder, data reader.Event) { buf.WriteString(" ]\n") } +func formatMetricUpdate(buf *strings.Builder, m metric.Measurement) { + buf.WriteString(m.Instrument.Kind.String()) + buf.WriteString(" ") + buf.WriteString(m.Instrument.Name) + buf.WriteString("=") + buf.WriteString(fmt.Sprint(m.Value)) +} + +func formatMetricLabels(buf *strings.Builder, l tag.Map) { + buf.WriteString(" {") + i := 0 + l.Foreach(func(kv core.KeyValue) bool { + if i != 0 { + buf.WriteString(",") + } + i++ + buf.WriteString(kv.Key.Name) + buf.WriteString("=") + buf.WriteString(kv.Value.Emit()) + return true + }) + buf.WriteString("}") +} + func EventToString(data reader.Event) string { var buf strings.Builder AppendEvent(&buf, data) diff --git a/experimental/streaming/exporter/reader/reader.go b/experimental/streaming/exporter/reader/reader.go index b8d4de5ab21..4fd435cd9b0 100644 --- a/experimental/streaming/exporter/reader/reader.go +++ b/experimental/streaming/exporter/reader/reader.go @@ -22,8 +22,9 @@ import ( "google.golang.org/grpc/codes" "go.opentelemetry.io/api/core" - "go.opentelemetry.io/api/stats" + "go.opentelemetry.io/api/metric" "go.opentelemetry.io/api/tag" + "go.opentelemetry.io/api/trace" "go.opentelemetry.io/experimental/streaming/exporter" ) @@ -32,13 +33,14 @@ type Reader interface { } type Event struct { - Type exporter.EventType - Time time.Time - Sequence exporter.EventID - SpanContext core.SpanContext - Tags tag.Map - Attributes tag.Map - Stats []Measurement + Type exporter.EventType + Time time.Time + Sequence exporter.EventID + SpanContext core.SpanContext + Tags tag.Map // context tags + Attributes tag.Map // span attributes, metric labels + Measurement metric.Measurement + Measurements []metric.Measurement Parent core.SpanContext ParentAttributes tag.Map @@ -49,23 +51,11 @@ type Event struct { Status codes.Code } -type Measurement struct { - Measure stats.Measure - Value float64 - Tags tag.Map -} - type readerObserver struct { readers []Reader // core.EventID -> *readerSpan or *readerScope scopes sync.Map - - // core.EventID -> *readerMeasure - measures sync.Map - - // core.EventID -> *readerMetric - metrics sync.Map } type readerSpan struct { @@ -78,14 +68,6 @@ type readerSpan struct { *readerScope } -type readerMeasure struct { - name string -} - -type readerMetric struct { - *readerMeasure -} - type readerScope struct { span *readerSpan parent exporter.EventID @@ -222,24 +204,6 @@ func (ro *readerObserver) orderedObserve(event exporter.Event) { read.Tags = span.startTags } - case exporter.NEW_MEASURE: - measure := &readerMeasure{ - name: event.String, - } - ro.measures.Store(event.Sequence, measure) - return - - case exporter.NEW_METRIC: - measureI, has := ro.measures.Load(event.Scope.EventID) - if !has { - panic("metric measure not found") - } - metric := &readerMetric{ - readerMeasure: measureI.(*readerMeasure), - } - ro.metrics.Store(event.Sequence, metric) - return - case exporter.ADD_EVENT: read.Type = exporter.ADD_EVENT read.Message = event.String @@ -252,18 +216,40 @@ func (ro *readerObserver) orderedObserve(event exporter.Event) { read.SpanContext = span.spanContext } - case exporter.RECORD_STATS: - read.Type = exporter.RECORD_STATS + case exporter.SINGLE_METRIC: + read.Type = exporter.SINGLE_METRIC - _, span := ro.readScope(event.Scope) - if span != nil { - read.SpanContext = span.spanContext + if event.Context != nil { + span := trace.CurrentSpan(event.Context) + if span != nil { + read.SpanContext = span.SpanContext() + } } - for _, es := range event.Stats { - ro.addMeasurement(&read, es) + attrs, _ := ro.readScope(event.Scope) + read.Attributes = attrs + read.Measurement = metric.Measurement{ + Instrument: event.Measurement.Instrument, + Value: event.Measurement.Value, } - if event.Stat.Measure != nil { - ro.addMeasurement(&read, event.Stat) + + case exporter.BATCH_METRIC: + read.Type = event.Type + + if event.Context != nil { + span := trace.CurrentSpan(event.Context) + if span != nil { + read.SpanContext = span.SpanContext() + } + } + + attrs, _ := ro.readScope(event.Scope) + read.Attributes = attrs + + for _, m := range event.Measurements { + read.Measurements = append(read.Measurements, metric.Measurement{ + Instrument: m.Instrument, + Value: m.Value, + }) } case exporter.SET_STATUS: @@ -292,20 +278,6 @@ func (ro *readerObserver) orderedObserve(event exporter.Event) { } } -func (ro *readerObserver) addMeasurement(e *Event, m stats.Measurement) { - attrs, _ := ro.readMeasureScope(m.Measure) - e.Stats = append(e.Stats, Measurement{ - Measure: m.Measure, - Value: m.Value, - Tags: attrs, - }) -} - -func (ro *readerObserver) readMeasureScope(m stats.Measure) (tag.Map, *readerSpan) { - // TODO - return tag.NewEmptyMap(), nil -} - func (ro *readerObserver) readScope(id exporter.ScopeID) (tag.Map, *readerSpan) { if id.EventID == 0 { return tag.NewEmptyMap(), nil diff --git a/experimental/streaming/exporter/spandata/spandata.go b/experimental/streaming/exporter/spandata/spandata.go index 407b86b5a55..54e7f77f9bc 100644 --- a/experimental/streaming/exporter/spandata/spandata.go +++ b/experimental/streaming/exporter/spandata/spandata.go @@ -25,7 +25,8 @@ type Reader interface { } type Span struct { - Events []reader.Event + Events []reader.Event + Aggregates map[string]float64 } type spanReader struct { @@ -56,6 +57,12 @@ func (s *spanReader) Read(data reader.Event) { } } + switch data.Type { + case exporter.SINGLE_METRIC: + s.updateMetric(data) + return + } + span.Events = append(span.Events, data) if data.Type == exporter.END_SPAN { @@ -65,3 +72,7 @@ func (s *spanReader) Read(data reader.Event) { delete(s.spans, data.SpanContext) } } + +func (s *spanReader) updateMetric(data reader.Event) { + // TODO aggregate +} diff --git a/experimental/streaming/sdk/metric.go b/experimental/streaming/sdk/metric.go new file mode 100644 index 00000000000..d28eae69140 --- /dev/null +++ b/experimental/streaming/sdk/metric.go @@ -0,0 +1,96 @@ +// 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 sdk + +import ( + "context" + + "go.opentelemetry.io/api/core" + "go.opentelemetry.io/api/metric" + "go.opentelemetry.io/experimental/streaming/exporter" +) + +type metricHandle struct { + instrument metric.Instrument + labels metricLabels +} + +type metricLabels struct { + sdk *sdk + scope exporter.ScopeID +} + +func (h *metricHandle) Record(ctx context.Context, value float64) { + h.labels.sdk.exporter.Record(exporter.Event{ + Type: exporter.SINGLE_METRIC, + Context: ctx, + Scope: h.labels.scope, + Measurement: exporter.Measurement{ + Instrument: h.instrument, + Value: value, + }, + }) +} + +func (m metricLabels) Meter() metric.Meter { + return m.sdk +} + +func (s *sdk) DefineLabels(ctx context.Context, labels ...core.KeyValue) metric.LabelSet { + return metricLabels{ + sdk: s, + scope: s.exporter.NewScope(exporter.ScopeID{}, labels...), + } +} + +func (s *sdk) RecorderFor(ctx context.Context, labels metric.LabelSet, inst metric.Instrument) metric.Recorder { + mlabels, _ := labels.(metricLabels) + + return &metricHandle{ + instrument: inst, + labels: mlabels, + } +} + +func (s *sdk) RecordSingle(ctx context.Context, labels metric.LabelSet, input metric.Measurement) { + mlabels, _ := labels.(metricLabels) + s.exporter.Record(exporter.Event{ + Type: exporter.SINGLE_METRIC, + Context: ctx, + Scope: mlabels.scope, + Measurement: exporter.Measurement{ + Instrument: input.Instrument, + Value: input.Value, + }}) +} + +func (s *sdk) RecordBatch(ctx context.Context, labels metric.LabelSet, ms ...metric.Measurement) { + oms := make([]exporter.Measurement, len(ms)) + mlabels, _ := labels.(metricLabels) + + for i, input := range ms { + oms[i] = exporter.Measurement{ + Instrument: input.Instrument, + Value: input.Value, + } + } + + s.exporter.Record(exporter.Event{ + Type: exporter.BATCH_METRIC, + Context: ctx, + Scope: mlabels.scope, + Measurements: oms, + }) +} diff --git a/experimental/streaming/sdk/package.go b/experimental/streaming/sdk/package.go new file mode 100644 index 00000000000..de519c904d2 --- /dev/null +++ b/experimental/streaming/sdk/package.go @@ -0,0 +1,19 @@ +package sdk + +import ( + "go.opentelemetry.io/api/metric" + "go.opentelemetry.io/api/trace" + "go.opentelemetry.io/experimental/streaming/exporter" +) + +type sdk struct { + exporter *exporter.Exporter + resources exporter.EventID +} + +type SDK interface { + trace.Tracer + metric.Meter +} + +var _ SDK = &sdk{} diff --git a/experimental/streaming/sdk/span.go b/experimental/streaming/sdk/span.go index 9c0c2f1a86f..e64cd277124 100644 --- a/experimental/streaming/sdk/span.go +++ b/experimental/streaming/sdk/span.go @@ -27,7 +27,7 @@ import ( ) type span struct { - tracer *tracer + sdk *sdk initial exporter.ScopeID } @@ -44,7 +44,7 @@ func (sp *span) IsRecordingEvents() bool { // SetStatus sets the status of the span. func (sp *span) SetStatus(status codes.Code) { - sp.tracer.exporter.Record(exporter.Event{ + sp.sdk.exporter.Record(exporter.Event{ Type: exporter.SET_STATUS, Scope: sp.ScopeID(), Status: status, @@ -56,7 +56,7 @@ func (sp *span) ScopeID() exporter.ScopeID { } func (sp *span) SetAttribute(attribute core.KeyValue) { - sp.tracer.exporter.Record(exporter.Event{ + sp.sdk.exporter.Record(exporter.Event{ Type: exporter.MODIFY_ATTR, Scope: sp.ScopeID(), Attribute: attribute, @@ -64,7 +64,7 @@ func (sp *span) SetAttribute(attribute core.KeyValue) { } func (sp *span) SetAttributes(attributes ...core.KeyValue) { - sp.tracer.exporter.Record(exporter.Event{ + sp.sdk.exporter.Record(exporter.Event{ Type: exporter.MODIFY_ATTR, Scope: sp.ScopeID(), Attributes: attributes, @@ -72,7 +72,7 @@ func (sp *span) SetAttributes(attributes ...core.KeyValue) { } func (sp *span) ModifyAttribute(mutator tag.Mutator) { - sp.tracer.exporter.Record(exporter.Event{ + sp.sdk.exporter.Record(exporter.Event{ Type: exporter.MODIFY_ATTR, Scope: sp.ScopeID(), Mutator: mutator, @@ -80,7 +80,7 @@ func (sp *span) ModifyAttribute(mutator tag.Mutator) { } func (sp *span) ModifyAttributes(mutators ...tag.Mutator) { - sp.tracer.exporter.Record(exporter.Event{ + sp.sdk.exporter.Record(exporter.Event{ Type: exporter.MODIFY_ATTR, Scope: sp.ScopeID(), Mutators: mutators, @@ -93,7 +93,7 @@ func (sp *span) End(options ...trace.EndOption) { for _, opt := range options { opt(&opts) } - sp.tracer.exporter.Record(exporter.Event{ + sp.sdk.exporter.Record(exporter.Event{ Time: opts.EndTime, Type: exporter.END_SPAN, Scope: sp.ScopeID(), @@ -105,7 +105,7 @@ func (sp *span) End(options ...trace.EndOption) { } func (sp *span) Tracer() trace.Tracer { - return sp.tracer + return sp.sdk } func (sp *span) AddEvent(ctx context.Context, msg string, attrs ...core.KeyValue) { @@ -117,7 +117,7 @@ func (sp *span) AddEventWithTimestamp(ctx context.Context, timestamp time.Time, } func (sp *span) addEventWithTime(ctx context.Context, timestamp time.Time, msg string, attrs ...core.KeyValue) { - sp.tracer.exporter.Record(exporter.Event{ + sp.sdk.exporter.Record(exporter.Event{ Time: timestamp, Type: exporter.ADD_EVENT, Scope: sp.ScopeID(), @@ -128,7 +128,7 @@ func (sp *span) addEventWithTime(ctx context.Context, timestamp time.Time, msg s } func (sp *span) SetName(name string) { - sp.tracer.exporter.Record(exporter.Event{ + sp.sdk.exporter.Record(exporter.Event{ Type: exporter.SET_NAME, String: name, }) diff --git a/experimental/streaming/sdk/trace.go b/experimental/streaming/sdk/trace.go index dc32f4fd463..789c643c99c 100644 --- a/experimental/streaming/sdk/trace.go +++ b/experimental/streaming/sdk/trace.go @@ -24,11 +24,6 @@ import ( "go.opentelemetry.io/experimental/streaming/exporter" ) -type tracer struct { - exporter *exporter.Exporter - resources exporter.EventID -} - var ( // TODO These should move somewhere in the api, right? ErrorKey = key.New("error") @@ -37,16 +32,16 @@ var ( MessageKey = key.New("message") ) -func New(observers ...exporter.Observer) trace.Tracer { - return &tracer{ +func New(observers ...exporter.Observer) SDK { + return &sdk{ exporter: exporter.NewExporter(observers...), } } -func (t *tracer) WithSpan(ctx context.Context, name string, body func(context.Context) error) error { - // TODO: use runtime/trace.WithRegion for execution tracer support +func (s *sdk) WithSpan(ctx context.Context, name string, body func(context.Context) error) error { + // TODO: use runtime/trace.WithRegion for execution sdk support // TODO: use runtime/pprof.Do for profile tags support - ctx, span := t.Start(ctx, name) + ctx, span := s.Start(ctx, name) defer span.End() if err := body(ctx); err != nil { @@ -57,7 +52,7 @@ func (t *tracer) WithSpan(ctx context.Context, name string, body func(context.Co return nil } -func (t *tracer) Start(ctx context.Context, name string, opts ...trace.SpanOption) (context.Context, trace.Span) { +func (s *sdk) Start(ctx context.Context, name string, opts ...trace.SpanOption) (context.Context, trace.Span) { var child core.SpanContext child.SpanID = rand.Uint64() @@ -87,17 +82,17 @@ func (t *tracer) Start(ctx context.Context, name string, opts ...trace.SpanOptio childScope := exporter.ScopeID{ SpanContext: child, - EventID: t.resources, + EventID: s.resources, } span := &span{ - tracer: t, + sdk: s, initial: exporter.ScopeID{ SpanContext: child, - EventID: t.exporter.Record(exporter.Event{ + EventID: s.exporter.Record(exporter.Event{ Time: o.StartTime, Type: exporter.START_SPAN, - Scope: t.exporter.NewScope(childScope, o.Attributes...), + Scope: s.exporter.NewScope(childScope, o.Attributes...), Context: ctx, Parent: parentScope, String: name, From 9bd13f8041b75444c966dd140fb35e52815a214c Mon Sep 17 00:00:00 2001 From: Krzesimir Nowak Date: Mon, 23 Sep 2019 17:15:50 +0200 Subject: [PATCH 02/35] rename cumulative to counter --- api/metric/api.go | 12 +++++----- api/metric/{cumulative.go => counter.go} | 28 ++++++++++++------------ api/metric/kind_string.go | 6 ++--- 3 files changed, 23 insertions(+), 23 deletions(-) rename api/metric/{cumulative.go => counter.go} (55%) diff --git a/api/metric/api.go b/api/metric/api.go index 9321072b20c..58f961777d4 100644 --- a/api/metric/api.go +++ b/api/metric/api.go @@ -26,10 +26,10 @@ type Kind int //go:generate stringer -type=Kind const ( - Invalid Kind = iota - CumulativeKind // Supports Add() - GaugeKind // Supports Set() - MeasureKind // Supports Record() + Invalid Kind = iota + CounterKind // Supports Add() + GaugeKind // Supports Set() + MeasureKind // Supports Record() ) // Recorder is the implementation-level interface to Set/Add/Record individual metrics. @@ -87,7 +87,7 @@ type Instrument struct { // Kind is the metric kind of this instrument. Kind Kind - // Bidirectional implies this is an up-down Cumulative. + // Bidirectional implies this is an up-down Counter. Bidirectional bool // Unidirectional implies this is a non-descending Gauge. @@ -134,7 +134,7 @@ func WithUnit(unit unit.Unit) Option { } } -// WithBidirectional sets whether a cumulative is permitted to go up AND down. +// WithBidirectional sets whether a counter is permitted to go up AND down. func WithBidirectional(bi bool) Option { return func(inst *Instrument) { inst.Bidirectional = bi diff --git a/api/metric/cumulative.go b/api/metric/counter.go similarity index 55% rename from api/metric/cumulative.go rename to api/metric/counter.go index 8357bbebc7c..c1886b78a96 100644 --- a/api/metric/cumulative.go +++ b/api/metric/counter.go @@ -18,60 +18,60 @@ import ( "context" ) -type Float64Cumulative struct { +type Float64Counter struct { Instrument } -type Int64Cumulative struct { +type Int64Counter struct { Instrument } -type Float64CumulativeHandle struct { +type Float64CounterHandle struct { Handle } -type Int64CumulativeHandle struct { +type Int64CounterHandle struct { Handle } -func NewFloat64Cumulative(name string, mos ...Option) (c Float64Cumulative) { - registerInstrument(name, CumulativeKind, mos, &c.Instrument) +func NewFloat64Counter(name string, mos ...Option) (c Float64Counter) { + registerInstrument(name, CounterKind, mos, &c.Instrument) return } -func NewInt64Cumulative(name string, mos ...Option) (c Int64Cumulative) { - registerInstrument(name, CumulativeKind, mos, &c.Instrument) +func NewInt64Counter(name string, mos ...Option) (c Int64Counter) { + registerInstrument(name, CounterKind, mos, &c.Instrument) return } -func (c *Float64Cumulative) GetHandle(ctx context.Context, labels LabelSet) (h Float64CumulativeHandle) { +func (c *Float64Counter) GetHandle(ctx context.Context, labels LabelSet) (h Float64CounterHandle) { h.Recorder = labels.Meter().RecorderFor(ctx, labels, c.Instrument) return } -func (c *Int64Cumulative) GetHandle(ctx context.Context, labels LabelSet) (h Int64CumulativeHandle) { +func (c *Int64Counter) GetHandle(ctx context.Context, labels LabelSet) (h Int64CounterHandle) { h.Recorder = labels.Meter().RecorderFor(ctx, labels, c.Instrument) return } -func (g *Float64Cumulative) Add(ctx context.Context, value float64, labels LabelSet) { +func (g *Float64Counter) Add(ctx context.Context, value float64, labels LabelSet) { labels.Meter().RecordSingle(ctx, labels, Measurement{ Instrument: g.Instrument, Value: value, }) } -func (g *Int64Cumulative) Add(ctx context.Context, value int64, labels LabelSet) { +func (g *Int64Counter) Add(ctx context.Context, value int64, labels LabelSet) { labels.Meter().RecordSingle(ctx, labels, Measurement{ Instrument: g.Instrument, Value: float64(value), }) } -func (g *Float64CumulativeHandle) Add(ctx context.Context, value float64) { +func (g *Float64CounterHandle) Add(ctx context.Context, value float64) { g.Recorder.Record(ctx, value) } -func (g *Int64CumulativeHandle) Add(ctx context.Context, value int64) { +func (g *Int64CounterHandle) Add(ctx context.Context, value int64) { g.Recorder.Record(ctx, float64(value)) } diff --git a/api/metric/kind_string.go b/api/metric/kind_string.go index 78a067e64ec..b64e5dc675d 100644 --- a/api/metric/kind_string.go +++ b/api/metric/kind_string.go @@ -9,14 +9,14 @@ func _() { // Re-run the stringer command to generate them again. var x [1]struct{} _ = x[Invalid-0] - _ = x[CumulativeKind-1] + _ = x[CounterKind-1] _ = x[GaugeKind-2] _ = x[MeasureKind-3] } -const _Kind_name = "InvalidCumulativeKindGaugeKindMeasureKind" +const _Kind_name = "InvalidCounterKindGaugeKindMeasureKind" -var _Kind_index = [...]uint8{0, 7, 21, 30, 41} +var _Kind_index = [...]uint8{0, 7, 18, 27, 38} func (i Kind) String() string { if i < 0 || i >= Kind(len(_Kind_index)-1) { From 12ce9d3bd358824ac59253cbf56d3ff2ae3cadeb Mon Sep 17 00:00:00 2001 From: Krzesimir Nowak Date: Mon, 23 Sep 2019 17:17:09 +0200 Subject: [PATCH 03/35] rename bidirectional to nonmonotonic --- api/metric/api.go | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/api/metric/api.go b/api/metric/api.go index 58f961777d4..51095716e90 100644 --- a/api/metric/api.go +++ b/api/metric/api.go @@ -87,8 +87,8 @@ type Instrument struct { // Kind is the metric kind of this instrument. Kind Kind - // Bidirectional implies this is an up-down Counter. - Bidirectional bool + // NonMonotonic implies this is an up-down Counter. + NonMonotonic bool // Unidirectional implies this is a non-descending Gauge. Unidirectional bool @@ -134,10 +134,10 @@ func WithUnit(unit unit.Unit) Option { } } -// WithBidirectional sets whether a counter is permitted to go up AND down. -func WithBidirectional(bi bool) Option { +// WithNonMonotonic sets whether a counter is permitted to go up AND down. +func WithNonMonotonic(nm bool) Option { return func(inst *Instrument) { - inst.Bidirectional = bi + inst.NonMonotonic = nm } } From 074ff1ffe7481716cf3138698e87cfed0d1e641e Mon Sep 17 00:00:00 2001 From: Krzesimir Nowak Date: Mon, 23 Sep 2019 17:18:38 +0200 Subject: [PATCH 04/35] rename unidirectional to monotonic --- api/metric/api.go | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/api/metric/api.go b/api/metric/api.go index 51095716e90..dc64d8c24ff 100644 --- a/api/metric/api.go +++ b/api/metric/api.go @@ -90,8 +90,8 @@ type Instrument struct { // NonMonotonic implies this is an up-down Counter. NonMonotonic bool - // Unidirectional implies this is a non-descending Gauge. - Unidirectional bool + // Monotonic implies this is a non-descending Gauge. + Monotonic bool // NonNegative implies this is a non-negative Measure. NonNegative bool @@ -141,10 +141,10 @@ func WithNonMonotonic(nm bool) Option { } } -// WithUnidirectional sets whether a gauge is not permitted to go down. -func WithUnidirectional(uni bool) Option { +// WithMonotonic sets whether a gauge is not permitted to go down. +func WithMonotonic(m bool) Option { return func(inst *Instrument) { - inst.Unidirectional = uni + inst.Monotonic = m } } From 671b9fe15db234d1bdcb85ddfc8ad9fb5dd920ed Mon Sep 17 00:00:00 2001 From: Krzesimir Nowak Date: Mon, 23 Sep 2019 17:23:42 +0200 Subject: [PATCH 05/35] rename nonnegative to signed this changes the default semantics a bit - before the change measure could record negative values by default, now it can't. The specification draft currently specifies both NonNegative and Signed, but I think it's a mistake. --- api/metric/api.go | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/api/metric/api.go b/api/metric/api.go index dc64d8c24ff..53e94ce00d2 100644 --- a/api/metric/api.go +++ b/api/metric/api.go @@ -93,8 +93,9 @@ type Instrument struct { // Monotonic implies this is a non-descending Gauge. Monotonic bool - // NonNegative implies this is a non-negative Measure. - NonNegative bool + // Signed implies this is a Measure that supports positive and + // negative values. + Signed bool // Disabled implies this instrument is disabled by default. Disabled bool @@ -148,10 +149,10 @@ func WithMonotonic(m bool) Option { } } -// WithNonNegative sets whether a measure is not permitted to be negative. -func WithNonNegative(non bool) Option { +// WithSigned sets whether a measure is permitted to be negative. +func WithSigned(s bool) Option { return func(inst *Instrument) { - inst.NonNegative = non + inst.Signed = s } } From 6926a5f6e50120c452dd4b24f4cad8434b6fa497 Mon Sep 17 00:00:00 2001 From: Krzesimir Nowak Date: Mon, 23 Sep 2019 17:32:16 +0200 Subject: [PATCH 06/35] rename instrument to descriptor --- api/metric/api.go | 56 +++++++++---------- api/metric/common.go | 12 ++-- api/metric/counter.go | 16 +++--- api/metric/gauge.go | 16 +++--- api/metric/measure.go | 16 +++--- api/metric/noop_meter.go | 2 +- example/basic/main.go | 4 +- experimental/streaming/example/basic/main.go | 4 +- experimental/streaming/exporter/exporter.go | 2 +- .../exporter/reader/format/format.go | 4 +- .../streaming/exporter/reader/reader.go | 4 +- experimental/streaming/sdk/metric.go | 12 ++-- 12 files changed, 74 insertions(+), 74 deletions(-) diff --git a/api/metric/api.go b/api/metric/api.go index 53e94ce00d2..59c07e433e5 100644 --- a/api/metric/api.go +++ b/api/metric/api.go @@ -56,7 +56,7 @@ type Meter interface { DefineLabels(context.Context, ...core.KeyValue) LabelSet // RecorderFor returns a handle for observing single measurements. - RecorderFor(context.Context, LabelSet, Instrument) Recorder + RecorderFor(context.Context, LabelSet, Descriptor) Recorder // RecordSingle records a single measurement without computing a handle. RecordSingle(context.Context, LabelSet, Measurement) @@ -67,24 +67,24 @@ type Meter interface { RecordBatch(context.Context, LabelSet, ...Measurement) } -type InstrumentID uint64 +type DescriptorID uint64 -// Instrument represents a named metric with recommended local-aggregation keys. -type Instrument struct { - // Name is a required field describing this metric instrument, +// Descriptor represents a named metric with recommended local-aggregation keys. +type Descriptor struct { + // Name is a required field describing this metric descriptor, // should have length > 0. Name string // ID is uniquely assigned to support per-SDK registration. - ID InstrumentID + ID DescriptorID - // Description is an optional field describing this metric instrument. + // Description is an optional field describing this metric descriptor. Description string - // Unit is an optional field describing this metric instrument. + // Unit is an optional field describing this metric descriptor. Unit unit.Unit - // Kind is the metric kind of this instrument. + // Kind is the metric kind of this descriptor. Kind Kind // NonMonotonic implies this is an up-down Counter. @@ -97,7 +97,7 @@ type Instrument struct { // negative values. Signed bool - // Disabled implies this instrument is disabled by default. + // Disabled implies this descriptor is disabled by default. Disabled bool // Keys are required keys determined in the handles @@ -114,66 +114,66 @@ type Handle struct { // Measurement is used for reporting a batch of metric values. type Measurement struct { - Instrument Instrument + Descriptor Descriptor Value float64 } // Option supports specifying the various metric options. -type Option func(*Instrument) +type Option func(*Descriptor) // WithDescription applies provided description. func WithDescription(desc string) Option { - return func(inst *Instrument) { - inst.Description = desc + return func(d *Descriptor) { + d.Description = desc } } // WithUnit applies provided unit. func WithUnit(unit unit.Unit) Option { - return func(inst *Instrument) { - inst.Unit = unit + return func(d *Descriptor) { + d.Unit = unit } } // WithNonMonotonic sets whether a counter is permitted to go up AND down. func WithNonMonotonic(nm bool) Option { - return func(inst *Instrument) { - inst.NonMonotonic = nm + return func(d *Descriptor) { + d.NonMonotonic = nm } } // WithMonotonic sets whether a gauge is not permitted to go down. func WithMonotonic(m bool) Option { - return func(inst *Instrument) { - inst.Monotonic = m + return func(d *Descriptor) { + d.Monotonic = m } } // WithSigned sets whether a measure is permitted to be negative. func WithSigned(s bool) Option { - return func(inst *Instrument) { - inst.Signed = s + return func(d *Descriptor) { + d.Signed = s } } // WithDisabled sets whether a measure is disabled by default func WithDisabled(dis bool) Option { - return func(inst *Instrument) { - inst.Disabled = dis + return func(d *Descriptor) { + d.Disabled = dis } } // WithKeys applies required label keys. Multiple `WithKeys` // options accumulate. func WithKeys(keys ...core.Key) Option { - return func(m *Instrument) { + return func(m *Descriptor) { m.Keys = append(m.Keys, keys...) } } -// Defined returns true when the instrument has been registered. -func (inst Instrument) Defined() bool { - return len(inst.Name) != 0 +// Defined returns true when the descriptor has been registered. +func (d Descriptor) Defined() bool { + return len(d.Name) != 0 } // RecordSingle reports to the global Meter. diff --git a/api/metric/common.go b/api/metric/common.go index 99e4370daed..a648ac7912d 100644 --- a/api/metric/common.go +++ b/api/metric/common.go @@ -17,15 +17,15 @@ package metric import "sync/atomic" var ( - instrumentID uint64 + descriptorID uint64 ) -func registerInstrument(name string, kind Kind, opts []Option, inst *Instrument) { - inst.Name = name - inst.Kind = kind - inst.ID = InstrumentID(atomic.AddUint64(&instrumentID, 1)) +func registerDescriptor(name string, kind Kind, opts []Option, d *Descriptor) { + d.Name = name + d.Kind = kind + d.ID = DescriptorID(atomic.AddUint64(&descriptorID, 1)) for _, opt := range opts { - opt(inst) + opt(d) } } diff --git a/api/metric/counter.go b/api/metric/counter.go index c1886b78a96..472ad53e3ce 100644 --- a/api/metric/counter.go +++ b/api/metric/counter.go @@ -19,11 +19,11 @@ import ( ) type Float64Counter struct { - Instrument + Descriptor } type Int64Counter struct { - Instrument + Descriptor } type Float64CounterHandle struct { @@ -35,35 +35,35 @@ type Int64CounterHandle struct { } func NewFloat64Counter(name string, mos ...Option) (c Float64Counter) { - registerInstrument(name, CounterKind, mos, &c.Instrument) + registerDescriptor(name, CounterKind, mos, &c.Descriptor) return } func NewInt64Counter(name string, mos ...Option) (c Int64Counter) { - registerInstrument(name, CounterKind, mos, &c.Instrument) + registerDescriptor(name, CounterKind, mos, &c.Descriptor) return } func (c *Float64Counter) GetHandle(ctx context.Context, labels LabelSet) (h Float64CounterHandle) { - h.Recorder = labels.Meter().RecorderFor(ctx, labels, c.Instrument) + h.Recorder = labels.Meter().RecorderFor(ctx, labels, c.Descriptor) return } func (c *Int64Counter) GetHandle(ctx context.Context, labels LabelSet) (h Int64CounterHandle) { - h.Recorder = labels.Meter().RecorderFor(ctx, labels, c.Instrument) + h.Recorder = labels.Meter().RecorderFor(ctx, labels, c.Descriptor) return } func (g *Float64Counter) Add(ctx context.Context, value float64, labels LabelSet) { labels.Meter().RecordSingle(ctx, labels, Measurement{ - Instrument: g.Instrument, + Descriptor: g.Descriptor, Value: value, }) } func (g *Int64Counter) Add(ctx context.Context, value int64, labels LabelSet) { labels.Meter().RecordSingle(ctx, labels, Measurement{ - Instrument: g.Instrument, + Descriptor: g.Descriptor, Value: float64(value), }) } diff --git a/api/metric/gauge.go b/api/metric/gauge.go index 29f5385f6de..3851e0978ac 100644 --- a/api/metric/gauge.go +++ b/api/metric/gauge.go @@ -19,11 +19,11 @@ import ( ) type Float64Gauge struct { - Instrument + Descriptor } type Int64Gauge struct { - Instrument + Descriptor } type Float64GaugeHandle struct { @@ -35,35 +35,35 @@ type Int64GaugeHandle struct { } func NewFloat64Gauge(name string, mos ...Option) (g Float64Gauge) { - registerInstrument(name, GaugeKind, mos, &g.Instrument) + registerDescriptor(name, GaugeKind, mos, &g.Descriptor) return } func NewInt64Gauge(name string, mos ...Option) (g Int64Gauge) { - registerInstrument(name, GaugeKind, mos, &g.Instrument) + registerDescriptor(name, GaugeKind, mos, &g.Descriptor) return } func (g *Float64Gauge) GetHandle(ctx context.Context, labels LabelSet) (h Float64GaugeHandle) { - h.Recorder = labels.Meter().RecorderFor(ctx, labels, g.Instrument) + h.Recorder = labels.Meter().RecorderFor(ctx, labels, g.Descriptor) return } func (g *Int64Gauge) GetHandle(ctx context.Context, labels LabelSet) (h Int64GaugeHandle) { - h.Recorder = labels.Meter().RecorderFor(ctx, labels, g.Instrument) + h.Recorder = labels.Meter().RecorderFor(ctx, labels, g.Descriptor) return } func (g *Float64Gauge) Set(ctx context.Context, value float64, labels LabelSet) { labels.Meter().RecordSingle(ctx, labels, Measurement{ - Instrument: g.Instrument, + Descriptor: g.Descriptor, Value: value, }) } func (g *Int64Gauge) Set(ctx context.Context, value int64, labels LabelSet) { labels.Meter().RecordSingle(ctx, labels, Measurement{ - Instrument: g.Instrument, + Descriptor: g.Descriptor, Value: float64(value), }) } diff --git a/api/metric/measure.go b/api/metric/measure.go index 68f911c8b08..14d5b439526 100644 --- a/api/metric/measure.go +++ b/api/metric/measure.go @@ -19,11 +19,11 @@ import ( ) type Float64Measure struct { - Instrument + Descriptor } type Int64Measure struct { - Instrument + Descriptor } type Float64MeasureHandle struct { @@ -35,35 +35,35 @@ type Int64MeasureHandle struct { } func NewFloat64Measure(name string, mos ...Option) (m Float64Measure) { - registerInstrument(name, MeasureKind, mos, &m.Instrument) + registerDescriptor(name, MeasureKind, mos, &m.Descriptor) return } func NewInt64Measure(name string, mos ...Option) (m Int64Measure) { - registerInstrument(name, MeasureKind, mos, &m.Instrument) + registerDescriptor(name, MeasureKind, mos, &m.Descriptor) return } func (m *Float64Measure) GetHandle(ctx context.Context, labels LabelSet) (h Float64MeasureHandle) { - h.Recorder = labels.Meter().RecorderFor(ctx, labels, m.Instrument) + h.Recorder = labels.Meter().RecorderFor(ctx, labels, m.Descriptor) return } func (m *Int64Measure) GetHandle(ctx context.Context, labels LabelSet) (h Int64MeasureHandle) { - h.Recorder = labels.Meter().RecorderFor(ctx, labels, m.Instrument) + h.Recorder = labels.Meter().RecorderFor(ctx, labels, m.Descriptor) return } func (g *Float64Measure) Record(ctx context.Context, value float64, labels LabelSet) { labels.Meter().RecordSingle(ctx, labels, Measurement{ - Instrument: g.Instrument, + Descriptor: g.Descriptor, Value: value, }) } func (g *Int64Measure) Record(ctx context.Context, value int64, labels LabelSet) { labels.Meter().RecordSingle(ctx, labels, Measurement{ - Instrument: g.Instrument, + Descriptor: g.Descriptor, Value: float64(value), }) } diff --git a/api/metric/noop_meter.go b/api/metric/noop_meter.go index 4c4aca6481b..46fd9feb5f6 100644 --- a/api/metric/noop_meter.go +++ b/api/metric/noop_meter.go @@ -31,6 +31,6 @@ func (noopMeter) RecordSingle(context.Context, LabelSet, Measurement) { func (noopMeter) RecordBatch(context.Context, LabelSet, ...Measurement) { } -func (noopMeter) RecorderFor(context.Context, LabelSet, Instrument) Recorder { +func (noopMeter) RecorderFor(context.Context, LabelSet, Descriptor) Recorder { return noopRecorder{} } diff --git a/example/basic/main.go b/example/basic/main.go index f71a128b67e..f307b64f18f 100644 --- a/example/basic/main.go +++ b/example/basic/main.go @@ -70,12 +70,12 @@ func main() { commonLabels, metric.Measurement{ - Instrument: oneMetric.Instrument, + Descriptor: oneMetric.Descriptor, Value: 1.0, }, metric.Measurement{ - Instrument: measureTwo.Instrument, + Descriptor: measureTwo.Descriptor, Value: 2.0, }, ) diff --git a/experimental/streaming/example/basic/main.go b/experimental/streaming/example/basic/main.go index 4425244f488..b8aa2a0ba1b 100644 --- a/experimental/streaming/example/basic/main.go +++ b/experimental/streaming/example/basic/main.go @@ -73,12 +73,12 @@ func main() { commonLabels, metric.Measurement{ - Instrument: oneMetric.Instrument, + Descriptor: oneMetric.Descriptor, Value: 1.0, }, metric.Measurement{ - Instrument: measureTwo.Instrument, + Descriptor: measureTwo.Descriptor, Value: 2.0, }, ) diff --git a/experimental/streaming/exporter/exporter.go b/experimental/streaming/exporter/exporter.go index 568f9b5269a..56d5e4501f9 100644 --- a/experimental/streaming/exporter/exporter.go +++ b/experimental/streaming/exporter/exporter.go @@ -22,7 +22,7 @@ type ScopeID struct { } type Measurement struct { - Instrument metric.Instrument + Descriptor metric.Descriptor Value float64 } diff --git a/experimental/streaming/exporter/reader/format/format.go b/experimental/streaming/exporter/reader/format/format.go index 8d75a832fd2..f257036fffc 100644 --- a/experimental/streaming/exporter/reader/format/format.go +++ b/experimental/streaming/exporter/reader/format/format.go @@ -135,9 +135,9 @@ func AppendEvent(buf *strings.Builder, data reader.Event) { } func formatMetricUpdate(buf *strings.Builder, m metric.Measurement) { - buf.WriteString(m.Instrument.Kind.String()) + buf.WriteString(m.Descriptor.Kind.String()) buf.WriteString(" ") - buf.WriteString(m.Instrument.Name) + buf.WriteString(m.Descriptor.Name) buf.WriteString("=") buf.WriteString(fmt.Sprint(m.Value)) } diff --git a/experimental/streaming/exporter/reader/reader.go b/experimental/streaming/exporter/reader/reader.go index 4fd435cd9b0..ebb6a1f52ab 100644 --- a/experimental/streaming/exporter/reader/reader.go +++ b/experimental/streaming/exporter/reader/reader.go @@ -228,7 +228,7 @@ func (ro *readerObserver) orderedObserve(event exporter.Event) { attrs, _ := ro.readScope(event.Scope) read.Attributes = attrs read.Measurement = metric.Measurement{ - Instrument: event.Measurement.Instrument, + Descriptor: event.Measurement.Descriptor, Value: event.Measurement.Value, } @@ -247,7 +247,7 @@ func (ro *readerObserver) orderedObserve(event exporter.Event) { for _, m := range event.Measurements { read.Measurements = append(read.Measurements, metric.Measurement{ - Instrument: m.Instrument, + Descriptor: m.Descriptor, Value: m.Value, }) } diff --git a/experimental/streaming/sdk/metric.go b/experimental/streaming/sdk/metric.go index d28eae69140..4ad0fa1f0dc 100644 --- a/experimental/streaming/sdk/metric.go +++ b/experimental/streaming/sdk/metric.go @@ -23,7 +23,7 @@ import ( ) type metricHandle struct { - instrument metric.Instrument + descriptor metric.Descriptor labels metricLabels } @@ -38,7 +38,7 @@ func (h *metricHandle) Record(ctx context.Context, value float64) { Context: ctx, Scope: h.labels.scope, Measurement: exporter.Measurement{ - Instrument: h.instrument, + Descriptor: h.descriptor, Value: value, }, }) @@ -55,11 +55,11 @@ func (s *sdk) DefineLabels(ctx context.Context, labels ...core.KeyValue) metric. } } -func (s *sdk) RecorderFor(ctx context.Context, labels metric.LabelSet, inst metric.Instrument) metric.Recorder { +func (s *sdk) RecorderFor(ctx context.Context, labels metric.LabelSet, descriptor metric.Descriptor) metric.Recorder { mlabels, _ := labels.(metricLabels) return &metricHandle{ - instrument: inst, + descriptor: descriptor, labels: mlabels, } } @@ -71,7 +71,7 @@ func (s *sdk) RecordSingle(ctx context.Context, labels metric.LabelSet, input me Context: ctx, Scope: mlabels.scope, Measurement: exporter.Measurement{ - Instrument: input.Instrument, + Descriptor: input.Descriptor, Value: input.Value, }}) } @@ -82,7 +82,7 @@ func (s *sdk) RecordBatch(ctx context.Context, labels metric.LabelSet, ms ...met for i, input := range ms { oms[i] = exporter.Measurement{ - Instrument: input.Instrument, + Descriptor: input.Descriptor, Value: input.Value, } } From 58885b5e2518e6ffe879ffadf9323e077c1942f2 Mon Sep 17 00:00:00 2001 From: Krzesimir Nowak Date: Tue, 1 Oct 2019 13:35:16 +0200 Subject: [PATCH 07/35] change some interfaces to match the spec --- api/metric/api.go | 29 ++++------------ api/metric/counter.go | 36 ++++++++++++-------- api/metric/gauge.go | 32 ++++++++++------- api/metric/measure.go | 36 ++++++++++++-------- api/metric/noop_meter.go | 14 ++++---- example/basic/main.go | 11 ++---- experimental/streaming/example/basic/main.go | 11 ++---- experimental/streaming/exporter/exporter.go | 2 +- experimental/streaming/sdk/metric.go | 25 +++++++------- 9 files changed, 94 insertions(+), 102 deletions(-) diff --git a/api/metric/api.go b/api/metric/api.go index 59c07e433e5..d50e7f7baf3 100644 --- a/api/metric/api.go +++ b/api/metric/api.go @@ -33,9 +33,9 @@ const ( ) // Recorder is the implementation-level interface to Set/Add/Record individual metrics. -type Recorder interface { - // Record allows the SDK to observe a single metric event - Record(ctx context.Context, value float64) +type Handle interface { + // RecordOne allows the SDK to observe a single metric event + RecordOne(ctx context.Context, value float64) } // LabelSet represents a []core.KeyValue for use as pre-defined labels @@ -55,15 +55,10 @@ type Meter interface { // cannot be read by the application. DefineLabels(context.Context, ...core.KeyValue) LabelSet - // RecorderFor returns a handle for observing single measurements. - RecorderFor(context.Context, LabelSet, Descriptor) Recorder + NewHandle(context.Context, Descriptor, LabelSet) Handle + DeleteHandle(Handle) - // RecordSingle records a single measurement without computing a handle. - RecordSingle(context.Context, LabelSet, Measurement) - - // RecordBatch atomically records a batch of measurements. An - // implementation may elect to call `RecordSingle` on each - // measurement, or it could choose a more-optimized approach. + // RecordBatch atomically records a batch of measurements.. RecordBatch(context.Context, LabelSet, ...Measurement) } @@ -105,13 +100,6 @@ type Descriptor struct { Keys []core.Key } -// Handle contains a Recorder to support the implementation-defined -// behavior of reporting a single metric with pre-determined label -// values. -type Handle struct { - Recorder -} - // Measurement is used for reporting a batch of metric values. type Measurement struct { Descriptor Descriptor @@ -176,11 +164,6 @@ func (d Descriptor) Defined() bool { return len(d.Name) != 0 } -// RecordSingle reports to the global Meter. -func RecordSingle(ctx context.Context, labels LabelSet, measurement Measurement) { - GlobalMeter().RecordSingle(ctx, labels, measurement) -} - // 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/counter.go b/api/metric/counter.go index 472ad53e3ce..7269353d7a7 100644 --- a/api/metric/counter.go +++ b/api/metric/counter.go @@ -45,33 +45,41 @@ func NewInt64Counter(name string, mos ...Option) (c Int64Counter) { } func (c *Float64Counter) GetHandle(ctx context.Context, labels LabelSet) (h Float64CounterHandle) { - h.Recorder = labels.Meter().RecorderFor(ctx, labels, c.Descriptor) + h.Handle = labels.Meter().NewHandle(ctx, c.Descriptor, labels) return } func (c *Int64Counter) GetHandle(ctx context.Context, labels LabelSet) (h Int64CounterHandle) { - h.Recorder = labels.Meter().RecorderFor(ctx, labels, c.Descriptor) + h.Handle = labels.Meter().NewHandle(ctx, c.Descriptor, labels) return } -func (g *Float64Counter) Add(ctx context.Context, value float64, labels LabelSet) { - labels.Meter().RecordSingle(ctx, labels, Measurement{ - Descriptor: g.Descriptor, +func (c *Float64Counter) Measurement(value float64) Measurement { + return Measurement{ + Descriptor: c.Descriptor, Value: value, - }) + } } -func (g *Int64Counter) Add(ctx context.Context, value int64, labels LabelSet) { - labels.Meter().RecordSingle(ctx, labels, Measurement{ - Descriptor: g.Descriptor, +func (c *Int64Counter) Measurement(value int64) Measurement { + return Measurement{ + Descriptor: c.Descriptor, Value: float64(value), - }) + } } -func (g *Float64CounterHandle) Add(ctx context.Context, value float64) { - g.Recorder.Record(ctx, value) +func (c *Float64Counter) Add(ctx context.Context, value float64, labels LabelSet) { + labels.Meter().RecordBatch(ctx, labels, c.Measurement(value)) } -func (g *Int64CounterHandle) Add(ctx context.Context, value int64) { - g.Recorder.Record(ctx, float64(value)) +func (c *Int64Counter) Add(ctx context.Context, value int64, labels LabelSet) { + labels.Meter().RecordBatch(ctx, labels, c.Measurement(value)) +} + +func (h *Float64CounterHandle) Add(ctx context.Context, value float64) { + h.RecordOne(ctx, value) +} + +func (h *Int64CounterHandle) Add(ctx context.Context, value int64) { + h.RecordOne(ctx, float64(value)) } diff --git a/api/metric/gauge.go b/api/metric/gauge.go index 3851e0978ac..6743cb3e0c9 100644 --- a/api/metric/gauge.go +++ b/api/metric/gauge.go @@ -45,33 +45,41 @@ func NewInt64Gauge(name string, mos ...Option) (g Int64Gauge) { } func (g *Float64Gauge) GetHandle(ctx context.Context, labels LabelSet) (h Float64GaugeHandle) { - h.Recorder = labels.Meter().RecorderFor(ctx, labels, g.Descriptor) + h.Handle = labels.Meter().NewHandle(ctx, g.Descriptor, labels) return } func (g *Int64Gauge) GetHandle(ctx context.Context, labels LabelSet) (h Int64GaugeHandle) { - h.Recorder = labels.Meter().RecorderFor(ctx, labels, g.Descriptor) + h.Handle = labels.Meter().NewHandle(ctx, g.Descriptor, labels) return } -func (g *Float64Gauge) Set(ctx context.Context, value float64, labels LabelSet) { - labels.Meter().RecordSingle(ctx, labels, Measurement{ +func (g *Float64Gauge) Measurement(value float64) Measurement { + return Measurement{ Descriptor: g.Descriptor, Value: value, - }) + } } -func (g *Int64Gauge) Set(ctx context.Context, value int64, labels LabelSet) { - labels.Meter().RecordSingle(ctx, labels, Measurement{ +func (g *Int64Gauge) Measurement(value int64) Measurement { + return Measurement{ Descriptor: g.Descriptor, Value: float64(value), - }) + } +} + +func (g *Float64Gauge) Set(ctx context.Context, value float64, labels LabelSet) { + labels.Meter().RecordBatch(ctx, labels, g.Measurement(value)) +} + +func (g *Int64Gauge) Set(ctx context.Context, value int64, labels LabelSet) { + labels.Meter().RecordBatch(ctx, labels, g.Measurement(value)) } -func (g *Float64GaugeHandle) Set(ctx context.Context, value float64) { - g.Recorder.Record(ctx, value) +func (h *Float64GaugeHandle) Set(ctx context.Context, value float64) { + h.RecordOne(ctx, value) } -func (g *Int64GaugeHandle) Set(ctx context.Context, value int64) { - g.Recorder.Record(ctx, float64(value)) +func (h *Int64GaugeHandle) Set(ctx context.Context, value int64) { + h.RecordOne(ctx, float64(value)) } diff --git a/api/metric/measure.go b/api/metric/measure.go index 14d5b439526..a562ff28856 100644 --- a/api/metric/measure.go +++ b/api/metric/measure.go @@ -45,33 +45,41 @@ func NewInt64Measure(name string, mos ...Option) (m Int64Measure) { } func (m *Float64Measure) GetHandle(ctx context.Context, labels LabelSet) (h Float64MeasureHandle) { - h.Recorder = labels.Meter().RecorderFor(ctx, labels, m.Descriptor) + h.Handle = labels.Meter().NewHandle(ctx, m.Descriptor, labels) return } func (m *Int64Measure) GetHandle(ctx context.Context, labels LabelSet) (h Int64MeasureHandle) { - h.Recorder = labels.Meter().RecorderFor(ctx, labels, m.Descriptor) + h.Handle = labels.Meter().NewHandle(ctx, m.Descriptor, labels) return } -func (g *Float64Measure) Record(ctx context.Context, value float64, labels LabelSet) { - labels.Meter().RecordSingle(ctx, labels, Measurement{ - Descriptor: g.Descriptor, +func (m *Float64Measure) Measurement(value float64) Measurement { + return Measurement{ + Descriptor: m.Descriptor, Value: value, - }) + } } -func (g *Int64Measure) Record(ctx context.Context, value int64, labels LabelSet) { - labels.Meter().RecordSingle(ctx, labels, Measurement{ - Descriptor: g.Descriptor, +func (m *Int64Measure) Measurement(value int64) Measurement { + return Measurement{ + Descriptor: m.Descriptor, Value: float64(value), - }) + } } -func (g *Float64MeasureHandle) Record(ctx context.Context, value float64) { - g.Recorder.Record(ctx, value) +func (m *Float64Measure) Record(ctx context.Context, value float64, labels LabelSet) { + labels.Meter().RecordBatch(ctx, labels, m.Measurement(value)) } -func (g *Int64MeasureHandle) Record(ctx context.Context, value int64) { - g.Recorder.Record(ctx, float64(value)) +func (m *Int64Measure) Record(ctx context.Context, value int64, labels LabelSet) { + labels.Meter().RecordBatch(ctx, labels, m.Measurement(value)) +} + +func (h *Float64MeasureHandle) Record(ctx context.Context, value float64) { + h.RecordOne(ctx, value) +} + +func (h *Int64MeasureHandle) Record(ctx context.Context, value int64) { + h.RecordOne(ctx, float64(value)) } diff --git a/api/metric/noop_meter.go b/api/metric/noop_meter.go index 46fd9feb5f6..6ffcade3f6a 100644 --- a/api/metric/noop_meter.go +++ b/api/metric/noop_meter.go @@ -7,14 +7,14 @@ import ( ) type noopMeter struct{} -type noopRecorder struct{} +type noopHandle struct{} type noopLabelSet struct{} var _ Meter = noopMeter{} -var _ Recorder = noopRecorder{} +var _ Handle = noopHandle{} var _ LabelSet = noopLabelSet{} -func (noopRecorder) Record(ctx context.Context, value float64) { +func (noopHandle) RecordOne(ctx context.Context, value float64) { } func (noopLabelSet) Meter() Meter { @@ -25,12 +25,12 @@ func (noopMeter) DefineLabels(ctx context.Context, labels ...core.KeyValue) Labe return noopLabelSet{} } -func (noopMeter) RecordSingle(context.Context, LabelSet, Measurement) { +func (noopMeter) NewHandle(context.Context, Descriptor, LabelSet) Handle { + return noopHandle{} } -func (noopMeter) RecordBatch(context.Context, LabelSet, ...Measurement) { +func (noopMeter) DeleteHandle(Handle) { } -func (noopMeter) RecorderFor(context.Context, LabelSet, Descriptor) Recorder { - return noopRecorder{} +func (noopMeter) RecordBatch(context.Context, LabelSet, ...Measurement) { } diff --git a/example/basic/main.go b/example/basic/main.go index f307b64f18f..214b29576df 100644 --- a/example/basic/main.go +++ b/example/basic/main.go @@ -69,15 +69,8 @@ func main() { tag.Insert(anotherKey.String("xyz"))), commonLabels, - metric.Measurement{ - Descriptor: oneMetric.Descriptor, - Value: 1.0, - }, - - metric.Measurement{ - Descriptor: measureTwo.Descriptor, - Value: 2.0, - }, + oneMetric.Measurement(1.0), + measureTwo.Measurement(2.0), ) return tracer.WithSpan( diff --git a/experimental/streaming/example/basic/main.go b/experimental/streaming/example/basic/main.go index b8aa2a0ba1b..60b5407c99c 100644 --- a/experimental/streaming/example/basic/main.go +++ b/experimental/streaming/example/basic/main.go @@ -72,15 +72,8 @@ func main() { tag.Insert(anotherKey.String("xyz"))), commonLabels, - metric.Measurement{ - Descriptor: oneMetric.Descriptor, - Value: 1.0, - }, - - metric.Measurement{ - Descriptor: measureTwo.Descriptor, - Value: 2.0, - }, + oneMetric.Measurement(1.0), + measureTwo.Measurement(2.0), ) return tracer.WithSpan( diff --git a/experimental/streaming/exporter/exporter.go b/experimental/streaming/exporter/exporter.go index 56d5e4501f9..596014b114e 100644 --- a/experimental/streaming/exporter/exporter.go +++ b/experimental/streaming/exporter/exporter.go @@ -65,7 +65,7 @@ const ( MODIFY_ATTR SET_STATUS SET_NAME - SINGLE_METRIC // A metric Set(), Add(), Record(), or RecordSingle() + SINGLE_METRIC // A metric Set(), Add(), Record() BATCH_METRIC // A RecordBatch() ) diff --git a/experimental/streaming/sdk/metric.go b/experimental/streaming/sdk/metric.go index 4ad0fa1f0dc..b4a44e61095 100644 --- a/experimental/streaming/sdk/metric.go +++ b/experimental/streaming/sdk/metric.go @@ -27,12 +27,16 @@ type metricHandle struct { labels metricLabels } +var _ metric.Handle = &metricHandle{} + type metricLabels struct { sdk *sdk scope exporter.ScopeID } -func (h *metricHandle) Record(ctx context.Context, value float64) { +var _ metric.LabelSet = &metricLabels{} + +func (h *metricHandle) RecordOne(ctx context.Context, value float64) { h.labels.sdk.exporter.Record(exporter.Event{ Type: exporter.SINGLE_METRIC, Context: ctx, @@ -55,7 +59,7 @@ func (s *sdk) DefineLabels(ctx context.Context, labels ...core.KeyValue) metric. } } -func (s *sdk) RecorderFor(ctx context.Context, labels metric.LabelSet, descriptor metric.Descriptor) metric.Recorder { +func (s *sdk) NewHandle(ctx context.Context, descriptor metric.Descriptor, labels metric.LabelSet) metric.Handle { mlabels, _ := labels.(metricLabels) return &metricHandle{ @@ -64,19 +68,14 @@ func (s *sdk) RecorderFor(ctx context.Context, labels metric.LabelSet, descripto } } -func (s *sdk) RecordSingle(ctx context.Context, labels metric.LabelSet, input metric.Measurement) { - mlabels, _ := labels.(metricLabels) - s.exporter.Record(exporter.Event{ - Type: exporter.SINGLE_METRIC, - Context: ctx, - Scope: mlabels.scope, - Measurement: exporter.Measurement{ - Descriptor: input.Descriptor, - Value: input.Value, - }}) +func (s *sdk) DeleteHandle(handle metric.Handle) { } func (s *sdk) RecordBatch(ctx context.Context, labels metric.LabelSet, ms ...metric.Measurement) { + eventType := exporter.BATCH_METRIC + if len(ms) == 1 { + eventType = exporter.SINGLE_METRIC + } oms := make([]exporter.Measurement, len(ms)) mlabels, _ := labels.(metricLabels) @@ -88,7 +87,7 @@ func (s *sdk) RecordBatch(ctx context.Context, labels metric.LabelSet, ms ...met } s.exporter.Record(exporter.Event{ - Type: exporter.BATCH_METRIC, + Type: eventType, Context: ctx, Scope: mlabels.scope, Measurements: oms, From 9680cd292fd7b215f4eb8b27dee070dfdc225140 Mon Sep 17 00:00:00 2001 From: Krzesimir Nowak Date: Tue, 1 Oct 2019 13:37:11 +0200 Subject: [PATCH 08/35] keep integral measurement separate from floating ones --- api/metric/api.go | 9 ++++++--- api/metric/counter.go | 8 ++++---- api/metric/gauge.go | 8 ++++---- api/metric/measure.go | 8 ++++---- api/metric/noop_meter.go | 5 ++++- .../streaming/exporter/reader/format/format.go | 6 +++++- .../streaming/exporter/reader/reader.go | 6 ++++-- experimental/streaming/sdk/metric.go | 18 ++++++++++++++++-- 8 files changed, 47 insertions(+), 21 deletions(-) diff --git a/api/metric/api.go b/api/metric/api.go index d50e7f7baf3..2bd3579e8d0 100644 --- a/api/metric/api.go +++ b/api/metric/api.go @@ -34,8 +34,10 @@ const ( // Recorder is the implementation-level interface to Set/Add/Record individual metrics. type Handle interface { - // RecordOne allows the SDK to observe a single metric event - RecordOne(ctx context.Context, value float64) + // Record allows the SDK to observe a single floating metric event + RecordFloat(ctx context.Context, value float64) + // Record allows the SDK to observe a single integral metric event + RecordInt(ctx context.Context, value int64) } // LabelSet represents a []core.KeyValue for use as pre-defined labels @@ -103,7 +105,8 @@ type Descriptor struct { // Measurement is used for reporting a batch of metric values. type Measurement struct { Descriptor Descriptor - Value float64 + ValueFloat float64 + ValueInt int64 } // Option supports specifying the various metric options. diff --git a/api/metric/counter.go b/api/metric/counter.go index 7269353d7a7..22252a4681a 100644 --- a/api/metric/counter.go +++ b/api/metric/counter.go @@ -57,14 +57,14 @@ func (c *Int64Counter) GetHandle(ctx context.Context, labels LabelSet) (h Int64C func (c *Float64Counter) Measurement(value float64) Measurement { return Measurement{ Descriptor: c.Descriptor, - Value: value, + ValueFloat: value, } } func (c *Int64Counter) Measurement(value int64) Measurement { return Measurement{ Descriptor: c.Descriptor, - Value: float64(value), + ValueInt: value, } } @@ -77,9 +77,9 @@ func (c *Int64Counter) Add(ctx context.Context, value int64, labels LabelSet) { } func (h *Float64CounterHandle) Add(ctx context.Context, value float64) { - h.RecordOne(ctx, value) + h.RecordFloat(ctx, value) } func (h *Int64CounterHandle) Add(ctx context.Context, value int64) { - h.RecordOne(ctx, float64(value)) + h.RecordInt(ctx, value) } diff --git a/api/metric/gauge.go b/api/metric/gauge.go index 6743cb3e0c9..0ce04fa8d79 100644 --- a/api/metric/gauge.go +++ b/api/metric/gauge.go @@ -57,14 +57,14 @@ func (g *Int64Gauge) GetHandle(ctx context.Context, labels LabelSet) (h Int64Gau func (g *Float64Gauge) Measurement(value float64) Measurement { return Measurement{ Descriptor: g.Descriptor, - Value: value, + ValueFloat: value, } } func (g *Int64Gauge) Measurement(value int64) Measurement { return Measurement{ Descriptor: g.Descriptor, - Value: float64(value), + ValueInt: value, } } @@ -77,9 +77,9 @@ func (g *Int64Gauge) Set(ctx context.Context, value int64, labels LabelSet) { } func (h *Float64GaugeHandle) Set(ctx context.Context, value float64) { - h.RecordOne(ctx, value) + h.RecordFloat(ctx, value) } func (h *Int64GaugeHandle) Set(ctx context.Context, value int64) { - h.RecordOne(ctx, float64(value)) + h.RecordInt(ctx, value) } diff --git a/api/metric/measure.go b/api/metric/measure.go index a562ff28856..8d8c4a1bf75 100644 --- a/api/metric/measure.go +++ b/api/metric/measure.go @@ -57,14 +57,14 @@ func (m *Int64Measure) GetHandle(ctx context.Context, labels LabelSet) (h Int64M func (m *Float64Measure) Measurement(value float64) Measurement { return Measurement{ Descriptor: m.Descriptor, - Value: value, + ValueFloat: value, } } func (m *Int64Measure) Measurement(value int64) Measurement { return Measurement{ Descriptor: m.Descriptor, - Value: float64(value), + ValueInt: value, } } @@ -77,9 +77,9 @@ func (m *Int64Measure) Record(ctx context.Context, value int64, labels LabelSet) } func (h *Float64MeasureHandle) Record(ctx context.Context, value float64) { - h.RecordOne(ctx, value) + h.RecordFloat(ctx, value) } func (h *Int64MeasureHandle) Record(ctx context.Context, value int64) { - h.RecordOne(ctx, float64(value)) + h.RecordInt(ctx, value) } diff --git a/api/metric/noop_meter.go b/api/metric/noop_meter.go index 6ffcade3f6a..7cd14db83a6 100644 --- a/api/metric/noop_meter.go +++ b/api/metric/noop_meter.go @@ -14,7 +14,10 @@ var _ Meter = noopMeter{} var _ Handle = noopHandle{} var _ LabelSet = noopLabelSet{} -func (noopHandle) RecordOne(ctx context.Context, value float64) { +func (noopHandle) RecordFloat(ctx context.Context, value float64) { +} + +func (noopHandle) RecordInt(ctx context.Context, value int64) { } func (noopLabelSet) Meter() Meter { diff --git a/experimental/streaming/exporter/reader/format/format.go b/experimental/streaming/exporter/reader/format/format.go index f257036fffc..e57b9a43e30 100644 --- a/experimental/streaming/exporter/reader/format/format.go +++ b/experimental/streaming/exporter/reader/format/format.go @@ -139,7 +139,11 @@ func formatMetricUpdate(buf *strings.Builder, m metric.Measurement) { buf.WriteString(" ") buf.WriteString(m.Descriptor.Name) buf.WriteString("=") - buf.WriteString(fmt.Sprint(m.Value)) + if m.ValueInt != 0 { + buf.WriteString(fmt.Sprint(m.ValueInt)) + } else { + buf.WriteString(fmt.Sprint(m.ValueFloat)) + } } func formatMetricLabels(buf *strings.Builder, l tag.Map) { diff --git a/experimental/streaming/exporter/reader/reader.go b/experimental/streaming/exporter/reader/reader.go index ebb6a1f52ab..842ebdbaf4d 100644 --- a/experimental/streaming/exporter/reader/reader.go +++ b/experimental/streaming/exporter/reader/reader.go @@ -229,7 +229,8 @@ func (ro *readerObserver) orderedObserve(event exporter.Event) { read.Attributes = attrs read.Measurement = metric.Measurement{ Descriptor: event.Measurement.Descriptor, - Value: event.Measurement.Value, + // meh, will be fixed later anyway + ValueFloat: event.Measurement.Value, } case exporter.BATCH_METRIC: @@ -248,7 +249,8 @@ func (ro *readerObserver) orderedObserve(event exporter.Event) { for _, m := range event.Measurements { read.Measurements = append(read.Measurements, metric.Measurement{ Descriptor: m.Descriptor, - Value: m.Value, + // meh, will be fixed later + ValueFloat: m.Value, }) } diff --git a/experimental/streaming/sdk/metric.go b/experimental/streaming/sdk/metric.go index b4a44e61095..52879c2e591 100644 --- a/experimental/streaming/sdk/metric.go +++ b/experimental/streaming/sdk/metric.go @@ -36,7 +36,7 @@ type metricLabels struct { var _ metric.LabelSet = &metricLabels{} -func (h *metricHandle) RecordOne(ctx context.Context, value float64) { +func (h *metricHandle) RecordFloat(ctx context.Context, value float64) { h.labels.sdk.exporter.Record(exporter.Event{ Type: exporter.SINGLE_METRIC, Context: ctx, @@ -48,6 +48,19 @@ func (h *metricHandle) RecordOne(ctx context.Context, value float64) { }) } +func (h *metricHandle) RecordInt(ctx context.Context, value int64) { + h.labels.sdk.exporter.Record(exporter.Event{ + Type: exporter.SINGLE_METRIC, + Context: ctx, + Scope: h.labels.scope, + Measurement: exporter.Measurement{ + Descriptor: h.descriptor, + // meh, will be fixed later + Value: float64(value), + }, + }) +} + func (m metricLabels) Meter() metric.Meter { return m.sdk } @@ -82,7 +95,8 @@ func (s *sdk) RecordBatch(ctx context.Context, labels metric.LabelSet, ms ...met for i, input := range ms { oms[i] = exporter.Measurement{ Descriptor: input.Descriptor, - Value: input.Value, + // meh, will be fixed later + Value: input.ValueFloat, } } From e50df6e639695bfca94b1832445c1b91a2a0ae44 Mon Sep 17 00:00:00 2001 From: Krzesimir Nowak Date: Tue, 1 Oct 2019 13:37:38 +0200 Subject: [PATCH 09/35] remove duplicated measurement type --- experimental/streaming/exporter/exporter.go | 9 ++------- .../streaming/exporter/reader/reader.go | 16 +++------------- experimental/streaming/sdk/metric.go | 19 ++++++------------- 3 files changed, 11 insertions(+), 33 deletions(-) diff --git a/experimental/streaming/exporter/exporter.go b/experimental/streaming/exporter/exporter.go index 596014b114e..29407999d90 100644 --- a/experimental/streaming/exporter/exporter.go +++ b/experimental/streaming/exporter/exporter.go @@ -21,11 +21,6 @@ type ScopeID struct { core.SpanContext } -type Measurement struct { - Descriptor metric.Descriptor - Value float64 -} - type Event struct { // Automatic fields Sequence EventID // Auto-filled @@ -47,8 +42,8 @@ type Event struct { // Values String string // START_SPAN, EVENT, SET_NAME, ... Parent ScopeID // START_SPAN - Measurement Measurement - Measurements []Measurement + Measurement metric.Measurement + Measurements []metric.Measurement } type Observer interface { diff --git a/experimental/streaming/exporter/reader/reader.go b/experimental/streaming/exporter/reader/reader.go index 842ebdbaf4d..5a470b6d7d8 100644 --- a/experimental/streaming/exporter/reader/reader.go +++ b/experimental/streaming/exporter/reader/reader.go @@ -227,11 +227,7 @@ func (ro *readerObserver) orderedObserve(event exporter.Event) { } attrs, _ := ro.readScope(event.Scope) read.Attributes = attrs - read.Measurement = metric.Measurement{ - Descriptor: event.Measurement.Descriptor, - // meh, will be fixed later anyway - ValueFloat: event.Measurement.Value, - } + read.Measurement = event.Measurement case exporter.BATCH_METRIC: read.Type = event.Type @@ -245,14 +241,8 @@ func (ro *readerObserver) orderedObserve(event exporter.Event) { attrs, _ := ro.readScope(event.Scope) read.Attributes = attrs - - for _, m := range event.Measurements { - read.Measurements = append(read.Measurements, metric.Measurement{ - Descriptor: m.Descriptor, - // meh, will be fixed later - ValueFloat: m.Value, - }) - } + read.Measurements = make([]metric.Measurement, len(event.Measurements)) + copy(read.Measurements, event.Measurements) case exporter.SET_STATUS: read.Type = exporter.SET_STATUS diff --git a/experimental/streaming/sdk/metric.go b/experimental/streaming/sdk/metric.go index 52879c2e591..57a818cb3b1 100644 --- a/experimental/streaming/sdk/metric.go +++ b/experimental/streaming/sdk/metric.go @@ -41,9 +41,9 @@ func (h *metricHandle) RecordFloat(ctx context.Context, value float64) { Type: exporter.SINGLE_METRIC, Context: ctx, Scope: h.labels.scope, - Measurement: exporter.Measurement{ + Measurement: metric.Measurement{ Descriptor: h.descriptor, - Value: value, + ValueFloat: value, }, }) } @@ -53,10 +53,9 @@ func (h *metricHandle) RecordInt(ctx context.Context, value int64) { Type: exporter.SINGLE_METRIC, Context: ctx, Scope: h.labels.scope, - Measurement: exporter.Measurement{ + Measurement: metric.Measurement{ Descriptor: h.descriptor, - // meh, will be fixed later - Value: float64(value), + ValueInt: value, }, }) } @@ -89,16 +88,10 @@ func (s *sdk) RecordBatch(ctx context.Context, labels metric.LabelSet, ms ...met if len(ms) == 1 { eventType = exporter.SINGLE_METRIC } - oms := make([]exporter.Measurement, len(ms)) + oms := make([]metric.Measurement, len(ms)) mlabels, _ := labels.(metricLabels) - for i, input := range ms { - oms[i] = exporter.Measurement{ - Descriptor: input.Descriptor, - // meh, will be fixed later - Value: input.ValueFloat, - } - } + copy(oms, ms) s.exporter.Record(exporter.Event{ Type: eventType, From 6b8298ce55937e54058bbddbe69a8fdb303f2c95 Mon Sep 17 00:00:00 2001 From: Krzesimir Nowak Date: Tue, 1 Oct 2019 13:38:02 +0200 Subject: [PATCH 10/35] add checking for options --- api/metric/common.go | 35 ++++++++++++++++++++++++++++++++++- 1 file changed, 34 insertions(+), 1 deletion(-) diff --git a/api/metric/common.go b/api/metric/common.go index a648ac7912d..dc67610ef46 100644 --- a/api/metric/common.go +++ b/api/metric/common.go @@ -14,7 +14,10 @@ package metric -import "sync/atomic" +import ( + "fmt" + "sync/atomic" +) var ( descriptorID uint64 @@ -28,4 +31,34 @@ func registerDescriptor(name string, kind Kind, opts []Option, d *Descriptor) { for _, opt := range opts { opt(d) } + ensureValidDescriptor(d) +} + +func ensureValidDescriptor(d *Descriptor) { + checkNonMonotonic := false + checkMonotonic := false + checkSigned := false + switch d.Kind { + case Invalid: + panic("tried to register a metric descriptor with invalid kind") + case CounterKind: + checkMonotonic, checkSigned = true, true + case GaugeKind: + checkNonMonotonic, checkSigned = true, true + case MeasureKind: + checkMonotonic, checkNonMonotonic = true, true + } + if checkNonMonotonic && d.NonMonotonic { + panicBadField(d.Kind, "NonMonotonic") + } + if checkMonotonic && d.Monotonic { + panicBadField(d.Kind, "Monotonic") + } + if checkSigned && d.Signed { + panicBadField(d.Kind, "Signed") + } +} + +func panicBadField(kind Kind, field string) { + panic(fmt.Sprintf("invalid %s descriptor, has set %s field", kind.String(), field)) } From f2a4abebae493e9f06de439367730c58fbb0ab2f Mon Sep 17 00:00:00 2001 From: Krzesimir Nowak Date: Tue, 1 Oct 2019 13:41:20 +0200 Subject: [PATCH 11/35] reorder some fields and functions --- api/metric/api.go | 48 +++++++++++++++++++++++------------------------ 1 file changed, 24 insertions(+), 24 deletions(-) diff --git a/api/metric/api.go b/api/metric/api.go index 2bd3579e8d0..0314b915bf0 100644 --- a/api/metric/api.go +++ b/api/metric/api.go @@ -72,6 +72,13 @@ type Descriptor struct { // should have length > 0. Name string + // Kind is the metric kind of this descriptor. + Kind Kind + + // Keys are required keys determined in the handles + // obtained for this metric. + Keys []core.Key + // ID is uniquely assigned to support per-SDK registration. ID DescriptorID @@ -81,8 +88,8 @@ type Descriptor struct { // Unit is an optional field describing this metric descriptor. Unit unit.Unit - // Kind is the metric kind of this descriptor. - Kind Kind + // Disabled implies this descriptor is disabled by default. + Disabled bool // NonMonotonic implies this is an up-down Counter. NonMonotonic bool @@ -93,13 +100,6 @@ type Descriptor struct { // Signed implies this is a Measure that supports positive and // negative values. Signed bool - - // Disabled implies this descriptor is disabled by default. - Disabled bool - - // Keys are required keys determined in the handles - // obtained for this metric. - Keys []core.Key } // Measurement is used for reporting a batch of metric values. @@ -126,6 +126,21 @@ func WithUnit(unit unit.Unit) Option { } } +// WithDisabled sets whether a metric is disabled by default +func WithDisabled(dis bool) Option { + return func(d *Descriptor) { + d.Disabled = dis + } +} + +// WithKeys applies required label keys. Multiple `WithKeys` +// options accumulate. +func WithKeys(keys ...core.Key) Option { + return func(d *Descriptor) { + d.Keys = append(d.Keys, keys...) + } +} + // WithNonMonotonic sets whether a counter is permitted to go up AND down. func WithNonMonotonic(nm bool) Option { return func(d *Descriptor) { @@ -147,21 +162,6 @@ func WithSigned(s bool) Option { } } -// WithDisabled sets whether a measure is disabled by default -func WithDisabled(dis bool) Option { - return func(d *Descriptor) { - d.Disabled = dis - } -} - -// WithKeys applies required label keys. Multiple `WithKeys` -// options accumulate. -func WithKeys(keys ...core.Key) Option { - return func(m *Descriptor) { - m.Keys = append(m.Keys, keys...) - } -} - // Defined returns true when the descriptor has been registered. func (d Descriptor) Defined() bool { return len(d.Name) != 0 From 32e3b6a8515b836aeac1b39675baeedc75d08622 Mon Sep 17 00:00:00 2001 From: Krzesimir Nowak Date: Wed, 25 Sep 2019 19:19:54 +0200 Subject: [PATCH 12/35] license --- experimental/streaming/sdk/package.go | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/experimental/streaming/sdk/package.go b/experimental/streaming/sdk/package.go index de519c904d2..aa0b1eb0fa8 100644 --- a/experimental/streaming/sdk/package.go +++ b/experimental/streaming/sdk/package.go @@ -1,3 +1,17 @@ +// 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 sdk import ( From 3f934ffa6711d6b488bbf778d0d6440712b5151a Mon Sep 17 00:00:00 2001 From: Krzesimir Nowak Date: Sat, 28 Sep 2019 13:01:37 +0200 Subject: [PATCH 13/35] rework measurement values --- api/metric/api.go | 3 +- api/metric/counter.go | 4 +- api/metric/gauge.go | 4 +- api/metric/measure.go | 4 +- api/metric/value.go | 111 ++++++++++++++++++ .../exporter/reader/format/format.go | 6 +- experimental/streaming/sdk/metric.go | 4 +- experimental/streaming/sdk/span_test.go | 21 ++++ 8 files changed, 142 insertions(+), 15 deletions(-) create mode 100644 api/metric/value.go diff --git a/api/metric/api.go b/api/metric/api.go index 0314b915bf0..0921bd08111 100644 --- a/api/metric/api.go +++ b/api/metric/api.go @@ -105,8 +105,7 @@ type Descriptor struct { // Measurement is used for reporting a batch of metric values. type Measurement struct { Descriptor Descriptor - ValueFloat float64 - ValueInt int64 + Value MeasurementValue } // Option supports specifying the various metric options. diff --git a/api/metric/counter.go b/api/metric/counter.go index 22252a4681a..a247f152553 100644 --- a/api/metric/counter.go +++ b/api/metric/counter.go @@ -57,14 +57,14 @@ func (c *Int64Counter) GetHandle(ctx context.Context, labels LabelSet) (h Int64C func (c *Float64Counter) Measurement(value float64) Measurement { return Measurement{ Descriptor: c.Descriptor, - ValueFloat: value, + Value: NewFloatMeasurementValue(value), } } func (c *Int64Counter) Measurement(value int64) Measurement { return Measurement{ Descriptor: c.Descriptor, - ValueInt: value, + Value: NewIntMeasurementValue(value), } } diff --git a/api/metric/gauge.go b/api/metric/gauge.go index 0ce04fa8d79..e8c98ea5a78 100644 --- a/api/metric/gauge.go +++ b/api/metric/gauge.go @@ -57,14 +57,14 @@ func (g *Int64Gauge) GetHandle(ctx context.Context, labels LabelSet) (h Int64Gau func (g *Float64Gauge) Measurement(value float64) Measurement { return Measurement{ Descriptor: g.Descriptor, - ValueFloat: value, + Value: NewFloatMeasurementValue(value), } } func (g *Int64Gauge) Measurement(value int64) Measurement { return Measurement{ Descriptor: g.Descriptor, - ValueInt: value, + Value: NewIntMeasurementValue(value), } } diff --git a/api/metric/measure.go b/api/metric/measure.go index 8d8c4a1bf75..dcf1a7b7b4b 100644 --- a/api/metric/measure.go +++ b/api/metric/measure.go @@ -57,14 +57,14 @@ func (m *Int64Measure) GetHandle(ctx context.Context, labels LabelSet) (h Int64M func (m *Float64Measure) Measurement(value float64) Measurement { return Measurement{ Descriptor: m.Descriptor, - ValueFloat: value, + Value: NewFloatMeasurementValue(value), } } func (m *Int64Measure) Measurement(value int64) Measurement { return Measurement{ Descriptor: m.Descriptor, - ValueInt: value, + Value: NewIntMeasurementValue(value), } } diff --git a/api/metric/value.go b/api/metric/value.go new file mode 100644 index 00000000000..532046319ca --- /dev/null +++ b/api/metric/value.go @@ -0,0 +1,111 @@ +// 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 ( + "fmt" +) + +type MeasurementValue struct { + i interface{} +} + +type MeasurementValueVisitor interface { + Int(int64) + Float(float64) +} + +func NewIntMeasurementValue(i int64) MeasurementValue { + return MeasurementValue{ + i: i, + } +} + +func NewFloatMeasurementValue(f float64) MeasurementValue { + return MeasurementValue{ + i: f, + } +} + +func (v MeasurementValue) GetInt() (int64, bool) { + i, ok := v.i.(int64) + return i, ok +} + +func (v MeasurementValue) GetFloat() (float64, bool) { + f, ok := v.i.(float64) + return f, ok +} + +func (v MeasurementValue) Visit(visitor MeasurementValueVisitor) { + switch rv := v.i.(type) { + case int64: + visitor.Int(rv) + case float64: + visitor.Float(rv) + default: + panic(fmt.Sprintf("invalid measurement value: %#v", rv)) + } +} + +func (v MeasurementValue) Emit() string { + return fmt.Sprintf("%v", v.i) +} + +type intCoercer struct { + i *int64 +} + +var _ MeasurementValueVisitor = intCoercer{} + +func (c intCoercer) Int(i int64) { + *c.i = i +} + +func (c intCoercer) Float(f float64) { + *c.i = int64(f) +} + +func (v MeasurementValue) CoerceToInt() int64 { + var i int64 + c := intCoercer{ + i: &i, + } + v.Visit(c) + return i +} + +type floatCoercer struct { + f *float64 +} + +var _ MeasurementValueVisitor = floatCoercer{} + +func (c floatCoercer) Int(i int64) { + *c.f = float64(i) +} + +func (c floatCoercer) Float(f float64) { + *c.f = f +} + +func (v MeasurementValue) CoerceToFloat() float64 { + var f float64 + c := floatCoercer{ + f: &f, + } + v.Visit(c) + return f +} diff --git a/experimental/streaming/exporter/reader/format/format.go b/experimental/streaming/exporter/reader/format/format.go index e57b9a43e30..3bbcaa193f7 100644 --- a/experimental/streaming/exporter/reader/format/format.go +++ b/experimental/streaming/exporter/reader/format/format.go @@ -139,11 +139,7 @@ func formatMetricUpdate(buf *strings.Builder, m metric.Measurement) { buf.WriteString(" ") buf.WriteString(m.Descriptor.Name) buf.WriteString("=") - if m.ValueInt != 0 { - buf.WriteString(fmt.Sprint(m.ValueInt)) - } else { - buf.WriteString(fmt.Sprint(m.ValueFloat)) - } + buf.WriteString(m.Value.Emit()) } func formatMetricLabels(buf *strings.Builder, l tag.Map) { diff --git a/experimental/streaming/sdk/metric.go b/experimental/streaming/sdk/metric.go index 57a818cb3b1..a937fa8deed 100644 --- a/experimental/streaming/sdk/metric.go +++ b/experimental/streaming/sdk/metric.go @@ -43,7 +43,7 @@ func (h *metricHandle) RecordFloat(ctx context.Context, value float64) { Scope: h.labels.scope, Measurement: metric.Measurement{ Descriptor: h.descriptor, - ValueFloat: value, + Value: metric.NewFloatMeasurementValue(value), }, }) } @@ -55,7 +55,7 @@ func (h *metricHandle) RecordInt(ctx context.Context, value int64) { Scope: h.labels.scope, Measurement: metric.Measurement{ Descriptor: h.descriptor, - ValueInt: value, + Value: metric.NewIntMeasurementValue(value), }, }) } diff --git a/experimental/streaming/sdk/span_test.go b/experimental/streaming/sdk/span_test.go index 6f72033a82e..080dcb345eb 100644 --- a/experimental/streaming/sdk/span_test.go +++ b/experimental/streaming/sdk/span_test.go @@ -24,6 +24,7 @@ import ( "go.opentelemetry.io/api/core" "go.opentelemetry.io/api/key" + "go.opentelemetry.io/api/metric" "go.opentelemetry.io/api/trace" "go.opentelemetry.io/experimental/streaming/exporter" @@ -130,6 +131,25 @@ func checkContext(t *testing.T, ctx context.Context, key, wantValue interface{}) return true } +func measurementCompare(mv1, mv2 metric.MeasurementValue) bool { + i1, ok1 := mv1.GetInt() + i2, ok2 := mv2.GetInt() + if ok1 != ok2 { + return false + } else if ok1 && ok2 { + return i1 == i2 + } + f1, ok1 := mv1.GetFloat() + f2, ok2 := mv2.GetFloat() + if ok1 != ok2 { + return false + } else if ok1 && ok2 { + return f1 == f2 + } + // both are not initialized + return true +} + func diffEvents(t *testing.T, got, want []exporter.Event, extraIgnoredFields ...string) bool { ignoredPaths := map[string]struct{}{ "Sequence": struct{}{}, @@ -143,6 +163,7 @@ func diffEvents(t *testing.T, got, want []exporter.Event, extraIgnoredFields ... _, found := ignoredPaths[path.String()] return found }, cmp.Ignore()), + cmp.Comparer(measurementCompare), } if diff := cmp.Diff(got, want, opts...); diff != "" { t.Errorf("Events: -got +want %s", diff) From 6e1139fc14c9f58cb194631c230206d2d64919cf Mon Sep 17 00:00:00 2001 From: Krzesimir Nowak Date: Tue, 1 Oct 2019 12:33:28 +0200 Subject: [PATCH 14/35] make measurement value a tagged union --- api/metric/counter.go | 4 +- api/metric/gauge.go | 4 +- api/metric/measure.go | 4 +- api/metric/value.go | 150 ++++++++++++++++++------ experimental/streaming/sdk/metric.go | 4 +- experimental/streaming/sdk/span_test.go | 8 +- 6 files changed, 128 insertions(+), 46 deletions(-) diff --git a/api/metric/counter.go b/api/metric/counter.go index a247f152553..6a3ba14efd1 100644 --- a/api/metric/counter.go +++ b/api/metric/counter.go @@ -57,14 +57,14 @@ func (c *Int64Counter) GetHandle(ctx context.Context, labels LabelSet) (h Int64C func (c *Float64Counter) Measurement(value float64) Measurement { return Measurement{ Descriptor: c.Descriptor, - Value: NewFloatMeasurementValue(value), + Value: NewFloat64MeasurementValue(value), } } func (c *Int64Counter) Measurement(value int64) Measurement { return Measurement{ Descriptor: c.Descriptor, - Value: NewIntMeasurementValue(value), + Value: NewInt64MeasurementValue(value), } } diff --git a/api/metric/gauge.go b/api/metric/gauge.go index e8c98ea5a78..9a2c3bb8fdc 100644 --- a/api/metric/gauge.go +++ b/api/metric/gauge.go @@ -57,14 +57,14 @@ func (g *Int64Gauge) GetHandle(ctx context.Context, labels LabelSet) (h Int64Gau func (g *Float64Gauge) Measurement(value float64) Measurement { return Measurement{ Descriptor: g.Descriptor, - Value: NewFloatMeasurementValue(value), + Value: NewFloat64MeasurementValue(value), } } func (g *Int64Gauge) Measurement(value int64) Measurement { return Measurement{ Descriptor: g.Descriptor, - Value: NewIntMeasurementValue(value), + Value: NewInt64MeasurementValue(value), } } diff --git a/api/metric/measure.go b/api/metric/measure.go index dcf1a7b7b4b..100e3a9e739 100644 --- a/api/metric/measure.go +++ b/api/metric/measure.go @@ -57,14 +57,14 @@ func (m *Int64Measure) GetHandle(ctx context.Context, labels LabelSet) (h Int64M func (m *Float64Measure) Measurement(value float64) Measurement { return Measurement{ Descriptor: m.Descriptor, - Value: NewFloatMeasurementValue(value), + Value: NewFloat64MeasurementValue(value), } } func (m *Int64Measure) Measurement(value int64) Measurement { return Measurement{ Descriptor: m.Descriptor, - Value: NewIntMeasurementValue(value), + Value: NewInt64MeasurementValue(value), } } diff --git a/api/metric/value.go b/api/metric/value.go index 532046319ca..24f090a42ae 100644 --- a/api/metric/value.go +++ b/api/metric/value.go @@ -16,94 +16,176 @@ package metric import ( "fmt" + "math" ) +type valueKind int8 + +const ( + int64Kind valueKind = iota + float64Kind +) + +// MeasurementValue represents either an integral or a floating point +// value of a measurement. Empty MeasurementValue is treated as +// integral zero. type MeasurementValue struct { - i interface{} + raw uint64 + kind valueKind } +// MeasurementValueVisitor is an interface used for visitation of +// MeasurementValues. type MeasurementValueVisitor interface { - Int(int64) - Float(float64) + // Int64 gets called for an integral MeasurementValue. + Int64(int64) + // Int64 gets called for a floating point MeasurementValue. + Float64(float64) + // Unknown gets called on the unknown value. This normally + // doesn't happen - it is meant for the future compatilibity + // when new types may get added to the MeasurementValue. The + // additional types would be handled an in extension interface + // of the MeasurementValueVisitor. + Unknown(interface{}) +} + +// NewInt64MeasurementValue creates an integral MeasurementValue. +func NewInt64MeasurementValue(i int64) MeasurementValue { + return MeasurementValue{ + raw: uint64(i), + kind: int64Kind, + } } -func NewIntMeasurementValue(i int64) MeasurementValue { +// NewFloat64MeasurementValue creates a floating point +// MeasurementValue. +func NewFloat64MeasurementValue(f float64) MeasurementValue { return MeasurementValue{ - i: i, + raw: math.Float64bits(f), + kind: float64Kind, } } -func NewFloatMeasurementValue(f float64) MeasurementValue { - return MeasurementValue{ - i: f, +// GetInt64 tries to get an integral value from MeasurementValue. The +// use of this function is similar to doing a checked type assertion +// (i, ok := v.(int64)). +func (v MeasurementValue) GetInt64() (int64, bool) { + if v.kind != int64Kind { + return 0, false } + return v.int64(), true } -func (v MeasurementValue) GetInt() (int64, bool) { - i, ok := v.i.(int64) - return i, ok +// GetFloat64 tries to get a floating point value from +// MeasurementValue. The use of this function is similar to doing a +// checked type assertion (i, ok := v.(float64)). +func (v MeasurementValue) GetFloat64() (float64, bool) { + if v.kind != float64Kind { + return 0, false + } + return v.float64(), true +} + +func (v MeasurementValue) int64() int64 { + return int64(v.raw) } -func (v MeasurementValue) GetFloat() (float64, bool) { - f, ok := v.i.(float64) - return f, ok +func (v MeasurementValue) float64() float64 { + return math.Float64frombits(v.raw) } +// Visit uses the visitor to find out the actual value of the +// MeasurementValue. func (v MeasurementValue) Visit(visitor MeasurementValueVisitor) { - switch rv := v.i.(type) { - case int64: - visitor.Int(rv) - case float64: - visitor.Float(rv) - default: - panic(fmt.Sprintf("invalid measurement value: %#v", rv)) + switch v.kind { + case int64Kind: + visitor.Int64(v.int64()) + case float64Kind: + visitor.Float64(v.float64()) } } +type stringEmitter struct { + s *string +} + +var _ MeasurementValueVisitor = stringEmitter{} + +func (e stringEmitter) Int64(i int64) { + *e.s = fmt.Sprintf("%d", i) +} + +func (e stringEmitter) Float64(f float64) { + *e.s = fmt.Sprintf("%f", f) +} + +func (e stringEmitter) Unknown(interface{}) { +} + +// Emit returns a string representation of the actual value of the +// MeasurementValue. A %d is used for integral values, %f for floating +// point values. func (v MeasurementValue) Emit() string { - return fmt.Sprintf("%v", v.i) + var s string + c := stringEmitter{ + s: &s, + } + v.Visit(c) + return s } -type intCoercer struct { +type int64Coercer struct { i *int64 } -var _ MeasurementValueVisitor = intCoercer{} +var _ MeasurementValueVisitor = int64Coercer{} -func (c intCoercer) Int(i int64) { +func (c int64Coercer) Int64(i int64) { *c.i = i } -func (c intCoercer) Float(f float64) { +func (c int64Coercer) Float64(f float64) { *c.i = int64(f) } -func (v MeasurementValue) CoerceToInt() int64 { +func (c int64Coercer) Unknown(interface{}) { +} + +// CoerceToInt64 returns the value of the MeasurementValue casted to +// int64. This may result in loss of precision of the actual value was +// a floating point one. +func (v MeasurementValue) CoerceToInt64() int64 { var i int64 - c := intCoercer{ + c := int64Coercer{ i: &i, } v.Visit(c) return i } -type floatCoercer struct { +type float64Coercer struct { f *float64 } -var _ MeasurementValueVisitor = floatCoercer{} +var _ MeasurementValueVisitor = float64Coercer{} -func (c floatCoercer) Int(i int64) { +func (c float64Coercer) Int64(i int64) { *c.f = float64(i) } -func (c floatCoercer) Float(f float64) { +func (c float64Coercer) Float64(f float64) { *c.f = f } -func (v MeasurementValue) CoerceToFloat() float64 { +func (c float64Coercer) Unknown(interface{}) { +} + +// CoerceToFloat64 returns the value of the MeasurementValue casted to +// float64. This may result in loss of precision of the actual value +// was an integral one. +func (v MeasurementValue) CoerceToFloat64() float64 { var f float64 - c := floatCoercer{ + c := float64Coercer{ f: &f, } v.Visit(c) diff --git a/experimental/streaming/sdk/metric.go b/experimental/streaming/sdk/metric.go index a937fa8deed..6c3c0a572d0 100644 --- a/experimental/streaming/sdk/metric.go +++ b/experimental/streaming/sdk/metric.go @@ -43,7 +43,7 @@ func (h *metricHandle) RecordFloat(ctx context.Context, value float64) { Scope: h.labels.scope, Measurement: metric.Measurement{ Descriptor: h.descriptor, - Value: metric.NewFloatMeasurementValue(value), + Value: metric.NewFloat64MeasurementValue(value), }, }) } @@ -55,7 +55,7 @@ func (h *metricHandle) RecordInt(ctx context.Context, value int64) { Scope: h.labels.scope, Measurement: metric.Measurement{ Descriptor: h.descriptor, - Value: metric.NewIntMeasurementValue(value), + Value: metric.NewInt64MeasurementValue(value), }, }) } diff --git a/experimental/streaming/sdk/span_test.go b/experimental/streaming/sdk/span_test.go index 080dcb345eb..d944d35c887 100644 --- a/experimental/streaming/sdk/span_test.go +++ b/experimental/streaming/sdk/span_test.go @@ -132,15 +132,15 @@ func checkContext(t *testing.T, ctx context.Context, key, wantValue interface{}) } func measurementCompare(mv1, mv2 metric.MeasurementValue) bool { - i1, ok1 := mv1.GetInt() - i2, ok2 := mv2.GetInt() + i1, ok1 := mv1.GetInt64() + i2, ok2 := mv2.GetInt64() if ok1 != ok2 { return false } else if ok1 && ok2 { return i1 == i2 } - f1, ok1 := mv1.GetFloat() - f2, ok2 := mv2.GetFloat() + f1, ok1 := mv1.GetFloat64() + f2, ok2 := mv2.GetFloat64() if ok1 != ok2 { return false } else if ok1 && ok2 { From 508422c56065d543bfa60519d10de94dd11c9920 Mon Sep 17 00:00:00 2001 From: Krzesimir Nowak Date: Tue, 1 Oct 2019 12:35:23 +0200 Subject: [PATCH 15/35] simplify to one kind of metrics --- api/metric/api.go | 6 +-- api/metric/counter.go | 57 +++++++++----------- api/metric/gauge.go | 57 +++++++++----------- api/metric/measure.go | 57 +++++++++----------- api/metric/noop_meter.go | 5 +- example/basic/main.go | 12 ++--- experimental/streaming/example/basic/main.go | 12 ++--- experimental/streaming/sdk/metric.go | 16 +----- 8 files changed, 92 insertions(+), 130 deletions(-) diff --git a/api/metric/api.go b/api/metric/api.go index 0921bd08111..b4daef2e755 100644 --- a/api/metric/api.go +++ b/api/metric/api.go @@ -34,10 +34,8 @@ const ( // Recorder is the implementation-level interface to Set/Add/Record individual metrics. type Handle interface { - // Record allows the SDK to observe a single floating metric event - RecordFloat(ctx context.Context, value float64) - // Record allows the SDK to observe a single integral metric event - RecordInt(ctx context.Context, value int64) + // Record allows the SDK to observe a single metric event + Record(ctx context.Context, value MeasurementValue) } // LabelSet represents a []core.KeyValue for use as pre-defined labels diff --git a/api/metric/counter.go b/api/metric/counter.go index 6a3ba14efd1..990f7b008d9 100644 --- a/api/metric/counter.go +++ b/api/metric/counter.go @@ -18,68 +18,61 @@ import ( "context" ) -type Float64Counter struct { +type Counter struct { Descriptor } -type Int64Counter struct { - Descriptor -} - -type Float64CounterHandle struct { - Handle -} - -type Int64CounterHandle struct { +type CounterHandle struct { Handle } -func NewFloat64Counter(name string, mos ...Option) (c Float64Counter) { - registerDescriptor(name, CounterKind, mos, &c.Descriptor) - return -} - -func NewInt64Counter(name string, mos ...Option) (c Int64Counter) { +func NewCounter(name string, mos ...Option) (c Counter) { registerDescriptor(name, CounterKind, mos, &c.Descriptor) return } -func (c *Float64Counter) GetHandle(ctx context.Context, labels LabelSet) (h Float64CounterHandle) { +func (c *Counter) GetHandle(ctx context.Context, labels LabelSet) (h CounterHandle) { h.Handle = labels.Meter().NewHandle(ctx, c.Descriptor, labels) return } -func (c *Int64Counter) GetHandle(ctx context.Context, labels LabelSet) (h Int64CounterHandle) { - h.Handle = labels.Meter().NewHandle(ctx, c.Descriptor, labels) - return -} - -func (c *Float64Counter) Measurement(value float64) Measurement { +func (c *Counter) Float64Measurement(value float64) Measurement { return Measurement{ Descriptor: c.Descriptor, Value: NewFloat64MeasurementValue(value), } } -func (c *Int64Counter) Measurement(value int64) Measurement { +func (c *Counter) Int64Measurement(value int64) Measurement { return Measurement{ Descriptor: c.Descriptor, Value: NewInt64MeasurementValue(value), } } -func (c *Float64Counter) Add(ctx context.Context, value float64, labels LabelSet) { - labels.Meter().RecordBatch(ctx, labels, c.Measurement(value)) +func (c *Counter) Add(ctx context.Context, value MeasurementValue, labels LabelSet) { + labels.Meter().RecordBatch(ctx, labels, Measurement{ + Descriptor: c.Descriptor, + Value: value, + }) +} + +func (c *Counter) AddFloat64(ctx context.Context, value float64, labels LabelSet) { + c.Add(ctx, NewFloat64MeasurementValue(value), labels) +} + +func (c *Counter) AddInt64(ctx context.Context, value int64, labels LabelSet) { + c.Add(ctx, NewInt64MeasurementValue(value), labels) } -func (c *Int64Counter) Add(ctx context.Context, value int64, labels LabelSet) { - labels.Meter().RecordBatch(ctx, labels, c.Measurement(value)) +func (h *CounterHandle) Add(ctx context.Context, value MeasurementValue) { + h.Record(ctx, value) } -func (h *Float64CounterHandle) Add(ctx context.Context, value float64) { - h.RecordFloat(ctx, value) +func (h *CounterHandle) AddFloat64(ctx context.Context, value float64) { + h.Add(ctx, NewFloat64MeasurementValue(value)) } -func (h *Int64CounterHandle) Add(ctx context.Context, value int64) { - h.RecordInt(ctx, value) +func (h *CounterHandle) AddInt64(ctx context.Context, value int64) { + h.Add(ctx, NewInt64MeasurementValue(value)) } diff --git a/api/metric/gauge.go b/api/metric/gauge.go index 9a2c3bb8fdc..a4caa92d025 100644 --- a/api/metric/gauge.go +++ b/api/metric/gauge.go @@ -18,68 +18,61 @@ import ( "context" ) -type Float64Gauge struct { +type Gauge struct { Descriptor } -type Int64Gauge struct { - Descriptor -} - -type Float64GaugeHandle struct { - Handle -} - -type Int64GaugeHandle struct { +type GaugeHandle struct { Handle } -func NewFloat64Gauge(name string, mos ...Option) (g Float64Gauge) { - registerDescriptor(name, GaugeKind, mos, &g.Descriptor) - return -} - -func NewInt64Gauge(name string, mos ...Option) (g Int64Gauge) { +func NewGauge(name string, mos ...Option) (g Gauge) { registerDescriptor(name, GaugeKind, mos, &g.Descriptor) return } -func (g *Float64Gauge) GetHandle(ctx context.Context, labels LabelSet) (h Float64GaugeHandle) { +func (g *Gauge) GetHandle(ctx context.Context, labels LabelSet) (h GaugeHandle) { h.Handle = labels.Meter().NewHandle(ctx, g.Descriptor, labels) return } -func (g *Int64Gauge) GetHandle(ctx context.Context, labels LabelSet) (h Int64GaugeHandle) { - h.Handle = labels.Meter().NewHandle(ctx, g.Descriptor, labels) - return -} - -func (g *Float64Gauge) Measurement(value float64) Measurement { +func (g *Gauge) Float64Measurement(value float64) Measurement { return Measurement{ Descriptor: g.Descriptor, Value: NewFloat64MeasurementValue(value), } } -func (g *Int64Gauge) Measurement(value int64) Measurement { +func (g *Gauge) Int64Measurement(value int64) Measurement { return Measurement{ Descriptor: g.Descriptor, Value: NewInt64MeasurementValue(value), } } -func (g *Float64Gauge) Set(ctx context.Context, value float64, labels LabelSet) { - labels.Meter().RecordBatch(ctx, labels, g.Measurement(value)) +func (g *Gauge) Set(ctx context.Context, value MeasurementValue, labels LabelSet) { + labels.Meter().RecordBatch(ctx, labels, Measurement{ + Descriptor: g.Descriptor, + Value: value, + }) +} + +func (g *Gauge) SetFloat64(ctx context.Context, value float64, labels LabelSet) { + g.Set(ctx, NewFloat64MeasurementValue(value), labels) +} + +func (g *Gauge) SetInt64(ctx context.Context, value int64, labels LabelSet) { + g.Set(ctx, NewInt64MeasurementValue(value), labels) } -func (g *Int64Gauge) Set(ctx context.Context, value int64, labels LabelSet) { - labels.Meter().RecordBatch(ctx, labels, g.Measurement(value)) +func (h *GaugeHandle) Set(ctx context.Context, value MeasurementValue) { + h.Record(ctx, value) } -func (h *Float64GaugeHandle) Set(ctx context.Context, value float64) { - h.RecordFloat(ctx, value) +func (h *GaugeHandle) SetFloat64(ctx context.Context, value float64) { + h.Set(ctx, NewFloat64MeasurementValue(value)) } -func (h *Int64GaugeHandle) Set(ctx context.Context, value int64) { - h.RecordInt(ctx, value) +func (h *GaugeHandle) SetInt64(ctx context.Context, value int64) { + h.Set(ctx, NewInt64MeasurementValue(value)) } diff --git a/api/metric/measure.go b/api/metric/measure.go index 100e3a9e739..deb909bb886 100644 --- a/api/metric/measure.go +++ b/api/metric/measure.go @@ -18,68 +18,61 @@ import ( "context" ) -type Float64Measure struct { +type Measure struct { Descriptor } -type Int64Measure struct { - Descriptor -} - -type Float64MeasureHandle struct { - Handle -} - -type Int64MeasureHandle struct { +type MeasureHandle struct { Handle } -func NewFloat64Measure(name string, mos ...Option) (m Float64Measure) { - registerDescriptor(name, MeasureKind, mos, &m.Descriptor) - return -} - -func NewInt64Measure(name string, mos ...Option) (m Int64Measure) { +func NewMeasure(name string, mos ...Option) (m Measure) { registerDescriptor(name, MeasureKind, mos, &m.Descriptor) return } -func (m *Float64Measure) GetHandle(ctx context.Context, labels LabelSet) (h Float64MeasureHandle) { +func (m *Measure) GetHandle(ctx context.Context, labels LabelSet) (h MeasureHandle) { h.Handle = labels.Meter().NewHandle(ctx, m.Descriptor, labels) return } -func (m *Int64Measure) GetHandle(ctx context.Context, labels LabelSet) (h Int64MeasureHandle) { - h.Handle = labels.Meter().NewHandle(ctx, m.Descriptor, labels) - return -} - -func (m *Float64Measure) Measurement(value float64) Measurement { +func (m *Measure) Float64Measurement(value float64) Measurement { return Measurement{ Descriptor: m.Descriptor, Value: NewFloat64MeasurementValue(value), } } -func (m *Int64Measure) Measurement(value int64) Measurement { +func (m *Measure) Int64Measurement(value int64) Measurement { return Measurement{ Descriptor: m.Descriptor, Value: NewInt64MeasurementValue(value), } } -func (m *Float64Measure) Record(ctx context.Context, value float64, labels LabelSet) { - labels.Meter().RecordBatch(ctx, labels, m.Measurement(value)) +func (m *Measure) Record(ctx context.Context, value MeasurementValue, labels LabelSet) { + labels.Meter().RecordBatch(ctx, labels, Measurement{ + Descriptor: m.Descriptor, + Value: value, + }) +} + +func (m *Measure) RecordFloat64(ctx context.Context, value float64, labels LabelSet) { + m.Record(ctx, NewFloat64MeasurementValue(value), labels) +} + +func (m *Measure) RecordInt64(ctx context.Context, value int64, labels LabelSet) { + m.Record(ctx, NewInt64MeasurementValue(value), labels) } -func (m *Int64Measure) Record(ctx context.Context, value int64, labels LabelSet) { - labels.Meter().RecordBatch(ctx, labels, m.Measurement(value)) +func (h *MeasureHandle) Record(ctx context.Context, value MeasurementValue) { + h.Handle.Record(ctx, value) } -func (h *Float64MeasureHandle) Record(ctx context.Context, value float64) { - h.RecordFloat(ctx, value) +func (h *MeasureHandle) RecordFloat64(ctx context.Context, value float64) { + h.Record(ctx, NewFloat64MeasurementValue(value)) } -func (h *Int64MeasureHandle) Record(ctx context.Context, value int64) { - h.RecordInt(ctx, value) +func (h *MeasureHandle) RecordInt64(ctx context.Context, value int64) { + h.Record(ctx, NewInt64MeasurementValue(value)) } diff --git a/api/metric/noop_meter.go b/api/metric/noop_meter.go index 7cd14db83a6..c765fe6f472 100644 --- a/api/metric/noop_meter.go +++ b/api/metric/noop_meter.go @@ -14,10 +14,7 @@ var _ Meter = noopMeter{} var _ Handle = noopHandle{} var _ LabelSet = noopLabelSet{} -func (noopHandle) RecordFloat(ctx context.Context, value float64) { -} - -func (noopHandle) RecordInt(ctx context.Context, value int64) { +func (noopHandle) Record(ctx context.Context, value MeasurementValue) { } func (noopLabelSet) Meter() Meter { diff --git a/example/basic/main.go b/example/basic/main.go index 214b29576df..1bbb222fe11 100644 --- a/example/basic/main.go +++ b/example/basic/main.go @@ -33,12 +33,12 @@ var ( lemonsKey = key.New("ex.com/lemons") anotherKey = key.New("ex.com/another") - oneMetric = metric.NewFloat64Gauge("ex.com/one", + oneMetric = metric.NewGauge("ex.com/one", metric.WithKeys(fooKey, barKey, lemonsKey), metric.WithDescription("A gauge set to 1.0"), ) - measureTwo = metric.NewFloat64Measure("ex.com/two") + measureTwo = metric.NewMeasure("ex.com/two") ) func main() { @@ -61,7 +61,7 @@ func main() { trace.CurrentSpan(ctx).SetAttributes(anotherKey.String("yes")) - gauge.Set(ctx, 1) + gauge.SetInt64(ctx, 1) meter.RecordBatch( // Note: call-site variables added as context tags: @@ -69,8 +69,8 @@ func main() { tag.Insert(anotherKey.String("xyz"))), commonLabels, - oneMetric.Measurement(1.0), - measureTwo.Measurement(2.0), + oneMetric.Float64Measurement(1.0), + measureTwo.Float64Measurement(2.0), ) return tracer.WithSpan( @@ -81,7 +81,7 @@ func main() { trace.CurrentSpan(ctx).AddEvent(ctx, "Sub span event") - measure.Record(ctx, 1.3) + measure.RecordFloat64(ctx, 1.3) return nil }, diff --git a/experimental/streaming/example/basic/main.go b/experimental/streaming/example/basic/main.go index 60b5407c99c..96ccc2059ea 100644 --- a/experimental/streaming/example/basic/main.go +++ b/experimental/streaming/example/basic/main.go @@ -36,12 +36,12 @@ var ( lemonsKey = key.New("ex.com/lemons") anotherKey = key.New("ex.com/another") - oneMetric = metric.NewFloat64Gauge("ex.com/one", + oneMetric = metric.NewGauge("ex.com/one", metric.WithKeys(fooKey, barKey, lemonsKey), metric.WithDescription("A gauge set to 1.0"), ) - measureTwo = metric.NewFloat64Measure("ex.com/two") + measureTwo = metric.NewMeasure("ex.com/two") ) func main() { @@ -64,7 +64,7 @@ func main() { trace.CurrentSpan(ctx).SetAttributes(anotherKey.String("yes")) - gauge.Set(ctx, 1) + gauge.SetFloat64(ctx, 1) meter.RecordBatch( // Note: call-site variables added as context tags: @@ -72,8 +72,8 @@ func main() { tag.Insert(anotherKey.String("xyz"))), commonLabels, - oneMetric.Measurement(1.0), - measureTwo.Measurement(2.0), + oneMetric.Float64Measurement(1.0), + measureTwo.Float64Measurement(2.0), ) return tracer.WithSpan( @@ -84,7 +84,7 @@ func main() { trace.CurrentSpan(ctx).AddEvent(ctx, "Sub span event") - measure.Record(ctx, 1.3) + measure.RecordFloat64(ctx, 1.3) return nil }, diff --git a/experimental/streaming/sdk/metric.go b/experimental/streaming/sdk/metric.go index 6c3c0a572d0..945085fdb1b 100644 --- a/experimental/streaming/sdk/metric.go +++ b/experimental/streaming/sdk/metric.go @@ -36,26 +36,14 @@ type metricLabels struct { var _ metric.LabelSet = &metricLabels{} -func (h *metricHandle) RecordFloat(ctx context.Context, value float64) { +func (h *metricHandle) Record(ctx context.Context, value metric.MeasurementValue) { h.labels.sdk.exporter.Record(exporter.Event{ Type: exporter.SINGLE_METRIC, Context: ctx, Scope: h.labels.scope, Measurement: metric.Measurement{ Descriptor: h.descriptor, - Value: metric.NewFloat64MeasurementValue(value), - }, - }) -} - -func (h *metricHandle) RecordInt(ctx context.Context, value int64) { - h.labels.sdk.exporter.Record(exporter.Event{ - Type: exporter.SINGLE_METRIC, - Context: ctx, - Scope: h.labels.scope, - Measurement: metric.Measurement{ - Descriptor: h.descriptor, - Value: metric.NewInt64MeasurementValue(value), + Value: value, }, }) } From 2b4ff72c2fb232db7f524151dff0e5e849fcad67 Mon Sep 17 00:00:00 2001 From: Krzesimir Nowak Date: Tue, 1 Oct 2019 12:36:12 +0200 Subject: [PATCH 16/35] add observers --- api/metric/api.go | 10 +++- api/metric/kind_string.go | 5 +- api/metric/noop_meter.go | 6 +++ api/metric/observer.go | 24 +++++++++ experimental/streaming/sdk/metric.go | 77 +++++++++++++++++++++++++++ experimental/streaming/sdk/package.go | 19 +++++++ experimental/streaming/sdk/trace.go | 6 --- 7 files changed, 138 insertions(+), 9 deletions(-) create mode 100644 api/metric/observer.go diff --git a/api/metric/api.go b/api/metric/api.go index b4daef2e755..0659b59971a 100644 --- a/api/metric/api.go +++ b/api/metric/api.go @@ -30,6 +30,7 @@ const ( CounterKind // Supports Add() GaugeKind // Supports Set() MeasureKind // Supports Record() + ObserverKind ) // Recorder is the implementation-level interface to Set/Add/Record individual metrics. @@ -49,6 +50,10 @@ type LabelSet interface { Meter() Meter } +// ObserverCallback defines a type of the callback SDK will call for +// the registered observers. +type ObserverCallback func(Meter, Observer) (LabelSet, MeasurementValue) + // 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 @@ -60,6 +65,9 @@ type Meter interface { // RecordBatch atomically records a batch of measurements.. RecordBatch(context.Context, LabelSet, ...Measurement) + + RegisterObserver(Observer, ObserverCallback) + UnregisterObserver(Observer) } type DescriptorID uint64 @@ -92,7 +100,7 @@ type Descriptor struct { // NonMonotonic implies this is an up-down Counter. NonMonotonic bool - // Monotonic implies this is a non-descending Gauge. + // Monotonic implies this is a non-descending Gauge/Observer. Monotonic bool // Signed implies this is a Measure that supports positive and diff --git a/api/metric/kind_string.go b/api/metric/kind_string.go index b64e5dc675d..064cdc7f4f4 100644 --- a/api/metric/kind_string.go +++ b/api/metric/kind_string.go @@ -12,11 +12,12 @@ func _() { _ = x[CounterKind-1] _ = x[GaugeKind-2] _ = x[MeasureKind-3] + _ = x[ObserverKind-4] } -const _Kind_name = "InvalidCounterKindGaugeKindMeasureKind" +const _Kind_name = "InvalidCounterKindGaugeKindMeasureKindObserverKind" -var _Kind_index = [...]uint8{0, 7, 18, 27, 38} +var _Kind_index = [...]uint8{0, 7, 18, 27, 38, 50} func (i Kind) String() string { if i < 0 || i >= Kind(len(_Kind_index)-1) { diff --git a/api/metric/noop_meter.go b/api/metric/noop_meter.go index c765fe6f472..75fa187e95d 100644 --- a/api/metric/noop_meter.go +++ b/api/metric/noop_meter.go @@ -34,3 +34,9 @@ func (noopMeter) DeleteHandle(Handle) { func (noopMeter) RecordBatch(context.Context, LabelSet, ...Measurement) { } + +func (noopMeter) RegisterObserver(Observer, ObserverCallback) { +} + +func (noopMeter) UnregisterObserver(Observer) { +} diff --git a/api/metric/observer.go b/api/metric/observer.go new file mode 100644 index 00000000000..c77f4e5685c --- /dev/null +++ b/api/metric/observer.go @@ -0,0 +1,24 @@ +// 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 + +type Observer struct { + Descriptor +} + +func NewObserver(name string, mos ...Option) (o Observer) { + registerDescriptor(name, ObserverKind, mos, &o.Descriptor) + return +} diff --git a/experimental/streaming/sdk/metric.go b/experimental/streaming/sdk/metric.go index 945085fdb1b..481fa3e9374 100644 --- a/experimental/streaming/sdk/metric.go +++ b/experimental/streaming/sdk/metric.go @@ -16,6 +16,7 @@ package sdk import ( "context" + "time" "go.opentelemetry.io/api/core" "go.opentelemetry.io/api/metric" @@ -88,3 +89,79 @@ func (s *sdk) RecordBatch(ctx context.Context, labels metric.LabelSet, ms ...met Measurements: oms, }) } + +func (s *sdk) RegisterObserver(observer metric.Observer, callback metric.ObserverCallback) { + if s.insertNewObserver(observer, callback) { + go s.observersRoutine() + } +} + +func (s *sdk) insertNewObserver(observer metric.Observer, callback metric.ObserverCallback) bool { + s.observersLock.Lock() + defer s.observersLock.Unlock() + old := s.loadObserversMap() + if _, ok := old[observer.Descriptor.ID]; ok { + return false + } + observers := make(observersMap) + for id, data := range old { + observers[id] = data + } + observers[observer.Descriptor.ID] = observerData{ + observer: observer, + callback: callback, + } + s.storeObserversMap(observers) + return old == nil +} + +func (s *sdk) UnregisterObserver(observer metric.Observer) { + s.observersLock.Lock() + defer s.observersLock.Unlock() + old := s.loadObserversMap() + if _, ok := old[observer.Descriptor.ID]; !ok { + return + } + if len(old) == 1 { + s.storeObserversMap(nil) + return + } + observers := make(observersMap) + for id, data := range old { + if id != observer.Descriptor.ID { + observers[id] = data + } + } + s.observers.Store(observers) +} + +func (s *sdk) observersRoutine() { + ticker := time.NewTicker(time.Second) + defer ticker.Stop() + for range ticker.C { + m := s.loadObserversMap() + if m == nil { + return + } + for _, data := range m { + labels, value := data.callback(s, data.observer) + s.RecordBatch(context.Background(), labels, metric.Measurement{ + Descriptor: data.observer.Descriptor, + Value: value, + }) + } + } +} + +func (s *sdk) loadObserversMap() observersMap { + i := s.observers.Load() + if i == nil { + return nil + } + m := i.(observersMap) + return m +} + +func (s *sdk) storeObserversMap(m observersMap) { + s.observers.Store(m) +} diff --git a/experimental/streaming/sdk/package.go b/experimental/streaming/sdk/package.go index aa0b1eb0fa8..c2606410a8a 100644 --- a/experimental/streaming/sdk/package.go +++ b/experimental/streaming/sdk/package.go @@ -15,14 +15,27 @@ package sdk import ( + "sync" + "sync/atomic" + "go.opentelemetry.io/api/metric" "go.opentelemetry.io/api/trace" "go.opentelemetry.io/experimental/streaming/exporter" ) +type observerData struct { + observer metric.Observer + callback metric.ObserverCallback +} + +type observersMap map[metric.DescriptorID]observerData + type sdk struct { exporter *exporter.Exporter resources exporter.EventID + + observersLock sync.Mutex + observers atomic.Value // observersMap } type SDK interface { @@ -31,3 +44,9 @@ type SDK interface { } var _ SDK = &sdk{} + +func New(observers ...exporter.Observer) SDK { + return &sdk{ + exporter: exporter.NewExporter(observers...), + } +} diff --git a/experimental/streaming/sdk/trace.go b/experimental/streaming/sdk/trace.go index 789c643c99c..5da5834b542 100644 --- a/experimental/streaming/sdk/trace.go +++ b/experimental/streaming/sdk/trace.go @@ -32,12 +32,6 @@ var ( MessageKey = key.New("message") ) -func New(observers ...exporter.Observer) SDK { - return &sdk{ - exporter: exporter.NewExporter(observers...), - } -} - func (s *sdk) WithSpan(ctx context.Context, name string, body func(context.Context) error) error { // TODO: use runtime/trace.WithRegion for execution sdk support // TODO: use runtime/pprof.Do for profile tags support From cb43448cd7143d55d61116fcfb4c52d29bf2025e Mon Sep 17 00:00:00 2001 From: Krzesimir Nowak Date: Tue, 1 Oct 2019 13:44:28 +0200 Subject: [PATCH 17/35] rename a function to avoid confusion between the Handle type and the Measure type --- api/metric/api.go | 2 +- api/metric/counter.go | 2 +- api/metric/gauge.go | 2 +- api/metric/measure.go | 2 +- api/metric/noop_meter.go | 2 +- experimental/streaming/sdk/metric.go | 2 +- 6 files changed, 6 insertions(+), 6 deletions(-) diff --git a/api/metric/api.go b/api/metric/api.go index 0659b59971a..71b00307752 100644 --- a/api/metric/api.go +++ b/api/metric/api.go @@ -36,7 +36,7 @@ const ( // Recorder is the implementation-level interface to Set/Add/Record individual metrics. type Handle interface { // Record allows the SDK to observe a single metric event - Record(ctx context.Context, value MeasurementValue) + RecordOne(ctx context.Context, value MeasurementValue) } // LabelSet represents a []core.KeyValue for use as pre-defined labels diff --git a/api/metric/counter.go b/api/metric/counter.go index 990f7b008d9..f8b49725dcf 100644 --- a/api/metric/counter.go +++ b/api/metric/counter.go @@ -66,7 +66,7 @@ func (c *Counter) AddInt64(ctx context.Context, value int64, labels LabelSet) { } func (h *CounterHandle) Add(ctx context.Context, value MeasurementValue) { - h.Record(ctx, value) + h.RecordOne(ctx, value) } func (h *CounterHandle) AddFloat64(ctx context.Context, value float64) { diff --git a/api/metric/gauge.go b/api/metric/gauge.go index a4caa92d025..52651046837 100644 --- a/api/metric/gauge.go +++ b/api/metric/gauge.go @@ -66,7 +66,7 @@ func (g *Gauge) SetInt64(ctx context.Context, value int64, labels LabelSet) { } func (h *GaugeHandle) Set(ctx context.Context, value MeasurementValue) { - h.Record(ctx, value) + h.RecordOne(ctx, value) } func (h *GaugeHandle) SetFloat64(ctx context.Context, value float64) { diff --git a/api/metric/measure.go b/api/metric/measure.go index deb909bb886..e2edb510785 100644 --- a/api/metric/measure.go +++ b/api/metric/measure.go @@ -66,7 +66,7 @@ func (m *Measure) RecordInt64(ctx context.Context, value int64, labels LabelSet) } func (h *MeasureHandle) Record(ctx context.Context, value MeasurementValue) { - h.Handle.Record(ctx, value) + h.RecordOne(ctx, value) } func (h *MeasureHandle) RecordFloat64(ctx context.Context, value float64) { diff --git a/api/metric/noop_meter.go b/api/metric/noop_meter.go index 75fa187e95d..4cd44441979 100644 --- a/api/metric/noop_meter.go +++ b/api/metric/noop_meter.go @@ -14,7 +14,7 @@ var _ Meter = noopMeter{} var _ Handle = noopHandle{} var _ LabelSet = noopLabelSet{} -func (noopHandle) Record(ctx context.Context, value MeasurementValue) { +func (noopHandle) RecordOne(ctx context.Context, value MeasurementValue) { } func (noopLabelSet) Meter() Meter { diff --git a/experimental/streaming/sdk/metric.go b/experimental/streaming/sdk/metric.go index 481fa3e9374..d41fa39a60f 100644 --- a/experimental/streaming/sdk/metric.go +++ b/experimental/streaming/sdk/metric.go @@ -37,7 +37,7 @@ type metricLabels struct { var _ metric.LabelSet = &metricLabels{} -func (h *metricHandle) Record(ctx context.Context, value metric.MeasurementValue) { +func (h *metricHandle) RecordOne(ctx context.Context, value metric.MeasurementValue) { h.labels.sdk.exporter.Record(exporter.Event{ Type: exporter.SINGLE_METRIC, Context: ctx, From cb91aebdd30607d48033cb89b4bf8b90f7c63aab Mon Sep 17 00:00:00 2001 From: Krzesimir Nowak Date: Tue, 1 Oct 2019 16:27:01 +0200 Subject: [PATCH 18/35] drop disabled field from descriptor --- api/metric/api.go | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/api/metric/api.go b/api/metric/api.go index 71b00307752..e2c93e25fd6 100644 --- a/api/metric/api.go +++ b/api/metric/api.go @@ -94,9 +94,6 @@ type Descriptor struct { // Unit is an optional field describing this metric descriptor. Unit unit.Unit - // Disabled implies this descriptor is disabled by default. - Disabled bool - // NonMonotonic implies this is an up-down Counter. NonMonotonic bool @@ -131,13 +128,6 @@ func WithUnit(unit unit.Unit) Option { } } -// WithDisabled sets whether a metric is disabled by default -func WithDisabled(dis bool) Option { - return func(d *Descriptor) { - d.Disabled = dis - } -} - // WithKeys applies required label keys. Multiple `WithKeys` // options accumulate. func WithKeys(keys ...core.Key) Option { From d933a2d1223d9eb46f464ccab299afb4dcf73d35 Mon Sep 17 00:00:00 2001 From: Krzesimir Nowak Date: Thu, 3 Oct 2019 15:00:56 +0200 Subject: [PATCH 19/35] add back typed API for metrics --- api/metric/api.go | 45 +++++++++++++- api/metric/common.go | 3 +- api/metric/counter.go | 64 +++++++++++++++++--- api/metric/gauge.go | 64 +++++++++++++++++--- api/metric/kind_string.go | 21 ++++++- api/metric/measure.go | 64 +++++++++++++++++--- api/metric/observer.go | 22 ++++++- example/basic/main.go | 12 ++-- experimental/streaming/example/basic/main.go | 12 ++-- 9 files changed, 260 insertions(+), 47 deletions(-) diff --git a/api/metric/api.go b/api/metric/api.go index e2c93e25fd6..2e6585c4cf1 100644 --- a/api/metric/api.go +++ b/api/metric/api.go @@ -12,6 +12,8 @@ // See the License for the specific language governing permissions and // limitations under the License. +//go:generate stringer -type=Kind,MetricValueKind + package metric import ( @@ -24,7 +26,6 @@ import ( // Kind categorizes different kinds of metric. type Kind int -//go:generate stringer -type=Kind const ( Invalid Kind = iota CounterKind // Supports Add() @@ -71,6 +72,13 @@ type Meter interface { } type DescriptorID uint64 +type MetricValueKind int8 + +const ( + AnyValueKind MetricValueKind = iota + Int64ValueKind + Float64ValueKind +) // Descriptor represents a named metric with recommended local-aggregation keys. type Descriptor struct { @@ -94,6 +102,9 @@ type Descriptor struct { // Unit is an optional field describing this metric descriptor. Unit unit.Unit + // ValueKind describes the type of values the metric produces. + ValueKind MetricValueKind + // NonMonotonic implies this is an up-down Counter. NonMonotonic bool @@ -166,3 +177,35 @@ func (d Descriptor) Defined() bool { func RecordBatch(ctx context.Context, labels LabelSet, batch ...Measurement) { GlobalMeter().RecordBatch(ctx, labels, batch...) } + +// Int64ObserverCallback defines a type of the callback SDK will call +// for the registered int64 observers. +type Int64ObserverCallback func(Meter, Int64Observer) (LabelSet, int64) + +func RegisterInt64Observer(meter Meter, observer Int64Observer, callback Int64ObserverCallback) { + cb := func(m Meter, o Observer) (LabelSet, MeasurementValue) { + l, i := callback(m, Int64Observer{o}) + return l, NewInt64MeasurementValue(i) + } + meter.RegisterObserver(observer.Observer, cb) +} + +func UnregisterInt64Observer(meter Meter, observer Int64Observer) { + meter.UnregisterObserver(observer.Observer) +} + +// Float64ObserverCallback defines a type of the callback SDK will +// call for the registered float64 observers. +type Float64ObserverCallback func(Meter, Float64Observer) (LabelSet, float64) + +func RegisterFloat64Observer(meter Meter, observer Float64Observer, callback Float64ObserverCallback) { + cb := func(m Meter, o Observer) (LabelSet, MeasurementValue) { + l, f := callback(m, Float64Observer{o}) + return l, NewFloat64MeasurementValue(f) + } + meter.RegisterObserver(observer.Observer, cb) +} + +func UnregisterFloat64Observer(meter Meter, observer Float64Observer) { + meter.UnregisterObserver(observer.Observer) +} diff --git a/api/metric/common.go b/api/metric/common.go index dc67610ef46..efc26f672b6 100644 --- a/api/metric/common.go +++ b/api/metric/common.go @@ -23,9 +23,10 @@ var ( descriptorID uint64 ) -func registerDescriptor(name string, kind Kind, opts []Option, d *Descriptor) { +func registerDescriptor(name string, kind Kind, valueKind MetricValueKind, opts []Option, d *Descriptor) { d.Name = name d.Kind = kind + d.ValueKind = valueKind d.ID = DescriptorID(atomic.AddUint64(&descriptorID, 1)) for _, opt := range opts { diff --git a/api/metric/counter.go b/api/metric/counter.go index f8b49725dcf..0b9dc598c0d 100644 --- a/api/metric/counter.go +++ b/api/metric/counter.go @@ -22,12 +22,38 @@ type Counter struct { Descriptor } +type Float64Counter struct { + Counter +} + +type Int64Counter struct { + Counter +} + type CounterHandle struct { Handle } -func NewCounter(name string, mos ...Option) (c Counter) { - registerDescriptor(name, CounterKind, mos, &c.Descriptor) +type Float64CounterHandle struct { + CounterHandle +} + +type Int64CounterHandle struct { + CounterHandle +} + +func NewCounter(name string, valueKind MetricValueKind, mos ...Option) (c Counter) { + registerDescriptor(name, CounterKind, valueKind, mos, &c.Descriptor) + return +} + +func NewFloat64Counter(name string, mos ...Option) (c Float64Counter) { + c.Counter = NewCounter(name, Float64ValueKind, mos...) + return +} + +func NewInt64Counter(name string, mos ...Option) (c Int64Counter) { + c.Counter = NewCounter(name, Int64ValueKind, mos...) return } @@ -36,6 +62,16 @@ func (c *Counter) GetHandle(ctx context.Context, labels LabelSet) (h CounterHand return } +func (c *Float64Counter) GetHandle(ctx context.Context, labels LabelSet) (h Float64CounterHandle) { + h.CounterHandle = c.Counter.GetHandle(ctx, labels) + return +} + +func (c *Int64Counter) GetHandle(ctx context.Context, labels LabelSet) (h Int64CounterHandle) { + h.CounterHandle = c.Counter.GetHandle(ctx, labels) + return +} + func (c *Counter) Float64Measurement(value float64) Measurement { return Measurement{ Descriptor: c.Descriptor, @@ -50,6 +86,14 @@ func (c *Counter) Int64Measurement(value int64) Measurement { } } +func (c *Float64Counter) Measurement(value float64) Measurement { + return c.Counter.Float64Measurement(value) +} + +func (c *Int64Counter) Measurement(value int64) Measurement { + return c.Counter.Int64Measurement(value) +} + func (c *Counter) Add(ctx context.Context, value MeasurementValue, labels LabelSet) { labels.Meter().RecordBatch(ctx, labels, Measurement{ Descriptor: c.Descriptor, @@ -57,22 +101,22 @@ func (c *Counter) Add(ctx context.Context, value MeasurementValue, labels LabelS }) } -func (c *Counter) AddFloat64(ctx context.Context, value float64, labels LabelSet) { - c.Add(ctx, NewFloat64MeasurementValue(value), labels) +func (c *Float64Counter) Add(ctx context.Context, value float64, labels LabelSet) { + c.Counter.Add(ctx, NewFloat64MeasurementValue(value), labels) } -func (c *Counter) AddInt64(ctx context.Context, value int64, labels LabelSet) { - c.Add(ctx, NewInt64MeasurementValue(value), labels) +func (c *Int64Counter) Add(ctx context.Context, value int64, labels LabelSet) { + c.Counter.Add(ctx, NewInt64MeasurementValue(value), labels) } func (h *CounterHandle) Add(ctx context.Context, value MeasurementValue) { h.RecordOne(ctx, value) } -func (h *CounterHandle) AddFloat64(ctx context.Context, value float64) { - h.Add(ctx, NewFloat64MeasurementValue(value)) +func (h *Float64CounterHandle) Add(ctx context.Context, value float64) { + h.CounterHandle.Add(ctx, NewFloat64MeasurementValue(value)) } -func (h *CounterHandle) AddInt64(ctx context.Context, value int64) { - h.Add(ctx, NewInt64MeasurementValue(value)) +func (h *Int64CounterHandle) Add(ctx context.Context, value int64) { + h.CounterHandle.Add(ctx, NewInt64MeasurementValue(value)) } diff --git a/api/metric/gauge.go b/api/metric/gauge.go index 52651046837..70b026fc329 100644 --- a/api/metric/gauge.go +++ b/api/metric/gauge.go @@ -22,12 +22,38 @@ type Gauge struct { Descriptor } +type Float64Gauge struct { + Gauge +} + +type Int64Gauge struct { + Gauge +} + type GaugeHandle struct { Handle } -func NewGauge(name string, mos ...Option) (g Gauge) { - registerDescriptor(name, GaugeKind, mos, &g.Descriptor) +type Float64GaugeHandle struct { + GaugeHandle +} + +type Int64GaugeHandle struct { + GaugeHandle +} + +func NewGauge(name string, valueKind MetricValueKind, mos ...Option) (g Gauge) { + registerDescriptor(name, GaugeKind, valueKind, mos, &g.Descriptor) + return +} + +func NewFloat64Gauge(name string, mos ...Option) (g Float64Gauge) { + g.Gauge = NewGauge(name, Float64ValueKind, mos...) + return +} + +func NewInt64Gauge(name string, mos ...Option) (g Int64Gauge) { + g.Gauge = NewGauge(name, Int64ValueKind, mos...) return } @@ -36,6 +62,16 @@ func (g *Gauge) GetHandle(ctx context.Context, labels LabelSet) (h GaugeHandle) return } +func (g *Float64Gauge) GetHandle(ctx context.Context, labels LabelSet) (h Float64GaugeHandle) { + h.GaugeHandle = g.Gauge.GetHandle(ctx, labels) + return +} + +func (g *Int64Gauge) GetHandle(ctx context.Context, labels LabelSet) (h Int64GaugeHandle) { + h.GaugeHandle = g.Gauge.GetHandle(ctx, labels) + return +} + func (g *Gauge) Float64Measurement(value float64) Measurement { return Measurement{ Descriptor: g.Descriptor, @@ -50,6 +86,14 @@ func (g *Gauge) Int64Measurement(value int64) Measurement { } } +func (g *Float64Gauge) Measurement(value float64) Measurement { + return g.Gauge.Float64Measurement(value) +} + +func (g *Int64Gauge) Measurement(value int64) Measurement { + return g.Gauge.Int64Measurement(value) +} + func (g *Gauge) Set(ctx context.Context, value MeasurementValue, labels LabelSet) { labels.Meter().RecordBatch(ctx, labels, Measurement{ Descriptor: g.Descriptor, @@ -57,22 +101,22 @@ func (g *Gauge) Set(ctx context.Context, value MeasurementValue, labels LabelSet }) } -func (g *Gauge) SetFloat64(ctx context.Context, value float64, labels LabelSet) { - g.Set(ctx, NewFloat64MeasurementValue(value), labels) +func (g *Float64Gauge) Set(ctx context.Context, value float64, labels LabelSet) { + g.Gauge.Set(ctx, NewFloat64MeasurementValue(value), labels) } -func (g *Gauge) SetInt64(ctx context.Context, value int64, labels LabelSet) { - g.Set(ctx, NewInt64MeasurementValue(value), labels) +func (g *Int64Gauge) Set(ctx context.Context, value int64, labels LabelSet) { + g.Gauge.Set(ctx, NewInt64MeasurementValue(value), labels) } func (h *GaugeHandle) Set(ctx context.Context, value MeasurementValue) { h.RecordOne(ctx, value) } -func (h *GaugeHandle) SetFloat64(ctx context.Context, value float64) { - h.Set(ctx, NewFloat64MeasurementValue(value)) +func (h *Float64GaugeHandle) Set(ctx context.Context, value float64) { + h.GaugeHandle.Set(ctx, NewFloat64MeasurementValue(value)) } -func (h *GaugeHandle) SetInt64(ctx context.Context, value int64) { - h.Set(ctx, NewInt64MeasurementValue(value)) +func (h *Int64GaugeHandle) Set(ctx context.Context, value int64) { + h.GaugeHandle.Set(ctx, NewInt64MeasurementValue(value)) } diff --git a/api/metric/kind_string.go b/api/metric/kind_string.go index 064cdc7f4f4..3c9fcfcd1b3 100644 --- a/api/metric/kind_string.go +++ b/api/metric/kind_string.go @@ -1,4 +1,4 @@ -// Code generated by "stringer -type=Kind"; DO NOT EDIT. +// Code generated by "stringer -type=Kind,MetricValueKind"; DO NOT EDIT. package metric @@ -25,3 +25,22 @@ func (i Kind) String() string { } 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[AnyValueKind-0] + _ = x[Int64ValueKind-1] + _ = x[Float64ValueKind-2] +} + +const _MetricValueKind_name = "AnyValueKindInt64ValueKindFloat64ValueKind" + +var _MetricValueKind_index = [...]uint8{0, 12, 26, 42} + +func (i MetricValueKind) String() string { + if i < 0 || i >= MetricValueKind(len(_MetricValueKind_index)-1) { + return "MetricValueKind(" + strconv.FormatInt(int64(i), 10) + ")" + } + return _MetricValueKind_name[_MetricValueKind_index[i]:_MetricValueKind_index[i+1]] +} diff --git a/api/metric/measure.go b/api/metric/measure.go index e2edb510785..a9cea0d763f 100644 --- a/api/metric/measure.go +++ b/api/metric/measure.go @@ -22,12 +22,38 @@ type Measure struct { Descriptor } +type Float64Measure struct { + Measure +} + +type Int64Measure struct { + Measure +} + type MeasureHandle struct { Handle } -func NewMeasure(name string, mos ...Option) (m Measure) { - registerDescriptor(name, MeasureKind, mos, &m.Descriptor) +type Float64MeasureHandle struct { + MeasureHandle +} + +type Int64MeasureHandle struct { + MeasureHandle +} + +func NewMeasure(name string, valueKind MetricValueKind, mos ...Option) (m Measure) { + registerDescriptor(name, MeasureKind, valueKind, mos, &m.Descriptor) + return +} + +func NewFloat64Measure(name string, mos ...Option) (c Float64Measure) { + c.Measure = NewMeasure(name, Float64ValueKind, mos...) + return +} + +func NewInt64Measure(name string, mos ...Option) (c Int64Measure) { + c.Measure = NewMeasure(name, Int64ValueKind, mos...) return } @@ -36,6 +62,16 @@ func (m *Measure) GetHandle(ctx context.Context, labels LabelSet) (h MeasureHand return } +func (c *Float64Measure) GetHandle(ctx context.Context, labels LabelSet) (h Float64MeasureHandle) { + h.MeasureHandle = c.Measure.GetHandle(ctx, labels) + return +} + +func (c *Int64Measure) GetHandle(ctx context.Context, labels LabelSet) (h Int64MeasureHandle) { + h.MeasureHandle = c.Measure.GetHandle(ctx, labels) + return +} + func (m *Measure) Float64Measurement(value float64) Measurement { return Measurement{ Descriptor: m.Descriptor, @@ -50,6 +86,14 @@ func (m *Measure) Int64Measurement(value int64) Measurement { } } +func (c *Float64Measure) Measurement(value float64) Measurement { + return c.Measure.Float64Measurement(value) +} + +func (c *Int64Measure) Measurement(value int64) Measurement { + return c.Measure.Int64Measurement(value) +} + func (m *Measure) Record(ctx context.Context, value MeasurementValue, labels LabelSet) { labels.Meter().RecordBatch(ctx, labels, Measurement{ Descriptor: m.Descriptor, @@ -57,22 +101,22 @@ func (m *Measure) Record(ctx context.Context, value MeasurementValue, labels Lab }) } -func (m *Measure) RecordFloat64(ctx context.Context, value float64, labels LabelSet) { - m.Record(ctx, NewFloat64MeasurementValue(value), labels) +func (c *Float64Measure) Record(ctx context.Context, value float64, labels LabelSet) { + c.Measure.Record(ctx, NewFloat64MeasurementValue(value), labels) } -func (m *Measure) RecordInt64(ctx context.Context, value int64, labels LabelSet) { - m.Record(ctx, NewInt64MeasurementValue(value), labels) +func (c *Int64Measure) Record(ctx context.Context, value int64, labels LabelSet) { + c.Measure.Record(ctx, NewInt64MeasurementValue(value), labels) } func (h *MeasureHandle) Record(ctx context.Context, value MeasurementValue) { h.RecordOne(ctx, value) } -func (h *MeasureHandle) RecordFloat64(ctx context.Context, value float64) { - h.Record(ctx, NewFloat64MeasurementValue(value)) +func (h *Float64MeasureHandle) Record(ctx context.Context, value float64) { + h.MeasureHandle.Record(ctx, NewFloat64MeasurementValue(value)) } -func (h *MeasureHandle) RecordInt64(ctx context.Context, value int64) { - h.Record(ctx, NewInt64MeasurementValue(value)) +func (h *Int64MeasureHandle) Record(ctx context.Context, value int64) { + h.MeasureHandle.Record(ctx, NewInt64MeasurementValue(value)) } diff --git a/api/metric/observer.go b/api/metric/observer.go index c77f4e5685c..27c60a2c023 100644 --- a/api/metric/observer.go +++ b/api/metric/observer.go @@ -18,7 +18,25 @@ type Observer struct { Descriptor } -func NewObserver(name string, mos ...Option) (o Observer) { - registerDescriptor(name, ObserverKind, mos, &o.Descriptor) +type Float64Observer struct { + Observer +} + +type Int64Observer struct { + Observer +} + +func NewObserver(name string, valueKind MetricValueKind, mos ...Option) (o Observer) { + registerDescriptor(name, ObserverKind, valueKind, mos, &o.Descriptor) + return +} + +func NewFloat64Observer(name string, mos ...Option) (o Float64Observer) { + o.Observer = NewObserver(name, Float64ValueKind, mos...) + return +} + +func NewInt64Observer(name string, mos ...Option) (o Int64Observer) { + o.Observer = NewObserver(name, Int64ValueKind, mos...) return } diff --git a/example/basic/main.go b/example/basic/main.go index 1bbb222fe11..214b29576df 100644 --- a/example/basic/main.go +++ b/example/basic/main.go @@ -33,12 +33,12 @@ var ( lemonsKey = key.New("ex.com/lemons") anotherKey = key.New("ex.com/another") - oneMetric = metric.NewGauge("ex.com/one", + oneMetric = metric.NewFloat64Gauge("ex.com/one", metric.WithKeys(fooKey, barKey, lemonsKey), metric.WithDescription("A gauge set to 1.0"), ) - measureTwo = metric.NewMeasure("ex.com/two") + measureTwo = metric.NewFloat64Measure("ex.com/two") ) func main() { @@ -61,7 +61,7 @@ func main() { trace.CurrentSpan(ctx).SetAttributes(anotherKey.String("yes")) - gauge.SetInt64(ctx, 1) + gauge.Set(ctx, 1) meter.RecordBatch( // Note: call-site variables added as context tags: @@ -69,8 +69,8 @@ func main() { tag.Insert(anotherKey.String("xyz"))), commonLabels, - oneMetric.Float64Measurement(1.0), - measureTwo.Float64Measurement(2.0), + oneMetric.Measurement(1.0), + measureTwo.Measurement(2.0), ) return tracer.WithSpan( @@ -81,7 +81,7 @@ func main() { trace.CurrentSpan(ctx).AddEvent(ctx, "Sub span event") - measure.RecordFloat64(ctx, 1.3) + measure.Record(ctx, 1.3) return nil }, diff --git a/experimental/streaming/example/basic/main.go b/experimental/streaming/example/basic/main.go index 96ccc2059ea..60b5407c99c 100644 --- a/experimental/streaming/example/basic/main.go +++ b/experimental/streaming/example/basic/main.go @@ -36,12 +36,12 @@ var ( lemonsKey = key.New("ex.com/lemons") anotherKey = key.New("ex.com/another") - oneMetric = metric.NewGauge("ex.com/one", + oneMetric = metric.NewFloat64Gauge("ex.com/one", metric.WithKeys(fooKey, barKey, lemonsKey), metric.WithDescription("A gauge set to 1.0"), ) - measureTwo = metric.NewMeasure("ex.com/two") + measureTwo = metric.NewFloat64Measure("ex.com/two") ) func main() { @@ -64,7 +64,7 @@ func main() { trace.CurrentSpan(ctx).SetAttributes(anotherKey.String("yes")) - gauge.SetFloat64(ctx, 1) + gauge.Set(ctx, 1) meter.RecordBatch( // Note: call-site variables added as context tags: @@ -72,8 +72,8 @@ func main() { tag.Insert(anotherKey.String("xyz"))), commonLabels, - oneMetric.Float64Measurement(1.0), - measureTwo.Float64Measurement(2.0), + oneMetric.Measurement(1.0), + measureTwo.Measurement(2.0), ) return tracer.WithSpan( @@ -84,7 +84,7 @@ func main() { trace.CurrentSpan(ctx).AddEvent(ctx, "Sub span event") - measure.RecordFloat64(ctx, 1.3) + measure.Record(ctx, 1.3) return nil }, From e7188d8747782ef1a5b7381f3c1e902081137abd Mon Sep 17 00:00:00 2001 From: Krzesimir Nowak Date: Thu, 3 Oct 2019 18:34:56 +0200 Subject: [PATCH 20/35] make metric options type safe --- api/metric/api.go | 77 ++++++++++++++++++++++++++++++++---------- api/metric/common.go | 41 ++-------------------- api/metric/counter.go | 25 +++++++++++--- api/metric/gauge.go | 25 +++++++++++--- api/metric/measure.go | 25 +++++++++++--- api/metric/observer.go | 11 +++--- 6 files changed, 131 insertions(+), 73 deletions(-) diff --git a/api/metric/api.go b/api/metric/api.go index 2e6585c4cf1..2c61c5530b3 100644 --- a/api/metric/api.go +++ b/api/metric/api.go @@ -125,46 +125,87 @@ type Measurement struct { // Option supports specifying the various metric options. type Option func(*Descriptor) +type OptionApplier interface { + CounterOptionApplier + GaugeOptionApplier + MeasureOptionApplier + ApplyOption(*Descriptor) +} + +type optionWrapper struct { + F Option +} + +var _ OptionApplier = optionWrapper{} + +func (o optionWrapper) ApplyCounterOption(d *Descriptor) { + o.ApplyOption(d) +} + +func (o optionWrapper) ApplyGaugeOption(d *Descriptor) { + o.ApplyOption(d) +} + +func (o optionWrapper) ApplyMeasureOption(d *Descriptor) { + o.ApplyOption(d) +} + +func (o optionWrapper) ApplyOption(d *Descriptor) { + o.F(d) +} + // WithDescription applies provided description. -func WithDescription(desc string) Option { - return func(d *Descriptor) { - d.Description = desc +func WithDescription(desc string) OptionApplier { + return optionWrapper{ + F: func(d *Descriptor) { + d.Description = desc + }, } } // WithUnit applies provided unit. -func WithUnit(unit unit.Unit) Option { - return func(d *Descriptor) { - d.Unit = unit +func WithUnit(unit unit.Unit) OptionApplier { + return optionWrapper{ + F: func(d *Descriptor) { + d.Unit = unit + }, } } // WithKeys applies required label keys. Multiple `WithKeys` // options accumulate. -func WithKeys(keys ...core.Key) Option { - return func(d *Descriptor) { - d.Keys = append(d.Keys, keys...) +func WithKeys(keys ...core.Key) OptionApplier { + return optionWrapper{ + F: func(d *Descriptor) { + d.Keys = append(d.Keys, keys...) + }, } } // WithNonMonotonic sets whether a counter is permitted to go up AND down. -func WithNonMonotonic(nm bool) Option { - return func(d *Descriptor) { - d.NonMonotonic = nm +func WithNonMonotonic(nm bool) CounterOptionApplier { + return counterOptionWrapper{ + F: func(d *Descriptor) { + d.NonMonotonic = nm + }, } } // WithMonotonic sets whether a gauge is not permitted to go down. -func WithMonotonic(m bool) Option { - return func(d *Descriptor) { - d.Monotonic = m +func WithMonotonic(m bool) GaugeOptionApplier { + return gaugeOptionWrapper{ + F: func(d *Descriptor) { + d.Monotonic = m + }, } } // WithSigned sets whether a measure is permitted to be negative. -func WithSigned(s bool) Option { - return func(d *Descriptor) { - d.Signed = s +func WithSigned(s bool) MeasureOptionApplier { + return measureOptionWrapper{ + F: func(d *Descriptor) { + d.Signed = s + }, } } diff --git a/api/metric/common.go b/api/metric/common.go index efc26f672b6..07fc1cb20dc 100644 --- a/api/metric/common.go +++ b/api/metric/common.go @@ -14,52 +14,15 @@ package metric -import ( - "fmt" - "sync/atomic" -) +import "sync/atomic" var ( descriptorID uint64 ) -func registerDescriptor(name string, kind Kind, valueKind MetricValueKind, opts []Option, d *Descriptor) { +func registerDescriptor(name string, kind Kind, valueKind MetricValueKind, d *Descriptor) { d.Name = name d.Kind = kind d.ValueKind = valueKind d.ID = DescriptorID(atomic.AddUint64(&descriptorID, 1)) - - for _, opt := range opts { - opt(d) - } - ensureValidDescriptor(d) -} - -func ensureValidDescriptor(d *Descriptor) { - checkNonMonotonic := false - checkMonotonic := false - checkSigned := false - switch d.Kind { - case Invalid: - panic("tried to register a metric descriptor with invalid kind") - case CounterKind: - checkMonotonic, checkSigned = true, true - case GaugeKind: - checkNonMonotonic, checkSigned = true, true - case MeasureKind: - checkMonotonic, checkNonMonotonic = true, true - } - if checkNonMonotonic && d.NonMonotonic { - panicBadField(d.Kind, "NonMonotonic") - } - if checkMonotonic && d.Monotonic { - panicBadField(d.Kind, "Monotonic") - } - if checkSigned && d.Signed { - panicBadField(d.Kind, "Signed") - } -} - -func panicBadField(kind Kind, field string) { - panic(fmt.Sprintf("invalid %s descriptor, has set %s field", kind.String(), field)) } diff --git a/api/metric/counter.go b/api/metric/counter.go index 0b9dc598c0d..3c2810affdb 100644 --- a/api/metric/counter.go +++ b/api/metric/counter.go @@ -42,17 +42,34 @@ type Int64CounterHandle struct { CounterHandle } -func NewCounter(name string, valueKind MetricValueKind, mos ...Option) (c Counter) { - registerDescriptor(name, CounterKind, valueKind, mos, &c.Descriptor) +type CounterOptionApplier interface { + ApplyCounterOption(*Descriptor) +} + +type counterOptionWrapper struct { + F Option +} + +var _ CounterOptionApplier = counterOptionWrapper{} + +func (o counterOptionWrapper) ApplyCounterOption(d *Descriptor) { + o.F(d) +} + +func NewCounter(name string, valueKind MetricValueKind, mos ...CounterOptionApplier) (c Counter) { + registerDescriptor(name, CounterKind, valueKind, &c.Descriptor) + for _, opt := range mos { + opt.ApplyCounterOption(&c.Descriptor) + } return } -func NewFloat64Counter(name string, mos ...Option) (c Float64Counter) { +func NewFloat64Counter(name string, mos ...CounterOptionApplier) (c Float64Counter) { c.Counter = NewCounter(name, Float64ValueKind, mos...) return } -func NewInt64Counter(name string, mos ...Option) (c Int64Counter) { +func NewInt64Counter(name string, mos ...CounterOptionApplier) (c Int64Counter) { c.Counter = NewCounter(name, Int64ValueKind, mos...) return } diff --git a/api/metric/gauge.go b/api/metric/gauge.go index 70b026fc329..2bfe005c35f 100644 --- a/api/metric/gauge.go +++ b/api/metric/gauge.go @@ -42,17 +42,34 @@ type Int64GaugeHandle struct { GaugeHandle } -func NewGauge(name string, valueKind MetricValueKind, mos ...Option) (g Gauge) { - registerDescriptor(name, GaugeKind, valueKind, mos, &g.Descriptor) +type GaugeOptionApplier interface { + ApplyGaugeOption(*Descriptor) +} + +type gaugeOptionWrapper struct { + F Option +} + +var _ GaugeOptionApplier = gaugeOptionWrapper{} + +func (o gaugeOptionWrapper) ApplyGaugeOption(d *Descriptor) { + o.F(d) +} + +func NewGauge(name string, valueKind MetricValueKind, mos ...GaugeOptionApplier) (g Gauge) { + registerDescriptor(name, GaugeKind, valueKind, &g.Descriptor) + for _, opt := range mos { + opt.ApplyGaugeOption(&g.Descriptor) + } return } -func NewFloat64Gauge(name string, mos ...Option) (g Float64Gauge) { +func NewFloat64Gauge(name string, mos ...GaugeOptionApplier) (g Float64Gauge) { g.Gauge = NewGauge(name, Float64ValueKind, mos...) return } -func NewInt64Gauge(name string, mos ...Option) (g Int64Gauge) { +func NewInt64Gauge(name string, mos ...GaugeOptionApplier) (g Int64Gauge) { g.Gauge = NewGauge(name, Int64ValueKind, mos...) return } diff --git a/api/metric/measure.go b/api/metric/measure.go index a9cea0d763f..21d49312943 100644 --- a/api/metric/measure.go +++ b/api/metric/measure.go @@ -42,17 +42,34 @@ type Int64MeasureHandle struct { MeasureHandle } -func NewMeasure(name string, valueKind MetricValueKind, mos ...Option) (m Measure) { - registerDescriptor(name, MeasureKind, valueKind, mos, &m.Descriptor) +type MeasureOptionApplier interface { + ApplyMeasureOption(*Descriptor) +} + +type measureOptionWrapper struct { + F Option +} + +var _ MeasureOptionApplier = measureOptionWrapper{} + +func (o measureOptionWrapper) ApplyMeasureOption(d *Descriptor) { + o.F(d) +} + +func NewMeasure(name string, valueKind MetricValueKind, mos ...MeasureOptionApplier) (m Measure) { + registerDescriptor(name, MeasureKind, valueKind, &m.Descriptor) + for _, opt := range mos { + opt.ApplyMeasureOption(&m.Descriptor) + } return } -func NewFloat64Measure(name string, mos ...Option) (c Float64Measure) { +func NewFloat64Measure(name string, mos ...MeasureOptionApplier) (c Float64Measure) { c.Measure = NewMeasure(name, Float64ValueKind, mos...) return } -func NewInt64Measure(name string, mos ...Option) (c Int64Measure) { +func NewInt64Measure(name string, mos ...MeasureOptionApplier) (c Int64Measure) { c.Measure = NewMeasure(name, Int64ValueKind, mos...) return } diff --git a/api/metric/observer.go b/api/metric/observer.go index 27c60a2c023..6b34d4582b3 100644 --- a/api/metric/observer.go +++ b/api/metric/observer.go @@ -26,17 +26,20 @@ type Int64Observer struct { Observer } -func NewObserver(name string, valueKind MetricValueKind, mos ...Option) (o Observer) { - registerDescriptor(name, ObserverKind, valueKind, mos, &o.Descriptor) +func NewObserver(name string, valueKind MetricValueKind, mos ...GaugeOptionApplier) (o Observer) { + registerDescriptor(name, ObserverKind, valueKind, &o.Descriptor) + for _, opt := range mos { + opt.ApplyGaugeOption(&o.Descriptor) + } return } -func NewFloat64Observer(name string, mos ...Option) (o Float64Observer) { +func NewFloat64Observer(name string, mos ...GaugeOptionApplier) (o Float64Observer) { o.Observer = NewObserver(name, Float64ValueKind, mos...) return } -func NewInt64Observer(name string, mos ...Option) (o Int64Observer) { +func NewInt64Observer(name string, mos ...GaugeOptionApplier) (o Int64Observer) { o.Observer = NewObserver(name, Int64ValueKind, mos...) return } From b519b222b9af6828d3008112b9f3771d4acc2fd1 Mon Sep 17 00:00:00 2001 From: Krzesimir Nowak Date: Thu, 3 Oct 2019 18:38:34 +0200 Subject: [PATCH 21/35] merge alternatives into a single bool --- api/metric/api.go | 25 +++++++++++++------------ 1 file changed, 13 insertions(+), 12 deletions(-) diff --git a/api/metric/api.go b/api/metric/api.go index 2c61c5530b3..88903e2551b 100644 --- a/api/metric/api.go +++ b/api/metric/api.go @@ -105,15 +105,16 @@ type Descriptor struct { // ValueKind describes the type of values the metric produces. ValueKind MetricValueKind - // NonMonotonic implies this is an up-down Counter. - NonMonotonic bool - - // Monotonic implies this is a non-descending Gauge/Observer. - Monotonic bool - - // Signed implies this is a Measure that supports positive and - // negative values. - Signed bool + // Meaning depends on the metric type: + // + // - for Counter it implies that this is an up-down Counter + // + // - for Gauge/Observer it implies that this is non-descending + // Gauge/Observer + // + // - for Measure it implies that it supports positive and + // negative values + Alternate bool } // Measurement is used for reporting a batch of metric values. @@ -186,7 +187,7 @@ func WithKeys(keys ...core.Key) OptionApplier { func WithNonMonotonic(nm bool) CounterOptionApplier { return counterOptionWrapper{ F: func(d *Descriptor) { - d.NonMonotonic = nm + d.Alternate = nm }, } } @@ -195,7 +196,7 @@ func WithNonMonotonic(nm bool) CounterOptionApplier { func WithMonotonic(m bool) GaugeOptionApplier { return gaugeOptionWrapper{ F: func(d *Descriptor) { - d.Monotonic = m + d.Alternate = m }, } } @@ -204,7 +205,7 @@ func WithMonotonic(m bool) GaugeOptionApplier { func WithSigned(s bool) MeasureOptionApplier { return measureOptionWrapper{ F: func(d *Descriptor) { - d.Signed = s + d.Alternate = s }, } } From 89f13f0904c4dcce227be6a018f1d4109402a9ba Mon Sep 17 00:00:00 2001 From: Krzesimir Nowak Date: Fri, 4 Oct 2019 21:24:05 +0200 Subject: [PATCH 22/35] make value kind name less stuttery --- api/metric/api.go | 8 ++++---- api/metric/common.go | 2 +- api/metric/counter.go | 2 +- api/metric/gauge.go | 2 +- api/metric/kind_string.go | 14 +++++++------- api/metric/measure.go | 2 +- api/metric/observer.go | 2 +- 7 files changed, 16 insertions(+), 16 deletions(-) diff --git a/api/metric/api.go b/api/metric/api.go index 88903e2551b..ad249857a85 100644 --- a/api/metric/api.go +++ b/api/metric/api.go @@ -12,7 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -//go:generate stringer -type=Kind,MetricValueKind +//go:generate stringer -type=Kind,ValueKind package metric @@ -72,10 +72,10 @@ type Meter interface { } type DescriptorID uint64 -type MetricValueKind int8 +type ValueKind int8 const ( - AnyValueKind MetricValueKind = iota + AnyValueKind ValueKind = iota Int64ValueKind Float64ValueKind ) @@ -103,7 +103,7 @@ type Descriptor struct { Unit unit.Unit // ValueKind describes the type of values the metric produces. - ValueKind MetricValueKind + ValueKind ValueKind // Meaning depends on the metric type: // diff --git a/api/metric/common.go b/api/metric/common.go index 07fc1cb20dc..9c0fb910206 100644 --- a/api/metric/common.go +++ b/api/metric/common.go @@ -20,7 +20,7 @@ var ( descriptorID uint64 ) -func registerDescriptor(name string, kind Kind, valueKind MetricValueKind, d *Descriptor) { +func registerDescriptor(name string, kind Kind, valueKind ValueKind, d *Descriptor) { d.Name = name d.Kind = kind d.ValueKind = valueKind diff --git a/api/metric/counter.go b/api/metric/counter.go index 3c2810affdb..b755a7b6d9f 100644 --- a/api/metric/counter.go +++ b/api/metric/counter.go @@ -56,7 +56,7 @@ func (o counterOptionWrapper) ApplyCounterOption(d *Descriptor) { o.F(d) } -func NewCounter(name string, valueKind MetricValueKind, mos ...CounterOptionApplier) (c Counter) { +func NewCounter(name string, valueKind ValueKind, mos ...CounterOptionApplier) (c Counter) { registerDescriptor(name, CounterKind, valueKind, &c.Descriptor) for _, opt := range mos { opt.ApplyCounterOption(&c.Descriptor) diff --git a/api/metric/gauge.go b/api/metric/gauge.go index 2bfe005c35f..d51f3fb52d2 100644 --- a/api/metric/gauge.go +++ b/api/metric/gauge.go @@ -56,7 +56,7 @@ func (o gaugeOptionWrapper) ApplyGaugeOption(d *Descriptor) { o.F(d) } -func NewGauge(name string, valueKind MetricValueKind, mos ...GaugeOptionApplier) (g Gauge) { +func NewGauge(name string, valueKind ValueKind, mos ...GaugeOptionApplier) (g Gauge) { registerDescriptor(name, GaugeKind, valueKind, &g.Descriptor) for _, opt := range mos { opt.ApplyGaugeOption(&g.Descriptor) diff --git a/api/metric/kind_string.go b/api/metric/kind_string.go index 3c9fcfcd1b3..fce98da324d 100644 --- a/api/metric/kind_string.go +++ b/api/metric/kind_string.go @@ -1,4 +1,4 @@ -// Code generated by "stringer -type=Kind,MetricValueKind"; DO NOT EDIT. +// Code generated by "stringer -type=Kind,ValueKind"; DO NOT EDIT. package metric @@ -34,13 +34,13 @@ func _() { _ = x[Float64ValueKind-2] } -const _MetricValueKind_name = "AnyValueKindInt64ValueKindFloat64ValueKind" +const _ValueKind_name = "AnyValueKindInt64ValueKindFloat64ValueKind" -var _MetricValueKind_index = [...]uint8{0, 12, 26, 42} +var _ValueKind_index = [...]uint8{0, 12, 26, 42} -func (i MetricValueKind) String() string { - if i < 0 || i >= MetricValueKind(len(_MetricValueKind_index)-1) { - return "MetricValueKind(" + strconv.FormatInt(int64(i), 10) + ")" +func (i ValueKind) String() string { + if i < 0 || i >= ValueKind(len(_ValueKind_index)-1) { + return "ValueKind(" + strconv.FormatInt(int64(i), 10) + ")" } - return _MetricValueKind_name[_MetricValueKind_index[i]:_MetricValueKind_index[i+1]] + return _ValueKind_name[_ValueKind_index[i]:_ValueKind_index[i+1]] } diff --git a/api/metric/measure.go b/api/metric/measure.go index 21d49312943..a6c7cfdb083 100644 --- a/api/metric/measure.go +++ b/api/metric/measure.go @@ -56,7 +56,7 @@ func (o measureOptionWrapper) ApplyMeasureOption(d *Descriptor) { o.F(d) } -func NewMeasure(name string, valueKind MetricValueKind, mos ...MeasureOptionApplier) (m Measure) { +func NewMeasure(name string, valueKind ValueKind, mos ...MeasureOptionApplier) (m Measure) { registerDescriptor(name, MeasureKind, valueKind, &m.Descriptor) for _, opt := range mos { opt.ApplyMeasureOption(&m.Descriptor) diff --git a/api/metric/observer.go b/api/metric/observer.go index 6b34d4582b3..cbb12111385 100644 --- a/api/metric/observer.go +++ b/api/metric/observer.go @@ -26,7 +26,7 @@ type Int64Observer struct { Observer } -func NewObserver(name string, valueKind MetricValueKind, mos ...GaugeOptionApplier) (o Observer) { +func NewObserver(name string, valueKind ValueKind, mos ...GaugeOptionApplier) (o Observer) { registerDescriptor(name, ObserverKind, valueKind, &o.Descriptor) for _, opt := range mos { opt.ApplyGaugeOption(&o.Descriptor) From f405bf45552514af8c0e61b8c4597031d2d2290e Mon Sep 17 00:00:00 2001 From: Krzesimir Nowak Date: Fri, 4 Oct 2019 21:54:49 +0200 Subject: [PATCH 23/35] fix observation callback prototype --- api/metric/api.go | 34 ++++++++++++++++++++-------- experimental/streaming/sdk/metric.go | 16 +++++++++---- 2 files changed, 36 insertions(+), 14 deletions(-) diff --git a/api/metric/api.go b/api/metric/api.go index ad249857a85..a3066669fa4 100644 --- a/api/metric/api.go +++ b/api/metric/api.go @@ -51,9 +51,13 @@ type LabelSet interface { Meter() Meter } +// ObservationCallback defines a type of the callback the observer +// will use to report the measurement +type ObservationCallback func(LabelSet, MeasurementValue) + // ObserverCallback defines a type of the callback SDK will call for // the registered observers. -type ObserverCallback func(Meter, Observer) (LabelSet, MeasurementValue) +type ObserverCallback func(Meter, Observer, ObservationCallback) // Meter is an interface to the metrics portion of the OpenTelemetry SDK. type Meter interface { @@ -220,14 +224,20 @@ func RecordBatch(ctx context.Context, labels LabelSet, batch ...Measurement) { GlobalMeter().RecordBatch(ctx, labels, batch...) } +// Int64ObservationCallback defines a type of the callback the +// observer will use to report the int64 measurement. +type Int64ObservationCallback func(LabelSet, int64) + // Int64ObserverCallback defines a type of the callback SDK will call // for the registered int64 observers. -type Int64ObserverCallback func(Meter, Int64Observer) (LabelSet, int64) +type Int64ObserverCallback func(Meter, Int64Observer, Int64ObservationCallback) func RegisterInt64Observer(meter Meter, observer Int64Observer, callback Int64ObserverCallback) { - cb := func(m Meter, o Observer) (LabelSet, MeasurementValue) { - l, i := callback(m, Int64Observer{o}) - return l, NewInt64MeasurementValue(i) + cb := func(m Meter, o Observer, ocb ObservationCallback) { + iocb := func(l LabelSet, i int64) { + ocb(l, NewInt64MeasurementValue(i)) + } + callback(m, Int64Observer{o}, iocb) } meter.RegisterObserver(observer.Observer, cb) } @@ -236,14 +246,20 @@ func UnregisterInt64Observer(meter Meter, observer Int64Observer) { meter.UnregisterObserver(observer.Observer) } +// Float64ObservationCallback defines a type of the callback the +// observer will use to report the float64 measurement. +type Float64ObservationCallback func(LabelSet, float64) + // Float64ObserverCallback defines a type of the callback SDK will // call for the registered float64 observers. -type Float64ObserverCallback func(Meter, Float64Observer) (LabelSet, float64) +type Float64ObserverCallback func(Meter, Float64Observer, Float64ObservationCallback) func RegisterFloat64Observer(meter Meter, observer Float64Observer, callback Float64ObserverCallback) { - cb := func(m Meter, o Observer) (LabelSet, MeasurementValue) { - l, f := callback(m, Float64Observer{o}) - return l, NewFloat64MeasurementValue(f) + cb := func(m Meter, o Observer, ocb ObservationCallback) { + focb := func(l LabelSet, f float64) { + ocb(l, NewFloat64MeasurementValue(f)) + } + callback(m, Float64Observer{o}, focb) } meter.RegisterObserver(observer.Observer, cb) } diff --git a/experimental/streaming/sdk/metric.go b/experimental/streaming/sdk/metric.go index d41fa39a60f..0c23de3ef34 100644 --- a/experimental/streaming/sdk/metric.go +++ b/experimental/streaming/sdk/metric.go @@ -144,15 +144,21 @@ func (s *sdk) observersRoutine() { return } for _, data := range m { - labels, value := data.callback(s, data.observer) - s.RecordBatch(context.Background(), labels, metric.Measurement{ - Descriptor: data.observer.Descriptor, - Value: value, - }) + ocb := s.getObservationCallback(data.observer.Descriptor) + data.callback(s, data.observer, ocb) } } } +func (s *sdk) getObservationCallback(descriptor metric.Descriptor) metric.ObservationCallback { + return func(l metric.LabelSet, v metric.MeasurementValue) { + s.RecordBatch(context.Background(), l, metric.Measurement{ + Descriptor: descriptor, + Value: v, + }) + } +} + func (s *sdk) loadObserversMap() observersMap { i := s.observers.Load() if i == nil { From 760906f57b78f20ede124b2a457e394ea068015b Mon Sep 17 00:00:00 2001 From: Krzesimir Nowak Date: Fri, 4 Oct 2019 22:03:23 +0200 Subject: [PATCH 24/35] drop context parameter from NewHandle --- api/metric/api.go | 2 +- api/metric/counter.go | 12 ++++++------ api/metric/gauge.go | 12 ++++++------ api/metric/measure.go | 12 ++++++------ api/metric/noop_meter.go | 2 +- example/basic/main.go | 4 ++-- experimental/streaming/example/basic/main.go | 4 ++-- experimental/streaming/sdk/metric.go | 2 +- 8 files changed, 25 insertions(+), 25 deletions(-) diff --git a/api/metric/api.go b/api/metric/api.go index a3066669fa4..a09be229875 100644 --- a/api/metric/api.go +++ b/api/metric/api.go @@ -65,7 +65,7 @@ type Meter interface { // cannot be read by the application. DefineLabels(context.Context, ...core.KeyValue) LabelSet - NewHandle(context.Context, Descriptor, LabelSet) Handle + NewHandle(Descriptor, LabelSet) Handle DeleteHandle(Handle) // RecordBatch atomically records a batch of measurements.. diff --git a/api/metric/counter.go b/api/metric/counter.go index b755a7b6d9f..0dd003bc97e 100644 --- a/api/metric/counter.go +++ b/api/metric/counter.go @@ -74,18 +74,18 @@ func NewInt64Counter(name string, mos ...CounterOptionApplier) (c Int64Counter) return } -func (c *Counter) GetHandle(ctx context.Context, labels LabelSet) (h CounterHandle) { - h.Handle = labels.Meter().NewHandle(ctx, c.Descriptor, labels) +func (c *Counter) GetHandle(labels LabelSet) (h CounterHandle) { + h.Handle = labels.Meter().NewHandle(c.Descriptor, labels) return } -func (c *Float64Counter) GetHandle(ctx context.Context, labels LabelSet) (h Float64CounterHandle) { - h.CounterHandle = c.Counter.GetHandle(ctx, labels) +func (c *Float64Counter) GetHandle(labels LabelSet) (h Float64CounterHandle) { + h.CounterHandle = c.Counter.GetHandle(labels) return } -func (c *Int64Counter) GetHandle(ctx context.Context, labels LabelSet) (h Int64CounterHandle) { - h.CounterHandle = c.Counter.GetHandle(ctx, labels) +func (c *Int64Counter) GetHandle(labels LabelSet) (h Int64CounterHandle) { + h.CounterHandle = c.Counter.GetHandle(labels) return } diff --git a/api/metric/gauge.go b/api/metric/gauge.go index d51f3fb52d2..6f74cd7729d 100644 --- a/api/metric/gauge.go +++ b/api/metric/gauge.go @@ -74,18 +74,18 @@ func NewInt64Gauge(name string, mos ...GaugeOptionApplier) (g Int64Gauge) { return } -func (g *Gauge) GetHandle(ctx context.Context, labels LabelSet) (h GaugeHandle) { - h.Handle = labels.Meter().NewHandle(ctx, g.Descriptor, labels) +func (g *Gauge) GetHandle(labels LabelSet) (h GaugeHandle) { + h.Handle = labels.Meter().NewHandle(g.Descriptor, labels) return } -func (g *Float64Gauge) GetHandle(ctx context.Context, labels LabelSet) (h Float64GaugeHandle) { - h.GaugeHandle = g.Gauge.GetHandle(ctx, labels) +func (g *Float64Gauge) GetHandle(labels LabelSet) (h Float64GaugeHandle) { + h.GaugeHandle = g.Gauge.GetHandle(labels) return } -func (g *Int64Gauge) GetHandle(ctx context.Context, labels LabelSet) (h Int64GaugeHandle) { - h.GaugeHandle = g.Gauge.GetHandle(ctx, labels) +func (g *Int64Gauge) GetHandle(labels LabelSet) (h Int64GaugeHandle) { + h.GaugeHandle = g.Gauge.GetHandle(labels) return } diff --git a/api/metric/measure.go b/api/metric/measure.go index a6c7cfdb083..4c4a8a68300 100644 --- a/api/metric/measure.go +++ b/api/metric/measure.go @@ -74,18 +74,18 @@ func NewInt64Measure(name string, mos ...MeasureOptionApplier) (c Int64Measure) return } -func (m *Measure) GetHandle(ctx context.Context, labels LabelSet) (h MeasureHandle) { - h.Handle = labels.Meter().NewHandle(ctx, m.Descriptor, labels) +func (m *Measure) GetHandle(labels LabelSet) (h MeasureHandle) { + h.Handle = labels.Meter().NewHandle(m.Descriptor, labels) return } -func (c *Float64Measure) GetHandle(ctx context.Context, labels LabelSet) (h Float64MeasureHandle) { - h.MeasureHandle = c.Measure.GetHandle(ctx, labels) +func (c *Float64Measure) GetHandle(labels LabelSet) (h Float64MeasureHandle) { + h.MeasureHandle = c.Measure.GetHandle(labels) return } -func (c *Int64Measure) GetHandle(ctx context.Context, labels LabelSet) (h Int64MeasureHandle) { - h.MeasureHandle = c.Measure.GetHandle(ctx, labels) +func (c *Int64Measure) GetHandle(labels LabelSet) (h Int64MeasureHandle) { + h.MeasureHandle = c.Measure.GetHandle(labels) return } diff --git a/api/metric/noop_meter.go b/api/metric/noop_meter.go index 4cd44441979..07a9ea81197 100644 --- a/api/metric/noop_meter.go +++ b/api/metric/noop_meter.go @@ -25,7 +25,7 @@ func (noopMeter) DefineLabels(ctx context.Context, labels ...core.KeyValue) Labe return noopLabelSet{} } -func (noopMeter) NewHandle(context.Context, Descriptor, LabelSet) Handle { +func (noopMeter) NewHandle(Descriptor, LabelSet) Handle { return noopHandle{} } diff --git a/example/basic/main.go b/example/basic/main.go index 214b29576df..431386f2890 100644 --- a/example/basic/main.go +++ b/example/basic/main.go @@ -51,9 +51,9 @@ func main() { commonLabels := meter.DefineLabels(ctx, lemonsKey.Int(10)) - gauge := oneMetric.GetHandle(ctx, commonLabels) + gauge := oneMetric.GetHandle(commonLabels) - measure := measureTwo.GetHandle(ctx, commonLabels) + measure := measureTwo.GetHandle(commonLabels) err := tracer.WithSpan(ctx, "operation", func(ctx context.Context) error { diff --git a/experimental/streaming/example/basic/main.go b/experimental/streaming/example/basic/main.go index 60b5407c99c..42f9a70b2a5 100644 --- a/experimental/streaming/example/basic/main.go +++ b/experimental/streaming/example/basic/main.go @@ -54,9 +54,9 @@ func main() { commonLabels := meter.DefineLabels(ctx, lemonsKey.Int(10)) - gauge := oneMetric.GetHandle(ctx, commonLabels) + gauge := oneMetric.GetHandle(commonLabels) - measure := measureTwo.GetHandle(ctx, commonLabels) + measure := measureTwo.GetHandle(commonLabels) err := tracer.WithSpan(ctx, "operation", func(ctx context.Context) error { diff --git a/experimental/streaming/sdk/metric.go b/experimental/streaming/sdk/metric.go index 0c23de3ef34..34db3b70be2 100644 --- a/experimental/streaming/sdk/metric.go +++ b/experimental/streaming/sdk/metric.go @@ -60,7 +60,7 @@ func (s *sdk) DefineLabels(ctx context.Context, labels ...core.KeyValue) metric. } } -func (s *sdk) NewHandle(ctx context.Context, descriptor metric.Descriptor, labels metric.LabelSet) metric.Handle { +func (s *sdk) NewHandle(descriptor metric.Descriptor, labels metric.LabelSet) metric.Handle { mlabels, _ := labels.(metricLabels) return &metricHandle{ From 1c8e7e76e0eff933c68c9cb78e7ea8c750d3ff4c Mon Sep 17 00:00:00 2001 From: Krzesimir Nowak Date: Fri, 4 Oct 2019 22:03:33 +0200 Subject: [PATCH 25/35] drop useless parameter names --- api/metric/noop_meter.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/api/metric/noop_meter.go b/api/metric/noop_meter.go index 07a9ea81197..2d75c49807f 100644 --- a/api/metric/noop_meter.go +++ b/api/metric/noop_meter.go @@ -14,14 +14,14 @@ var _ Meter = noopMeter{} var _ Handle = noopHandle{} var _ LabelSet = noopLabelSet{} -func (noopHandle) RecordOne(ctx context.Context, value MeasurementValue) { +func (noopHandle) RecordOne(context.Context, MeasurementValue) { } func (noopLabelSet) Meter() Meter { return noopMeter{} } -func (noopMeter) DefineLabels(ctx context.Context, labels ...core.KeyValue) LabelSet { +func (noopMeter) DefineLabels(context.Context, ...core.KeyValue) LabelSet { return noopLabelSet{} } From bfca093ce9221bcbf48b385ad1e8f125df849608 Mon Sep 17 00:00:00 2001 From: Krzesimir Nowak Date: Fri, 4 Oct 2019 22:25:49 +0200 Subject: [PATCH 26/35] make descriptor an opaque struct --- api/metric/api.go | 112 +++++++++++------- api/metric/common.go | 12 +- api/metric/counter.go | 6 +- api/metric/gauge.go | 6 +- api/metric/measure.go | 6 +- api/metric/noop_meter.go | 2 +- api/metric/observer.go | 6 +- .../exporter/reader/format/format.go | 4 +- experimental/streaming/sdk/metric.go | 24 ++-- 9 files changed, 104 insertions(+), 74 deletions(-) diff --git a/api/metric/api.go b/api/metric/api.go index a09be229875..3147aa3bd48 100644 --- a/api/metric/api.go +++ b/api/metric/api.go @@ -65,7 +65,7 @@ type Meter interface { // cannot be read by the application. DefineLabels(context.Context, ...core.KeyValue) LabelSet - NewHandle(Descriptor, LabelSet) Handle + NewHandle(*Descriptor, LabelSet) Handle DeleteHandle(Handle) // RecordBatch atomically records a batch of measurements.. @@ -84,46 +84,72 @@ const ( Float64ValueKind ) -// Descriptor represents a named metric with recommended local-aggregation keys. +// Descriptor represents a named metric with recommended +// local-aggregation keys. type Descriptor struct { - // Name is a required field describing this metric descriptor, - // should have length > 0. - Name string - - // Kind is the metric kind of this descriptor. - Kind Kind - - // Keys are required keys determined in the handles - // obtained for this metric. - Keys []core.Key - - // ID is uniquely assigned to support per-SDK registration. - ID DescriptorID - - // Description is an optional field describing this metric descriptor. - Description string - - // Unit is an optional field describing this metric descriptor. - Unit unit.Unit - - // ValueKind describes the type of values the metric produces. - ValueKind ValueKind - - // Meaning depends on the metric type: - // - // - for Counter it implies that this is an up-down Counter - // - // - for Gauge/Observer it implies that this is non-descending - // Gauge/Observer - // - // - for Measure it implies that it supports positive and - // negative values - Alternate bool + 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 +} + +// Meaning depends on the metric type: +// +// - for Counter it implies that this is an up-down Counter +// +// - for Gauge/Observer it implies that this is non-descending +// Gauge/Observer +// +// - for Measure it implies that it 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 + Descriptor *Descriptor Value MeasurementValue } @@ -163,7 +189,7 @@ func (o optionWrapper) ApplyOption(d *Descriptor) { func WithDescription(desc string) OptionApplier { return optionWrapper{ F: func(d *Descriptor) { - d.Description = desc + d.description = desc }, } } @@ -172,7 +198,7 @@ func WithDescription(desc string) OptionApplier { func WithUnit(unit unit.Unit) OptionApplier { return optionWrapper{ F: func(d *Descriptor) { - d.Unit = unit + d.unit = unit }, } } @@ -182,7 +208,7 @@ func WithUnit(unit unit.Unit) OptionApplier { func WithKeys(keys ...core.Key) OptionApplier { return optionWrapper{ F: func(d *Descriptor) { - d.Keys = append(d.Keys, keys...) + d.keys = append(d.keys, keys...) }, } } @@ -191,7 +217,7 @@ func WithKeys(keys ...core.Key) OptionApplier { func WithNonMonotonic(nm bool) CounterOptionApplier { return counterOptionWrapper{ F: func(d *Descriptor) { - d.Alternate = nm + d.alternate = nm }, } } @@ -200,7 +226,7 @@ func WithNonMonotonic(nm bool) CounterOptionApplier { func WithMonotonic(m bool) GaugeOptionApplier { return gaugeOptionWrapper{ F: func(d *Descriptor) { - d.Alternate = m + d.alternate = m }, } } @@ -209,14 +235,14 @@ func WithMonotonic(m bool) GaugeOptionApplier { func WithSigned(s bool) MeasureOptionApplier { return measureOptionWrapper{ F: func(d *Descriptor) { - d.Alternate = s + d.alternate = s }, } } // Defined returns true when the descriptor has been registered. func (d Descriptor) Defined() bool { - return len(d.Name) != 0 + return len(d.name) != 0 } // RecordBatch reports to the global Meter. diff --git a/api/metric/common.go b/api/metric/common.go index 9c0fb910206..0201b45d0c3 100644 --- a/api/metric/common.go +++ b/api/metric/common.go @@ -20,9 +20,11 @@ var ( descriptorID uint64 ) -func registerDescriptor(name string, kind Kind, valueKind ValueKind, d *Descriptor) { - d.Name = name - d.Kind = kind - d.ValueKind = valueKind - d.ID = DescriptorID(atomic.AddUint64(&descriptorID, 1)) +func registerDescriptor(name string, kind Kind, valueKind ValueKind) *Descriptor { + return &Descriptor{ + name: name, + kind: kind, + valueKind: valueKind, + id: DescriptorID(atomic.AddUint64(&descriptorID, 1)), + } } diff --git a/api/metric/counter.go b/api/metric/counter.go index 0dd003bc97e..e7d57e48037 100644 --- a/api/metric/counter.go +++ b/api/metric/counter.go @@ -19,7 +19,7 @@ import ( ) type Counter struct { - Descriptor + *Descriptor } type Float64Counter struct { @@ -57,9 +57,9 @@ func (o counterOptionWrapper) ApplyCounterOption(d *Descriptor) { } func NewCounter(name string, valueKind ValueKind, mos ...CounterOptionApplier) (c Counter) { - registerDescriptor(name, CounterKind, valueKind, &c.Descriptor) + c.Descriptor = registerDescriptor(name, CounterKind, valueKind) for _, opt := range mos { - opt.ApplyCounterOption(&c.Descriptor) + opt.ApplyCounterOption(c.Descriptor) } return } diff --git a/api/metric/gauge.go b/api/metric/gauge.go index 6f74cd7729d..3a7f2b66f65 100644 --- a/api/metric/gauge.go +++ b/api/metric/gauge.go @@ -19,7 +19,7 @@ import ( ) type Gauge struct { - Descriptor + *Descriptor } type Float64Gauge struct { @@ -57,9 +57,9 @@ func (o gaugeOptionWrapper) ApplyGaugeOption(d *Descriptor) { } func NewGauge(name string, valueKind ValueKind, mos ...GaugeOptionApplier) (g Gauge) { - registerDescriptor(name, GaugeKind, valueKind, &g.Descriptor) + g.Descriptor = registerDescriptor(name, GaugeKind, valueKind) for _, opt := range mos { - opt.ApplyGaugeOption(&g.Descriptor) + opt.ApplyGaugeOption(g.Descriptor) } return } diff --git a/api/metric/measure.go b/api/metric/measure.go index 4c4a8a68300..9350a5495d4 100644 --- a/api/metric/measure.go +++ b/api/metric/measure.go @@ -19,7 +19,7 @@ import ( ) type Measure struct { - Descriptor + *Descriptor } type Float64Measure struct { @@ -57,9 +57,9 @@ func (o measureOptionWrapper) ApplyMeasureOption(d *Descriptor) { } func NewMeasure(name string, valueKind ValueKind, mos ...MeasureOptionApplier) (m Measure) { - registerDescriptor(name, MeasureKind, valueKind, &m.Descriptor) + m.Descriptor = registerDescriptor(name, MeasureKind, valueKind) for _, opt := range mos { - opt.ApplyMeasureOption(&m.Descriptor) + opt.ApplyMeasureOption(m.Descriptor) } return } diff --git a/api/metric/noop_meter.go b/api/metric/noop_meter.go index 2d75c49807f..2dda662666d 100644 --- a/api/metric/noop_meter.go +++ b/api/metric/noop_meter.go @@ -25,7 +25,7 @@ func (noopMeter) DefineLabels(context.Context, ...core.KeyValue) LabelSet { return noopLabelSet{} } -func (noopMeter) NewHandle(Descriptor, LabelSet) Handle { +func (noopMeter) NewHandle(*Descriptor, LabelSet) Handle { return noopHandle{} } diff --git a/api/metric/observer.go b/api/metric/observer.go index cbb12111385..318fd1940cd 100644 --- a/api/metric/observer.go +++ b/api/metric/observer.go @@ -15,7 +15,7 @@ package metric type Observer struct { - Descriptor + *Descriptor } type Float64Observer struct { @@ -27,9 +27,9 @@ type Int64Observer struct { } func NewObserver(name string, valueKind ValueKind, mos ...GaugeOptionApplier) (o Observer) { - registerDescriptor(name, ObserverKind, valueKind, &o.Descriptor) + o.Descriptor = registerDescriptor(name, ObserverKind, valueKind) for _, opt := range mos { - opt.ApplyGaugeOption(&o.Descriptor) + opt.ApplyGaugeOption(o.Descriptor) } return } diff --git a/experimental/streaming/exporter/reader/format/format.go b/experimental/streaming/exporter/reader/format/format.go index 3bbcaa193f7..1f456ce0e08 100644 --- a/experimental/streaming/exporter/reader/format/format.go +++ b/experimental/streaming/exporter/reader/format/format.go @@ -135,9 +135,9 @@ func AppendEvent(buf *strings.Builder, data reader.Event) { } func formatMetricUpdate(buf *strings.Builder, m metric.Measurement) { - buf.WriteString(m.Descriptor.Kind.String()) + buf.WriteString(m.Descriptor.Kind().String()) buf.WriteString(" ") - buf.WriteString(m.Descriptor.Name) + buf.WriteString(m.Descriptor.Name()) buf.WriteString("=") buf.WriteString(m.Value.Emit()) } diff --git a/experimental/streaming/sdk/metric.go b/experimental/streaming/sdk/metric.go index 34db3b70be2..0b47fadb3f6 100644 --- a/experimental/streaming/sdk/metric.go +++ b/experimental/streaming/sdk/metric.go @@ -24,7 +24,7 @@ import ( ) type metricHandle struct { - descriptor metric.Descriptor + descriptor *metric.Descriptor labels metricLabels } @@ -60,7 +60,7 @@ func (s *sdk) DefineLabels(ctx context.Context, labels ...core.KeyValue) metric. } } -func (s *sdk) NewHandle(descriptor metric.Descriptor, labels metric.LabelSet) metric.Handle { +func (s *sdk) NewHandle(descriptor *metric.Descriptor, labels metric.LabelSet) metric.Handle { mlabels, _ := labels.(metricLabels) return &metricHandle{ @@ -100,14 +100,15 @@ func (s *sdk) insertNewObserver(observer metric.Observer, callback metric.Observ s.observersLock.Lock() defer s.observersLock.Unlock() old := s.loadObserversMap() - if _, ok := old[observer.Descriptor.ID]; ok { + id := observer.Descriptor.ID() + if _, ok := old[id]; ok { return false } observers := make(observersMap) - for id, data := range old { - observers[id] = data + for oid, data := range old { + observers[oid] = data } - observers[observer.Descriptor.ID] = observerData{ + observers[id] = observerData{ observer: observer, callback: callback, } @@ -119,7 +120,8 @@ func (s *sdk) UnregisterObserver(observer metric.Observer) { s.observersLock.Lock() defer s.observersLock.Unlock() old := s.loadObserversMap() - if _, ok := old[observer.Descriptor.ID]; !ok { + id := observer.Descriptor.ID() + if _, ok := old[id]; !ok { return } if len(old) == 1 { @@ -127,9 +129,9 @@ func (s *sdk) UnregisterObserver(observer metric.Observer) { return } observers := make(observersMap) - for id, data := range old { - if id != observer.Descriptor.ID { - observers[id] = data + for oid, data := range old { + if oid != id { + observers[oid] = data } } s.observers.Store(observers) @@ -150,7 +152,7 @@ func (s *sdk) observersRoutine() { } } -func (s *sdk) getObservationCallback(descriptor metric.Descriptor) metric.ObservationCallback { +func (s *sdk) getObservationCallback(descriptor *metric.Descriptor) metric.ObservationCallback { return func(l metric.LabelSet, v metric.MeasurementValue) { s.RecordBatch(context.Background(), l, metric.Measurement{ Descriptor: descriptor, From 200178eef388708b9ce928fb48ba191de8e46175 Mon Sep 17 00:00:00 2001 From: Krzesimir Nowak Date: Fri, 4 Oct 2019 22:26:00 +0200 Subject: [PATCH 27/35] use a store helper --- experimental/streaming/sdk/metric.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/experimental/streaming/sdk/metric.go b/experimental/streaming/sdk/metric.go index 0b47fadb3f6..6974b335b78 100644 --- a/experimental/streaming/sdk/metric.go +++ b/experimental/streaming/sdk/metric.go @@ -134,7 +134,7 @@ func (s *sdk) UnregisterObserver(observer metric.Observer) { observers[oid] = data } } - s.observers.Store(observers) + s.storeObserversMap(observers) } func (s *sdk) observersRoutine() { From ba4bb01b17841d05a3cc89d94f5ac26329df6961 Mon Sep 17 00:00:00 2001 From: Krzesimir Nowak Date: Fri, 4 Oct 2019 22:27:59 +0200 Subject: [PATCH 28/35] handle comment fixes --- api/metric/api.go | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/api/metric/api.go b/api/metric/api.go index 3147aa3bd48..1da47872346 100644 --- a/api/metric/api.go +++ b/api/metric/api.go @@ -34,9 +34,10 @@ const ( ObserverKind ) -// Recorder is the implementation-level interface to Set/Add/Record individual metrics. +// Handle is the implementation-level interface to Set/Add/Record +// individual metrics. type Handle interface { - // Record 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) } From 2eb049a4e479bf39a9f0a0924fd6023751408154 Mon Sep 17 00:00:00 2001 From: Krzesimir Nowak Date: Fri, 4 Oct 2019 22:30:25 +0200 Subject: [PATCH 29/35] reword Alternate comment --- api/metric/api.go | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/api/metric/api.go b/api/metric/api.go index 1da47872346..708e3d5d2bf 100644 --- a/api/metric/api.go +++ b/api/metric/api.go @@ -135,15 +135,16 @@ func (d *Descriptor) ValueKind() ValueKind { return d.valueKind } -// Meaning depends on the metric type: +// Alternate defines the property of metric value dependent on a +// metric type. // -// - for Counter it implies that this is an up-down Counter +// - for Counter, true implies that the metric is an up-down Counter // -// - for Gauge/Observer it implies that this is non-descending -// Gauge/Observer +// - for Gauge/Observer, true implies that the metric is a +// non-descending Gauge/Observer // -// - for Measure it implies that it supports positive and negative -// values +// - for Measure, true implies that the metric supports positive and +// negative values func (d *Descriptor) Alternate() bool { return d.alternate } From 780507740bb0f71272948e94679920be32641b9c Mon Sep 17 00:00:00 2001 From: Krzesimir Nowak Date: Sat, 5 Oct 2019 15:04:19 +0200 Subject: [PATCH 30/35] drop the "any value" metrics --- api/metric/api.go | 3 +- api/metric/common.go | 42 +++++++++++++++++++++- api/metric/counter.go | 74 ++++++++++----------------------------- api/metric/gauge.go | 74 ++++++++++----------------------------- api/metric/kind_string.go | 9 +++-- api/metric/measure.go | 72 +++++++++---------------------------- api/metric/observer.go | 6 ++-- 7 files changed, 102 insertions(+), 178 deletions(-) diff --git a/api/metric/api.go b/api/metric/api.go index 708e3d5d2bf..286d0629882 100644 --- a/api/metric/api.go +++ b/api/metric/api.go @@ -80,8 +80,7 @@ type DescriptorID uint64 type ValueKind int8 const ( - AnyValueKind ValueKind = iota - Int64ValueKind + Int64ValueKind ValueKind = iota Float64ValueKind ) diff --git a/api/metric/common.go b/api/metric/common.go index 0201b45d0c3..ced41c67b72 100644 --- a/api/metric/common.go +++ b/api/metric/common.go @@ -14,12 +14,52 @@ package metric -import "sync/atomic" +import ( + "context" + "sync/atomic" +) var ( descriptorID uint64 ) +// TODO: Maybe unexport that and document very _very_ clearly, that +// you can still get a descriptor with NewInt64Counter(…).Descriptor +type CommonMetric struct { + *Descriptor +} + +func (m CommonMetric) getHandle(labels LabelSet) Handle { + return labels.Meter().NewHandle(m.Descriptor, labels) +} + +func (m CommonMetric) float64Measurement(value float64) Measurement { + return Measurement{ + Descriptor: m.Descriptor, + Value: NewFloat64MeasurementValue(value), + } +} + +func (m CommonMetric) int64Measurement(value int64) Measurement { + return Measurement{ + Descriptor: m.Descriptor, + Value: NewInt64MeasurementValue(value), + } +} + +func (m CommonMetric) recordOne(ctx context.Context, value MeasurementValue, labels LabelSet) { + labels.Meter().RecordBatch(ctx, labels, Measurement{ + Descriptor: m.Descriptor, + Value: value, + }) +} + +func registerCommonMetric(name string, kind Kind, valueKind ValueKind) CommonMetric { + return CommonMetric{ + Descriptor: registerDescriptor(name, kind, valueKind), + } +} + func registerDescriptor(name string, kind Kind, valueKind ValueKind) *Descriptor { return &Descriptor{ name: name, diff --git a/api/metric/counter.go b/api/metric/counter.go index e7d57e48037..30fe464a5ad 100644 --- a/api/metric/counter.go +++ b/api/metric/counter.go @@ -18,28 +18,20 @@ import ( "context" ) -type Counter struct { - *Descriptor -} - type Float64Counter struct { - Counter + CommonMetric } type Int64Counter struct { - Counter -} - -type CounterHandle struct { - Handle + CommonMetric } type Float64CounterHandle struct { - CounterHandle + Handle } type Int64CounterHandle struct { - CounterHandle + Handle } type CounterOptionApplier interface { @@ -56,84 +48,54 @@ func (o counterOptionWrapper) ApplyCounterOption(d *Descriptor) { o.F(d) } -func NewCounter(name string, valueKind ValueKind, mos ...CounterOptionApplier) (c Counter) { - c.Descriptor = registerDescriptor(name, CounterKind, valueKind) +func newCounter(name string, valueKind ValueKind, mos ...CounterOptionApplier) CommonMetric { + m := registerCommonMetric(name, CounterKind, valueKind) for _, opt := range mos { - opt.ApplyCounterOption(c.Descriptor) + opt.ApplyCounterOption(m.Descriptor) } - return + return m } func NewFloat64Counter(name string, mos ...CounterOptionApplier) (c Float64Counter) { - c.Counter = NewCounter(name, Float64ValueKind, mos...) + c.CommonMetric = newCounter(name, Float64ValueKind, mos...) return } func NewInt64Counter(name string, mos ...CounterOptionApplier) (c Int64Counter) { - c.Counter = NewCounter(name, Int64ValueKind, mos...) - return -} - -func (c *Counter) GetHandle(labels LabelSet) (h CounterHandle) { - h.Handle = labels.Meter().NewHandle(c.Descriptor, labels) + c.CommonMetric = newCounter(name, Int64ValueKind, mos...) return } func (c *Float64Counter) GetHandle(labels LabelSet) (h Float64CounterHandle) { - h.CounterHandle = c.Counter.GetHandle(labels) + h.Handle = c.getHandle(labels) return } func (c *Int64Counter) GetHandle(labels LabelSet) (h Int64CounterHandle) { - h.CounterHandle = c.Counter.GetHandle(labels) + h.Handle = c.getHandle(labels) return } -func (c *Counter) Float64Measurement(value float64) Measurement { - return Measurement{ - Descriptor: c.Descriptor, - Value: NewFloat64MeasurementValue(value), - } -} - -func (c *Counter) Int64Measurement(value int64) Measurement { - return Measurement{ - Descriptor: c.Descriptor, - Value: NewInt64MeasurementValue(value), - } -} - func (c *Float64Counter) Measurement(value float64) Measurement { - return c.Counter.Float64Measurement(value) + return c.float64Measurement(value) } func (c *Int64Counter) Measurement(value int64) Measurement { - return c.Counter.Int64Measurement(value) -} - -func (c *Counter) Add(ctx context.Context, value MeasurementValue, labels LabelSet) { - labels.Meter().RecordBatch(ctx, labels, Measurement{ - Descriptor: c.Descriptor, - Value: value, - }) + return c.int64Measurement(value) } func (c *Float64Counter) Add(ctx context.Context, value float64, labels LabelSet) { - c.Counter.Add(ctx, NewFloat64MeasurementValue(value), labels) + c.recordOne(ctx, NewFloat64MeasurementValue(value), labels) } func (c *Int64Counter) Add(ctx context.Context, value int64, labels LabelSet) { - c.Counter.Add(ctx, NewInt64MeasurementValue(value), labels) -} - -func (h *CounterHandle) Add(ctx context.Context, value MeasurementValue) { - h.RecordOne(ctx, value) + c.recordOne(ctx, NewInt64MeasurementValue(value), labels) } func (h *Float64CounterHandle) Add(ctx context.Context, value float64) { - h.CounterHandle.Add(ctx, NewFloat64MeasurementValue(value)) + h.RecordOne(ctx, NewFloat64MeasurementValue(value)) } func (h *Int64CounterHandle) Add(ctx context.Context, value int64) { - h.CounterHandle.Add(ctx, NewInt64MeasurementValue(value)) + h.RecordOne(ctx, NewInt64MeasurementValue(value)) } diff --git a/api/metric/gauge.go b/api/metric/gauge.go index 3a7f2b66f65..0806adf0a88 100644 --- a/api/metric/gauge.go +++ b/api/metric/gauge.go @@ -18,28 +18,20 @@ import ( "context" ) -type Gauge struct { - *Descriptor -} - type Float64Gauge struct { - Gauge + CommonMetric } type Int64Gauge struct { - Gauge -} - -type GaugeHandle struct { - Handle + CommonMetric } type Float64GaugeHandle struct { - GaugeHandle + Handle } type Int64GaugeHandle struct { - GaugeHandle + Handle } type GaugeOptionApplier interface { @@ -56,84 +48,54 @@ func (o gaugeOptionWrapper) ApplyGaugeOption(d *Descriptor) { o.F(d) } -func NewGauge(name string, valueKind ValueKind, mos ...GaugeOptionApplier) (g Gauge) { - g.Descriptor = registerDescriptor(name, GaugeKind, valueKind) +func newGauge(name string, valueKind ValueKind, mos ...GaugeOptionApplier) CommonMetric { + m := registerCommonMetric(name, GaugeKind, valueKind) for _, opt := range mos { - opt.ApplyGaugeOption(g.Descriptor) + opt.ApplyGaugeOption(m.Descriptor) } - return + return m } func NewFloat64Gauge(name string, mos ...GaugeOptionApplier) (g Float64Gauge) { - g.Gauge = NewGauge(name, Float64ValueKind, mos...) + g.CommonMetric = newGauge(name, Float64ValueKind, mos...) return } func NewInt64Gauge(name string, mos ...GaugeOptionApplier) (g Int64Gauge) { - g.Gauge = NewGauge(name, Int64ValueKind, mos...) - return -} - -func (g *Gauge) GetHandle(labels LabelSet) (h GaugeHandle) { - h.Handle = labels.Meter().NewHandle(g.Descriptor, labels) + g.CommonMetric = newGauge(name, Int64ValueKind, mos...) return } func (g *Float64Gauge) GetHandle(labels LabelSet) (h Float64GaugeHandle) { - h.GaugeHandle = g.Gauge.GetHandle(labels) + h.Handle = g.getHandle(labels) return } func (g *Int64Gauge) GetHandle(labels LabelSet) (h Int64GaugeHandle) { - h.GaugeHandle = g.Gauge.GetHandle(labels) + h.Handle = g.getHandle(labels) return } -func (g *Gauge) Float64Measurement(value float64) Measurement { - return Measurement{ - Descriptor: g.Descriptor, - Value: NewFloat64MeasurementValue(value), - } -} - -func (g *Gauge) Int64Measurement(value int64) Measurement { - return Measurement{ - Descriptor: g.Descriptor, - Value: NewInt64MeasurementValue(value), - } -} - func (g *Float64Gauge) Measurement(value float64) Measurement { - return g.Gauge.Float64Measurement(value) + return g.float64Measurement(value) } func (g *Int64Gauge) Measurement(value int64) Measurement { - return g.Gauge.Int64Measurement(value) -} - -func (g *Gauge) Set(ctx context.Context, value MeasurementValue, labels LabelSet) { - labels.Meter().RecordBatch(ctx, labels, Measurement{ - Descriptor: g.Descriptor, - Value: value, - }) + return g.int64Measurement(value) } func (g *Float64Gauge) Set(ctx context.Context, value float64, labels LabelSet) { - g.Gauge.Set(ctx, NewFloat64MeasurementValue(value), labels) + g.recordOne(ctx, NewFloat64MeasurementValue(value), labels) } func (g *Int64Gauge) Set(ctx context.Context, value int64, labels LabelSet) { - g.Gauge.Set(ctx, NewInt64MeasurementValue(value), labels) -} - -func (h *GaugeHandle) Set(ctx context.Context, value MeasurementValue) { - h.RecordOne(ctx, value) + g.recordOne(ctx, NewInt64MeasurementValue(value), labels) } func (h *Float64GaugeHandle) Set(ctx context.Context, value float64) { - h.GaugeHandle.Set(ctx, NewFloat64MeasurementValue(value)) + h.RecordOne(ctx, NewFloat64MeasurementValue(value)) } func (h *Int64GaugeHandle) Set(ctx context.Context, value int64) { - h.GaugeHandle.Set(ctx, NewInt64MeasurementValue(value)) + h.RecordOne(ctx, NewInt64MeasurementValue(value)) } diff --git a/api/metric/kind_string.go b/api/metric/kind_string.go index fce98da324d..aa40a559bfc 100644 --- a/api/metric/kind_string.go +++ b/api/metric/kind_string.go @@ -29,14 +29,13 @@ 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[AnyValueKind-0] - _ = x[Int64ValueKind-1] - _ = x[Float64ValueKind-2] + _ = x[Int64ValueKind-0] + _ = x[Float64ValueKind-1] } -const _ValueKind_name = "AnyValueKindInt64ValueKindFloat64ValueKind" +const _ValueKind_name = "Int64ValueKindFloat64ValueKind" -var _ValueKind_index = [...]uint8{0, 12, 26, 42} +var _ValueKind_index = [...]uint8{0, 14, 30} func (i ValueKind) String() string { if i < 0 || i >= ValueKind(len(_ValueKind_index)-1) { diff --git a/api/metric/measure.go b/api/metric/measure.go index 9350a5495d4..b6b259e8f23 100644 --- a/api/metric/measure.go +++ b/api/metric/measure.go @@ -18,28 +18,20 @@ import ( "context" ) -type Measure struct { - *Descriptor -} - type Float64Measure struct { - Measure + CommonMetric } type Int64Measure struct { - Measure -} - -type MeasureHandle struct { - Handle + CommonMetric } type Float64MeasureHandle struct { - MeasureHandle + Handle } type Int64MeasureHandle struct { - MeasureHandle + Handle } type MeasureOptionApplier interface { @@ -56,84 +48,54 @@ func (o measureOptionWrapper) ApplyMeasureOption(d *Descriptor) { o.F(d) } -func NewMeasure(name string, valueKind ValueKind, mos ...MeasureOptionApplier) (m Measure) { - m.Descriptor = registerDescriptor(name, MeasureKind, valueKind) +func newMeasure(name string, valueKind ValueKind, mos ...MeasureOptionApplier) CommonMetric { + m := registerCommonMetric(name, MeasureKind, valueKind) for _, opt := range mos { opt.ApplyMeasureOption(m.Descriptor) } - return + return m } func NewFloat64Measure(name string, mos ...MeasureOptionApplier) (c Float64Measure) { - c.Measure = NewMeasure(name, Float64ValueKind, mos...) + c.CommonMetric = newMeasure(name, Float64ValueKind, mos...) return } func NewInt64Measure(name string, mos ...MeasureOptionApplier) (c Int64Measure) { - c.Measure = NewMeasure(name, Int64ValueKind, mos...) - return -} - -func (m *Measure) GetHandle(labels LabelSet) (h MeasureHandle) { - h.Handle = labels.Meter().NewHandle(m.Descriptor, labels) + c.CommonMetric = newMeasure(name, Int64ValueKind, mos...) return } func (c *Float64Measure) GetHandle(labels LabelSet) (h Float64MeasureHandle) { - h.MeasureHandle = c.Measure.GetHandle(labels) + h.Handle = c.getHandle(labels) return } func (c *Int64Measure) GetHandle(labels LabelSet) (h Int64MeasureHandle) { - h.MeasureHandle = c.Measure.GetHandle(labels) + h.Handle = c.getHandle(labels) return } -func (m *Measure) Float64Measurement(value float64) Measurement { - return Measurement{ - Descriptor: m.Descriptor, - Value: NewFloat64MeasurementValue(value), - } -} - -func (m *Measure) Int64Measurement(value int64) Measurement { - return Measurement{ - Descriptor: m.Descriptor, - Value: NewInt64MeasurementValue(value), - } -} - func (c *Float64Measure) Measurement(value float64) Measurement { - return c.Measure.Float64Measurement(value) + return c.float64Measurement(value) } func (c *Int64Measure) Measurement(value int64) Measurement { - return c.Measure.Int64Measurement(value) -} - -func (m *Measure) Record(ctx context.Context, value MeasurementValue, labels LabelSet) { - labels.Meter().RecordBatch(ctx, labels, Measurement{ - Descriptor: m.Descriptor, - Value: value, - }) + return c.int64Measurement(value) } func (c *Float64Measure) Record(ctx context.Context, value float64, labels LabelSet) { - c.Measure.Record(ctx, NewFloat64MeasurementValue(value), labels) + c.recordOne(ctx, NewFloat64MeasurementValue(value), labels) } func (c *Int64Measure) Record(ctx context.Context, value int64, labels LabelSet) { - c.Measure.Record(ctx, NewInt64MeasurementValue(value), labels) -} - -func (h *MeasureHandle) Record(ctx context.Context, value MeasurementValue) { - h.RecordOne(ctx, value) + c.recordOne(ctx, NewInt64MeasurementValue(value), labels) } func (h *Float64MeasureHandle) Record(ctx context.Context, value float64) { - h.MeasureHandle.Record(ctx, NewFloat64MeasurementValue(value)) + h.RecordOne(ctx, NewFloat64MeasurementValue(value)) } func (h *Int64MeasureHandle) Record(ctx context.Context, value int64) { - h.MeasureHandle.Record(ctx, NewInt64MeasurementValue(value)) + h.RecordOne(ctx, NewInt64MeasurementValue(value)) } diff --git a/api/metric/observer.go b/api/metric/observer.go index 318fd1940cd..98a9cbc0871 100644 --- a/api/metric/observer.go +++ b/api/metric/observer.go @@ -26,7 +26,7 @@ type Int64Observer struct { Observer } -func NewObserver(name string, valueKind ValueKind, mos ...GaugeOptionApplier) (o Observer) { +func newObserver(name string, valueKind ValueKind, mos ...GaugeOptionApplier) (o Observer) { o.Descriptor = registerDescriptor(name, ObserverKind, valueKind) for _, opt := range mos { opt.ApplyGaugeOption(o.Descriptor) @@ -35,11 +35,11 @@ func NewObserver(name string, valueKind ValueKind, mos ...GaugeOptionApplier) (o } func NewFloat64Observer(name string, mos ...GaugeOptionApplier) (o Float64Observer) { - o.Observer = NewObserver(name, Float64ValueKind, mos...) + o.Observer = newObserver(name, Float64ValueKind, mos...) return } func NewInt64Observer(name string, mos ...GaugeOptionApplier) (o Int64Observer) { - o.Observer = NewObserver(name, Int64ValueKind, mos...) + o.Observer = newObserver(name, Int64ValueKind, mos...) return } From 1eaac50ff61bbfda70fc7371ba320117e6a89479 Mon Sep 17 00:00:00 2001 From: Krzesimir Nowak Date: Sat, 5 Oct 2019 15:05:02 +0200 Subject: [PATCH 31/35] make measurement value simpler --- api/metric/value.go | 202 +++++++----------- .../exporter/reader/format/format.go | 2 +- experimental/streaming/sdk/span_test.go | 27 +-- 3 files changed, 91 insertions(+), 140 deletions(-) diff --git a/api/metric/value.go b/api/metric/value.go index 24f090a42ae..bf972c83554 100644 --- a/api/metric/value.go +++ b/api/metric/value.go @@ -19,175 +19,125 @@ import ( "math" ) -type valueKind int8 - -const ( - int64Kind valueKind = iota - float64Kind -) - // MeasurementValue represents either an integral or a floating point -// value of a measurement. Empty MeasurementValue is treated as -// integral zero. -type MeasurementValue struct { - raw uint64 - kind valueKind -} - -// MeasurementValueVisitor is an interface used for visitation of -// MeasurementValues. -type MeasurementValueVisitor interface { - // Int64 gets called for an integral MeasurementValue. - Int64(int64) - // Int64 gets called for a floating point MeasurementValue. - Float64(float64) - // Unknown gets called on the unknown value. This normally - // doesn't happen - it is meant for the future compatilibity - // when new types may get added to the MeasurementValue. The - // additional types would be handled an in extension interface - // of the MeasurementValueVisitor. - Unknown(interface{}) -} +// 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. +type MeasurementValue uint64 // NewInt64MeasurementValue creates an integral MeasurementValue. func NewInt64MeasurementValue(i int64) MeasurementValue { - return MeasurementValue{ - raw: uint64(i), - kind: int64Kind, - } + return newFromRaw(int64ToRaw(i)) } // NewFloat64MeasurementValue creates a floating point // MeasurementValue. func NewFloat64MeasurementValue(f float64) MeasurementValue { - return MeasurementValue{ - raw: math.Float64bits(f), - kind: float64Kind, - } -} - -// GetInt64 tries to get an integral value from MeasurementValue. The -// use of this function is similar to doing a checked type assertion -// (i, ok := v.(int64)). -func (v MeasurementValue) GetInt64() (int64, bool) { - if v.kind != int64Kind { - return 0, false - } - return v.int64(), true + return newFromRaw(float64ToRaw(f)) } -// GetFloat64 tries to get a floating point value from -// MeasurementValue. The use of this function is similar to doing a -// checked type assertion (i, ok := v.(float64)). -func (v MeasurementValue) GetFloat64() (float64, bool) { - if v.kind != float64Kind { - return 0, false - } - return v.float64(), true +func newFromRaw(raw uint64) MeasurementValue { + return MeasurementValue(raw) } -func (v MeasurementValue) int64() int64 { - return int64(v.raw) +func (v MeasurementValue) AsInt64() int64 { + return rawToInt64(v.AsRaw()) } -func (v MeasurementValue) float64() float64 { - return math.Float64frombits(v.raw) +func (v MeasurementValue) AsFloat64() float64 { + return rawToFloat64(v.AsRaw()) } -// Visit uses the visitor to find out the actual value of the -// MeasurementValue. -func (v MeasurementValue) Visit(visitor MeasurementValueVisitor) { - switch v.kind { - case int64Kind: - visitor.Int64(v.int64()) - case float64Kind: - visitor.Float64(v.float64()) - } +func (v MeasurementValue) AsRaw() uint64 { + return uint64(v) } -type stringEmitter struct { - s *string -} - -var _ MeasurementValueVisitor = stringEmitter{} - -func (e stringEmitter) Int64(i int64) { - *e.s = fmt.Sprintf("%d", i) -} - -func (e stringEmitter) Float64(f float64) { - *e.s = fmt.Sprintf("%f", f) -} - -func (e stringEmitter) Unknown(interface{}) { +func (v *MeasurementValue) AsRawPtr() *uint64 { + return (*uint64)(v) } // Emit returns a string representation of the actual value of the // MeasurementValue. A %d is used for integral values, %f for floating // point values. -func (v MeasurementValue) Emit() string { - var s string - c := stringEmitter{ - s: &s, +func (v MeasurementValue) Emit(kind ValueKind) string { + switch kind { + case Int64ValueKind: + return fmt.Sprintf("%d", v.AsInt64()) + case Float64ValueKind: + return fmt.Sprintf("%f", v.AsFloat64()) + default: + return "" } - v.Visit(c) - return s } -type int64Coercer struct { - i *int64 +func (v MeasurementValue) Float64Compare(other float64) int { + this := v.AsFloat64() + if this < other { + return -1 + } else if this > other { + return 1 + } + return 0 } -var _ MeasurementValueVisitor = int64Coercer{} - -func (c int64Coercer) Int64(i int64) { - *c.i = i +func (v MeasurementValue) Int64Compare(other int64) int { + this := v.AsInt64() + if this < other { + return -1 + } else if this > other { + return 1 + } + return 0 +} + +func (v MeasurementValue) RawCompare(other uint64, kind ValueKind) int { + switch kind { + case Int64ValueKind: + return v.Int64Compare(rawToInt64(other)) + case Float64ValueKind: + return v.Float64Compare(rawToFloat64(other)) + default: + // you get what you deserve + return 0 + } } -func (c int64Coercer) Float64(f float64) { - *c.i = int64(f) +func (v MeasurementValue) IsPositive(kind ValueKind) bool { + return v.compareWithZero(kind) > 0 } -func (c int64Coercer) Unknown(interface{}) { +func (v MeasurementValue) IsNegative(kind ValueKind) bool { + return v.compareWithZero(kind) < 0 } -// CoerceToInt64 returns the value of the MeasurementValue casted to -// int64. This may result in loss of precision of the actual value was -// a floating point one. -func (v MeasurementValue) CoerceToInt64() int64 { - var i int64 - c := int64Coercer{ - i: &i, - } - v.Visit(c) - return i +func (v MeasurementValue) IsZero(kind ValueKind) bool { + return v.compareWithZero(kind) == 0 } -type float64Coercer struct { - f *float64 +func (v MeasurementValue) compareWithZero(kind ValueKind) int { + switch kind { + case Int64ValueKind: + return v.Int64Compare(0) + case Float64ValueKind: + return v.Float64Compare(0.) + default: + // you get what you deserve + return 0 + } } -var _ MeasurementValueVisitor = float64Coercer{} - -func (c float64Coercer) Int64(i int64) { - *c.f = float64(i) +func rawToFloat64(r uint64) float64 { + return math.Float64frombits(r) } -func (c float64Coercer) Float64(f float64) { - *c.f = f +func float64ToRaw(f float64) uint64 { + return math.Float64bits(f) } -func (c float64Coercer) Unknown(interface{}) { +func rawToInt64(r uint64) int64 { + return int64(r) } -// CoerceToFloat64 returns the value of the MeasurementValue casted to -// float64. This may result in loss of precision of the actual value -// was an integral one. -func (v MeasurementValue) CoerceToFloat64() float64 { - var f float64 - c := float64Coercer{ - f: &f, - } - v.Visit(c) - return f +func int64ToRaw(i int64) uint64 { + return uint64(i) } diff --git a/experimental/streaming/exporter/reader/format/format.go b/experimental/streaming/exporter/reader/format/format.go index 1f456ce0e08..322d3c8585c 100644 --- a/experimental/streaming/exporter/reader/format/format.go +++ b/experimental/streaming/exporter/reader/format/format.go @@ -139,7 +139,7 @@ func formatMetricUpdate(buf *strings.Builder, m metric.Measurement) { buf.WriteString(" ") buf.WriteString(m.Descriptor.Name()) buf.WriteString("=") - buf.WriteString(m.Value.Emit()) + buf.WriteString(m.Value.Emit(m.Descriptor.ValueKind())) } func formatMetricLabels(buf *strings.Builder, l tag.Map) { diff --git a/experimental/streaming/sdk/span_test.go b/experimental/streaming/sdk/span_test.go index d944d35c887..06db8796529 100644 --- a/experimental/streaming/sdk/span_test.go +++ b/experimental/streaming/sdk/span_test.go @@ -131,23 +131,24 @@ func checkContext(t *testing.T, ctx context.Context, key, wantValue interface{}) return true } -func measurementCompare(mv1, mv2 metric.MeasurementValue) bool { - i1, ok1 := mv1.GetInt64() - i2, ok2 := mv2.GetInt64() - if ok1 != ok2 { +func measurementCompare(m1, m2 metric.Measurement) bool { + // Nil descriptor normally shouldn't happen, unless there is + // some struct with the Measurement field that didn't get + // initialized. + m1Nil := m1.Descriptor == nil + m2Nil := m2.Descriptor == nil + if m1Nil != m2Nil { return false - } else if ok1 && ok2 { - return i1 == i2 } - f1, ok1 := mv1.GetFloat64() - f2, ok2 := mv2.GetFloat64() - if ok1 != ok2 { + if m1Nil { + return m1.Value.AsRaw() == m2.Value.AsRaw() + } + if m1.Descriptor.ID() != m2.Descriptor.ID() { return false - } else if ok1 && ok2 { - return f1 == f2 } - // both are not initialized - return true + m2Raw := m2.Value.AsRaw() + kind := m1.Descriptor.ValueKind() + return m1.Value.RawCompare(m2Raw, kind) == 0 } func diffEvents(t *testing.T, got, want []exporter.Event, extraIgnoredFields ...string) bool { From cf4b0b06179ac011670495937cc556b6c393c772 Mon Sep 17 00:00:00 2001 From: Krzesimir Nowak Date: Sat, 5 Oct 2019 16:08:07 +0200 Subject: [PATCH 32/35] document value stuff --- api/metric/value.go | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/api/metric/value.go b/api/metric/value.go index bf972c83554..b0368b27ea2 100644 --- a/api/metric/value.go +++ b/api/metric/value.go @@ -40,18 +40,28 @@ func newFromRaw(raw uint64) MeasurementValue { return MeasurementValue(raw) } +// 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. 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. func (v MeasurementValue) AsFloat64() float64 { return rawToFloat64(v.AsRaw()) } +// AsRaw gets the raw, uninterpreted value of the measurement. Might +// be useful for some atomic operations. func (v MeasurementValue) AsRaw() uint64 { return uint64(v) } +// AsRawPtr gets the pointer to the raw, uninterpreted value of the +// measurement. Might be useful for some atomic operations. func (v *MeasurementValue) AsRawPtr() *uint64 { return (*uint64)(v) } @@ -70,6 +80,11 @@ func (v MeasurementValue) Emit(kind ValueKind) string { } } +// Float64Compare assumes that the MeasurementValue contains a float64 +// and performs a comparison between the value and the other value. It +// returns the typical result of the compare function: -1 if the value +// is less than the other, 0 if both are equal, 1 if the value is +// greater than the other. func (v MeasurementValue) Float64Compare(other float64) int { this := v.AsFloat64() if this < other { @@ -80,6 +95,11 @@ func (v MeasurementValue) Float64Compare(other float64) int { return 0 } +// Int64Compare assumes that the MeasurementValue contains an int64 +// and performs a comparison between the value and the other value. It +// returns the typical result of the compare function: -1 if the value +// is less than the other, 0 if both are equal, 1 if the value is +// greater than the other. func (v MeasurementValue) Int64Compare(other int64) int { this := v.AsInt64() if this < other { @@ -90,6 +110,8 @@ func (v MeasurementValue) Int64Compare(other int64) int { return 0 } +// RawCompare calls either Float64Compare or Int64Compare, depending +// on the passed kind. func (v MeasurementValue) RawCompare(other uint64, kind ValueKind) int { switch kind { case Int64ValueKind: @@ -102,14 +124,17 @@ func (v MeasurementValue) RawCompare(other uint64, kind ValueKind) int { } } +// IsPositive returns true if the actual value is greater than zero. func (v MeasurementValue) IsPositive(kind ValueKind) bool { return v.compareWithZero(kind) > 0 } +// IsNegative returns true if the actual value is less than zero. func (v MeasurementValue) IsNegative(kind ValueKind) bool { return v.compareWithZero(kind) < 0 } +// IsZero returns true if the actual value is equal to zero. func (v MeasurementValue) IsZero(kind ValueKind) bool { return v.compareWithZero(kind) == 0 } From b7fd1b5b189f2d4586b03ad1122a0bac9c7016cd Mon Sep 17 00:00:00 2001 From: Krzesimir Nowak Date: Sat, 5 Oct 2019 16:08:23 +0200 Subject: [PATCH 33/35] add tests for values --- api/metric/value_test.go | 148 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 148 insertions(+) create mode 100644 api/metric/value_test.go diff --git a/api/metric/value_test.go b/api/metric/value_test.go new file mode 100644 index 00000000000..3b017818175 --- /dev/null +++ b/api/metric/value_test.go @@ -0,0 +1,148 @@ +// 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 ( + "testing" + "unsafe" +) + +func TestMeasurementValue(t *testing.T) { + iNeg := NewInt64MeasurementValue(-42) + iZero := NewInt64MeasurementValue(0) + iPos := NewInt64MeasurementValue(42) + i64Values := [3]MeasurementValue{iNeg, iZero, iPos} + + for idx, i := range []int64{-42, 0, 42} { + v := i64Values[idx] + if got := v.AsInt64(); got != i { + t.Errorf("Value %#v (%s) int64 check failed, expected %d, got %d", v, v.Emit(Int64ValueKind), i, got) + } + } + + for _, v := range i64Values { + expected := unsafe.Pointer(&v) + got := unsafe.Pointer(v.AsRawPtr()) + if expected != got { + t.Errorf("Getting raw pointer failed, got %v, expected %v", got, expected) + } + } + + fNeg := NewFloat64MeasurementValue(-42.) + fZero := NewFloat64MeasurementValue(0.) + fPos := NewFloat64MeasurementValue(42.) + f64Values := [3]MeasurementValue{fNeg, fZero, fPos} + + for idx, f := range []float64{-42., 0., 42.} { + v := f64Values[idx] + if got := v.AsFloat64(); got != f { + t.Errorf("Value %#v (%s) float64 check failed, expected %f, got %f", v, v.Emit(Int64ValueKind), f, got) + } + } + + for _, v := range f64Values { + expected := unsafe.Pointer(&v) + got := unsafe.Pointer(v.AsRawPtr()) + if expected != got { + t.Errorf("Getting raw pointer failed, got %v, expected %v", got, expected) + } + } + + cmpsForNeg := [3]int{0, -1, -1} + cmpsForZero := [3]int{1, 0, -1} + cmpsForPos := [3]int{1, 1, 0} + + type testcase struct { + v MeasurementValue + kind ValueKind + pos bool + zero bool + neg bool + vals [3]MeasurementValue + cmps [3]int + } + testcases := []testcase{ + { + v: iNeg, + kind: Int64ValueKind, + pos: false, + zero: false, + neg: true, + vals: i64Values, + cmps: cmpsForNeg, + }, + { + v: iZero, + kind: Int64ValueKind, + pos: false, + zero: true, + neg: false, + vals: i64Values, + cmps: cmpsForZero, + }, + { + v: iPos, + kind: Int64ValueKind, + pos: true, + zero: false, + neg: false, + vals: i64Values, + cmps: cmpsForPos, + }, + { + v: fNeg, + kind: Float64ValueKind, + pos: false, + zero: false, + neg: true, + vals: f64Values, + cmps: cmpsForNeg, + }, + { + v: fZero, + kind: Float64ValueKind, + pos: false, + zero: true, + neg: false, + vals: f64Values, + cmps: cmpsForZero, + }, + { + v: fPos, + kind: Float64ValueKind, + pos: true, + zero: false, + neg: false, + vals: f64Values, + cmps: cmpsForPos, + }, + } + for _, tt := range testcases { + if got := tt.v.IsPositive(tt.kind); got != tt.pos { + t.Errorf("Value %#v (%s) positive check failed, expected %v, got %v", tt.v, tt.v.Emit(tt.kind), tt.pos, got) + } + if got := tt.v.IsZero(tt.kind); got != tt.zero { + t.Errorf("Value %#v (%s) zero check failed, expected %v, got %v", tt.v, tt.v.Emit(tt.kind), tt.pos, got) + } + if got := tt.v.IsNegative(tt.kind); got != tt.neg { + t.Errorf("Value %#v (%s) negative check failed, expected %v, got %v", tt.v, tt.v.Emit(tt.kind), tt.pos, got) + } + for i := 0; i < 3; i++ { + if got := tt.v.RawCompare(tt.vals[i].AsRaw(), tt.kind); got != tt.cmps[i] { + t.Errorf("Value %#v (%s) compare check with %#v (%s) failed, expected %d, got %d", tt.v, tt.v.Emit(tt.kind), tt.vals[i], tt.vals[i].Emit(tt.kind), tt.cmps[i], got) + } + } + } +} From 94ce64ae1ced2760c1c426f3563123190d66f314 Mon Sep 17 00:00:00 2001 From: Krzesimir Nowak Date: Mon, 7 Oct 2019 22:25:33 +0200 Subject: [PATCH 34/35] docs --- api/metric/api.go | 71 +++++++++++++++++++++++++++++++++--------- api/metric/common.go | 3 ++ api/metric/counter.go | 28 +++++++++++++++++ api/metric/doc.go | 64 +++++++++++++++++++++++++++++++++++++ api/metric/gauge.go | 28 +++++++++++++++++ api/metric/global.go | 4 +-- api/metric/measure.go | 28 +++++++++++++++++ api/metric/observer.go | 5 +++ 8 files changed, 215 insertions(+), 16 deletions(-) diff --git a/api/metric/api.go b/api/metric/api.go index 286d0629882..4ea202227d0 100644 --- a/api/metric/api.go +++ b/api/metric/api.go @@ -27,10 +27,16 @@ import ( type Kind int const ( - Invalid Kind = iota - CounterKind // Supports Add() - GaugeKind // Supports Set() - MeasureKind // Supports Record() + // 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 + // ObserverKind describes a metric that reports measurement on + // demand. ObserverKind ) @@ -41,13 +47,13 @@ type Handle interface { RecordOne(ctx context.Context, value MeasurementValue) } -// LabelSet represents a []core.KeyValue for use as pre-defined labels -// in the metrics API. -// // TODO this belongs outside the metrics API, in some sense, but that -// might create a dependency. Putting this here means we can't re-use +// might create a dependency. Putting this here means we can't re-use // 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. type LabelSet interface { Meter() Meter } @@ -66,21 +72,44 @@ type Meter interface { // cannot be read by the application. DefineLabels(context.Context, ...core.KeyValue) LabelSet + // 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(*Descriptor, LabelSet) Handle + // DeleteHandle destroys the Handle and does a cleanup of the + // underlying resources. DeleteHandle(Handle) - // RecordBatch atomically records a batch of measurements.. - RecordBatch(context.Context, LabelSet, ...Measurement) - + // RegisterObserver registers the observer with callback + // returning a measurement. When and how often the callback + // will be called is defined by SDK. This should not be used + // directly - prefer either RegisterInt64Observer or + // RegisterFloat64Observer, depending on the type of the + // observer to be registered. RegisterObserver(Observer, ObserverCallback) + // UnregisterObserver removes the observer from registered + // observers. This should not be used directly - prefer either + // UnregisterInt64Observer or UnregisterFloat64Observer, + // depending on the type of the observer to be registered. UnregisterObserver(Observer) } +// 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 ) @@ -157,10 +186,13 @@ type Measurement struct { // Option supports specifying the various metric options. type Option func(*Descriptor) +// OptionApplier is an interface for applying metric options that are +// valid for all the kinds of metrics. type OptionApplier interface { CounterOptionApplier GaugeOptionApplier MeasureOptionApplier + // ApplyOption is used to make some changes in the Descriptor. ApplyOption(*Descriptor) } @@ -204,8 +236,8 @@ func WithUnit(unit unit.Unit) OptionApplier { } } -// WithKeys applies required label keys. Multiple `WithKeys` -// options accumulate. +// WithKeys applies required label keys. Multiple `WithKeys` options +// accumulate. func WithKeys(keys ...core.Key) OptionApplier { return optionWrapper{ F: func(d *Descriptor) { @@ -214,7 +246,8 @@ func WithKeys(keys ...core.Key) OptionApplier { } } -// WithNonMonotonic sets whether a counter is permitted to go up AND down. +// WithNonMonotonic sets whether a counter is permitted to go up AND +// down. func WithNonMonotonic(nm bool) CounterOptionApplier { return counterOptionWrapper{ F: func(d *Descriptor) { @@ -259,6 +292,9 @@ type Int64ObservationCallback func(LabelSet, int64) // for the registered int64 observers. type Int64ObserverCallback func(Meter, Int64Observer, Int64ObservationCallback) +// RegisterInt64Observer is a convenience wrapper around +// Meter.RegisterObserver that provides a type-safe callback for +// Int64Observer. func RegisterInt64Observer(meter Meter, observer Int64Observer, callback Int64ObserverCallback) { cb := func(m Meter, o Observer, ocb ObservationCallback) { iocb := func(l LabelSet, i int64) { @@ -269,6 +305,8 @@ func RegisterInt64Observer(meter Meter, observer Int64Observer, callback Int64Ob meter.RegisterObserver(observer.Observer, cb) } +// UnregisterInt64Observer is a convenience wrapper around +// Meter.UnregisterObserver for Int64Observer. func UnregisterInt64Observer(meter Meter, observer Int64Observer) { meter.UnregisterObserver(observer.Observer) } @@ -281,6 +319,9 @@ type Float64ObservationCallback func(LabelSet, float64) // call for the registered float64 observers. type Float64ObserverCallback func(Meter, Float64Observer, Float64ObservationCallback) +// RegisterFloat64Observer is a convenience wrapper around +// Meter.RegisterObserver that provides a type-safe callback for +// Float64Observer. func RegisterFloat64Observer(meter Meter, observer Float64Observer, callback Float64ObserverCallback) { cb := func(m Meter, o Observer, ocb ObservationCallback) { focb := func(l LabelSet, f float64) { @@ -291,6 +332,8 @@ func RegisterFloat64Observer(meter Meter, observer Float64Observer, callback Flo meter.RegisterObserver(observer.Observer, cb) } +// UnregisterFloat64Observer is a convenience wrapper around +// Meter.UnregisterObserver for Float64Observer. func UnregisterFloat64Observer(meter Meter, observer Float64Observer) { meter.UnregisterObserver(observer.Observer) } diff --git a/api/metric/common.go b/api/metric/common.go index ced41c67b72..52faaaf0a0f 100644 --- a/api/metric/common.go +++ b/api/metric/common.go @@ -25,6 +25,9 @@ var ( // TODO: Maybe unexport that and document very _very_ clearly, that // you can still get a descriptor with NewInt64Counter(…).Descriptor + +// CommonMetric holds a descriptor. It is used mostly to implement the +// common parts for every metric kind. type CommonMetric struct { *Descriptor } diff --git a/api/metric/counter.go b/api/metric/counter.go index 30fe464a5ad..796d6916625 100644 --- a/api/metric/counter.go +++ b/api/metric/counter.go @@ -18,23 +18,31 @@ import ( "context" ) +// Float64Counter is a metric that accumulates float64 values. type Float64Counter struct { CommonMetric } +// Int64Counter is a metric that accumulates int64 values. type Int64Counter struct { CommonMetric } +// Float64CounterHandle is a handle for Float64Counter. type Float64CounterHandle struct { Handle } +// Int64CounterHandle is a handle for Int64Counter. type Int64CounterHandle struct { Handle } +// 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) } @@ -56,46 +64,66 @@ func newCounter(name string, valueKind ValueKind, mos ...CounterOptionApplier) C 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 +// contain the keys and values specified in the counter with the +// WithKeys option. func (c *Float64Counter) GetHandle(labels LabelSet) (h Float64CounterHandle) { h.Handle = c.getHandle(labels) return } +// GetHandle creates a handle for this counter. The labels should +// contain the keys and values specified in the counter with the +// WithKeys option. func (c *Int64Counter) GetHandle(labels LabelSet) (h Int64CounterHandle) { h.Handle = c.getHandle(labels) return } +// Measurement creates a Measurement object to use with batch +// recording. func (c *Float64Counter) Measurement(value float64) Measurement { return c.float64Measurement(value) } +// Measurement creates a Measurement object to use with batch +// recording. func (c *Int64Counter) Measurement(value int64) Measurement { return c.int64Measurement(value) } +// Add adds the value to the counter's sum. The labels should contain +// the keys and values specified in the counter with the WithKeys +// option. func (c *Float64Counter) Add(ctx context.Context, value float64, labels LabelSet) { c.recordOne(ctx, NewFloat64MeasurementValue(value), labels) } +// Add adds the value to the counter's sum. The labels should contain +// the keys and values specified in the counter with the WithKeys +// option. func (c *Int64Counter) Add(ctx context.Context, value int64, labels LabelSet) { c.recordOne(ctx, NewInt64MeasurementValue(value), labels) } +// Add adds the value to the counter's sum. func (h *Float64CounterHandle) Add(ctx context.Context, value float64) { 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)) } diff --git a/api/metric/doc.go b/api/metric/doc.go index 4b0b6346fc5..65eb4df5338 100644 --- a/api/metric/doc.go +++ b/api/metric/doc.go @@ -12,4 +12,68 @@ // See the License for the specific language governing permissions and // limitations under the License. +// metric package provides an API for reporting diagnostic +// measurements using three basic kinds of instruments (or four, if +// calling one special case a separate one). +// +// The three basic kinds are: +// +// - counters +// - gauges +// - measures +// +// There is also a special case of a gauge instrument called +// observer. It will be discussed later. +// +// All instruments report either float64 or int64 values. +// +// The primary object that handles metrics is Meter. The +// implementation of the Meter is provided by SDK. Normally, the Meter +// is used directly only for the LabelSet generation, batch recording +// and the handle destruction. +// +// LabelSet is a set of keys and values that are in a suitable, +// optimized form to be used by Meter. +// +// Counters are instruments that are reporting a quantity or a sum. An +// 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. +// +// Gauges are instruments that are reporting a current state of a +// value. An example could be voltage or temperature. Gauges can be +// created with either NewFloat64Gauge or NewInt64Gauge. Gauges by +// default have no limitations about reported values - they can be +// less or greater than the last reported value. This can be changed +// with the WithMonotonic option passed to the New*Gauge function - +// this permits the reported values only to go up. To report a new +// value, use the Set function. +// +// Measures are instruments that are reporting values that are +// recorded separately to figure out some statistical properties from +// those values (like average). An example could be temperature over +// 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. +// +// A special case of a gauge is observer. It has the same role as +// gauge, but reports values in a different way. The observer can be +// created with NewFloat64Observer or NewInt64Observer and then +// registered within a Meter with either RegisterInt64Observer or +// RegisterFloat64Observer functions. These take a callback for +// reporting the values. The callback will be called by the Meter when +// it deems it necessary. +// +// All the basic kinds of instruments (so, not observers) 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. package metric // import "go.opentelemetry.io/api/metric" diff --git a/api/metric/gauge.go b/api/metric/gauge.go index 0806adf0a88..88af1f8d6c0 100644 --- a/api/metric/gauge.go +++ b/api/metric/gauge.go @@ -18,23 +18,31 @@ import ( "context" ) +// 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 } +// Float64GaugeHandle is a handle for Float64Gauge. type Float64GaugeHandle struct { Handle } +// Int64GaugeHandle is a handle for Int64Gauge. type Int64GaugeHandle struct { Handle } +// 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) } @@ -56,46 +64,66 @@ func newGauge(name string, valueKind ValueKind, mos ...GaugeOptionApplier) Commo 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 +// contain the keys and values specified in the gauge with the +// WithKeys option. func (g *Float64Gauge) GetHandle(labels LabelSet) (h Float64GaugeHandle) { h.Handle = g.getHandle(labels) return } +// GetHandle creates a handle for this gauge. The labels should +// contain the keys and values specified in the gauge with the +// WithKeys option. func (g *Int64Gauge) GetHandle(labels LabelSet) (h Int64GaugeHandle) { h.Handle = g.getHandle(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 sets the value of the gauge to the passed value. The labels +// should contain the keys and values specified in the gauge with the +// WithKeys option. func (g *Float64Gauge) Set(ctx context.Context, value float64, labels LabelSet) { g.recordOne(ctx, NewFloat64MeasurementValue(value), labels) } +// Set sets the value of the gauge to the passed value. The labels +// should contain the keys and values specified in the gauge with the +// WithKeys option. func (g *Int64Gauge) Set(ctx context.Context, value int64, labels LabelSet) { g.recordOne(ctx, NewInt64MeasurementValue(value), labels) } +// Set sets the value of the gauge to the passed value. func (h *Float64GaugeHandle) Set(ctx context.Context, value float64) { h.RecordOne(ctx, NewFloat64MeasurementValue(value)) } +// Set sets the value of the gauge to the passed value. func (h *Int64GaugeHandle) Set(ctx context.Context, value int64) { h.RecordOne(ctx, NewInt64MeasurementValue(value)) } diff --git a/api/metric/global.go b/api/metric/global.go index f5527b75c77..1840429d35d 100644 --- a/api/metric/global.go +++ b/api/metric/global.go @@ -18,8 +18,8 @@ import "sync/atomic" var global atomic.Value -// GlobalMeter return meter registered with global registry. -// If no meter is registered then an instance of noop Meter is returned. +// GlobalMeter returns a meter registered as a global meter. If no +// meter is registered then an instance of noop Meter is returned. func GlobalMeter() Meter { if t := global.Load(); t != nil { return t.(Meter) diff --git a/api/metric/measure.go b/api/metric/measure.go index b6b259e8f23..0bc892d3d3c 100644 --- a/api/metric/measure.go +++ b/api/metric/measure.go @@ -18,23 +18,31 @@ import ( "context" ) +// Float64Measure is a metric that records float64 values. type Float64Measure struct { CommonMetric } +// Int64Measure is a metric that records int64 values. type Int64Measure struct { CommonMetric } +// Float64MeasureHandle is a handle for Float64Measure. type Float64MeasureHandle struct { Handle } +// Int64MeasureHandle is a handle for Int64Measure. type Int64MeasureHandle struct { Handle } +// 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) } @@ -56,46 +64,66 @@ func newMeasure(name string, valueKind ValueKind, mos ...MeasureOptionApplier) C 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 +// contain the keys and values specified in the measure with the +// WithKeys option. func (c *Float64Measure) GetHandle(labels LabelSet) (h Float64MeasureHandle) { h.Handle = c.getHandle(labels) return } +// GetHandle creates a handle for this measure. The labels should +// contain the keys and values specified in the measure with the +// WithKeys option. func (c *Int64Measure) GetHandle(labels LabelSet) (h Int64MeasureHandle) { h.Handle = c.getHandle(labels) return } +// Measurement creates a Measurement object to use with batch +// recording. func (c *Float64Measure) Measurement(value float64) Measurement { return c.float64Measurement(value) } +// Measurement creates a Measurement object to use with batch +// recording. func (c *Int64Measure) Measurement(value int64) Measurement { return c.int64Measurement(value) } +// Record adds a new value to the list of measure's records. The +// labels should contain the keys and values specified in the measure +// with the WithKeys option. func (c *Float64Measure) Record(ctx context.Context, value float64, labels LabelSet) { c.recordOne(ctx, NewFloat64MeasurementValue(value), labels) } +// Record adds a new value to the list of measure's records. The +// labels should contain the keys and values specified in the measure +// with the WithKeys option. func (c *Int64Measure) Record(ctx context.Context, value int64, labels LabelSet) { c.recordOne(ctx, NewInt64MeasurementValue(value), labels) } +// 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)) } +// 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)) } diff --git a/api/metric/observer.go b/api/metric/observer.go index 98a9cbc0871..0d93997b879 100644 --- a/api/metric/observer.go +++ b/api/metric/observer.go @@ -14,14 +14,17 @@ package metric +// Observer is a base of typed-observers. Shouldn't be used directly. type Observer struct { *Descriptor } +// Float64Observer is an observer that reports float64 values. type Float64Observer struct { Observer } +// Int64Observer is an observer that reports int64 values. type Int64Observer struct { Observer } @@ -34,11 +37,13 @@ func newObserver(name string, valueKind ValueKind, mos ...GaugeOptionApplier) (o return } +// NewFloat64Observer creates a new observer for float64. func NewFloat64Observer(name string, mos ...GaugeOptionApplier) (o Float64Observer) { o.Observer = newObserver(name, Float64ValueKind, mos...) return } +// NewInt64Observer creates a new observer for int64. func NewInt64Observer(name string, mos ...GaugeOptionApplier) (o Int64Observer) { o.Observer = newObserver(name, Int64ValueKind, mos...) return From 9b9c2741e445ea02f77ec703b4d792a6b5dd8952 Mon Sep 17 00:00:00 2001 From: Krzesimir Nowak Date: Mon, 7 Oct 2019 22:25:53 +0200 Subject: [PATCH 35/35] do not panic if there is no span ID in the event --- .../streaming/exporter/spandata/spandata.go | 34 +++++++++---------- 1 file changed, 17 insertions(+), 17 deletions(-) diff --git a/experimental/streaming/exporter/spandata/spandata.go b/experimental/streaming/exporter/spandata/spandata.go index 54e7f77f9bc..fda54b2f26b 100644 --- a/experimental/streaming/exporter/spandata/spandata.go +++ b/experimental/streaming/exporter/spandata/spandata.go @@ -42,18 +42,17 @@ func NewReaderObserver(readers ...Reader) exporter.Observer { } func (s *spanReader) Read(data reader.Event) { - if !data.SpanContext.HasSpanID() { - panic("How is this?") - } var span *Span - if data.Type == exporter.START_SPAN { - span = &Span{Events: make([]reader.Event, 0, 4)} - s.spans[data.SpanContext] = span - } else { - span = s.spans[data.SpanContext] - if span == nil { - // TODO count and report this. - return + if data.SpanContext.HasSpanID() { + if data.Type == exporter.START_SPAN { + span = &Span{Events: make([]reader.Event, 0, 4)} + s.spans[data.SpanContext] = span + } else { + span = s.spans[data.SpanContext] + if span == nil { + // TODO count and report this. + return + } } } @@ -63,13 +62,14 @@ func (s *spanReader) Read(data reader.Event) { return } - span.Events = append(span.Events, data) - - if data.Type == exporter.END_SPAN { - for _, r := range s.readers { - r.Read(span) + if span != nil { + span.Events = append(span.Events, data) + if data.Type == exporter.END_SPAN { + for _, r := range s.readers { + r.Read(span) + } + delete(s.spans, data.SpanContext) } - delete(s.spans, data.SpanContext) } }