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  }