Codebase list golang-github-go-kit-kit / 5616015
Add comments and use default quantiles. Cameron Stitt 7 years ago
1 changed file(s) with 83 addition(s) and 56 deletion(s). Raw diff Collapse all Expand all
33 "sync"
44
55 "time"
6
7 "strconv"
86
97 "fmt"
108
1513 "github.com/go-kit/kit/metrics/generic"
1614 )
1715
18 // CloudWatch ...
16 // CloudWatch receives metrics observations and forwards them to CloudWatch.
17 // Create a CloudWatch object, use it to create metrics, and pass those metrics as
18 // dependencies to the components that will use them.
19 //
20 // To regularly report metrics to CloudWatch, use the WriteLoop helper method.
1921 type CloudWatch struct {
2022 mtx sync.RWMutex
2123 namespace string
2628 logger log.Logger
2729 }
2830
29 // New ...
31 // New returns a CloudWatch object that may be used to create metrics. Namespace is
32 // applied to all created metrics and maps to the CloudWatch namespace.
33 // Callers must ensure that regular calls to Send are performed, either manually or with one of the helper methods.
3034 func New(namespace string, logger log.Logger, svc *cloudwatch.CloudWatch) *CloudWatch {
3135 return &CloudWatch{
3236 namespace: namespace,
3842 }
3943 }
4044
45 // NewCounter returns a counter. Observations are aggregated and emitted once
46 // per write invocation.
4147 func (cw *CloudWatch) NewCounter(name string) *Counter {
4248 c := NewCounter(name)
4349 cw.mtx.Lock()
4652 return c
4753 }
4854
55 // NewGauge returns a gauge. Observations are aggregated and emitted once per
56 // write invocation.
4957 func (cw *CloudWatch) NewGauge(name string) *Gauge {
5058 g := NewGauge(name)
5159 cw.mtx.Lock()
5462 return g
5563 }
5664
57 func (cw *CloudWatch) NewHistogram(name string, quantiles []float64, buckets int) *Histogram {
58 h := NewHistogram(name, quantiles, buckets)
65 // NewHistogram returns a histogram. Observations are aggregated and emitted as
66 // per-quantile gauges, once per write invocation. 50 is a good default value
67 // for buckets.
68 func (cw *CloudWatch) NewHistogram(name string, buckets int) *Histogram {
69 h := NewHistogram(name, buckets)
5970 cw.mtx.Lock()
6071 cw.histograms[name] = h
6172 cw.mtx.Unlock()
6273 return h
6374 }
6475
65 // WriteLoop is a helper method that invokes WriteTo to the passed writer every
76 // WriteLoop is a helper method that invokes Send every
6677 // time the passed channel fires. This method blocks until the channel is
6778 // closed, so clients probably want to run it in its own goroutine. For typical
6879 // usage, create a time.Ticker and pass its C channel to this method.
6980 func (cw *CloudWatch) WriteLoop(c <-chan time.Time) {
7081 for range c {
71 cw.mtx.RLock()
72 defer cw.mtx.RUnlock()
73 now := time.Now()
74
75 datums := []*cloudwatch.MetricDatum{}
76
77 for name, c := range cw.counters {
82 if err := cw.Send(); err != nil {
83 cw.logger.Log("during", "Send", "err", err)
84 }
85 }
86 }
87
88 // Send will fire an api request to CloudWatch with the latest stats for
89 // all metrics.
90 func (cw *CloudWatch) Send() error {
91 cw.mtx.RLock()
92 defer cw.mtx.RUnlock()
93 now := time.Now()
94
95 datums := []*cloudwatch.MetricDatum{}
96
97 for name, c := range cw.counters {
98 datums = append(datums, &cloudwatch.MetricDatum{
99 MetricName: aws.String(name),
100 Dimensions: makeDimensions(c.c.LabelValues()...),
101 Value: aws.Float64(c.c.Value()),
102 Timestamp: aws.Time(now),
103 })
104 }
105
106 for name, g := range cw.gauges {
107 datums = append(datums, &cloudwatch.MetricDatum{
108 MetricName: aws.String(name),
109 Dimensions: makeDimensions(g.g.LabelValues()...),
110 Value: aws.Float64(g.g.Value()),
111 Timestamp: aws.Time(now),
112 })
113 }
114
115 for name, h := range cw.histograms {
116 for _, p := range []struct {
117 s string
118 f float64
119 }{
120 {"50", 0.50},
121 {"90", 0.90},
122 {"95", 0.95},
123 {"99", 0.99},
124 } {
78125 datums = append(datums, &cloudwatch.MetricDatum{
79 MetricName: aws.String(name),
80 Dimensions: makeDimensions(c.c.LabelValues()...),
81 Value: aws.Float64(c.c.Value()),
126 MetricName: aws.String(fmt.Sprintf("%s_%s", name, p.s)),
127 Dimensions: makeDimensions(h.h.LabelValues()...),
128 Value: aws.Float64(h.h.Quantile(p.f)),
82129 Timestamp: aws.Time(now),
83130 })
84131 }
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 })
104 }
105 }
106
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)
113 }
114 }
132 }
133
134 _, err := cw.svc.PutMetricData(&cloudwatch.PutMetricDataInput{
135 Namespace: aws.String(cw.namespace),
136 MetricData: datums,
137 })
138 return err
115139 }
116140
117141 // Counter is a CloudWatch counter metric.
126150 }
127151 }
128152
129 // With is a no-op.
153 // With implements counter
130154 func (c *Counter) With(labelValues ...string) metrics.Counter {
131155 return c.c.With(labelValues...)
132156 }
148172 }
149173 }
150174
175 // With implements gauge
151176 func (g *Gauge) With(labelValues ...string) metrics.Gauge {
152177 return &Gauge{
153178 g: g.g.With(labelValues...).(*generic.Gauge),
154179 }
155180 }
156181
182 // Set implements gauge
157183 func (g *Gauge) Set(value float64) {
158184 g.g.Set(value)
159185 }
160186
187 // Add implements gauge
161188 func (g *Gauge) Add(delta float64) {
162189 g.g.Add(delta)
163190 }
164191
165192 // Histogram is a CloudWatch histogram metric
166193 type Histogram struct {
167 quantiles []float64
168 h *generic.Histogram
194 h *generic.Histogram
169195 }
170196
171197 // NewHistogram returns a new usable histogram metric
172 func NewHistogram(name string, quantiles []float64, buckets int) *Histogram {
198 func NewHistogram(name string, buckets int) *Histogram {
173199 return &Histogram{
174 quantiles: quantiles,
175 h: generic.NewHistogram(name, buckets),
176 }
177 }
178
200 h: generic.NewHistogram(name, buckets),
201 }
202 }
203
204 // With implements histogram
179205 func (h *Histogram) With(labelValues ...string) metrics.Histogram {
180206 return &Histogram{
181207 h: h.h.With(labelValues...).(*generic.Histogram),
182208 }
183209 }
184210
211 // Observe implements histogram
185212 func (h *Histogram) Observe(value float64) {
186213 h.h.Observe(value)
187214 }