summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorCosta Tsaousis <costa@tsaousis.gr>2018-04-17 17:41:44 +0300
committerGitHub <noreply@github.com>2018-04-17 17:41:44 +0300
commitb6b2812eee2b70e2fe25134e8dac54242603b74f (patch)
tree3f050157cc061d272dec291ff24ed19d135f39f0
parentc644813e4756ef014bdf15339c4ba5a4b0445c61 (diff)
parente447276bccf331aa09677706af23d64374b4c82d (diff)
Merge pull request #3633 from ktsaou/master
statsd improvements
-rw-r--r--src/statsd.c159
1 files changed, 110 insertions, 49 deletions
diff --git a/src/statsd.c b/src/statsd.c
index 44ebd88947..a0a63c1f49 100644
--- a/src/statsd.c
+++ b/src/statsd.c
@@ -87,7 +87,10 @@ typedef enum statsd_metric_options {
STATSD_METRIC_OPTION_PRIVATE_CHART_ENABLED = 0x00000002, // render a private chart for this metric
STATSD_METRIC_OPTION_PRIVATE_CHART_CHECKED = 0x00000004, // the metric has been checked if it should get private chart or not
STATSD_METRIC_OPTION_CHART_DIMENSION_COUNT = 0x00000008, // show the count of events for this private chart
- STATSD_METRIC_OPTION_CHECKED_IN_APPS = 0x00000010, // set when this metric has been checked agains apps
+ STATSD_METRIC_OPTION_CHECKED_IN_APPS = 0x00000010, // set when this metric has been checked against apps
+ STATSD_METRIC_OPTION_USED_IN_APPS = 0x00000020, // set when this metric is used in apps
+ STATSD_METRIC_OPTION_CHECKED = 0x00000040, // set when the charting thread checks this metric for use in charts (its usefulness)
+ STATSD_METRIC_OPTION_USEFUL = 0x00000080, // set when the charting thread finds the metric useful (i.e. used in a chart)
} STATS_METRIC_OPTIONS;
typedef enum statsd_metric_type {
@@ -101,7 +104,7 @@ typedef enum statsd_metric_type {
typedef struct statsd_metric {
- avl avl; // indexing
+ avl avl; // indexing - has to be first
const char *name; // the name of the metric
uint32_t hash; // hash of the name
@@ -122,14 +125,15 @@ typedef struct statsd_metric {
// chart related members
STATS_METRIC_OPTIONS options; // STATSD_METRIC_OPTION_* (bitfield)
- char reset; // set to 1 to reset this metric to zero
+ char reset; // set to 1 by the charting thread to instruct the collector thread(s) to reset this metric
collected_number last; // the last value sent to netdata
- RRDSET *st; // the chart of this metric
+ RRDSET *st; // the private chart of this metric
RRDDIM *rd_value; // the dimension of this metric value
RRDDIM *rd_count; // the dimension for the number of events received
// linking, used for walking through all metrics
struct statsd_metric *next;
+ struct statsd_metric *next_useful;
} STATSD_METRIC;
@@ -140,10 +144,12 @@ typedef struct statsd_index {
char *name; // the name of the index of metrics
size_t events; // the number of events processed for this index
size_t metrics; // the number of metrics in this index
+ size_t useful; // the number of useful metrics in this index
STATSD_AVL_TREE index; // the AVL tree
STATSD_METRIC *first; // the linked list of metrics (new metrics are added in front)
+ STATSD_METRIC *first_useful; // the linked list of useful metrics (new metrics are added in front)
STATSD_FIRST_PTR_MUTEX; // when mutli-threading is enabled, a lock to protect the linked list
STATS_METRIC_OPTIONS default_options; // default options for all metrics in this index
@@ -455,7 +461,12 @@ static inline int value_is_zinit(const char *value) {
return (value && *value == 'z' && *++value == 'i' && *++value == 'n' && *++value == 'i' && *++value == 't' && *++value == '\0');
}
+#define is_metric_checked(m) ((m)->options & STATSD_METRIC_OPTION_CHECKED)
+#define is_metric_useful_for_collection(m) (!is_metric_checked(m) || ((m)->options & STATSD_METRIC_OPTION_USEFUL))
+
static inline void statsd_process_gauge(STATSD_METRIC *m, const char *value, const char *sampling) {
+ if(!is_metric_useful_for_collection(m)) return;
+
if(unlikely(!value || !*value)) {
error("STATSD: metric '%s' of type gauge, with empty value is ignored.", m->name);
return;
@@ -480,7 +491,9 @@ static inline void statsd_process_gauge(STATSD_METRIC *m, const char *value, con
}
}
-static inline void statsd_process_counter(STATSD_METRIC *m, const char *value, const char *sampling) {
+static inline void statsd_process_counter_or_meter(STATSD_METRIC *m, const char *value, const char *sampling) {
+ if(!is_metric_useful_for_collection(m)) return;
+
// we accept empty values for counters
if(unlikely(m->reset)) statsd_reset_metric(m);
@@ -496,14 +509,14 @@ static inline void statsd_process_counter(STATSD_METRIC *m, const char *value, c
}
}
-static inline void statsd_process_meter(STATSD_METRIC *m, const char *value, const char *sampling) {
- // this is the same with the counter
- statsd_process_counter(m, value, sampling);
-}
+#define statsd_process_counter(m, value, sampling) statsd_process_counter_or_meter(m, value, sampling)
+#define statsd_process_meter(m, value, sampling) statsd_process_counter_or_meter(m, value, sampling)
+
+static inline void statsd_process_histogram_or_timer(STATSD_METRIC *m, const char *value, const char *sampling, const char *type) {
+ if(!is_metric_useful_for_collection(m)) return;
-static inline void statsd_process_histogram(STATSD_METRIC *m, const char *value, const char *sampling) {
if(unlikely(!value || !*value)) {
- error("STATSD: metric '%s' of type histogram, with empty value is ignored.", m->name);
+ error("STATSD: metric of type %s, with empty value is ignored.", type);
return;
}
@@ -530,17 +543,12 @@ 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 timer, with empty value is ignored.");
- return;
- }
-
- // timers are a use case of histogram
- statsd_process_histogram(m, value, sampling);
-}
+#define statsd_process_timer(m, value, sampling) statsd_process_histogram_or_timer(m, value, sampling, "timer")
+#define statsd_process_histogram(m, value, sampling) statsd_process_histogram_or_timer(m, value, sampling, "histogram")
static inline void statsd_process_set(STATSD_METRIC *m, const char *value) {
+ if(!is_metric_useful_for_collection(m)) return;
+
if(unlikely(!value || !*value)) {
error("STATSD: metric of type set, with empty value is ignored.");
return;
@@ -1649,14 +1657,14 @@ static inline void statsd_flush_gauge(STATSD_METRIC *m) {
debug(D_STATSD, "flushing gauge metric '%s'", m->name);
int updated = 0;
- if(m->count && !m->reset) {
+ if(unlikely(!m->reset && m->count)) {
m->last = (collected_number) (m->gauge.value * statsd.decimal_detail);
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)))
+ if(unlikely(m->options & STATSD_METRIC_OPTION_PRIVATE_CHART_ENABLED && (updated || !(m->options & STATSD_METRIC_OPTION_SHOW_GAPS_WHEN_NOT_COLLECTED))))
statsd_private_chart_gauge(m);
}
@@ -1664,14 +1672,14 @@ static inline void statsd_flush_counter_or_meter(STATSD_METRIC *m, const char *d
debug(D_STATSD, "flushing %s metric '%s'", dim, m->name);
int updated = 0;
- if(m->count && !m->reset) {
+ if(unlikely(!m->reset && m->count)) {
m->last = m->counter.value;
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)))
+ if(unlikely(m->options & STATSD_METRIC_OPTION_PRIVATE_CHART_ENABLED && (updated || !(m->options & STATSD_METRIC_OPTION_SHOW_GAPS_WHEN_NOT_COLLECTED))))
statsd_private_chart_counter_or_meter(m, dim, family);
}
@@ -1687,40 +1695,27 @@ static inline void statsd_flush_set(STATSD_METRIC *m) {
debug(D_STATSD, "flushing set metric '%s'", m->name);
int updated = 0;
- if(m->count && !m->reset) {
+ if(unlikely(!m->reset && m->count)) {
m->last = (collected_number)m->set.unique;
m->reset = 1;
updated = 1;
}
+ else {
+ m->last = 0;
+ }
- if(m->options & STATSD_METRIC_OPTION_PRIVATE_CHART_ENABLED && (updated || !(m->options & STATSD_METRIC_OPTION_SHOW_GAPS_WHEN_NOT_COLLECTED)))
+ if(unlikely(m->options & STATSD_METRIC_OPTION_PRIVATE_CHART_ENABLED && (updated || !(m->options & STATSD_METRIC_OPTION_SHOW_GAPS_WHEN_NOT_COLLECTED))))
statsd_private_chart_set(m);
}
static inline void statsd_flush_timer_or_histogram(STATSD_METRIC *m, const char *dim, const char *family, const char *units) {
debug(D_STATSD, "flushing %s metric '%s'", dim, m->name);
- 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) {
+ if(unlikely(!m->reset && m->count && m->histogram.ext->used > 0)) {
+ netdata_mutex_lock(&m->histogram.ext->mutex);
+
size_t len = m->histogram.ext->used;
LONG_DOUBLE *series = m->histogram.ext->values;
sort_series(series, len);
@@ -1738,6 +1733,8 @@ static inline void statsd_flush_timer_or_histogram(STATSD_METRIC *m, const char
else
m->histogram.ext->last_percentile = (collected_number)roundl(series[pct_len - 1] * statsd.decimal_detail);
+ netdata_mutex_unlock(&m->histogram.ext->mutex);
+
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);
@@ -1745,11 +1742,24 @@ static inline void statsd_flush_timer_or_histogram(STATSD_METRIC *m, const char
m->reset = 1;
updated = 1;
}
+ else 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;
+ }
- if(m->options & STATSD_METRIC_OPTION_PRIVATE_CHART_ENABLED && (updated || !(m->options & STATSD_METRIC_OPTION_SHOW_GAPS_WHEN_NOT_COLLECTED)))
+ if(unlikely(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);
-
- netdata_mutex_unlock(&m->histogram.ext->mutex);
}
static inline void statsd_flush_timer(STATSD_METRIC *m) {
@@ -1837,6 +1847,7 @@ static inline void link_metric_to_app_dimension(STATSD_APP *app, STATSD_METRIC *
}
chart->dimensions_linked_count++;
+ m->options |= STATSD_METRIC_OPTION_USED_IN_APPS;
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));
}
@@ -2043,7 +2054,13 @@ const char *statsd_metric_type_string(STATSD_METRIC_TYPE type) {
static inline void statsd_flush_index_metrics(STATSD_INDEX *index, void (*flush_metric)(STATSD_METRIC *)) {
STATSD_METRIC *m;
+
+ // find the useful metrics (incremental = each time we are called, we check the new metrics only)
for(m = index->first; m ; m = m->next) {
+ // since we add new metrics at the beginning
+ // check for useful charts, until the point we last checked
+ if(unlikely(is_metric_checked(m))) break;
+
if(unlikely(!(m->options & STATSD_METRIC_OPTION_CHECKED_IN_APPS))) {
log_access("NEW STATSD METRIC '%s': '%s'", statsd_metric_type_string(m->type), m->name);
check_if_metric_is_for_app(index, m);
@@ -2051,7 +2068,7 @@ static inline void statsd_flush_index_metrics(STATSD_INDEX *index, void (*flush_
}
if(unlikely(!(m->options & STATSD_METRIC_OPTION_PRIVATE_CHART_CHECKED))) {
- if(statsd.private_charts >= statsd.max_private_charts_hard) {
+ if(unlikely(statsd.private_charts >= statsd.max_private_charts_hard)) {
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);
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);
m->options &= ~STATSD_METRIC_OPTION_PRIVATE_CHART_ENABLED;
@@ -2069,6 +2086,20 @@ static inline void statsd_flush_index_metrics(STATSD_INDEX *index, void (*flush_
m->options |= STATSD_METRIC_OPTION_PRIVATE_CHART_CHECKED;
}
+ // mark it as checked
+ m->options |= STATSD_METRIC_OPTION_CHECKED;
+
+ // check if it is used in charts
+ if((m->options & (STATSD_METRIC_OPTION_PRIVATE_CHART_ENABLED|STATSD_METRIC_OPTION_USED_IN_APPS)) && !(m->options & STATSD_METRIC_OPTION_USEFUL)) {
+ m->options |= STATSD_METRIC_OPTION_USEFUL;
+ index->useful++;
+ m->next_useful = index->first_useful;
+ index->first_useful = m;
+ }
+ }
+
+ // flush all the useful metrics
+ for(m = index->first_useful; m ; m = m->next_useful) {
flush_metric(m);
}
}
@@ -2237,6 +2268,27 @@ void *statsd_main(void *ptr) {
RRDDIM *rd_metrics_histogram = rrddim_add(st_metrics, "histograms", NULL, 1, 1, RRD_ALGORITHM_ABSOLUTE);
RRDDIM *rd_metrics_set = rrddim_add(st_metrics, "sets", NULL, 1, 1, RRD_ALGORITHM_ABSOLUTE);
+ RRDSET *st_useful_metrics = rrdset_create_localhost(
+ "netdata"
+ , "statsd_useful_metrics"
+ , NULL
+ , "statsd"
+ , NULL
+ , "Useful metrics in the netdata statsd database"
+ , "metrics"
+ , "statsd"
+ , "stats"
+ , 132010
+ , statsd.update_every
+ , RRDSET_TYPE_STACKED
+ );
+ RRDDIM *rd_useful_metrics_gauge = rrddim_add(st_useful_metrics, "gauges", NULL, 1, 1, RRD_ALGORITHM_ABSOLUTE);
+ RRDDIM *rd_useful_metrics_counter = rrddim_add(st_useful_metrics, "counters", NULL, 1, 1, RRD_ALGORITHM_ABSOLUTE);
+ RRDDIM *rd_useful_metrics_timer = rrddim_add(st_useful_metrics, "timers", NULL, 1, 1, RRD_ALGORITHM_ABSOLUTE);
+ RRDDIM *rd_useful_metrics_meter = rrddim_add(st_useful_metrics, "meters", NULL, 1, 1, RRD_ALGORITHM_ABSOLUTE);
+ RRDDIM *rd_useful_metrics_histogram = rrddim_add(st_useful_metrics, "histograms", NULL, 1, 1, RRD_ALGORITHM_ABSOLUTE);
+ RRDDIM *rd_useful_metrics_set = rrddim_add(st_useful_metrics, "sets", NULL, 1, 1, RRD_ALGORITHM_ABSOLUTE);
+
RRDSET *st_events = rrdset_create_localhost(
"netdata"
, "statsd_events"
@@ -2430,6 +2482,7 @@ void *statsd_main(void *ptr) {
if(likely(hb_dt)) {
rrdset_next(st_metrics);
+ rrdset_next(st_useful_metrics);
rrdset_next(st_events);
rrdset_next(st_reads);
rrdset_next(st_bytes);
@@ -2450,6 +2503,14 @@ void *statsd_main(void *ptr) {
rrddim_set_by_pointer(st_metrics, rd_metrics_set, (collected_number)statsd.sets.metrics);
rrdset_done(st_metrics);
+ rrddim_set_by_pointer(st_useful_metrics, rd_useful_metrics_gauge, (collected_number)statsd.gauges.useful);
+ rrddim_set_by_pointer(st_useful_metrics, rd_useful_metrics_counter, (collected_number)statsd.counters.useful);
+ rrddim_set_by_pointer(st_useful_metrics, rd_useful_metrics_timer, (collected_number)statsd.timers.useful);
+ rrddim_set_by_pointer(st_useful_metrics, rd_useful_metrics_meter, (collected_number)statsd.meters.useful);
+ rrddim_set_by_pointer(st_useful_metrics, rd_useful_metrics_histogram, (collected_number)statsd.histograms.useful);
+ rrddim_set_by_pointer(st_useful_metrics, rd_useful_metrics_set, (collected_number)statsd.sets.useful);
+ rrdset_done(st_useful_metrics);
+
rrddim_set_by_pointer(st_events, rd_events_gauge, (collected_number)statsd.gauges.events);
rrddim_set_by_pointer(st_events, rd_events_counter, (collected_number)statsd.counters.events);
rrddim_set_by_pointer(st_events, rd_events_timer, (collected_number)statsd.timers.events);