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  }