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