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