1package statsd
2
3import (
4	"math"
5	"sync"
6	"sync/atomic"
7)
8
9/*
10Those are metrics type that can be aggregated on the client side:
11  - Gauge
12  - Count
13  - Set
14*/
15
16type countMetric struct {
17	value int64
18	name  string
19	tags  []string
20}
21
22func newCountMetric(name string, value int64, tags []string) *countMetric {
23	return &countMetric{
24		value: value,
25		name:  name,
26		tags:  tags,
27	}
28}
29
30func (c *countMetric) sample(v int64) {
31	atomic.AddInt64(&c.value, v)
32}
33
34func (c *countMetric) flushUnsafe() metric {
35	return metric{
36		metricType: count,
37		name:       c.name,
38		tags:       c.tags,
39		rate:       1,
40		ivalue:     c.value,
41	}
42}
43
44// Gauge
45
46type gaugeMetric struct {
47	value uint64
48	name  string
49	tags  []string
50}
51
52func newGaugeMetric(name string, value float64, tags []string) *gaugeMetric {
53	return &gaugeMetric{
54		value: math.Float64bits(value),
55		name:  name,
56		tags:  tags,
57	}
58}
59
60func (g *gaugeMetric) sample(v float64) {
61	atomic.StoreUint64(&g.value, math.Float64bits(v))
62}
63
64func (g *gaugeMetric) flushUnsafe() metric {
65	return metric{
66		metricType: gauge,
67		name:       g.name,
68		tags:       g.tags,
69		rate:       1,
70		fvalue:     math.Float64frombits(g.value),
71	}
72}
73
74// Set
75
76type setMetric struct {
77	data map[string]struct{}
78	name string
79	tags []string
80	sync.Mutex
81}
82
83func newSetMetric(name string, value string, tags []string) *setMetric {
84	set := &setMetric{
85		data: map[string]struct{}{},
86		name: name,
87		tags: tags,
88	}
89	set.data[value] = struct{}{}
90	return set
91}
92
93func (s *setMetric) sample(v string) {
94	s.Lock()
95	defer s.Unlock()
96	s.data[v] = struct{}{}
97}
98
99// Sets are aggregated on the agent side too. We flush the keys so a set from
100// multiple application can be correctly aggregated on the agent side.
101func (s *setMetric) flushUnsafe() []metric {
102	if len(s.data) == 0 {
103		return nil
104	}
105
106	metrics := make([]metric, len(s.data))
107	i := 0
108	for value := range s.data {
109		metrics[i] = metric{
110			metricType: set,
111			name:       s.name,
112			tags:       s.tags,
113			rate:       1,
114			svalue:     value,
115		}
116		i++
117	}
118	return metrics
119}
120
121// Histograms, Distributions and Timings
122
123type bufferedMetric struct {
124	sync.Mutex
125
126	data []float64
127	name string
128	// Histograms and Distributions store tags as one string since we need
129	// to compute its size multiple time when serializing.
130	tags  string
131	mtype metricType
132}
133
134func (s *bufferedMetric) sample(v float64) {
135	s.Lock()
136	defer s.Unlock()
137	s.data = append(s.data, v)
138}
139
140func (s *bufferedMetric) flushUnsafe() metric {
141	return metric{
142		metricType: s.mtype,
143		name:       s.name,
144		stags:      s.tags,
145		rate:       1,
146		fvalues:    s.data,
147	}
148}
149
150type histogramMetric = bufferedMetric
151
152func newHistogramMetric(name string, value float64, stringTags string) *histogramMetric {
153	return &histogramMetric{
154		data:  []float64{value},
155		name:  name,
156		tags:  stringTags,
157		mtype: histogramAggregated,
158	}
159}
160
161type distributionMetric = bufferedMetric
162
163func newDistributionMetric(name string, value float64, stringTags string) *distributionMetric {
164	return &distributionMetric{
165		data:  []float64{value},
166		name:  name,
167		tags:  stringTags,
168		mtype: distributionAggregated,
169	}
170}
171
172type timingMetric = bufferedMetric
173
174func newTimingMetric(name string, value float64, stringTags string) *timingMetric {
175	return &timingMetric{
176		data:  []float64{value},
177		name:  name,
178		tags:  stringTags,
179		mtype: timingAggregated,
180	}
181}
182