github.com/aigarnetwork/aigar@v0.0.0-20191115204914-d59a6eb70f8e/metrics/exp/exp.go (about) 1 // Copyright 2018 The go-ethereum Authors 2 // Copyright 2019 The go-aigar Authors 3 // This file is part of the go-aigar library. 4 // 5 // The go-aigar library is free software: you can redistribute it and/or modify 6 // it under the terms of the GNU Lesser General Public License as published by 7 // the Free Software Foundation, either version 3 of the License, or 8 // (at your option) any later version. 9 // 10 // The go-aigar library is distributed in the hope that it will be useful, 11 // but WITHOUT ANY WARRANTY; without even the implied warranty of 12 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 // GNU Lesser General Public License for more details. 14 // 15 // You should have received a copy of the GNU Lesser General Public License 16 // along with the go-aigar library. If not, see <http://www.gnu.org/licenses/>. 17 18 // Hook go-metrics into expvar 19 // on any /debug/metrics request, load all vars from the registry into expvar, and execute regular expvar handler 20 package exp 21 22 import ( 23 "expvar" 24 "fmt" 25 "net/http" 26 "sync" 27 28 "github.com/AigarNetwork/aigar/metrics" 29 "github.com/AigarNetwork/aigar/metrics/prometheus" 30 ) 31 32 type exp struct { 33 expvarLock sync.Mutex // expvar panics if you try to register the same var twice, so we must probe it safely 34 registry metrics.Registry 35 } 36 37 func (exp *exp) expHandler(w http.ResponseWriter, r *http.Request) { 38 // load our variables into expvar 39 exp.syncToExpvar() 40 41 // now just run the official expvar handler code (which is not publicly callable, so pasted inline) 42 w.Header().Set("Content-Type", "application/json; charset=utf-8") 43 fmt.Fprintf(w, "{\n") 44 first := true 45 expvar.Do(func(kv expvar.KeyValue) { 46 if !first { 47 fmt.Fprintf(w, ",\n") 48 } 49 first = false 50 fmt.Fprintf(w, "%q: %s", kv.Key, kv.Value) 51 }) 52 fmt.Fprintf(w, "\n}\n") 53 } 54 55 // Exp will register an expvar powered metrics handler with http.DefaultServeMux on "/debug/vars" 56 func Exp(r metrics.Registry) { 57 h := ExpHandler(r) 58 // this would cause a panic: 59 // panic: http: multiple registrations for /debug/vars 60 // http.HandleFunc("/debug/vars", e.expHandler) 61 // haven't found an elegant way, so just use a different endpoint 62 http.Handle("/debug/metrics", h) 63 http.Handle("/debug/metrics/prometheus", prometheus.Handler(r)) 64 } 65 66 // ExpHandler will return an expvar powered metrics handler. 67 func ExpHandler(r metrics.Registry) http.Handler { 68 e := exp{sync.Mutex{}, r} 69 return http.HandlerFunc(e.expHandler) 70 } 71 72 func (exp *exp) getInt(name string) *expvar.Int { 73 var v *expvar.Int 74 exp.expvarLock.Lock() 75 p := expvar.Get(name) 76 if p != nil { 77 v = p.(*expvar.Int) 78 } else { 79 v = new(expvar.Int) 80 expvar.Publish(name, v) 81 } 82 exp.expvarLock.Unlock() 83 return v 84 } 85 86 func (exp *exp) getFloat(name string) *expvar.Float { 87 var v *expvar.Float 88 exp.expvarLock.Lock() 89 p := expvar.Get(name) 90 if p != nil { 91 v = p.(*expvar.Float) 92 } else { 93 v = new(expvar.Float) 94 expvar.Publish(name, v) 95 } 96 exp.expvarLock.Unlock() 97 return v 98 } 99 100 func (exp *exp) publishCounter(name string, metric metrics.Counter) { 101 v := exp.getInt(name) 102 v.Set(metric.Count()) 103 } 104 105 func (exp *exp) publishGauge(name string, metric metrics.Gauge) { 106 v := exp.getInt(name) 107 v.Set(metric.Value()) 108 } 109 func (exp *exp) publishGaugeFloat64(name string, metric metrics.GaugeFloat64) { 110 exp.getFloat(name).Set(metric.Value()) 111 } 112 113 func (exp *exp) publishHistogram(name string, metric metrics.Histogram) { 114 h := metric.Snapshot() 115 ps := h.Percentiles([]float64{0.5, 0.75, 0.95, 0.99, 0.999}) 116 exp.getInt(name + ".count").Set(h.Count()) 117 exp.getFloat(name + ".min").Set(float64(h.Min())) 118 exp.getFloat(name + ".max").Set(float64(h.Max())) 119 exp.getFloat(name + ".mean").Set(h.Mean()) 120 exp.getFloat(name + ".std-dev").Set(h.StdDev()) 121 exp.getFloat(name + ".50-percentile").Set(ps[0]) 122 exp.getFloat(name + ".75-percentile").Set(ps[1]) 123 exp.getFloat(name + ".95-percentile").Set(ps[2]) 124 exp.getFloat(name + ".99-percentile").Set(ps[3]) 125 exp.getFloat(name + ".999-percentile").Set(ps[4]) 126 } 127 128 func (exp *exp) publishMeter(name string, metric metrics.Meter) { 129 m := metric.Snapshot() 130 exp.getInt(name + ".count").Set(m.Count()) 131 exp.getFloat(name + ".one-minute").Set(m.Rate1()) 132 exp.getFloat(name + ".five-minute").Set(m.Rate5()) 133 exp.getFloat(name + ".fifteen-minute").Set((m.Rate15())) 134 exp.getFloat(name + ".mean").Set(m.RateMean()) 135 } 136 137 func (exp *exp) publishTimer(name string, metric metrics.Timer) { 138 t := metric.Snapshot() 139 ps := t.Percentiles([]float64{0.5, 0.75, 0.95, 0.99, 0.999}) 140 exp.getInt(name + ".count").Set(t.Count()) 141 exp.getFloat(name + ".min").Set(float64(t.Min())) 142 exp.getFloat(name + ".max").Set(float64(t.Max())) 143 exp.getFloat(name + ".mean").Set(t.Mean()) 144 exp.getFloat(name + ".std-dev").Set(t.StdDev()) 145 exp.getFloat(name + ".50-percentile").Set(ps[0]) 146 exp.getFloat(name + ".75-percentile").Set(ps[1]) 147 exp.getFloat(name + ".95-percentile").Set(ps[2]) 148 exp.getFloat(name + ".99-percentile").Set(ps[3]) 149 exp.getFloat(name + ".999-percentile").Set(ps[4]) 150 exp.getFloat(name + ".one-minute").Set(t.Rate1()) 151 exp.getFloat(name + ".five-minute").Set(t.Rate5()) 152 exp.getFloat(name + ".fifteen-minute").Set(t.Rate15()) 153 exp.getFloat(name + ".mean-rate").Set(t.RateMean()) 154 } 155 156 func (exp *exp) publishResettingTimer(name string, metric metrics.ResettingTimer) { 157 t := metric.Snapshot() 158 ps := t.Percentiles([]float64{50, 75, 95, 99}) 159 exp.getInt(name + ".count").Set(int64(len(t.Values()))) 160 exp.getFloat(name + ".mean").Set(t.Mean()) 161 exp.getInt(name + ".50-percentile").Set(ps[0]) 162 exp.getInt(name + ".75-percentile").Set(ps[1]) 163 exp.getInt(name + ".95-percentile").Set(ps[2]) 164 exp.getInt(name + ".99-percentile").Set(ps[3]) 165 } 166 167 func (exp *exp) syncToExpvar() { 168 exp.registry.Each(func(name string, i interface{}) { 169 switch i := i.(type) { 170 case metrics.Counter: 171 exp.publishCounter(name, i) 172 case metrics.Gauge: 173 exp.publishGauge(name, i) 174 case metrics.GaugeFloat64: 175 exp.publishGaugeFloat64(name, i) 176 case metrics.Histogram: 177 exp.publishHistogram(name, i) 178 case metrics.Meter: 179 exp.publishMeter(name, i) 180 case metrics.Timer: 181 exp.publishTimer(name, i) 182 case metrics.ResettingTimer: 183 exp.publishResettingTimer(name, i) 184 default: 185 panic(fmt.Sprintf("unsupported type for '%s': %T", name, i)) 186 } 187 }) 188 }