123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170 |
- // Hook go-metrics into expvar
- // on any /debug/metrics request, load all vars from the registry into expvar, and execute regular expvar handler
- package exp
- import (
- "expvar"
- "fmt"
- "net/http"
- "sync"
- "github.com/ethereum/go-ethereum/metrics"
- )
- type exp struct {
- expvarLock sync.Mutex // expvar panics if you try to register the same var twice, so we must probe it safely
- registry metrics.Registry
- }
- func (exp *exp) expHandler(w http.ResponseWriter, r *http.Request) {
- // load our variables into expvar
- exp.syncToExpvar()
- // now just run the official expvar handler code (which is not publicly callable, so pasted inline)
- w.Header().Set("Content-Type", "application/json; charset=utf-8")
- fmt.Fprintf(w, "{\n")
- first := true
- expvar.Do(func(kv expvar.KeyValue) {
- if !first {
- fmt.Fprintf(w, ",\n")
- }
- first = false
- fmt.Fprintf(w, "%q: %s", kv.Key, kv.Value)
- })
- fmt.Fprintf(w, "\n}\n")
- }
- // Exp will register an expvar powered metrics handler with http.DefaultServeMux on "/debug/vars"
- func Exp(r metrics.Registry) {
- h := ExpHandler(r)
- // this would cause a panic:
- // panic: http: multiple registrations for /debug/vars
- // http.HandleFunc("/debug/vars", e.expHandler)
- // haven't found an elegant way, so just use a different endpoint
- http.Handle("/debug/metrics", h)
- }
- // ExpHandler will return an expvar powered metrics handler.
- func ExpHandler(r metrics.Registry) http.Handler {
- e := exp{sync.Mutex{}, r}
- return http.HandlerFunc(e.expHandler)
- }
- func (exp *exp) getInt(name string) *expvar.Int {
- var v *expvar.Int
- exp.expvarLock.Lock()
- p := expvar.Get(name)
- if p != nil {
- v = p.(*expvar.Int)
- } else {
- v = new(expvar.Int)
- expvar.Publish(name, v)
- }
- exp.expvarLock.Unlock()
- return v
- }
- func (exp *exp) getFloat(name string) *expvar.Float {
- var v *expvar.Float
- exp.expvarLock.Lock()
- p := expvar.Get(name)
- if p != nil {
- v = p.(*expvar.Float)
- } else {
- v = new(expvar.Float)
- expvar.Publish(name, v)
- }
- exp.expvarLock.Unlock()
- return v
- }
- func (exp *exp) publishCounter(name string, metric metrics.Counter) {
- v := exp.getInt(name)
- v.Set(metric.Count())
- }
- func (exp *exp) publishGauge(name string, metric metrics.Gauge) {
- v := exp.getInt(name)
- v.Set(metric.Value())
- }
- func (exp *exp) publishGaugeFloat64(name string, metric metrics.GaugeFloat64) {
- exp.getFloat(name).Set(metric.Value())
- }
- func (exp *exp) publishHistogram(name string, metric metrics.Histogram) {
- h := metric.Snapshot()
- ps := h.Percentiles([]float64{0.5, 0.75, 0.95, 0.99, 0.999})
- exp.getInt(name + ".count").Set(h.Count())
- exp.getFloat(name + ".min").Set(float64(h.Min()))
- exp.getFloat(name + ".max").Set(float64(h.Max()))
- exp.getFloat(name + ".mean").Set(h.Mean())
- exp.getFloat(name + ".std-dev").Set(h.StdDev())
- exp.getFloat(name + ".50-percentile").Set(ps[0])
- exp.getFloat(name + ".75-percentile").Set(ps[1])
- exp.getFloat(name + ".95-percentile").Set(ps[2])
- exp.getFloat(name + ".99-percentile").Set(ps[3])
- exp.getFloat(name + ".999-percentile").Set(ps[4])
- }
- func (exp *exp) publishMeter(name string, metric metrics.Meter) {
- m := metric.Snapshot()
- exp.getInt(name + ".count").Set(m.Count())
- exp.getFloat(name + ".one-minute").Set(m.Rate1())
- exp.getFloat(name + ".five-minute").Set(m.Rate5())
- exp.getFloat(name + ".fifteen-minute").Set((m.Rate15()))
- exp.getFloat(name + ".mean").Set(m.RateMean())
- }
- func (exp *exp) publishTimer(name string, metric metrics.Timer) {
- t := metric.Snapshot()
- ps := t.Percentiles([]float64{0.5, 0.75, 0.95, 0.99, 0.999})
- exp.getInt(name + ".count").Set(t.Count())
- exp.getFloat(name + ".min").Set(float64(t.Min()))
- exp.getFloat(name + ".max").Set(float64(t.Max()))
- exp.getFloat(name + ".mean").Set(t.Mean())
- exp.getFloat(name + ".std-dev").Set(t.StdDev())
- exp.getFloat(name + ".50-percentile").Set(ps[0])
- exp.getFloat(name + ".75-percentile").Set(ps[1])
- exp.getFloat(name + ".95-percentile").Set(ps[2])
- exp.getFloat(name + ".99-percentile").Set(ps[3])
- exp.getFloat(name + ".999-percentile").Set(ps[4])
- exp.getFloat(name + ".one-minute").Set(t.Rate1())
- exp.getFloat(name + ".five-minute").Set(t.Rate5())
- exp.getFloat(name + ".fifteen-minute").Set(t.Rate15())
- exp.getFloat(name + ".mean-rate").Set(t.RateMean())
- }
- func (exp *exp) publishResettingTimer(name string, metric metrics.ResettingTimer) {
- t := metric.Snapshot()
- ps := t.Percentiles([]float64{50, 75, 95, 99})
- exp.getInt(name + ".count").Set(int64(len(t.Values())))
- exp.getFloat(name + ".mean").Set(t.Mean())
- exp.getInt(name + ".50-percentile").Set(ps[0])
- exp.getInt(name + ".75-percentile").Set(ps[1])
- exp.getInt(name + ".95-percentile").Set(ps[2])
- exp.getInt(name + ".99-percentile").Set(ps[3])
- }
- func (exp *exp) syncToExpvar() {
- exp.registry.Each(func(name string, i interface{}) {
- switch i.(type) {
- case metrics.Counter:
- exp.publishCounter(name, i.(metrics.Counter))
- case metrics.Gauge:
- exp.publishGauge(name, i.(metrics.Gauge))
- case metrics.GaugeFloat64:
- exp.publishGaugeFloat64(name, i.(metrics.GaugeFloat64))
- case metrics.Histogram:
- exp.publishHistogram(name, i.(metrics.Histogram))
- case metrics.Meter:
- exp.publishMeter(name, i.(metrics.Meter))
- case metrics.Timer:
- exp.publishTimer(name, i.(metrics.Timer))
- case metrics.ResettingTimer:
- exp.publishResettingTimer(name, i.(metrics.ResettingTimer))
- default:
- panic(fmt.Sprintf("unsupported type for '%s': %T", name, i))
- }
- })
- }
|