@cryptotaxi247 / netdata-1 / commits / 3bb33eb2e

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;