github.com/ethereum/go-ethereum@v1.16.1/metrics/meter.go (about) 1 package metrics 2 3 import ( 4 "math" 5 "sync" 6 "sync/atomic" 7 "time" 8 ) 9 10 // GetOrRegisterMeter returns an existing Meter or constructs and registers a 11 // new Meter. 12 // Be sure to unregister the meter from the registry once it is of no use to 13 // allow for garbage collection. 14 func GetOrRegisterMeter(name string, r Registry) *Meter { 15 return getOrRegister(name, NewMeter, r) 16 } 17 18 // NewMeter constructs a new Meter and launches a goroutine. 19 // Be sure to call Stop() once the meter is of no use to allow for garbage collection. 20 func NewMeter() *Meter { 21 m := newMeter() 22 arbiter.add(m) 23 return m 24 } 25 26 // NewInactiveMeter returns a meter but does not start any goroutines. This 27 // method is mainly intended for testing. 28 func NewInactiveMeter() *Meter { 29 return newMeter() 30 } 31 32 // NewRegisteredMeter constructs and registers a new Meter 33 // and launches a goroutine. 34 // Be sure to unregister the meter from the registry once it is of no use to 35 // allow for garbage collection. 36 func NewRegisteredMeter(name string, r Registry) *Meter { 37 return GetOrRegisterMeter(name, r) 38 } 39 40 // MeterSnapshot is a read-only copy of the meter's internal values. 41 type MeterSnapshot struct { 42 count int64 43 rate1, rate5, rate15, rateMean float64 44 } 45 46 // Count returns the count of events at the time the snapshot was taken. 47 func (m *MeterSnapshot) Count() int64 { return m.count } 48 49 // Rate1 returns the one-minute moving average rate of events per second at the 50 // time the snapshot was taken. 51 func (m *MeterSnapshot) Rate1() float64 { return m.rate1 } 52 53 // Rate5 returns the five-minute moving average rate of events per second at 54 // the time the snapshot was taken. 55 func (m *MeterSnapshot) Rate5() float64 { return m.rate5 } 56 57 // Rate15 returns the fifteen-minute moving average rate of events per second 58 // at the time the snapshot was taken. 59 func (m *MeterSnapshot) Rate15() float64 { return m.rate15 } 60 61 // RateMean returns the meter's mean rate of events per second at the time the 62 // snapshot was taken. 63 func (m *MeterSnapshot) RateMean() float64 { return m.rateMean } 64 65 // Meter count events to produce exponentially-weighted moving average rates 66 // at one-, five-, and fifteen-minutes and a mean rate. 67 type Meter struct { 68 count atomic.Int64 69 uncounted atomic.Int64 // not yet added to the EWMAs 70 rateMean atomic.Uint64 71 72 a1, a5, a15 *EWMA 73 startTime time.Time 74 stopped atomic.Bool 75 } 76 77 func newMeter() *Meter { 78 return &Meter{ 79 a1: NewEWMA1(), 80 a5: NewEWMA5(), 81 a15: NewEWMA15(), 82 startTime: time.Now(), 83 } 84 } 85 86 // Stop stops the meter, Mark() will be a no-op if you use it after being stopped. 87 func (m *Meter) Stop() { 88 if stopped := m.stopped.Swap(true); !stopped { 89 arbiter.remove(m) 90 } 91 } 92 93 // Mark records the occurrence of n events. 94 func (m *Meter) Mark(n int64) { 95 m.uncounted.Add(n) 96 } 97 98 // Snapshot returns a read-only copy of the meter. 99 func (m *Meter) Snapshot() *MeterSnapshot { 100 return &MeterSnapshot{ 101 count: m.count.Load() + m.uncounted.Load(), 102 rate1: m.a1.Snapshot().Rate(), 103 rate5: m.a5.Snapshot().Rate(), 104 rate15: m.a15.Snapshot().Rate(), 105 rateMean: math.Float64frombits(m.rateMean.Load()), 106 } 107 } 108 109 func (m *Meter) tick() { 110 // Take the uncounted values, add to count 111 n := m.uncounted.Swap(0) 112 count := m.count.Add(n) 113 m.rateMean.Store(math.Float64bits(float64(count) / time.Since(m.startTime).Seconds())) 114 // Update the EWMA's internal state 115 m.a1.Update(n) 116 m.a5.Update(n) 117 m.a15.Update(n) 118 // And trigger them to calculate the rates 119 m.a1.tick() 120 m.a5.tick() 121 m.a15.tick() 122 } 123 124 var arbiter = meterTicker{meters: make(map[*Meter]struct{})} 125 126 // meterTicker ticks meters every 5s from a single goroutine. 127 // meters are references in a set for future stopping. 128 type meterTicker struct { 129 mu sync.RWMutex 130 131 once sync.Once 132 meters map[*Meter]struct{} 133 } 134 135 // add a *Meter to the arbiter 136 func (ma *meterTicker) add(m *Meter) { 137 ma.mu.Lock() 138 defer ma.mu.Unlock() 139 ma.meters[m] = struct{}{} 140 } 141 142 // remove removes a meter from the set of ticked meters. 143 func (ma *meterTicker) remove(m *Meter) { 144 ma.mu.Lock() 145 delete(ma.meters, m) 146 ma.mu.Unlock() 147 } 148 149 // loop ticks meters on a 5-second interval. 150 func (ma *meterTicker) loop() { 151 ticker := time.NewTicker(5 * time.Second) 152 for range ticker.C { 153 if !metricsEnabled { 154 continue 155 } 156 ma.mu.RLock() 157 for meter := range ma.meters { 158 meter.tick() 159 } 160 ma.mu.RUnlock() 161 } 162 } 163 164 // startMeterTickerLoop will start the arbiter ticker. 165 func startMeterTickerLoop() { 166 arbiter.once.Do(func() { go arbiter.loop() }) 167 }