Add support for cumulative. (#1090)
* Add support for cumulative.
* fix review comments.
diff --git a/metric/common.go b/metric/common.go
index 3dcaf19..c370f7b 100644
--- a/metric/common.go
+++ b/metric/common.go
@@ -24,7 +24,7 @@
// baseMetric is common representation for gauge and cumulative metrics.
//
-// baseMetric maintains a value for each combination of of label values passed to
+// baseMetric maintains a value for each combination of label values passed to
// Set, Add, or Inc method.
//
// baseMetric should not be used directly, use metric specific type such as
@@ -54,9 +54,23 @@
read(t time.Time) metricdata.Point
}
+func (bm *baseMetric) startTime() *time.Time {
+ switch bm.bmType {
+ case cumulativeInt64, cumulativeFloat64, derivedCumulativeInt64, derivedCumulativeFloat64:
+ return &bm.start
+ default:
+ // gauges don't have start time.
+ return nil
+ }
+}
+
// Read returns the current values of the baseMetric as a metric for export.
func (bm *baseMetric) read() *metricdata.Metric {
now := time.Now()
+ startTime := bm.startTime()
+ if startTime == nil {
+ startTime = &now
+ }
m := &metricdata.Metric{
Descriptor: bm.desc,
}
@@ -65,7 +79,7 @@
key := k.(string)
labelVals := bm.decodeLabelVals(key)
m.TimeSeries = append(m.TimeSeries, &metricdata.TimeSeries{
- StartTime: now, // Gauge value is instantaneous.
+ StartTime: *startTime,
LabelValues: labelVals,
Points: []metricdata.Point{
entry.read(now),
diff --git a/metric/cumulative.go b/metric/cumulative.go
new file mode 100644
index 0000000..6d3be3f
--- /dev/null
+++ b/metric/cumulative.go
@@ -0,0 +1,224 @@
+// 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 (
+ "math"
+ "sync/atomic"
+ "time"
+
+ "go.opencensus.io/metric/metricdata"
+)
+
+// Float64Cumulative represents a float64 value that can only go up.
+//
+// Float64Cumulative maintains a float64 value for each combination of label values
+// passed to the Set or Inc methods.
+type Float64Cumulative struct {
+ bm baseMetric
+}
+
+// Float64CumulativeEntry represents a single value of the cumulative corresponding to a set
+// of label values.
+type Float64CumulativeEntry struct {
+ val uint64 // needs to be uint64 for atomic access, interpret with math.Float64frombits
+}
+
+func (e *Float64CumulativeEntry) 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 cumulative entry where each key for this cumulative has the value
+// given.
+//
+// The number of label values supplied must be exactly the same as the number
+// of keys supplied when this cumulative was created.
+func (c *Float64Cumulative) GetEntry(labelVals ...metricdata.LabelValue) (*Float64CumulativeEntry, error) {
+ entry, err := c.bm.entryForValues(labelVals, func() baseEntry {
+ return &Float64CumulativeEntry{}
+ })
+ if err != nil {
+ return nil, err
+ }
+ return entry.(*Float64CumulativeEntry), nil
+}
+
+// Set sets the cumulative entry value to provided val. It returns without updating if the value is
+// negative or lower than previously stored value.
+func (e *Float64CumulativeEntry) Set(val float64) {
+ var swapped, equalOrLess bool
+ if val <= 0.0 {
+ return
+ }
+ for !swapped && !equalOrLess {
+ oldBits := atomic.LoadUint64(&e.val)
+ oldVal := math.Float64frombits(oldBits)
+ if val > oldVal {
+ valBits := math.Float64bits(val)
+ swapped = atomic.CompareAndSwapUint64(&e.val, oldBits, valBits)
+ } else {
+ equalOrLess = true
+ }
+ }
+}
+
+// Inc increments the cumulative entry value by val. It returns without incrementing if the val
+// is negative.
+func (e *Float64CumulativeEntry) Inc(val float64) {
+ var swapped bool
+ if val <= 0.0 {
+ return
+ }
+ for !swapped {
+ oldVal := atomic.LoadUint64(&e.val)
+ newVal := math.Float64bits(math.Float64frombits(oldVal) + val)
+ swapped = atomic.CompareAndSwapUint64(&e.val, oldVal, newVal)
+ }
+}
+
+// Int64Cumulative represents a int64 cumulative value that can only go up.
+//
+// Int64Cumulative maintains an int64 value for each combination of label values passed to the
+// Set or Inc methods.
+type Int64Cumulative struct {
+ bm baseMetric
+}
+
+// Int64CumulativeEntry represents a single value of the cumulative corresponding to a set
+// of label values.
+type Int64CumulativeEntry struct {
+ val int64
+}
+
+func (e *Int64CumulativeEntry) 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 cumulative entry where each key for this cumulative has the value
+// given.
+//
+// The number of label values supplied must be exactly the same as the number
+// of keys supplied when this cumulative was created.
+func (c *Int64Cumulative) GetEntry(labelVals ...metricdata.LabelValue) (*Int64CumulativeEntry, error) {
+ entry, err := c.bm.entryForValues(labelVals, func() baseEntry {
+ return &Int64CumulativeEntry{}
+ })
+ if err != nil {
+ return nil, err
+ }
+ return entry.(*Int64CumulativeEntry), nil
+}
+
+// Set sets the value of the cumulative entry to the provided value. It returns without updating
+// if the val is negative or if the val is lower than previously stored value.
+func (e *Int64CumulativeEntry) Set(val int64) {
+ var swapped, equalOrLess bool
+ if val <= 0 {
+ return
+ }
+ for !swapped && !equalOrLess {
+ old := atomic.LoadInt64(&e.val)
+ if val > old {
+ swapped = atomic.CompareAndSwapInt64(&e.val, old, val)
+ } else {
+ equalOrLess = true
+ }
+ }
+}
+
+// Inc increments the current cumulative entry value by val. It returns without incrementing if
+// the val is negative.
+func (e *Int64CumulativeEntry) Inc(val int64) {
+ if val <= 0 {
+ return
+ }
+ atomic.AddInt64(&e.val, int64(val))
+}
+
+// Int64DerivedCumulative represents int64 cumulative value that is derived from an object.
+//
+// Int64DerivedCumulative maintains objects for each combination of label values.
+// These objects implement Int64DerivedCumulativeInterface to read instantaneous value
+// representing the object.
+type Int64DerivedCumulative struct {
+ bm baseMetric
+}
+
+type int64DerivedCumulativeEntry struct {
+ fn func() int64
+}
+
+func (e *int64DerivedCumulativeEntry) read(t time.Time) metricdata.Point {
+ // TODO: [rghetia] handle a condition where new value return by fn is lower than previous call.
+ // It requires that we maintain the old values.
+ return metricdata.NewInt64Point(t, e.fn())
+}
+
+// UpsertEntry inserts or updates a derived cumulative entry for the given set of label values.
+// The object for which this cumulative 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 cumulative was created.
+// 2. fn func() int64 is nil.
+func (c *Int64DerivedCumulative) UpsertEntry(fn func() int64, labelVals ...metricdata.LabelValue) error {
+ if fn == nil {
+ return errInvalidParam
+ }
+ return c.bm.upsertEntry(labelVals, func() baseEntry {
+ return &int64DerivedCumulativeEntry{fn}
+ })
+}
+
+// Float64DerivedCumulative represents float64 cumulative value that is derived from an object.
+//
+// Float64DerivedCumulative maintains objects for each combination of label values.
+// These objects implement Float64DerivedCumulativeInterface to read instantaneous value
+// representing the object.
+type Float64DerivedCumulative struct {
+ bm baseMetric
+}
+
+type float64DerivedCumulativeEntry struct {
+ fn func() float64
+}
+
+func (e *float64DerivedCumulativeEntry) read(t time.Time) metricdata.Point {
+ return metricdata.NewFloat64Point(t, e.fn())
+}
+
+// UpsertEntry inserts or updates a derived cumulative entry for the given set of label values.
+// The object for which this cumulative 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 cumulative was created.
+// 2. fn func() float64 is nil.
+func (c *Float64DerivedCumulative) UpsertEntry(fn func() float64, labelVals ...metricdata.LabelValue) error {
+ if fn == nil {
+ return errInvalidParam
+ }
+ return c.bm.upsertEntry(labelVals, func() baseEntry {
+ return &float64DerivedCumulativeEntry{fn}
+ })
+}
diff --git a/metric/cumulative_test.go b/metric/cumulative_test.go
new file mode 100644
index 0000000..2e0d3c2
--- /dev/null
+++ b/metric/cumulative_test.go
@@ -0,0 +1,290 @@
+// 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 (
+ "testing"
+ "time"
+
+ "github.com/google/go-cmp/cmp"
+ "go.opencensus.io/metric/metricdata"
+)
+
+func TestCumulative(t *testing.T) {
+ r := NewRegistry()
+
+ f, _ := r.AddFloat64Cumulative("TestCumulative",
+ WithLabelKeys("k1", "k2"))
+ e, _ := f.GetEntry(metricdata.LabelValue{}, metricdata.LabelValue{})
+ e.Set(5)
+ e, _ = f.GetEntry(metricdata.NewLabelValue("k1v1"), metricdata.LabelValue{})
+ e.Inc(1)
+ e, _ = f.GetEntry(metricdata.NewLabelValue("k1v1"), metricdata.LabelValue{})
+ e.Inc(1)
+ e, _ = f.GetEntry(metricdata.NewLabelValue("k1v2"), metricdata.NewLabelValue("k2v2"))
+ e.Inc(1)
+ m := r.Read()
+ want := []*metricdata.Metric{
+ {
+ Descriptor: metricdata.Descriptor{
+ Name: "TestCumulative",
+ LabelKeys: []string{"k1", "k2"},
+ Type: metricdata.TypeCumulativeFloat64,
+ },
+ TimeSeries: []*metricdata.TimeSeries{
+ {
+ LabelValues: []metricdata.LabelValue{
+ {}, {},
+ },
+ Points: []metricdata.Point{
+ metricdata.NewFloat64Point(time.Time{}, 5),
+ },
+ },
+ {
+ LabelValues: []metricdata.LabelValue{
+ metricdata.NewLabelValue("k1v1"),
+ {},
+ },
+ Points: []metricdata.Point{
+ metricdata.NewFloat64Point(time.Time{}, 2),
+ },
+ },
+ {
+ LabelValues: []metricdata.LabelValue{
+ metricdata.NewLabelValue("k1v2"),
+ metricdata.NewLabelValue("k2v2"),
+ },
+ Points: []metricdata.Point{
+ metricdata.NewFloat64Point(time.Time{}, 1),
+ },
+ },
+ },
+ },
+ }
+ canonicalize(m)
+ canonicalize(want)
+ if diff := cmp.Diff(m, want, cmp.Comparer(ignoreTimes)); diff != "" {
+ t.Errorf("-got +want: %s", diff)
+ }
+}
+
+func TestCumulativeMetricDescriptor(t *testing.T) {
+ r := NewRegistry()
+
+ gf, _ := r.AddFloat64Cumulative("float64_gauge")
+ compareType(gf.bm.desc.Type, metricdata.TypeCumulativeFloat64, t)
+ gi, _ := r.AddInt64Cumulative("int64_gauge")
+ compareType(gi.bm.desc.Type, metricdata.TypeCumulativeInt64, t)
+ dgf, _ := r.AddFloat64DerivedCumulative("derived_float64_gauge")
+ compareType(dgf.bm.desc.Type, metricdata.TypeCumulativeFloat64, t)
+ dgi, _ := r.AddInt64DerivedCumulative("derived_int64_gauge")
+ compareType(dgi.bm.desc.Type, metricdata.TypeCumulativeInt64, t)
+}
+
+func readAndCompareInt64Val(testname string, r *Registry, want int64, t *testing.T) {
+ ms := r.Read()
+ if got := ms[0].TimeSeries[0].Points[0].Value.(int64); got != want {
+ t.Errorf("testname: %s, got = %v, want %v\n", testname, got, want)
+ }
+}
+
+func TestInt64CumulativeEntry_IncAndSet(t *testing.T) {
+ r := NewRegistry()
+ g, _ := r.AddInt64Cumulative("bm")
+ e, _ := g.GetEntry()
+ e.Inc(5)
+ readAndCompareInt64Val("inc", r, 5, t)
+ e.Inc(-2)
+ readAndCompareInt64Val("inc negative", r, 5, t)
+ e.Set(-2)
+ readAndCompareInt64Val("set negative", r, 5, t)
+ e.Set(4)
+ readAndCompareInt64Val("set lower", r, 5, t)
+ e.Set(9)
+ readAndCompareInt64Val("set higher", r, 9, t)
+}
+
+func readAndCompareFloat64Val(testname string, r *Registry, want float64, t *testing.T) {
+ ms := r.Read()
+ if got := ms[0].TimeSeries[0].Points[0].Value.(float64); got != want {
+ t.Errorf("testname: %s, got = %v, want %v\n", testname, got, want)
+ }
+}
+
+func TestFloat64CumulativeEntry_IncAndSet(t *testing.T) {
+ r := NewRegistry()
+ g, _ := r.AddFloat64Cumulative("bm")
+ e, _ := g.GetEntry()
+ e.Inc(5.0)
+ readAndCompareFloat64Val("inc", r, 5.0, t)
+ e.Inc(-2.0)
+ readAndCompareFloat64Val("inc negative", r, 5.0, t)
+ e.Set(-2.0)
+ readAndCompareFloat64Val("set negative", r, 5.0, t)
+ e.Set(4.0)
+ readAndCompareFloat64Val("set lower", r, 5.0, t)
+ e.Set(9.9)
+ readAndCompareFloat64Val("set higher", r, 9.9, t)
+}
+
+func TestCumulativeWithSameNameDiffType(t *testing.T) {
+ r := NewRegistry()
+ r.AddInt64Cumulative("bm")
+ _, gotErr := r.AddFloat64Cumulative("bm")
+ if gotErr == nil {
+ t.Errorf("got: nil, want error: %v", errMetricExistsWithDiffType)
+ }
+ _, gotErr = r.AddInt64DerivedCumulative("bm")
+ if gotErr == nil {
+ t.Errorf("got: nil, want error: %v", errMetricExistsWithDiffType)
+ }
+ _, gotErr = r.AddFloat64DerivedCumulative("bm")
+ if gotErr == nil {
+ t.Errorf("got: nil, want error: %v", errMetricExistsWithDiffType)
+ }
+}
+
+func TestCumulativeWithLabelMismatch(t *testing.T) {
+ r := NewRegistry()
+ g, _ := r.AddInt64Cumulative("bm", WithLabelKeys("k1"))
+ _, gotErr := g.GetEntry(metricdata.NewLabelValue("k1v2"), metricdata.NewLabelValue("k2v2"))
+ if gotErr == nil {
+ t.Errorf("got: nil, want error: %v", errKeyValueMismatch)
+ }
+}
+
+type sysUpTimeInNanoSecs struct {
+ size int64
+}
+
+func (q *sysUpTimeInNanoSecs) ToInt64() int64 {
+ return q.size
+}
+
+func TestInt64DerivedCumulativeEntry_Inc(t *testing.T) {
+ r := NewRegistry()
+ q := &sysUpTimeInNanoSecs{3}
+ g, _ := r.AddInt64DerivedCumulative("bm", WithLabelKeys("k1", "k2"))
+ err := g.UpsertEntry(q.ToInt64, metricdata.NewLabelValue("k1v1"), metricdata.LabelValue{})
+ if err != nil {
+ t.Errorf("want: nil, got: %v", err)
+ }
+ ms := r.Read()
+ if got, want := ms[0].TimeSeries[0].Points[0].Value.(int64), int64(3); got != want {
+ t.Errorf("value = %v, want %v", got, want)
+ }
+ q.size = 5
+ ms = r.Read()
+ if got, want := ms[0].TimeSeries[0].Points[0].Value.(int64), int64(5); got != want {
+ t.Errorf("value = %v, want %v", got, want)
+ }
+}
+
+func TestInt64DerivedCumulativeEntry_IncWithNilObj(t *testing.T) {
+ r := NewRegistry()
+ g, _ := r.AddInt64DerivedCumulative("bm", WithLabelKeys("k1", "k2"))
+ gotErr := g.UpsertEntry(nil, metricdata.NewLabelValue("k1v1"), metricdata.LabelValue{})
+ if gotErr == nil {
+ t.Errorf("expected error but got nil")
+ }
+}
+
+func TestInt64DerivedCumulativeEntry_IncWithInvalidLabels(t *testing.T) {
+ r := NewRegistry()
+ q := &sysUpTimeInNanoSecs{3}
+ g, _ := r.AddInt64DerivedCumulative("bm", WithLabelKeys("k1", "k2"))
+ gotErr := g.UpsertEntry(q.ToInt64, metricdata.NewLabelValue("k1v1"))
+ if gotErr == nil {
+ t.Errorf("expected error but got nil")
+ }
+}
+
+func TestInt64DerivedCumulativeEntry_Update(t *testing.T) {
+ r := NewRegistry()
+ q := &sysUpTimeInNanoSecs{3}
+ q2 := &sysUpTimeInNanoSecs{5}
+ g, _ := r.AddInt64DerivedCumulative("bm", WithLabelKeys("k1", "k2"))
+ g.UpsertEntry(q.ToInt64, metricdata.NewLabelValue("k1v1"), metricdata.LabelValue{})
+ gotErr := g.UpsertEntry(q2.ToInt64, metricdata.NewLabelValue("k1v1"), metricdata.LabelValue{})
+ if gotErr != nil {
+ t.Errorf("got: %v, want: nil", gotErr)
+ }
+ ms := r.Read()
+ if got, want := ms[0].TimeSeries[0].Points[0].Value.(int64), int64(5); got != want {
+ t.Errorf("value = %v, want %v", got, want)
+ }
+}
+
+type sysUpTimeInSeconds struct {
+ size float64
+}
+
+func (q *sysUpTimeInSeconds) ToFloat64() float64 {
+ return q.size
+}
+
+func TestFloat64DerivedCumulativeEntry_Inc(t *testing.T) {
+ r := NewRegistry()
+ q := &sysUpTimeInSeconds{5.0}
+ g, _ := r.AddFloat64DerivedCumulative("bm", WithLabelKeys("k1", "k2"))
+ err := g.UpsertEntry(q.ToFloat64, metricdata.NewLabelValue("k1v1"), metricdata.LabelValue{})
+ if err != nil {
+ t.Errorf("want: nil, got: %v", err)
+ }
+ ms := r.Read()
+ if got, want := ms[0].TimeSeries[0].Points[0].Value.(float64), float64(5.0); got != want {
+ t.Errorf("value = %v, want %v", got, want)
+ }
+ q.size = 7
+ ms = r.Read()
+ if got, want := ms[0].TimeSeries[0].Points[0].Value.(float64), float64(7.0); got != want {
+ t.Errorf("value = %v, want %v", got, want)
+ }
+}
+
+func TestFloat64DerivedCumulativeEntry_IncWithNilObj(t *testing.T) {
+ r := NewRegistry()
+ g, _ := r.AddFloat64DerivedCumulative("bm", WithLabelKeys("k1", "k2"))
+ gotErr := g.UpsertEntry(nil, metricdata.NewLabelValue("k1v1"), metricdata.LabelValue{})
+ if gotErr == nil {
+ t.Errorf("expected error but got nil")
+ }
+}
+
+func TestFloat64DerivedCumulativeEntry_IncWithInvalidLabels(t *testing.T) {
+ r := NewRegistry()
+ q := &sysUpTimeInSeconds{3}
+ g, _ := r.AddFloat64DerivedCumulative("bm", WithLabelKeys("k1", "k2"))
+ gotErr := g.UpsertEntry(q.ToFloat64, metricdata.NewLabelValue("k1v1"))
+ if gotErr == nil {
+ t.Errorf("expected error but got nil")
+ }
+}
+
+func TestFloat64DerivedCumulativeEntry_Update(t *testing.T) {
+ r := NewRegistry()
+ q := &sysUpTimeInSeconds{3.0}
+ q2 := &sysUpTimeInSeconds{5.0}
+ g, _ := r.AddFloat64DerivedCumulative("bm", WithLabelKeys("k1", "k2"))
+ g.UpsertEntry(q.ToFloat64, metricdata.NewLabelValue("k1v1"), metricdata.LabelValue{})
+ gotErr := g.UpsertEntry(q2.ToFloat64, metricdata.NewLabelValue("k1v1"), metricdata.LabelValue{})
+ if gotErr != nil {
+ t.Errorf("got: %v, want: nil", gotErr)
+ }
+ ms := r.Read()
+ if got, want := ms[0].TimeSeries[0].Points[0].Value.(float64), float64(5.0); got != want {
+ t.Errorf("value = %v, want %v", got, want)
+ }
+}
diff --git a/metric/registry.go b/metric/registry.go
index 6b1ff32..5dc4163 100644
--- a/metric/registry.go
+++ b/metric/registry.go
@@ -134,11 +134,79 @@
return metricdata.TypeGaugeFloat64
case gaugeInt64:
return metricdata.TypeGaugeInt64
+ case derivedCumulativeFloat64:
+ return metricdata.TypeCumulativeFloat64
+ case derivedCumulativeInt64:
+ return metricdata.TypeCumulativeInt64
+ case cumulativeFloat64:
+ return metricdata.TypeCumulativeFloat64
+ case cumulativeInt64:
+ return metricdata.TypeCumulativeInt64
default:
panic("unsupported metric type")
}
}
+// AddFloat64Cumulative creates and adds a new float64-valued cumulative to this registry.
+func (r *Registry) AddFloat64Cumulative(name string, mos ...Options) (*Float64Cumulative, error) {
+ f := &Float64Cumulative{
+ bm: baseMetric{
+ bmType: cumulativeFloat64,
+ },
+ }
+ _, err := r.initBaseMetric(&f.bm, name, mos...)
+ if err != nil {
+ return nil, err
+ }
+ return f, nil
+}
+
+// AddInt64Cumulative creates and adds a new int64-valued cumulative to this registry.
+func (r *Registry) AddInt64Cumulative(name string, mos ...Options) (*Int64Cumulative, error) {
+ i := &Int64Cumulative{
+ bm: baseMetric{
+ bmType: cumulativeInt64,
+ },
+ }
+ _, err := r.initBaseMetric(&i.bm, name, mos...)
+ if err != nil {
+ return nil, err
+ }
+ return i, nil
+}
+
+// AddInt64DerivedCumulative creates and adds a new derived int64-valued cumulative to this registry.
+// A derived cumulative is convenient form of cumulative where the object associated with the cumulative
+// provides its value by implementing func() int64.
+func (r *Registry) AddInt64DerivedCumulative(name string, mos ...Options) (*Int64DerivedCumulative, error) {
+ i := &Int64DerivedCumulative{
+ bm: baseMetric{
+ bmType: derivedCumulativeInt64,
+ },
+ }
+ _, err := r.initBaseMetric(&i.bm, name, mos...)
+ if err != nil {
+ return nil, err
+ }
+ return i, nil
+}
+
+// AddFloat64DerivedCumulative creates and adds a new derived float64-valued gauge to this registry.
+// A derived cumulative is convenient form of cumulative where the object associated with the cumulative
+// provides its value by implementing func() float64.
+func (r *Registry) AddFloat64DerivedCumulative(name string, mos ...Options) (*Float64DerivedCumulative, error) {
+ f := &Float64DerivedCumulative{
+ bm: baseMetric{
+ bmType: derivedCumulativeFloat64,
+ },
+ }
+ _, err := r.initBaseMetric(&f.bm, name, mos...)
+ if err != nil {
+ return nil, err
+ }
+ return f, nil
+}
+
func createMetricOption(mos ...Options) *metricOptions {
o := &metricOptions{}
for _, mo := range mos {
@@ -169,7 +237,7 @@
return bm, nil
}
-// Read reads all gauges in this registry and returns their values as metrics.
+// Read reads all gauges and cumulatives in this registry and returns their values as metrics.
func (r *Registry) Read() []*metricdata.Metric {
ms := []*metricdata.Metric{}
r.baseMetrics.Range(func(k, v interface{}) bool {