Skip to content

Commit

Permalink
Added JSON->io.Writer; consistent ordering to WriteOnce
Browse files Browse the repository at this point in the history
  • Loading branch information
stuartcarnie committed Jun 6, 2014
1 parent 6ffbdf3 commit 6d82eb1
Show file tree
Hide file tree
Showing 5 changed files with 129 additions and 10 deletions.
68 changes: 67 additions & 1 deletion json.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
package metrics

import "encoding/json"
import (
"encoding/json"
"io"
)

// MarshalJSON returns a byte slice containing a JSON representation of all
// the metrics in the Registry.
Expand Down Expand Up @@ -63,3 +66,66 @@ func (r StandardRegistry) MarshalJSON() ([]byte, error) {
})
return json.Marshal(data)
}

// WriteJSONOnce outputs metrics from the given registry to the specified writer in JSON format
func WriteJSONOnce(r Registry, w io.Writer) {
data := make(map[string]map[string]interface{})
r.Each(func(name string, i interface{}) {
values := make(map[string]interface{})
switch metric := i.(type) {
case Counter:
values["count"] = metric.Count()
case Gauge:
values["value"] = metric.Value()
case GaugeFloat64:
values["value"] = metric.Value()
case Healthcheck:
values["error"] = nil
metric.Check()
if err := metric.Error(); nil != err {
values["error"] = metric.Error().Error()
}
case Histogram:
h := metric.Snapshot()
ps := h.Percentiles([]float64{0.5, 0.75, 0.95, 0.99, 0.999})
values["count"] = h.Count()
values["min"] = h.Min()
values["max"] = h.Max()
values["mean"] = h.Mean()
values["stddev"] = h.StdDev()
values["median"] = ps[0]
values["75%"] = ps[1]
values["95%"] = ps[2]
values["99%"] = ps[3]
values["99.9%"] = ps[4]
case Meter:
m := metric.Snapshot()
values["count"] = m.Count()
values["1m.rate"] = m.Rate1()
values["5m.rate"] = m.Rate5()
values["15m.rate"] = m.Rate15()
values["mean.rate"] = m.RateMean()
case Timer:
t := metric.Snapshot()
ps := t.Percentiles([]float64{0.5, 0.75, 0.95, 0.99, 0.999})
values["count"] = t.Count()
values["min"] = t.Min()
values["max"] = t.Max()
values["mean"] = t.Mean()
values["stddev"] = t.StdDev()
values["median"] = ps[0]
values["75%"] = ps[1]
values["95%"] = ps[2]
values["99%"] = ps[3]
values["99.9%"] = ps[4]
values["1m.rate"] = t.Rate1()
values["5m.rate"] = t.Rate5()
values["15m.rate"] = t.Rate15()
values["mean.rate"] = t.RateMean()
}
data[name] = values
})

enc := json.NewEncoder(w)
enc.Encode(data)
}
10 changes: 10 additions & 0 deletions json_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,3 +16,13 @@ func TestRegistryMarshallJSON(t *testing.T) {
t.Fatalf(s)
}
}

func TestRegistryWriteJSONOnce(t *testing.T) {
r := NewRegistry()
r.Register("counter", NewCounter())
b := &bytes.Buffer{}
WriteJSONOnce(r, b)
if s := b.String(); s != "{\"counter\":{\"count\":0}}\n" {
t.Fail()
}
}
16 changes: 16 additions & 0 deletions metrics.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,3 +11,19 @@ package metrics
// This global kill-switch helps quantify the observer effect and makes
// for less cluttered pprof profiles.
var UseNilMetrics bool = false

type metric struct {
name string
m interface{}
}

// metrics is a slice of metrics that implements sort.Interface
type metrics []metric

func (m metrics) Len() int { return len(m) }

func (m metrics) Swap(i, j int) { m[i], m[j] = m[j], m[i] }

func (m metrics) Less(i, j int) bool {
return m[i].name < m[j].name
}
17 changes: 17 additions & 0 deletions metrics_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import (
"log"
"sync"
"testing"
"sort"
)

const FANOUT = 128
Expand Down Expand Up @@ -105,3 +106,19 @@ func BenchmarkMetrics(b *testing.B) {
wgR.Wait()
wgW.Wait()
}

func TestMetricsSorting(t *testing.T) {
var data = metrics{
{name:"zzz"},
{name:"bbb"},
{name:"fff"},
{name:"ggg"},
}

sort.Sort(data)
for i, d := range []string{"bbb","fff","ggg","zzz"} {
if data[i].name != d {
t.Fail()
}
}
}
28 changes: 19 additions & 9 deletions writer.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import (
"fmt"
"io"
"time"
"sort"
)

// Output each metric in the given registry periodically using the given
Expand All @@ -15,26 +16,35 @@ func Write(r Registry, d time.Duration, w io.Writer) {
}
}

// WriteOnce outputs metrics for the given registry to the specified io.Writer
//
// WriteOnce sorts by metric name to ensure the output remains consistent between runs
func WriteOnce(r Registry, w io.Writer) {
var data metrics
r.Each(func(name string, i interface{}) {
switch metric := i.(type) {
data = append(data, metric{name, i})
})

sort.Sort(data)
for _, i := range data {
switch metric := i.m.(type) {
case Counter:
fmt.Fprintf(w, "counter %s\n", name)
fmt.Fprintf(w, "counter %s\n", i.name)
fmt.Fprintf(w, " count: %9d\n", metric.Count())
case Gauge:
fmt.Fprintf(w, "gauge %s\n", name)
fmt.Fprintf(w, "gauge %s\n", i.name)
fmt.Fprintf(w, " value: %9d\n", metric.Value())
case GaugeFloat64:
fmt.Fprintf(w, "gauge %s\n", name)
fmt.Fprintf(w, "gauge %s\n", i.name)
fmt.Fprintf(w, " value: %f\n", metric.Value())
case Healthcheck:
metric.Check()
fmt.Fprintf(w, "healthcheck %s\n", name)
fmt.Fprintf(w, "healthcheck %s\n", i.name)
fmt.Fprintf(w, " error: %v\n", metric.Error())
case Histogram:
h := metric.Snapshot()
ps := h.Percentiles([]float64{0.5, 0.75, 0.95, 0.99, 0.999})
fmt.Fprintf(w, "histogram %s\n", name)
fmt.Fprintf(w, "histogram %s\n", i.name)
fmt.Fprintf(w, " count: %9d\n", h.Count())
fmt.Fprintf(w, " min: %9d\n", h.Min())
fmt.Fprintf(w, " max: %9d\n", h.Max())
Expand All @@ -47,7 +57,7 @@ func WriteOnce(r Registry, w io.Writer) {
fmt.Fprintf(w, " 99.9%%: %12.2f\n", ps[4])
case Meter:
m := metric.Snapshot()
fmt.Fprintf(w, "meter %s\n", name)
fmt.Fprintf(w, "meter %s\n", i.name)
fmt.Fprintf(w, " count: %9d\n", m.Count())
fmt.Fprintf(w, " 1-min rate: %12.2f\n", m.Rate1())
fmt.Fprintf(w, " 5-min rate: %12.2f\n", m.Rate5())
Expand All @@ -56,7 +66,7 @@ func WriteOnce(r Registry, w io.Writer) {
case Timer:
t := metric.Snapshot()
ps := t.Percentiles([]float64{0.5, 0.75, 0.95, 0.99, 0.999})
fmt.Fprintf(w, "timer %s\n", name)
fmt.Fprintf(w, "timer %s\n", i.name)
fmt.Fprintf(w, " count: %9d\n", t.Count())
fmt.Fprintf(w, " min: %9d\n", t.Min())
fmt.Fprintf(w, " max: %9d\n", t.Max())
Expand All @@ -72,5 +82,5 @@ func WriteOnce(r Registry, w io.Writer) {
fmt.Fprintf(w, " 15-min rate: %12.2f\n", t.Rate15())
fmt.Fprintf(w, " mean rate: %12.2f\n", t.RateMean())
}
})
}
}

0 comments on commit 6d82eb1

Please sign in to comment.