github.com/aigarnetwork/aigar@v0.0.0-20191115204914-d59a6eb70f8e/metrics/exp/exp.go (about)

     1  //  Copyright 2018 The go-ethereum Authors
     2  //  Copyright 2019 The go-aigar Authors
     3  //  This file is part of the go-aigar library.
     4  //
     5  //  The go-aigar library is free software: you can redistribute it and/or modify
     6  //  it under the terms of the GNU Lesser General Public License as published by
     7  //  the Free Software Foundation, either version 3 of the License, or
     8  //  (at your option) any later version.
     9  //
    10  //  The go-aigar library is distributed in the hope that it will be useful,
    11  //  but WITHOUT ANY WARRANTY; without even the implied warranty of
    12  //  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
    13  //  GNU Lesser General Public License for more details.
    14  //
    15  //  You should have received a copy of the GNU Lesser General Public License
    16  //  along with the go-aigar library. If not, see <http://www.gnu.org/licenses/>.
    17  
    18  // Hook go-metrics into expvar
    19  // on any /debug/metrics request, load all vars from the registry into expvar, and execute regular expvar handler
    20  package exp
    21  
    22  import (
    23  	"expvar"
    24  	"fmt"
    25  	"net/http"
    26  	"sync"
    27  
    28  	"github.com/AigarNetwork/aigar/metrics"
    29  	"github.com/AigarNetwork/aigar/metrics/prometheus"
    30  )
    31  
    32  type exp struct {
    33  	expvarLock sync.Mutex // expvar panics if you try to register the same var twice, so we must probe it safely
    34  	registry   metrics.Registry
    35  }
    36  
    37  func (exp *exp) expHandler(w http.ResponseWriter, r *http.Request) {
    38  	// load our variables into expvar
    39  	exp.syncToExpvar()
    40  
    41  	// now just run the official expvar handler code (which is not publicly callable, so pasted inline)
    42  	w.Header().Set("Content-Type", "application/json; charset=utf-8")
    43  	fmt.Fprintf(w, "{\n")
    44  	first := true
    45  	expvar.Do(func(kv expvar.KeyValue) {
    46  		if !first {
    47  			fmt.Fprintf(w, ",\n")
    48  		}
    49  		first = false
    50  		fmt.Fprintf(w, "%q: %s", kv.Key, kv.Value)
    51  	})
    52  	fmt.Fprintf(w, "\n}\n")
    53  }
    54  
    55  // Exp will register an expvar powered metrics handler with http.DefaultServeMux on "/debug/vars"
    56  func Exp(r metrics.Registry) {
    57  	h := ExpHandler(r)
    58  	// this would cause a panic:
    59  	// panic: http: multiple registrations for /debug/vars
    60  	// http.HandleFunc("/debug/vars", e.expHandler)
    61  	// haven't found an elegant way, so just use a different endpoint
    62  	http.Handle("/debug/metrics", h)
    63  	http.Handle("/debug/metrics/prometheus", prometheus.Handler(r))
    64  }
    65  
    66  // ExpHandler will return an expvar powered metrics handler.
    67  func ExpHandler(r metrics.Registry) http.Handler {
    68  	e := exp{sync.Mutex{}, r}
    69  	return http.HandlerFunc(e.expHandler)
    70  }
    71  
    72  func (exp *exp) getInt(name string) *expvar.Int {
    73  	var v *expvar.Int
    74  	exp.expvarLock.Lock()
    75  	p := expvar.Get(name)
    76  	if p != nil {
    77  		v = p.(*expvar.Int)
    78  	} else {
    79  		v = new(expvar.Int)
    80  		expvar.Publish(name, v)
    81  	}
    82  	exp.expvarLock.Unlock()
    83  	return v
    84  }
    85  
    86  func (exp *exp) getFloat(name string) *expvar.Float {
    87  	var v *expvar.Float
    88  	exp.expvarLock.Lock()
    89  	p := expvar.Get(name)
    90  	if p != nil {
    91  		v = p.(*expvar.Float)
    92  	} else {
    93  		v = new(expvar.Float)
    94  		expvar.Publish(name, v)
    95  	}
    96  	exp.expvarLock.Unlock()
    97  	return v
    98  }
    99  
   100  func (exp *exp) publishCounter(name string, metric metrics.Counter) {
   101  	v := exp.getInt(name)
   102  	v.Set(metric.Count())
   103  }
   104  
   105  func (exp *exp) publishGauge(name string, metric metrics.Gauge) {
   106  	v := exp.getInt(name)
   107  	v.Set(metric.Value())
   108  }
   109  func (exp *exp) publishGaugeFloat64(name string, metric metrics.GaugeFloat64) {
   110  	exp.getFloat(name).Set(metric.Value())
   111  }
   112  
   113  func (exp *exp) publishHistogram(name string, metric metrics.Histogram) {
   114  	h := metric.Snapshot()
   115  	ps := h.Percentiles([]float64{0.5, 0.75, 0.95, 0.99, 0.999})
   116  	exp.getInt(name + ".count").Set(h.Count())
   117  	exp.getFloat(name + ".min").Set(float64(h.Min()))
   118  	exp.getFloat(name + ".max").Set(float64(h.Max()))
   119  	exp.getFloat(name + ".mean").Set(h.Mean())
   120  	exp.getFloat(name + ".std-dev").Set(h.StdDev())
   121  	exp.getFloat(name + ".50-percentile").Set(ps[0])
   122  	exp.getFloat(name + ".75-percentile").Set(ps[1])
   123  	exp.getFloat(name + ".95-percentile").Set(ps[2])
   124  	exp.getFloat(name + ".99-percentile").Set(ps[3])
   125  	exp.getFloat(name + ".999-percentile").Set(ps[4])
   126  }
   127  
   128  func (exp *exp) publishMeter(name string, metric metrics.Meter) {
   129  	m := metric.Snapshot()
   130  	exp.getInt(name + ".count").Set(m.Count())
   131  	exp.getFloat(name + ".one-minute").Set(m.Rate1())
   132  	exp.getFloat(name + ".five-minute").Set(m.Rate5())
   133  	exp.getFloat(name + ".fifteen-minute").Set((m.Rate15()))
   134  	exp.getFloat(name + ".mean").Set(m.RateMean())
   135  }
   136  
   137  func (exp *exp) publishTimer(name string, metric metrics.Timer) {
   138  	t := metric.Snapshot()
   139  	ps := t.Percentiles([]float64{0.5, 0.75, 0.95, 0.99, 0.999})
   140  	exp.getInt(name + ".count").Set(t.Count())
   141  	exp.getFloat(name + ".min").Set(float64(t.Min()))
   142  	exp.getFloat(name + ".max").Set(float64(t.Max()))
   143  	exp.getFloat(name + ".mean").Set(t.Mean())
   144  	exp.getFloat(name + ".std-dev").Set(t.StdDev())
   145  	exp.getFloat(name + ".50-percentile").Set(ps[0])
   146  	exp.getFloat(name + ".75-percentile").Set(ps[1])
   147  	exp.getFloat(name + ".95-percentile").Set(ps[2])
   148  	exp.getFloat(name + ".99-percentile").Set(ps[3])
   149  	exp.getFloat(name + ".999-percentile").Set(ps[4])
   150  	exp.getFloat(name + ".one-minute").Set(t.Rate1())
   151  	exp.getFloat(name + ".five-minute").Set(t.Rate5())
   152  	exp.getFloat(name + ".fifteen-minute").Set(t.Rate15())
   153  	exp.getFloat(name + ".mean-rate").Set(t.RateMean())
   154  }
   155  
   156  func (exp *exp) publishResettingTimer(name string, metric metrics.ResettingTimer) {
   157  	t := metric.Snapshot()
   158  	ps := t.Percentiles([]float64{50, 75, 95, 99})
   159  	exp.getInt(name + ".count").Set(int64(len(t.Values())))
   160  	exp.getFloat(name + ".mean").Set(t.Mean())
   161  	exp.getInt(name + ".50-percentile").Set(ps[0])
   162  	exp.getInt(name + ".75-percentile").Set(ps[1])
   163  	exp.getInt(name + ".95-percentile").Set(ps[2])
   164  	exp.getInt(name + ".99-percentile").Set(ps[3])
   165  }
   166  
   167  func (exp *exp) syncToExpvar() {
   168  	exp.registry.Each(func(name string, i interface{}) {
   169  		switch i := i.(type) {
   170  		case metrics.Counter:
   171  			exp.publishCounter(name, i)
   172  		case metrics.Gauge:
   173  			exp.publishGauge(name, i)
   174  		case metrics.GaugeFloat64:
   175  			exp.publishGaugeFloat64(name, i)
   176  		case metrics.Histogram:
   177  			exp.publishHistogram(name, i)
   178  		case metrics.Meter:
   179  			exp.publishMeter(name, i)
   180  		case metrics.Timer:
   181  			exp.publishTimer(name, i)
   182  		case metrics.ResettingTimer:
   183  			exp.publishResettingTimer(name, i)
   184  		default:
   185  			panic(fmt.Sprintf("unsupported type for '%s': %T", name, i))
   186  		}
   187  	})
   188  }