github.com/bloxroute-labs/bor@v0.1.4/metrics/exp/exp.go (about) 1 // Hook go-metrics into expvar 2 // on any /debug/metrics request, load all vars from the registry into expvar, and execute regular expvar handler 3 package exp 4 5 import ( 6 "expvar" 7 "fmt" 8 "net/http" 9 "sync" 10 11 "github.com/maticnetwork/bor/metrics" 12 "github.com/maticnetwork/bor/metrics/prometheus" 13 ) 14 15 type exp struct { 16 expvarLock sync.Mutex // expvar panics if you try to register the same var twice, so we must probe it safely 17 registry metrics.Registry 18 } 19 20 func (exp *exp) expHandler(w http.ResponseWriter, r *http.Request) { 21 // load our variables into expvar 22 exp.syncToExpvar() 23 24 // now just run the official expvar handler code (which is not publicly callable, so pasted inline) 25 w.Header().Set("Content-Type", "application/json; charset=utf-8") 26 fmt.Fprintf(w, "{\n") 27 first := true 28 expvar.Do(func(kv expvar.KeyValue) { 29 if !first { 30 fmt.Fprintf(w, ",\n") 31 } 32 first = false 33 fmt.Fprintf(w, "%q: %s", kv.Key, kv.Value) 34 }) 35 fmt.Fprintf(w, "\n}\n") 36 } 37 38 // Exp will register an expvar powered metrics handler with http.DefaultServeMux on "/debug/vars" 39 func Exp(r metrics.Registry) { 40 h := ExpHandler(r) 41 // this would cause a panic: 42 // panic: http: multiple registrations for /debug/vars 43 // http.HandleFunc("/debug/vars", e.expHandler) 44 // haven't found an elegant way, so just use a different endpoint 45 http.Handle("/debug/metrics", h) 46 http.Handle("/debug/metrics/prometheus", prometheus.Handler(r)) 47 } 48 49 // ExpHandler will return an expvar powered metrics handler. 50 func ExpHandler(r metrics.Registry) http.Handler { 51 e := exp{sync.Mutex{}, r} 52 return http.HandlerFunc(e.expHandler) 53 } 54 55 func (exp *exp) getInt(name string) *expvar.Int { 56 var v *expvar.Int 57 exp.expvarLock.Lock() 58 p := expvar.Get(name) 59 if p != nil { 60 v = p.(*expvar.Int) 61 } else { 62 v = new(expvar.Int) 63 expvar.Publish(name, v) 64 } 65 exp.expvarLock.Unlock() 66 return v 67 } 68 69 func (exp *exp) getFloat(name string) *expvar.Float { 70 var v *expvar.Float 71 exp.expvarLock.Lock() 72 p := expvar.Get(name) 73 if p != nil { 74 v = p.(*expvar.Float) 75 } else { 76 v = new(expvar.Float) 77 expvar.Publish(name, v) 78 } 79 exp.expvarLock.Unlock() 80 return v 81 } 82 83 func (exp *exp) publishCounter(name string, metric metrics.Counter) { 84 v := exp.getInt(name) 85 v.Set(metric.Count()) 86 } 87 88 func (exp *exp) publishGauge(name string, metric metrics.Gauge) { 89 v := exp.getInt(name) 90 v.Set(metric.Value()) 91 } 92 func (exp *exp) publishGaugeFloat64(name string, metric metrics.GaugeFloat64) { 93 exp.getFloat(name).Set(metric.Value()) 94 } 95 96 func (exp *exp) publishHistogram(name string, metric metrics.Histogram) { 97 h := metric.Snapshot() 98 ps := h.Percentiles([]float64{0.5, 0.75, 0.95, 0.99, 0.999}) 99 exp.getInt(name + ".count").Set(h.Count()) 100 exp.getFloat(name + ".min").Set(float64(h.Min())) 101 exp.getFloat(name + ".max").Set(float64(h.Max())) 102 exp.getFloat(name + ".mean").Set(h.Mean()) 103 exp.getFloat(name + ".std-dev").Set(h.StdDev()) 104 exp.getFloat(name + ".50-percentile").Set(ps[0]) 105 exp.getFloat(name + ".75-percentile").Set(ps[1]) 106 exp.getFloat(name + ".95-percentile").Set(ps[2]) 107 exp.getFloat(name + ".99-percentile").Set(ps[3]) 108 exp.getFloat(name + ".999-percentile").Set(ps[4]) 109 } 110 111 func (exp *exp) publishMeter(name string, metric metrics.Meter) { 112 m := metric.Snapshot() 113 exp.getInt(name + ".count").Set(m.Count()) 114 exp.getFloat(name + ".one-minute").Set(m.Rate1()) 115 exp.getFloat(name + ".five-minute").Set(m.Rate5()) 116 exp.getFloat(name + ".fifteen-minute").Set((m.Rate15())) 117 exp.getFloat(name + ".mean").Set(m.RateMean()) 118 } 119 120 func (exp *exp) publishTimer(name string, metric metrics.Timer) { 121 t := metric.Snapshot() 122 ps := t.Percentiles([]float64{0.5, 0.75, 0.95, 0.99, 0.999}) 123 exp.getInt(name + ".count").Set(t.Count()) 124 exp.getFloat(name + ".min").Set(float64(t.Min())) 125 exp.getFloat(name + ".max").Set(float64(t.Max())) 126 exp.getFloat(name + ".mean").Set(t.Mean()) 127 exp.getFloat(name + ".std-dev").Set(t.StdDev()) 128 exp.getFloat(name + ".50-percentile").Set(ps[0]) 129 exp.getFloat(name + ".75-percentile").Set(ps[1]) 130 exp.getFloat(name + ".95-percentile").Set(ps[2]) 131 exp.getFloat(name + ".99-percentile").Set(ps[3]) 132 exp.getFloat(name + ".999-percentile").Set(ps[4]) 133 exp.getFloat(name + ".one-minute").Set(t.Rate1()) 134 exp.getFloat(name + ".five-minute").Set(t.Rate5()) 135 exp.getFloat(name + ".fifteen-minute").Set(t.Rate15()) 136 exp.getFloat(name + ".mean-rate").Set(t.RateMean()) 137 } 138 139 func (exp *exp) publishResettingTimer(name string, metric metrics.ResettingTimer) { 140 t := metric.Snapshot() 141 ps := t.Percentiles([]float64{50, 75, 95, 99}) 142 exp.getInt(name + ".count").Set(int64(len(t.Values()))) 143 exp.getFloat(name + ".mean").Set(t.Mean()) 144 exp.getInt(name + ".50-percentile").Set(ps[0]) 145 exp.getInt(name + ".75-percentile").Set(ps[1]) 146 exp.getInt(name + ".95-percentile").Set(ps[2]) 147 exp.getInt(name + ".99-percentile").Set(ps[3]) 148 } 149 150 func (exp *exp) syncToExpvar() { 151 exp.registry.Each(func(name string, i interface{}) { 152 switch i := i.(type) { 153 case metrics.Counter: 154 exp.publishCounter(name, i) 155 case metrics.Gauge: 156 exp.publishGauge(name, i) 157 case metrics.GaugeFloat64: 158 exp.publishGaugeFloat64(name, i) 159 case metrics.Histogram: 160 exp.publishHistogram(name, i) 161 case metrics.Meter: 162 exp.publishMeter(name, i) 163 case metrics.Timer: 164 exp.publishTimer(name, i) 165 case metrics.ResettingTimer: 166 exp.publishResettingTimer(name, i) 167 default: 168 panic(fmt.Sprintf("unsupported type for '%s': %T", name, i)) 169 } 170 }) 171 }