Skip to content
This repository was archived by the owner on Apr 1, 2025. It is now read-only.

Added support for logging metrics as single line json instead of printing on separate lines #200

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
67 changes: 67 additions & 0 deletions json.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import (
"encoding/json"
"io"
"time"
"fmt"
)

// MarshalJSON returns a byte slice containing a JSON representation of all
Expand Down Expand Up @@ -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) {
Expand Down
19 changes: 19 additions & 0 deletions log.go
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Expand Down Expand Up @@ -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)
}
}
}
}