diff options
-rwxr-xr-x | CMakeLists.txt | 2 | ||||
-rw-r--r-- | src/Makefile.am | 2 | ||||
-rw-r--r-- | src/backend_prometheus.c | 234 | ||||
-rw-r--r-- | src/backend_prometheus.h | 11 | ||||
-rw-r--r-- | src/backends.c | 84 | ||||
-rw-r--r-- | src/backends.h | 18 | ||||
-rw-r--r-- | src/common.h | 1 | ||||
-rw-r--r-- | src/rrd2json.c | 131 | ||||
-rw-r--r-- | src/rrd2json.h | 2 | ||||
-rw-r--r-- | src/web_api_v1.c | 12 |
10 files changed, 321 insertions, 176 deletions
diff --git a/CMakeLists.txt b/CMakeLists.txt index f11487fe33..8e59e094d8 100755 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -152,7 +152,7 @@ set(NETDATA_SOURCE_FILES src/web_client.h src/web_server.c src/web_server.h - src/locks.h src/statsd.c src/statsd.h src/statistical.c src/statistical.h) + src/locks.h src/statsd.c src/statsd.h src/statistical.c src/statistical.h src/backend_prometheus.c src/backend_prometheus.h) set(APPS_PLUGIN_SOURCE_FILES src/appconfig.c diff --git a/src/Makefile.am b/src/Makefile.am index 4891908a94..601d3204ff 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -44,6 +44,8 @@ netdata_SOURCES = \ appconfig.h \ avl.c \ avl.h \ + backend_prometheus.c \ + backend_prometheus.h \ backends.c \ backends.h \ clocks.c \ diff --git a/src/backend_prometheus.c b/src/backend_prometheus.c new file mode 100644 index 0000000000..1739b11666 --- /dev/null +++ b/src/backend_prometheus.c @@ -0,0 +1,234 @@ +#include "common.h" + +// ---------------------------------------------------------------------------- +// PROMETHEUS +// /api/v1/allmetrics?format=prometheus + +static struct prometheus_server { + const char *server; + uint32_t hash; + time_t last_access; + struct prometheus_server *next; +} *prometheus_server_root = NULL; + +static inline time_t prometheus_server_last_access(const char *server, time_t now) { + uint32_t hash = simple_hash(server); + + struct prometheus_server *ps; + for(ps = prometheus_server_root; ps ;ps = ps->next) { + if (hash == ps->hash && !strcmp(server, ps->server)) { + time_t last = ps->last_access; + ps->last_access = now; + return last; + } + } + + ps = callocz(1, sizeof(struct prometheus_server)); + ps->server = strdupz(server); + ps->hash = hash; + ps->last_access = now; + ps->next = prometheus_server_root; + prometheus_server_root = ps; + + return 0; +} + +static inline size_t prometheus_name_copy(char *d, const char *s, size_t usable) { + size_t n; + + for(n = 0; *s && n < usable ; d++, s++, n++) { + register char c = *s; + + if(!isalnum(c)) *d = '_'; + else *d = c; + } + *d = '\0'; + + return n; +} + +static inline size_t prometheus_label_copy(char *d, const char *s, size_t usable) { + size_t n; + + // make sure we can escape one character without overflowing the buffer + usable--; + + for(n = 0; *s && n < usable ; d++, s++, n++) { + register char c = *s; + + if(unlikely(c == '"' || c == '\\' || c == '\n')) { + *d++ = '\\'; + n++; + } + *d = c; + } + *d = '\0'; + + return n; +} + +#define PROMETHEUS_ELEMENT_MAX 256 +#define PROMETHEUS_LABELS_MAX 1024 + +void rrd_stats_api_v1_charts_allmetrics_prometheus(RRDHOST *host, BUFFER *wb, uint32_t options, time_t after, time_t before, int allhosts, int help, int types, int names) { + rrdhost_rdlock(host); + + char hostname[PROMETHEUS_ELEMENT_MAX + 1]; + prometheus_label_copy(hostname, host->hostname, PROMETHEUS_ELEMENT_MAX); + + char labels[PROMETHEUS_LABELS_MAX + 1] = ""; + if(allhosts) { + if(host->tags && *(host->tags)) + buffer_sprintf(wb, "netdata_host_tags{instance=\"%s\",%s} 1 %llu\n", hostname, host->tags, now_realtime_usec() / USEC_PER_MS); + + snprintfz(labels, PROMETHEUS_LABELS_MAX, ",instance=\"%s\"", hostname); + } + else { + if(host->tags && *(host->tags)) + buffer_sprintf(wb, "netdata_host_tags{%s} 1 %llu\n", host->tags, now_realtime_usec() / USEC_PER_MS); + } + + // for each chart + RRDSET *st; + rrdset_foreach_read(st, host) { + char chart[PROMETHEUS_ELEMENT_MAX + 1]; + char context[PROMETHEUS_ELEMENT_MAX + 1]; + char family[PROMETHEUS_ELEMENT_MAX + 1]; + + prometheus_label_copy(chart, (names && st->name)?st->name:st->id, PROMETHEUS_ELEMENT_MAX); + prometheus_label_copy(family, st->family, PROMETHEUS_ELEMENT_MAX); + prometheus_name_copy(context, st->context, PROMETHEUS_ELEMENT_MAX); + + if(unlikely(help || types)) + buffer_strcat(wb, "\n"); + + if(rrdset_is_available_for_backends(st)) { + rrdset_rdlock(st); + + // for each dimension + RRDDIM *rd; + rrddim_foreach_read(rd, st) { + if(rd->collections_counter) { + char dimension[PROMETHEUS_ELEMENT_MAX + 1]; + + if ((options & BACKEND_SOURCE_BITS) == BACKEND_SOURCE_DATA_AS_COLLECTED) { + // we need as-collected / raw data + + prometheus_name_copy(dimension, (names && rd->name) ? rd->name : rd->id, PROMETHEUS_ELEMENT_MAX); + + const char *t = "gauge", *h = "gives"; + if (rd->algorithm == RRD_ALGORITHM_INCREMENTAL || + rd->algorithm == RRD_ALGORITHM_PCENT_OVER_DIFF_TOTAL) { + t = "counter"; + h = "delta gives"; + } + + if (unlikely(help)) + buffer_sprintf(wb, "# HELP %s_%s netdata chart \"%s\", context \"%s\", family \"%s\", dimension \"%s\", value * " COLLECTED_NUMBER_FORMAT " / " COLLECTED_NUMBER_FORMAT " %s %s (%s)\n", + context, dimension, (names && st->name) ? st->name : st->id, st->context, + st->family, (names && rd->name) ? rd->name : rd->id, rd->multiplier, + rd->divisor, h, st->units, t + ); + + if (unlikely(types)) + buffer_sprintf(wb, "# TYPE %s_%s %s\n", context, dimension, t); + + buffer_sprintf(wb, "%s_%s{chart=\"%s\",family=\"%s\"%s} " COLLECTED_NUMBER_FORMAT " %llu\n", + context, dimension, chart, family, labels, rd->last_collected_value, + timeval_msec(&rd->last_collected_time) + ); + } + else { + // we need average or sum of the data + + calculated_number value = backend_calculate_value_from_stored_data(st, rd, after, before, options); + + if(!isnan(value) && !isinf(value)) { + prometheus_label_copy(dimension, (names && rd->name) ? rd->name : rd->id, PROMETHEUS_ELEMENT_MAX); + + if (unlikely(help)) + buffer_sprintf(wb, "# HELP %s netdata chart \"%s\", context \"%s\", family \"%s\", dimension \"%s\", value gives %s (gauge)\n", + context, (names && st->name) ? st->name : st->id, st->context, + st->family, (names && rd->name) ? rd->name : rd->id, + st->units + ); + + if (unlikely(types)) + buffer_sprintf(wb, "# TYPE %s gauge\n", context); + + buffer_sprintf(wb, "%s{chart=\"%s\",family=\"%s\",dimension=\"%s\"%s} " CALCULATED_NUMBER_FORMAT " %llu\n", + context, chart, family, dimension, labels, value, + timeval_msec(&rd->last_collected_time) + ); + } + } + } + } + + rrdset_unlock(st); + } + } + + rrdhost_unlock(host); +} + +static inline time_t prometheus_preparation(RRDHOST *host, BUFFER *wb, uint32_t options, const char *server, time_t now, int help) { + if(!server || !*server) server = "default"; + + time_t after = prometheus_server_last_access(server, now); + + int first_seen = 0; + if(!after) { + after = now - backend_update_every; + first_seen = 1; + } + + if(help) { + int show_range = 1; + char *mode; + if((options & BACKEND_SOURCE_BITS) == BACKEND_SOURCE_DATA_AS_COLLECTED) { + mode = "as collected"; + show_range = 0; + } + else if((options & BACKEND_SOURCE_BITS) == BACKEND_SOURCE_DATA_AVERAGE) + mode = "average"; + else if((options & BACKEND_SOURCE_BITS) == BACKEND_SOURCE_DATA_SUM) + mode = "sum"; + else + mode = "unknown"; + + buffer_sprintf(wb, "# HELP netdata \"%s\" to %sprometheus \"%s\", source \"%s\", last seen %lu %s\n" + , host->hostname + , (first_seen)?"FIRST SEEN ":"" + , server + , mode + , (unsigned long)((first_seen)?0:(now - after)) + , (first_seen)?"never":"seconds ago" + ); + + if(show_range) + buffer_sprintf(wb, "# HELP netdata values for time range %lu to %lu\n", (unsigned long)after, (unsigned long)now); + + buffer_strcat(wb, "\n"); + } + + return after; +} + +void rrd_stats_api_v1_charts_allmetrics_prometheus_single_host(RRDHOST *host, BUFFER *wb, const char *server, uint32_t options, int help, int types, int names) { + time_t before = now_realtime_sec(); + time_t after = prometheus_preparation(host, wb, options, server, before, help); + + rrd_stats_api_v1_charts_allmetrics_prometheus(host, wb, options, after, before, 0, help, types, names); +} + +void rrd_stats_api_v1_charts_allmetrics_prometheus_all_hosts(RRDHOST *host, BUFFER *wb, const char *server, uint32_t options, int help, int types, int names) { + time_t before = now_realtime_sec(); + time_t after = prometheus_preparation(host, wb, options, server, before, help); + + rrd_rdlock(); + rrdhost_foreach_read(host) { + rrd_stats_api_v1_charts_allmetrics_prometheus(host, wb, options, after, before, 1, help, types, names); + } + rrd_unlock(); +} diff --git a/src/backend_prometheus.h b/src/backend_prometheus.h new file mode 100644 index 0000000000..b17252900b --- /dev/null +++ b/src/backend_prometheus.h @@ -0,0 +1,11 @@ +// +// Created by costa on 09/07/17. +// + +#ifndef NETDATA_BACKEND_PROMETHEUS_H +#define NETDATA_BACKEND_PROMETHEUS_H + +extern void rrd_stats_api_v1_charts_allmetrics_prometheus_single_host(RRDHOST *host, BUFFER *wb, const char *server, uint32_t options, int help, int types, int names); +extern void rrd_stats_api_v1_charts_allmetrics_prometheus_all_hosts(RRDHOST *host, BUFFER *wb, const char *server, uint32_t options, int help, int types, int names); + +#endif //NETDATA_BACKEND_PROMETHEUS_H diff --git a/src/backends.c b/src/backends.c index 4c817f16a9..ef617ce372 100644 --- a/src/backends.c +++ b/src/backends.c @@ -22,11 +22,9 @@ // 5. repeats the above forever. // -#define BACKEND_SOURCE_DATA_AS_COLLECTED 0x00000001 -#define BACKEND_SOURCE_DATA_AVERAGE 0x00000002 -#define BACKEND_SOURCE_DATA_SUM 0x00000004 - int backend_send_names = 1; +int backend_update_every = 10; +uint32_t backend_options = BACKEND_SOURCE_DATA_AVERAGE; // ---------------------------------------------------------------------------- // helper functions for backends @@ -48,7 +46,7 @@ static inline size_t backend_name_copy(char *d, const char *s, size_t usable) { // calculate the SUM or AVERAGE of a dimension, for any timeframe // may return NAN if the database does not have any value in the give timeframe -static inline calculated_number backend_calculate_value_from_stored_data( +inline calculated_number backend_calculate_value_from_stored_data( RRDSET *st // the chart , RRDDIM *rd // the dimension , time_t after // the start timestamp @@ -124,7 +122,7 @@ static inline calculated_number backend_calculate_value_from_stored_data( return NAN; } - if(unlikely(options & BACKEND_SOURCE_DATA_SUM)) + if(unlikely((options & BACKEND_SOURCE_BITS) == BACKEND_SOURCE_DATA_SUM)) return sum; return sum / (calculated_number)counter; @@ -457,7 +455,7 @@ static inline int backends_can_send_rrdset(uint32_t options, RRDSET *st) { return 0; } - if(unlikely(st->rrd_memory_mode == RRD_MEMORY_MODE_NONE && !(options & BACKEND_SOURCE_DATA_AS_COLLECTED))) { + if(unlikely(st->rrd_memory_mode == RRD_MEMORY_MODE_NONE && !((options & BACKEND_SOURCE_BITS) == BACKEND_SOURCE_DATA_AS_COLLECTED))) { 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)); return 0; } @@ -465,6 +463,26 @@ static inline int backends_can_send_rrdset(uint32_t options, RRDSET *st) { return 1; } +inline uint32_t backend_parse_data_source(const char *source, uint32_t mode) { + if(!strcmp(source, "raw") || !strcmp(source, "as collected") || !strcmp(source, "as-collected") || !strcmp(source, "as_collected") || !strcmp(source, "ascollected")) { + mode |= BACKEND_SOURCE_DATA_AS_COLLECTED; + mode &= ~(BACKEND_SOURCE_BITS ^ BACKEND_SOURCE_DATA_AS_COLLECTED); + } + else if(!strcmp(source, "average")) { + mode |= BACKEND_SOURCE_DATA_AVERAGE; + mode &= ~(BACKEND_SOURCE_BITS ^ BACKEND_SOURCE_DATA_AVERAGE); + } + else if(!strcmp(source, "sum") || !strcmp(source, "volume")) { + mode |= BACKEND_SOURCE_DATA_SUM; + mode &= ~(BACKEND_SOURCE_BITS ^ BACKEND_SOURCE_DATA_SUM); + } + else { + error("BACKEND: invalid data source method '%s'.", source); + } + + return mode; +} + void *backends_main(void *ptr) { int default_port = 0; int sock = -1; @@ -489,16 +507,15 @@ void *backends_main(void *ptr) { .tv_sec = 0, .tv_usec = 0 }; - uint32_t options = 0x00000000; int enabled = config_get_boolean(CONFIG_SECTION_BACKEND, "enabled", 0); const char *source = config_get(CONFIG_SECTION_BACKEND, "data source", "average"); const char *type = config_get(CONFIG_SECTION_BACKEND, "type", "graphite"); const char *destination = config_get(CONFIG_SECTION_BACKEND, "destination", "localhost"); const char *prefix = config_get(CONFIG_SECTION_BACKEND, "prefix", "netdata"); const char *hostname = config_get(CONFIG_SECTION_BACKEND, "hostname", localhost->hostname); - int frequency = (int)config_get_number(CONFIG_SECTION_BACKEND, "update every", 10); + backend_update_every = (int)config_get_number(CONFIG_SECTION_BACKEND, "update every", backend_update_every); int buffer_on_failures = (int)config_get_number(CONFIG_SECTION_BACKEND, "buffer on failures", 10); - long timeoutms = config_get_number(CONFIG_SECTION_BACKEND, "timeout ms", frequency * 2 * 1000); + long timeoutms = config_get_number(CONFIG_SECTION_BACKEND, "timeout ms", backend_update_every * 2 * 1000); backend_send_names = config_get_boolean(CONFIG_SECTION_BACKEND, "send names instead of ids", backend_send_names); charts_pattern = simple_pattern_create(config_get(CONFIG_SECTION_BACKEND, "send charts matching", "*"), SIMPLE_PATTERN_EXACT); @@ -508,30 +525,17 @@ void *backends_main(void *ptr) { // validate configuration options // and prepare for sending data to our backend - if(!enabled || frequency < 1) - goto cleanup; - - if(!strcmp(source, "as collected")) { - options |= BACKEND_SOURCE_DATA_AS_COLLECTED; - } - else if(!strcmp(source, "average")) { - options |= BACKEND_SOURCE_DATA_AVERAGE; - } - else if(!strcmp(source, "sum") || !strcmp(source, "volume")) { - options |= BACKEND_SOURCE_DATA_SUM; - } - else { - error("BACKEND: invalid data source method '%s' for backend given. Disabling backed.", source); - goto cleanup; - } + backend_options = backend_parse_data_source(source, backend_options); if(timeoutms < 1) { - error("BACKEND: invalid timeout %ld ms given. Assuming %d ms.", timeoutms, frequency * 2 * 1000); - timeoutms = frequency * 2 * 1000; + error("BACKEND: invalid timeout %ld ms given. Assuming %d ms.", timeoutms, backend_update_every * 2 * 1000); + timeoutms = backend_update_every * 2 * 1000; } timeout.tv_sec = (timeoutms * 1000) / 1000000; timeout.tv_usec = (timeoutms * 1000) % 1000000; + if(!enabled || backend_update_every < 1) + goto cleanup; // ------------------------------------------------------------------------ // select the backend type @@ -541,7 +545,7 @@ void *backends_main(void *ptr) { default_port = 2003; backend_response_checker = process_graphite_response; - if(options & BACKEND_SOURCE_DATA_AS_COLLECTED) + if((backend_options & BACKEND_SOURCE_BITS) == BACKEND_SOURCE_DATA_AS_COLLECTED) backend_request_formatter = format_dimension_collected_graphite_plaintext; else backend_request_formatter = format_dimension_stored_graphite_plaintext; @@ -552,7 +556,7 @@ void *backends_main(void *ptr) { default_port = 4242; backend_response_checker = process_opentsdb_response; - if(options & BACKEND_SOURCE_DATA_AS_COLLECTED) + if((backend_options & BACKEND_SOURCE_BITS) == BACKEND_SOURCE_DATA_AS_COLLECTED) backend_request_formatter = format_dimension_collected_opentsdb_telnet; else backend_request_formatter = format_dimension_stored_opentsdb_telnet; @@ -563,7 +567,7 @@ void *backends_main(void *ptr) { default_port = 5448; backend_response_checker = process_json_response; - if (options & BACKEND_SOURCE_DATA_AS_COLLECTED) + if ((backend_options & BACKEND_SOURCE_BITS) == BACKEND_SOURCE_DATA_AS_COLLECTED) backend_request_formatter = format_dimension_collected_json_plaintext; else backend_request_formatter = format_dimension_stored_json_plaintext; @@ -600,18 +604,18 @@ void *backends_main(void *ptr) { chart_backend_reconnects = 0, chart_backend_latency = 0; - RRDSET *chart_metrics = rrdset_create_localhost("netdata", "backend_metrics", NULL, "backend", NULL, "Netdata Buffered Metrics", "metrics", 130600, frequency, RRDSET_TYPE_LINE); + RRDSET *chart_metrics = rrdset_create_localhost("netdata", "backend_metrics", NULL, "backend", NULL, "Netdata Buffered Metrics", "metrics", 130600, backend_update_every, RRDSET_TYPE_LINE); rrddim_add(chart_metrics, "buffered", NULL, 1, 1, RRD_ALGORITHM_ABSOLUTE); rrddim_add(chart_metrics, "lost", NULL, 1, 1, RRD_ALGORITHM_ABSOLUTE); rrddim_add(chart_metrics, "sent", NULL, 1, 1, RRD_ALGORITHM_ABSOLUTE); - RRDSET *chart_bytes = rrdset_create_localhost("netdata", "backend_bytes", NULL, "backend", NULL, "Netdata Backend Data Size", "KB", 130610, frequency, RRDSET_TYPE_AREA); + RRDSET *chart_bytes = rrdset_create_localhost("netdata", "backend_bytes", NULL, "backend", NULL, "Netdata Backend Data Size", "KB", 130610, backend_update_every, RRDSET_TYPE_AREA); rrddim_add(chart_bytes, "buffered", NULL, 1, 1024, RRD_ALGORITHM_ABSOLUTE); rrddim_add(chart_bytes, "lost", NULL, 1, 1024, RRD_ALGORITHM_ABSOLUTE); rrddim_add(chart_bytes, "sent", NULL, 1, 1024, RRD_ALGORITHM_ABSOLUTE); rrddim_add(chart_bytes, "received", NULL, 1, 1024, RRD_ALGORITHM_ABSOLUTE); - RRDSET *chart_ops = rrdset_create_localhost("netdata", "backend_ops", NULL, "backend", NULL, "Netdata Backend Operations", "operations", 130630, frequency, RRDSET_TYPE_LINE); + RRDSET *chart_ops = rrdset_create_localhost("netdata", "backend_ops", NULL, "backend", NULL, "Netdata Backend Operations", "operations", 130630, backend_update_every, RRDSET_TYPE_LINE); rrddim_add(chart_ops, "write", NULL, 1, 1, RRD_ALGORITHM_ABSOLUTE); rrddim_add(chart_ops, "discard", NULL, 1, 1, RRD_ALGORITHM_ABSOLUTE); rrddim_add(chart_ops, "reconnect", NULL, 1, 1, RRD_ALGORITHM_ABSOLUTE); @@ -625,11 +629,11 @@ void *backends_main(void *ptr) { * * issue #1432 and https://www.softlab.ntua.gr/facilities/documentation/unix/unix-socket-faq/unix-socket-faq-2.html * - RRDSET *chart_latency = rrdset_create_localhost("netdata", "backend_latency", NULL, "backend", NULL, "Netdata Backend Latency", "ms", 130620, frequency, RRDSET_TYPE_AREA); + RRDSET *chart_latency = rrdset_create_localhost("netdata", "backend_latency", NULL, "backend", NULL, "Netdata Backend Latency", "ms", 130620, backend_update_every, RRDSET_TYPE_AREA); rrddim_add(chart_latency, "latency", NULL, 1, 1000, RRD_ALGORITHM_ABSOLUTE); */ - RRDSET *chart_rusage = rrdset_create_localhost("netdata", "backend_thread_cpu", NULL, "backend", NULL, "NetData Backend Thread CPU usage", "milliseconds/s", 130630, frequency, RRDSET_TYPE_STACKED); + RRDSET *chart_rusage = rrdset_create_localhost("netdata", "backend_thread_cpu", NULL, "backend", NULL, "NetData Backend Thread CPU usage", "milliseconds/s", 130630, backend_update_every, RRDSET_TYPE_STACKED); rrddim_add(chart_rusage, "user", NULL, 1, 1000, RRD_ALGORITHM_INCREMENTAL); rrddim_add(chart_rusage, "system", NULL, 1, 1000, RRD_ALGORITHM_INCREMENTAL); @@ -637,9 +641,9 @@ void *backends_main(void *ptr) { // ------------------------------------------------------------------------ // prepare the backend main loop - info("BACKEND: configured ('%s' on '%s' sending '%s' data, every %d seconds, as host '%s', with prefix '%s')", type, destination, source, frequency, hostname, prefix); + info("BACKEND: configured ('%s' on '%s' sending '%s' data, every %d seconds, as host '%s', with prefix '%s')", type, destination, source, backend_update_every, hostname, prefix); - usec_t step_ut = frequency * USEC_PER_SEC; + usec_t step_ut = backend_update_every * USEC_PER_SEC; time_t after = now_realtime_sec(); int failures = 0; heartbeat_t hb; @@ -680,7 +684,7 @@ void *backends_main(void *ptr) { RRDSET *st; rrdset_foreach_read(st, host) { - if(likely(backends_can_send_rrdset(options, st))) { + if(likely(backends_can_send_rrdset(backend_options, st))) { rrdset_rdlock(st); count_charts++; @@ -688,7 +692,7 @@ void *backends_main(void *ptr) { RRDDIM *rd; rrddim_foreach_read(rd, st) { if (likely(rd->last_collected_time.tv_sec >= after)) { - chart_buffered_metrics += backend_request_formatter(b, prefix, host, __hostname, st, rd, after, before, options); + chart_buffered_metrics += backend_request_formatter(b, prefix, host, __hostname, st, rd, after, before, backend_options); count_dims++; } else { diff --git a/src/backends.h b/src/backends.h index afd246e6e4..aa1967ee9b 100644 --- a/src/backends.h +++ b/src/backends.h @@ -1,7 +1,25 @@ #ifndef NETDATA_BACKENDS_H #define NETDATA_BACKENDS_H 1 +#define BACKEND_SOURCE_DATA_AS_COLLECTED 0x00000001 +#define BACKEND_SOURCE_DATA_AVERAGE 0x00000002 +#define BACKEND_SOURCE_DATA_SUM 0x00000004 + +#define BACKEND_SOURCE_BITS (BACKEND_SOURCE_DATA_AS_COLLECTED|BACKEND_SOURCE_DATA_AVERAGE|BACKEND_SOURCE_DATA_SUM) + extern int backend_send_names; +extern int backend_update_every; +extern uint32_t backend_options; extern void *backends_main(void *ptr); +extern uint32_t backend_parse_data_source(const char *source, uint32_t mode); + +extern calculated_number backend_calculate_value_from_stored_data( + RRDSET *st // the chart + , RRDDIM *rd // the dimension + , time_t after // the start timestamp + , time_t before // the end timestamp + , uint32_t options // BACKEND_SOURCE_* bitmap +); + #endif /* NETDATA_BACKENDS_H */ diff --git a/src/common.h b/src/common.h index 55155db070..efeebf16f0 100644 --- a/src/common.h +++ b/src/common.h @@ -220,6 +220,7 @@ #include "unit_test.h" #include "ipc.h" #include "backends.h" +#include "backend_prometheus.h" #include "inlined.h" #include "adaptive_resortable_list.h" #include "rrdpush.h" diff --git a/src/rrd2json.c b/src/rrd2json.c index 16806b03b0..98080139ca 100644 --- a/src/rrd2json.c +++ b/src/rrd2json.c @@ -167,137 +167,6 @@ void rrd_stats_api_v1_charts(RRDHOST *host, BUFFER *wb) { } // ---------------------------------------------------------------------------- -// PROMETHEUS -// /api/v1/allmetrics?format=prometheus - -static inline size_t prometheus_name_copy(char *d, const char *s, size_t usable) { - size_t n; - - for(n = 0; *s && n < usable ; d++, s++, n++) { - register char c = *s; - - if(!isalnum(c)) *d = '_'; - else *d = c; - } - *d = '\0'; - - return n; -} - -static inline size_t prometheus_label_copy(char *d, const char *s, size_t usable) { - size_t n; - - // make sure we can escape one character without overflowing the buffer - usable--; - - for(n = 0; *s && n < usable ; d++, s++, n++) { - register char c = *s; - - if(unlikely(c == '"' || c == '\\' || c == '\n')) { - *d++ = '\\'; - n++; - } - *d = c; - } - *d = '\0'; - - return n; -} - -#define PROMETHEUS_ELEMENT_MAX 256 -#define PROMETHEUS_LABELS_MAX 1024 - -void rrd_stats_api_v1_charts_allmetrics_prometheus(RRDHOST *host, BUFFER *wb, int allhosts, int help, int types, int names) { - rrdhost_rdlock(host); - - char hostname[PROMETHEUS_ELEMENT_MAX + 1]; - prometheus_label_copy(hostname, host->hostname, PROMETHEUS_ELEMENT_MAX); - - char labels[PROMETHEUS_LABELS_MAX + 1] = ""; - if(allhosts) { - if(host->tags && *(host->tags)) - buffer_sprintf(wb, "netdata_host_tags{instance=\"%s\",%s} 1 %llu\n", hostname, host->tags, now_realtime_usec() / USEC_PER_MS); - - snprintfz(labels, PROMETHEUS_LABELS_MAX, ",instance=\"%s\"", hostname); - } - else { - if(host->tags && *(host->tags)) - buffer_sprintf(wb, "netdata_host_tags{%s} 1 %llu\n", host->tags, now_realtime_usec() / USEC_PER_MS); - } - - // for each chart - RRDSET *st; - rrdset_foreach_read(st, host) { - char chart[PROMETHEUS_ELEMENT_MAX + 1]; - char context[PROMETHEUS_ELEMENT_MAX + 1]; - char family[PROMETHEUS_ELEMENT_MAX + 1]; - - prometheus_label_copy(chart, (names && st->name)?st->name:st->id, PROMETHEUS_ELEMENT_MAX); - prometheus_name_copy(context, st->context, PROMETHEUS_ELEMENT_MAX); - prometheus_name_copy(family, st->family, PROMETHEUS_ELEMENT_MAX); - - if(unlikely(help || types)) - buffer_strcat(wb, "\n"); - - if(rrdset_is_available_for_backends(st)) { - rrdset_rdlock(st); - - // for each dimension - RRDDIM *rd; - rrddim_foreach_read(rd, st) { - if(rd->collections_counter) { - char dimension[PROMETHEUS_ELEMENT_MAX + 1]; - prometheus_name_copy(dimension, (names && rd->name)?rd->name:rd->id, PROMETHEUS_ELEMENT_MAX); - - const char *t = "gauge", *h = "gives"; - if(rd->algorithm == RRD_ALGORITHM_INCREMENTAL || rd->algorithm == RRD_ALGORITHM_PCENT_OVER_DIFF_TOTAL) { - t = "counter"; - h = "delta gives"; - } - - if(unlikely(help)) - buffer_sprintf(wb, "# HELP %s_%s_%s netdata chart \"%s\", context \"%s\", family \"%s\", dimension \"%s\", value * " COLLECTED_NUMBER_FORMAT " / " COLLECTED_NUMBER_FORMAT " %s %s (%s)\n" - , context, family, dimension - , (names && st->name)?st->name:st->id - , st->context - , st->family - , (names && rd->name)?rd->name:rd->id - , rd->multiplier - , rd->divisor - , h - , st->units - , t - ); - - if(unlikely(types)) - buffer_sprintf(wb, "# TYPE %s_%s_%s %s\n", context, family, dimension, t); - - // calculated_number n = (calculated_number)rd->last_collected_value * (calculated_number)(abs(rd->multiplier)) / (calculated_number)(abs(rd->divisor)); - // buffer_sprintf(wb, "%s.%s " CALCULATED_NUMBER_FORMAT " %llu\n", st->id, rd->id, n, timeval_msec(&rd->last_collected_time)); - - buffer_sprintf(wb, "%s_%s_%s{chart=\"%s\"%s} " COLLECTED_NUMBER_FORMAT " %llu\n", - context, family, dimension, chart, labels, rd->last_collected_value, timeval_msec(&rd->last_collected_time) - ); - } - } - - rrdset_unlock(st); - } - } - - rrdhost_unlock(host); -} - -void rrd_stats_api_v1_charts_allmetrics_prometheus_all_hosts(BUFFER *wb, int help, int types, int names) { - RRDHOST *host; - rrd_rdlock(); - rrdhost_foreach_read(host) { - rrd_stats_api_v1_charts_allmetrics_prometheus(host, wb, 1, help, types, names); - } - rrd_unlock(); -} - -// ---------------------------------------------------------------------------- // BASH // /api/v1/allmetrics?format=bash diff --git a/src/rrd2json.h b/src/rrd2json.h index dc163ce73b..a3b9c9509b 100644 --- a/src/rrd2json.h +++ b/src/rrd2json.h @@ -67,8 +67,6 @@ extern void rrd_stats_api_v1_charts(RRDHOST *host, BUFFER *wb); extern void rrd_stats_api_v1_charts_allmetrics_json(RRDHOST *host, BUFFER *wb); extern void rrd_stats_api_v1_charts_allmetrics_shell(RRDHOST *host, BUFFER *wb); -extern void rrd_stats_api_v1_charts_allmetrics_prometheus(RRDHOST *host, BUFFER *wb, int allhosts, int help, int types, int names); -extern void rrd_stats_api_v1_charts_allmetrics_prometheus_all_hosts(BUFFER *wb, int help, int types, int names); extern int rrdset2anything_api_v1(RRDSET *st, BUFFER *out, BUFFER *dimensions, uint32_t format, long points , long long after, long long before, int group_method, uint32_t options diff --git a/src/web_api_v1.c b/src/web_api_v1.c index e4bd617def..eef4f78665 100644 --- a/src/web_api_v1.c +++ b/src/web_api_v1.c @@ -209,6 +209,8 @@ inline int web_client_api_request_v1_charts(RRDHOST *host, struct web_client *w, inline int web_client_api_request_v1_allmetrics(RRDHOST *host, struct web_client *w, char *url) { int format = ALLMETRICS_SHELL; int help = 0, types = 0, names = backend_send_names; // prometheus options + const char *prometheus_server = w->client_ip; + uint32_t prometheus_options = backend_options; while(url) { char *value = mystrsep(&url, "?&"); @@ -248,6 +250,12 @@ inline int web_client_api_request_v1_allmetrics(RRDHOST *host, struct web_client else names = 0; } + else if(!strcmp(name, "server")) { + prometheus_server = value; + } + else if(!strcmp(name, "source")) { + prometheus_options = backend_parse_data_source(value, prometheus_options); + } } buffer_flush(w->response.data); @@ -266,12 +274,12 @@ inline int web_client_api_request_v1_allmetrics(RRDHOST *host, struct web_client case ALLMETRICS_PROMETHEUS: w->response.data->contenttype = CT_PROMETHEUS; - rrd_stats_api_v1_charts_allmetrics_prometheus(host, w->response.data, 0, help, types, names); + rrd_stats_api_v1_charts_allmetrics_prometheus_single_host(host, w->response.data, prometheus_server, prometheus_options, help, types, names); return 200; case ALLMETRICS_PROMETHEUS_ALL_HOSTS: w->response.data->contenttype = CT_PROMETHEUS; - rrd_stats_api_v1_charts_allmetrics_prometheus_all_hosts(w->response.data, help, types, names); + rrd_stats_api_v1_charts_allmetrics_prometheus_all_hosts(host, w->response.data, prometheus_server, prometheus_options, help, types, names); return 200; default: |