summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rwxr-xr-xCMakeLists.txt2
-rw-r--r--src/Makefile.am2
-rw-r--r--src/backend_prometheus.c234
-rw-r--r--src/backend_prometheus.h11
-rw-r--r--src/backends.c84
-rw-r--r--src/backends.h18
-rw-r--r--src/common.h1
-rw-r--r--src/rrd2json.c131
-rw-r--r--src/rrd2json.h2
-rw-r--r--src/web_api_v1.c12
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: