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