Add histogram instrument. Update WriteLoop to put all metrics at once.
Cameron Stitt
6 years ago
3 | 3 | "sync" |
4 | 4 | |
5 | 5 | "time" |
6 | ||
7 | "strconv" | |
8 | ||
9 | "fmt" | |
6 | 10 | |
7 | 11 | "github.com/aws/aws-sdk-go/aws" |
8 | 12 | "github.com/aws/aws-sdk-go/service/cloudwatch" |
13 | 17 | |
14 | 18 | // CloudWatch ... |
15 | 19 | type CloudWatch struct { |
16 | mtx sync.RWMutex | |
17 | prefix string | |
18 | svc *cloudwatch.CloudWatch | |
19 | counters map[string]*Counter | |
20 | gauges map[string]*Gauge | |
21 | logger log.Logger | |
20 | mtx sync.RWMutex | |
21 | namespace string | |
22 | svc *cloudwatch.CloudWatch | |
23 | counters map[string]*Counter | |
24 | gauges map[string]*Gauge | |
25 | histograms map[string]*Histogram | |
26 | logger log.Logger | |
22 | 27 | } |
23 | 28 | |
24 | 29 | // New ... |
25 | func New(prefix string, logger log.Logger, svc *cloudwatch.CloudWatch) *CloudWatch { | |
30 | func New(namespace string, logger log.Logger, svc *cloudwatch.CloudWatch) *CloudWatch { | |
26 | 31 | return &CloudWatch{ |
27 | prefix: prefix, | |
28 | svc: svc, | |
29 | counters: map[string]*Counter{}, | |
30 | gauges: map[string]*Gauge{}, | |
31 | logger: logger, | |
32 | namespace: namespace, | |
33 | svc: svc, | |
34 | counters: map[string]*Counter{}, | |
35 | gauges: map[string]*Gauge{}, | |
36 | histograms: map[string]*Histogram{}, | |
37 | logger: logger, | |
32 | 38 | } |
33 | 39 | } |
34 | 40 | |
35 | 41 | func (cw *CloudWatch) NewCounter(name string) *Counter { |
36 | 42 | c := NewCounter(name) |
37 | 43 | cw.mtx.Lock() |
38 | cw.counters[cw.prefix+name] = c | |
44 | cw.counters[name] = c | |
39 | 45 | cw.mtx.Unlock() |
40 | 46 | return c |
41 | 47 | } |
42 | 48 | |
43 | 49 | func (cw *CloudWatch) NewGauge(name string) *Gauge { |
44 | c := NewGauge(name) | |
50 | g := NewGauge(name) | |
45 | 51 | cw.mtx.Lock() |
46 | cw.counters[cw.prefix+name] = c | |
52 | cw.gauges[name] = g | |
47 | 53 | cw.mtx.Unlock() |
48 | return c | |
54 | return g | |
55 | } | |
56 | ||
57 | func (cw *CloudWatch) NewHistogram(name string, quantiles []float64, buckets int) *Histogram { | |
58 | h := NewHistogram(name, quantiles, buckets) | |
59 | cw.mtx.Lock() | |
60 | cw.histograms[name] = h | |
61 | cw.mtx.Unlock() | |
62 | return h | |
49 | 63 | } |
50 | 64 | |
51 | 65 | // WriteLoop is a helper method that invokes WriteTo to the passed writer every |
58 | 72 | defer cw.mtx.RUnlock() |
59 | 73 | now := time.Now() |
60 | 74 | |
75 | datums := []*cloudwatch.MetricDatum{} | |
76 | ||
61 | 77 | for name, c := range cw.counters { |
62 | _, err := cw.svc.PutMetricData(&cloudwatch.PutMetricDataInput{ | |
63 | Namespace: aws.String(cw.prefix), | |
64 | MetricData: []*cloudwatch.MetricDatum{ | |
65 | { | |
66 | MetricName: aws.String(name), | |
67 | Dimensions: makeDimensions(c.c.LabelValues()...), | |
68 | Value: aws.Float64(c.c.Value()), | |
69 | Timestamp: aws.Time(now), | |
70 | }, | |
71 | }, | |
78 | datums = append(datums, &cloudwatch.MetricDatum{ | |
79 | MetricName: aws.String(name), | |
80 | Dimensions: makeDimensions(c.c.LabelValues()...), | |
81 | Value: aws.Float64(c.c.Value()), | |
82 | Timestamp: aws.Time(now), | |
72 | 83 | }) |
73 | if err != nil { | |
74 | cw.logger.Log("during", "WriteLoop", "err", err) | |
84 | } | |
85 | ||
86 | for name, g := range cw.gauges { | |
87 | datums = append(datums, &cloudwatch.MetricDatum{ | |
88 | MetricName: aws.String(name), | |
89 | Dimensions: makeDimensions(g.g.LabelValues()...), | |
90 | Value: aws.Float64(g.g.Value()), | |
91 | Timestamp: aws.Time(now), | |
92 | }) | |
93 | } | |
94 | ||
95 | for name, h := range cw.histograms { | |
96 | for _, quantile := range h.quantiles { | |
97 | quantileStr := strconv.FormatFloat(quantile, 'f', 2, 64) | |
98 | datums = append(datums, &cloudwatch.MetricDatum{ | |
99 | MetricName: aws.String(fmt.Sprintf("%s_%s", name, quantileStr)), | |
100 | Dimensions: makeDimensions(h.h.LabelValues()...), | |
101 | Value: aws.Float64(h.h.Quantile(quantile)), | |
102 | Timestamp: aws.Time(now), | |
103 | }) | |
75 | 104 | } |
76 | 105 | } |
77 | 106 | |
78 | for name, g := range cw.gauges { | |
79 | _, err := cw.svc.PutMetricData(&cloudwatch.PutMetricDataInput{ | |
80 | Namespace: aws.String(cw.prefix), | |
81 | MetricData: []*cloudwatch.MetricDatum{ | |
82 | { | |
83 | MetricName: aws.String(name), | |
84 | Dimensions: makeDimensions(g.g.LabelValues()...), | |
85 | Value: aws.Float64(g.g.Value()), | |
86 | Timestamp: aws.Time(now), | |
87 | }, | |
88 | }, | |
89 | }) | |
90 | if err != nil { | |
91 | cw.logger.Log("during", "WriteLoop", "err", err) | |
92 | } | |
107 | _, err := cw.svc.PutMetricData(&cloudwatch.PutMetricDataInput{ | |
108 | Namespace: aws.String(cw.namespace), | |
109 | MetricData: datums, | |
110 | }) | |
111 | if err != nil { | |
112 | cw.logger.Log("during", "WriteLoop", "err", err) | |
93 | 113 | } |
94 | 114 | } |
95 | 115 | } |
130 | 150 | |
131 | 151 | func (g *Gauge) With(labelValues ...string) metrics.Gauge { |
132 | 152 | return &Gauge{ |
133 | g: g.g.With(labelValues...), | |
153 | g: g.g.With(labelValues...).(*generic.Gauge), | |
134 | 154 | } |
135 | 155 | } |
136 | 156 | |
140 | 160 | |
141 | 161 | func (g *Gauge) Add(delta float64) { |
142 | 162 | g.g.Add(delta) |
163 | } | |
164 | ||
165 | // Histogram is a CloudWatch histogram metric | |
166 | type Histogram struct { | |
167 | quantiles []float64 | |
168 | h *generic.Histogram | |
169 | } | |
170 | ||
171 | // NewHistogram returns a new usable histogram metric | |
172 | func NewHistogram(name string, quantiles []float64, buckets int) *Histogram { | |
173 | return &Histogram{ | |
174 | quantiles: quantiles, | |
175 | h: generic.NewHistogram(name, buckets), | |
176 | } | |
177 | } | |
178 | ||
179 | func (h *Histogram) With(labelValues ...string) metrics.Histogram { | |
180 | return &Histogram{ | |
181 | h: h.h.With(labelValues...).(*generic.Histogram), | |
182 | } | |
183 | } | |
184 | ||
185 | func (h *Histogram) Observe(value float64) { | |
186 | h.h.Observe(value) | |
143 | 187 | } |
144 | 188 | |
145 | 189 | func makeDimensions(labelValues ...string) []*cloudwatch.Dimension { |