|
- // Copyright 2018, 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 (
- "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
- }
-
- // Float64Entry represents a single value of the gauge corresponding to a set
- // of label values.
- type Float64Entry struct {
- val uint64 // needs to be uint64 for atomic access, interpret with math.Float64frombits
- }
-
- func (e *Float64Entry) read(t time.Time) metricdata.Point {
- v := math.Float64frombits(atomic.LoadUint64(&e.val))
- if v < 0 {
- v = 0
- }
- return metricdata.NewFloat64Point(t, v)
- }
-
- // GetEntry returns a gauge entry where each key for this gauge has the value
- // given.
- //
- // 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 {
- return &Float64Entry{}
- })
- if err != nil {
- return nil, err
- }
- return entry.(*Float64Entry), nil
- }
-
- // Set sets the gauge entry value to val.
- func (e *Float64Entry) Set(val float64) {
- atomic.StoreUint64(&e.val, math.Float64bits(val))
- }
-
- // Add increments the gauge entry value by val.
- func (e *Float64Entry) Add(val float64) {
- var swapped bool
- for !swapped {
- oldVal := atomic.LoadUint64(&e.val)
- newVal := math.Float64bits(math.Float64frombits(oldVal) + val)
- swapped = atomic.CompareAndSwapUint64(&e.val, oldVal, newVal)
- }
- }
-
- // Int64Gauge represents a int64 gauge value that can go up and down.
- //
- // Int64Gauge maintains an int64 value for each combination of label values passed to the
- // Set or Add methods.
- type Int64Gauge struct {
- g gauge
- }
-
- // Int64GaugeEntry represents a single value of the gauge corresponding to a set
- // of label values.
- type Int64GaugeEntry struct {
- val int64
- }
-
- func (e *Int64GaugeEntry) read(t time.Time) metricdata.Point {
- v := atomic.LoadInt64(&e.val)
- if v < 0 {
- v = 0.0
- }
- return metricdata.NewInt64Point(t, v)
- }
-
- // GetEntry returns a gauge entry where each key for this gauge has the value
- // given.
- //
- // 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 {
- return &Int64GaugeEntry{}
- })
- if err != nil {
- return nil, err
- }
- return entry.(*Int64GaugeEntry), nil
- }
-
- // Set sets the value of the gauge entry to the provided value.
- func (e *Int64GaugeEntry) Set(val int64) {
- atomic.StoreInt64(&e.val, val)
- }
-
- // Add increments the current gauge entry value by val, which may be negative.
- func (e *Int64GaugeEntry) Add(val int64) {
- atomic.AddInt64(&e.val, val)
- }
-
- // Int64DerivedGauge represents int64 gauge value that is derived from an object.
- //
- // Int64DerivedGauge maintains objects for each combination of label values.
- // These objects implement Int64DerivedGaugeInterface to read instantaneous value
- // representing the object.
- type Int64DerivedGauge struct {
- g gauge
- }
-
- type int64DerivedGaugeEntry struct {
- fn func() int64
- }
-
- func (e *int64DerivedGaugeEntry) read(t time.Time) metricdata.Point {
- return metricdata.NewInt64Point(t, e.fn())
- }
-
- // UpsertEntry inserts or updates a derived gauge entry for the given set of label values.
- // The object for which this gauge entry is inserted or updated, must implement func() int64
- //
- // It returns an error if
- // 1. The number of label values supplied are not the same as the number
- // of keys supplied when this gauge was created.
- // 2. fn func() int64 is nil.
- func (g *Int64DerivedGauge) UpsertEntry(fn func() int64, labelVals ...metricdata.LabelValue) error {
- if fn == nil {
- return errInvalidParam
- }
- return g.g.upsertEntry(labelVals, func() gaugeEntry {
- return &int64DerivedGaugeEntry{fn}
- })
- }
-
- // Float64DerivedGauge represents float64 gauge value that is derived from an object.
- //
- // Float64DerivedGauge maintains objects for each combination of label values.
- // These objects implement Float64DerivedGaugeInterface to read instantaneous value
- // representing the object.
- type Float64DerivedGauge struct {
- g gauge
- }
-
- type float64DerivedGaugeEntry struct {
- fn func() float64
- }
-
- func (e *float64DerivedGaugeEntry) read(t time.Time) metricdata.Point {
- return metricdata.NewFloat64Point(t, e.fn())
- }
-
- // UpsertEntry inserts or updates a derived gauge entry for the given set of label values.
- // The object for which this gauge entry is inserted or updated, must implement func() float64
- //
- // It returns an error if
- // 1. The number of label values supplied are not the same as the number
- // of keys supplied when this gauge was created.
- // 2. fn func() float64 is nil.
- func (g *Float64DerivedGauge) UpsertEntry(fn func() float64, labelVals ...metricdata.LabelValue) error {
- if fn == nil {
- return errInvalidParam
- }
- return g.g.upsertEntry(labelVals, func() gaugeEntry {
- return &float64DerivedGaugeEntry{fn}
- })
- }
|