From 3bae82251763c21c9fb3659844f8e1f076ef7c0c Mon Sep 17 00:00:00 2001 From: Danielle Gaydorus Date: Tue, 14 Feb 2017 11:33:19 -0800 Subject: [PATCH] Added support for logging metrics as single line json instead of printing on separate lines Included separate methods for marshaling JSON values in metrics registry as strings to make this backward compatible with existing functionality. --- json.go | 67 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++ log.go | 19 ++++++++++++++++ 2 files changed, 86 insertions(+) diff --git a/json.go b/json.go index 2fdcbcf..edd1b84 100644 --- a/json.go +++ b/json.go @@ -4,6 +4,7 @@ import ( "encoding/json" "io" "time" + "fmt" ) // MarshalJSON returns a byte slice containing a JSON representation of all @@ -68,6 +69,72 @@ func (r *StandardRegistry) MarshalJSON() ([]byte, error) { return json.Marshal(data) } + +// MarshalJSONStringified returns a byte slice containing a JSON representation of all +// the metrics in the Registry with values stringified. +func (r *StandardRegistry) MarshalJSONStringified(scale time.Duration) ([]byte, error) { + du := float64(scale) + duSuffix := scale.String()[1:] + + 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"] = fmt.Sprintf("%9d", metric.Count()) + case Gauge: + values["value"] = fmt.Sprintf("%9d", metric.Value()) + case GaugeFloat64: + values["value"] = fmt.Sprintf("%f", 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"] = fmt.Sprintf("%9d", h.Count()) + values["min"] = fmt.Sprintf("%9d", h.Min()) + values["max"] = fmt.Sprintf("%9d", h.Max()) + values["mean"] = fmt.Sprintf("%12.2f", h.Mean()) + values["stddev"] = fmt.Sprintf("%12.2f", h.StdDev()) + values["median"] = fmt.Sprintf("%12.2f", ps[0]) + values["75%"] = fmt.Sprintf("%12.2f", ps[1]) + values["95%"] = fmt.Sprintf("%12.2f", ps[2]) + values["99%"] = fmt.Sprintf("%12.2f", ps[3]) + values["99.9%"] = fmt.Sprintf("%12.2f", ps[4]) + case Meter: + m := metric.Snapshot() + values["count"] = fmt.Sprintf("%9d", m.Count()) + values["1m.rate"] = fmt.Sprintf("%12.2f", m.Rate1()) + values["5m.rate"] = fmt.Sprintf("%12.2f", m.Rate5()) + values["15m.rate"] = fmt.Sprintf("%12.2f", m.Rate15()) + values["mean.rate"] = fmt.Sprintf("%12.2f", m.RateMean()) + case Timer: + t := metric.Snapshot() + ps := t.Percentiles([]float64{0.5, 0.75, 0.95, 0.99, 0.999}) + values["count"] = fmt.Sprintf("%9d", t.Count()) + values["min"] = fmt.Sprintf("%12.2f%s", float64(t.Min())/du, duSuffix) + values["max"] = fmt.Sprintf("%12.2f%s", float64(t.Max())/du, duSuffix) + values["mean"] = fmt.Sprintf("%12.2f%s", t.Mean()/du, duSuffix) + values["stddev"] = fmt.Sprintf("%12.2f%s", t.StdDev()/du, duSuffix) + values["median"] = fmt.Sprintf("%12.2f%s", ps[0]/du, duSuffix) + values["75%"] = fmt.Sprintf("%12.2f%s", ps[1]/du, duSuffix) + values["95%"] = fmt.Sprintf("%12.2f%s", ps[2]/du, duSuffix) + values["99%"] = fmt.Sprintf("%12.2f%s", ps[3]/du, duSuffix) + values["99.9%"] = fmt.Sprintf("%12.2f%s", ps[4]/du, duSuffix) + values["1m.rate"] = fmt.Sprintf("%12.2f", t.Rate1()) + values["5m.rate"] = fmt.Sprintf("%12.2f", t.Rate5()) + values["15m.rate"] = fmt.Sprintf("%12.2f", t.Rate15()) + values["mean.rate"] = fmt.Sprintf("%12.2f", t.RateMean()) + } + data[name] = values + }) + return json.Marshal(data) +} + // WriteJSON writes metrics from the given registry periodically to the // specified io.Writer as JSON. func WriteJSON(r Registry, d time.Duration, w io.Writer) { diff --git a/log.go b/log.go index f8074c0..819eccc 100644 --- a/log.go +++ b/log.go @@ -12,6 +12,10 @@ func Log(r Registry, freq time.Duration, l Logger) { LogScaled(r, freq, time.Nanosecond, l) } +func LogJSON(r Registry, freq time.Duration, l Logger) { + LogJSONScaled(r, freq, time.Nanosecond, l) +} + // Output each metric in the given registry periodically using the given // logger. Print timings in `scale` units (eg time.Millisecond) rather than nanos. func LogScaled(r Registry, freq time.Duration, scale time.Duration, l Logger) { @@ -78,3 +82,18 @@ func LogScaled(r Registry, freq time.Duration, scale time.Duration, l Logger) { }) } } + +// Output each metric in the given registry periodically, in JSON format using the given logger. +// Timings are converted into `scale` units (eg time.Millisecond) rather than nanos when the JSON is marshaled. +func LogJSONScaled(r Registry, freq time.Duration, scale time.Duration, l Logger) { + + for _ = range time.Tick(freq) { + sr, ok := r.(*StandardRegistry) + if ok { + jsonMetrics, err := sr.MarshalJSONStringified(scale) + if err == nil { + l.Printf("%s\n", jsonMetrics) + } + } + } +}