more accurate metrics parsing and support for metrics split into multiple TCP packets
Costa Tsaousis (ktsaou) committed
Apr 28, 2017 at 01:06 UTC
3bb33eb2e0f568f9c787d954c3efb79fb17f678f
1 file changed
+112
-112
src/statsd.c
+112
-112
@@ -238,17 +238,17 @@ static inline STATSD_METRIC *stasd_metric_index_find(STATSD_INDEX *index, const
238
return (STATSD_METRIC *)STATSD_AVL_SEARCH(&index->index, (avl *)&tmp);
239
}
240
241
-static inline STATSD_METRIC *statsd_find_or_add_metric(STATSD_INDEX *index, char *metric) {
242
- debug(D_STATSD, "finding or adding metric '%s' under '%s'", metric, index->name);
241
+static inline STATSD_METRIC *statsd_find_or_add_metric(STATSD_INDEX *index, const char *name) {
242
+ debug(D_STATSD, "finding or adding metric '%s' under '%s'", name, index->name);
243
244
- uint32_t hash = simple_hash(metric);
244
+ uint32_t hash = simple_hash(name);
245
246
- STATSD_METRIC *m = stasd_metric_index_find(index, metric, hash);
246
+ STATSD_METRIC *m = stasd_metric_index_find(index, name, hash);
247
if(unlikely(!m)) {
248
- debug(D_STATSD, "Creating new %s metric '%s'", index->name, metric);
248
+ debug(D_STATSD, "Creating new %s metric '%s'", index->name, name);
249
250
m = (STATSD_METRIC *)callocz(sizeof(STATSD_METRIC), 1);
251
- m->name = strdupz(metric);
251
+ m->name = strdupz(name);
252
m->hash = hash;
253
m->options = index->default_options;
254
@@ -315,8 +315,8 @@ static inline void statsd_reset_metric(STATSD_METRIC *m) {
315
m->count = 0;
316
}
317
318
-static inline void statsd_process_gauge(STATSD_METRIC *m, char *v, char *r) {
319
- if(unlikely(!v || !*v)) {
318
+static inline void statsd_process_gauge(STATSD_METRIC *m, const char *value, const char *sampling) {
319
+ if(unlikely(!value || !*value)) {
320
error("STATSD: metric '%s' of type gauge, with empty value is ignored.", m->name);
321
return;
322
}
@@ -326,33 +326,33 @@ static inline void statsd_process_gauge(STATSD_METRIC *m, char *v, char *r) {
326
statsd_reset_metric(m);
327
}
328
329
- if(*v == '+' || *v == '-')
330
- m->gauge.value += statsd_parse_float(v, 1.0) / statsd_parse_float(r, 1.0);
329
+ if(*value == '+' || *value == '-')
330
+ m->gauge.value += statsd_parse_float(value, 1.0) / statsd_parse_float(sampling, 1.0);
331
else
332
- m->gauge.value = statsd_parse_float(v, 1.0) / statsd_parse_float(r, 1.0);
332
+ m->gauge.value = statsd_parse_float(value, 1.0) / statsd_parse_float(sampling, 1.0);
333
334
m->events++;
335
m->count++;
336
}
337
338
-static inline void statsd_process_counter(STATSD_METRIC *m, char *v, char *r) {
338
+static inline void statsd_process_counter(STATSD_METRIC *m, const char *value, const char *sampling) {
339
// we accept empty values for counters
340
341
if(unlikely(m->reset)) statsd_reset_metric(m);
342
343
- m->counter.value += roundl((long double)statsd_parse_int(v, 1) / statsd_parse_float(r, 1.0));
343
+ m->counter.value += roundl((long double)statsd_parse_int(value, 1) / statsd_parse_float(sampling, 1.0));
344
345
m->events++;
346
m->count++;
347
}
348
349
-static inline void statsd_process_meter(STATSD_METRIC *m, char *v, char *r) {
349
+static inline void statsd_process_meter(STATSD_METRIC *m, const char *value, const char *sampling) {
350
// this is the same with the counter
351
- statsd_process_counter(m, v, r);
351
+ statsd_process_counter(m, value, sampling);
352
}
353
354
-static inline void statsd_process_histogram(STATSD_METRIC *m, char *v, char *r) {
355
- if(unlikely(!v || !*v)) {
354
+static inline void statsd_process_histogram(STATSD_METRIC *m, const char *value, const char *sampling) {
355
+ if(unlikely(!value || !*value)) {
356
error("STATSD: metric '%s' of type histogram, with empty value is ignored.", m->name);
357
return;
358
}
@@ -369,26 +369,24 @@ static inline void statsd_process_histogram(STATSD_METRIC *m, char *v, char *r)
369
netdata_mutex_unlock(&m->histogram.mutex);
370
}
371
372
- m->histogram.ext->values[m->histogram.ext->used++] = statsd_parse_float(v, 1.0) / statsd_parse_float(r, 1.0);
372
+ m->histogram.ext->values[m->histogram.ext->used++] = statsd_parse_float(value, 1.0) / statsd_parse_float(sampling, 1.0);
373
374
m->events++;
375
m->count++;
376
}
377
378
-static inline void statsd_process_timer(STATSD_METRIC *m, char *v, char *r) {
379
- if(unlikely(!v || !*v)) {
378
+static inline void statsd_process_timer(STATSD_METRIC *m, const char *value, const char *sampling) {
379
+ if(unlikely(!value || !*value)) {
380
error("STATSD: metric of type set, with empty value is ignored.");
381
return;
382
}
383
384
// timers are a use case of histogram
385
- statsd_process_histogram(m, v, r);
385
+ statsd_process_histogram(m, value, sampling);
386
}
387
388
-static inline void statsd_process_set(STATSD_METRIC *m, char *v, char *r) {
389
- (void)r;
390
-
391
- if(unlikely(!v || !*v)) {
388
+static inline void statsd_process_set(STATSD_METRIC *m, const char *value) {
389
+ if(unlikely(!value || !*value)) {
390
error("STATSD: metric of type set, with empty value is ignored.");
391
return;
392
}
@@ -406,9 +404,9 @@ static inline void statsd_process_set(STATSD_METRIC *m, char *v, char *r) {
404
m->set.unique = 0;
405
}
406
409
- void *t = dictionary_get(m->set.dict, v);
407
+ void *t = dictionary_get(m->set.dict, value);
408
if(unlikely(!t)) {
411
- dictionary_set(m->set.dict, v, v, 1);
409
+ dictionary_set(m->set.dict, value, NULL, 1);
410
m->set.unique++;
411
}
412
@@ -420,158 +418,160 @@ static inline void statsd_process_set(STATSD_METRIC *m, char *v, char *r) {
418
// --------------------------------------------------------------------------------------------------------------------
419
// statsd parsing
420
423
-static void statsd_process_metric(char *m, char *v, char *t, char *r) {
424
- debug(D_STATSD, "STATSD: raw metric '%s', value '%s', type '%s', rate '%s'", m, v, t, r);
421
+static void statsd_process_metric(const char *name, const char *value, const char *type, const char *sampling) {
422
+ debug(D_STATSD, "STATSD: raw metric '%s', value '%s', type '%s', rate '%s'", name, value, type, sampling);
423
426
- if(unlikely(!m || !*m)) return;
427
- if(unlikely(!t || !*t)) t = "m";
424
+ if(unlikely(!name || !*name)) return;
425
+ if(unlikely(!type || !*type)) type = "m";
426
429
- switch (*t) {
427
+ switch (*type) {
428
case 'g':
429
statsd_process_gauge(
432
- statsd_find_or_add_metric(&statsd.gauges, m),
433
- v, r);
430
+ statsd_find_or_add_metric(&statsd.gauges, name),
431
+ value, sampling);
432
break;
433
434
case 'c':
435
statsd_process_counter(
438
- statsd_find_or_add_metric(&statsd.counters, m),
439
- v, r);
436
+ statsd_find_or_add_metric(&statsd.counters, name),
437
+ value, sampling);
438
break;
439
440
case 'm':
443
- if (t[1] == 's')
441
+ if (type[1] == 's')
442
statsd_process_timer(
445
- statsd_find_or_add_metric(&statsd.timers, m),
446
- v, r);
443
+ statsd_find_or_add_metric(&statsd.timers, name),
444
+ value, sampling);
445
else
446
statsd_process_meter(
449
- statsd_find_or_add_metric(&statsd.meters, m),
450
- v, r);
447
+ statsd_find_or_add_metric(&statsd.meters, name),
448
+ value, sampling);
449
break;
450
451
case 'h':
452
statsd_process_histogram(
455
- statsd_find_or_add_metric(&statsd.histograms, m),
456
- v, r);
453
+ statsd_find_or_add_metric(&statsd.histograms, name),
454
+ value, sampling);
455
break;
456
457
case 's':
458
statsd_process_set(
461
- statsd_find_or_add_metric(&statsd.sets, m),
462
- v, r);
459
+ statsd_find_or_add_metric(&statsd.sets, name),
460
+ value);
461
break;
462
463
default:
466
- error("STATSD: metric '%s' with value '%s' specifies an unknown type '%s'.", m, v?v:"<unset>", t);
464
+ error("STATSD: metric '%s' with value '%s' specifies an unknown type '%s'.", name, value?value:"<unset>", type);
465
break;
466
}
467
}
468
471
-static inline int is_statsd_space(char c) {
472
- if(c == ' ' || c == '\t')
473
- return 1;
469
+static inline const char *statsd_parse_name(const char *s, const char **name) {
470
+ char c;
471
475
- return 0;
472
+ *name = s;
473
+ for(c = *s; c && c != ':' && c != '|' && c != '\n'; c = *++s) ;
474
+
475
+ return s;
476
}
477
478
-static inline int is_statsd_newline(char c) {
479
- if(c == '\r' || c == '\n')
480
- return 1;
478
+static inline const char *statsd_parse_value(const char *s, const char **value) {
479
+ char c;
480
482
- return 0;
481
+ *value = s;
482
+ for(c = *s; c && c != '|' && c != '\n'; c = *++s) ;
483
+
484
+ return s;
485
}
486
485
-static inline int is_statsd_separator(char c) {
486
- if(c == ':' || c == '|' || c == '@')
487
- return 1;
487
+static inline const char *statsd_parse_type(const char *s, const char **type) {
488
+ char c;
489
489
- return 0;
490
+ *type = s;
491
+ for(c = *s; c && c != '|' && c != '@' && c != '\n'; c = *++s) ;
492
+
493
+ return s;
494
}
495
492
-static inline char *skip_to_next_separator(char *s) {
493
- while(*s && !is_statsd_separator(*s) && !is_statsd_newline(*s))
494
- s++;
496
+static inline const char *statsd_parse_sampling(const char *s, const char **sampling) {
497
+ char c;
498
+
499
+ *sampling = s;
500
+ for(c = *s; c && c != '\n'; c = *++s) ;
501
502
return s;
503
}
504
499
-static inline char *statsd_get_field(char *s, char **field, int *line_break) {
500
- *line_break = 0;
505
+const char *statsd_parse_skip_spaces(const char *s) {
506
+ char c;
507
502
- while(is_statsd_separator(*s) || is_statsd_space(*s))
503
- s++;
508
+ for(c = *s; c && ( c == ' ' || c == '\t' || c == '\r' || c == '\n' ); c = *++s) ;
509
505
- if(unlikely(is_statsd_newline(*s))) {
506
- *field = NULL;
507
- *line_break = 1;
510
+ return s;
511
+}
512
509
- while(is_statsd_newline(*s))
510
- *s++ = '\0';
513
+static inline const char *statsd_parse_field_trim(const char *start, char *end) {
514
+ *end = '\0';
515
512
- return s;
516
+ if(unlikely(!start)) {
517
+ start = end;
518
+ return start;
519
}
520
515
- *field = s;
516
- s = skip_to_next_separator(s);
517
-
518
- if(unlikely(is_statsd_newline(*s))) {
519
- *line_break = 1;
521
+ while(start <= end && (*start == ' ' || *start == '\t'))
522
+ start++;
523
521
- while(is_statsd_newline(*s))
522
- *s++ = '\0';
523
- }
524
- else if(likely(*s))
525
- *s++ = '\0';
524
+ end--;
525
+ while(end >= start && (*end == ' ' || *end == '\t'))
526
+ *end-- = '\0';
527
527
- return s;
528
+ return start;
529
}
530
531
static inline size_t statsd_process(char *buffer, size_t size, int require_newlines) {
531
- (void)require_newlines;
532
- // FIXME: respect require_newlines to support metrics split in multiple TCP packets
533
-
532
buffer[size] = '\0';
535
- debug(D_STATSD, "RECEIVED: '%s'", buffer);
533
+ debug(D_STATSD, "RECEIVED: %zu bytes: '%s'", size, buffer);
534
537
- char *s = buffer, *m, *v, *t, *r;
535
+ const char *s = buffer;
536
while(*s) {
539
- m = v = t = r = NULL;
540
- int line_break = 0;
537
+ const char *name = NULL, *value = NULL, *type = NULL, *sampling = NULL;
538
+ char *name_end, *value_end, *type_end, *sampling_end;
539
542
- s = statsd_get_field(s, &m, &line_break);
543
- if(unlikely(line_break)) {
544
- statsd_process_metric(m, v, t, r);
540
+ s = statsd_parse_name(s, &name);
541
+ if(name == s || !*name) {
542
+ s = statsd_parse_skip_spaces(s);
543
continue;
544
}
545
+ name_end = (char *)s;
546
548
- s = statsd_get_field(s, &v, &line_break);
549
- if(unlikely(line_break)) {
550
- statsd_process_metric(m, v, t, r);
551
- continue;
552
- }
547
+ if(likely(*s == ':')) s = statsd_parse_value(++s, &value);
548
+ value_end = (char *)s;
549
554
- s = statsd_get_field(s, &t, &line_break);
555
- if(likely(line_break)) {
556
- statsd_process_metric(m, v, t, r);
557
- continue;
558
- }
550
+ if(likely(*s == '|')) s = statsd_parse_type(++s, &type);
551
+ type_end = (char *)s;
552
560
- s = statsd_get_field(s, &r, &line_break);
561
- if(likely(line_break)) {
562
- statsd_process_metric(m, v, t, r);
563
- continue;
553
+ if(unlikely(*s == '|' || *s == '@')) {
554
+ s = statsd_parse_sampling(++s, &sampling);
555
+ if(*sampling == '@') sampling++;
556
}
557
+ sampling_end = (char *)s;
558
566
- if(*s && *s != '\r' && *s != '\n') {
567
- error("STATSD: excess data '%s' at end of line, metric '%s', value '%s', sampling rate '%s'", s, m, v, r);
559
+ // skip everything until the end of the line
560
+ while(*s && *s != '\n') s++;
561
569
- // delete the excess data at the end if line
570
- while(*s && *s != '\r' && *s != '\n')
571
- s++;
562
+ if(unlikely(require_newlines && *s != '\n' && s > buffer)) {
563
+ // move the remaining data to the beginning
564
+ size -= (name - buffer);
565
+ memmove(buffer, name, size);
566
+ return size;
567
}
568
574
- statsd_process_metric(m, v, t, r);
569
+ statsd_process_metric(
570
+ statsd_parse_field_trim(name, name_end)
571
+ , statsd_parse_field_trim(value, value_end)
572
+ , statsd_parse_field_trim(type, type_end)
573
+ , statsd_parse_field_trim(sampling, sampling_end)
574
+ );
575
}
576
577
return 0;