github.com/v2fly/tools@v0.100.0/internal/event/export/metric/data.go (about) 1 // Copyright 2019 The Go Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style 3 // license that can be found in the LICENSE file. 4 5 package metric 6 7 import ( 8 "fmt" 9 "sort" 10 "time" 11 12 "github.com/v2fly/tools/internal/event/keys" 13 "github.com/v2fly/tools/internal/event/label" 14 ) 15 16 // Data represents a single point in the time series of a metric. 17 // This provides the common interface to all metrics no matter their data 18 // format. 19 // To get the actual values for the metric you must type assert to a concrete 20 // metric type. 21 type Data interface { 22 // Handle returns the metric handle this data is for. 23 //TODO: rethink the concept of metric handles 24 Handle() string 25 // Groups reports the rows that currently exist for this metric. 26 Groups() [][]label.Label 27 } 28 29 // Int64Data is a concrete implementation of Data for int64 scalar metrics. 30 type Int64Data struct { 31 // Info holds the original construction information. 32 Info *Scalar 33 // IsGauge is true for metrics that track values, rather than increasing over time. 34 IsGauge bool 35 // Rows holds the per group values for the metric. 36 Rows []int64 37 // End is the last time this metric was updated. 38 EndTime time.Time 39 40 groups [][]label.Label 41 key *keys.Int64 42 } 43 44 // Float64Data is a concrete implementation of Data for float64 scalar metrics. 45 type Float64Data struct { 46 // Info holds the original construction information. 47 Info *Scalar 48 // IsGauge is true for metrics that track values, rather than increasing over time. 49 IsGauge bool 50 // Rows holds the per group values for the metric. 51 Rows []float64 52 // End is the last time this metric was updated. 53 EndTime time.Time 54 55 groups [][]label.Label 56 key *keys.Float64 57 } 58 59 // HistogramInt64Data is a concrete implementation of Data for int64 histogram metrics. 60 type HistogramInt64Data struct { 61 // Info holds the original construction information. 62 Info *HistogramInt64 63 // Rows holds the per group values for the metric. 64 Rows []*HistogramInt64Row 65 // End is the last time this metric was updated. 66 EndTime time.Time 67 68 groups [][]label.Label 69 key *keys.Int64 70 } 71 72 // HistogramInt64Row holds the values for a single row of a HistogramInt64Data. 73 type HistogramInt64Row struct { 74 // Values is the counts per bucket. 75 Values []int64 76 // Count is the total count. 77 Count int64 78 // Sum is the sum of all the values recorded. 79 Sum int64 80 // Min is the smallest recorded value. 81 Min int64 82 // Max is the largest recorded value. 83 Max int64 84 } 85 86 // HistogramFloat64Data is a concrete implementation of Data for float64 histogram metrics. 87 type HistogramFloat64Data struct { 88 // Info holds the original construction information. 89 Info *HistogramFloat64 90 // Rows holds the per group values for the metric. 91 Rows []*HistogramFloat64Row 92 // End is the last time this metric was updated. 93 EndTime time.Time 94 95 groups [][]label.Label 96 key *keys.Float64 97 } 98 99 // HistogramFloat64Row holds the values for a single row of a HistogramFloat64Data. 100 type HistogramFloat64Row struct { 101 // Values is the counts per bucket. 102 Values []int64 103 // Count is the total count. 104 Count int64 105 // Sum is the sum of all the values recorded. 106 Sum float64 107 // Min is the smallest recorded value. 108 Min float64 109 // Max is the largest recorded value. 110 Max float64 111 } 112 113 func labelListEqual(a, b []label.Label) bool { 114 //TODO: make this more efficient 115 return fmt.Sprint(a) == fmt.Sprint(b) 116 } 117 118 func labelListLess(a, b []label.Label) bool { 119 //TODO: make this more efficient 120 return fmt.Sprint(a) < fmt.Sprint(b) 121 } 122 123 func getGroup(lm label.Map, g *[][]label.Label, keys []label.Key) (int, bool) { 124 group := make([]label.Label, len(keys)) 125 for i, key := range keys { 126 l := lm.Find(key) 127 if l.Valid() { 128 group[i] = l 129 } 130 } 131 old := *g 132 index := sort.Search(len(old), func(i int) bool { 133 return !labelListLess(old[i], group) 134 }) 135 if index < len(old) && labelListEqual(group, old[index]) { 136 // not a new group 137 return index, false 138 } 139 *g = make([][]label.Label, len(old)+1) 140 copy(*g, old[:index]) 141 copy((*g)[index+1:], old[index:]) 142 (*g)[index] = group 143 return index, true 144 } 145 146 func (data *Int64Data) Handle() string { return data.Info.Name } 147 func (data *Int64Data) Groups() [][]label.Label { return data.groups } 148 149 func (data *Int64Data) modify(at time.Time, lm label.Map, f func(v int64) int64) Data { 150 index, insert := getGroup(lm, &data.groups, data.Info.Keys) 151 old := data.Rows 152 if insert { 153 data.Rows = make([]int64, len(old)+1) 154 copy(data.Rows, old[:index]) 155 copy(data.Rows[index+1:], old[index:]) 156 } else { 157 data.Rows = make([]int64, len(old)) 158 copy(data.Rows, old) 159 } 160 data.Rows[index] = f(data.Rows[index]) 161 data.EndTime = at 162 frozen := *data 163 return &frozen 164 } 165 166 func (data *Int64Data) count(at time.Time, lm label.Map, l label.Label) Data { 167 return data.modify(at, lm, func(v int64) int64 { 168 return v + 1 169 }) 170 } 171 172 func (data *Int64Data) sum(at time.Time, lm label.Map, l label.Label) Data { 173 return data.modify(at, lm, func(v int64) int64 { 174 return v + data.key.From(l) 175 }) 176 } 177 178 func (data *Int64Data) latest(at time.Time, lm label.Map, l label.Label) Data { 179 return data.modify(at, lm, func(v int64) int64 { 180 return data.key.From(l) 181 }) 182 } 183 184 func (data *Float64Data) Handle() string { return data.Info.Name } 185 func (data *Float64Data) Groups() [][]label.Label { return data.groups } 186 187 func (data *Float64Data) modify(at time.Time, lm label.Map, f func(v float64) float64) Data { 188 index, insert := getGroup(lm, &data.groups, data.Info.Keys) 189 old := data.Rows 190 if insert { 191 data.Rows = make([]float64, len(old)+1) 192 copy(data.Rows, old[:index]) 193 copy(data.Rows[index+1:], old[index:]) 194 } else { 195 data.Rows = make([]float64, len(old)) 196 copy(data.Rows, old) 197 } 198 data.Rows[index] = f(data.Rows[index]) 199 data.EndTime = at 200 frozen := *data 201 return &frozen 202 } 203 204 func (data *Float64Data) sum(at time.Time, lm label.Map, l label.Label) Data { 205 return data.modify(at, lm, func(v float64) float64 { 206 return v + data.key.From(l) 207 }) 208 } 209 210 func (data *Float64Data) latest(at time.Time, lm label.Map, l label.Label) Data { 211 return data.modify(at, lm, func(v float64) float64 { 212 return data.key.From(l) 213 }) 214 } 215 216 func (data *HistogramInt64Data) Handle() string { return data.Info.Name } 217 func (data *HistogramInt64Data) Groups() [][]label.Label { return data.groups } 218 219 func (data *HistogramInt64Data) modify(at time.Time, lm label.Map, f func(v *HistogramInt64Row)) Data { 220 index, insert := getGroup(lm, &data.groups, data.Info.Keys) 221 old := data.Rows 222 var v HistogramInt64Row 223 if insert { 224 data.Rows = make([]*HistogramInt64Row, len(old)+1) 225 copy(data.Rows, old[:index]) 226 copy(data.Rows[index+1:], old[index:]) 227 } else { 228 data.Rows = make([]*HistogramInt64Row, len(old)) 229 copy(data.Rows, old) 230 v = *data.Rows[index] 231 } 232 oldValues := v.Values 233 v.Values = make([]int64, len(data.Info.Buckets)) 234 copy(v.Values, oldValues) 235 f(&v) 236 data.Rows[index] = &v 237 data.EndTime = at 238 frozen := *data 239 return &frozen 240 } 241 242 func (data *HistogramInt64Data) record(at time.Time, lm label.Map, l label.Label) Data { 243 return data.modify(at, lm, func(v *HistogramInt64Row) { 244 value := data.key.From(l) 245 v.Sum += value 246 if v.Min > value || v.Count == 0 { 247 v.Min = value 248 } 249 if v.Max < value || v.Count == 0 { 250 v.Max = value 251 } 252 v.Count++ 253 for i, b := range data.Info.Buckets { 254 if value <= b { 255 v.Values[i]++ 256 } 257 } 258 }) 259 } 260 261 func (data *HistogramFloat64Data) Handle() string { return data.Info.Name } 262 func (data *HistogramFloat64Data) Groups() [][]label.Label { return data.groups } 263 264 func (data *HistogramFloat64Data) modify(at time.Time, lm label.Map, f func(v *HistogramFloat64Row)) Data { 265 index, insert := getGroup(lm, &data.groups, data.Info.Keys) 266 old := data.Rows 267 var v HistogramFloat64Row 268 if insert { 269 data.Rows = make([]*HistogramFloat64Row, len(old)+1) 270 copy(data.Rows, old[:index]) 271 copy(data.Rows[index+1:], old[index:]) 272 } else { 273 data.Rows = make([]*HistogramFloat64Row, len(old)) 274 copy(data.Rows, old) 275 v = *data.Rows[index] 276 } 277 oldValues := v.Values 278 v.Values = make([]int64, len(data.Info.Buckets)) 279 copy(v.Values, oldValues) 280 f(&v) 281 data.Rows[index] = &v 282 data.EndTime = at 283 frozen := *data 284 return &frozen 285 } 286 287 func (data *HistogramFloat64Data) record(at time.Time, lm label.Map, l label.Label) Data { 288 return data.modify(at, lm, func(v *HistogramFloat64Row) { 289 value := data.key.From(l) 290 v.Sum += value 291 if v.Min > value || v.Count == 0 { 292 v.Min = value 293 } 294 if v.Max < value || v.Count == 0 { 295 v.Max = value 296 } 297 v.Count++ 298 for i, b := range data.Info.Buckets { 299 if value <= b { 300 v.Values[i]++ 301 } 302 } 303 }) 304 }