@cryptotaxi247 / netdata-1 / commits / 23b55cf93

implemented app charts for statsd - untested yet

Costa Tsaousis (ktsaou) committed May 2, 2017 at 01:42 UTC 23b55cf93ebbfa2d553f23cbc838865cde8f3357
7 files changed +474 -135
README.md
+1 -1
@@ -281,7 +281,7 @@ And you can extend it, by writing plugins that collect data from any source, usi
281
282 This is a high level overview of netdata feature set and architecture. Click it to to interact with it.
283
284 -[![netdata-overview](https://cloud.githubusercontent.com/assets/2662304/25578239/484665c0-2e75-11e7-8a79-71e0486951fd.png)](https://my-netdata.io/infographic.html)
284 +[![netdata-overview](https://cloud.githubusercontent.com/assets/2662304/25580009/bf7016a4-2e85-11e7-9a7a-b36c57db7b91.png)](https://my-netdata.io/infographic.html)
285
286 ---
287
src/appconfig.c
+9 -9
@@ -439,11 +439,11 @@ int appconfig_load(struct config *root, char *filename, int overwrite_used)
439
440 if(!filename) filename = CONFIG_DIR "/" CONFIG_FILENAME;
441
442 - debug(D_CONFIG, "Opening config file '%s'", filename);
442 + debug(D_CONFIG, "CONFIG: opening config file '%s'", filename);
443
444 FILE *fp = fopen(filename, "r");
445 if(!fp) {
446 - error("Cannot open file '%s'", filename);
446 + error("CONFIG: cannot open file '%s'", filename);
447 return 0;
448 }
449
@@ -453,7 +453,7 @@ int appconfig_load(struct config *root, char *filename, int overwrite_used)
453
454 s = trim(buffer);
455 if(!s || *s == '#') {
456 - debug(D_CONFIG, "Ignoring line %d, it is empty.", line);
456 + debug(D_CONFIG, "CONFIG: ignoring line %d of file '%s', it is empty.", line, filename);
457 continue;
458 }
459
@@ -471,14 +471,14 @@ int appconfig_load(struct config *root, char *filename, int overwrite_used)
471
472 if(!co) {
473 // line outside a section
474 - error("Ignoring line %d ('%s'), it is outside all sections.", line, s);
474 + error("CONFIG: ignoring line %d ('%s') of file '%s', it is outside all sections.", line, s, filename);
475 continue;
476 }
477
478 char *name = s;
479 char *value = strchr(s, '=');
480 if(!value) {
481 - error("Ignoring line %d ('%s'), there is no = in it.", line, s);
481 + error("CONFIG: ignoring line %d ('%s') of file '%s', there is no = in it.", line, s, filename);
482 continue;
483 }
484 *value = '\0';
@@ -488,11 +488,11 @@ int appconfig_load(struct config *root, char *filename, int overwrite_used)
488 value = trim(value);
489
490 if(!name || *name == '#') {
491 - error("Ignoring line %d, name is empty.", line);
491 + error("CONFIG: ignoring line %d of file '%s', name is empty.", line, filename);
492 continue;
493 }
494 if(!value) {
495 - debug(D_CONFIG, "Ignoring line %d, value is empty.", line);
495 + debug(D_CONFIG, "CONFIG: ignoring line %d of file '%s', value is empty.", line, filename);
496 continue;
497 }
498
@@ -501,12 +501,12 @@ int appconfig_load(struct config *root, char *filename, int overwrite_used)
501 if(!cv) cv = appconfig_value_create(co, name, value);
502 else {
503 if(((cv->flags & CONFIG_VALUE_USED) && overwrite_used) || !(cv->flags & CONFIG_VALUE_USED)) {
504 - debug(D_CONFIG, "Line %d, overwriting '%s/%s'.", line, co->name, cv->name);
504 + debug(D_CONFIG, "CONFIG: line %d of file '%s', overwriting '%s/%s'.", line, filename, co->name, cv->name);
505 freez(cv->value);
506 cv->value = strdupz(value);
507 }
508 else
509 - debug(D_CONFIG, "Ignoring line %d, '%s/%s' is already present and used.", line, co->name, cv->name);
509 + debug(D_CONFIG, "CONFIG: ignoring line %d of file '%s', '%s/%s' is already present and used.", line, filename, co->name, cv->name);
510 }
511 cv->flags |= CONFIG_VALUE_LOADED;
512 }
src/plugin_tc.c
+3 -3
@@ -713,7 +713,7 @@ static inline void tc_device_free_all()
713 tc_device_free(tc_device_root);
714 }
715
716 -#define MAX_WORDS 20
716 +#define PLUGINSD_MAX_WORDS 20
717
718 static inline int tc_space(char c) {
719 switch(c) {
@@ -779,7 +779,7 @@ void *tc_main(void *ptr) {
779 RRDSET *stcpu = NULL, *sttime = NULL;
780
781 char buffer[TC_LINE_MAX+1] = "";
782 - char *words[MAX_WORDS] = { NULL };
782 + char *words[PLUGINSD_MAX_WORDS] = { NULL };
783
784 uint32_t BEGIN_HASH = simple_hash("BEGIN");
785 uint32_t END_HASH = simple_hash("END");
@@ -822,7 +822,7 @@ void *tc_main(void *ptr) {
822 buffer[TC_LINE_MAX] = '\0';
823 // debug(D_TC_LOOP, "TC: read '%s'", buffer);
824
825 - tc_split_words(buffer, words, MAX_WORDS);
825 + tc_split_words(buffer, words, PLUGINSD_MAX_WORDS);
826
827 if(unlikely(!words[0] || !*words[0])) {
828 // debug(D_TC_LOOP, "empty line");
src/plugins_d.c
+16 -35
@@ -2,8 +2,6 @@
2
3 struct plugind *pluginsd_root = NULL;
4
5 -#define MAX_WORDS 20
6 -
5 static inline int pluginsd_space(char c) {
6 switch(c) {
7 case ' ':
@@ -18,7 +16,8 @@ static inline int pluginsd_space(char c) {
16 }
17 }
18
21 -static int pluginsd_split_words(char *str, char **words, int max_words) {
19 +// split a text into words, respecting quotes
20 +inline int pluginsd_split_words(char *str, char **words, int max_words) {
21 char *s = str, quote = 0;
22 int i = 0, j;
23
@@ -95,14 +94,13 @@ inline size_t pluginsd_process(RRDHOST *host, struct plugind *cd, FILE *fp, int
94
95 char line[PLUGINSD_LINE_MAX + 1];
96
98 - char *words[MAX_WORDS] = { NULL };
99 - /* uint32_t HOST_HASH = simple_hash("HOST"); */
100 - uint32_t BEGIN_HASH = simple_hash("BEGIN");
101 - uint32_t END_HASH = simple_hash("END");
102 - uint32_t FLUSH_HASH = simple_hash("FLUSH");
103 - uint32_t CHART_HASH = simple_hash("CHART");
104 - uint32_t DIMENSION_HASH = simple_hash("DIMENSION");
105 - uint32_t DISABLE_HASH = simple_hash("DISABLE");
97 + char *words[PLUGINSD_MAX_WORDS] = { NULL };
98 + uint32_t BEGIN_HASH = simple_hash(PLUGINSD_KEYWORD_BEGIN);
99 + uint32_t END_HASH = simple_hash(PLUGINSD_KEYWORD_END);
100 + uint32_t FLUSH_HASH = simple_hash(PLUGINSD_KEYWORD_FLUSH);
101 + uint32_t CHART_HASH = simple_hash(PLUGINSD_KEYWORD_CHART);
102 + uint32_t DIMENSION_HASH = simple_hash(PLUGINSD_KEYWORD_DIMENSION);
103 + uint32_t DISABLE_HASH = simple_hash(PLUGINSD_KEYWORD_DISABLE);
104
105 RRDSET *st = NULL;
106 uint32_t hash;
@@ -130,7 +128,7 @@ inline size_t pluginsd_process(RRDHOST *host, struct plugind *cd, FILE *fp, int
128
129 // debug(D_PLUGINSD, "PLUGINSD: %s: %s", cd->filename, line);
130
133 - int w = pluginsd_split_words(line, words, MAX_WORDS);
131 + int w = pluginsd_split_words(line, words, PLUGINSD_MAX_WORDS);
132 char *s = words[0];
133 if(unlikely(!s || !*s || !w)) {
134 // debug(D_PLUGINSD, "PLUGINSD: empty line");
@@ -170,7 +168,7 @@ inline size_t pluginsd_process(RRDHOST *host, struct plugind *cd, FILE *fp, int
168 rrddim_set_by_pointer(st, rd, strtoll(value, NULL, 0));
169 }
170 }
173 - else if(likely(hash == BEGIN_HASH && !strcmp(s, "BEGIN"))) {
171 + else if(likely(hash == BEGIN_HASH && !strcmp(s, PLUGINSD_KEYWORD_BEGIN))) {
172 char *id = words[1];
173 char *microseconds_txt = words[2];
174
@@ -200,7 +198,7 @@ inline size_t pluginsd_process(RRDHOST *host, struct plugind *cd, FILE *fp, int
198 else rrdset_next(st);
199 }
200 }
203 - else if(likely(hash == END_HASH && !strcmp(s, "END"))) {
201 + else if(likely(hash == END_HASH && !strcmp(s, PLUGINSD_KEYWORD_END))) {
202 if(unlikely(!st)) {
203 error("PLUGINSD: '%s' is requesting an END, without a BEGIN on host '%s'. Disabling it.", cd->fullfilename, host->hostname);
204 enabled = 0;
@@ -214,28 +212,11 @@ inline size_t pluginsd_process(RRDHOST *host, struct plugind *cd, FILE *fp, int
212
213 count++;
214 }
217 -/* else if(likely(hash == HOST_HASH && !strcmp(s, "HOST"))) {
218 - char *guid = words[1];
219 - char *hostname = words[2];
220 -
221 - if(unlikely(!guid || !*guid)) {
222 - error("PLUGINSD: '%s' is requesting HOST with guid '%s' and hostname '%s', without a guid. Disabling it.", cd->fullfilename, guid?guid:"", hostname?hostname:"");
223 - enabled = 0;
224 - break;
225 - }
226 - if(unlikely(!hostname || !*hostname)) {
227 - error("PLUGINSD: '%s' is requesting HOST with guid '%s' and hostname '%s', without a hostname. Disabling it.", cd->fullfilename, guid?guid:"", hostname?hostname:"");
228 - enabled = 0;
229 - break;
230 - }
231 -
232 - host = rrdhost_find_or_create(hostname, guid);
233 - } */
234 - else if(likely(hash == FLUSH_HASH && !strcmp(s, "FLUSH"))) {
215 + else if(likely(hash == FLUSH_HASH && !strcmp(s, PLUGINSD_KEYWORD_FLUSH))) {
216 debug(D_PLUGINSD, "PLUGINSD: '%s' is requesting a FLUSH", cd->fullfilename);
217 st = NULL;
218 }
238 - else if(likely(hash == CHART_HASH && !strcmp(s, "CHART"))) {
219 + else if(likely(hash == CHART_HASH && !strcmp(s, PLUGINSD_KEYWORD_CHART))) {
220 int noname = 0;
221 st = NULL;
222
@@ -294,7 +275,7 @@ inline size_t pluginsd_process(RRDHOST *host, struct plugind *cd, FILE *fp, int
275 }
276 else debug(D_PLUGINSD, "PLUGINSD: Chart '%s' already exists. Not adding it again.", st->id);
277 }
297 - else if(likely(hash == DIMENSION_HASH && !strcmp(s, "DIMENSION"))) {
278 + else if(likely(hash == DIMENSION_HASH && !strcmp(s, PLUGINSD_KEYWORD_DIMENSION))) {
279 char *id = words[1];
280 char *name = words[2];
281 char *algorithm = words[3];
@@ -349,7 +330,7 @@ inline size_t pluginsd_process(RRDHOST *host, struct plugind *cd, FILE *fp, int
330 else if(unlikely(rrdset_flag_check(st, RRDSET_FLAG_DEBUG)))
331 debug(D_PLUGINSD, "PLUGINSD: dimension %s/%s already exists. Not adding it again.", st->id, id);
332 }
352 - else if(unlikely(hash == DISABLE_HASH && !strcmp(s, "DISABLE"))) {
333 + else if(unlikely(hash == DISABLE_HASH && !strcmp(s, PLUGINSD_KEYWORD_DISABLE))) {
334 info("PLUGINSD: '%s' called DISABLE. Disabling it.", cd->fullfilename);
335 enabled = 0;
336 break;
src/plugins_d.h
+10
@@ -4,7 +4,16 @@
4 #define PLUGINSD_FILE_SUFFIX ".plugin"
5 #define PLUGINSD_FILE_SUFFIX_LEN strlen(PLUGINSD_FILE_SUFFIX)
6 #define PLUGINSD_CMD_MAX (FILENAME_MAX*2)
7 +
8 +#define PLUGINSD_KEYWORD_CHART "CHART"
9 +#define PLUGINSD_KEYWORD_DIMENSION "DIMENSION"
10 +#define PLUGINSD_KEYWORD_BEGIN "BEGIN"
11 +#define PLUGINSD_KEYWORD_END "END"
12 +#define PLUGINSD_KEYWORD_FLUSH "FLUSH"
13 +#define PLUGINSD_KEYWORD_DISABLE "DISABLE"
14 +
15 #define PLUGINSD_LINE_MAX 1024
16 +#define PLUGINSD_MAX_WORDS 20
17
18 struct plugind {
19 char id[CONFIG_MAX_NAME+1]; // config node id
@@ -35,5 +44,6 @@ extern struct plugind *pluginsd_root;
44
45 extern void *pluginsd_main(void *ptr);
46 extern size_t pluginsd_process(RRDHOST *host, struct plugind *cd, FILE *fp, int trust_durations);
47 +extern int pluginsd_split_words(char *str, char **words, int max_words);
48
49 #endif /* NETDATA_PLUGINS_D_H */
src/statsd.c
+433 -85
@@ -1,5 +1,9 @@
1 #include "common.h"
2
3 +#define STATSD_CHART_PREFIX "statsd"
4 +#define STATSD_CHART_PRIORITY 90000
5 +
6 +
7 // --------------------------------------------------------------------------------------
8
9 // #define STATSD_MULTITHREADED 1
@@ -73,16 +77,28 @@ typedef enum statsd_metric_options {
77 STATSD_METRIC_OPTION_NONE = 0x00000000, // no options set
78 STATSD_METRIC_OPTION_SHOW_GAPS_WHEN_NOT_COLLECTED = 0x00000001, // do not update the chart dimension, when this metric is not collected
79 STATSD_METRIC_OPTION_PRIVATE_CHART_ENABLED = 0x00000002, // render a private chart for this metric
76 - STATSD_METRIC_OPTION_PRIVATE_CHART_DISABLED = 0x00000004, // do not render a private chart for this metric
80 + STATSD_METRIC_OPTION_PRIVATE_CHART_CHECKED = 0x00000004, // the metric has been checked if it should get private chart or not
81 STATSD_METRIC_OPTION_CHART_DIMENSION_COUNT = 0x00000008, // show the count of events for this private chart
82 + STATSD_METRIC_OPTION_CHECKED_IN_APPS = 0x00000010, // set when this metric has been checked agains apps
83 } STATS_METRIC_OPTIONS;
84
85 +typedef enum statsd_metric_type {
86 + STATSD_METRIC_TYPE_GAUGE,
87 + STATSD_METRIC_TYPE_COUNTER,
88 + STATSD_METRIC_TYPE_METER,
89 + STATSD_METRIC_TYPE_TIMER,
90 + STATSD_METRIC_TYPE_HISTOGRAM,
91 + STATSD_METRIC_TYPE_SET
92 +} STATSD_METRIC_TYPE;
93 +
94 typedef struct statsd_metric {
95 avl avl; // indexing
96
97 const char *name; // the name of the metric
98 uint32_t hash; // hash of the name
99
100 + STATSD_METRIC_TYPE type;
101 +
102 // metadata about data collection
103 size_t events; // the number of times this metric has been collected (never resets)
104 size_t count; // the number of times this metric has been collected since the last flush
@@ -133,6 +149,7 @@ typedef struct statsd_app_chart_dim {
149 const char *name;
150 const char *metric;
151 uint32_t metric_hash;
152 + const char *options;
153 collected_number multiplier;
154 collected_number divider;
155 STATSD_INDEX *index;
@@ -153,6 +170,8 @@ typedef struct statsd_app_chart {
170 long priority;
171 RRDSET_TYPE chart_type;
172 STATSD_APP_CHART_DIM *dimensions;
173 + size_t dimensions_count;
174 + size_t dimensions_linked_count;
175
176 RRDSET *st;
177 struct statsd_app_chart *next;
@@ -162,9 +181,12 @@ typedef struct statsd_app {
181 const char *name;
182 SIMPLE_PATTERN *metrics;
183 STATS_METRIC_OPTIONS default_options;
184 + RRD_MEMORY_MODE rrd_memory_mode;
185 + long rrd_history_entries;
186
187 const char *source;
188 STATSD_APP_CHART *charts;
189 + size_t charts_count;
190 struct statsd_app *next;
191 } STATSD_APP;
192
@@ -195,7 +217,7 @@ static struct statsd {
217 size_t max_private_charts;
218 size_t max_private_charts_hard;
219 RRD_MEMORY_MODE private_charts_memory_mode;
198 - int private_charts_history;
220 + long private_charts_rrd_history_entries;
221
222 STATSD_APP *apps;
223 size_t recvmmsg_size;
@@ -295,7 +317,7 @@ static inline STATSD_METRIC *stasd_metric_index_find(STATSD_INDEX *index, const
317 return (STATSD_METRIC *)STATSD_AVL_SEARCH(&index->index, (avl *)&tmp);
318 }
319
298 -static inline STATSD_METRIC *statsd_find_or_add_metric(STATSD_INDEX *index, const char *name) {
320 +static inline STATSD_METRIC *statsd_find_or_add_metric(STATSD_INDEX *index, const char *name, STATSD_METRIC_TYPE type) {
321 debug(D_STATSD, "finding or adding metric '%s' under '%s'", name, index->name);
322
323 uint32_t hash = simple_hash(name);
@@ -307,9 +329,10 @@ static inline STATSD_METRIC *statsd_find_or_add_metric(STATSD_INDEX *index, cons
329 m = (STATSD_METRIC *)callocz(sizeof(STATSD_METRIC), 1);
330 m->name = strdupz(name);
331 m->hash = hash;
332 + m->type = type;
333 m->options = index->default_options;
334
312 - if(index == &statsd.histograms || index == &statsd.timers) {
335 + if(type == STATSD_METRIC_TYPE_HISTOGRAM || type == STATSD_METRIC_TYPE_TIMER) {
336 m->histogram.ext = callocz(sizeof(STATSD_METRIC_HISTOGRAM_EXTENSIONS), 1);
337 netdata_mutex_init(&m->histogram.mutex);
338 }
@@ -484,25 +507,25 @@ static void statsd_process_metric(const char *name, const char *value, const cha
507 switch (*type) {
508 case 'g':
509 statsd_process_gauge(
487 - statsd_find_or_add_metric(&statsd.gauges, name),
510 + statsd_find_or_add_metric(&statsd.gauges, name, STATSD_METRIC_TYPE_GAUGE),
511 value, sampling);
512 break;
513
514 case 'c': // etsy/statsd, but brubeck uses it as 'meter' - sorry brubeck, this is stupid
515 case 'C': // brubeck
516 statsd_process_counter(
494 - statsd_find_or_add_metric(&statsd.counters, name),
517 + statsd_find_or_add_metric(&statsd.counters, name, STATSD_METRIC_TYPE_COUNTER),
518 value, sampling);
519 break;
520
521 case 'm':
522 if (type[1] == 's')
523 statsd_process_timer(
501 - statsd_find_or_add_metric(&statsd.timers, name),
524 + statsd_find_or_add_metric(&statsd.timers, name, STATSD_METRIC_TYPE_TIMER),
525 value, sampling);
526 else if (type[1] == '\0')
527 statsd_process_meter(
505 - statsd_find_or_add_metric(&statsd.meters, name),
528 + statsd_find_or_add_metric(&statsd.meters, name, STATSD_METRIC_TYPE_METER),
529 value, sampling);
530 else
531 statsd.unknown_types++;
@@ -510,13 +533,13 @@ static void statsd_process_metric(const char *name, const char *value, const cha
533
534 case 'h':
535 statsd_process_histogram(
513 - statsd_find_or_add_metric(&statsd.histograms, name),
536 + statsd_find_or_add_metric(&statsd.histograms, name, STATSD_METRIC_TYPE_HISTOGRAM),
537 value, sampling);
538 break;
539
540 case 's':
541 statsd_process_set(
519 - statsd_find_or_add_metric(&statsd.sets, name),
542 + statsd_find_or_add_metric(&statsd.sets, name, STATSD_METRIC_TYPE_SET),
543 value);
544 break;
545
@@ -905,12 +928,198 @@ void *statsd_collector_thread(void *ptr) {
928 // --------------------------------------------------------------------------------------------------------------------
929 // statsd applications configuration files parsing
930
908 -#define STATSD_CONF_MAX_LINE 8192
931 +#define STATSD_CONF_LINE_MAX 8192
932
933 int statsd_readfile(const char *path, const char *filename) {
934 debug(D_STATSD, "STATSD configuration reading file '%s/%s'", path, filename);
912 - // FIXME
913 - return 1;
935 +
936 + char buffer[STATSD_CONF_LINE_MAX + 1];
937 +
938 + FILE *fp = NULL;
939 + snprintfz(buffer, STATSD_CONF_LINE_MAX, "%s/%s", path, filename);
940 + fp = fopen(buffer, "r");
941 + if(!fp) {
942 + error("STATSD: cannot open file '%s'.", buffer);
943 + return -1;
944 + }
945 +
946 + STATSD_APP *app = NULL;
947 + STATSD_APP_CHART *chart = NULL;
948 +
949 + size_t line = 0;
950 + char *s;
951 + while(fgets(buffer, STATSD_CONF_LINE_MAX, fp) != NULL) {
952 + buffer[STATSD_CONF_LINE_MAX] = '\0';
953 + line++;
954 +
955 + s = trim(buffer);
956 + if (!s || *s == '#') {
957 + debug(D_STATSD, "STATSD: ignoring line %zu of file '%s/%s', it is empty.", line, path, filename);
958 + continue;
959 + }
960 +
961 + int len = (int) strlen(s);
962 + if (*s == '[' && s[len - 1] == ']') {
963 + // new section
964 + s[len - 1] = '\0';
965 + s++;
966 +
967 + if (!strcmp(s, "app")) {
968 + // a new app
969 + app = callocz(sizeof(STATSD_APP), 1);
970 + app->name = strdupz("unnamed");
971 + app->rrd_memory_mode = localhost->rrd_memory_mode;
972 + app->rrd_history_entries = localhost->rrd_history_entries;
973 +
974 + app->next = statsd.apps;
975 + statsd.apps = app;
976 + chart = NULL;
977 + }
978 + else {
979 + // a new chart
980 + chart = callocz(sizeof(STATSD_APP_CHART), 1);
981 + chart->id = strdupz(s);
982 + chart->title = strdupz("Statsd chart");
983 + chart->context = strdupz(s);
984 + chart->family = strdupz("overview");
985 + chart->units = strdupz("value");
986 + chart->priority = STATSD_CHART_PRIORITY;
987 + chart->chart_type = RRDSET_TYPE_LINE;
988 +
989 + chart->next = chart;
990 + app->charts = chart;
991 + }
992 +
993 + continue;
994 + }
995 +
996 + if(!app) {
997 + error("STATSD: ignoring line %zu ('%s') of file '%s/%s', it is outside all sections.", line, s, path, filename);
998 + continue;
999 + }
1000 +
1001 + char *name = s;
1002 + char *value = strchr(s, '=');
1003 + if(!value) {
1004 + error("STATSD: ignoring line %zu ('%s') of file '%s/%s', there is no = in it.", line, s, path, filename);
1005 + continue;
1006 + }
1007 + *value = '\0';
1008 + value++;
1009 +
1010 + name = trim(name);
1011 + value = trim(value);
1012 +
1013 + if(!name || *name == '#') {
1014 + error("STATSD: ignoring line %zu of file '%s/%s', name is empty.", line, path, filename);
1015 + continue;
1016 + }
1017 + if(!value) {
1018 + debug(D_CONFIG, "STATSD: ignoring line %zu of file '%s/%s', value is empty.", line, path, filename);
1019 + continue;
1020 + }
1021 +
1022 + if(!chart) {
1023 + if(!strcmp(name, "name")) {
1024 + freez((void *)app->name);
1025 + app->name = strdupz(value);
1026 + }
1027 + else if (!strcmp(name, "metrics")) {
1028 + simple_pattern_free(app->metrics);
1029 + app->metrics = simple_pattern_create(value, SIMPLE_PATTERN_EXACT);
1030 + }
1031 + else if (!strcmp(name, "private charts")) {
1032 + if (!strcmp(value, "yes") || !strcmp(value, "on"))
1033 + app->default_options |= STATSD_METRIC_OPTION_PRIVATE_CHART_ENABLED;
1034 + else
1035 + app->default_options &= ~STATSD_METRIC_OPTION_PRIVATE_CHART_ENABLED;
1036 + }
1037 + else if (!strcmp(name, "gaps when not collected")) {
1038 + if (!strcmp(value, "yes") || !strcmp(value, "on"))
1039 + app->default_options |= STATSD_METRIC_OPTION_SHOW_GAPS_WHEN_NOT_COLLECTED;
1040 + }
1041 + else if (!strcmp(name, "memory mode")) {
1042 + app->rrd_memory_mode = rrd_memory_mode_id(value);
1043 + }
1044 + else if (!strcmp(name, "history")) {
1045 + app->rrd_history_entries = atol(value);
1046 + if (app->rrd_history_entries < 5)
1047 + app->rrd_history_entries = 5;
1048 + }
1049 + else {
1050 + error("STATSD: ignoring line %zu ('%s') of file '%s/%s'. Unknown keyword for the [app] section.", line, name, path, filename);
1051 + continue;
1052 + }
1053 + }
1054 + else {
1055 + if(!strcmp(name, "name")) {
1056 + freez((void *)chart->name);
1057 + chart->name = strdupz(value);
1058 + }
1059 + else if(!strcmp(name, "title")) {
1060 + freez((void *)chart->title);
1061 + chart->title = strdupz(value);
1062 + }
1063 + else if (!strcmp(name, "family")) {
1064 + freez((void *)chart->family);
1065 + chart->family = strdupz(value);
1066 + }
1067 + else if (!strcmp(name, "context")) {
1068 + freez((void *)chart->context);
1069 + chart->context = strdupz(value);
1070 + }
1071 + else if (!strcmp(name, "units")) {
1072 + freez((void *)chart->units);
1073 + chart->units = strdupz(value);
1074 + }
1075 + else if (!strcmp(name, "priority")) {
1076 + chart->priority = atol(value);
1077 + }
1078 + else if (!strcmp(name, "type")) {
1079 + chart->chart_type = rrdset_type_id(value);
1080 + }
1081 + else if (!strcmp(name, "dimension")) {
1082 + // metric [name [multiplier [divider [options]]]
1083 + char *words[5];
1084 + pluginsd_split_words(value, words, 5);
1085 +
1086 + char *metric_name = words[0];
1087 + char *dim_name = words[1];
1088 + char *multipler = words[2];
1089 + char *divider = words[3];
1090 + char *options = words[4];
1091 +
1092 + STATSD_APP_CHART_DIM *dim = callocz(sizeof(STATSD_APP_CHART_DIM), 1);
1093 + dim->next = chart->dimensions;
1094 + chart->dimensions = dim;
1095 + chart->dimensions_count++;
1096 +
1097 + dim->metric = strdupz(metric_name);
1098 + dim->metric_hash = simple_hash(dim->metric);
1099 +
1100 + dim->name = strdupz((dim_name && *dim_name)?dim_name:metric_name);
1101 + dim->multiplier = (multipler && *multipler)?str2l(multipler):1;
1102 + dim->divider = (divider && *divider)?str2l(divider):1;
1103 + dim->options = (options && *options)?strdupz(options):NULL;
1104 +
1105 + if(!dim->multiplier) {
1106 + error("STATSD: invalid multiplier value '%s' at line %zu of file '%s/%s'. Using 1.", multipler, line, path, filename);
1107 + dim->multiplier = 1;
1108 + }
1109 + if(!dim->divider) {
1110 + error("STATSD: invalid divider value '%s' at line %zu of file '%s/%s'. Using 1.", divider, line, path, filename);
1111 + dim->divider = 1;
1112 + }
1113 + }
1114 + else {
1115 + error("STATSD: ignoring line %zu ('%s') of file '%s/%s'. Unknown keyword for the [%s] section.", line, name, path, filename, chart->id);
1116 + continue;
1117 + }
1118 + }
1119 + }
1120 +
1121 + fclose(fp);
1122 + return 0;
1123 }
1124
1125 static void statsd_readdir(const char *path) {
@@ -961,11 +1170,8 @@ static void statsd_readdir(const char *path) {
1170 // --------------------------------------------------------------------------------------------------------------------
1171 // send metrics to netdata - in private charts - called from the main thread
1172
964 -#define STATSD_CHART_PREFIX "statsd"
965 -#define STATSD_CHART_PRIORITY 90000
966 -
1173 // extract chart type and chart id from metric name
968 -static inline void statsd_get_metric_type_and_id(STATSD_METRIC *m, char *type, char *id, char *defid, size_t len) {
1174 +static inline void statsd_get_metric_type_and_id(STATSD_METRIC *m, char *type, char *id, const char *defid, size_t len) {
1175 char *s;
1176
1177 snprintfz(type, len, "%s_%s_%s", STATSD_CHART_PREFIX, defid, m->name);
@@ -998,7 +1204,7 @@ static inline RRDSET *statsd_private_rrdset_create(
1204 , RRDSET_TYPE chart_type
1205 ) {
1206 RRD_MEMORY_MODE memory_mode = statsd.private_charts_memory_mode;
1001 - long history = statsd.private_charts_history;
1207 + long history = statsd.private_charts_rrd_history_entries;
1208
1209 if(unlikely(statsd.private_charts >= statsd.max_private_charts)) {
1210 debug(D_STATSD, "STATSD: metric '%s' will be charted with memory mode = none, because the maximum number of charts has been reached.", m->name);
@@ -1025,8 +1231,7 @@ static inline RRDSET *statsd_private_rrdset_create(
1231 );
1232 }
1233
1028 -
1029 -static inline void statsd_chart_from_gauge(STATSD_METRIC *m) {
1234 +static inline void statsd_private_chart_gauge(STATSD_METRIC *m) {
1235 if(unlikely(!m->st)) {
1236 char type[RRD_ID_LENGTH_MAX + 1], id[RRD_ID_LENGTH_MAX + 1];
1237 statsd_get_metric_type_and_id(m, type, id, "gauge", RRD_ID_LENGTH_MAX);
@@ -1046,15 +1251,12 @@ static inline void statsd_chart_from_gauge(STATSD_METRIC *m) {
1251 );
1252
1253 m->rd_value = rrddim_add(m->st, "gauge", NULL, 1, 1000, RRD_ALGORITHM_ABSOLUTE);
1254 +
1255 if(m->options & STATSD_METRIC_OPTION_CHART_DIMENSION_COUNT)
1256 m->rd_count = rrddim_add(m->st, "events", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL);
1257 }
1258 else rrdset_next(m->st);
1259
1054 - if(m->count && !m->reset)
1055 - m->last = (collected_number )(m->gauge.value * 1000.0);
1056 -
1057 - m->reset = 1;
1260 rrddim_set_by_pointer(m->st, m->rd_value, m->last);
1261
1262 if(m->rd_count)
@@ -1063,7 +1265,7 @@ static inline void statsd_chart_from_gauge(STATSD_METRIC *m) {
1265 rrdset_done(m->st);
1266 }
1267
1066 -static inline void statsd_chart_from_counter_or_meter(STATSD_METRIC *m, char *dim, char *family) {
1268 +static inline void statsd_private_chart_counter_or_meter(STATSD_METRIC *m, const char *dim, const char *family) {
1269 if(unlikely(!m->st)) {
1270 char type[RRD_ID_LENGTH_MAX + 1], id[RRD_ID_LENGTH_MAX + 1];
1271 statsd_get_metric_type_and_id(m, type, id, dim, RRD_ID_LENGTH_MAX);
@@ -1082,16 +1284,13 @@ static inline void statsd_chart_from_counter_or_meter(STATSD_METRIC *m, char *di
1284 , RRDSET_TYPE_AREA
1285 );
1286
1085 - m->rd_value = rrddim_add(m->st, dim, NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL);
1287 + m->rd_value = rrddim_add(m->st, dim, NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL);
1288 +
1289 if(m->options & STATSD_METRIC_OPTION_CHART_DIMENSION_COUNT)
1290 m->rd_count = rrddim_add(m->st, "events", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL);
1291 }
1292 else rrdset_next(m->st);
1293
1091 - if(m->count && !m->reset)
1092 - m->last = m->counter.value;
1093 -
1094 - m->reset = 1;
1294 rrddim_set_by_pointer(m->st, m->rd_value, m->last);
1295
1296 if(m->rd_count)
@@ -1100,15 +1299,7 @@ static inline void statsd_chart_from_counter_or_meter(STATSD_METRIC *m, char *di
1299 rrdset_done(m->st);
1300 }
1301
1103 -static inline void statsd_chart_from_counter(STATSD_METRIC *m) {
1104 - statsd_chart_from_counter_or_meter(m, "counter", "counters");
1105 -}
1106 -
1107 -static inline void statsd_chart_from_meter(STATSD_METRIC *m) {
1108 - statsd_chart_from_counter_or_meter(m, "meter", "meters");
1109 -}
1110 -
1111 -static inline void statsd_chart_from_set(STATSD_METRIC *m) {
1302 +static inline void statsd_private_chart_set(STATSD_METRIC *m) {
1303 if(unlikely(!m->st)) {
1304 char type[RRD_ID_LENGTH_MAX + 1], id[RRD_ID_LENGTH_MAX + 1];
1305 statsd_get_metric_type_and_id(m, type, id, "set", RRD_ID_LENGTH_MAX);
@@ -1130,14 +1321,10 @@ static inline void statsd_chart_from_set(STATSD_METRIC *m) {
1321 m->rd_value = rrddim_add(m->st, "set", "set size", 1, 1, RRD_ALGORITHM_ABSOLUTE);
1322
1323 if(m->options & STATSD_METRIC_OPTION_CHART_DIMENSION_COUNT)
1133 - m->rd_count = rrddim_add(m->st, "events", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL);
1324 + m->rd_count = rrddim_add(m->st, "events", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL);
1325 }
1326 else rrdset_next(m->st);
1327
1137 - if(m->count && !m->reset)
1138 - m->last = m->set.unique;
1139 -
1140 - m->reset = 1;
1328 rrddim_set_by_pointer(m->st, m->rd_value, m->last);
1329
1330 if(m->rd_count)
@@ -1146,9 +1333,7 @@ static inline void statsd_chart_from_set(STATSD_METRIC *m) {
1333 rrdset_done(m->st);
1334 }
1335
1149 -static inline void statsd_chart_from_timer_or_histogram(STATSD_METRIC *m, char *dim, char *family, char *units) {
1150 - netdata_mutex_lock(&m->histogram.mutex);
1151 -
1336 +static inline void statsd_private_chart_timer_or_histogram(STATSD_METRIC *m, const char *dim, const char *family, const char *units) {
1337 if(unlikely(!m->st)) {
1338 char type[RRD_ID_LENGTH_MAX + 1], id[RRD_ID_LENGTH_MAX + 1];
1339 statsd_get_metric_type_and_id(m, type, id, dim, RRD_ID_LENGTH_MAX);
@@ -1179,6 +1364,76 @@ static inline void statsd_chart_from_timer_or_histogram(STATSD_METRIC *m, char *
1364 }
1365 else rrdset_next(m->st);
1366
1367 + rrddim_set_by_pointer(m->st, m->histogram.ext->rd_min, m->histogram.ext->last_min);
1368 + rrddim_set_by_pointer(m->st, m->histogram.ext->rd_max, m->histogram.ext->last_max);
1369 + rrddim_set_by_pointer(m->st, m->histogram.ext->rd_percentile, m->histogram.ext->last_percentile);
1370 + rrddim_set_by_pointer(m->st, m->histogram.ext->rd_median, m->histogram.ext->last_median);
1371 + rrddim_set_by_pointer(m->st, m->histogram.ext->rd_stddev, m->histogram.ext->last_stddev);
1372 + rrddim_set_by_pointer(m->st, m->rd_value, m->last);
1373 +
1374 + if(m->rd_count)
1375 + rrddim_set_by_pointer(m->st, m->rd_count, (collected_number)m->events);
1376 +
1377 + rrdset_done(m->st);
1378 +}
1379 +
1380 +// --------------------------------------------------------------------------------------------------------------------
1381 +// statsd flush metrics
1382 +
1383 +static inline void statsd_flush_gauge(STATSD_METRIC *m) {
1384 + int updated = 0;
1385 +
1386 + if(m->count && !m->reset)
1387 + m->last = (collected_number )(m->gauge.value * 1000.0);
1388 +
1389 + m->reset = 1;
1390 +
1391 + if(m->options & STATSD_METRIC_OPTION_PRIVATE_CHART_ENABLED && (updated || !(m->options & STATSD_METRIC_OPTION_SHOW_GAPS_WHEN_NOT_COLLECTED)))
1392 + statsd_private_chart_gauge(m);
1393 +}
1394 +
1395 +static inline void statsd_flush_counter_or_meter(STATSD_METRIC *m, const char *dim, const char *family) {
1396 + int updated = 0;
1397 +
1398 + if(m->count && !m->reset) {
1399 + m->last = m->counter.value;
1400 +
1401 + m->reset = 1;
1402 + updated = 1;
1403 + }
1404 +
1405 + if(m->options & STATSD_METRIC_OPTION_PRIVATE_CHART_ENABLED && (updated || !(m->options & STATSD_METRIC_OPTION_SHOW_GAPS_WHEN_NOT_COLLECTED)))
1406 + statsd_private_chart_counter_or_meter(m, dim, family);
1407 +}
1408 +
1409 +
1410 +static inline void statsd_flush_counter(STATSD_METRIC *m) {
1411 + statsd_flush_counter_or_meter(m, "counter", "counters");
1412 +}
1413 +
1414 +static inline void statsd_flush_meter(STATSD_METRIC *m) {
1415 + statsd_flush_counter_or_meter(m, "meter", "meters");
1416 +}
1417 +
1418 +static inline void statsd_flush_set(STATSD_METRIC *m) {
1419 + int updated = 0;
1420 +
1421 + if(m->count && !m->reset) {
1422 + m->last = m->set.unique;
1423 +
1424 + m->reset = 1;
1425 + updated = 1;
1426 + }
1427 +
1428 + if(m->options & STATSD_METRIC_OPTION_PRIVATE_CHART_ENABLED && (updated || !(m->options & STATSD_METRIC_OPTION_SHOW_GAPS_WHEN_NOT_COLLECTED)))
1429 + statsd_private_chart_set(m);
1430 +}
1431 +
1432 +static inline void statsd_flush_timer_or_histogram(STATSD_METRIC *m, const char *dim, const char *family, const char *units) {
1433 + int updated = 0;
1434 +
1435 + netdata_mutex_lock(&m->histogram.mutex);
1436 +
1437 if(m->count && !m->reset) {
1438 size_t len = m->histogram.ext->used;
1439 long double *series = m->histogram.ext->values;
@@ -1190,68 +1445,152 @@ static inline void statsd_chart_from_timer_or_histogram(STATSD_METRIC *m, char *
1445 m->histogram.ext->last_percentile = (collected_number)roundl(average(series, (size_t)floor((double)len * statsd.histogram_percentile / 100.0)) * 1000);
1446 m->histogram.ext->last_median = (collected_number)roundl(median_on_sorted_series(series, len) * 1000);
1447 m->histogram.ext->last_stddev = (collected_number)roundl(standard_deviation(series, len) * 1000);
1193 - }
1448
1195 - m->reset = 1;
1196 - rrddim_set_by_pointer(m->st, m->histogram.ext->rd_min, m->histogram.ext->last_min);
1197 - rrddim_set_by_pointer(m->st, m->histogram.ext->rd_max, m->histogram.ext->last_max);
1198 - rrddim_set_by_pointer(m->st, m->histogram.ext->rd_percentile, m->histogram.ext->last_percentile);
1199 - rrddim_set_by_pointer(m->st, m->histogram.ext->rd_median, m->histogram.ext->last_median);
1200 - rrddim_set_by_pointer(m->st, m->histogram.ext->rd_stddev, m->histogram.ext->last_stddev);
1201 - rrddim_set_by_pointer(m->st, m->rd_value, m->last);
1449 + m->reset = 1;
1450 + updated = 1;
1451 + }
1452
1203 - if(m->rd_count)
1204 - rrddim_set_by_pointer(m->st, m->rd_count, (collected_number)m->events);
1453
1206 - rrdset_done(m->st);
1454 + if(m->options & STATSD_METRIC_OPTION_PRIVATE_CHART_ENABLED && (updated || !(m->options & STATSD_METRIC_OPTION_SHOW_GAPS_WHEN_NOT_COLLECTED)))
1455 + statsd_private_chart_timer_or_histogram(m, dim, family, units);
1456
1457 netdata_mutex_unlock(&m->histogram.mutex);
1458 }
1459
1460 +static inline void statsd_flush_timer(STATSD_METRIC *m) {
1461 + statsd_flush_timer_or_histogram(m, "timer", "timers", "milliseconds");
1462 +}
1463 +
1464 +static inline void statsd_flush_histogram(STATSD_METRIC *m) {
1465 + statsd_flush_timer_or_histogram(m, "histogram", "histograms", "value");
1466 +}
1467 +
1468 +static inline void check_if_metric_is_for_app(STATSD_INDEX *index, STATSD_METRIC *m) {
1469 + (void)index;
1470 +
1471 + STATSD_APP *app;
1472 + for(app = statsd.apps; app ;app = app->next) {
1473 + if(unlikely(simple_pattern_matches(app->metrics, m->name))) {
1474 +
1475 + // the metric should get the options from the app
1476 +
1477 + if(app->default_options & STATSD_METRIC_OPTION_PRIVATE_CHART_ENABLED)
1478 + m->options |= STATSD_METRIC_OPTION_PRIVATE_CHART_ENABLED;
1479 + else
1480 + m->options &= ~STATSD_METRIC_OPTION_PRIVATE_CHART_ENABLED;
1481 +
1482 + if(app->default_options & STATSD_METRIC_OPTION_SHOW_GAPS_WHEN_NOT_COLLECTED)
1483 + m->options |= STATSD_METRIC_OPTION_SHOW_GAPS_WHEN_NOT_COLLECTED;
1484 + else
1485 + m->options &= ~STATSD_METRIC_OPTION_SHOW_GAPS_WHEN_NOT_COLLECTED;
1486 +
1487 + m->options |= STATSD_METRIC_OPTION_PRIVATE_CHART_CHECKED;
1488 +
1489 + // check if there is a chart in this app, willing to get this metric
1490 + STATSD_APP_CHART *chart;
1491 + for(chart = app->charts; chart; chart = chart->next) {
1492 + STATSD_APP_CHART_DIM *dim;
1493 + for(dim = chart->dimensions; dim ; dim = dim->next) {
1494 + if(!dim->m && dim->metric_hash == m->hash && !strcmp(dim->metric, m->name)) {
1495 + // we have a match - this metric should be linked to this dimension
1496 + debug(D_STATSD, "new metric '%s' is now linked with app '%s', chart '%s', dimension '%s'", m->name, app->name, chart->id, dim->name);
1497 + dim->m = m;
1498 + chart->dimensions_linked_count++;
1499 + }
1500 + }
1501 + }
1502 + }
1503 + }
1504 +}
1505 +
1506 +static inline RRD_ALGORITHM statsd_algorithm_for_metric(STATSD_METRIC *m) {
1507 + switch(m->type) {
1508 + default:
1509 + case STATSD_METRIC_TYPE_GAUGE:
1510 + case STATSD_METRIC_TYPE_SET:
1511 + case STATSD_METRIC_TYPE_TIMER:
1512 + case STATSD_METRIC_TYPE_HISTOGRAM:
1513 + return RRD_ALGORITHM_ABSOLUTE;
1514 +
1515 + case STATSD_METRIC_TYPE_METER:
1516 + case STATSD_METRIC_TYPE_COUNTER:
1517 + return RRD_ALGORITHM_INCREMENTAL;
1518 + }
1519 +}
1520 +
1521 +static inline void statsd_update_app_chart(STATSD_APP *app, STATSD_APP_CHART *chart) {
1522 + if(!chart->st) {
1523 + chart->st = rrdset_create_custom(
1524 + localhost
1525 + , app->name
1526 + , chart->id
1527 + , chart->name
1528 + , chart->family
1529 + , chart->context
1530 + , chart->title
1531 + , chart->units
1532 + , chart->priority
1533 + , statsd.update_every
1534 + , chart->chart_type
1535 + , app->rrd_memory_mode
1536 + , app->rrd_history_entries
1537 + );
1538 + }
1539 + else rrdset_next(chart->st);
1540 +
1541 + STATSD_APP_CHART_DIM *dim;
1542 + for(dim = chart->dimensions; dim ;dim = dim->next) {
1543 + if(dim->m) {
1544 + if(unlikely(!dim->rd))
1545 + dim->rd = rrddim_add(chart->st, dim->metric, dim->name, dim->multiplier, dim->divider,statsd_algorithm_for_metric(dim->m));
1546 +
1547 + rrddim_set_by_pointer(chart->st, dim->rd, dim->m->last);
1548 + }
1549 + }
1550
1212 -static inline void statsd_chart_from_timer(STATSD_METRIC *m) {
1213 - statsd_chart_from_timer_or_histogram(m, "timer", "timers", "milliseconds");
1551 + rrdset_done(chart->st);
1552 }
1553
1216 -static inline void statsd_chart_from_histogram(STATSD_METRIC *m) {
1217 - statsd_chart_from_timer_or_histogram(m, "histogram", "histograms", "value");
1554 +static inline void statsd_update_all_app_charts(void) {
1555 + STATSD_APP *app;
1556 + for(app = statsd.apps; app ;app = app->next) {
1557 + STATSD_APP_CHART *chart;
1558 + for(chart = app->charts; chart ;chart = chart->next) {
1559 + if(unlikely(chart->dimensions_linked_count)) {
1560 + statsd_update_app_chart(app, chart);
1561 + }
1562 + }
1563 + }
1564 }
1565
1220 -static inline void statsd_private_charts_from_index(STATSD_INDEX *index, void (*chart_from_metric)(STATSD_METRIC *)) {
1566 +static inline void statsd_flush_index_metrics(STATSD_INDEX *index, void (*flush_metric)(STATSD_METRIC *)) {
1567 STATSD_METRIC *m;
1568 for(m = index->first; m ; m = m->next) {
1223 - if(unlikely(!(m->options & (STATSD_METRIC_OPTION_PRIVATE_CHART_DISABLED|STATSD_METRIC_OPTION_PRIVATE_CHART_ENABLED)))) {
1569 + if(unlikely(!(m->options & STATSD_METRIC_OPTION_CHECKED_IN_APPS))) {
1570 + check_if_metric_is_for_app(index, m);
1571 + m->options |= STATSD_METRIC_OPTION_CHECKED_IN_APPS;
1572 + }
1573 +
1574 + if(unlikely(!(m->options & STATSD_METRIC_OPTION_PRIVATE_CHART_CHECKED))) {
1575 if(statsd.private_charts >= statsd.max_private_charts_hard) {
1576 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);
1577 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);
1227 - m->options |= STATSD_METRIC_OPTION_PRIVATE_CHART_DISABLED;
1578 m->options &= ~STATSD_METRIC_OPTION_PRIVATE_CHART_ENABLED;
1579 }
1580 else {
1581 if (simple_pattern_matches(statsd.charts_for, m->name)) {
1582 debug(D_STATSD, "STATSD: metric '%s' will be charted.", m->name);
1583 m->options |= STATSD_METRIC_OPTION_PRIVATE_CHART_ENABLED;
1234 - m->options &= ~STATSD_METRIC_OPTION_PRIVATE_CHART_DISABLED;
1584 } else {
1585 debug(D_STATSD, "STATSD: metric '%s' will not be charted.", m->name);
1237 - m->options |= STATSD_METRIC_OPTION_PRIVATE_CHART_DISABLED;
1586 m->options &= ~STATSD_METRIC_OPTION_PRIVATE_CHART_ENABLED;
1587 }
1588 }
1241 - }
1589
1243 - // if no values are collected, and we need to show gaps
1244 - if(unlikely((m->reset || !m->count) && m->options & STATSD_METRIC_OPTION_SHOW_GAPS_WHEN_NOT_COLLECTED)) {
1245 - m->reset = 1; // reset it to allow housekeeping
1246 - continue;
1590 + m->options |= STATSD_METRIC_OPTION_PRIVATE_CHART_CHECKED;
1591 }
1592
1249 - if(likely(m->options & STATSD_METRIC_OPTION_PRIVATE_CHART_ENABLED && chart_from_metric))
1250 - // send it to netdata
1251 - chart_from_metric(m);
1252 - else
1253 - // since we not going to send it, reset it to allow housekeeping
1254 - m->reset = 1;
1593 + flush_metric(m);
1594 }
1595 }
1596
@@ -1294,7 +1633,7 @@ void *statsd_main(void *ptr) {
1633 statsd.max_private_charts = (size_t)config_get_number(CONFIG_SECTION_STATSD, "max private charts allowed", (long long)statsd.max_private_charts);
1634 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);
1635 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)));
1297 - statsd.private_charts_history = (int)config_get_number(CONFIG_SECTION_STATSD, "private charts history", default_rrd_history_entries);
1636 + statsd.private_charts_rrd_history_entries = (int)config_get_number(CONFIG_SECTION_STATSD, "private charts history", default_rrd_history_entries);
1637
1638 statsd.histogram_percentile = (double)config_get_float(CONFIG_SECTION_STATSD, "histograms and timers percentile (percentThreshold)", statsd.histogram_percentile);
1639 if(isless(statsd.histogram_percentile, 0) || isgreater(statsd.histogram_percentile, 100)) {
@@ -1345,6 +1684,13 @@ void *statsd_main(void *ptr) {
1684 statsd.threads = 1;
1685 #endif
1686
1687 + // read custom application definitions
1688 + {
1689 + char filename[FILENAME_MAX + 1];
1690 + snprintfz(filename, FILENAME_MAX, "%s/statsd.d", netdata_configured_config_dir);
1691 + statsd_readdir(filename);
1692 + }
1693 +
1694 // ----------------------------------------------------------------------------------------------------------------
1695 // statsd setup
1696
@@ -1482,12 +1828,14 @@ void *statsd_main(void *ptr) {
1828 if(unlikely(netdata_exit))
1829 break;
1830
1485 - statsd_private_charts_from_index(&statsd.gauges, statsd_chart_from_gauge);
1486 - statsd_private_charts_from_index(&statsd.counters, statsd_chart_from_counter);
1487 - statsd_private_charts_from_index(&statsd.meters, statsd_chart_from_meter);
1488 - statsd_private_charts_from_index(&statsd.timers, statsd_chart_from_timer);
1489 - statsd_private_charts_from_index(&statsd.histograms, statsd_chart_from_histogram);
1490 - statsd_private_charts_from_index(&statsd.sets, statsd_chart_from_set);
1831 + statsd_flush_index_metrics(&statsd.gauges, statsd_flush_gauge);
1832 + statsd_flush_index_metrics(&statsd.counters, statsd_flush_counter);
1833 + statsd_flush_index_metrics(&statsd.meters, statsd_flush_meter);
1834 + statsd_flush_index_metrics(&statsd.timers, statsd_flush_timer);
1835 + statsd_flush_index_metrics(&statsd.histograms, statsd_flush_histogram);
1836 + statsd_flush_index_metrics(&statsd.sets, statsd_flush_set);
1837 +
1838 + statsd_update_all_app_charts();
1839
1840 if(unlikely(netdata_exit))
1841 break;
web/infographic.html
+2 -2
@@ -14,7 +14,7 @@
14 <meta property="og:type" content="website" />
15 <meta property="og:title" content="netdata infographic" />
16 <meta property="og:description" content="Unparalleled insights, in real-time, of everything happening on your Linux systems and applications, with stunning, interactive web dashboards and powerful performance and health alarms." />
17 - <meta property="og:image" content="https://cloud.githubusercontent.com/assets/2662304/25578239/484665c0-2e75-11e7-8a79-71e0486951fd.png" />
17 + <meta property="og:image" content="https://cloud.githubusercontent.com/assets/2662304/25580009/bf7016a4-2e85-11e7-9a7a-b36c57db7b91.png" />
18 <meta property="og:image:type" content="image/png" />
19 <meta property="fb:app_id" content="1200089276712916" />
20
@@ -22,7 +22,7 @@
22 <meta name="twitter:site" content="@linuxnetdata" />
23 <meta name="twitter:title" content="netdata infographic" />
24 <meta name="twitter:description" content="Unparalleled insights, in real-time, of everything happening on your Linux systems and applications, with stunning, interactive web dashboards and powerful performance and health alarms." />
25 - <meta name="twitter:image" content="https://cloud.githubusercontent.com/assets/2662304/25578239/484665c0-2e75-11e7-8a79-71e0486951fd.png" />
25 + <meta name="twitter:image" content="https://cloud.githubusercontent.com/assets/2662304/25580009/bf7016a4-2e85-11e7-9a7a-b36c57db7b91.png" />
26
27 <meta name="google-site-verification" content="3Xmk2kyCvai8p9HEnYHoQ9RBW20-b1NvPAgu07Fkkds" />
28 <meta name="msvalidate.01" content="896DCA31C9A664CE359FCF1A645DD476" />