github.com/calmw/ethereum@v0.1.1/metrics/registry.go (about)

     1  package metrics
     2  
     3  import (
     4  	"fmt"
     5  	"reflect"
     6  	"strings"
     7  	"sync"
     8  )
     9  
    10  // DuplicateMetric is the error returned by Registry.Register when a metric
    11  // already exists.  If you mean to Register that metric you must first
    12  // Unregister the existing metric.
    13  type DuplicateMetric string
    14  
    15  func (err DuplicateMetric) Error() string {
    16  	return fmt.Sprintf("duplicate metric: %s", string(err))
    17  }
    18  
    19  // A Registry holds references to a set of metrics by name and can iterate
    20  // over them, calling callback functions provided by the user.
    21  //
    22  // This is an interface so as to encourage other structs to implement
    23  // the Registry API as appropriate.
    24  type Registry interface {
    25  
    26  	// Call the given function for each registered metric.
    27  	Each(func(string, interface{}))
    28  
    29  	// Get the metric by the given name or nil if none is registered.
    30  	Get(string) interface{}
    31  
    32  	// GetAll metrics in the Registry.
    33  	GetAll() map[string]map[string]interface{}
    34  
    35  	// Gets an existing metric or registers the given one.
    36  	// The interface can be the metric to register if not found in registry,
    37  	// or a function returning the metric for lazy instantiation.
    38  	GetOrRegister(string, interface{}) interface{}
    39  
    40  	// Register the given metric under the given name.
    41  	Register(string, interface{}) error
    42  
    43  	// Run all registered healthchecks.
    44  	RunHealthchecks()
    45  
    46  	// Unregister the metric with the given name.
    47  	Unregister(string)
    48  }
    49  
    50  // The standard implementation of a Registry uses sync.map
    51  // of names to metrics.
    52  type StandardRegistry struct {
    53  	metrics sync.Map
    54  }
    55  
    56  // Create a new registry.
    57  func NewRegistry() Registry {
    58  	return &StandardRegistry{}
    59  }
    60  
    61  // Call the given function for each registered metric.
    62  func (r *StandardRegistry) Each(f func(string, interface{})) {
    63  	for name, i := range r.registered() {
    64  		f(name, i)
    65  	}
    66  }
    67  
    68  // Get the metric by the given name or nil if none is registered.
    69  func (r *StandardRegistry) Get(name string) interface{} {
    70  	item, _ := r.metrics.Load(name)
    71  	return item
    72  }
    73  
    74  // Gets an existing metric or creates and registers a new one. Threadsafe
    75  // alternative to calling Get and Register on failure.
    76  // The interface can be the metric to register if not found in registry,
    77  // or a function returning the metric for lazy instantiation.
    78  func (r *StandardRegistry) GetOrRegister(name string, i interface{}) interface{} {
    79  	// fast path
    80  	cached, ok := r.metrics.Load(name)
    81  	if ok {
    82  		return cached
    83  	}
    84  	if v := reflect.ValueOf(i); v.Kind() == reflect.Func {
    85  		i = v.Call(nil)[0].Interface()
    86  	}
    87  	item, _, ok := r.loadOrRegister(name, i)
    88  	if !ok {
    89  		return i
    90  	}
    91  	return item
    92  }
    93  
    94  // Register the given metric under the given name.  Returns a DuplicateMetric
    95  // if a metric by the given name is already registered.
    96  func (r *StandardRegistry) Register(name string, i interface{}) error {
    97  	// fast path
    98  	_, ok := r.metrics.Load(name)
    99  	if ok {
   100  		return DuplicateMetric(name)
   101  	}
   102  
   103  	if v := reflect.ValueOf(i); v.Kind() == reflect.Func {
   104  		i = v.Call(nil)[0].Interface()
   105  	}
   106  	_, loaded, _ := r.loadOrRegister(name, i)
   107  	if loaded {
   108  		return DuplicateMetric(name)
   109  	}
   110  	return nil
   111  }
   112  
   113  // Run all registered healthchecks.
   114  func (r *StandardRegistry) RunHealthchecks() {
   115  	r.metrics.Range(func(key, value any) bool {
   116  		if h, ok := value.(Healthcheck); ok {
   117  			h.Check()
   118  		}
   119  		return true
   120  	})
   121  }
   122  
   123  // GetAll metrics in the Registry
   124  func (r *StandardRegistry) GetAll() map[string]map[string]interface{} {
   125  	data := make(map[string]map[string]interface{})
   126  	r.Each(func(name string, i interface{}) {
   127  		values := make(map[string]interface{})
   128  		switch metric := i.(type) {
   129  		case Counter:
   130  			values["count"] = metric.Count()
   131  		case CounterFloat64:
   132  			values["count"] = metric.Count()
   133  		case Gauge:
   134  			values["value"] = metric.Value()
   135  		case GaugeFloat64:
   136  			values["value"] = metric.Value()
   137  		case Healthcheck:
   138  			values["error"] = nil
   139  			metric.Check()
   140  			if err := metric.Error(); nil != err {
   141  				values["error"] = metric.Error().Error()
   142  			}
   143  		case Histogram:
   144  			h := metric.Snapshot()
   145  			ps := h.Percentiles([]float64{0.5, 0.75, 0.95, 0.99, 0.999})
   146  			values["count"] = h.Count()
   147  			values["min"] = h.Min()
   148  			values["max"] = h.Max()
   149  			values["mean"] = h.Mean()
   150  			values["stddev"] = h.StdDev()
   151  			values["median"] = ps[0]
   152  			values["75%"] = ps[1]
   153  			values["95%"] = ps[2]
   154  			values["99%"] = ps[3]
   155  			values["99.9%"] = ps[4]
   156  		case Meter:
   157  			m := metric.Snapshot()
   158  			values["count"] = m.Count()
   159  			values["1m.rate"] = m.Rate1()
   160  			values["5m.rate"] = m.Rate5()
   161  			values["15m.rate"] = m.Rate15()
   162  			values["mean.rate"] = m.RateMean()
   163  		case Timer:
   164  			t := metric.Snapshot()
   165  			ps := t.Percentiles([]float64{0.5, 0.75, 0.95, 0.99, 0.999})
   166  			values["count"] = t.Count()
   167  			values["min"] = t.Min()
   168  			values["max"] = t.Max()
   169  			values["mean"] = t.Mean()
   170  			values["stddev"] = t.StdDev()
   171  			values["median"] = ps[0]
   172  			values["75%"] = ps[1]
   173  			values["95%"] = ps[2]
   174  			values["99%"] = ps[3]
   175  			values["99.9%"] = ps[4]
   176  			values["1m.rate"] = t.Rate1()
   177  			values["5m.rate"] = t.Rate5()
   178  			values["15m.rate"] = t.Rate15()
   179  			values["mean.rate"] = t.RateMean()
   180  		}
   181  		data[name] = values
   182  	})
   183  	return data
   184  }
   185  
   186  // Unregister the metric with the given name.
   187  func (r *StandardRegistry) Unregister(name string) {
   188  	r.stop(name)
   189  	r.metrics.LoadAndDelete(name)
   190  }
   191  
   192  func (r *StandardRegistry) loadOrRegister(name string, i interface{}) (interface{}, bool, bool) {
   193  	switch i.(type) {
   194  	case Counter, CounterFloat64, Gauge, GaugeFloat64, Healthcheck, Histogram, Meter, Timer, ResettingTimer:
   195  	default:
   196  		return nil, false, false
   197  	}
   198  	item, loaded := r.metrics.LoadOrStore(name, i)
   199  	return item, loaded, true
   200  }
   201  
   202  func (r *StandardRegistry) registered() map[string]interface{} {
   203  	metrics := make(map[string]interface{})
   204  	r.metrics.Range(func(key, value any) bool {
   205  		metrics[key.(string)] = value
   206  		return true
   207  	})
   208  	return metrics
   209  }
   210  
   211  func (r *StandardRegistry) stop(name string) {
   212  	if i, ok := r.metrics.Load(name); ok {
   213  		if s, ok := i.(Stoppable); ok {
   214  			s.Stop()
   215  		}
   216  	}
   217  }
   218  
   219  // Stoppable defines the metrics which has to be stopped.
   220  type Stoppable interface {
   221  	Stop()
   222  }
   223  
   224  type PrefixedRegistry struct {
   225  	underlying Registry
   226  	prefix     string
   227  }
   228  
   229  func NewPrefixedRegistry(prefix string) Registry {
   230  	return &PrefixedRegistry{
   231  		underlying: NewRegistry(),
   232  		prefix:     prefix,
   233  	}
   234  }
   235  
   236  func NewPrefixedChildRegistry(parent Registry, prefix string) Registry {
   237  	return &PrefixedRegistry{
   238  		underlying: parent,
   239  		prefix:     prefix,
   240  	}
   241  }
   242  
   243  // Call the given function for each registered metric.
   244  func (r *PrefixedRegistry) Each(fn func(string, interface{})) {
   245  	wrappedFn := func(prefix string) func(string, interface{}) {
   246  		return func(name string, iface interface{}) {
   247  			if strings.HasPrefix(name, prefix) {
   248  				fn(name, iface)
   249  			} else {
   250  				return
   251  			}
   252  		}
   253  	}
   254  
   255  	baseRegistry, prefix := findPrefix(r, "")
   256  	baseRegistry.Each(wrappedFn(prefix))
   257  }
   258  
   259  func findPrefix(registry Registry, prefix string) (Registry, string) {
   260  	switch r := registry.(type) {
   261  	case *PrefixedRegistry:
   262  		return findPrefix(r.underlying, r.prefix+prefix)
   263  	case *StandardRegistry:
   264  		return r, prefix
   265  	}
   266  	return nil, ""
   267  }
   268  
   269  // Get the metric by the given name or nil if none is registered.
   270  func (r *PrefixedRegistry) Get(name string) interface{} {
   271  	realName := r.prefix + name
   272  	return r.underlying.Get(realName)
   273  }
   274  
   275  // Gets an existing metric or registers the given one.
   276  // The interface can be the metric to register if not found in registry,
   277  // or a function returning the metric for lazy instantiation.
   278  func (r *PrefixedRegistry) GetOrRegister(name string, metric interface{}) interface{} {
   279  	realName := r.prefix + name
   280  	return r.underlying.GetOrRegister(realName, metric)
   281  }
   282  
   283  // Register the given metric under the given name. The name will be prefixed.
   284  func (r *PrefixedRegistry) Register(name string, metric interface{}) error {
   285  	realName := r.prefix + name
   286  	return r.underlying.Register(realName, metric)
   287  }
   288  
   289  // Run all registered healthchecks.
   290  func (r *PrefixedRegistry) RunHealthchecks() {
   291  	r.underlying.RunHealthchecks()
   292  }
   293  
   294  // GetAll metrics in the Registry
   295  func (r *PrefixedRegistry) GetAll() map[string]map[string]interface{} {
   296  	return r.underlying.GetAll()
   297  }
   298  
   299  // Unregister the metric with the given name. The name will be prefixed.
   300  func (r *PrefixedRegistry) Unregister(name string) {
   301  	realName := r.prefix + name
   302  	r.underlying.Unregister(realName)
   303  }
   304  
   305  var (
   306  	DefaultRegistry    = NewRegistry()
   307  	EphemeralRegistry  = NewRegistry()
   308  	AccountingRegistry = NewRegistry() // registry used in swarm
   309  )
   310  
   311  // Call the given function for each registered metric.
   312  func Each(f func(string, interface{})) {
   313  	DefaultRegistry.Each(f)
   314  }
   315  
   316  // Get the metric by the given name or nil if none is registered.
   317  func Get(name string) interface{} {
   318  	return DefaultRegistry.Get(name)
   319  }
   320  
   321  // Gets an existing metric or creates and registers a new one. Threadsafe
   322  // alternative to calling Get and Register on failure.
   323  func GetOrRegister(name string, i interface{}) interface{} {
   324  	return DefaultRegistry.GetOrRegister(name, i)
   325  }
   326  
   327  // Register the given metric under the given name.  Returns a DuplicateMetric
   328  // if a metric by the given name is already registered.
   329  func Register(name string, i interface{}) error {
   330  	return DefaultRegistry.Register(name, i)
   331  }
   332  
   333  // Register the given metric under the given name.  Panics if a metric by the
   334  // given name is already registered.
   335  func MustRegister(name string, i interface{}) {
   336  	if err := Register(name, i); err != nil {
   337  		panic(err)
   338  	}
   339  }
   340  
   341  // Run all registered healthchecks.
   342  func RunHealthchecks() {
   343  	DefaultRegistry.RunHealthchecks()
   344  }
   345  
   346  // Unregister the metric with the given name.
   347  func Unregister(name string) {
   348  	DefaultRegistry.Unregister(name)
   349  }