github.com/neatlab/neatio@v1.7.3-0.20220425043230-d903e92fcc75/utilities/metrics/resetting_timer.go (about) 1 package metrics 2 3 import ( 4 "math" 5 "sort" 6 "sync" 7 "time" 8 ) 9 10 const InitialResettingTimerSliceCap = 10 11 12 type ResettingTimer interface { 13 Values() []int64 14 Snapshot() ResettingTimer 15 Percentiles([]float64) []int64 16 Mean() float64 17 Time(func()) 18 Update(time.Duration) 19 UpdateSince(time.Time) 20 } 21 22 func GetOrRegisterResettingTimer(name string, r Registry) ResettingTimer { 23 if nil == r { 24 r = DefaultRegistry 25 } 26 return r.GetOrRegister(name, NewResettingTimer).(ResettingTimer) 27 } 28 29 func NewRegisteredResettingTimer(name string, r Registry) ResettingTimer { 30 c := NewResettingTimer() 31 if nil == r { 32 r = DefaultRegistry 33 } 34 r.Register(name, c) 35 return c 36 } 37 38 func NewResettingTimer() ResettingTimer { 39 if !Enabled { 40 return NilResettingTimer{} 41 } 42 return &StandardResettingTimer{ 43 values: make([]int64, 0, InitialResettingTimerSliceCap), 44 } 45 } 46 47 type NilResettingTimer struct { 48 } 49 50 func (NilResettingTimer) Values() []int64 { return nil } 51 52 func (NilResettingTimer) Snapshot() ResettingTimer { return NilResettingTimer{} } 53 54 func (NilResettingTimer) Time(func()) {} 55 56 func (NilResettingTimer) Update(time.Duration) {} 57 58 func (NilResettingTimer) Percentiles([]float64) []int64 { 59 panic("Percentiles called on a NilResettingTimer") 60 } 61 62 func (NilResettingTimer) Mean() float64 { 63 panic("Mean called on a NilResettingTimer") 64 } 65 66 func (NilResettingTimer) UpdateSince(time.Time) {} 67 68 type StandardResettingTimer struct { 69 values []int64 70 mutex sync.Mutex 71 } 72 73 func (t *StandardResettingTimer) Values() []int64 { 74 return t.values 75 } 76 77 func (t *StandardResettingTimer) Snapshot() ResettingTimer { 78 t.mutex.Lock() 79 defer t.mutex.Unlock() 80 currentValues := t.values 81 t.values = make([]int64, 0, InitialResettingTimerSliceCap) 82 83 return &ResettingTimerSnapshot{ 84 values: currentValues, 85 } 86 } 87 88 func (t *StandardResettingTimer) Percentiles([]float64) []int64 { 89 panic("Percentiles called on a StandardResettingTimer") 90 } 91 92 func (t *StandardResettingTimer) Mean() float64 { 93 panic("Mean called on a StandardResettingTimer") 94 } 95 96 func (t *StandardResettingTimer) Time(f func()) { 97 ts := time.Now() 98 f() 99 t.Update(time.Since(ts)) 100 } 101 102 func (t *StandardResettingTimer) Update(d time.Duration) { 103 t.mutex.Lock() 104 defer t.mutex.Unlock() 105 t.values = append(t.values, int64(d)) 106 } 107 108 func (t *StandardResettingTimer) UpdateSince(ts time.Time) { 109 t.mutex.Lock() 110 defer t.mutex.Unlock() 111 t.values = append(t.values, int64(time.Since(ts))) 112 } 113 114 type ResettingTimerSnapshot struct { 115 values []int64 116 mean float64 117 thresholdBoundaries []int64 118 calculated bool 119 } 120 121 func (t *ResettingTimerSnapshot) Snapshot() ResettingTimer { return t } 122 123 func (*ResettingTimerSnapshot) Time(func()) { 124 panic("Time called on a ResettingTimerSnapshot") 125 } 126 127 func (*ResettingTimerSnapshot) Update(time.Duration) { 128 panic("Update called on a ResettingTimerSnapshot") 129 } 130 131 func (*ResettingTimerSnapshot) UpdateSince(time.Time) { 132 panic("UpdateSince called on a ResettingTimerSnapshot") 133 } 134 135 func (t *ResettingTimerSnapshot) Values() []int64 { 136 return t.values 137 } 138 139 func (t *ResettingTimerSnapshot) Percentiles(percentiles []float64) []int64 { 140 t.calc(percentiles) 141 142 return t.thresholdBoundaries 143 } 144 145 func (t *ResettingTimerSnapshot) Mean() float64 { 146 if !t.calculated { 147 t.calc([]float64{}) 148 } 149 150 return t.mean 151 } 152 153 func (t *ResettingTimerSnapshot) calc(percentiles []float64) { 154 sort.Sort(Int64Slice(t.values)) 155 156 count := len(t.values) 157 if count > 0 { 158 min := t.values[0] 159 max := t.values[count-1] 160 161 cumulativeValues := make([]int64, count) 162 cumulativeValues[0] = min 163 for i := 1; i < count; i++ { 164 cumulativeValues[i] = t.values[i] + cumulativeValues[i-1] 165 } 166 167 t.thresholdBoundaries = make([]int64, len(percentiles)) 168 169 thresholdBoundary := max 170 171 for i, pct := range percentiles { 172 if count > 1 { 173 var abs float64 174 if pct >= 0 { 175 abs = pct 176 } else { 177 abs = 100 + pct 178 } 179 180 indexOfPerc := int(math.Floor(((abs / 100.0) * float64(count)) + 0.5)) 181 if pct >= 0 { 182 indexOfPerc -= 1 183 } 184 thresholdBoundary = t.values[indexOfPerc] 185 } 186 187 t.thresholdBoundaries[i] = thresholdBoundary 188 } 189 190 sum := cumulativeValues[count-1] 191 t.mean = float64(sum) / float64(count) 192 } else { 193 t.thresholdBoundaries = make([]int64, len(percentiles)) 194 t.mean = 0 195 } 196 197 t.calculated = true 198 } 199 200 type Int64Slice []int64 201 202 func (s Int64Slice) Len() int { return len(s) } 203 func (s Int64Slice) Less(i, j int) bool { return s[i] < s[j] } 204 func (s Int64Slice) Swap(i, j int) { s[i], s[j] = s[j], s[i] }