@cryptotaxi247 / netdata-1 / commits / 2e1261d82

backend functionality can now send metrics directly from headless collectors, if the backend mode is 'as-collected'.

backend functionality can now send metrics directly from headless collectors, if the backend mode is 'as-collected'. statsd now has a protection from creating too many charts. There are 2 thresholds, a soft limit and a hard limit. Soft limit, allows charts to be created with memory mode = none which means that although netdata will not maintain a database, and no charts will be visible, netdata will be able to send the metrics to backend servers. Once hard limit is reached, statsd will disable exposure of the excess metrics completely. Dashboards now filter charts with memory mode = none. statsd now reports the number of charts it has created. statsd can now create private metric charts with different memory mode and history size (compared to the rest of netdata).

Costa Tsaousis (ktsaou) committed Apr 26, 2017 at 20:59 UTC 2e1261d828cae6d6249f294c4e1d1d516e5d634f
6 files changed +191 -86
src/backends.c
+37 -26
@@ -378,6 +378,19 @@ static inline int process_json_response(BUFFER *b) {
378 // ----------------------------------------------------------------------------
379 // the backend thread
380
381 +static inline int backends_can_send_rrdset(uint32_t options, RRDSET *st) {
382 + if(unlikely(!rrdset_is_available_for_backends(st)))
383 + debug(D_BACKEND, "BACKEND: not sending chart '%s' of host '%s', because it is not available for backends.", st->id, st->rrdhost->hostname);
384 + return 0;
385 +
386 + if(unlikely(st->rrd_memory_mode == RRD_MEMORY_MODE_NONE && !(options & BACKEND_SOURCE_DATA_AS_COLLECTED))) {
387 + debug(D_BACKEND, "BACKEND: not sending chart '%s' of host '%s' because its memory mode is '%s' and the backend requires database access.", st->id, st->rrdhost->hostname, rrd_memory_mode_name(st->rrdhost->rrd_memory_mode));
388 + return 0;
389 + }
390 +
391 + return 1;
392 +}
393 +
394 void *backends_main(void *ptr) {
395 int default_port = 0;
396 int sock = -1;
@@ -402,7 +415,7 @@ void *backends_main(void *ptr) {
415 .tv_sec = 0,
416 .tv_usec = 0
417 };
405 - uint32_t options;
418 + uint32_t options = 0x00000000;
419 int enabled = config_get_boolean(CONFIG_SECTION_BACKEND, "enabled", 0);
420 const char *source = config_get(CONFIG_SECTION_BACKEND, "data source", "average");
421 const char *type = config_get(CONFIG_SECTION_BACKEND, "type", "graphite");
@@ -421,13 +434,13 @@ void *backends_main(void *ptr) {
434 goto cleanup;
435
436 if(!strcmp(source, "as collected")) {
424 - options = BACKEND_SOURCE_DATA_AS_COLLECTED;
437 + options |= BACKEND_SOURCE_DATA_AS_COLLECTED;
438 }
439 else if(!strcmp(source, "average")) {
427 - options = BACKEND_SOURCE_DATA_AVERAGE;
440 + options |= BACKEND_SOURCE_DATA_AVERAGE;
441 }
442 else if(!strcmp(source, "sum") || !strcmp(source, "volume")) {
430 - options = BACKEND_SOURCE_DATA_SUM;
443 + options |= BACKEND_SOURCE_DATA_SUM;
444 }
445 else {
446 error("Invalid data source method '%s' for backend given. Disabling backed.", source);
@@ -450,7 +463,7 @@ void *backends_main(void *ptr) {
463 default_port = 2003;
464 backend_response_checker = process_graphite_response;
465
453 - if(options == BACKEND_SOURCE_DATA_AS_COLLECTED)
466 + if(options & BACKEND_SOURCE_DATA_AS_COLLECTED)
467 backend_request_formatter = format_dimension_collected_graphite_plaintext;
468 else
469 backend_request_formatter = format_dimension_stored_graphite_plaintext;
@@ -461,7 +474,7 @@ void *backends_main(void *ptr) {
474 default_port = 4242;
475 backend_response_checker = process_opentsdb_response;
476
464 - if(options == BACKEND_SOURCE_DATA_AS_COLLECTED)
477 + if(options & BACKEND_SOURCE_DATA_AS_COLLECTED)
478 backend_request_formatter = format_dimension_collected_opentsdb_telnet;
479 else
480 backend_request_formatter = format_dimension_stored_opentsdb_telnet;
@@ -472,7 +485,7 @@ void *backends_main(void *ptr) {
485 default_port = 5448;
486 backend_response_checker = process_json_response;
487
475 - if (options == BACKEND_SOURCE_DATA_AS_COLLECTED)
488 + if (options & BACKEND_SOURCE_DATA_AS_COLLECTED)
489 backend_request_formatter = format_dimension_collected_json_plaintext;
490 else
491 backend_request_formatter = format_dimension_stored_json_plaintext;
@@ -577,11 +590,6 @@ void *backends_main(void *ptr) {
590 rrd_rdlock();
591 RRDHOST *host;
592 rrdhost_foreach_read(host) {
580 - if(host->rrd_memory_mode == RRD_MEMORY_MODE_NONE) {
581 - debug(D_BACKEND, "BACKEND: not sending host '%s' because its memory mode is '%s'", host->hostname, rrd_memory_mode_name(host->rrd_memory_mode));
582 - continue;
583 - }
584 -
593 rrdhost_rdlock(host);
594
595 count_hosts++;
@@ -593,22 +601,25 @@ void *backends_main(void *ptr) {
601
602 RRDSET *st;
603 rrdset_foreach_read(st, host) {
596 - rrdset_rdlock(st);
597 -
598 - count_charts++;
599 -
600 - RRDDIM *rd;
601 - rrddim_foreach_read(rd, st) {
602 - if(likely(rd->last_collected_time.tv_sec >= after)) {
603 - chart_buffered_metrics += backend_request_formatter(b, prefix, host, __hostname, st, rd, after, before, options);
604 - count_dims++;
605 - }
606 - else {
607 - debug(D_BACKEND, "BACKEND: not sending dimension '%s' of chart '%s' from host '%s', its last data collection is not within our timeframe", rd->id, st->id, __hostname);
608 - count_dims_skipped++;
604 + if(likely(backends_can_send_rrdset(options, st))) {
605 + rrdset_rdlock(st);
606 +
607 + count_charts++;
608 +
609 + RRDDIM *rd;
610 + rrddim_foreach_read(rd, st) {
611 + if (likely(rd->last_collected_time.tv_sec >= after)) {
612 + chart_buffered_metrics += backend_request_formatter(b, prefix, host, __hostname, st, rd, after, before, options);
613 + count_dims++;
614 + }
615 + else {
616 + debug(D_BACKEND, "BACKEND: not sending dimension '%s' of chart '%s' from host '%s', its last data collection is not within our timeframe", rd->id, st->id, __hostname);
617 + count_dims_skipped++;
618 + }
619 }
620 +
621 + rrdset_unlock(st);
622 }
611 - rrdset_unlock(st);
623 }
624
625 debug(D_BACKEND, "BACKEND: sending host '%s', metrics of %zu dimensions, of %zu charts. Skipped %zu dimensions.", __hostname, count_dims, count_charts, count_dims_skipped);
src/rrd.h
+13 -5
@@ -528,7 +528,7 @@ extern void __rrd_check_wrlock(const char *file, const char *function, const uns
528
529 extern void rrdset_set_name(RRDSET *st, const char *name);
530
531 -extern RRDSET *rrdset_create(RRDHOST *host
531 +extern RRDSET *rrdset_create_custom(RRDHOST *host
532 , const char *type
533 , const char *id
534 , const char *name
@@ -538,9 +538,15 @@ extern RRDSET *rrdset_create(RRDHOST *host
538 , const char *units
539 , long priority
540 , int update_every
541 - , RRDSET_TYPE chart_type);
541 + , RRDSET_TYPE chart_type
542 + , RRD_MEMORY_MODE memory_mode
543 + , long history_entries);
544
543 -#define rrdset_create_localhost(type, id, name, family, context, title, units, priority, update_every, chart_type) rrdset_create(localhost, type, id, name, family, context, title, units, priority, update_every, chart_type)
545 +#define rrdset_create(host, type, id, name, family, context, title, units, priority, update_every, chart_type) \
546 + rrdset_create_custom(host, type, id, name, family, context, title, units, priority, update_every, chart_type, (host)->rrd_memory_mode, (host)->rrd_history_entries)
547 +
548 +#define rrdset_create_localhost(type, id, name, family, context, title, units, priority, update_every, chart_type) \
549 + rrdset_create(localhost, type, id, name, family, context, title, units, priority, update_every, chart_type)
550
551 extern void rrdhost_free_all(void);
552 extern void rrdhost_save_all(void);
@@ -566,7 +572,8 @@ extern void rrdset_next_usec(RRDSET *st, usec_t microseconds);
572 extern void rrdset_done(RRDSET *st);
573
574 // checks if the RRDSET should be offered to viewers
569 -#define rrdset_is_available_for_viewers(st) (rrdset_flag_check(st, RRDSET_FLAG_ENABLED) && !rrdset_flag_check(st, RRDSET_FLAG_OBSOLETE) && (st)->dimensions)
575 +#define rrdset_is_available_for_viewers(st) (rrdset_flag_check(st, RRDSET_FLAG_ENABLED) && !rrdset_flag_check(st, RRDSET_FLAG_OBSOLETE) && (st)->dimensions && (st)->rrd_memory_mode != RRD_MEMORY_MODE_NONE)
576 +#define rrdset_is_available_for_backends(st) (rrdset_flag_check(st, RRDSET_FLAG_ENABLED) && !rrdset_flag_check(st, RRDSET_FLAG_OBSOLETE) && (st)->dimensions)
577
578 // get the total duration in seconds of the round robin database
579 #define rrdset_duration(st) ((time_t)( (((st)->counter >= ((unsigned long)(st)->entries))?(unsigned long)(st)->entries:(st)->counter) * (st)->update_every ))
@@ -604,7 +611,8 @@ extern void rrdset_done(RRDSET *st);
611 // ----------------------------------------------------------------------------
612 // RRD DIMENSION functions
613
607 -extern RRDDIM *rrddim_add(RRDSET *st, const char *id, const char *name, collected_number multiplier, collected_number divisor, RRD_ALGORITHM algorithm);
614 +extern RRDDIM *rrddim_add_custom(RRDSET *st, const char *id, const char *name, collected_number multiplier, collected_number divisor, RRD_ALGORITHM algorithm, RRD_MEMORY_MODE memory_mode);
615 +#define rrddim_add(st, id, name, multiplier, divisor, algorithm) rrddim_add_custom(st, id, name, multiplier, divisor, algorithm, (st)->rrd_memory_mode)
616
617 extern void rrddim_set_name(RRDSET *st, RRDDIM *rd, const char *name);
618 extern RRDDIM *rrddim_find(RRDSET *st, const char *id);
src/rrd2json.c
+1 -1
@@ -199,7 +199,7 @@ void rrd_stats_api_v1_charts_allmetrics_prometheus(RRDHOST *host, BUFFER *wb, in
199 prometheus_name_copy(chart, st->id, PROMETHEUS_ELEMENT_MAX);
200
201 buffer_strcat(wb, "\n");
202 - if(rrdset_is_available_for_viewers(st)) {
202 + if(rrdset_is_available_for_backends(st)) {
203 rrdset_rdlock(st);
204
205 // for each dimension
src/rrddim.c
+5 -6
@@ -53,7 +53,7 @@ inline void rrddim_set_name(RRDSET *st, RRDDIM *rd, const char *name) {
53 // ----------------------------------------------------------------------------
54 // RRDDIM create a dimension
55
56 -RRDDIM *rrddim_add(RRDSET *st, const char *id, const char *name, collected_number multiplier, collected_number divisor, RRD_ALGORITHM algorithm) {
56 +RRDDIM *rrddim_add_custom(RRDSET *st, const char *id, const char *name, collected_number multiplier, collected_number divisor, RRD_ALGORITHM algorithm, RRD_MEMORY_MODE memory_mode) {
57 RRDDIM *rd = rrddim_find(st, id);
58 if(unlikely(rd)) {
59 debug(D_RRD_CALLS, "Cannot create rrd dimension '%s/%s', it already exists.", st->id, name?name:"<NONAME>");
@@ -71,8 +71,8 @@ RRDDIM *rrddim_add(RRDSET *st, const char *id, const char *name, collected_numbe
71 rrdset_strncpyz_name(filename, id, FILENAME_MAX);
72 snprintfz(fullfilename, FILENAME_MAX, "%s/%s.db", st->cache_dir, filename);
73
74 - if(st->rrd_memory_mode == RRD_MEMORY_MODE_SAVE || st->rrd_memory_mode == RRD_MEMORY_MODE_MAP) {
75 - rd = (RRDDIM *)mymmap(fullfilename, size, ((st->rrd_memory_mode == RRD_MEMORY_MODE_MAP) ? MAP_SHARED : MAP_PRIVATE), 1);
74 + if(memory_mode == RRD_MEMORY_MODE_SAVE || memory_mode == RRD_MEMORY_MODE_MAP) {
75 + rd = (RRDDIM *)mymmap(fullfilename, size, ((memory_mode == RRD_MEMORY_MODE_MAP) ? MAP_SHARED : MAP_PRIVATE), 1);
76 if(likely(rd)) {
77 // we have a file mapped for rd
78
@@ -126,14 +126,14 @@ RRDDIM *rrddim_add(RRDSET *st, const char *id, const char *name, collected_numbe
126
127 // make sure we have the right memory mode
128 // even if we cleared the memory
129 - rd->rrd_memory_mode = st->rrd_memory_mode;
129 + rd->rrd_memory_mode = memory_mode;
130 }
131 }
132
133 if(unlikely(!rd)) {
134 // if we didn't manage to get a mmap'd dimension, just create one
135 rd = callocz(1, size);
136 - rd->rrd_memory_mode = (st->rrd_memory_mode == RRD_MEMORY_MODE_NONE) ? RRD_MEMORY_MODE_NONE : RRD_MEMORY_MODE_RAM;
136 + rd->rrd_memory_mode = (memory_mode == RRD_MEMORY_MODE_NONE) ? RRD_MEMORY_MODE_NONE : RRD_MEMORY_MODE_RAM;
137 }
138
139 rd->memsize = size;
@@ -203,7 +203,6 @@ RRDDIM *rrddim_add(RRDSET *st, const char *id, const char *name, collected_numbe
203 return(rd);
204 }
205
206 -
206 // ----------------------------------------------------------------------------
207 // RRDDIM remove / free a dimension
208
src/rrdset.c
+10 -8
@@ -344,7 +344,7 @@ static inline RRDSET *rrdset_find_on_create(RRDHOST *host, const char *fullid) {
344 return NULL;
345 }
346
347 -RRDSET *rrdset_create(
347 +RRDSET *rrdset_create_custom(
348 RRDHOST *host
349 , const char *type
350 , const char *id
@@ -356,6 +356,8 @@ RRDSET *rrdset_create(
356 , long priority
357 , int update_every
358 , RRDSET_TYPE chart_type
359 + , RRD_MEMORY_MODE memory_mode
360 + , long history_entries
361 ) {
362 if(!type || !type[0]) {
363 fatal("Cannot create rrd stats without a type.");
@@ -398,11 +400,11 @@ RRDSET *rrdset_create(
400 // ------------------------------------------------------------------------
401 // get the options from the config, we need to create it
402
401 - long rentries = config_get_number(config_section, "history", host->rrd_history_entries);
402 - long entries = align_entries_to_pagesize(host->rrd_memory_mode, rentries);
403 + long rentries = config_get_number(config_section, "history", history_entries);
404 + long entries = align_entries_to_pagesize(memory_mode, rentries);
405 if(entries != rentries) entries = config_set_number(config_section, "history", entries);
406
405 - if(host->rrd_memory_mode == RRD_MEMORY_MODE_NONE && entries != rentries)
407 + if(memory_mode == RRD_MEMORY_MODE_NONE && entries != rentries)
408 entries = config_set_number(config_section, "history", 10);
409
410 int enabled = config_get_boolean(config_section, "enabled", 1);
@@ -419,8 +421,8 @@ RRDSET *rrdset_create(
421 debug(D_RRD_CALLS, "Creating RRD_STATS for '%s.%s'.", type, id);
422
423 snprintfz(fullfilename, FILENAME_MAX, "%s/main.db", cache_dir);
422 - if(host->rrd_memory_mode == RRD_MEMORY_MODE_SAVE || host->rrd_memory_mode == RRD_MEMORY_MODE_MAP) {
423 - st = (RRDSET *) mymmap(fullfilename, size, ((host->rrd_memory_mode == RRD_MEMORY_MODE_MAP) ? MAP_SHARED : MAP_PRIVATE), 0);
424 + if(memory_mode == RRD_MEMORY_MODE_SAVE || memory_mode == RRD_MEMORY_MODE_MAP) {
425 + st = (RRDSET *) mymmap(fullfilename, size, ((memory_mode == RRD_MEMORY_MODE_MAP) ? MAP_SHARED : MAP_PRIVATE), 0);
426 if(st) {
427 memset(&st->avl, 0, sizeof(avl));
428 memset(&st->avlname, 0, sizeof(avl));
@@ -480,13 +482,13 @@ RRDSET *rrdset_create(
482
483 // make sure we have the right memory mode
484 // even if we cleared the memory
483 - st->rrd_memory_mode = host->rrd_memory_mode;
485 + st->rrd_memory_mode = memory_mode;
486 }
487 }
488
489 if(unlikely(!st)) {
490 st = callocz(1, size);
489 - st->rrd_memory_mode = (host->rrd_memory_mode == RRD_MEMORY_MODE_NONE) ? RRD_MEMORY_MODE_NONE : RRD_MEMORY_MODE_RAM;
491 + st->rrd_memory_mode = (memory_mode == RRD_MEMORY_MODE_NONE) ? RRD_MEMORY_MODE_NONE : RRD_MEMORY_MODE_RAM;
492 }
493
494 st->config_section = strdup(config_section);
src/statsd.c
+125 -40
@@ -126,11 +126,20 @@ static struct statsd {
126 int update_every;
127 SIMPLE_PATTERN *charts_for;
128
129 + size_t private_charts;
130 + size_t max_private_charts;
131 + size_t max_private_charts_hard;
132 + RRD_MEMORY_MODE private_charts_memory_mode;
133 + int private_charts_history;
134 +
135 size_t histogram_increase_step;
136 int threads;
137 LISTEN_SOCKETS sockets;
138 } statsd = {
139 .enabled = 1,
140 + .private_charts = 0,
141 + .max_private_charts = 200,
142 + .max_private_charts_hard = 1000,
143
144 .gauges = {
145 .name = "gauge",
@@ -706,22 +715,65 @@ static inline void statsd_get_metric_type_and_id(STATSD_METRIC *m, char *type, c
715 netdata_fix_chart_id(id);
716 }
717
718 +static inline RRDSET *statsd_private_rrdset_create(
719 + STATSD_METRIC *m
720 + , const char *type
721 + , const char *id
722 + , const char *name
723 + , const char *family
724 + , const char *context
725 + , const char *title
726 + , const char *units
727 + , long priority
728 + , int update_every
729 + , RRDSET_TYPE chart_type
730 +) {
731 + RRD_MEMORY_MODE memory_mode = statsd.private_charts_memory_mode;
732 + long history = statsd.private_charts_history;
733 +
734 + if(unlikely(statsd.private_charts >= statsd.max_private_charts)) {
735 + debug(D_STATSD, "STATSD: metric '%s' will be charted with memory mode = none, because the maximum number of charts has been reached.", m->name);
736 + info("STATSD: metric '%s' will be charted with memory mode = none, because the maximum number of charts (%zu) has been reached. Increase the number of charts by editing netdata.conf, [statsd] section.", m->name, statsd.max_private_charts);
737 + memory_mode = RRD_MEMORY_MODE_NONE;
738 + history = 5;
739 + }
740 +
741 + statsd.private_charts++;
742 + return rrdset_create_custom(
743 + localhost
744 + , type
745 + , id
746 + , name
747 + , family
748 + , context
749 + , title
750 + , units
751 + , priority
752 + , update_every
753 + , chart_type
754 + , memory_mode
755 + , history
756 + );
757 +}
758 +
759 +
760 static inline void statsd_chart_from_gauge(STATSD_METRIC *m) {
761 if(unlikely(!m->st)) {
762 char type[RRD_ID_LENGTH_MAX + 1], id[RRD_ID_LENGTH_MAX + 1];
763 statsd_get_metric_type_and_id(m, type, id, "gauge", RRD_ID_LENGTH_MAX);
764
714 - m->st = rrdset_create_localhost(
715 - type
716 - , id
717 - , NULL // name
718 - , "gauges" // family (submenu)
719 - , m->name // context
720 - , m->name // title
721 - , "value" // units
722 - , STATSD_CHART_PRIORITY
723 - , statsd.update_every
724 - , RRDSET_TYPE_LINE
765 + m->st = statsd_private_rrdset_create(
766 + m
767 + , type
768 + , id
769 + , NULL // name
770 + , "gauges" // family (submenu)
771 + , m->name // context
772 + , m->name // title
773 + , "value" // units
774 + , STATSD_CHART_PRIORITY
775 + , statsd.update_every
776 + , RRDSET_TYPE_LINE
777 );
778
779 m->rd_value = rrddim_add(m->st, "gauge", NULL, 1, 1000, RRD_ALGORITHM_ABSOLUTE);
@@ -747,8 +799,9 @@ static inline void statsd_chart_from_counter_or_meter(STATSD_METRIC *m, char *di
799 char type[RRD_ID_LENGTH_MAX + 1], id[RRD_ID_LENGTH_MAX + 1];
800 statsd_get_metric_type_and_id(m, type, id, dim, RRD_ID_LENGTH_MAX);
801
750 - m->st = rrdset_create_localhost(
751 - type
802 + m->st = statsd_private_rrdset_create(
803 + m
804 + , type
805 , id
806 , NULL // name
807 , family // family (submenu)
@@ -791,8 +844,9 @@ static inline void statsd_chart_from_set(STATSD_METRIC *m) {
844 char type[RRD_ID_LENGTH_MAX + 1], id[RRD_ID_LENGTH_MAX + 1];
845 statsd_get_metric_type_and_id(m, type, id, "set", RRD_ID_LENGTH_MAX);
846
794 - m->st = rrdset_create_localhost(
795 - type
847 + m->st = statsd_private_rrdset_create(
848 + m
849 + , type
850 , id
851 , NULL // name
852 , "sets" // family (submenu)
@@ -804,7 +858,7 @@ static inline void statsd_chart_from_set(STATSD_METRIC *m) {
858 , RRDSET_TYPE_LINE
859 );
860
807 - m->rd_value = rrddim_add(m->st, "set", "set size", 1, 1, RRD_ALGORITHM_ABSOLUTE);
861 + m->rd_value = rrddim_add(m->st, "set", "set size", 1, 1, RRD_ALGORITHM_ABSOLUTE);
862
863 if(m->options & STATSD_METRIC_OPTION_CHART_DIMENSION_COUNT)
864 m->rd_count = rrddim_add(m->st, "events", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL);
@@ -823,23 +877,27 @@ static inline void statsd_chart_from_set(STATSD_METRIC *m) {
877 rrdset_done(m->st);
878 }
879
826 -static inline void statsd_charts_from_index(STATSD_INDEX *index, void (*chart_from_metric)(STATSD_METRIC *m)) {
880 +static inline void statsd_private_charts_from_index(STATSD_INDEX *index, void (*chart_from_metric)(STATSD_METRIC *m)) {
881 STATSD_METRIC *m;
882 for(m = index->first; m ; m = m->next) {
829 -
830 - // if this is the first time we see this metric
831 - // check it against our list
832 - if(unlikely(!(m->options & STATSD_METRIC_OPTION_PRIVATE_CHART_DISABLED|STATSD_METRIC_OPTION_PRIVATE_CHART_ENABLED))) {
833 - if(simple_pattern_matches(statsd.charts_for, m->name))
834 - m->options |= STATSD_METRIC_OPTION_PRIVATE_CHART_ENABLED;
835 - else
883 + if(unlikely(!(m->options & (STATSD_METRIC_OPTION_PRIVATE_CHART_DISABLED|STATSD_METRIC_OPTION_PRIVATE_CHART_ENABLED)))) {
884 + if(statsd.private_charts >= statsd.max_private_charts_hard) {
885 + debug(D_STATSD, "STATSD: metric '%s' will not be charted, because the hard limit of the maximum number of charts has been reached.", m->name);
886 + info("STATSD: metric '%s' will not be charted, because the hard limit of the maximum number of charts (%zu) has been reached. Increase the number of charts by editing netdata.conf, [statsd] section.", m->name, statsd.max_private_charts);
887 m->options |= STATSD_METRIC_OPTION_PRIVATE_CHART_DISABLED;
837 - }
838 -
839 - // if it is not enabled, don't do anything
840 - if(unlikely(m->options & STATSD_METRIC_OPTION_PRIVATE_CHART_DISABLED)) {
841 - m->reset = 1; // reset it to allow housekeeping
842 - continue;
888 + m->options &= ~STATSD_METRIC_OPTION_PRIVATE_CHART_ENABLED;
889 + }
890 + else {
891 + if (simple_pattern_matches(statsd.charts_for, m->name)) {
892 + debug(D_STATSD, "STATSD: metric '%s' will be charted.", m->name);
893 + m->options |= STATSD_METRIC_OPTION_PRIVATE_CHART_ENABLED;
894 + m->options &= ~STATSD_METRIC_OPTION_PRIVATE_CHART_DISABLED;
895 + } else {
896 + debug(D_STATSD, "STATSD: metric '%s' will not be charted.", m->name);
897 + m->options |= STATSD_METRIC_OPTION_PRIVATE_CHART_DISABLED;
898 + m->options &= ~STATSD_METRIC_OPTION_PRIVATE_CHART_ENABLED;
899 + }
900 + }
901 }
902
903 // if no values are collected, and we need to show gaps
@@ -848,9 +906,12 @@ static inline void statsd_charts_from_index(STATSD_INDEX *index, void (*chart_fr
906 continue;
907 }
908
851 - // send it to netdata
852 - if(likely(chart_from_metric))
909 + if(likely(m->options & STATSD_METRIC_OPTION_PRIVATE_CHART_ENABLED && chart_from_metric))
910 + // send it to netdata
911 chart_from_metric(m);
912 + else
913 + // since we not going to send it, reset it to allow housekeeping
914 + m->reset = 1;
915 }
916 }
917
@@ -884,6 +945,10 @@ void *statsd_main(void *ptr) {
945 }
946
947 statsd.charts_for = simple_pattern_create(config_get(CONFIG_SECTION_STATSD, "create private charts for metrics matching", "*"), SIMPLE_PATTERN_EXACT);
948 + statsd.max_private_charts = (size_t)config_get_number(CONFIG_SECTION_STATSD, "max private charts allowed", (long long)statsd.max_private_charts);
949 + statsd.max_private_charts_hard = (size_t)config_get_number(CONFIG_SECTION_STATSD, "max private charts hard limit", (long long)statsd.max_private_charts * 5);
950 + statsd.private_charts_memory_mode = rrd_memory_mode_id(config_get(CONFIG_SECTION_STATSD, "private charts memory mode", rrd_memory_mode_name(default_rrd_memory_mode)));
951 + statsd.private_charts_history = (int)config_get_number(CONFIG_SECTION_STATSD, "private charts history", default_rrd_history_entries);
952
953 if(config_get_boolean(CONFIG_SECTION_STATSD, "add dimension for number of events received", 1)) {
954 statsd.gauges.default_options |= STATSD_METRIC_OPTION_CHART_DIMENSION_COUNT;
@@ -978,18 +1043,43 @@ void *statsd_main(void *ptr) {
1043 RRDDIM *rd_events_histogram = rrddim_add(st_events, "histograms", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL);
1044 RRDDIM *rd_events_set = rrddim_add(st_events, "sets", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL);
1045
1046 + RRDSET *st_pcharts = rrdset_create_localhost(
1047 + "netdata"
1048 + , "private_charts"
1049 + , NULL
1050 + , "statsd"
1051 + , NULL
1052 + , "Private metric charts created by the netdata statsd server"
1053 + , "charts"
1054 + , 132002
1055 + , statsd.update_every
1056 + , RRDSET_TYPE_AREA
1057 + );
1058 + RRDDIM *rd_pcharts = rrddim_add(st_pcharts, "charts", NULL, 1, 1, RRD_ALGORITHM_ABSOLUTE);
1059 +
1060 usec_t step = statsd.update_every * USEC_PER_SEC;
1061 heartbeat_t hb;
1062 heartbeat_init(&hb);
1063 for(;;) {
1064 usec_t hb_dt = heartbeat_next(&hb, step);
1065
1066 + if(unlikely(netdata_exit))
1067 + break;
1068 +
1069 + statsd_private_charts_from_index(&statsd.gauges, statsd_chart_from_gauge);
1070 + statsd_private_charts_from_index(&statsd.counters, statsd_chart_from_counter);
1071 + statsd_private_charts_from_index(&statsd.meters, statsd_chart_from_meter);
1072 + statsd_private_charts_from_index(&statsd.timers, NULL);
1073 + statsd_private_charts_from_index(&statsd.histograms, NULL);
1074 + statsd_private_charts_from_index(&statsd.sets, statsd_chart_from_set);
1075 +
1076 if(unlikely(netdata_exit))
1077 break;
1078
1079 if(hb_dt) {
1080 rrdset_next(st_metrics);
1081 rrdset_next(st_events);
1082 + rrdset_next(st_pcharts);
1083 }
1084
1085 rrddim_set_by_pointer(st_metrics, rd_metrics_gauge, (collected_number)statsd.gauges.metrics);
@@ -1006,22 +1096,17 @@ void *statsd_main(void *ptr) {
1096 rrddim_set_by_pointer(st_events, rd_events_histogram, (collected_number)statsd.histograms.events);
1097 rrddim_set_by_pointer(st_events, rd_events_set, (collected_number)statsd.sets.events);
1098
1099 + rrddim_set_by_pointer(st_pcharts, rd_pcharts, (collected_number)statsd.private_charts);
1100 +
1101 if(unlikely(netdata_exit))
1102 break;
1103
1104 rrdset_done(st_metrics);
1105 rrdset_done(st_events);
1106 + rrdset_done(st_pcharts);
1107
1108 if(unlikely(netdata_exit))
1109 break;
1017 -
1018 - statsd_charts_from_index(&statsd.gauges, statsd_chart_from_gauge);
1019 - statsd_charts_from_index(&statsd.counters, statsd_chart_from_counter);
1020 - statsd_charts_from_index(&statsd.meters, statsd_chart_from_meter);
1021 - statsd_charts_from_index(&statsd.timers, NULL);
1022 - statsd_charts_from_index(&statsd.histograms, NULL);
1023 - statsd_charts_from_index(&statsd.sets, statsd_chart_from_set);
1024 -
1110 }
1111
1112 for(i = 0; i < statsd.threads ;i++)