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