Added Gauge.Add
Daniel Middlecote authored 6 years ago
Daniel Middlecote committed 6 years ago
1 | 1 | package circonus |
2 | 2 | |
3 | 3 | import ( |
4 | "sync" | |
5 | ||
4 | 6 | "github.com/circonus-labs/circonus-gometrics" |
5 | 7 | |
6 | 8 | "github.com/go-kit/kit/metrics" |
62 | 64 | type Gauge struct { |
63 | 65 | name string |
64 | 66 | m *circonusgometrics.CirconusMetrics |
67 | val float64 | |
68 | mtx sync.RWMutex | |
65 | 69 | } |
66 | 70 | |
67 | 71 | // With implements Gauge, but is a no-op, because Circonus metrics have no |
69 | 73 | func (g *Gauge) With(labelValues ...string) metrics.Gauge { return g } |
70 | 74 | |
71 | 75 | // Set implements Gauge. |
72 | func (g *Gauge) Set(value float64) { g.m.SetGauge(g.name, value) } | |
76 | func (g *Gauge) Set(value float64) { | |
77 | g.mtx.Lock() | |
78 | defer g.mtx.Unlock() | |
79 | g.val = value | |
80 | g.m.SetGauge(g.name, value) | |
81 | } | |
82 | ||
83 | // Add implements metrics.Gauge. | |
84 | func (g *Gauge) Add(delta float64) { | |
85 | g.mtx.Lock() | |
86 | defer g.mtx.Unlock() | |
87 | value := g.val + delta | |
88 | g.val = value | |
89 | g.m.SetGauge(g.name, value) | |
90 | } | |
73 | 91 | |
74 | 92 | // Histogram is a Circonus implementation of a histogram metric. |
75 | 93 | type Histogram struct { |
24 | 24 | // Set implements Gauge. |
25 | 25 | func (g gauge) Set(value float64) {} |
26 | 26 | |
27 | // Add implements metrics.Gauge. | |
28 | func (g gauge) Add(delta float64) {} | |
29 | ||
27 | 30 | type histogram struct{} |
28 | 31 | |
29 | 32 | // NewHistogram returns a new no-op histogram. |
71 | 71 | return &Gauge{ |
72 | 72 | name: d.prefix + name, |
73 | 73 | obs: d.gauges.Observe, |
74 | add: d.gauges.Add, | |
74 | 75 | } |
75 | 76 | } |
76 | 77 | |
243 | 244 | name string |
244 | 245 | lvs lv.LabelValues |
245 | 246 | obs observeFunc |
247 | add observeFunc | |
246 | 248 | } |
247 | 249 | |
248 | 250 | // With implements metrics.Gauge. |
251 | 253 | name: g.name, |
252 | 254 | lvs: g.lvs.With(labelValues...), |
253 | 255 | obs: g.obs, |
256 | add: g.add, | |
254 | 257 | } |
255 | 258 | } |
256 | 259 | |
257 | 260 | // Set implements metrics.Gauge. |
258 | 261 | func (g *Gauge) Set(value float64) { |
259 | 262 | g.obs(g.name, g.lvs, value) |
263 | } | |
264 | ||
265 | // Add implements metrics.Gauge. | |
266 | func (g *Gauge) Add(delta float64) { | |
267 | g.add(g.name, g.lvs, delta) | |
260 | 268 | } |
261 | 269 | |
262 | 270 | // Timing is a DogStatsD timing, or metrics.Histogram. Observations are |
49 | 49 | // Set implements Gauge. |
50 | 50 | func (g *Gauge) Set(value float64) { g.f.Set(value) } |
51 | 51 | |
52 | // Add implements metrics.Gauge. | |
53 | func (g *Gauge) Add(delta float64) { g.f.Add(delta) } | |
54 | ||
52 | 55 | // Histogram implements the histogram metric with a combination of the generic |
53 | 56 | // Histogram object and several expvar Floats, one for each of the 50th, 90th, |
54 | 57 | // 95th, and 99th quantiles of observed values, with the quantile attached to |
104 | 104 | atomic.StoreUint64(&g.bits, math.Float64bits(value)) |
105 | 105 | } |
106 | 106 | |
107 | // Add implements metrics.Gauge. | |
108 | func (g *Gauge) Add(delta float64) { | |
109 | for { | |
110 | var ( | |
111 | old = atomic.LoadUint64(&g.bits) | |
112 | newf = math.Float64frombits(old) + delta | |
113 | new = math.Float64bits(newf) | |
114 | ) | |
115 | if atomic.CompareAndSwapUint64(&g.bits, old, new) { | |
116 | break | |
117 | } | |
118 | } | |
119 | } | |
120 | ||
107 | 121 | // Value returns the current value of the gauge. |
108 | 122 | func (g *Gauge) Value() float64 { |
109 | 123 | return math.Float64frombits(atomic.LoadUint64(&g.bits)) |
181 | 181 | // Set implements gauge. |
182 | 182 | func (g *Gauge) Set(value float64) { g.g.Set(value) } |
183 | 183 | |
184 | // Add implements metrics.Gauge. | |
185 | func (g *Gauge) Add(delta float64) { g.g.Add(delta) } | |
186 | ||
184 | 187 | // Histogram is a Graphite histogram metric. Observations are bucketed into |
185 | 188 | // per-quantile gauges. |
186 | 189 | type Histogram struct { |
43 | 43 | gauge.With("error", "true").Set(1) |
44 | 44 | gauge.With("error", "false").Set(2) |
45 | 45 | gauge.Set(50) |
46 | gauge.With("test", "true").Set(1) | |
47 | gauge.With("test", "true").Add(1) | |
46 | 48 | |
47 | 49 | client := &bufWriter{} |
48 | 50 | in.WriteTo(client) |
49 | 51 | |
50 | 52 | expectedLines := []string{ |
53 | `(influx_gauge,a=b,test=true value=2) [0-9]{19}`, | |
51 | 54 | `(influx_gauge,a=b value=50) [0-9]{19}`, |
52 | 55 | `(influx_gauge,a=b,error=true value=1) [0-9]{19}`, |
53 | 56 | `(influx_gauge,a=b,error=false value=2) [0-9]{19}`, |
58 | 61 | } |
59 | 62 | |
60 | 63 | // Output: |
64 | // influx_gauge,a=b,test=true value=2 | |
61 | 65 | // influx_gauge,a=b value=50 |
62 | 66 | // influx_gauge,a=b,error=true value=1 |
63 | 67 | // influx_gauge,a=b,error=false value=2 |
65 | 65 | return &Gauge{ |
66 | 66 | name: name, |
67 | 67 | obs: in.gauges.Observe, |
68 | add: in.gauges.Add, | |
68 | 69 | } |
69 | 70 | } |
70 | 71 | |
219 | 220 | name string |
220 | 221 | lvs lv.LabelValues |
221 | 222 | obs observeFunc |
223 | add observeFunc | |
222 | 224 | } |
223 | 225 | |
224 | 226 | // With implements metrics.Gauge. |
227 | 229 | name: g.name, |
228 | 230 | lvs: g.lvs.With(labelValues...), |
229 | 231 | obs: g.obs, |
232 | add: g.add, | |
230 | 233 | } |
231 | 234 | } |
232 | 235 | |
233 | 236 | // Set implements metrics.Gauge. |
234 | 237 | func (g *Gauge) Set(value float64) { |
235 | 238 | g.obs(g.name, g.lvs, value) |
239 | } | |
240 | ||
241 | // Add implements metrics.Gauge. | |
242 | func (g *Gauge) Add(delta float64) { | |
243 | g.add(g.name, g.lvs, delta) | |
236 | 244 | } |
237 | 245 | |
238 | 246 | // Histogram is an Influx histrogram. Observations are aggregated into a |
18 | 18 | // the vector space, and appends the value to the list of observations. |
19 | 19 | func (s *Space) Observe(name string, lvs LabelValues, value float64) { |
20 | 20 | s.nodeFor(name).observe(lvs, value) |
21 | } | |
22 | ||
23 | // Add locates the time series identified by the name and label values in | |
24 | // the vector space, and appends the delta to the last value in the list of | |
25 | // observations. | |
26 | func (s *Space) Add(name string, lvs LabelValues, delta float64) { | |
27 | s.nodeFor(name).add(lvs, delta) | |
21 | 28 | } |
22 | 29 | |
23 | 30 | // Walk traverses the vector space and invokes fn for each non-empty time series |
90 | 97 | child.observe(tail, value) |
91 | 98 | } |
92 | 99 | |
100 | func (n *node) add(lvs LabelValues, delta float64) { | |
101 | n.mtx.Lock() | |
102 | defer n.mtx.Unlock() | |
103 | if len(lvs) == 0 { | |
104 | var value float64 | |
105 | if len(n.observations) > 0 { | |
106 | value = last(n.observations) + delta | |
107 | } else { | |
108 | value = delta | |
109 | } | |
110 | n.observations = append(n.observations, value) | |
111 | return | |
112 | } | |
113 | if len(lvs) < 2 { | |
114 | panic("too few LabelValues; programmer error!") | |
115 | } | |
116 | head, tail := pair{lvs[0], lvs[1]}, lvs[2:] | |
117 | if n.children == nil { | |
118 | n.children = map[pair]*node{} | |
119 | } | |
120 | child, ok := n.children[head] | |
121 | if !ok { | |
122 | child = &node{} | |
123 | n.children[head] = child | |
124 | } | |
125 | child.add(tail, delta) | |
126 | } | |
127 | ||
93 | 128 | func (n *node) walk(lvs LabelValues, fn func(LabelValues, []float64) bool) bool { |
94 | 129 | n.mtx.RLock() |
95 | 130 | defer n.mtx.RUnlock() |
103 | 138 | } |
104 | 139 | return true |
105 | 140 | } |
141 | ||
142 | func last(a []float64) float64 { | |
143 | return a[len(a)-1] | |
144 | } |
11 | 11 | type Gauge interface { |
12 | 12 | With(labelValues ...string) Gauge |
13 | 13 | Set(value float64) |
14 | Add(delta float64) | |
14 | 15 | } |
15 | 16 | |
16 | 17 | // Histogram describes a metric that takes repeated observations of the same |
53 | 53 | return next |
54 | 54 | } |
55 | 55 | |
56 | // Add implements metrics.Gauge. | |
57 | func (g Gauge) Add(delta float64) { | |
58 | for _, gauge := range g { | |
59 | gauge.Add(delta) | |
60 | } | |
61 | } | |
62 | ||
56 | 63 | // Histogram collects multiple individual histograms and treats them as a unit. |
57 | 64 | type Histogram []metrics.Histogram |
58 | 65 |
32 | 32 | mg.Set(9) |
33 | 33 | mg.Set(8) |
34 | 34 | mg.Set(7) |
35 | mg.Add(3) | |
35 | 36 | |
36 | want := "[9 8 7]" | |
37 | want := "[9 8 7 10]" | |
37 | 38 | for i, m := range []fmt.Stringer{g1, g2, g3} { |
38 | 39 | if have := m.String(); want != have { |
39 | 40 | t.Errorf("g%d: want %q, have %q", i+1, want, have) |
75 | 76 | func (g *mockGauge) Set(value float64) { g.obs = append(g.obs, value) } |
76 | 77 | func (g *mockGauge) With(...string) metrics.Gauge { return g } |
77 | 78 | func (g *mockGauge) String() string { return fmt.Sprintf("%v", g.obs) } |
79 | func (g *mockGauge) Add(delta float64) { | |
80 | var value float64 | |
81 | if len(g.obs) > 0 { | |
82 | value = g.obs[len(g.obs)-1] + delta | |
83 | } else { | |
84 | value = delta | |
85 | } | |
86 | g.obs = append(g.obs, value) | |
87 | } | |
78 | 88 | |
79 | 89 | type mockHistogram struct { |
80 | 90 | obs []float64 |
81 | 81 | func (g *Gauge) Set(value float64) { g.g.Set(value) } |
82 | 82 | |
83 | 83 | // Add adds a value to the gauge. |
84 | func (g *Gauge) Add(value float64) { g.g.Inc(value) } | |
84 | func (g *Gauge) Add(delta float64) { g.g.Inc(delta) } | |
85 | 85 | |
86 | 86 | // Histogram wraps a speed Histogram. |
87 | 87 | type Histogram struct { |
73 | 73 | return &Gauge{ |
74 | 74 | name: s.prefix + name, |
75 | 75 | obs: s.gauges.Observe, |
76 | add: s.gauges.Add, | |
76 | 77 | } |
77 | 78 | } |
78 | 79 | |
200 | 201 | type Gauge struct { |
201 | 202 | name string |
202 | 203 | obs observeFunc |
204 | add observeFunc | |
203 | 205 | } |
204 | 206 | |
205 | 207 | // With is a no-op. |
210 | 212 | // Set implements metrics.Gauge. |
211 | 213 | func (g *Gauge) Set(value float64) { |
212 | 214 | g.obs(g.name, lv.LabelValues{}, value) |
215 | } | |
216 | ||
217 | // Add implements metrics.Gauge. | |
218 | func (g *Gauge) Add(delta float64) { | |
219 | g.add(g.name, lv.LabelValues{}, delta) | |
213 | 220 | } |
214 | 221 | |
215 | 222 | // Timing is a StatsD timing, or metrics.Histogram. Observations are |