@cryptotaxi247 / netdata-1 / commits / 3bb48381c

statsd now supports sampling rate; various etsy/stasd compatibility improvements

Costa Tsaousis (ktsaou) committed Apr 23, 2017 at 21:43 UTC 3bb48381c3e7bac64f1a8c7e324cdf270cab5031
1 file changed +170 -116
src/statsd.c
+170 -116
@@ -28,19 +28,18 @@ typedef enum statsd_metric_type {
28 typedef struct statsd_metric {
29 avl avl; // indexing
30
31 - const char *key; // "type|name" for indexing
32 - uint32_t hash; // hash of the key
33 -
31 const char *name;
32 + uint32_t hash; // hash of the name
33 +
34 STATSD_METRIC_TYPE type;
35
36 usec_t last_collected_ut; // the last time this metric was updated
37 usec_t last_exposed_ut; // the last time this metric was sent to netdata
38 size_t events; // the number of times this metrics has been collected
39
41 - size_t count; // number of events since the last exposure to netdata
40 + calculated_number count; // number of events since the last exposure to netdata
41 calculated_number last; // the last value collected
43 - calculated_number total; // the sum of all values collected since the last exposure to netdata
42 + calculated_number value; // the value of the metric
43 calculated_number min; // the min value collected since the last exposure to netdata
44 calculated_number max; // the max value collected since the last exposure to netdata
45
@@ -59,7 +58,7 @@ typedef struct statsd_metric {
58 int statsd_compare(void* a, void* b) {
59 if(((STATSD_METRIC *)a)->hash < ((STATSD_METRIC *)b)->hash) return -1;
60 else if(((STATSD_METRIC *)a)->hash > ((STATSD_METRIC *)b)->hash) return 1;
62 - else return strcmp(((STATSD_METRIC *)a)->key, ((STATSD_METRIC *)b)->key);
61 + else return strcmp(((STATSD_METRIC *)a)->name, ((STATSD_METRIC *)b)->name);
62 }
63
64 static avl_tree statsd_index = {
@@ -67,10 +66,10 @@ static avl_tree statsd_index = {
66 .root = NULL
67 };
68
70 -static inline STATSD_METRIC *stasd_metric_index_find(const char *key, uint32_t hash) {
69 +static inline STATSD_METRIC *stasd_metric_index_find(const char *name, uint32_t hash) {
70 STATSD_METRIC tmp;
72 - tmp.key = key;
73 - tmp.hash = (hash)?hash:simple_hash(tmp.key);
71 + tmp.name = name;
72 + tmp.hash = (hash)?hash:simple_hash(tmp.name);
73
74 return (STATSD_METRIC *)avl_search(&statsd_index, (avl *)&tmp);
75 }
@@ -79,188 +78,243 @@ static inline STATSD_METRIC *stasd_metric_index_find(const char *key, uint32_t h
78 // --------------------------------------------------------------------------------------------------------------------
79 // statsd data collection
80
82 -static inline void statsd_collected_value(STATSD_METRIC *m, calculated_number value, const char *options) {
83 - debug(D_STATSD, "Updating metric '%s'", m->key);
81 +#define STATSD_GAUGE_COLLECTION_RELATIVE 0x00000001
82
85 - (void)options;
83 +static inline void statsd_collected_value(STATSD_METRIC *m, calculated_number value, calculated_number sample_rate, uint32_t options) {
84 + debug(D_STATSD, "Updating metric '%s'", m->name);
85
86 m->last_collected_ut = now_realtime_usec();
87 m->events++;
89 - m->count++;
88 + m->count += sample_rate;
89 +
90 + m->last = value;
91 + if(value < m->min)
92 + m->min = value;
93 + if(value > m->max)
94 + m->max = value;
95
96 switch(m->type) {
97 case STATSD_METRIC_TYPE_HISTOGRAM:
98 // FIXME: not implemented yet
99 + m->value += value;
100 + break;
101
95 - case STATSD_METRIC_TYPE_GAUGE:
96 - case STATSD_METRIC_TYPE_TIMER:
102 case STATSD_METRIC_TYPE_METER:
98 - m->last = value;
99 - m->total += value;
100 - if(value < m->min)
101 - m->min = value;
102 - if(value > m->max)
103 - m->max = value;
103 + // we add to this metric
104 + m->value += value;
105 + break;
106 +
107 + case STATSD_METRIC_TYPE_TIMER:
108 + // we add time to this metric
109 + m->value += value;
110 + break;
111 +
112 + case STATSD_METRIC_TYPE_GAUGE:
113 + if(unlikely(options & STATSD_GAUGE_COLLECTION_RELATIVE))
114 + // we add the collected value
115 + m->value += value;
116 + else
117 + // we replace the value of the metric
118 + m->value = value;
119 break;
120
121 case STATSD_METRIC_TYPE_COUNTER:
107 - m->total = m->last = value;
122 + // we add the collected value
123 + m->value += value;
124 break;
125 }
126
111 - debug(D_STATSD, "Updated metric '%s', name '%s', type '%c', events %zu, count %zu, last_collected %llu, last value %0.5Lf, total %0.5Lf, min %0.5Lf, max %0.5Lf"
112 - , m->key
127 + debug(D_STATSD, "Updated metric '%s', type '%c', events %zu, count %0.5Lf, last_collected %llu, last value %0.5Lf, value %0.5Lf, min %0.5Lf, max %0.5Lf"
128 , m->name
129 , (char)m->type
130 , m->events
131 , m->count
132 , m->last_collected_ut
133 , m->last
119 - , m->total
134 + , m->value
135 , m->min
136 , m->max
137 );
138 }
139
125 -static inline void statsd_process_metric(const char *metric, STATSD_METRIC_TYPE type, calculated_number value, const char *options) {
126 - debug(D_STATSD, "processing metric '%s', type '%c', value %0.5Lf, options '%s'", metric, (char)type, value, options?options:"");
127 -
128 - char key[STATSD_MAX_METRIC_LENGTH + 1];
129 - key[0] = (char)type;
130 - key[1] = '|';
131 - strncpyz(&key[2], metric, sizeof(key) - 3);
140 +static inline void statsd_process_metric(const char *metric, STATSD_METRIC_TYPE type, calculated_number value, calculated_number sample_rate, uint32_t options) {
141 + debug(D_STATSD, "processing metric '%s', type '%c', value %0.5Lf, sample_rate %0.5Lf, options '0x%08x'", metric, (char)type, value, sample_rate, options);
142
133 - uint32_t hash = simple_hash(key);
143 + uint32_t hash = simple_hash(metric);
144
135 - STATSD_METRIC *m = stasd_metric_index_find(key, hash);
145 + STATSD_METRIC *m = stasd_metric_index_find(metric, hash);
146 if(unlikely(!m)) {
137 - debug(D_STATSD, "Creating new metric '%s'", key);
147 + debug(D_STATSD, "Creating new metric '%s'", metric);
148
149 m = (STATSD_METRIC *)callocz(sizeof(STATSD_METRIC), 1);
140 - m->key = strdupz(key);
141 - m->hash = hash;
150 m->name = strdupz(metric);
151 + m->hash = hash;
152 m->type = type;
153 m = (STATSD_METRIC *)avl_insert(&statsd_index, (avl *)m);
154 }
155
147 - statsd_collected_value(m, value, options);
156 + statsd_collected_value(m, value, sample_rate, options);
157 }
158
159
160 // --------------------------------------------------------------------------------------------------------------------
161 // statsd parsing
162
154 -static inline char *skip_to_next_separator(char *s) {
155 - while(*s && *s != ':' && *s != '|' && *s != ' ' && *s != '\t' && *s != '\r' && *s != '\n')
156 - s++;
163 +static void statsd_process_metric_raw(char *m, char *v, char *t, char *r) {
164 + debug(D_STATSD, "STATSD: raw metric '%s', value '%s', type '%s', rate '%s'", m, v, t, r);
165
158 - return s;
166 + if(unlikely(!m || !*m)) return;
167 +
168 + STATSD_METRIC_TYPE type = STATSD_METRIC_TYPE_METER;
169 + calculated_number value = 1.0;
170 + calculated_number sample_rate = 1.0;
171 + uint32_t options = 0;
172 + char *e;
173 +
174 + // collect the value
175 + if(likely(v && *v)) {
176 + e = NULL;
177 + value = strtold(v, &e);
178 + if(e && *e)
179 + error("STATSD: excess data '%s' after value, metric '%s', value '%s'", e, m, v);
180 + }
181 +
182 + if(likely(r && *r)) {
183 + e = NULL;
184 + sample_rate = strtold(r, &e);
185 + if(e && *e)
186 + error("STATSD: excess data '%s' after sampling rate, metric '%s', value '%s', sampling rate '%s'", e, m, v, r);
187 + }
188 +
189 + // we have the metric name, value and type
190 + if(likely(t && *t)) {
191 + switch (*t) {
192 + case 'g':
193 + type = STATSD_METRIC_TYPE_GAUGE;
194 + if (unlikely(*v == '-' || *v == '+'))
195 + options |= STATSD_GAUGE_COLLECTION_RELATIVE;
196 + break;
197 +
198 + default:
199 + case 'c':
200 + type = STATSD_METRIC_TYPE_COUNTER;
201 + break;
202 +
203 + case 'm':
204 + if (t[1] == 's') type = STATSD_METRIC_TYPE_TIMER;
205 + else type = STATSD_METRIC_TYPE_METER;
206 + break;
207 +
208 + case 'h':
209 + type = STATSD_METRIC_TYPE_HISTOGRAM;
210 + break;
211 + }
212 + }
213 +
214 + statsd_process_metric(m, type, value, sample_rate, options);
215 }
216
161 -static inline char *skip_all_spaces(char *s) {
162 - while(*s == ' ' || *s == '\t')
163 - s++;
217 +static inline int is_statsd_space(char c) {
218 + if(c == ' ' || c == '\t')
219 + return 1;
220
165 - return s;
221 + return 0;
222 }
223
168 -static inline char *skip_all_spaces_and_newlines(char *s) {
169 - while(*s == ' ' || *s == '\t' || *s == '\r' || *s == '\n')
170 - s++;
224 +static inline int is_statsd_newline(char c) {
225 + if(c == '\r' || c == '\n')
226 + return 1;
227
172 - return s;
228 + return 0;
229 }
230
175 -static void statsd_process(char *buffer, size_t size) {
176 - STATSD_METRIC_TYPE type;
231 +static inline int is_statsd_separator(char c) {
232 + if(c == ':' || c == '|' || c == '@')
233 + return 1;
234
178 - buffer[size] = '\0';
179 - debug(D_STATSD, "RECEIVED: '%s'", buffer);
235 + return 0;
236 +}
237
181 - char *s = buffer, *m, *v, *t;
182 - while(*s) {
183 - // skip all spaces and newlines
184 - s = skip_all_spaces_and_newlines(s);
238 +static inline char *skip_to_next_separator(char *s) {
239 + while(*s && !is_statsd_separator(*s) && !is_statsd_newline(*s))
240 + s++;
241
186 - if(unlikely(!*s)) {
187 - // we have nothing
188 - continue;
189 - }
242 + return s;
243 +}
244
191 - // the beginning of the metric name
192 - m = s;
245 +static char *statsd_get_field(char *s, char **field, int *line_break) {
246 + *line_break = 0;
247
194 - // find the next separator
195 - s = skip_to_next_separator(s);
248 + while(is_statsd_separator(*s) || is_statsd_space(*s))
249 + s++;
250
197 - // terminate the metric name
198 - if(*s != '\0')
199 - *s++ = '\0';
251 + if(unlikely(is_statsd_newline(*s))) {
252 + *field = NULL;
253 + *line_break = 1;
254
201 - // skip all spaces
202 - s = skip_all_spaces(s);
255 + while(is_statsd_newline(*s))
256 + *s++ = '\0';
257
204 - if(unlikely(!*s || *s == '\r' || *s == '\n')) {
205 - // we have only the metric name
206 - statsd_process_metric(m, STATSD_METRIC_TYPE_METER, 1.0, 0);
207 - continue;
208 - }
258 + return s;
259 + }
260
210 - // the beginning of the metric value
211 - v = s;
261 + *field = s;
262 + s = skip_to_next_separator(s);
263
213 - // find the next separator
214 - s = skip_to_next_separator(s);
264 + if(unlikely(is_statsd_newline(*s))) {
265 + *line_break = 1;
266
216 - // terminate the value
217 - if(*s != '\0')
267 + while(is_statsd_newline(*s))
268 *s++ = '\0';
269 + }
270 + else if(likely(*s))
271 + *s++ = '\0';
272
220 - // skip all spaces
221 - s = skip_all_spaces(s);
222 -
223 - if(unlikely(!*s || *s == '\r' || *s == '\n')) {
224 - // we have only the metric name and value
225 - statsd_process_metric(m, STATSD_METRIC_TYPE_GAUGE, strtold(v, NULL), 0);
226 - continue;
227 - }
273 + return s;
274 +}
275
229 - // the beginning of type
230 - t = s;
276 +static void statsd_process(char *buffer, size_t size) {
277 + buffer[size] = '\0';
278 + debug(D_STATSD, "RECEIVED: '%s'", buffer);
279
232 - // find the next separator
233 - s = skip_to_next_separator(s);
280 + char *s = buffer, *m, *v, *t, *r;
281 + while(*s) {
282 + m = v = t = r = NULL;
283 + int line_break = 0;
284
235 - // terminate the type
236 - if(*s != '\0')
237 - *s++ = '\0';
285 + s = statsd_get_field(s, &m, &line_break);
286 + if(unlikely(line_break)) {
287 + statsd_process_metric_raw(m, v, t, r);
288 + continue;
289 + }
290
239 - // skip all spaces
240 - s = skip_all_spaces(s);
291 + s = statsd_get_field(s, &v, &line_break);
292 + if(unlikely(line_break)) {
293 + statsd_process_metric_raw(m, v, t, r);
294 + continue;
295 + }
296
242 - // we have the metric name, value and type
243 - switch(*t) {
244 - default:
245 - case 'g':
246 - type = STATSD_METRIC_TYPE_GAUGE;
247 - break;
297 + s = statsd_get_field(s, &t, &line_break);
298 + if(likely(line_break)) {
299 + statsd_process_metric_raw(m, v, t, r);
300 + continue;
301 + }
302
249 - case 'c':
250 - type = STATSD_METRIC_TYPE_COUNTER;
251 - break;
303 + s = statsd_get_field(s, &r, &line_break);
304 + if(likely(line_break)) {
305 + statsd_process_metric_raw(m, v, t, r);
306 + continue;
307 + }
308
253 - case 'm':
254 - if(t[1] == 's') type = STATSD_METRIC_TYPE_TIMER;
255 - else type = STATSD_METRIC_TYPE_METER;
256 - break;
309 + if(*s && *s != '\r' && *s != '\n') {
310 + error("STATSD: excess data '%s' at end of line, metric '%s', value '%s', sampling rate '%s'", s, m, v, r);
311
258 - case 'h':
259 - type = STATSD_METRIC_TYPE_HISTOGRAM;
260 - break;
312 + // delete the excess data at the end if line
313 + while(*s && *s != '\r' && *s != '\n')
314 + s++;
315 }
316
263 - statsd_process_metric(m, type, strtold(v, NULL), 0);
317 + statsd_process_metric_raw(m, v, t, r);
318 }
319 }
320