github.com/ethereum/go-ethereum@v1.16.1/metrics/registry.go (about)

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