Skip to content
This repository has been archived by the owner on Jul 31, 2023. It is now read-only.

Refactor gauge and registry to accommodate cumulative. #1089

Merged
merged 2 commits into from
Apr 3, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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