Skip to content

Commit

Permalink
Merge pull request #1504 from ktsaou/master
Browse files Browse the repository at this point in the history
prometheus support & shell scripts integration with netdata
  • Loading branch information
ktsaou committed Jan 5, 2017
2 parents b285b13 + e60a258 commit db45632
Show file tree
Hide file tree
Showing 10 changed files with 314 additions and 57 deletions.
2 changes: 1 addition & 1 deletion src/health.c
Expand Up @@ -1023,7 +1023,7 @@ void rrdsetvar_free(RRDSETVAR *rs) {
// ----------------------------------------------------------------------------
// RRDCALC management

static inline const char *rrdcalc_status2string(int status) {
inline const char *rrdcalc_status2string(int status) {
switch(status) {
case RRDCALC_STATUS_REMOVED:
return "REMOVED";
Expand Down
2 changes: 2 additions & 0 deletions src/health.h
Expand Up @@ -358,4 +358,6 @@ extern RRDVAR *rrdvar_custom_host_variable_create(RRDHOST *host, const char *nam
extern void rrdvar_custom_host_variable_destroy(RRDHOST *host, const char *name);
extern void rrdvar_custom_host_variable_set(RRDVAR *rv, calculated_number value);

extern const char *rrdcalc_status2string(int status);

#endif //NETDATA_HEALTH_H
170 changes: 169 additions & 1 deletion src/rrd2json.c
Expand Up @@ -107,7 +107,8 @@ void rrd_stats_api_v1_charts(BUFFER *wb)

RRDCALC *rc;
for(rc = localhost.alarms; rc ; rc = rc->next) {
alarms++;
if(rc->rrdset)
alarms++;
}
pthread_rwlock_unlock(&localhost.rrdset_root_rwlock);

Expand All @@ -124,6 +125,173 @@ void rrd_stats_api_v1_charts(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(unlikely(!isalnum(c))) *d = '_';
else *d = c;
}
*d = '\0';

return n;
}

#define PROMETHEUS_ELEMENT_MAX 256

void rrd_stats_api_v1_charts_allmetrics_prometheus(BUFFER *wb)
{
pthread_rwlock_rdlock(&localhost.rrdset_root_rwlock);

char host[PROMETHEUS_ELEMENT_MAX + 1];
prometheus_name_copy(host, config_get("global", "hostname", "localhost"), PROMETHEUS_ELEMENT_MAX);

// for each chart
RRDSET *st;
for(st = localhost.rrdset_root; st ; st = st->next) {
char chart[PROMETHEUS_ELEMENT_MAX + 1];
prometheus_name_copy(chart, st->id, PROMETHEUS_ELEMENT_MAX);

buffer_strcat(wb, "\n");
if(st->enabled && st->dimensions) {
pthread_rwlock_rdlock(&st->rwlock);

// for each dimension
RRDDIM *rd;
for(rd = st->dimensions; rd ; rd = rd->next) {
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 RRDDIM_INCREMENTAL:
case RRDDIM_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, host, rd->last_collected_value,
(unsigned long long)((rd->last_collected_time.tv_sec * 1000) + (rd->last_collected_time.tv_usec / 1000)));

}
}

pthread_rwlock_unlock(&st->rwlock);
}
}

pthread_rwlock_unlock(&localhost.rrdset_root_rwlock);
}

// ----------------------------------------------------------------------------
// 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(BUFFER *wb)
{
pthread_rwlock_rdlock(&localhost.rrdset_root_rwlock);

char host[SHELL_ELEMENT_MAX + 1];
shell_name_copy(host, config_get("global", "hostname", "localhost"), SHELL_ELEMENT_MAX);

// for each chart
RRDSET *st;
for(st = localhost.rrdset_root; st ; st = st->next) {
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(st->enabled && st->dimensions) {
pthread_rwlock_rdlock(&st->rwlock);

// for each dimension
RRDDIM *rd;
for(rd = st->dimensions; rd ; rd = rd->next) {
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(!(rd->flags & 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);
pthread_rwlock_unlock(&st->rwlock);
}
}

buffer_strcat(wb, "\n# NETDATA ALARMS RUNNING\n");

RRDCALC *rc;
for(rc = localhost.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));
}

pthread_rwlock_unlock(&localhost.rrdset_root_rwlock);
}

// ----------------------------------------------------------------------------

unsigned long rrd_stats_one_json(RRDSET *st, char *options, BUFFER *wb)
{
Expand Down
10 changes: 9 additions & 1 deletion src/rrd2json.h
Expand Up @@ -2,7 +2,6 @@
#define NETDATA_RRD2JSON_H 1

#define HOSTNAME_MAX 1024
extern char *hostname;

#define API_RELATIVE_TIME_MAX (3 * 365 * 86400)

Expand Down Expand Up @@ -32,6 +31,12 @@ extern char *hostname;
#define DATASOURCE_FORMAT_SSV_COMMA "ssvcomma"
#define DATASOURCE_FORMAT_CSV_JSON_ARRAY "csvjsonarray"

#define ALLMETRICS_FORMAT_SHELL "shell"
#define ALLMETRICS_FORMAT_PROMETHEUS "prometheus"

#define ALLMETRICS_SHELL 1
#define ALLMETRICS_PROMETHEUS 2

#define GROUP_UNDEFINED 0
#define GROUP_AVERAGE 1
#define GROUP_MIN 2
Expand All @@ -56,6 +61,9 @@ extern char *hostname;
extern void rrd_stats_api_v1_chart(RRDSET *st, BUFFER *wb);
extern void rrd_stats_api_v1_charts(BUFFER *wb);

extern void rrd_stats_api_v1_charts_allmetrics_shell(BUFFER *wb);
extern void rrd_stats_api_v1_charts_allmetrics_prometheus(BUFFER *wb);

extern unsigned long rrd_stats_one_json(RRDSET *st, char *options, BUFFER *wb);

extern void rrd_stats_graph_json(RRDSET *st, char *options, BUFFER *wb);
Expand Down
78 changes: 45 additions & 33 deletions src/sys_fs_cgroup.c
Expand Up @@ -1005,93 +1005,105 @@ void find_all_cgroups() {
// check for newly added cgroups
// and update the filenames they read
char filename[FILENAME_MAX + 1];
if(cgroup_enable_cpuacct_stat && !cg->cpuacct_stat.filename) {
if(unlikely(cgroup_enable_cpuacct_stat && !cg->cpuacct_stat.filename)) {
snprintfz(filename, FILENAME_MAX, "%s%s/cpuacct.stat", cgroup_cpuacct_base, cg->id);
if(stat(filename, &buf) != -1) {
cg->cpuacct_stat.filename = strdupz(filename);
debug(D_CGROUP, "cpuacct.stat filename for cgroup '%s': '%s'", cg->id, cg->cpuacct_stat.filename);
}
else debug(D_CGROUP, "cpuacct.stat file for cgroup '%s': '%s' does not exist.", cg->id, filename);
}
if(cgroup_enable_cpuacct_usage && !cg->cpuacct_usage.filename) {

if(unlikely(cgroup_enable_cpuacct_usage && !cg->cpuacct_usage.filename)) {
snprintfz(filename, FILENAME_MAX, "%s%s/cpuacct.usage_percpu", cgroup_cpuacct_base, cg->id);
if(stat(filename, &buf) != -1) {
cg->cpuacct_usage.filename = strdupz(filename);
debug(D_CGROUP, "cpuacct.usage_percpu filename for cgroup '%s': '%s'", cg->id, cg->cpuacct_usage.filename);
}
else debug(D_CGROUP, "cpuacct.usage_percpu file for cgroup '%s': '%s' does not exist.", cg->id, filename);
}
if(cgroup_enable_memory && !cg->memory.filename) {
snprintfz(filename, FILENAME_MAX, "%s%s/memory.stat", cgroup_memory_base, cg->id);
if(stat(filename, &buf) != -1) {
cg->memory.filename = strdupz(filename);
debug(D_CGROUP, "memory.stat filename for cgroup '%s': '%s'", cg->id, cg->memory.filename);
}
else debug(D_CGROUP, "memory.stat file for cgroup '%s': '%s' does not exist.", cg->id, filename);

snprintfz(filename, FILENAME_MAX, "%s%s/memory.usage_in_bytes", cgroup_memory_base, cg->id);
if(stat(filename, &buf) != -1) {
cg->memory.filename_usage_in_bytes = strdupz(filename);
debug(D_CGROUP, "memory.usage_in_bytes filename for cgroup '%s': '%s'", cg->id, cg->memory.filename_usage_in_bytes);
if(unlikely(cgroup_enable_memory)) {
if(unlikely(!cg->memory.filename)) {
snprintfz(filename, FILENAME_MAX, "%s%s/memory.stat", cgroup_memory_base, cg->id);
if(stat(filename, &buf) != -1) {
cg->memory.filename = strdupz(filename);
debug(D_CGROUP, "memory.stat filename for cgroup '%s': '%s'", cg->id, cg->memory.filename);
}
else
debug(D_CGROUP, "memory.stat file for cgroup '%s': '%s' does not exist.", cg->id, filename);
}
else debug(D_CGROUP, "memory.usage_in_bytes file for cgroup '%s': '%s' does not exist.", cg->id, filename);

snprintfz(filename, FILENAME_MAX, "%s%s/memory.msw_usage_in_bytes", cgroup_memory_base, cg->id);
if(stat(filename, &buf) != -1) {
cg->memory.filename_msw_usage_in_bytes = strdupz(filename);
debug(D_CGROUP, "memory.msw_usage_in_bytes filename for cgroup '%s': '%s'", cg->id, cg->memory.filename_msw_usage_in_bytes);
if(unlikely(!cg->memory.filename_usage_in_bytes)) {
snprintfz(filename, FILENAME_MAX, "%s%s/memory.usage_in_bytes", cgroup_memory_base, cg->id);
if(stat(filename, &buf) != -1) {
cg->memory.filename_usage_in_bytes = strdupz(filename);
debug(D_CGROUP, "memory.usage_in_bytes filename for cgroup '%s': '%s'", cg->id, cg->memory.filename_usage_in_bytes);
}
else
debug(D_CGROUP, "memory.usage_in_bytes file for cgroup '%s': '%s' does not exist.", cg->id, filename);
}
else debug(D_CGROUP, "memory.msw_usage_in_bytes file for cgroup '%s': '%s' does not exist.", cg->id, filename);

snprintfz(filename, FILENAME_MAX, "%s%s/memory.failcnt", cgroup_memory_base, cg->id);
if(stat(filename, &buf) != -1) {
cg->memory.filename_failcnt = strdupz(filename);
debug(D_CGROUP, "memory.failcnt filename for cgroup '%s': '%s'", cg->id, cg->memory.filename_failcnt);
if(unlikely(!cg->memory.filename_msw_usage_in_bytes)) {
snprintfz(filename, FILENAME_MAX, "%s%s/memory.msw_usage_in_bytes", cgroup_memory_base, cg->id);
if(stat(filename, &buf) != -1) {
cg->memory.filename_msw_usage_in_bytes = strdupz(filename);
debug(D_CGROUP, "memory.msw_usage_in_bytes filename for cgroup '%s': '%s'", cg->id, cg->memory.filename_msw_usage_in_bytes);
}
else
debug(D_CGROUP, "memory.msw_usage_in_bytes file for cgroup '%s': '%s' does not exist.", cg->id, filename);
}
if(unlikely(!cg->memory.filename_failcnt)) {
snprintfz(filename, FILENAME_MAX, "%s%s/memory.failcnt", cgroup_memory_base, cg->id);
if(stat(filename, &buf) != -1) {
cg->memory.filename_failcnt = strdupz(filename);
debug(D_CGROUP, "memory.failcnt filename for cgroup '%s': '%s'", cg->id, cg->memory.filename_failcnt);
}
else
debug(D_CGROUP, "memory.failcnt file for cgroup '%s': '%s' does not exist.", cg->id, filename);
}
else debug(D_CGROUP, "memory.failcnt file for cgroup '%s': '%s' does not exist.", cg->id, filename);
}
if(cgroup_enable_blkio) {
if(!cg->io_service_bytes.filename) {

if(unlikely(cgroup_enable_blkio)) {
if(unlikely(!cg->io_service_bytes.filename)) {
snprintfz(filename, FILENAME_MAX, "%s%s/blkio.io_service_bytes", cgroup_blkio_base, cg->id);
if(stat(filename, &buf) != -1) {
cg->io_service_bytes.filename = strdupz(filename);
debug(D_CGROUP, "io_service_bytes filename for cgroup '%s': '%s'", cg->id, cg->io_service_bytes.filename);
}
else debug(D_CGROUP, "io_service_bytes file for cgroup '%s': '%s' does not exist.", cg->id, filename);
}
if(!cg->io_serviced.filename) {
if(unlikely(!cg->io_serviced.filename)) {
snprintfz(filename, FILENAME_MAX, "%s%s/blkio.io_serviced", cgroup_blkio_base, cg->id);
if(stat(filename, &buf) != -1) {
cg->io_serviced.filename = strdupz(filename);
debug(D_CGROUP, "io_serviced filename for cgroup '%s': '%s'", cg->id, cg->io_serviced.filename);
}
else debug(D_CGROUP, "io_serviced file for cgroup '%s': '%s' does not exist.", cg->id, filename);
}
if(!cg->throttle_io_service_bytes.filename) {
if(unlikely(!cg->throttle_io_service_bytes.filename)) {
snprintfz(filename, FILENAME_MAX, "%s%s/blkio.throttle.io_service_bytes", cgroup_blkio_base, cg->id);
if(stat(filename, &buf) != -1) {
cg->throttle_io_service_bytes.filename = strdupz(filename);
debug(D_CGROUP, "throttle_io_service_bytes filename for cgroup '%s': '%s'", cg->id, cg->throttle_io_service_bytes.filename);
}
else debug(D_CGROUP, "throttle_io_service_bytes file for cgroup '%s': '%s' does not exist.", cg->id, filename);
}
if(!cg->throttle_io_serviced.filename) {
if(unlikely(!cg->throttle_io_serviced.filename)) {
snprintfz(filename, FILENAME_MAX, "%s%s/blkio.throttle.io_serviced", cgroup_blkio_base, cg->id);
if(stat(filename, &buf) != -1) {
cg->throttle_io_serviced.filename = strdupz(filename);
debug(D_CGROUP, "throttle_io_serviced filename for cgroup '%s': '%s'", cg->id, cg->throttle_io_serviced.filename);
}
else debug(D_CGROUP, "throttle_io_serviced file for cgroup '%s': '%s' does not exist.", cg->id, filename);
}
if(!cg->io_merged.filename) {
if(unlikely(!cg->io_merged.filename)) {
snprintfz(filename, FILENAME_MAX, "%s%s/blkio.io_merged", cgroup_blkio_base, cg->id);
if(stat(filename, &buf) != -1) {
cg->io_merged.filename = strdupz(filename);
debug(D_CGROUP, "io_merged filename for cgroup '%s': '%s'", cg->id, cg->io_merged.filename);
}
else debug(D_CGROUP, "io_merged file for cgroup '%s': '%s' does not exist.", cg->id, filename);
}
if(!cg->io_queued.filename) {
if(unlikely(!cg->io_queued.filename)) {
snprintfz(filename, FILENAME_MAX, "%s%s/blkio.io_queued", cgroup_blkio_base, cg->id);
if(stat(filename, &buf) != -1) {
cg->io_queued.filename = strdupz(filename);
Expand Down
1 change: 1 addition & 0 deletions src/web_buffer.h
Expand Up @@ -39,6 +39,7 @@ typedef struct web_buffer {
#define CT_IMAGE_XICON 19
#define CT_IMAGE_ICNS 20
#define CT_IMAGE_BMP 21
#define CT_PROMETHEUS 22

#define buffer_cacheable(wb) do { (wb)->options |= WB_CONTENT_CACHEABLE; if((wb)->options & WB_CONTENT_NO_CACHEABLE) (wb)->options &= ~WB_CONTENT_NO_CACHEABLE; } while(0)
#define buffer_no_cacheable(wb) do { (wb)->options |= WB_CONTENT_NO_CACHEABLE; if((wb)->options & WB_CONTENT_CACHEABLE) (wb)->options &= ~WB_CONTENT_CACHEABLE; (wb)->expires = 0; } while(0)
Expand Down

0 comments on commit db45632

Please sign in to comment.