Skip to content

Commit

Permalink
Refactor gauge and registry to accommodate cumulative. (census-instru…
Browse files Browse the repository at this point in the history
…mentation#1089)

* Refactor gauge and registry to accomodate cummulative.
- use common baseMetric type to manage gauge and cumulative.

* fix copyright and renamed couple of func.
  • Loading branch information
rghetia committed Apr 25, 2019
1 parent 75c0cca commit 7a33b99
Show file tree
Hide file tree
Showing 6 changed files with 184 additions and 159 deletions.
126 changes: 126 additions & 0 deletions metric/common.go
Original file line number Diff line number Diff line change
@@ -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
}
2 changes: 1 addition & 1 deletion metric/doc.go
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
6 changes: 3 additions & 3 deletions metric/error_const.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ package metric
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")
)
108 changes: 8 additions & 100 deletions metric/gauge.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,110 +16,18 @@ package metric

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
Expand All @@ -142,7 +50,7 @@ func (e *Float64Entry) read(t time.Time) metricdata.Point {
// 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 {
Expand Down Expand Up @@ -171,7 +79,7 @@ func (e *Float64Entry) Add(val float64) {
// 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
Expand All @@ -194,7 +102,7 @@ func (e *Int64GaugeEntry) read(t time.Time) metricdata.Point {
// 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 {
Expand All @@ -219,7 +127,7 @@ func (e *Int64GaugeEntry) Add(val int64) {
// These objects implement Int64DerivedGaugeInterface to read instantaneous value
// representing the object.
type Int64DerivedGauge struct {
g gauge
bm baseMetric
}

type int64DerivedGaugeEntry struct {
Expand All @@ -241,7 +149,7 @@ func (g *Int64DerivedGauge) UpsertEntry(fn func() int64, labelVals ...metricdata
if fn == nil {
return errInvalidParam
}
return g.g.upsertEntry(labelVals, func() gaugeEntry {
return g.bm.upsertEntry(labelVals, func() baseEntry {
return &int64DerivedGaugeEntry{fn}
})
}
Expand All @@ -252,7 +160,7 @@ func (g *Int64DerivedGauge) UpsertEntry(fn func() int64, labelVals ...metricdata
// These objects implement Float64DerivedGaugeInterface to read instantaneous value
// representing the object.
type Float64DerivedGauge struct {
g gauge
bm baseMetric
}

type float64DerivedGaugeEntry struct {
Expand All @@ -274,7 +182,7 @@ func (g *Float64DerivedGauge) UpsertEntry(fn func() float64, labelVals ...metric
if fn == nil {
return errInvalidParam
}
return g.g.upsertEntry(labelVals, func() gaugeEntry {
return g.bm.upsertEntry(labelVals, func() baseEntry {
return &float64DerivedGaugeEntry{fn}
})
}
Loading

0 comments on commit 7a33b99

Please sign in to comment.