github.com/aaa256/atlantis@v0.0.0-20210707112435-42ee889287a2/metrics/resetting_timer.go (about) 1 package metrics 2 3 import ( 4 "math" 5 "sort" 6 "sync" 7 "time" 8 ) 9 10 // Initial slice capacity for the values stored in a ResettingTimer 11 const InitialResettingTimerSliceCap = 10 12 13 // ResettingTimer is used for storing aggregated values for timers, which are reset on every flush interval. 14 type ResettingTimer interface { 15 Values() []int64 16 Snapshot() ResettingTimer 17 Percentiles([]float64) []int64 18 Mean() float64 19 Time(func()) 20 Update(time.Duration) 21 UpdateSince(time.Time) 22 } 23 24 // GetOrRegisterResettingTimer returns an existing ResettingTimer or constructs and registers a 25 // new StandardResettingTimer. 26 func GetOrRegisterResettingTimer(name string, r Registry) ResettingTimer { 27 if nil == r { 28 r = DefaultRegistry 29 } 30 return r.GetOrRegister(name, NewResettingTimer).(ResettingTimer) 31 } 32 33 // NewRegisteredResettingTimer constructs and registers a new StandardResettingTimer. 34 func NewRegisteredResettingTimer(name string, r Registry) ResettingTimer { 35 c := NewResettingTimer() 36 if nil == r { 37 r = DefaultRegistry 38 } 39 r.Register(name, c) 40 return c 41 } 42 43 // NewResettingTimer constructs a new StandardResettingTimer 44 func NewResettingTimer() ResettingTimer { 45 if !Enabled { 46 return NilResettingTimer{} 47 } 48 return &StandardResettingTimer{ 49 values: make([]int64, 0, InitialResettingTimerSliceCap), 50 } 51 } 52 53 // NilResettingTimer is a no-op ResettingTimer. 54 type NilResettingTimer struct { 55 } 56 57 // Values is a no-op. 58 func (NilResettingTimer) Values() []int64 { return nil } 59 60 // Snapshot is a no-op. 61 func (NilResettingTimer) Snapshot() ResettingTimer { 62 return &ResettingTimerSnapshot{ 63 values: []int64{}, 64 } 65 } 66 67 // Time is a no-op. 68 func (NilResettingTimer) Time(func()) {} 69 70 // Update is a no-op. 71 func (NilResettingTimer) Update(time.Duration) {} 72 73 // Percentiles panics. 74 func (NilResettingTimer) Percentiles([]float64) []int64 { 75 panic("Percentiles called on a NilResettingTimer") 76 } 77 78 // Mean panics. 79 func (NilResettingTimer) Mean() float64 { 80 panic("Mean called on a NilResettingTimer") 81 } 82 83 // UpdateSince is a no-op. 84 func (NilResettingTimer) UpdateSince(time.Time) {} 85 86 // StandardResettingTimer is the standard implementation of a ResettingTimer. 87 // and Meter. 88 type StandardResettingTimer struct { 89 values []int64 90 mutex sync.Mutex 91 } 92 93 // Values returns a slice with all measurements. 94 func (t *StandardResettingTimer) Values() []int64 { 95 return t.values 96 } 97 98 // Snapshot resets the timer and returns a read-only copy of its contents. 99 func (t *StandardResettingTimer) Snapshot() ResettingTimer { 100 t.mutex.Lock() 101 defer t.mutex.Unlock() 102 currentValues := t.values 103 t.values = make([]int64, 0, InitialResettingTimerSliceCap) 104 105 return &ResettingTimerSnapshot{ 106 values: currentValues, 107 } 108 } 109 110 // Percentiles panics. 111 func (t *StandardResettingTimer) Percentiles([]float64) []int64 { 112 panic("Percentiles called on a StandardResettingTimer") 113 } 114 115 // Mean panics. 116 func (t *StandardResettingTimer) Mean() float64 { 117 panic("Mean called on a StandardResettingTimer") 118 } 119 120 // Record the duration of the execution of the given function. 121 func (t *StandardResettingTimer) Time(f func()) { 122 ts := time.Now() 123 f() 124 t.Update(time.Since(ts)) 125 } 126 127 // Record the duration of an event. 128 func (t *StandardResettingTimer) Update(d time.Duration) { 129 t.mutex.Lock() 130 defer t.mutex.Unlock() 131 t.values = append(t.values, int64(d)) 132 } 133 134 // Record the duration of an event that started at a time and ends now. 135 func (t *StandardResettingTimer) UpdateSince(ts time.Time) { 136 t.mutex.Lock() 137 defer t.mutex.Unlock() 138 t.values = append(t.values, int64(time.Since(ts))) 139 } 140 141 // ResettingTimerSnapshot is a point-in-time copy of another ResettingTimer. 142 type ResettingTimerSnapshot struct { 143 values []int64 144 mean float64 145 thresholdBoundaries []int64 146 calculated bool 147 } 148 149 // Snapshot returns the snapshot. 150 func (t *ResettingTimerSnapshot) Snapshot() ResettingTimer { return t } 151 152 // Time panics. 153 func (*ResettingTimerSnapshot) Time(func()) { 154 panic("Time called on a ResettingTimerSnapshot") 155 } 156 157 // Update panics. 158 func (*ResettingTimerSnapshot) Update(time.Duration) { 159 panic("Update called on a ResettingTimerSnapshot") 160 } 161 162 // UpdateSince panics. 163 func (*ResettingTimerSnapshot) UpdateSince(time.Time) { 164 panic("UpdateSince called on a ResettingTimerSnapshot") 165 } 166 167 // Values returns all values from snapshot. 168 func (t *ResettingTimerSnapshot) Values() []int64 { 169 return t.values 170 } 171 172 // Percentiles returns the boundaries for the input percentiles. 173 func (t *ResettingTimerSnapshot) Percentiles(percentiles []float64) []int64 { 174 t.calc(percentiles) 175 176 return t.thresholdBoundaries 177 } 178 179 // Mean returns the mean of the snapshotted values 180 func (t *ResettingTimerSnapshot) Mean() float64 { 181 if !t.calculated { 182 t.calc([]float64{}) 183 } 184 185 return t.mean 186 } 187 188 func (t *ResettingTimerSnapshot) calc(percentiles []float64) { 189 sort.Sort(Int64Slice(t.values)) 190 191 count := len(t.values) 192 if count > 0 { 193 min := t.values[0] 194 max := t.values[count-1] 195 196 cumulativeValues := make([]int64, count) 197 cumulativeValues[0] = min 198 for i := 1; i < count; i++ { 199 cumulativeValues[i] = t.values[i] + cumulativeValues[i-1] 200 } 201 202 t.thresholdBoundaries = make([]int64, len(percentiles)) 203 204 thresholdBoundary := max 205 206 for i, pct := range percentiles { 207 if count > 1 { 208 var abs float64 209 if pct >= 0 { 210 abs = pct 211 } else { 212 abs = 100 + pct 213 } 214 // poor man's math.Round(x): 215 // math.Floor(x + 0.5) 216 indexOfPerc := int(math.Floor(((abs / 100.0) * float64(count)) + 0.5)) 217 if pct >= 0 && indexOfPerc > 0 { 218 indexOfPerc -= 1 // index offset=0 219 } 220 thresholdBoundary = t.values[indexOfPerc] 221 } 222 223 t.thresholdBoundaries[i] = thresholdBoundary 224 } 225 226 sum := cumulativeValues[count-1] 227 t.mean = float64(sum) / float64(count) 228 } else { 229 t.thresholdBoundaries = make([]int64, len(percentiles)) 230 t.mean = 0 231 } 232 233 t.calculated = true 234 } 235 236 // Int64Slice attaches the methods of sort.Interface to []int64, sorting in increasing order. 237 type Int64Slice []int64 238 239 func (s Int64Slice) Len() int { return len(s) } 240 func (s Int64Slice) Less(i, j int) bool { return s[i] < s[j] } 241 func (s Int64Slice) Swap(i, j int) { s[i], s[j] = s[j], s[i] }