#include "common.h"
-inline void rrd_stats_api_v1_chart_with_data(RRDSET *st, BUFFER *wb, size_t *dimensions_count, size_t *memory_used)
+void rrd_stats_api_v1_chart_with_data(RRDSET *st, BUFFER *wb, size_t *dimensions_count, size_t *memory_used)
{
- pthread_rwlock_rdlock(&st->rwlock);
+ rrdset_rdlock(st);
buffer_sprintf(wb,
"\t\t{\n"
, st->context
, st->title
, st->priority
- , st->enabled?"true":"false"
+ , rrdset_flag_check(st, RRDSET_FLAG_ENABLED)?"true":"false"
, st->units
, st->name
, rrdset_type_name(st->chart_type)
size_t dimensions = 0;
RRDDIM *rd;
- for(rd = st->dimensions; rd ; rd = rd->next) {
- if(rd->flags & RRDDIM_FLAG_HIDDEN) continue;
+ rrddim_foreach_read(rd, st) {
+ if(rrddim_flag_check(rd, RRDDIM_FLAG_HIDDEN)) continue;
memory += rd->memsize;
"\n\t\t}"
);
- pthread_rwlock_unlock(&st->rwlock);
+ rrdset_unlock(st);
}
void rrd_stats_api_v1_chart(RRDSET *st, BUFFER *wb) {
rrd_stats_api_v1_chart_with_data(st, wb, NULL, NULL);
}
-void rrd_stats_api_v1_charts(BUFFER *wb)
+void rrd_stats_api_v1_charts(RRDHOST *host, BUFFER *wb)
{
size_t c, dimensions = 0, memory = 0, alarms = 0;
RRDSET *st;
buffer_sprintf(wb, "{\n"
"\t\"hostname\": \"%s\""
+ ",\n\t\"version\": \"%s\""
+ ",\n\t\"os\": \"%s\""
",\n\t\"update_every\": %d"
",\n\t\"history\": %d"
",\n\t\"charts\": {"
- , localhost.hostname
- , rrd_update_every
- , rrd_default_history_entries
+ , host->hostname
+ , program_version
+ , host->os
+ , host->rrd_update_every
+ , host->rrd_history_entries
);
- pthread_rwlock_rdlock(&localhost.rrdset_root_rwlock);
- for(st = localhost.rrdset_root, c = 0; st ; st = st->next) {
- if(st->enabled && st->dimensions) {
+ c = 0;
+ rrdhost_rdlock(host);
+ rrdset_foreach_read(st, host) {
+ if(rrdset_flag_check(st, RRDSET_FLAG_ENABLED) && st->dimensions) {
if(c) buffer_strcat(wb, ",");
buffer_strcat(wb, "\n\t\t\"");
buffer_strcat(wb, st->id);
}
RRDCALC *rc;
- for(rc = localhost.alarms; rc ; rc = rc->next) {
- alarms++;
+ for(rc = host->alarms; rc ; rc = rc->next) {
+ if(rc->rrdset)
+ alarms++;
}
- pthread_rwlock_unlock(&localhost.rrdset_root_rwlock);
+ rrdhost_unlock(host);
buffer_sprintf(wb, "\n\t}"
",\n\t\"charts_count\": %zu"
);
}
+// ----------------------------------------------------------------------------
+// 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(unlikely(!isalnum(c))) *d = '_';
+ else *d = c;
+ }
+ *d = '\0';
+
+ return n;
+}
+
+#define PROMETHEUS_ELEMENT_MAX 256
+
+void rrd_stats_api_v1_charts_allmetrics_prometheus(RRDHOST *host, BUFFER *wb) {
+ rrdhost_rdlock(host);
+
+ char hostname[PROMETHEUS_ELEMENT_MAX + 1];
+ prometheus_name_copy(hostname, host->hostname, PROMETHEUS_ELEMENT_MAX);
+
+ // for each chart
+ RRDSET *st;
+ rrdset_foreach_read(st, host) {
+ char chart[PROMETHEUS_ELEMENT_MAX + 1];
+ prometheus_name_copy(chart, st->id, PROMETHEUS_ELEMENT_MAX);
+
+ buffer_strcat(wb, "\n");
+ if(rrdset_flag_check(st, RRDSET_FLAG_ENABLED) && st->dimensions) {
+ rrdset_rdlock(st);
+
+ // for each dimension
+ RRDDIM *rd;
+ rrddim_foreach_read(rd, st) {
+ if(rd->counter) {
+ char dimension[PROMETHEUS_ELEMENT_MAX + 1];
+ prometheus_name_copy(dimension, rd->id, PROMETHEUS_ELEMENT_MAX);
+
+ // buffer_sprintf(wb, "# HELP %s.%s %s\n", st->id, rd->id, st->units);
+
+ switch(rd->algorithm) {
+ case RRD_ALGORITHM_INCREMENTAL:
+ case RRD_ALGORITHM_PCENT_OVER_DIFF_TOTAL:
+ buffer_sprintf(wb, "# TYPE %s_%s counter\n", chart, dimension);
+ break;
+
+ default:
+ buffer_sprintf(wb, "# TYPE %s_%s gauge\n", chart, dimension);
+ break;
+ }
+
+ // 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,
+ // (unsigned long long)((rd->last_collected_time.tv_sec * 1000) + (rd->last_collected_time.tv_usec / 1000)));
+
+ buffer_sprintf(wb, "%s_%s{instance=\"%s\"} " COLLECTED_NUMBER_FORMAT " %llu\n",
+ chart, dimension, hostname, rd->last_collected_value,
+ (unsigned long long)((rd->last_collected_time.tv_sec * 1000) + (rd->last_collected_time.tv_usec / 1000)));
+
+ }
+ }
+
+ rrdset_unlock(st);
+ }
+ }
+
+ rrdhost_unlock(host);
+}
+
+// ----------------------------------------------------------------------------
+// BASH
+// /api/v1/allmetrics?format=bash
+
+static inline size_t shell_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(unlikely(!isalnum(c))) *d = '_';
+ else *d = (char)toupper(c);
+ }
+ *d = '\0';
+
+ return n;
+}
+
+#define SHELL_ELEMENT_MAX 100
+
+void rrd_stats_api_v1_charts_allmetrics_shell(RRDHOST *host, BUFFER *wb) {
+ rrdhost_rdlock(host);
+
+ // for each chart
+ RRDSET *st;
+ rrdset_foreach_read(st, host) {
+ calculated_number total = 0.0;
+ char chart[SHELL_ELEMENT_MAX + 1];
+ shell_name_copy(chart, st->id, SHELL_ELEMENT_MAX);
+
+ buffer_sprintf(wb, "\n# chart: %s (name: %s)\n", st->id, st->name);
+ if(rrdset_flag_check(st, RRDSET_FLAG_ENABLED) && st->dimensions) {
+ rrdset_rdlock(st);
+
+ // for each dimension
+ RRDDIM *rd;
+ rrddim_foreach_read(rd, st) {
+ if(rd->counter) {
+ char dimension[SHELL_ELEMENT_MAX + 1];
+ shell_name_copy(dimension, rd->id, SHELL_ELEMENT_MAX);
+
+ calculated_number n = rd->last_stored_value;
+
+ if(isnan(n) || isinf(n))
+ buffer_sprintf(wb, "NETDATA_%s_%s=\"\" # %s\n", chart, dimension, st->units);
+ else {
+ if(rd->multiplier < 0 || rd->divisor < 0) n = -n;
+ n = roundl(n);
+ if(!rrddim_flag_check(rd, RRDDIM_FLAG_HIDDEN)) total += n;
+ buffer_sprintf(wb, "NETDATA_%s_%s=\"%0.0Lf\" # %s\n", chart, dimension, n, st->units);
+ }
+ }
+ }
+
+ total = roundl(total);
+ buffer_sprintf(wb, "NETDATA_%s_VISIBLETOTAL=\"%0.0Lf\" # %s\n", chart, total, st->units);
+ rrdset_unlock(st);
+ }
+ }
+
+ buffer_strcat(wb, "\n# NETDATA ALARMS RUNNING\n");
+
+ RRDCALC *rc;
+ for(rc = host->alarms; rc ;rc = rc->next) {
+ if(!rc->rrdset) continue;
+
+ char chart[SHELL_ELEMENT_MAX + 1];
+ shell_name_copy(chart, rc->rrdset->id, SHELL_ELEMENT_MAX);
+
+ char alarm[SHELL_ELEMENT_MAX + 1];
+ shell_name_copy(alarm, rc->name, SHELL_ELEMENT_MAX);
+
+ calculated_number n = rc->value;
+
+ if(isnan(n) || isinf(n))
+ buffer_sprintf(wb, "NETDATA_ALARM_%s_%s_VALUE=\"\" # %s\n", chart, alarm, rc->units);
+ else {
+ n = roundl(n);
+ buffer_sprintf(wb, "NETDATA_ALARM_%s_%s_VALUE=\"%0.0Lf\" # %s\n", chart, alarm, n, rc->units);
+ }
+
+ buffer_sprintf(wb, "NETDATA_ALARM_%s_%s_STATUS=\"%s\"\n", chart, alarm, rrdcalc_status2string(rc->status));
+ }
+
+ rrdhost_unlock(host);
+}
+
+// ----------------------------------------------------------------------------
unsigned long rrd_stats_one_json(RRDSET *st, char *options, BUFFER *wb)
{
- time_t now = time(NULL);
+ time_t now = now_realtime_sec();
- pthread_rwlock_rdlock(&st->rwlock);
+ rrdset_rdlock(st);
buffer_sprintf(wb,
"\t\t{\n"
, st->context
, st->title
, st->priority
- , st->enabled
+ , rrdset_flag_check(st, RRDSET_FLAG_ENABLED)?1:0
, st->units
, st->name, options?options:""
, rrdset_type_name(st->chart_type)
, rrdset_last_entry_t(st)
, (now < rrdset_last_entry_t(st)) ? (time_t)0 : now - rrdset_last_entry_t(st)
, st->update_every
- , st->isdetail
+ , rrdset_flag_check(st, RRDSET_FLAG_DETAIL)?1:0
, st->usec_since_last_update
, st->collected_total
, st->last_collected_total
unsigned long memory = st->memsize;
RRDDIM *rd;
- for(rd = st->dimensions; rd ; rd = rd->next) {
+ rrddim_foreach_read(rd, st) {
memory += rd->memsize;
"\t\t\t\t\t\"entries\": %ld,\n"
"\t\t\t\t\t\"isHidden\": %d,\n"
"\t\t\t\t\t\"algorithm\": \"%s\",\n"
- "\t\t\t\t\t\"multiplier\": %ld,\n"
- "\t\t\t\t\t\"divisor\": %ld,\n"
+ "\t\t\t\t\t\"multiplier\": " COLLECTED_NUMBER_FORMAT ",\n"
+ "\t\t\t\t\t\"divisor\": " COLLECTED_NUMBER_FORMAT ",\n"
"\t\t\t\t\t\"last_entry_t\": %ld,\n"
"\t\t\t\t\t\"collected_value\": " COLLECTED_NUMBER_FORMAT ",\n"
"\t\t\t\t\t\"calculated_value\": " CALCULATED_NUMBER_FORMAT ",\n"
, rd->id
, rd->name
, rd->entries
- , (rd->flags & RRDDIM_FLAG_HIDDEN)?1:0
- , rrddim_algorithm_name(rd->algorithm)
+ , rrddim_flag_check(rd, RRDDIM_FLAG_HIDDEN)?1:0
+ , rrd_algorithm_name(rd->algorithm)
, rd->multiplier
, rd->divisor
, rd->last_collected_time.tv_sec
, memory
);
- pthread_rwlock_unlock(&st->rwlock);
+ rrdset_unlock(st);
return memory;
}
buffer_strcat(wb, RRD_GRAPH_JSON_FOOTER);
}
-void rrd_stats_all_json(BUFFER *wb)
+void rrd_stats_all_json(RRDHOST *host, BUFFER *wb)
{
unsigned long memory = 0;
- long c;
+ long c = 0;
RRDSET *st;
buffer_strcat(wb, RRD_GRAPH_JSON_HEADER);
- pthread_rwlock_rdlock(&localhost.rrdset_root_rwlock);
- for(st = localhost.rrdset_root, c = 0; st ; st = st->next) {
- if(st->enabled && st->dimensions) {
+ rrdhost_rdlock(host);
+ rrdset_foreach_read(st, host) {
+ if(rrdset_flag_check(st, RRDSET_FLAG_ENABLED) && st->dimensions) {
if(c) buffer_strcat(wb, ",\n");
memory += rrd_stats_one_json(st, NULL, wb);
c++;
}
}
- pthread_rwlock_unlock(&localhost.rrdset_root_rwlock);
+ rrdhost_unlock(host);
buffer_sprintf(wb, "\n\t],\n"
"\t\"hostname\": \"%s\",\n"
"\t\"history\": %d,\n"
"\t\"memory\": %lu\n"
"}\n"
- , localhost.hostname
- , rrd_update_every
- , rrd_default_history_entries
+ , host->hostname
+ , host->rrd_update_every
+ , host->rrd_history_entries
, memory
);
}
#define RRDR_RESET 0x02 // the dimension contains / the value is reset
#define RRDR_HIDDEN 0x04 // the dimension contains / the value is hidden
#define RRDR_NONZERO 0x08 // the dimension contains / the value is non-zero
+#define RRDR_SELECTED 0x10 // the dimension is selected
// RRDR result options
#define RRDR_RESULT_OPTION_ABSOLUTE 0x00000001
}
*/
-void rrdr_disable_not_selected_dimensions(RRDR *r, const char *dims)
-{
+void rrdr_disable_not_selected_dimensions(RRDR *r, uint32_t options, const char *dims) {
+ rrdset_check_rdlock(r->st);
+
+ if(unlikely(!dims || !*dims)) return;
+
char b[strlen(dims) + 1];
char *o = b, *tok;
strcpy(o, dims);
- long c;
+ long c, dims_selected = 0, dims_not_hidden_not_zero = 0;
RRDDIM *d;
// disable all of them
// find it and enable it
for(c = 0, d = r->st->dimensions; d ;c++, d = d->next) {
- if(unlikely((hash == d->hash && !strcmp(d->id, tok)) || !strcmp(d->name, tok))) {
- r->od[c] &= ~RRDR_HIDDEN;
+ if(unlikely((hash == d->hash && !strcmp(d->id, tok)) || (hash == d->hash_name && !strcmp(d->name, tok)))) {
+
+ if(likely(r->od[c] & RRDR_HIDDEN)) {
+ r->od[c] |= RRDR_SELECTED;
+ r->od[c] &= ~RRDR_HIDDEN;
+ dims_selected++;
+ }
// since the user needs this dimension
// make it appear as NONZERO, to return it
// even if the dimension has only zeros
- r->od[c] |= RRDR_NONZERO;
+ // unless option non_zero is set
+ if(likely(!(options & RRDR_OPTION_NONZERO)))
+ r->od[c] |= RRDR_NONZERO;
+
+ // count the visible dimensions
+ if(likely(r->od[c] & RRDR_NONZERO))
+ dims_not_hidden_not_zero++;
}
}
}
+
+ // check if all dimensions are hidden
+ if(unlikely(!dims_not_hidden_not_zero && dims_selected)) {
+ // there are a few selected dimensions
+ // but they are all zero
+ // enable the selected ones
+ // to avoid returning an empty chart
+ for(c = 0, d = r->st->dimensions; d ;c++, d = d->next)
+ if(unlikely(r->od[c] & RRDR_SELECTED))
+ r->od[c] |= RRDR_NONZERO;
+ }
}
void rrdr_buffer_print_format(BUFFER *wb, uint32_t format)
uint32_t rrdr_check_options(RRDR *r, uint32_t options, const char *dims)
{
+ rrdset_check_rdlock(r->st);
+
+ (void)dims;
+
if(options & RRDR_OPTION_NONZERO) {
long i;
- if(dims && *dims) {
+ // commented due to #1514
+
+ //if(dims && *dims) {
// the caller wants specific dimensions
// disable NONZERO option
// to make sure we don't accidentally prevent
// the specific dimensions from being returned
- i = 0;
- }
- else {
+ // i = 0;
+ //}
+ //else {
// find how many dimensions are not zero
long c;
RRDDIM *rd;
if(unlikely(!(r->od[c] & RRDR_NONZERO))) continue;
i++;
}
- }
+ //}
// if with nonzero we get i = 0 (no dimensions will be returned)
// disable nonzero to show all dimensions
void rrdr_json_wrapper_begin(RRDR *r, BUFFER *wb, uint32_t format, uint32_t options, int string_value)
{
+ rrdset_check_rdlock(r->st);
+
long rows = rrdr_rows(r);
long c, i;
RRDDIM *rd;
static void rrdr2json(RRDR *r, BUFFER *wb, uint32_t options, int datatable)
{
+ rrdset_check_rdlock(r->st);
+
//info("RRD2JSON(): %s: BEGIN", r->st->id);
int row_annotations = 0, dates, dates_with_new = 0;
char kq[2] = "", // key quote
else {
kq[0] = '"';
sq[0] = '"';
- if((options & RRDR_OPTION_SECONDS) || (options & RRDR_OPTION_MILLISECONDS)) {
- dates = JSON_DATES_TIMESTAMP;
- dates_with_new = 0;
- }
- else {
+ if(options & RRDR_OPTION_GOOGLE_JSON) {
dates = JSON_DATES_JS;
dates_with_new = 1;
}
+ else {
+ dates = JSON_DATES_TIMESTAMP;
+ dates_with_new = 0;
+ }
if( options & RRDR_OPTION_OBJECTSROWS )
strcpy(pre_date, " { ");
else
static void rrdr2csv(RRDR *r, BUFFER *wb, uint32_t options, const char *startline, const char *separator, const char *endline, const char *betweenlines)
{
+ rrdset_check_rdlock(r->st);
+
//info("RRD2CSV(): %s: BEGIN", r->st->id);
long c, i;
RRDDIM *d;
}
inline static calculated_number rrdr2value(RRDR *r, long i, uint32_t options, int *all_values_are_null) {
+ rrdset_check_rdlock(r->st);
+
long c;
RRDDIM *d;
return;
}
- pthread_rwlock_rdlock(&r->st->rwlock);
+ rrdset_rdlock(r->st);
r->has_st_lock = 1;
}
}
if(likely(r->has_st_lock)) {
- pthread_rwlock_unlock(&r->st->rwlock);
+ rrdset_unlock(r->st);
r->has_st_lock = 0;
}
}
rrdr_lock_rrdset(r);
RRDDIM *rd;
- for(rd = st->dimensions ; rd ; rd = rd->next) r->d++;
+ rrddim_foreach_read(rd, st) r->d++;
r->n = n;
// set the hidden flag on hidden dimensions
int c;
for(c = 0, rd = st->dimensions ; rd ; c++, rd = rd->next) {
- if(unlikely(rd->flags & RRDDIM_FLAG_HIDDEN)) r->od[c] = RRDR_HIDDEN;
- else r->od[c] = 0;
+ if(unlikely(rrddim_flag_check(rd, RRDDIM_FLAG_HIDDEN)))
+ r->od[c] = RRDR_HIDDEN;
+ else
+ r->od[c] = 0;
}
r->c = -1;
RRDR *rrd2rrdr(RRDSET *st, long points, long long after, long long before, int group_method, int aligned)
{
- int debug = st->debug;
+ int debug = rrdset_flag_check(st, RRDSET_FLAG_DEBUG)?1:0;
int absolute_period_requested = -1;
time_t first_entry_t = rrdset_first_entry_t(st);
// initialize them
RRDDIM *rd;
long c;
+ rrdset_check_rdlock(st);
for( rd = st->dimensions, c = 0 ; rd && c < dimensions ; rd = rd->next, c++) {
last_values[c] = 0;
group_values[c] = (group_method == GROUP_MAX || group_method == GROUP_MIN)?NAN:0;
options = rrdr_check_options(r, options, dimensions);
if(dimensions)
- rrdr_disable_not_selected_dimensions(r, dimensions);
+ rrdr_disable_not_selected_dimensions(r, options, dimensions);
if(db_after) *db_after = r->after;
if(db_before) *db_before = r->before;
options = rrdr_check_options(r, options, (dimensions)?buffer_tostring(dimensions):NULL);
if(dimensions)
- rrdr_disable_not_selected_dimensions(r, buffer_tostring(dimensions));
+ rrdr_disable_not_selected_dimensions(r, options, buffer_tostring(dimensions));
if(latest_timestamp && rrdr_rows(r) > 0)
*latest_timestamp = r->before;
time_t rrd_stats_json(int type, RRDSET *st, BUFFER *wb, long points, long group, int group_method, time_t after, time_t before, int only_non_zero)
{
int c;
- pthread_rwlock_rdlock(&st->rwlock);
+ rrdset_rdlock(st);
// -------------------------------------------------------------------------
int dimensions = 0;
RRDDIM *rd;
- for( rd = st->dimensions ; rd ; rd = rd->next) dimensions++;
+ rrddim_foreach_read(rd, st) dimensions++;
if(!dimensions) {
- pthread_rwlock_unlock(&st->rwlock);
+ rrdset_unlock(st);
buffer_strcat(wb, "No dimensions yet.");
return 0;
}
// -------------------------------------------------------------------------
// checks for debugging
- if(st->debug) {
+ if(rrdset_flag_check(st, RRDSET_FLAG_DEBUG)) {
debug(D_RRD_STATS, "%s first_entry_t = %ld, last_entry_t = %ld, duration = %ld, after = %ld, before = %ld, duration = %ld, entries_to_show = %ld, group = %ld"
, st->id
, rrdset_first_entry_t(st)
// initialize them
for( rd = st->dimensions, c = 0 ; rd && c < dimensions ; rd = rd->next, c++) {
group_values[c] = 0;
- print_hidden[c] = (rd->flags & RRDDIM_FLAG_HIDDEN)?1:0;
+ print_hidden[c] = rrddim_flag_check(rd, RRDDIM_FLAG_HIDDEN)?1:0;
found_non_zero[c] = 0;
found_non_existing[c] = 0;
}
long count = 0, printed = 0, group_count = 0;
last_timestamp = 0;
- if(st->debug) debug(D_RRD_STATS, "%s: REQUEST after:%u before:%u, points:%ld, group:%ld, CHART cur:%ld first: %u last:%u, CALC start_t:%ld, stop_t:%ld"
+ if(unlikely(rrdset_flag_check(st, RRDSET_FLAG_DEBUG)))
+ debug(D_RRD_STATS, "%s: REQUEST after:%u before:%u, points:%ld, group:%ld, CHART cur:%ld first: %u last:%u, CALC start_t:%ld, stop_t:%ld"
, st->id
, (uint32_t)after
, (uint32_t)before
int print_this = 0;
- if(st->debug) debug(D_RRD_STATS, "%s t = %ld, count = %ld, group_count = %ld, printed = %ld, now = %ld, %s %s"
+ if(unlikely(rrdset_flag_check(st, RRDSET_FLAG_DEBUG)))
+ debug(D_RRD_STATS, "%s t = %ld, count = %ld, group_count = %ld, printed = %ld, now = %ld, %s %s"
, st->id
, t
, count + 1
} // max_loop
- debug(D_RRD_STATS, "RRD_STATS_JSON: %s total %lu bytes", st->name, wb->len);
+ debug(D_RRD_STATS, "RRD_STATS_JSON: %s total %zu bytes", st->name, wb->len);
- pthread_rwlock_unlock(&st->rwlock);
+ rrdset_unlock(st);
return last_timestamp;
}