Cleaner/easier way for user to specify Cloudwatch metric percentiles.
Eric Feliksik
6 years ago
13 | 13 | "github.com/go-kit/kit/metrics" |
14 | 14 | "github.com/go-kit/kit/metrics/generic" |
15 | 15 | "github.com/go-kit/kit/metrics/internal/lv" |
16 | "strconv" | |
16 | 17 | ) |
17 | 18 | |
18 | 19 | const ( |
37 | 38 | counters *lv.Space |
38 | 39 | gauges *lv.Space |
39 | 40 | histograms *lv.Space |
40 | percentiles Percentiles | |
41 | percentiles []float64 // percentiles to track | |
41 | 42 | logger log.Logger |
42 | 43 | numConcurrentRequests int |
43 | 44 | } |
56 | 57 | } |
57 | 58 | } |
58 | 59 | |
59 | func WithPercentiles(p Percentiles) option { | |
60 | // WithPercentiles registers the percentiles to track, overriding the | |
61 | // existing/default values. | |
62 | func WithPercentiles(percentiles ...float64) option { | |
60 | 63 | return func(c *CloudWatch) { |
61 | validated := Percentiles{} | |
62 | for _, entry := range p { | |
63 | if entry.f < 0 || entry.f > 1 { | |
64 | continue // illegal entry | |
64 | c.percentiles = make([]float64, 0) | |
65 | for _, p := range percentiles { | |
66 | if p < 0 || p > 1 { | |
67 | continue // illegal entry; ignore | |
65 | 68 | } |
66 | validated = append(validated, entry) | |
67 | } | |
68 | c.percentiles = validated | |
69 | c.percentiles = append(c.percentiles, p) | |
70 | } | |
69 | 71 | } |
70 | 72 | } |
71 | 73 | |
92 | 94 | histograms: lv.NewSpace(), |
93 | 95 | numConcurrentRequests: 10, |
94 | 96 | logger: log.NewLogfmtLogger(os.Stderr), |
95 | percentiles: Percentiles{ | |
96 | {"50", 0.50}, | |
97 | {"90", 0.90}, | |
98 | {"95", 0.95}, | |
99 | {"99", 0.99}, | |
100 | }, | |
97 | percentiles: []float64{0.50, 0.90, 0.95, 0.99}, | |
101 | 98 | } |
102 | 99 | |
103 | 100 | for _, optFunc := range options { |
178 | 175 | return true |
179 | 176 | }) |
180 | 177 | |
178 | // format a [0,1]-float value to a percentile value, with minimum nr of decimals | |
179 | // 0.90 -> "90" | |
180 | // 0.95 -> "95" | |
181 | // 0.999 -> "99.9" | |
182 | formatPerc := func(p float64) string { | |
183 | return strconv.FormatFloat(p*100, 'f', -1, 64) | |
184 | } | |
185 | ||
181 | 186 | cw.histograms.Reset().Walk(func(name string, lvs lv.LabelValues, values []float64) bool { |
182 | 187 | histogram := generic.NewHistogram(name, 50) |
183 | 188 | |
185 | 190 | histogram.Observe(v) |
186 | 191 | } |
187 | 192 | |
188 | for _, p := range cw.percentiles { | |
189 | value := histogram.Quantile(p.f) | |
193 | for _, perc := range cw.percentiles { | |
194 | value := histogram.Quantile(perc) | |
190 | 195 | datums = append(datums, &cloudwatch.MetricDatum{ |
191 | MetricName: aws.String(fmt.Sprintf("%s_%s", name, p.s)), | |
196 | MetricName: aws.String(fmt.Sprintf("%s_p%s", name, formatPerc(perc))), | |
192 | 197 | Dimensions: makeDimensions(lvs...), |
193 | 198 | Value: aws.Float64(value), |
194 | 199 | Timestamp: aws.Time(now), |