Refactor gauge and registry to accommodate cumulative. (#1089)
* Refactor gauge and registry to accomodate cummulative.
- use common baseMetric type to manage gauge and cumulative.
* fix copyright and renamed couple of func.
diff --git a/metric/common.go b/metric/common.go
new file mode 100644
index 0000000..3dcaf19
--- /dev/null
+++ b/metric/common.go
@@ -0,0 +1,126 @@
+// Copyright 2019, OpenCensus 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 (
+ "sync"
+ "time"
+
+ "go.opencensus.io/internal/tagencoding"
+ "go.opencensus.io/metric/metricdata"
+)
+
+// baseMetric is common representation for gauge and cumulative metrics.
+//
+// baseMetric maintains a value for each combination of of label values passed to
+// Set, Add, or Inc method.
+//
+// baseMetric should not be used directly, use metric specific type such as
+// Float64Gauge or Int64Gauge.
+type baseMetric struct {
+ vals sync.Map
+ desc metricdata.Descriptor
+ start time.Time
+ keys []string
+ bmType baseMetricType
+}
+
+type baseMetricType int
+
+const (
+ gaugeInt64 baseMetricType = iota
+ gaugeFloat64
+ derivedGaugeInt64
+ derivedGaugeFloat64
+ cumulativeInt64
+ cumulativeFloat64
+ derivedCumulativeInt64
+ derivedCumulativeFloat64
+)
+
+type baseEntry interface {
+ read(t time.Time) metricdata.Point
+}
+
+// Read returns the current values of the baseMetric as a metric for export.
+func (bm *baseMetric) read() *metricdata.Metric {
+ now := time.Now()
+ m := &metricdata.Metric{
+ Descriptor: bm.desc,
+ }
+ bm.vals.Range(func(k, v interface{}) bool {
+ entry := v.(baseEntry)
+ key := k.(string)
+ labelVals := bm.decodeLabelVals(key)
+ m.TimeSeries = append(m.TimeSeries, &metricdata.TimeSeries{
+ StartTime: now, // Gauge value is instantaneous.
+ LabelValues: labelVals,
+ Points: []metricdata.Point{
+ entry.read(now),
+ },
+ })
+ return true
+ })
+ return m
+}
+
+func (bm *baseMetric) encodeLabelVals(labelVals []metricdata.LabelValue) string {
+ vb := &tagencoding.Values{}
+ for _, v := range labelVals {
+ b := make([]byte, 1, len(v.Value)+1)
+ if v.Present {
+ b[0] = 1
+ b = append(b, []byte(v.Value)...)
+ }
+ vb.WriteValue(b)
+ }
+ return string(vb.Bytes())
+}
+
+func (bm *baseMetric) decodeLabelVals(s string) []metricdata.LabelValue {
+ vals := make([]metricdata.LabelValue, 0, len(bm.keys))
+ vb := &tagencoding.Values{Buffer: []byte(s)}
+ for range bm.keys {
+ v := vb.ReadValue()
+ if v[0] == 0 {
+ vals = append(vals, metricdata.LabelValue{})
+ } else {
+ vals = append(vals, metricdata.NewLabelValue(string(v[1:])))
+ }
+ }
+ return vals
+}
+
+func (bm *baseMetric) entryForValues(labelVals []metricdata.LabelValue, newEntry func() baseEntry) (interface{}, error) {
+ if len(labelVals) != len(bm.keys) {
+ return nil, errKeyValueMismatch
+ }
+ mapKey := bm.encodeLabelVals(labelVals)
+ if entry, ok := bm.vals.Load(mapKey); ok {
+ return entry, nil
+ }
+ entry, _ := bm.vals.LoadOrStore(mapKey, newEntry())
+ return entry, nil
+}
+
+func (bm *baseMetric) upsertEntry(labelVals []metricdata.LabelValue, newEntry func() baseEntry) error {
+ if len(labelVals) != len(bm.keys) {
+ return errKeyValueMismatch
+ }
+ mapKey := bm.encodeLabelVals(labelVals)
+ bm.vals.Delete(mapKey)
+ bm.vals.Store(mapKey, newEntry())
+ return nil
+}
diff --git a/metric/doc.go b/metric/doc.go
index 485ee8f..4b69f9a 100644
--- a/metric/doc.go
+++ b/metric/doc.go
@@ -12,7 +12,7 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-// Package metric support for gauge metrics.
+// Package metric support for gauge and cumulative metrics.
//
// This is an EXPERIMENTAL package, and may change in arbitrary ways without
// notice.
diff --git a/metric/error_const.go b/metric/error_const.go
index c2bdf2b..da15894 100644
--- a/metric/error_const.go
+++ b/metric/error_const.go
@@ -17,7 +17,7 @@
import "errors"
var (
- errInvalidParam = errors.New("invalid parameter")
- errGaugeExistsWithDiffType = errors.New("gauge with same name exists with a different type")
- errKeyValueMismatch = errors.New("must supply the same number of label values as keys used to construct this gauge")
+ errInvalidParam = errors.New("invalid parameter")
+ errMetricExistsWithDiffType = errors.New("metric with same name exists with a different type")
+ errKeyValueMismatch = errors.New("must supply the same number of label values as keys used to construct this metric")
)
diff --git a/metric/gauge.go b/metric/gauge.go
index 0f5dcba..d810484 100644
--- a/metric/gauge.go
+++ b/metric/gauge.go
@@ -16,110 +16,18 @@
import (
"math"
- "sync"
"sync/atomic"
"time"
- "go.opencensus.io/internal/tagencoding"
"go.opencensus.io/metric/metricdata"
)
-// gauge represents a quantity that can go up an down, for example queue depth
-// or number of outstanding requests.
-//
-// gauge maintains a value for each combination of of label values passed to
-// the Set or Add methods.
-//
-// gauge should not be used directly, use Float64Gauge or Int64Gauge.
-type gauge struct {
- vals sync.Map
- desc metricdata.Descriptor
- start time.Time
- keys []string
- gType gaugeType
-}
-
-type gaugeEntry interface {
- read(t time.Time) metricdata.Point
-}
-
-// Read returns the current values of the gauge as a metric for export.
-func (g *gauge) read() *metricdata.Metric {
- now := time.Now()
- m := &metricdata.Metric{
- Descriptor: g.desc,
- }
- g.vals.Range(func(k, v interface{}) bool {
- entry := v.(gaugeEntry)
- key := k.(string)
- labelVals := g.labelValues(key)
- m.TimeSeries = append(m.TimeSeries, &metricdata.TimeSeries{
- StartTime: now, // Gauge value is instantaneous.
- LabelValues: labelVals,
- Points: []metricdata.Point{
- entry.read(now),
- },
- })
- return true
- })
- return m
-}
-
-func (g *gauge) mapKey(labelVals []metricdata.LabelValue) string {
- vb := &tagencoding.Values{}
- for _, v := range labelVals {
- b := make([]byte, 1, len(v.Value)+1)
- if v.Present {
- b[0] = 1
- b = append(b, []byte(v.Value)...)
- }
- vb.WriteValue(b)
- }
- return string(vb.Bytes())
-}
-
-func (g *gauge) labelValues(s string) []metricdata.LabelValue {
- vals := make([]metricdata.LabelValue, 0, len(g.keys))
- vb := &tagencoding.Values{Buffer: []byte(s)}
- for range g.keys {
- v := vb.ReadValue()
- if v[0] == 0 {
- vals = append(vals, metricdata.LabelValue{})
- } else {
- vals = append(vals, metricdata.NewLabelValue(string(v[1:])))
- }
- }
- return vals
-}
-
-func (g *gauge) entryForValues(labelVals []metricdata.LabelValue, newEntry func() gaugeEntry) (interface{}, error) {
- if len(labelVals) != len(g.keys) {
- return nil, errKeyValueMismatch
- }
- mapKey := g.mapKey(labelVals)
- if entry, ok := g.vals.Load(mapKey); ok {
- return entry, nil
- }
- entry, _ := g.vals.LoadOrStore(mapKey, newEntry())
- return entry, nil
-}
-
-func (g *gauge) upsertEntry(labelVals []metricdata.LabelValue, newEntry func() gaugeEntry) error {
- if len(labelVals) != len(g.keys) {
- return errKeyValueMismatch
- }
- mapKey := g.mapKey(labelVals)
- g.vals.Delete(mapKey)
- g.vals.Store(mapKey, newEntry())
- return nil
-}
-
// Float64Gauge represents a float64 value that can go up and down.
//
// Float64Gauge maintains a float64 value for each combination of of label values
// passed to the Set or Add methods.
type Float64Gauge struct {
- g gauge
+ bm baseMetric
}
// Float64Entry represents a single value of the gauge corresponding to a set
@@ -142,7 +50,7 @@
// The number of label values supplied must be exactly the same as the number
// of keys supplied when this gauge was created.
func (g *Float64Gauge) GetEntry(labelVals ...metricdata.LabelValue) (*Float64Entry, error) {
- entry, err := g.g.entryForValues(labelVals, func() gaugeEntry {
+ entry, err := g.bm.entryForValues(labelVals, func() baseEntry {
return &Float64Entry{}
})
if err != nil {
@@ -171,7 +79,7 @@
// Int64Gauge maintains an int64 value for each combination of label values passed to the
// Set or Add methods.
type Int64Gauge struct {
- g gauge
+ bm baseMetric
}
// Int64GaugeEntry represents a single value of the gauge corresponding to a set
@@ -194,7 +102,7 @@
// The number of label values supplied must be exactly the same as the number
// of keys supplied when this gauge was created.
func (g *Int64Gauge) GetEntry(labelVals ...metricdata.LabelValue) (*Int64GaugeEntry, error) {
- entry, err := g.g.entryForValues(labelVals, func() gaugeEntry {
+ entry, err := g.bm.entryForValues(labelVals, func() baseEntry {
return &Int64GaugeEntry{}
})
if err != nil {
@@ -219,7 +127,7 @@
// These objects implement Int64DerivedGaugeInterface to read instantaneous value
// representing the object.
type Int64DerivedGauge struct {
- g gauge
+ bm baseMetric
}
type int64DerivedGaugeEntry struct {
@@ -241,7 +149,7 @@
if fn == nil {
return errInvalidParam
}
- return g.g.upsertEntry(labelVals, func() gaugeEntry {
+ return g.bm.upsertEntry(labelVals, func() baseEntry {
return &int64DerivedGaugeEntry{fn}
})
}
@@ -252,7 +160,7 @@
// These objects implement Float64DerivedGaugeInterface to read instantaneous value
// representing the object.
type Float64DerivedGauge struct {
- g gauge
+ bm baseMetric
}
type float64DerivedGaugeEntry struct {
@@ -274,7 +182,7 @@
if fn == nil {
return errInvalidParam
}
- return g.g.upsertEntry(labelVals, func() gaugeEntry {
+ return g.bm.upsertEntry(labelVals, func() baseEntry {
return &float64DerivedGaugeEntry{fn}
})
}
diff --git a/metric/gauge_test.go b/metric/gauge_test.go
index 8ed358b..b8c41d1 100644
--- a/metric/gauge_test.go
+++ b/metric/gauge_test.go
@@ -86,13 +86,13 @@
r := NewRegistry()
gf, _ := r.AddFloat64Gauge("float64_gauge")
- compareType(gf.g.desc.Type, metricdata.TypeGaugeFloat64, t)
+ compareType(gf.bm.desc.Type, metricdata.TypeGaugeFloat64, t)
gi, _ := r.AddInt64Gauge("int64_gauge")
- compareType(gi.g.desc.Type, metricdata.TypeGaugeInt64, t)
+ compareType(gi.bm.desc.Type, metricdata.TypeGaugeInt64, t)
dgf, _ := r.AddFloat64DerivedGauge("derived_float64_gauge")
- compareType(dgf.g.desc.Type, metricdata.TypeGaugeFloat64, t)
+ compareType(dgf.bm.desc.Type, metricdata.TypeGaugeFloat64, t)
dgi, _ := r.AddInt64DerivedGauge("derived_int64_gauge")
- compareType(dgi.g.desc.Type, metricdata.TypeGaugeInt64, t)
+ compareType(dgi.bm.desc.Type, metricdata.TypeGaugeInt64, t)
}
func compareType(got, want metricdata.Type, t *testing.T) {
@@ -110,7 +110,7 @@
Description: "test",
Type: metricdata.TypeGaugeFloat64,
}
- got := gf.g.desc
+ got := gf.bm.desc
if !cmp.Equal(got, want) {
t.Errorf("metric option description: got %v, want %v\n", got, want)
}
@@ -125,7 +125,7 @@
Unit: metricdata.UnitMilliseconds,
Type: metricdata.TypeGaugeFloat64,
}
- got := gf.g.desc
+ got := gf.bm.desc
if !cmp.Equal(got, want) {
t.Errorf("metric descriptor: got %v, want %v\n", got, want)
}
@@ -140,7 +140,7 @@
LabelKeys: []string{"k1", "k3"},
Type: metricdata.TypeGaugeFloat64,
}
- got := gf.g.desc
+ got := gf.bm.desc
if !cmp.Equal(got, want) {
t.Errorf("metric descriptor: got %v, want %v\n", got, want)
}
@@ -154,7 +154,7 @@
Name: name,
Type: metricdata.TypeGaugeFloat64,
}
- got := gf.g.desc
+ got := gf.bm.desc
if !cmp.Equal(got, want) {
t.Errorf("metric descriptor: got %v, want %v\n", got, want)
}
@@ -227,15 +227,15 @@
r.AddInt64Gauge("g")
_, gotErr := r.AddFloat64Gauge("g")
if gotErr == nil {
- t.Errorf("got: nil, want error: %v", errGaugeExistsWithDiffType)
+ t.Errorf("got: nil, want error: %v", errMetricExistsWithDiffType)
}
_, gotErr = r.AddInt64DerivedGauge("g")
if gotErr == nil {
- t.Errorf("got: nil, want error: %v", errGaugeExistsWithDiffType)
+ t.Errorf("got: nil, want error: %v", errMetricExistsWithDiffType)
}
_, gotErr = r.AddFloat64DerivedGauge("g")
if gotErr == nil {
- t.Errorf("got: nil, want error: %v", errGaugeExistsWithDiffType)
+ t.Errorf("got: nil, want error: %v", errMetricExistsWithDiffType)
}
}
@@ -262,11 +262,11 @@
}
for i, tc := range cases {
t.Run(fmt.Sprintf("case %d", i), func(t *testing.T) {
- g := &gauge{
+ g := &baseMetric{
keys: make([]string, len(tc)),
}
- mk := g.mapKey(tc)
- vals := g.labelValues(mk)
+ mk := g.encodeLabelVals(tc)
+ vals := g.decodeLabelVals(mk)
if diff := cmp.Diff(vals, tc); diff != "" {
t.Errorf("values differ after serialization -got +want: %s", diff)
}
diff --git a/metric/registry.go b/metric/registry.go
index 80df542..6b1ff32 100644
--- a/metric/registry.go
+++ b/metric/registry.go
@@ -21,22 +21,13 @@
"go.opencensus.io/metric/metricdata"
)
-// Registry creates and manages a set of gauges.
-// External synchronization is required if you want to add gauges to the same
+// Registry creates and manages a set of gauges and cumulative.
+// External synchronization is required if you want to add gauges and cumulative to the same
// registry from multiple goroutines.
type Registry struct {
- gauges sync.Map
+ baseMetrics sync.Map
}
-type gaugeType int
-
-const (
- gaugeInt64 gaugeType = iota
- gaugeFloat64
- derivedGaugeInt64
- derivedGaugeFloat64
-)
-
//TODO: [rghetia] add constant labels.
type metricOptions struct {
unit metricdata.Unit
@@ -76,11 +67,11 @@
// AddFloat64Gauge creates and adds a new float64-valued gauge to this registry.
func (r *Registry) AddFloat64Gauge(name string, mos ...Options) (*Float64Gauge, error) {
f := &Float64Gauge{
- g: gauge{
- gType: gaugeFloat64,
+ bm: baseMetric{
+ bmType: gaugeFloat64,
},
}
- _, err := r.initGauge(&f.g, name, mos...)
+ _, err := r.initBaseMetric(&f.bm, name, mos...)
if err != nil {
return nil, err
}
@@ -90,11 +81,11 @@
// AddInt64Gauge creates and adds a new int64-valued gauge to this registry.
func (r *Registry) AddInt64Gauge(name string, mos ...Options) (*Int64Gauge, error) {
i := &Int64Gauge{
- g: gauge{
- gType: gaugeInt64,
+ bm: baseMetric{
+ bmType: gaugeInt64,
},
}
- _, err := r.initGauge(&i.g, name, mos...)
+ _, err := r.initBaseMetric(&i.bm, name, mos...)
if err != nil {
return nil, err
}
@@ -106,11 +97,11 @@
// provides its value by implementing func() int64.
func (r *Registry) AddInt64DerivedGauge(name string, mos ...Options) (*Int64DerivedGauge, error) {
i := &Int64DerivedGauge{
- g: gauge{
- gType: derivedGaugeInt64,
+ bm: baseMetric{
+ bmType: derivedGaugeInt64,
},
}
- _, err := r.initGauge(&i.g, name, mos...)
+ _, err := r.initBaseMetric(&i.bm, name, mos...)
if err != nil {
return nil, err
}
@@ -122,19 +113,19 @@
// provides its value by implementing func() float64.
func (r *Registry) AddFloat64DerivedGauge(name string, mos ...Options) (*Float64DerivedGauge, error) {
f := &Float64DerivedGauge{
- g: gauge{
- gType: derivedGaugeFloat64,
+ bm: baseMetric{
+ bmType: derivedGaugeFloat64,
},
}
- _, err := r.initGauge(&f.g, name, mos...)
+ _, err := r.initBaseMetric(&f.bm, name, mos...)
if err != nil {
return nil, err
}
return f, nil
}
-func gTypeToMetricType(g *gauge) metricdata.Type {
- switch g.gType {
+func bmTypeToMetricType(bm *baseMetric) metricdata.Type {
+ switch bm.bmType {
case derivedGaugeFloat64:
return metricdata.TypeGaugeFloat64
case derivedGaugeInt64:
@@ -144,7 +135,7 @@
case gaugeInt64:
return metricdata.TypeGaugeInt64
default:
- panic("unsupported gauge type")
+ panic("unsupported metric type")
}
}
@@ -156,34 +147,34 @@
return o
}
-func (r *Registry) initGauge(g *gauge, name string, mos ...Options) (*gauge, error) {
- val, ok := r.gauges.Load(name)
+func (r *Registry) initBaseMetric(bm *baseMetric, name string, mos ...Options) (*baseMetric, error) {
+ val, ok := r.baseMetrics.Load(name)
if ok {
- existing := val.(*gauge)
- if existing.gType != g.gType {
- return nil, errGaugeExistsWithDiffType
+ existing := val.(*baseMetric)
+ if existing.bmType != bm.bmType {
+ return nil, errMetricExistsWithDiffType
}
}
- g.start = time.Now()
+ bm.start = time.Now()
o := createMetricOption(mos...)
- g.keys = o.labelkeys
- g.desc = metricdata.Descriptor{
+ bm.keys = o.labelkeys
+ bm.desc = metricdata.Descriptor{
Name: name,
Description: o.desc,
Unit: o.unit,
LabelKeys: o.labelkeys,
- Type: gTypeToMetricType(g),
+ Type: bmTypeToMetricType(bm),
}
- r.gauges.Store(name, g)
- return g, nil
+ r.baseMetrics.Store(name, bm)
+ return bm, nil
}
// Read reads all gauges in this registry and returns their values as metrics.
func (r *Registry) Read() []*metricdata.Metric {
ms := []*metricdata.Metric{}
- r.gauges.Range(func(k, v interface{}) bool {
- g := v.(*gauge)
- ms = append(ms, g.read())
+ r.baseMetrics.Range(func(k, v interface{}) bool {
+ bm := v.(*baseMetric)
+ ms = append(ms, bm.read())
return true
})
return ms