statsd operational data collection; no charts yet
Costa Tsaousis (ktsaou) committed
Apr 23, 2017 at 19:00 UTC
98fb127ba66d5396b4b3db281dbd1cb24afe8734
1 file changed
+199
-23
src/statsd.c
+199
-23
@@ -1,6 +1,6 @@
1
#include "common.h"
2
3
-static int statsd_threads = 0;
3
+#define STATSD_MAX_METRIC_LENGTH 200
4
5
// --------------------------------------------------------------------------------------
6
@@ -49,53 +49,225 @@ typedef struct statsd_metric {
49
RRDDIM *rd_max;
50
RRDDIM *rd_avg;
51
52
- netdata_mutex_t mutex;
53
-
52
struct statsd_metric *next;
53
} STATSD_METRIC;
54
57
-static inline void statsd_collected_value(STATSD_METRIC *mt, calculated_number value, const char *options) {
58
- (void)options;
55
60
- int lock = 0;
61
- if(unlikely(statsd_threads > 1)) {
62
- netdata_mutex_lock(&mt->mutex);
63
- lock = 1;
64
- }
56
+// --------------------------------------------------------------------------------------------------------------------
57
+// statsd index
58
+
59
+int statsd_compare(void* a, void* b) {
60
+ if(((STATSD_METRIC *)a)->hash < ((STATSD_METRIC *)b)->hash) return -1;
61
+ else if(((STATSD_METRIC *)a)->hash > ((STATSD_METRIC *)b)->hash) return 1;
62
+ else return strcmp(((STATSD_METRIC *)a)->key, ((STATSD_METRIC *)b)->key);
63
+}
64
66
- mt->last_collected_ut = now_realtime_usec();
67
- mt->events++;
68
- mt->count++;
65
+static avl_tree statsd_index = {
66
+ .compar = statsd_compare,
67
+ .root = NULL
68
+};
69
+
70
+static inline STATSD_METRIC *stasd_metric_index_find(const char *key, uint32_t hash) {
71
+ STATSD_METRIC tmp;
72
+ tmp.key = key;
73
+ tmp.hash = (hash)?hash:simple_hash(tmp.key);
74
+
75
+ return (STATSD_METRIC *)avl_search(&statsd_index, (avl *)&tmp);
76
+}
77
70
- switch(mt->type) {
78
+
79
+// --------------------------------------------------------------------------------------------------------------------
80
+// statsd data collection
81
+
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);
84
+
85
+ (void)options;
86
+
87
+ m->last_collected_ut = now_realtime_usec();
88
+ m->events++;
89
+ m->count++;
90
+
91
+ switch(m->type) {
92
case STATSD_METRIC_TYPE_HISTOGRAM:
93
// FIXME: not implemented yet
94
95
case STATSD_METRIC_TYPE_GAUGE:
96
case STATSD_METRIC_TYPE_TIMER:
97
case STATSD_METRIC_TYPE_METER:
77
- mt->last = value;
78
- mt->total += value;
79
- if(value < mt->min)
80
- mt->min = value;
81
- if(value > mt->max)
82
- mt->max = value;
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;
104
break;
105
106
case STATSD_METRIC_TYPE_COUNTER:
86
- mt->total = mt->last = value;
107
+ m->total = m->last = value;
108
break;
109
}
110
90
- if(unlikely(lock))
91
- netdata_mutex_unlock(&mt->mutex);
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
113
+ , m->name
114
+ , (char)m->type
115
+ , m->events
116
+ , m->count
117
+ , m->last_collected_ut
118
+ , m->last
119
+ , m->total
120
+ , m->min
121
+ , m->max
122
+ );
123
+}
124
+
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);
132
+
133
+ uint32_t hash = simple_hash(key);
134
+
135
+ STATSD_METRIC *m = stasd_metric_index_find(key, hash);
136
+ if(unlikely(!m)) {
137
+ debug(D_STATSD, "Creating new metric '%s'", key);
138
+
139
+ m = (STATSD_METRIC *)callocz(sizeof(STATSD_METRIC), 1);
140
+ m->key = strdupz(key);
141
+ m->hash = hash;
142
+ m->name = strdupz(metric);
143
+ m->type = type;
144
+ m = (STATSD_METRIC *)avl_insert(&statsd_index, (avl *)m);
145
+ }
146
+
147
+ statsd_collected_value(m, value, options);
148
+}
149
+
150
+
151
+// --------------------------------------------------------------------------------------------------------------------
152
+// statsd parsing
153
+
154
+static inline char *skip_to_next_separator(char *s) {
155
+ while(*s && *s != ':' && *s != '|' && *s != ' ' && *s != '\t' && *s != '\r' && *s != '\n')
156
+ s++;
157
+
158
+ return s;
159
+}
160
+
161
+static inline char *skip_all_spaces(char *s) {
162
+ while(*s == ' ' || *s == '\t')
163
+ s++;
164
+
165
+ return s;
166
+}
167
+
168
+static inline char *skip_all_spaces_and_newlines(char *s) {
169
+ while(*s == ' ' || *s == '\t' || *s == '\r' || *s == '\n')
170
+ s++;
171
+
172
+ return s;
173
}
174
175
static void statsd_process(char *buffer, size_t size) {
176
+ STATSD_METRIC_TYPE type;
177
+
178
buffer[size] = '\0';
179
debug(D_STATSD, "RECEIVED: '%s'", buffer);
180
+
181
+ char *s = buffer, *m, *v, *t;
182
+ while(*s) {
183
+ // skip all spaces and newlines
184
+ s = skip_all_spaces_and_newlines(s);
185
+
186
+ if(unlikely(!*s)) {
187
+ // we have nothing
188
+ continue;
189
+ }
190
+
191
+ // the beginning of the metric name
192
+ m = s;
193
+
194
+ // find the next separator
195
+ s = skip_to_next_separator(s);
196
+
197
+ // terminate the metric name
198
+ if(*s != '\0')
199
+ *s++ = '\0';
200
+
201
+ // skip all spaces
202
+ s = skip_all_spaces(s);
203
+
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
+ }
209
+
210
+ // the beginning of the metric value
211
+ v = s;
212
+
213
+ // find the next separator
214
+ s = skip_to_next_separator(s);
215
+
216
+ // terminate the value
217
+ if(*s != '\0')
218
+ *s++ = '\0';
219
+
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
+ }
228
+
229
+ // the beginning of type
230
+ t = s;
231
+
232
+ // find the next separator
233
+ s = skip_to_next_separator(s);
234
+
235
+ // terminate the type
236
+ if(*s != '\0')
237
+ *s++ = '\0';
238
+
239
+ // skip all spaces
240
+ s = skip_all_spaces(s);
241
+
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;
248
+
249
+ case 'c':
250
+ type = STATSD_METRIC_TYPE_COUNTER;
251
+ break;
252
+
253
+ case 'm':
254
+ if(t[1] == 's') type = STATSD_METRIC_TYPE_TIMER;
255
+ else type = STATSD_METRIC_TYPE_METER;
256
+ break;
257
+
258
+ case 'h':
259
+ type = STATSD_METRIC_TYPE_HISTOGRAM;
260
+ break;
261
+ }
262
+
263
+ statsd_process_metric(m, type, strtold(v, NULL), 0);
264
+ }
265
}
266
267
+
268
+// --------------------------------------------------------------------------------------------------------------------
269
+// statsd pollfd interface
270
+
271
static char statsd_read_buffer[65536];
272
273
// new TCP client connected
@@ -171,6 +343,10 @@ static int statsd_rcv_callback(int fd, int socktype, void *data, short int *even
343
return 0;
344
}
345
346
+
347
+// --------------------------------------------------------------------------------------------------------------------
348
+// statsd main thread
349
+
350
void *statsd_main(void *ptr) {
351
struct netdata_static_thread *static_thread = (struct netdata_static_thread *)ptr;
352