github.com/ethereum/go-ethereum@v1.16.1/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 4 package exp 5 6 import ( 7 "expvar" 8 "fmt" 9 "net/http" 10 "sync" 11 12 "github.com/ethereum/go-ethereum/log" 13 "github.com/ethereum/go-ethereum/metrics" 14 "github.com/ethereum/go-ethereum/metrics/prometheus" 15 ) 16 17 type exp struct { 18 expvarLock sync.Mutex // expvar panics if you try to register the same var twice, so we must probe it safely 19 registry metrics.Registry 20 } 21 22 func (exp *exp) expHandler(w http.ResponseWriter, r *http.Request) { 23 // load our variables into expvar 24 exp.syncToExpvar() 25 26 // now just run the official expvar handler code (which is not publicly callable, so pasted inline) 27 w.Header().Set("Content-Type", "application/json; charset=utf-8") 28 fmt.Fprintf(w, "{\n") 29 first := true 30 expvar.Do(func(kv expvar.KeyValue) { 31 if !first { 32 fmt.Fprintf(w, ",\n") 33 } 34 first = false 35 fmt.Fprintf(w, "%q: %s", kv.Key, kv.Value) 36 }) 37 fmt.Fprintf(w, "\n}\n") 38 } 39 40 // Exp will register an expvar powered metrics handler with http.DefaultServeMux on "/debug/vars" 41 func Exp(r metrics.Registry) { 42 h := ExpHandler(r) 43 // this would cause a panic: 44 // panic: http: multiple registrations for /debug/vars 45 // http.HandleFunc("/debug/vars", e.expHandler) 46 // haven't found an elegant way, so just use a different endpoint 47 http.Handle("/debug/metrics", h) 48 http.Handle("/debug/metrics/prometheus", prometheus.Handler(r)) 49 } 50 51 // ExpHandler will return an expvar powered metrics handler. 52 func ExpHandler(r metrics.Registry) http.Handler { 53 e := exp{sync.Mutex{}, r} 54 return http.HandlerFunc(e.expHandler) 55 } 56 57 // Setup starts a dedicated metrics server at the given address. 58 // This function enables metrics reporting separate from pprof. 59 func Setup(address string) { 60 m := http.NewServeMux() 61 m.Handle("/debug/metrics", ExpHandler(metrics.DefaultRegistry)) 62 m.Handle("/debug/metrics/prometheus", prometheus.Handler(metrics.DefaultRegistry)) 63 log.Info("Starting metrics server", "addr", fmt.Sprintf("http://%s/debug/metrics", address)) 64 go func() { 65 if err := http.ListenAndServe(address, m); err != nil { 66 log.Error("Failure in running metrics server", "err", err) 67 } 68 }() 69 } 70 71 func (exp *exp) getInt(name string) *expvar.Int { 72 var v *expvar.Int 73 exp.expvarLock.Lock() 74 p := expvar.Get(name) 75 if p != nil { 76 v = p.(*expvar.Int) 77 } else { 78 v = new(expvar.Int) 79 expvar.Publish(name, v) 80 } 81 exp.expvarLock.Unlock() 82 return v 83 } 84 85 func (exp *exp) getFloat(name string) *expvar.Float { 86 var v *expvar.Float 87 exp.expvarLock.Lock() 88 p := expvar.Get(name) 89 if p != nil { 90 v = p.(*expvar.Float) 91 } else { 92 v = new(expvar.Float) 93 expvar.Publish(name, v) 94 } 95 exp.expvarLock.Unlock() 96 return v 97 } 98 99 func (exp *exp) getInfo(name string) *expvar.String { 100 var v *expvar.String 101 exp.expvarLock.Lock() 102 p := expvar.Get(name) 103 if p != nil { 104 v = p.(*expvar.String) 105 } else { 106 v = new(expvar.String) 107 expvar.Publish(name, v) 108 } 109 exp.expvarLock.Unlock() 110 return v 111 } 112 113 func (exp *exp) publishCounter(name string, metric metrics.CounterSnapshot) { 114 v := exp.getInt(name) 115 v.Set(metric.Count()) 116 } 117 118 func (exp *exp) publishCounterFloat64(name string, metric metrics.CounterFloat64Snapshot) { 119 v := exp.getFloat(name) 120 v.Set(metric.Count()) 121 } 122 123 func (exp *exp) publishGauge(name string, metric metrics.GaugeSnapshot) { 124 v := exp.getInt(name) 125 v.Set(metric.Value()) 126 } 127 func (exp *exp) publishGaugeFloat64(name string, metric metrics.GaugeFloat64Snapshot) { 128 exp.getFloat(name).Set(metric.Value()) 129 } 130 131 func (exp *exp) publishGaugeInfo(name string, metric metrics.GaugeInfoSnapshot) { 132 exp.getInfo(name).Set(metric.Value().String()) 133 } 134 135 func (exp *exp) publishHistogram(name string, metric metrics.Histogram) { 136 h := metric.Snapshot() 137 ps := h.Percentiles([]float64{0.5, 0.75, 0.95, 0.99, 0.999}) 138 exp.getInt(name + ".count").Set(h.Count()) 139 exp.getFloat(name + ".min").Set(float64(h.Min())) 140 exp.getFloat(name + ".max").Set(float64(h.Max())) 141 exp.getFloat(name + ".mean").Set(h.Mean()) 142 exp.getFloat(name + ".std-dev").Set(h.StdDev()) 143 exp.getFloat(name + ".50-percentile").Set(ps[0]) 144 exp.getFloat(name + ".75-percentile").Set(ps[1]) 145 exp.getFloat(name + ".95-percentile").Set(ps[2]) 146 exp.getFloat(name + ".99-percentile").Set(ps[3]) 147 exp.getFloat(name + ".999-percentile").Set(ps[4]) 148 } 149 150 func (exp *exp) publishMeter(name string, metric *metrics.Meter) { 151 m := metric.Snapshot() 152 exp.getInt(name + ".count").Set(m.Count()) 153 exp.getFloat(name + ".one-minute").Set(m.Rate1()) 154 exp.getFloat(name + ".five-minute").Set(m.Rate5()) 155 exp.getFloat(name + ".fifteen-minute").Set(m.Rate15()) 156 exp.getFloat(name + ".mean").Set(m.RateMean()) 157 } 158 159 func (exp *exp) publishTimer(name string, metric *metrics.Timer) { 160 t := metric.Snapshot() 161 ps := t.Percentiles([]float64{0.5, 0.75, 0.95, 0.99, 0.999}) 162 exp.getInt(name + ".count").Set(t.Count()) 163 exp.getFloat(name + ".min").Set(float64(t.Min())) 164 exp.getFloat(name + ".max").Set(float64(t.Max())) 165 exp.getFloat(name + ".mean").Set(t.Mean()) 166 exp.getFloat(name + ".std-dev").Set(t.StdDev()) 167 exp.getFloat(name + ".50-percentile").Set(ps[0]) 168 exp.getFloat(name + ".75-percentile").Set(ps[1]) 169 exp.getFloat(name + ".95-percentile").Set(ps[2]) 170 exp.getFloat(name + ".99-percentile").Set(ps[3]) 171 exp.getFloat(name + ".999-percentile").Set(ps[4]) 172 exp.getFloat(name + ".one-minute").Set(t.Rate1()) 173 exp.getFloat(name + ".five-minute").Set(t.Rate5()) 174 exp.getFloat(name + ".fifteen-minute").Set(t.Rate15()) 175 exp.getFloat(name + ".mean-rate").Set(t.RateMean()) 176 } 177 178 func (exp *exp) publishResettingTimer(name string, metric *metrics.ResettingTimer) { 179 t := metric.Snapshot() 180 ps := t.Percentiles([]float64{0.50, 0.75, 0.95, 0.99}) 181 exp.getInt(name + ".count").Set(int64(t.Count())) 182 exp.getFloat(name + ".mean").Set(t.Mean()) 183 exp.getFloat(name + ".50-percentile").Set(ps[0]) 184 exp.getFloat(name + ".75-percentile").Set(ps[1]) 185 exp.getFloat(name + ".95-percentile").Set(ps[2]) 186 exp.getFloat(name + ".99-percentile").Set(ps[3]) 187 } 188 189 func (exp *exp) syncToExpvar() { 190 exp.registry.Each(func(name string, i interface{}) { 191 switch i := i.(type) { 192 case *metrics.Counter: 193 exp.publishCounter(name, i.Snapshot()) 194 case *metrics.CounterFloat64: 195 exp.publishCounterFloat64(name, i.Snapshot()) 196 case *metrics.Gauge: 197 exp.publishGauge(name, i.Snapshot()) 198 case *metrics.GaugeFloat64: 199 exp.publishGaugeFloat64(name, i.Snapshot()) 200 case *metrics.GaugeInfo: 201 exp.publishGaugeInfo(name, i.Snapshot()) 202 case metrics.Histogram: 203 exp.publishHistogram(name, i) 204 case *metrics.Meter: 205 exp.publishMeter(name, i) 206 case *metrics.Timer: 207 exp.publishTimer(name, i) 208 case *metrics.ResettingTimer: 209 exp.publishResettingTimer(name, i) 210 default: 211 panic(fmt.Sprintf("unsupported type for '%s': %T", name, i)) 212 } 213 }) 214 }