| // 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} |
| }) |
| } |