summaryrefslogtreecommitdiffstats
path: root/src/statsd.c
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--src/statsd.c562
1 files changed, 390 insertions, 172 deletions
diff --git a/src/statsd.c b/src/statsd.c
index 08ce3e2f5..39041ca88 100644
--- a/src/statsd.c
+++ b/src/statsd.c
@@ -54,6 +54,8 @@ typedef struct statsd_histogram_extensions {
collected_number last_stddev;
collected_number last_sum;
+ int zeroed;
+
RRDDIM *rd_min;
RRDDIM *rd_max;
RRDDIM *rd_percentile;
@@ -165,18 +167,21 @@ typedef enum statsd_app_chart_dimension_value_type {
} STATSD_APP_CHART_DIM_VALUE_TYPE;
typedef struct statsd_app_chart_dimension {
- const char *name;
- const char *metric;
- uint32_t metric_hash;
- collected_number multiplier;
- collected_number divisor;
- STATSD_APP_CHART_DIM_VALUE_TYPE value_type;
+ const char *name; // the name of this dimension
+ const char *metric; // the source metric name of this dimension
+ uint32_t metric_hash; // hash for fast string comparisons
+
+ SIMPLE_PATTERN *metric_pattern; // set when the 'metric' is a simple pattern
+
+ collected_number multiplier; // the multipler of the dimension
+ collected_number divisor; // the divisor of the dimension
+ STATSD_APP_CHART_DIM_VALUE_TYPE value_type; // which value to use of the source metric
- RRDDIM *rd;
- collected_number *value_ptr;
- RRD_ALGORITHM algorithm;
+ RRDDIM *rd; // a pointer to the RRDDIM that has been created for this dimension
+ collected_number *value_ptr; // a pointer to the source metric value
+ RRD_ALGORITHM algorithm; // the algorithm of this dimension
- struct statsd_app_chart_dimension *next;
+ struct statsd_app_chart_dimension *next; // the next dimension for this chart
} STATSD_APP_CHART_DIM;
typedef struct statsd_app_chart {
@@ -202,6 +207,7 @@ typedef struct statsd_app {
SIMPLE_PATTERN *metrics;
STATS_METRIC_OPTIONS default_options;
RRD_MEMORY_MODE rrd_memory_mode;
+ DICTIONARY *dict;
long rrd_history_entries;
const char *source;
@@ -482,7 +488,7 @@ static inline void statsd_process_histogram(STATSD_METRIC *m, const char *value,
static inline void statsd_process_timer(STATSD_METRIC *m, const char *value, const char *sampling) {
if(unlikely(!value || !*value)) {
- error("STATSD: metric of type set, with empty value is ignored.");
+ error("STATSD: metric of type timer, with empty value is ignored.");
return;
}
@@ -685,8 +691,9 @@ struct statsd_udp {
#endif
// new TCP client connected
-static void *statsd_add_callback(int fd, short int *events) {
+static void *statsd_add_callback(int fd, int socktype, short int *events) {
(void)fd;
+ (void)socktype;
*events = POLLIN;
struct statsd_tcp *data = (struct statsd_tcp *)callocz(sizeof(struct statsd_tcp) + STATSD_TCP_BUFFER_SIZE, 1);
@@ -697,8 +704,9 @@ static void *statsd_add_callback(int fd, short int *events) {
}
// TCP client disconnected
-static void statsd_del_callback(int fd, void *data) {
+static void statsd_del_callback(int fd, int socktype, void *data) {
(void)fd;
+ (void)socktype;
if(data) {
struct statsd_tcp *t = data;
@@ -912,6 +920,7 @@ void *statsd_collector_thread(void *ptr) {
, statsd_del_callback
, statsd_rcv_callback
, statsd_snd_callback
+ , NULL
, (void *)d
);
@@ -928,6 +937,83 @@ void *statsd_collector_thread(void *ptr) {
#define STATSD_CONF_LINE_MAX 8192
+static STATSD_APP_CHART_DIM_VALUE_TYPE string2valuetype(const char *type, size_t line, const char *path, const char *filename) {
+ if(!type || !*type) type = "last";
+
+ if(!strcmp(type, "events")) return STATSD_APP_CHART_DIM_VALUE_TYPE_EVENTS;
+ else if(!strcmp(type, "last")) return STATSD_APP_CHART_DIM_VALUE_TYPE_LAST;
+ else if(!strcmp(type, "min")) return STATSD_APP_CHART_DIM_VALUE_TYPE_MIN;
+ else if(!strcmp(type, "max")) return STATSD_APP_CHART_DIM_VALUE_TYPE_MAX;
+ else if(!strcmp(type, "sum")) return STATSD_APP_CHART_DIM_VALUE_TYPE_SUM;
+ else if(!strcmp(type, "average")) return STATSD_APP_CHART_DIM_VALUE_TYPE_AVERAGE;
+ else if(!strcmp(type, "median")) return STATSD_APP_CHART_DIM_VALUE_TYPE_MEDIAN;
+ else if(!strcmp(type, "stddev")) return STATSD_APP_CHART_DIM_VALUE_TYPE_STDDEV;
+ else if(!strcmp(type, "percentile")) return STATSD_APP_CHART_DIM_VALUE_TYPE_PERCENTILE;
+
+ error("STATSD: invalid type '%s' at line %zu of file '%s/%s'. Using 'last'.", type, line, path, filename);
+ return STATSD_APP_CHART_DIM_VALUE_TYPE_LAST;
+}
+
+static const char *valuetype2string(STATSD_APP_CHART_DIM_VALUE_TYPE type) {
+ switch(type) {
+ case STATSD_APP_CHART_DIM_VALUE_TYPE_EVENTS: return "events";
+ case STATSD_APP_CHART_DIM_VALUE_TYPE_LAST: return "last";
+ case STATSD_APP_CHART_DIM_VALUE_TYPE_MIN: return "min";
+ case STATSD_APP_CHART_DIM_VALUE_TYPE_MAX: return "max";
+ case STATSD_APP_CHART_DIM_VALUE_TYPE_SUM: return "sum";
+ case STATSD_APP_CHART_DIM_VALUE_TYPE_AVERAGE: return "average";
+ case STATSD_APP_CHART_DIM_VALUE_TYPE_MEDIAN: return "median";
+ case STATSD_APP_CHART_DIM_VALUE_TYPE_STDDEV: return "stddev";
+ case STATSD_APP_CHART_DIM_VALUE_TYPE_PERCENTILE: return "percentile";
+ }
+
+ return "unknown";
+}
+
+static STATSD_APP_CHART_DIM *add_dimension_to_app_chart(
+ STATSD_APP *app
+ , STATSD_APP_CHART *chart
+ , const char *metric_name
+ , const char *dim_name
+ , collected_number multiplier
+ , collected_number divisor
+ , STATSD_APP_CHART_DIM_VALUE_TYPE value_type
+) {
+ STATSD_APP_CHART_DIM *dim = callocz(sizeof(STATSD_APP_CHART_DIM), 1);
+
+ dim->metric = strdupz(metric_name);
+ dim->metric_hash = simple_hash(dim->metric);
+
+ dim->name = strdupz((dim_name)?dim_name:"");
+ dim->multiplier = multiplier;
+ dim->divisor = divisor;
+ dim->value_type = value_type;
+
+ if(!dim->multiplier)
+ dim->multiplier = 1;
+
+ if(!dim->divisor)
+ dim->divisor = 1;
+
+ // append it to the list of dimension
+ STATSD_APP_CHART_DIM *tdim;
+ for(tdim = chart->dimensions; tdim && tdim->next ; tdim = tdim->next) ;
+ if(!tdim) {
+ dim->next = chart->dimensions;
+ chart->dimensions = dim;
+ }
+ else {
+ dim->next = tdim->next;
+ tdim->next = dim;
+ }
+ chart->dimensions_count++;
+
+ debug(D_STATSD, "Added dimension '%s' to chart '%s' of app '%s', for metric '%s', with type %u, multiplier " COLLECTED_NUMBER_FORMAT ", divisor " COLLECTED_NUMBER_FORMAT,
+ dim->name, chart->id, app->name, dim->metric, dim->value_type, dim->multiplier, dim->divisor);
+
+ return dim;
+}
+
int statsd_readfile(const char *path, const char *filename) {
debug(D_STATSD, "STATSD configuration reading file '%s/%s'", path, filename);
@@ -943,6 +1029,7 @@ int statsd_readfile(const char *path, const char *filename) {
STATSD_APP *app = NULL;
STATSD_APP_CHART *chart = NULL;
+ DICTIONARY *dict = NULL;
size_t line = 0;
char *s;
@@ -973,22 +1060,33 @@ int statsd_readfile(const char *path, const char *filename) {
app->next = statsd.apps;
statsd.apps = app;
chart = NULL;
+ dict = NULL;
}
else if(app) {
- // a new chart
- chart = callocz(sizeof(STATSD_APP_CHART), 1);
- netdata_fix_chart_id(s);
- chart->id = strdupz(s);
- chart->name = strdupz(s);
- chart->title = strdupz("Statsd chart");
- chart->context = strdupz(s);
- chart->family = strdupz("overview");
- chart->units = strdupz("value");
- chart->priority = STATSD_CHART_PRIORITY;
- chart->chart_type = RRDSET_TYPE_LINE;
-
- chart->next = app->charts;
- app->charts = chart;
+ if(!strcmp(s, "dictionary")) {
+ if(!app->dict)
+ app->dict = dictionary_create(DICTIONARY_FLAG_SINGLE_THREADED);
+
+ dict = app->dict;
+ }
+ else {
+ dict = NULL;
+
+ // a new chart
+ chart = callocz(sizeof(STATSD_APP_CHART), 1);
+ netdata_fix_chart_id(s);
+ chart->id = strdupz(s);
+ chart->name = strdupz(s);
+ chart->title = strdupz("Statsd chart");
+ chart->context = strdupz(s);
+ chart->family = strdupz("overview");
+ chart->units = strdupz("value");
+ chart->priority = STATSD_CHART_PRIORITY;
+ chart->chart_type = RRDSET_TYPE_LINE;
+
+ chart->next = app->charts;
+ app->charts = chart;
+ }
}
else
error("STATSD: ignoring line %zu ('%s') of file '%s/%s', [app] is not defined.", line, s, path, filename);
@@ -1022,7 +1120,14 @@ int statsd_readfile(const char *path, const char *filename) {
continue;
}
- if(!chart) {
+ if(unlikely(dict)) {
+ // parse [dictionary] members
+
+ dictionary_set(dict, name, value, strlen(value) + 1);
+ }
+ else if(!chart) {
+ // parse [app] members
+
if(!strcmp(name, "name")) {
freez((void *)app->name);
netdata_fix_chart_name(value);
@@ -1056,6 +1161,8 @@ int statsd_readfile(const char *path, const char *filename) {
}
}
else {
+ // parse [chart] members
+
if(!strcmp(name, "name")) {
freez((void *)chart->name);
netdata_fix_chart_id(value);
@@ -1086,63 +1193,50 @@ int statsd_readfile(const char *path, const char *filename) {
}
else if (!strcmp(name, "dimension")) {
// metric [name [type [multiplier [divisor]]]]
- char *words[5];
- pluginsd_split_words(value, words, 5);
-
- char *metric_name = words[0];
- char *dim_name = words[1];
- char *type = words[2];
- char *multipler = words[3];
- char *divisor = words[4];
-
- STATSD_APP_CHART_DIM *dim = callocz(sizeof(STATSD_APP_CHART_DIM), 1);
-
- dim->metric = strdupz(metric_name);
- dim->metric_hash = simple_hash(dim->metric);
-
- dim->name = strdupz((dim_name && *dim_name)?dim_name:metric_name);
- dim->multiplier = (multipler && *multipler)?str2l(multipler):1;
- dim->divisor = (divisor && *divisor)?str2l(divisor):1;
-
- if(!type || !*type) type = "last";
- if(!strcmp(type, "events")) dim->value_type = STATSD_APP_CHART_DIM_VALUE_TYPE_EVENTS;
- else if(!strcmp(type, "last")) dim->value_type = STATSD_APP_CHART_DIM_VALUE_TYPE_LAST;
- else if(!strcmp(type, "min")) dim->value_type = STATSD_APP_CHART_DIM_VALUE_TYPE_MIN;
- else if(!strcmp(type, "max")) dim->value_type = STATSD_APP_CHART_DIM_VALUE_TYPE_MAX;
- else if(!strcmp(type, "sum")) dim->value_type = STATSD_APP_CHART_DIM_VALUE_TYPE_SUM;
- else if(!strcmp(type, "average")) dim->value_type = STATSD_APP_CHART_DIM_VALUE_TYPE_AVERAGE;
- else if(!strcmp(type, "median")) dim->value_type = STATSD_APP_CHART_DIM_VALUE_TYPE_MEDIAN;
- else if(!strcmp(type, "stddev")) dim->value_type = STATSD_APP_CHART_DIM_VALUE_TYPE_STDDEV;
- else if(!strcmp(type, "percentile")) dim->value_type = STATSD_APP_CHART_DIM_VALUE_TYPE_PERCENTILE;
- else {
- error("STATSD: invalid type '%s' at line %zu of file '%s/%s'. Using 'last'.", type, line, path, filename);
- dim->value_type = STATSD_APP_CHART_DIM_VALUE_TYPE_LAST;
- }
+ char *words[10];
+ pluginsd_split_words(value, words, 10);
- if(!dim->multiplier) {
- error("STATSD: invalid multiplier value '%s' at line %zu of file '%s/%s'. Using 1.", multipler, line, path, filename);
- dim->multiplier = 1;
- }
- if(!dim->divisor) {
- error("STATSD: invalid divisor value '%s' at line %zu of file '%s/%s'. Using 1.", divisor, line, path, filename);
- dim->divisor = 1;
- }
+ int pattern = 0;
+ size_t i = 0;
+ char *metric_name = words[i++];
- // append it to the list of dimension
- STATSD_APP_CHART_DIM *tdim;
- for(tdim = chart->dimensions; tdim && tdim->next ; tdim = tdim->next) ;
- if(!tdim) {
- dim->next = chart->dimensions;
- chart->dimensions = dim;
+ if(strcmp(metric_name, "pattern") == 0) {
+ metric_name = words[i++];
+ pattern = 1;
}
- else {
- dim->next = tdim->next;
- tdim->next = dim;
+
+ char *dim_name = words[i++];
+ char *type = words[i++];
+ char *multipler = words[i++];
+ char *divisor = words[i++];
+
+ if(!pattern) {
+ if(app->dict) {
+ if(dim_name && *dim_name) {
+ char *n = dictionary_get(app->dict, dim_name);
+ if(n) dim_name = n;
+ }
+ else {
+ dim_name = dictionary_get(app->dict, metric_name);
+ }
+ }
+
+ if(!dim_name || !*dim_name)
+ dim_name = metric_name;
}
- chart->dimensions_count++;
- debug(D_STATSD, "Added dimension '%s' to chart '%s' of app '%s', for metric '%s', with type %u, multiplier " COLLECTED_NUMBER_FORMAT ", divisor " COLLECTED_NUMBER_FORMAT,
- dim->name, chart->id, app->name, dim->metric, dim->value_type, dim->multiplier, dim->divisor);
+ STATSD_APP_CHART_DIM *dim = add_dimension_to_app_chart(
+ app
+ , chart
+ , metric_name
+ , dim_name
+ , (multipler && *multipler)?str2l(multipler):1
+ , (divisor && *divisor)?str2l(divisor):1
+ , string2valuetype(type, line, path, filename)
+ );
+
+ if(pattern)
+ dim->metric_pattern = simple_pattern_create(dim->metric, SIMPLE_PATTERN_EXACT);
}
else {
error("STATSD: ignoring line %zu ('%s') of file '%s/%s'. Unknown keyword for the [%s] section.", line, name, path, filename, chart->id);
@@ -1248,19 +1342,21 @@ static inline RRDSET *statsd_private_rrdset_create(
statsd.private_charts++;
RRDSET *st = rrdset_create_custom(
- localhost
- , type
- , id
- , name
- , family
- , context
- , title
- , units
- , priority
- , update_every
- , chart_type
- , memory_mode
- , history
+ localhost // host
+ , type // type
+ , id // id
+ , name // name
+ , family // family
+ , context // context
+ , title // title
+ , units // units
+ , "statsd" // plugin
+ , NULL // module
+ , priority // priority
+ , update_every // update every
+ , chart_type // chart type
+ , memory_mode // memory mode
+ , history // history
);
rrdset_flag_set(st, RRDSET_FLAG_STORE_FIRST);
// rrdset_flag_set(st, RRDSET_FLAG_DEBUG);
@@ -1484,6 +1580,22 @@ static inline void statsd_flush_timer_or_histogram(STATSD_METRIC *m, const char
netdata_mutex_lock(&m->histogram.ext->mutex);
+ if(unlikely(!m->histogram.ext->zeroed)) {
+ // reset the metrics
+ // if we collected anything, they will be updated below
+ // this ensures that we report zeros if nothing is collected
+
+ m->histogram.ext->last_min = 0;
+ m->histogram.ext->last_max = 0;
+ m->last = 0;
+ m->histogram.ext->last_median = 0;
+ m->histogram.ext->last_stddev = 0;
+ m->histogram.ext->last_sum = 0;
+ m->histogram.ext->last_percentile = 0;
+
+ m->histogram.ext->zeroed = 1;
+ }
+
int updated = 0;
if(m->count && !m->reset && m->histogram.ext->used > 0) {
size_t len = m->histogram.ext->used;
@@ -1506,11 +1618,11 @@ static inline void statsd_flush_timer_or_histogram(STATSD_METRIC *m, const char
debug(D_STATSD, "STATSD %s metric %s: min " COLLECTED_NUMBER_FORMAT ", max " COLLECTED_NUMBER_FORMAT ", last " COLLECTED_NUMBER_FORMAT ", pcent " COLLECTED_NUMBER_FORMAT ", median " COLLECTED_NUMBER_FORMAT ", stddev " COLLECTED_NUMBER_FORMAT ", sum " COLLECTED_NUMBER_FORMAT,
dim, m->name, m->histogram.ext->last_min, m->histogram.ext->last_max, m->last, m->histogram.ext->last_percentile, m->histogram.ext->last_median, m->histogram.ext->last_stddev, m->histogram.ext->last_sum);
+ m->histogram.ext->zeroed = 0;
m->reset = 1;
updated = 1;
}
-
if(m->options & STATSD_METRIC_OPTION_PRIVATE_CHART_ENABLED && (updated || !(m->options & STATSD_METRIC_OPTION_SHOW_GAPS_WHEN_NOT_COLLECTED)))
statsd_private_chart_timer_or_histogram(m, dim, family, units);
@@ -1540,6 +1652,71 @@ static inline RRD_ALGORITHM statsd_algorithm_for_metric(STATSD_METRIC *m) {
}
}
+static inline void link_metric_to_app_dimension(STATSD_APP *app, STATSD_METRIC *m, STATSD_APP_CHART *chart, STATSD_APP_CHART_DIM *dim) {
+ if(dim->value_type == STATSD_APP_CHART_DIM_VALUE_TYPE_EVENTS) {
+ dim->value_ptr = &m->events;
+ dim->algorithm = RRD_ALGORITHM_INCREMENTAL;
+ }
+ else if(m->type == STATSD_METRIC_TYPE_HISTOGRAM || m->type == STATSD_METRIC_TYPE_TIMER) {
+ dim->algorithm = RRD_ALGORITHM_ABSOLUTE;
+ dim->divisor *= statsd.decimal_detail;
+
+ switch(dim->value_type) {
+ case STATSD_APP_CHART_DIM_VALUE_TYPE_EVENTS:
+ // will never match - added to avoid warning
+ break;
+
+ case STATSD_APP_CHART_DIM_VALUE_TYPE_LAST:
+ case STATSD_APP_CHART_DIM_VALUE_TYPE_AVERAGE:
+ dim->value_ptr = &m->last;
+ break;
+
+ case STATSD_APP_CHART_DIM_VALUE_TYPE_SUM:
+ dim->value_ptr = &m->histogram.ext->last_sum;
+ break;
+
+ case STATSD_APP_CHART_DIM_VALUE_TYPE_MIN:
+ dim->value_ptr = &m->histogram.ext->last_min;
+ break;
+
+ case STATSD_APP_CHART_DIM_VALUE_TYPE_MAX:
+ dim->value_ptr = &m->histogram.ext->last_max;
+ break;
+
+ case STATSD_APP_CHART_DIM_VALUE_TYPE_MEDIAN:
+ dim->value_ptr = &m->histogram.ext->last_median;
+ break;
+
+ case STATSD_APP_CHART_DIM_VALUE_TYPE_PERCENTILE:
+ dim->value_ptr = &m->histogram.ext->last_percentile;
+ break;
+
+ case STATSD_APP_CHART_DIM_VALUE_TYPE_STDDEV:
+ dim->value_ptr = &m->histogram.ext->last_stddev;
+ break;
+ }
+ }
+ else {
+ if (dim->value_type != STATSD_APP_CHART_DIM_VALUE_TYPE_LAST)
+ error("STATSD: unsupported value type for dimension '%s' of chart '%s' of app '%s' on metric '%s'", dim->name, chart->id, app->name, m->name);
+
+ dim->value_ptr = &m->last;
+ dim->algorithm = statsd_algorithm_for_metric(m);
+
+ if(m->type == STATSD_METRIC_TYPE_GAUGE)
+ dim->divisor *= statsd.decimal_detail;
+ }
+
+ if(unlikely(chart->st && dim->rd)) {
+ rrddim_set_algorithm(chart->st, dim->rd, dim->algorithm);
+ rrddim_set_multiplier(chart->st, dim->rd, dim->multiplier);
+ rrddim_set_divisor(chart->st, dim->rd, dim->divisor);
+ }
+
+ chart->dimensions_linked_count++;
+ debug(D_STATSD, "metric '%s' of type %u linked with app '%s', chart '%s', dimension '%s', algorithm '%s'", m->name, m->type, app->name, chart->id, dim->name, rrd_algorithm_name(dim->algorithm));
+}
+
static inline void check_if_metric_is_for_app(STATSD_INDEX *index, STATSD_METRIC *m) {
(void)index;
@@ -1565,78 +1742,103 @@ static inline void check_if_metric_is_for_app(STATSD_INDEX *index, STATSD_METRIC
// check if there is a chart in this app, willing to get this metric
STATSD_APP_CHART *chart;
for(chart = app->charts; chart; chart = chart->next) {
+
STATSD_APP_CHART_DIM *dim;
for(dim = chart->dimensions; dim ; dim = dim->next) {
- if(!dim->value_ptr && dim->metric_hash == m->hash && !strcmp(dim->metric, m->name)) {
- // we have a match - this metric should be linked to this dimension
+ if(unlikely(dim->metric_pattern)) {
+ size_t dim_name_len = strlen(dim->name);
+ size_t wildcarded_len = dim_name_len + strlen(m->name) + 1;
+ char wildcarded[wildcarded_len];
- if(dim->value_type == STATSD_APP_CHART_DIM_VALUE_TYPE_EVENTS) {
- dim->value_ptr = &m->events;
- dim->algorithm = RRD_ALGORITHM_INCREMENTAL;
- }
- else if(m->type == STATSD_METRIC_TYPE_HISTOGRAM || m->type == STATSD_METRIC_TYPE_TIMER) {
- dim->algorithm = RRD_ALGORITHM_ABSOLUTE;
- dim->divisor *= statsd.decimal_detail;
-
- switch(dim->value_type) {
- case STATSD_APP_CHART_DIM_VALUE_TYPE_EVENTS:
- // will never match - added to avoid warning
- break;
-
- case STATSD_APP_CHART_DIM_VALUE_TYPE_LAST:
- case STATSD_APP_CHART_DIM_VALUE_TYPE_AVERAGE:
- dim->value_ptr = &m->last;
- break;
-
- case STATSD_APP_CHART_DIM_VALUE_TYPE_SUM:
- dim->value_ptr = &m->histogram.ext->last_sum;
- break;
-
- case STATSD_APP_CHART_DIM_VALUE_TYPE_MIN:
- dim->value_ptr = &m->histogram.ext->last_min;
- break;
-
- case STATSD_APP_CHART_DIM_VALUE_TYPE_MAX:
- dim->value_ptr = &m->histogram.ext->last_max;
- break;
-
- case STATSD_APP_CHART_DIM_VALUE_TYPE_MEDIAN:
- dim->value_ptr = &m->histogram.ext->last_median;
- break;
-
- case STATSD_APP_CHART_DIM_VALUE_TYPE_PERCENTILE:
- dim->value_ptr = &m->histogram.ext->last_percentile;
- break;
-
- case STATSD_APP_CHART_DIM_VALUE_TYPE_STDDEV:
- dim->value_ptr = &m->histogram.ext->last_stddev;
- break;
- }
- }
- else {
- if (dim->value_type != STATSD_APP_CHART_DIM_VALUE_TYPE_LAST)
- error("STATSD: unsupported value type for dimension '%s' of chart '%s' of app '%s' on metric '%s'", dim->name, chart->id, app->name, m->name);
+ strcpy(wildcarded, dim->name);
+ char *ws = &wildcarded[dim_name_len];
- dim->value_ptr = &m->last;
- dim->algorithm = statsd_algorithm_for_metric(m);
+ if(simple_pattern_matches_extract(dim->metric_pattern, m->name, ws, wildcarded_len - dim_name_len)) {
- if(m->type == STATSD_METRIC_TYPE_GAUGE)
- dim->divisor *= statsd.decimal_detail;
- }
+ char *final_name = NULL;
- if(unlikely(chart->st && dim->rd)) {
- rrddim_set_algorithm(chart->st, dim->rd, dim->algorithm);
- rrddim_set_multiplier(chart->st, dim->rd, dim->multiplier);
- rrddim_set_divisor(chart->st, dim->rd, dim->divisor);
- }
+ if(app->dict) {
+ if(likely(*wildcarded)) {
+ // use the name of the wildcarded string
+ final_name = dictionary_get(app->dict, wildcarded);
+ }
+
+ if(unlikely(!final_name)) {
+ // use the name of the metric
+ final_name = dictionary_get(app->dict, m->name);
+ }
+ }
- chart->dimensions_linked_count++;
- debug(D_STATSD, "metric '%s' of type %u linked with app '%s', chart '%s', dimension '%s', algorithm '%s'", m->name, m->type, app->name, chart->id, dim->name, rrd_algorithm_name(dim->algorithm));
+ if(unlikely(!final_name))
+ final_name = wildcarded;
+
+ add_dimension_to_app_chart(
+ app
+ , chart
+ , m->name
+ , final_name
+ , dim->multiplier
+ , dim->divisor
+ , dim->value_type
+ );
+
+ // the new dimension is appended to the list
+ // so, it will be matched and linked later too
+ }
+ }
+ else if(!dim->value_ptr && dim->metric_hash == m->hash && !strcmp(dim->metric, m->name)) {
+ // we have a match - this metric should be linked to this dimension
+ link_metric_to_app_dimension(app, m, chart, dim);
}
}
+
+ }
+ }
+ }
+}
+
+static inline RRDDIM *statsd_add_dim_to_app_chart(STATSD_APP *app, STATSD_APP_CHART *chart, STATSD_APP_CHART_DIM *dim) {
+ (void)app;
+
+ // allow the same statsd metric to be added multiple times to the same chart
+
+ STATSD_APP_CHART_DIM *tdim;
+ size_t count_same_metric = 0, count_same_metric_value_type = 0;
+ size_t pos_same_metric_value_type = 0;
+
+ for (tdim = chart->dimensions; tdim && tdim->next; tdim = tdim->next) {
+ if (dim->metric_hash == tdim->metric_hash && !strcmp(dim->metric, tdim->metric)) {
+ count_same_metric++;
+
+ if(dim->value_type == tdim->value_type) {
+ count_same_metric_value_type++;
+ if (tdim == dim)
+ pos_same_metric_value_type = count_same_metric_value_type;
}
}
}
+
+ if(count_same_metric > 1) {
+ // the same metric is found multiple times
+
+ size_t len = strlen(dim->metric) + 100;
+ char metric[ len + 1 ];
+
+ if(count_same_metric_value_type > 1) {
+ // the same metric, with the same value type, is added multiple times
+ snprintfz(metric, len, "%s_%s%zu", dim->metric, valuetype2string(dim->value_type), pos_same_metric_value_type);
+ }
+ else {
+ // the same metric, with different value type is added
+ snprintfz(metric, len, "%s_%s", dim->metric, valuetype2string(dim->value_type));
+ }
+
+ dim->rd = rrddim_add(chart->st, metric, dim->name, dim->multiplier, dim->divisor, dim->algorithm);
+ return dim->rd;
+ }
+
+ dim->rd = rrddim_add(chart->st, dim->metric, dim->name, dim->multiplier, dim->divisor, dim->algorithm);
+ return dim->rd;
}
static inline void statsd_update_app_chart(STATSD_APP *app, STATSD_APP_CHART *chart) {
@@ -1644,19 +1846,21 @@ static inline void statsd_update_app_chart(STATSD_APP *app, STATSD_APP_CHART *ch
if(!chart->st) {
chart->st = rrdset_create_custom(
- localhost
- , app->name
- , chart->id
- , chart->name
- , chart->family
- , chart->context
- , chart->title
- , chart->units
- , chart->priority
- , statsd.update_every
- , chart->chart_type
- , app->rrd_memory_mode
- , app->rrd_history_entries
+ localhost // host
+ , app->name // type
+ , chart->id // id
+ , chart->name // name
+ , chart->family // family
+ , chart->context // context
+ , chart->title // title
+ , chart->units // units
+ , "statsd" // plugin
+ , NULL // module
+ , chart->priority // priority
+ , statsd.update_every // update every
+ , chart->chart_type // chart type
+ , app->rrd_memory_mode // memory mode
+ , app->rrd_history_entries // history
);
rrdset_flag_set(chart->st, RRDSET_FLAG_STORE_FIRST);
@@ -1666,12 +1870,14 @@ static inline void statsd_update_app_chart(STATSD_APP *app, STATSD_APP_CHART *ch
STATSD_APP_CHART_DIM *dim;
for(dim = chart->dimensions; dim ;dim = dim->next) {
- if(unlikely(!dim->rd))
- dim->rd = rrddim_add(chart->st, dim->name, NULL, dim->multiplier, dim->divisor, dim->algorithm);
+ if(likely(!dim->metric_pattern)) {
+ if (unlikely(!dim->rd))
+ statsd_add_dim_to_app_chart(app, chart, dim);
- if(unlikely(dim->value_ptr)) {
- debug(D_STATSD, "updating dimension '%s' (%s) of chart '%s' (%s) for app '%s' with value " COLLECTED_NUMBER_FORMAT, dim->name, dim->rd->id, chart->id, chart->st->id, app->name, *dim->value_ptr);
- rrddim_set_by_pointer(chart->st, dim->rd, *dim->value_ptr);
+ if (unlikely(dim->value_ptr)) {
+ debug(D_STATSD, "updating dimension '%s' (%s) of chart '%s' (%s) for app '%s' with value " COLLECTED_NUMBER_FORMAT, dim->name, dim->rd->id, chart->id, chart->st->id, app->name, *dim->value_ptr);
+ rrddim_set_by_pointer(chart->st, dim->rd, *dim->value_ptr);
+ }
}
}
@@ -1871,6 +2077,8 @@ void *statsd_main(void *ptr) {
, NULL
, "Metrics in the netdata statsd database"
, "metrics"
+ , "netdata"
+ , "stats"
, 132000
, statsd.update_every
, RRDSET_TYPE_STACKED
@@ -1890,6 +2098,8 @@ void *statsd_main(void *ptr) {
, NULL
, "Events processed by the netdata statsd server"
, "events/s"
+ , "netdata"
+ , "stats"
, 132001
, statsd.update_every
, RRDSET_TYPE_STACKED
@@ -1911,6 +2121,8 @@ void *statsd_main(void *ptr) {
, NULL
, "Read operations made by the netdata statsd server"
, "reads/s"
+ , "netdata"
+ , "stats"
, 132002
, statsd.update_every
, RRDSET_TYPE_STACKED
@@ -1926,6 +2138,8 @@ void *statsd_main(void *ptr) {
, NULL
, "Bytes read by the netdata statsd server"
, "kilobits/s"
+ , "netdata"
+ , "stats"
, 132003
, statsd.update_every
, RRDSET_TYPE_STACKED
@@ -1941,6 +2155,8 @@ void *statsd_main(void *ptr) {
, NULL
, "Network packets processed by the netdata statsd server"
, "packets/s"
+ , "netdata"
+ , "stats"
, 132004
, statsd.update_every
, RRDSET_TYPE_STACKED
@@ -1956,6 +2172,8 @@ void *statsd_main(void *ptr) {
, NULL
, "Private metric charts created by the netdata statsd server"
, "charts"
+ , "netdata"
+ , "stats"
, 132010
, statsd.update_every
, RRDSET_TYPE_AREA