From 68b130797a47231259759fbe3d27b778d42888fe Mon Sep 17 00:00:00 2001 From: Daniel Garcia Date: Sat, 22 Mar 2014 02:53:41 -0500 Subject: [PATCH] initial implementation of GaugeFloat64 --- cmd/metrics-bench/metrics-bench.go | 1 + cmd/metrics-example/metrics-example.go | 17 +++++ gauge_float64.go | 91 ++++++++++++++++++++++++++ gauge_float64_test.go | 39 +++++++++++ graphite.go | 2 + influxdb/influxdb.go | 8 +++ json.go | 2 + librato/librato.go | 4 ++ log.go | 3 + metrics_test.go | 2 + registry.go | 2 +- stathat/stathat.go | 2 + syslog.go | 2 + writer.go | 3 + 14 files changed, 177 insertions(+), 1 deletion(-) create mode 100644 gauge_float64.go create mode 100644 gauge_float64_test.go diff --git a/cmd/metrics-bench/metrics-bench.go b/cmd/metrics-bench/metrics-bench.go index 4233303..dddaf4b 100644 --- a/cmd/metrics-bench/metrics-bench.go +++ b/cmd/metrics-bench/metrics-bench.go @@ -11,6 +11,7 @@ func main() { for i := 0; i < 10000; i++ { r.Register(fmt.Sprintf("counter-%d", i), metrics.NewCounter()) r.Register(fmt.Sprintf("gauge-%d", i), metrics.NewGauge()) + r.Register(fmt.Sprintf("gaugefloat64-%d", i), metrics.NewGaugeFloat64()) r.Register(fmt.Sprintf("histogram-uniform-%d", i), metrics.NewHistogram(metrics.NewUniformSample(1028))) r.Register(fmt.Sprintf("histogram-exp-%d", i), metrics.NewHistogram(metrics.NewExpDecaySample(1028, 0.015))) r.Register(fmt.Sprintf("meter-%d", i), metrics.NewMeter()) diff --git a/cmd/metrics-example/metrics-example.go b/cmd/metrics-example/metrics-example.go index 4411097..66f42c0 100644 --- a/cmd/metrics-example/metrics-example.go +++ b/cmd/metrics-example/metrics-example.go @@ -51,6 +51,23 @@ func main() { }() } + gf := metrics.NewGaugeFloat64() + r.Register("barfloat64", gf) + for i := 0; i < fanout; i++ { + go func() { + for { + g.Update(19.0) + time.Sleep(300e6) + } + }() + go func() { + for { + g.Update(47.0) + time.Sleep(400e6) + } + }() + } + hc := metrics.NewHealthcheck(func(h metrics.Healthcheck) { if 0 < rand.Intn(2) { h.Healthy() diff --git a/gauge_float64.go b/gauge_float64.go new file mode 100644 index 0000000..97e49ea --- /dev/null +++ b/gauge_float64.go @@ -0,0 +1,91 @@ +package metrics + +import "sync" + +// GaugeFloat64s hold a float64 value that can be set arbitrarily. +type GaugeFloat64 interface { + Snapshot() GaugeFloat64 + Update(float64) + Value() float64 +} + +// GetOrRegisterGaugeFloat64 returns an existing GaugeFloat64 or constructs and registers a +// new StandardGaugeFloat64. +func GetOrRegisterGaugeFloat64(name string, r Registry) GaugeFloat64 { + if nil == r { + r = DefaultRegistry + } + return r.GetOrRegister(name, NewGaugeFloat64()).(GaugeFloat64) +} + +// NewGaugeFloat64 constructs a new StandardGaugeFloat64. +func NewGaugeFloat64() GaugeFloat64 { + if UseNilMetrics { + return NilGaugeFloat64{} + } + return &StandardGaugeFloat64{ + value: 0.0, + } +} + +// NewRegisteredGaugeFloat64 constructs and registers a new StandardGaugeFloat64. +func NewRegisteredGaugeFloat64(name string, r Registry) GaugeFloat64 { + c := NewGaugeFloat64() + if nil == r { + r = DefaultRegistry + } + r.Register(name, c) + return c +} + +// GaugeSnapshotFloat64 is a read-only copy of another GaugeFloat64. +type GaugeSnapshotFloat64 float64 + +// Snapshot returns the snapshot. +func (g GaugeSnapshotFloat64) Snapshot() GaugeFloat64 { return g } + +// Update panics. +func (GaugeSnapshotFloat64) Update(float64) { + panic("Update called on a GaugeSnapshotFloat64") +} + +// Value returns the value at the time the snapshot was taken. +func (g GaugeSnapshotFloat64) Value() float64 { return float64(g) } + +// NilGauge is a no-op Gauge. +type NilGaugeFloat64 struct{} + +// Snapshot is a no-op. +func (NilGaugeFloat64) Snapshot() GaugeFloat64 { return NilGaugeFloat64{} } + +// Update is a no-op. +func (NilGaugeFloat64) Update(v float64) {} + +// Value is a no-op. +func (NilGaugeFloat64) Value() float64 { return 0.0 } + +// StandardGaugeFloat64 is the standard implementation of a GaugeFloat64 and uses +// sync.Mutex to manage a single float64 value. +type StandardGaugeFloat64 struct { + value float64 + sync.Mutex +} + +// Snapshot returns a read-only copy of the gauge. +func (g *StandardGaugeFloat64) Snapshot() GaugeFloat64 { + return GaugeSnapshotFloat64(g.Value()) +} + +// Update updates the gauge's value. +func (g *StandardGaugeFloat64) Update(v float64) { + g.Lock() + defer g.Unlock() + g.value = v +} + +// Value returns the gauge's current value. +func (g *StandardGaugeFloat64) Value() float64 { + g.Lock() + defer g.Unlock() + return g.value +} diff --git a/gauge_float64_test.go b/gauge_float64_test.go new file mode 100644 index 0000000..866f54f --- /dev/null +++ b/gauge_float64_test.go @@ -0,0 +1,39 @@ +package metrics + +import "testing" + + +func BenchmarkGuageFloat64(b *testing.B) { + g := NewGaugeFloat64() + b.ResetTimer() + for i := 0; i < b.N; i++ { + g.Update(float64(i)) + } +} + +func TestGaugeFloat64(t *testing.T) { + g := NewGaugeFloat64() + g.Update(float64(47.0)) + if v := g.Value(); float64(47.0) != v { + t.Errorf("g.Value(): 47.0 != %v\n", v) + } +} + +func TestGaugeFloat64Snapshot(t *testing.T) { + g := NewGaugeFloat64() + g.Update(float64(47.0)) + snapshot := g.Snapshot() + g.Update(float64(0)) + if v := snapshot.Value(); float64(47.0) != v { + t.Errorf("g.Value(): 47.0 != %v\n", v) + } +} + +func TestGetOrRegisterGaugeFloat64(t *testing.T) { + r := NewRegistry() + NewRegisteredGaugeFloat64("foo", r).Update(float64(47.0)) + t.Logf("registry: %v", r) + if g := GetOrRegisterGaugeFloat64("foo", r); float64(47.0) != g.Value() { + t.Fatal(g) + } +} diff --git a/graphite.go b/graphite.go index e706ad5..76aa3c7 100644 --- a/graphite.go +++ b/graphite.go @@ -56,6 +56,8 @@ func graphite(c *GraphiteConfig) error { fmt.Fprintf(w, "%s.%s.count %d %d\n", c.Prefix, name, metric.Count(), now) case Gauge: fmt.Fprintf(w, "%s.%s.value %d %d\n", c.Prefix, name, metric.Value(), now) + case GaugeFloat64: + fmt.Fprintf(w, "%s.%s.value %f %d\n", c.Prefix, name, metric.Value(), now) case Histogram: h := metric.Snapshot() ps := h.Percentiles([]float64{0.5, 0.75, 0.95, 0.99, 0.999}) diff --git a/influxdb/influxdb.go b/influxdb/influxdb.go index dc28a03..2f5144d 100644 --- a/influxdb/influxdb.go +++ b/influxdb/influxdb.go @@ -56,6 +56,14 @@ func send(r metrics.Registry, client *influxClient.Client) error { {now, metric.Value()}, }, }) + case metrics.GaugeFloat64: + series = append(series, &influxClient.Series{ + Name: fmt.Sprintf("%s.value", name), + Columns: []string{"time", "value"}, + Points: [][]interface{}{ + {now, metric.Value()}, + }, + }) case metrics.Histogram: h := metric.Snapshot() ps := h.Percentiles([]float64{0.5, 0.75, 0.95, 0.99, 0.999}) diff --git a/json.go b/json.go index c0807c1..4d2f6cc 100644 --- a/json.go +++ b/json.go @@ -13,6 +13,8 @@ func (r StandardRegistry) MarshalJSON() ([]byte, error) { values["count"] = metric.Count() case Gauge: values["value"] = metric.Value() + case GaugeFloat64: + values["value"] = metric.Value() case Healthcheck: metric.Check() values["error"] = metric.Error().Error() diff --git a/librato/librato.go b/librato/librato.go index b3d6929..947225e 100644 --- a/librato/librato.go +++ b/librato/librato.go @@ -99,6 +99,10 @@ func (self *Reporter) BuildRequest(now time.Time, r metrics.Registry) (snapshot measurement[Name] = name measurement[Value] = float64(m.Value()) snapshot.Gauges = append(snapshot.Gauges, measurement) + case metrics.GaugeFloat64: + measurement[Name] = name + measurement[Value] = float64(m.Value()) + snapshot.Gauges = append(snapshot.Gauges, measurement) case metrics.Histogram: if m.Count() > 0 { gauges := make([]Measurement, histogramGaugeCount, histogramGaugeCount) diff --git a/log.go b/log.go index 5d36b6e..4ae5252 100644 --- a/log.go +++ b/log.go @@ -17,6 +17,9 @@ func Log(r Registry, d time.Duration, l *log.Logger) { case Gauge: l.Printf("gauge %s\n", name) l.Printf(" value: %9d\n", metric.Value()) + case GaugeFloat64: + l.Printf("gauge %s\n", name) + l.Printf(" value: %f\n", metric.Value()) case Healthcheck: metric.Check() l.Printf("healthcheck %s\n", name) diff --git a/metrics_test.go b/metrics_test.go index 8d03d3e..083f967 100644 --- a/metrics_test.go +++ b/metrics_test.go @@ -19,6 +19,7 @@ func BenchmarkMetrics(b *testing.B) { r := NewRegistry() c := NewRegisteredCounter("counter", r) g := NewRegisteredGauge("gauge", r) + gf := NewRegisteredGaugeFloat64("gaugefloat64", r) h := NewRegisteredHistogram("histogram", r, NewUniformSample(100)) m := NewRegisteredMeter("meter", r) t := NewRegisteredTimer("timer", r) @@ -90,6 +91,7 @@ func BenchmarkMetrics(b *testing.B) { for i := 0; i < b.N; i++ { c.Inc(1) g.Update(int64(i)) + gf.Update(float64(i)) h.Update(int64(i)) m.Mark(1) t.Update(1) diff --git a/registry.go b/registry.go index d6b4803..3c4fa3f 100644 --- a/registry.go +++ b/registry.go @@ -93,7 +93,7 @@ func (r *StandardRegistry) Unregister(name string) { func (r *StandardRegistry) register(name string, i interface{}) { switch i.(type) { - case Counter, Gauge, Healthcheck, Histogram, Meter, Timer: + case Counter, Gauge, GaugeFloat64, Healthcheck, Histogram, Meter, Timer: r.metrics[name] = i } } diff --git a/stathat/stathat.go b/stathat/stathat.go index 7265cbe..0afcb48 100644 --- a/stathat/stathat.go +++ b/stathat/stathat.go @@ -24,6 +24,8 @@ func sh(r metrics.Registry, userkey string) error { stathat.PostEZCount(name, userkey, int(metric.Count())) case metrics.Gauge: stathat.PostEZValue(name, userkey, float64(metric.Value())) + case metrics.GaugeFloat64: + stathat.PostEZValue(name, userkey, float64(metric.Value())) case metrics.Histogram: h := metric.Snapshot() ps := h.Percentiles([]float64{0.5, 0.75, 0.95, 0.99, 0.999}) diff --git a/syslog.go b/syslog.go index ff07bb4..af2cbab 100644 --- a/syslog.go +++ b/syslog.go @@ -18,6 +18,8 @@ func Syslog(r Registry, d time.Duration, w *syslog.Writer) { w.Info(fmt.Sprintf("counter %s: count: %d", name, metric.Count())) case Gauge: w.Info(fmt.Sprintf("gauge %s: value: %d", name, metric.Value())) + case GaugeFloat64: + w.Info(fmt.Sprintf("gauge %s: value: %f", name, metric.Value())) case Healthcheck: metric.Check() w.Info(fmt.Sprintf("healthcheck %s: error: %v", name, metric.Error())) diff --git a/writer.go b/writer.go index 8abd5cc..fa37a6f 100644 --- a/writer.go +++ b/writer.go @@ -24,6 +24,9 @@ func WriteOnce(r Registry, w io.Writer) { case Gauge: fmt.Fprintf(w, "gauge %s\n", name) fmt.Fprintf(w, " value: %9d\n", metric.Value()) + case GaugeFloat64: + fmt.Fprintf(w, "gauge %s\n", name) + fmt.Fprintf(w, " value: %f\n", metric.Value()) case Healthcheck: metric.Check() fmt.Fprintf(w, "healthcheck %s\n", name)