| 
									
										
										
										
											2016-02-11 16:16:52 +02:00
										 |  |  | // Hook go-metrics into expvar | 
					
						
							|  |  |  | // on any /debug/metrics request, load all vars from the registry into expvar, and execute regular expvar handler | 
					
						
							|  |  |  | package exp | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | import ( | 
					
						
							|  |  |  | 	"expvar" | 
					
						
							|  |  |  | 	"fmt" | 
					
						
							|  |  |  | 	"net/http" | 
					
						
							|  |  |  | 	"sync" | 
					
						
							| 
									
										
										
										
											2016-10-28 20:05:01 +03:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-07-03 12:12:22 -05:00
										 |  |  | 	"github.com/ethereum/go-ethereum/log" | 
					
						
							| 
									
										
										
										
											2018-02-23 10:56:08 +01:00
										 |  |  | 	"github.com/ethereum/go-ethereum/metrics" | 
					
						
							| 
									
										
										
										
											2019-04-11 12:56:19 +03:00
										 |  |  | 	"github.com/ethereum/go-ethereum/metrics/prometheus" | 
					
						
							| 
									
										
										
										
											2016-02-11 16:16:52 +02:00
										 |  |  | ) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | type exp struct { | 
					
						
							|  |  |  | 	expvarLock sync.Mutex // expvar panics if you try to register the same var twice, so we must probe it safely | 
					
						
							|  |  |  | 	registry   metrics.Registry | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | func (exp *exp) expHandler(w http.ResponseWriter, r *http.Request) { | 
					
						
							|  |  |  | 	// load our variables into expvar | 
					
						
							|  |  |  | 	exp.syncToExpvar() | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// now just run the official expvar handler code (which is not publicly callable, so pasted inline) | 
					
						
							|  |  |  | 	w.Header().Set("Content-Type", "application/json; charset=utf-8") | 
					
						
							|  |  |  | 	fmt.Fprintf(w, "{\n") | 
					
						
							|  |  |  | 	first := true | 
					
						
							|  |  |  | 	expvar.Do(func(kv expvar.KeyValue) { | 
					
						
							|  |  |  | 		if !first { | 
					
						
							|  |  |  | 			fmt.Fprintf(w, ",\n") | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		first = false | 
					
						
							|  |  |  | 		fmt.Fprintf(w, "%q: %s", kv.Key, kv.Value) | 
					
						
							|  |  |  | 	}) | 
					
						
							|  |  |  | 	fmt.Fprintf(w, "\n}\n") | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-10-28 20:05:01 +03:00
										 |  |  | // Exp will register an expvar powered metrics handler with http.DefaultServeMux on "/debug/vars" | 
					
						
							| 
									
										
										
										
											2016-02-11 16:16:52 +02:00
										 |  |  | func Exp(r metrics.Registry) { | 
					
						
							| 
									
										
										
										
											2016-10-28 20:05:01 +03:00
										 |  |  | 	h := ExpHandler(r) | 
					
						
							| 
									
										
										
										
											2016-02-11 16:16:52 +02:00
										 |  |  | 	// this would cause a panic: | 
					
						
							|  |  |  | 	// panic: http: multiple registrations for /debug/vars | 
					
						
							|  |  |  | 	// http.HandleFunc("/debug/vars", e.expHandler) | 
					
						
							|  |  |  | 	// haven't found an elegant way, so just use a different endpoint | 
					
						
							| 
									
										
										
										
											2016-10-28 20:05:01 +03:00
										 |  |  | 	http.Handle("/debug/metrics", h) | 
					
						
							| 
									
										
										
										
											2019-04-11 12:56:19 +03:00
										 |  |  | 	http.Handle("/debug/metrics/prometheus", prometheus.Handler(r)) | 
					
						
							| 
									
										
										
										
											2016-10-28 20:05:01 +03:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // ExpHandler will return an expvar powered metrics handler. | 
					
						
							|  |  |  | func ExpHandler(r metrics.Registry) http.Handler { | 
					
						
							|  |  |  | 	e := exp{sync.Mutex{}, r} | 
					
						
							|  |  |  | 	return http.HandlerFunc(e.expHandler) | 
					
						
							| 
									
										
										
										
											2016-02-11 16:16:52 +02:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-07-03 12:12:22 -05:00
										 |  |  | // Setup starts a dedicated metrics server at the given address. | 
					
						
							|  |  |  | // This function enables metrics reporting separate from pprof. | 
					
						
							|  |  |  | func Setup(address string) { | 
					
						
							|  |  |  | 	m := http.NewServeMux() | 
					
						
							|  |  |  | 	m.Handle("/debug/metrics", ExpHandler(metrics.DefaultRegistry)) | 
					
						
							|  |  |  | 	m.Handle("/debug/metrics/prometheus", prometheus.Handler(metrics.DefaultRegistry)) | 
					
						
							|  |  |  | 	log.Info("Starting metrics server", "addr", fmt.Sprintf("http://%s/debug/metrics", address)) | 
					
						
							|  |  |  | 	go func() { | 
					
						
							|  |  |  | 		if err := http.ListenAndServe(address, m); err != nil { | 
					
						
							|  |  |  | 			log.Error("Failure in running metrics server", "err", err) | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	}() | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-02-11 16:16:52 +02:00
										 |  |  | func (exp *exp) getInt(name string) *expvar.Int { | 
					
						
							|  |  |  | 	var v *expvar.Int | 
					
						
							|  |  |  | 	exp.expvarLock.Lock() | 
					
						
							|  |  |  | 	p := expvar.Get(name) | 
					
						
							|  |  |  | 	if p != nil { | 
					
						
							|  |  |  | 		v = p.(*expvar.Int) | 
					
						
							|  |  |  | 	} else { | 
					
						
							|  |  |  | 		v = new(expvar.Int) | 
					
						
							|  |  |  | 		expvar.Publish(name, v) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	exp.expvarLock.Unlock() | 
					
						
							|  |  |  | 	return v | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | func (exp *exp) getFloat(name string) *expvar.Float { | 
					
						
							|  |  |  | 	var v *expvar.Float | 
					
						
							|  |  |  | 	exp.expvarLock.Lock() | 
					
						
							|  |  |  | 	p := expvar.Get(name) | 
					
						
							|  |  |  | 	if p != nil { | 
					
						
							|  |  |  | 		v = p.(*expvar.Float) | 
					
						
							|  |  |  | 	} else { | 
					
						
							|  |  |  | 		v = new(expvar.Float) | 
					
						
							|  |  |  | 		expvar.Publish(name, v) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	exp.expvarLock.Unlock() | 
					
						
							|  |  |  | 	return v | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | func (exp *exp) publishCounter(name string, metric metrics.Counter) { | 
					
						
							|  |  |  | 	v := exp.getInt(name) | 
					
						
							|  |  |  | 	v.Set(metric.Count()) | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | func (exp *exp) publishGauge(name string, metric metrics.Gauge) { | 
					
						
							|  |  |  | 	v := exp.getInt(name) | 
					
						
							|  |  |  | 	v.Set(metric.Value()) | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | func (exp *exp) publishGaugeFloat64(name string, metric metrics.GaugeFloat64) { | 
					
						
							|  |  |  | 	exp.getFloat(name).Set(metric.Value()) | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | func (exp *exp) publishHistogram(name string, metric metrics.Histogram) { | 
					
						
							|  |  |  | 	h := metric.Snapshot() | 
					
						
							|  |  |  | 	ps := h.Percentiles([]float64{0.5, 0.75, 0.95, 0.99, 0.999}) | 
					
						
							|  |  |  | 	exp.getInt(name + ".count").Set(h.Count()) | 
					
						
							|  |  |  | 	exp.getFloat(name + ".min").Set(float64(h.Min())) | 
					
						
							|  |  |  | 	exp.getFloat(name + ".max").Set(float64(h.Max())) | 
					
						
							| 
									
										
										
										
											2018-02-23 10:56:08 +01:00
										 |  |  | 	exp.getFloat(name + ".mean").Set(h.Mean()) | 
					
						
							|  |  |  | 	exp.getFloat(name + ".std-dev").Set(h.StdDev()) | 
					
						
							|  |  |  | 	exp.getFloat(name + ".50-percentile").Set(ps[0]) | 
					
						
							|  |  |  | 	exp.getFloat(name + ".75-percentile").Set(ps[1]) | 
					
						
							|  |  |  | 	exp.getFloat(name + ".95-percentile").Set(ps[2]) | 
					
						
							|  |  |  | 	exp.getFloat(name + ".99-percentile").Set(ps[3]) | 
					
						
							|  |  |  | 	exp.getFloat(name + ".999-percentile").Set(ps[4]) | 
					
						
							| 
									
										
										
										
											2016-02-11 16:16:52 +02:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | func (exp *exp) publishMeter(name string, metric metrics.Meter) { | 
					
						
							|  |  |  | 	m := metric.Snapshot() | 
					
						
							|  |  |  | 	exp.getInt(name + ".count").Set(m.Count()) | 
					
						
							| 
									
										
										
										
											2018-02-23 10:56:08 +01:00
										 |  |  | 	exp.getFloat(name + ".one-minute").Set(m.Rate1()) | 
					
						
							|  |  |  | 	exp.getFloat(name + ".five-minute").Set(m.Rate5()) | 
					
						
							| 
									
										
										
										
											2021-02-02 10:32:44 +01:00
										 |  |  | 	exp.getFloat(name + ".fifteen-minute").Set(m.Rate15()) | 
					
						
							| 
									
										
										
										
											2018-02-23 10:56:08 +01:00
										 |  |  | 	exp.getFloat(name + ".mean").Set(m.RateMean()) | 
					
						
							| 
									
										
										
										
											2016-02-11 16:16:52 +02:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | func (exp *exp) publishTimer(name string, metric metrics.Timer) { | 
					
						
							|  |  |  | 	t := metric.Snapshot() | 
					
						
							|  |  |  | 	ps := t.Percentiles([]float64{0.5, 0.75, 0.95, 0.99, 0.999}) | 
					
						
							|  |  |  | 	exp.getInt(name + ".count").Set(t.Count()) | 
					
						
							|  |  |  | 	exp.getFloat(name + ".min").Set(float64(t.Min())) | 
					
						
							|  |  |  | 	exp.getFloat(name + ".max").Set(float64(t.Max())) | 
					
						
							| 
									
										
										
										
											2018-02-23 10:56:08 +01:00
										 |  |  | 	exp.getFloat(name + ".mean").Set(t.Mean()) | 
					
						
							|  |  |  | 	exp.getFloat(name + ".std-dev").Set(t.StdDev()) | 
					
						
							|  |  |  | 	exp.getFloat(name + ".50-percentile").Set(ps[0]) | 
					
						
							|  |  |  | 	exp.getFloat(name + ".75-percentile").Set(ps[1]) | 
					
						
							|  |  |  | 	exp.getFloat(name + ".95-percentile").Set(ps[2]) | 
					
						
							|  |  |  | 	exp.getFloat(name + ".99-percentile").Set(ps[3]) | 
					
						
							|  |  |  | 	exp.getFloat(name + ".999-percentile").Set(ps[4]) | 
					
						
							|  |  |  | 	exp.getFloat(name + ".one-minute").Set(t.Rate1()) | 
					
						
							|  |  |  | 	exp.getFloat(name + ".five-minute").Set(t.Rate5()) | 
					
						
							|  |  |  | 	exp.getFloat(name + ".fifteen-minute").Set(t.Rate15()) | 
					
						
							|  |  |  | 	exp.getFloat(name + ".mean-rate").Set(t.RateMean()) | 
					
						
							| 
									
										
										
										
											2016-02-11 16:16:52 +02:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-06-04 13:05:16 +03:00
										 |  |  | func (exp *exp) publishResettingTimer(name string, metric metrics.ResettingTimer) { | 
					
						
							|  |  |  | 	t := metric.Snapshot() | 
					
						
							|  |  |  | 	ps := t.Percentiles([]float64{50, 75, 95, 99}) | 
					
						
							|  |  |  | 	exp.getInt(name + ".count").Set(int64(len(t.Values()))) | 
					
						
							|  |  |  | 	exp.getFloat(name + ".mean").Set(t.Mean()) | 
					
						
							|  |  |  | 	exp.getInt(name + ".50-percentile").Set(ps[0]) | 
					
						
							|  |  |  | 	exp.getInt(name + ".75-percentile").Set(ps[1]) | 
					
						
							|  |  |  | 	exp.getInt(name + ".95-percentile").Set(ps[2]) | 
					
						
							|  |  |  | 	exp.getInt(name + ".99-percentile").Set(ps[3]) | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-02-11 16:16:52 +02:00
										 |  |  | func (exp *exp) syncToExpvar() { | 
					
						
							|  |  |  | 	exp.registry.Each(func(name string, i interface{}) { | 
					
						
							| 
									
										
										
										
											2018-07-30 11:30:09 +02:00
										 |  |  | 		switch i := i.(type) { | 
					
						
							| 
									
										
										
										
											2016-02-11 16:16:52 +02:00
										 |  |  | 		case metrics.Counter: | 
					
						
							| 
									
										
										
										
											2018-07-30 11:30:09 +02:00
										 |  |  | 			exp.publishCounter(name, i) | 
					
						
							| 
									
										
										
										
											2016-02-11 16:16:52 +02:00
										 |  |  | 		case metrics.Gauge: | 
					
						
							| 
									
										
										
										
											2018-07-30 11:30:09 +02:00
										 |  |  | 			exp.publishGauge(name, i) | 
					
						
							| 
									
										
										
										
											2016-02-11 16:16:52 +02:00
										 |  |  | 		case metrics.GaugeFloat64: | 
					
						
							| 
									
										
										
										
											2018-07-30 11:30:09 +02:00
										 |  |  | 			exp.publishGaugeFloat64(name, i) | 
					
						
							| 
									
										
										
										
											2016-02-11 16:16:52 +02:00
										 |  |  | 		case metrics.Histogram: | 
					
						
							| 
									
										
										
										
											2018-07-30 11:30:09 +02:00
										 |  |  | 			exp.publishHistogram(name, i) | 
					
						
							| 
									
										
										
										
											2016-02-11 16:16:52 +02:00
										 |  |  | 		case metrics.Meter: | 
					
						
							| 
									
										
										
										
											2018-07-30 11:30:09 +02:00
										 |  |  | 			exp.publishMeter(name, i) | 
					
						
							| 
									
										
										
										
											2016-02-11 16:16:52 +02:00
										 |  |  | 		case metrics.Timer: | 
					
						
							| 
									
										
										
										
											2018-07-30 11:30:09 +02:00
										 |  |  | 			exp.publishTimer(name, i) | 
					
						
							| 
									
										
										
										
											2018-06-04 13:05:16 +03:00
										 |  |  | 		case metrics.ResettingTimer: | 
					
						
							| 
									
										
										
										
											2018-07-30 11:30:09 +02:00
										 |  |  | 			exp.publishResettingTimer(name, i) | 
					
						
							| 
									
										
										
										
											2016-02-11 16:16:52 +02:00
										 |  |  | 		default: | 
					
						
							|  |  |  | 			panic(fmt.Sprintf("unsupported type for '%s': %T", name, i)) | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	}) | 
					
						
							|  |  |  | } |