Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Added Gauge.Add #428

Merged
merged 1 commit into from Jan 11, 2017
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.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
20 changes: 19 additions & 1 deletion metrics/circonus/circonus.go
Expand Up @@ -2,6 +2,8 @@
package circonus

import (
"sync"

"github.com/circonus-labs/circonus-gometrics"

"github.com/go-kit/kit/metrics"
Expand Down Expand Up @@ -63,14 +65,30 @@ func (c *Counter) Add(delta float64) { c.m.Add(c.name, uint64(delta)) }
type Gauge struct {
name string
m *circonusgometrics.CirconusMetrics
val float64
mtx sync.RWMutex
}

// With implements Gauge, but is a no-op, because Circonus metrics have no
// concept of per-observation label values.
func (g *Gauge) With(labelValues ...string) metrics.Gauge { return g }

// Set implements Gauge.
func (g *Gauge) Set(value float64) { g.m.SetGauge(g.name, value) }
func (g *Gauge) Set(value float64) {
g.mtx.Lock()
defer g.mtx.Unlock()
g.val = value
g.m.SetGauge(g.name, value)
}

// Add implements metrics.Gauge.
func (g *Gauge) Add(delta float64) {
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think that this could be better, but couldn't see any way in circonus-gometrics to get the value of a Gauge.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah Circonus has a very... esoteric view of the world

g.mtx.Lock()
defer g.mtx.Unlock()
value := g.val + delta
g.val = value
g.m.SetGauge(g.name, value)
}

// Histogram is a Circonus implementation of a histogram metric.
type Histogram struct {
Expand Down
3 changes: 3 additions & 0 deletions metrics/discard/discard.go
Expand Up @@ -25,6 +25,9 @@ func (g gauge) With(labelValues ...string) metrics.Gauge { return g }
// Set implements Gauge.
func (g gauge) Set(value float64) {}

// Add implements metrics.Gauge.
func (g gauge) Add(delta float64) {}

type histogram struct{}

// NewHistogram returns a new no-op histogram.
Expand Down
8 changes: 8 additions & 0 deletions metrics/dogstatsd/dogstatsd.go
Expand Up @@ -72,6 +72,7 @@ func (d *Dogstatsd) NewGauge(name string) *Gauge {
return &Gauge{
name: d.prefix + name,
obs: d.gauges.Observe,
add: d.gauges.Add,
}
}

Expand Down Expand Up @@ -244,6 +245,7 @@ type Gauge struct {
name string
lvs lv.LabelValues
obs observeFunc
add observeFunc
}

// With implements metrics.Gauge.
Expand All @@ -252,6 +254,7 @@ func (g *Gauge) With(labelValues ...string) metrics.Gauge {
name: g.name,
lvs: g.lvs.With(labelValues...),
obs: g.obs,
add: g.add,
}
}

Expand All @@ -260,6 +263,11 @@ func (g *Gauge) Set(value float64) {
g.obs(g.name, g.lvs, value)
}

// Add implements metrics.Gauge.
func (g *Gauge) Add(delta float64) {
g.add(g.name, g.lvs, delta)
}

// Timing is a DogStatsD timing, or metrics.Histogram. Observations are
// forwarded to a Dogstatsd object, and collected (but not aggregated) per
// timeseries.
Expand Down
3 changes: 3 additions & 0 deletions metrics/expvar/expvar.go
Expand Up @@ -50,6 +50,9 @@ func (g *Gauge) With(labelValues ...string) metrics.Gauge { return g }
// Set implements Gauge.
func (g *Gauge) Set(value float64) { g.f.Set(value) }

// Add implements metrics.Gauge.
func (g *Gauge) Add(delta float64) { g.f.Add(delta) }

// Histogram implements the histogram metric with a combination of the generic
// Histogram object and several expvar Floats, one for each of the 50th, 90th,
// 95th, and 99th quantiles of observed values, with the quantile attached to
Expand Down
14 changes: 14 additions & 0 deletions metrics/generic/generic.go
Expand Up @@ -105,6 +105,20 @@ func (g *Gauge) Set(value float64) {
atomic.StoreUint64(&g.bits, math.Float64bits(value))
}

// Add implements metrics.Gauge.
func (g *Gauge) Add(delta float64) {
for {
var (
old = atomic.LoadUint64(&g.bits)
newf = math.Float64frombits(old) + delta
new = math.Float64bits(newf)
)
if atomic.CompareAndSwapUint64(&g.bits, old, new) {
break
}
}
}

// Value returns the current value of the gauge.
func (g *Gauge) Value() float64 {
return math.Float64frombits(atomic.LoadUint64(&g.bits))
Expand Down
3 changes: 3 additions & 0 deletions metrics/graphite/graphite.go
Expand Up @@ -182,6 +182,9 @@ func (g *Gauge) With(...string) metrics.Gauge { return g }
// Set implements gauge.
func (g *Gauge) Set(value float64) { g.g.Set(value) }

// Add implements metrics.Gauge.
func (g *Gauge) Add(delta float64) { g.g.Add(delta) }

// Histogram is a Graphite histogram metric. Observations are bucketed into
// per-quantile gauges.
type Histogram struct {
Expand Down
4 changes: 4 additions & 0 deletions metrics/influx/example_test.go
Expand Up @@ -44,11 +44,14 @@ func ExampleGauge() {
gauge.With("error", "true").Set(1)
gauge.With("error", "false").Set(2)
gauge.Set(50)
gauge.With("test", "true").Set(1)
gauge.With("test", "true").Add(1)

client := &bufWriter{}
in.WriteTo(client)

expectedLines := []string{
`(influx_gauge,a=b,test=true value=2) [0-9]{19}`,
`(influx_gauge,a=b value=50) [0-9]{19}`,
`(influx_gauge,a=b,error=true value=1) [0-9]{19}`,
`(influx_gauge,a=b,error=false value=2) [0-9]{19}`,
Expand All @@ -59,6 +62,7 @@ func ExampleGauge() {
}

// Output:
// influx_gauge,a=b,test=true value=2
// influx_gauge,a=b value=50
// influx_gauge,a=b,error=true value=1
// influx_gauge,a=b,error=false value=2
Expand Down
8 changes: 8 additions & 0 deletions metrics/influx/influx.go
Expand Up @@ -66,6 +66,7 @@ func (in *Influx) NewGauge(name string) *Gauge {
return &Gauge{
name: name,
obs: in.gauges.Observe,
add: in.gauges.Add,
}
}

Expand Down Expand Up @@ -220,6 +221,7 @@ type Gauge struct {
name string
lvs lv.LabelValues
obs observeFunc
add observeFunc
}

// With implements metrics.Gauge.
Expand All @@ -228,6 +230,7 @@ func (g *Gauge) With(labelValues ...string) metrics.Gauge {
name: g.name,
lvs: g.lvs.With(labelValues...),
obs: g.obs,
add: g.add,
}
}

Expand All @@ -236,6 +239,11 @@ func (g *Gauge) Set(value float64) {
g.obs(g.name, g.lvs, value)
}

// Add implements metrics.Gauge.
func (g *Gauge) Add(delta float64) {
g.add(g.name, g.lvs, delta)
}

// Histogram is an Influx histrogram. Observations are aggregated into a
// generic.Histogram and emitted as per-quantile gauges to the Influx server.
type Histogram struct {
Expand Down
39 changes: 39 additions & 0 deletions metrics/internal/lv/space.go
Expand Up @@ -21,6 +21,13 @@ func (s *Space) Observe(name string, lvs LabelValues, value float64) {
s.nodeFor(name).observe(lvs, value)
}

// Add locates the time series identified by the name and label values in
// the vector space, and appends the delta to the last value in the list of
// observations.
func (s *Space) Add(name string, lvs LabelValues, delta float64) {
s.nodeFor(name).add(lvs, delta)
}

// Walk traverses the vector space and invokes fn for each non-empty time series
// which is encountered. Return false to abort the traversal.
func (s *Space) Walk(fn func(name string, lvs LabelValues, observations []float64) bool) {
Expand Down Expand Up @@ -91,6 +98,34 @@ func (n *node) observe(lvs LabelValues, value float64) {
child.observe(tail, value)
}

func (n *node) add(lvs LabelValues, delta float64) {
n.mtx.Lock()
defer n.mtx.Unlock()
if len(lvs) == 0 {
var value float64
if len(n.observations) > 0 {
value = last(n.observations) + delta
} else {
value = delta
}
n.observations = append(n.observations, value)
return
}
if len(lvs) < 2 {
panic("too few LabelValues; programmer error!")
}
head, tail := pair{lvs[0], lvs[1]}, lvs[2:]
if n.children == nil {
n.children = map[pair]*node{}
}
child, ok := n.children[head]
if !ok {
child = &node{}
n.children[head] = child
}
child.add(tail, delta)
}

func (n *node) walk(lvs LabelValues, fn func(LabelValues, []float64) bool) bool {
n.mtx.RLock()
defer n.mtx.RUnlock()
Expand All @@ -104,3 +139,7 @@ func (n *node) walk(lvs LabelValues, fn func(LabelValues, []float64) bool) bool
}
return true
}

func last(a []float64) float64 {
return a[len(a)-1]
}
1 change: 1 addition & 0 deletions metrics/metrics.go
Expand Up @@ -12,6 +12,7 @@ type Counter interface {
type Gauge interface {
With(labelValues ...string) Gauge
Set(value float64)
Add(delta float64)
}

// Histogram describes a metric that takes repeated observations of the same
Expand Down
7 changes: 7 additions & 0 deletions metrics/multi/multi.go
Expand Up @@ -54,6 +54,13 @@ func (g Gauge) With(labelValues ...string) metrics.Gauge {
return next
}

// Add implements metrics.Gauge.
func (g Gauge) Add(delta float64) {
for _, gauge := range g {
gauge.Add(delta)
}
}

// Histogram collects multiple individual histograms and treats them as a unit.
type Histogram []metrics.Histogram

Expand Down
12 changes: 11 additions & 1 deletion metrics/multi/multi_test.go
Expand Up @@ -33,8 +33,9 @@ func TestMultiGauge(t *testing.T) {
mg.Set(9)
mg.Set(8)
mg.Set(7)
mg.Add(3)

want := "[9 8 7]"
want := "[9 8 7 10]"
for i, m := range []fmt.Stringer{g1, g2, g3} {
if have := m.String(); want != have {
t.Errorf("g%d: want %q, have %q", i+1, want, have)
Expand Down Expand Up @@ -76,6 +77,15 @@ type mockGauge struct {
func (g *mockGauge) Set(value float64) { g.obs = append(g.obs, value) }
func (g *mockGauge) With(...string) metrics.Gauge { return g }
func (g *mockGauge) String() string { return fmt.Sprintf("%v", g.obs) }
func (g *mockGauge) Add(delta float64) {
var value float64
if len(g.obs) > 0 {
value = g.obs[len(g.obs)-1] + delta
} else {
value = delta
}
g.obs = append(g.obs, value)
}

type mockHistogram struct {
obs []float64
Expand Down
2 changes: 1 addition & 1 deletion metrics/pcp/pcp.go
Expand Up @@ -82,7 +82,7 @@ func (g *Gauge) With(labelValues ...string) metrics.Gauge { return g }
func (g *Gauge) Set(value float64) { g.g.Set(value) }

// Add adds a value to the gauge.
func (g *Gauge) Add(value float64) { g.g.Inc(value) }
func (g *Gauge) Add(delta float64) { g.g.Inc(delta) }

// Histogram wraps a speed Histogram.
type Histogram struct {
Expand Down
7 changes: 7 additions & 0 deletions metrics/statsd/statsd.go
Expand Up @@ -74,6 +74,7 @@ func (s *Statsd) NewGauge(name string) *Gauge {
return &Gauge{
name: s.prefix + name,
obs: s.gauges.Observe,
add: s.gauges.Add,
}
}

Expand Down Expand Up @@ -201,6 +202,7 @@ func (c *Counter) Add(delta float64) {
type Gauge struct {
name string
obs observeFunc
add observeFunc
}

// With is a no-op.
Expand All @@ -213,6 +215,11 @@ func (g *Gauge) Set(value float64) {
g.obs(g.name, lv.LabelValues{}, value)
}

// Add implements metrics.Gauge.
func (g *Gauge) Add(delta float64) {
g.add(g.name, lv.LabelValues{}, delta)
}

// Timing is a StatsD timing, or metrics.Histogram. Observations are
// forwarded to a Statsd object, and collected (but not aggregated) per
// timeseries.
Expand Down
6 changes: 6 additions & 0 deletions metrics/teststat/teststat.go
Expand Up @@ -44,6 +44,12 @@ func TestGauge(gauge metrics.Gauge, value func() float64) error {
want = f
}

for i := 0; i < n; i++ {
f := float64(a[i])
gauge.Add(f)
want += f
}

if have := value(); want != have {
return fmt.Errorf("want %f, have %f", want, have)
}
Expand Down