diff --git a/src/cpu.c b/src/cpu.c index c06cc32799..d2cdb7dfb5 100644 --- a/src/cpu.c +++ b/src/cpu.c @@ -139,7 +139,7 @@ typedef struct { /* count is a scaled counter, so that all states in sum increase by 1000000 * per second. */ - fpcounter_t count; + double count; bool has_count; rate_to_value_state_t to_count; } usage_state_t; @@ -488,9 +488,9 @@ static void usage_finalize(usage_t *u) { gauge_t ratio = us->rate / cpu_rate; value_t v = {0}; int status = rate_to_value(&v, ratio, &us->to_count, - METRIC_TYPE_FPCOUNTER, u->time); + METRIC_TYPE_COUNTER_FP, u->time); if (status == 0) { - us->count = v.fpcounter; + us->count = v.counter_fp; us->has_count = true; } @@ -504,16 +504,16 @@ static void usage_finalize(usage_t *u) { us->count = NAN; if (!us->has_rate) { /* Ensure that us->to_count is initialized. */ - rate_to_value(&(value_t){0}, 0.0, &us->to_count, METRIC_TYPE_FPCOUNTER, + rate_to_value(&(value_t){0}, 0.0, &us->to_count, METRIC_TYPE_COUNTER_FP, u->time); continue; } value_t v = {0}; int status = rate_to_value(&v, state_ratio[s], &us->to_count, - METRIC_TYPE_FPCOUNTER, u->time); + METRIC_TYPE_COUNTER_FP, u->time); if (status == 0) { - us->count = v.fpcounter; + us->count = v.counter_fp; us->has_count = true; } } @@ -554,7 +554,7 @@ static gauge_t usage_ratio(usage_t *u, size_t cpu, state_t state) { return usage_rate(u, cpu, state) / global_rate; } -static fpcounter_t usage_count(usage_t *u, size_t cpu, state_t state) { +static double usage_count(usage_t *u, size_t cpu, state_t state) { usage_finalize(u); usage_state_t us; @@ -598,7 +598,7 @@ static void commit_cpu_usage(usage_t *u, size_t cpu_num) { .name = "system.cpu.time", .help = "Microseconds each logical CPU spent in each state", .unit = "s", - .type = METRIC_TYPE_FPCOUNTER, + .type = METRIC_TYPE_COUNTER_FP, }; metric_t m = {0}; @@ -610,18 +610,18 @@ static void commit_cpu_usage(usage_t *u, size_t cpu_num) { if (report_by_state) { for (state_t state = 0; state < STATE_ACTIVE; state++) { - fpcounter_t usage = usage_count(u, cpu_num, state); + double usage = usage_count(u, cpu_num, state); if (isnan(usage)) { continue; } metric_family_append(&fam, label_state, cpu_state_names[state], - (value_t){.fpcounter = usage}, &m); + (value_t){.counter_fp = usage}, &m); } } else { - fpcounter_t usage = usage_count(u, cpu_num, STATE_ACTIVE); + double usage = usage_count(u, cpu_num, STATE_ACTIVE); if (!isnan(usage)) { metric_family_append(&fam, label_state, cpu_state_names[STATE_ACTIVE], - (value_t){.fpcounter = usage}, &m); + (value_t){.counter_fp = usage}, &m); } } diff --git a/src/cpu_test.c b/src/cpu_test.c index cab4e23c7f..a0f9f8aa2d 100644 --- a/src/cpu_test.c +++ b/src/cpu_test.c @@ -124,7 +124,7 @@ DEF_TEST(usage_ratio) { return 0; } -static bool expect_usage_count(fpcounter_t want, fpcounter_t got, size_t cpu, +static bool expect_usage_count(double want, double got, size_t cpu, state_t state) { char cpu_str[64] = "CPU_ALL"; if (cpu != SIZE_MAX) { @@ -174,8 +174,8 @@ DEF_TEST(usage_count) { } } - fpcounter_t state_time[STATE_MAX] = {0}; - fpcounter_t sum_time = 0; + double state_time[STATE_MAX] = {0}; + double sum_time = 0; for (size_t cpu = 0; cpu < CPU_NUM; cpu++) { derive_t active_increment = 0; for (state_t s = 0; s < STATE_ACTIVE; s++) { @@ -184,9 +184,8 @@ DEF_TEST(usage_count) { active_increment += increment; } - fpcounter_t want_time = CDTIME_T_TO_DOUBLE(interval) * - ((fpcounter_t)increment) / - ((fpcounter_t)cpu_increment[cpu]); + double want_time = CDTIME_T_TO_DOUBLE(interval) * ((double)increment) / + ((double)cpu_increment[cpu]); state_time[s] += want_time; sum_time += want_time; @@ -195,9 +194,9 @@ DEF_TEST(usage_count) { ret = ret || !ok; } - fpcounter_t want_active_time = CDTIME_T_TO_DOUBLE(interval) * - ((fpcounter_t)active_increment) / - ((fpcounter_t)cpu_increment[cpu]); + double want_active_time = CDTIME_T_TO_DOUBLE(interval) * + ((double)active_increment) / + ((double)cpu_increment[cpu]); state_time[STATE_ACTIVE] += want_active_time; bool ok = expect_usage_count(want_active_time, usage_count(&usage, cpu, STATE_ACTIVE), cpu, diff --git a/src/daemon/metric.c b/src/daemon/metric.c index 2247220ea7..25b897aca2 100644 --- a/src/daemon/metric.c +++ b/src/daemon/metric.c @@ -52,14 +52,26 @@ int value_marshal_text(strbuf_t *buf, value_t v, metric_type_t type) { switch (type) { case METRIC_TYPE_GAUGE: - case METRIC_TYPE_FPCOUNTER: + if (isnan(v.gauge)) { + return strbuf_print(buf, "nan"); + } return strbuf_printf(buf, GAUGE_FORMAT, v.gauge); case METRIC_TYPE_COUNTER: return strbuf_printf(buf, "%" PRIu64, v.counter); - default: - ERROR("Unknown metric value type: %d", (int)type); - return EINVAL; + case METRIC_TYPE_COUNTER_FP: + return strbuf_printf(buf, GAUGE_FORMAT, v.counter_fp); + case METRIC_TYPE_UP_DOWN: + return strbuf_printf(buf, "%" PRId64, v.up_down); + case METRIC_TYPE_UP_DOWN_FP: + if (isnan(v.up_down_fp)) { + return strbuf_print(buf, "nan"); + } + return strbuf_printf(buf, GAUGE_FORMAT, v.up_down_fp); + case METRIC_TYPE_UNTYPED: + break; } + ERROR("Unknown metric value type: %d", (int)type); + return EINVAL; } static int label_name_compare(void const *a, void const *b) { diff --git a/src/daemon/metric.h b/src/daemon/metric.h index 0cbc50882f..e98c2c0d10 100644 --- a/src/daemon/metric.h +++ b/src/daemon/metric.h @@ -34,25 +34,51 @@ #define METRIC_ATTR_DOUBLE 0x01 #define METRIC_ATTR_CUMULATIVE 0x02 +#define METRIC_ATTR_MONOTONIC 0x04 typedef enum { METRIC_TYPE_UNTYPED = 0, + // METRIC_TYPE_GAUGE are absolute metrics that cannot (meaningfully) be summed + // up. Examples are temperatures and utilization ratios. METRIC_TYPE_GAUGE = METRIC_ATTR_DOUBLE, - METRIC_TYPE_COUNTER = METRIC_ATTR_CUMULATIVE, - METRIC_TYPE_FPCOUNTER = METRIC_ATTR_DOUBLE | METRIC_ATTR_CUMULATIVE, + // METRIC_TYPE_COUNTER are monotonically increasing integer counts. The rate + // of change is meaningful, the absolute value is not. + METRIC_TYPE_COUNTER = METRIC_ATTR_CUMULATIVE | METRIC_ATTR_MONOTONIC, + // METRIC_TYPE_COUNTER_FP are monotonically increasing floating point counts. + // The rate of change is meaningful, the absolute value is not. + METRIC_TYPE_COUNTER_FP = + METRIC_ATTR_DOUBLE | METRIC_ATTR_CUMULATIVE | METRIC_ATTR_MONOTONIC, + // METRIC_TYPE_UP_DOWN are absolute integer metrics that can + // (meaningfully) be summed up. Examples are filesystem space used and + // physical memory. + METRIC_TYPE_UP_DOWN = METRIC_ATTR_CUMULATIVE, + // METRIC_TYPE_UP_DOWN_FP are absolute floating point metrics that can + // (meaningfully) be summed up. + METRIC_TYPE_UP_DOWN_FP = METRIC_ATTR_DOUBLE | METRIC_ATTR_CUMULATIVE, } metric_type_t; -#define IS_CUMULATIVE(t) ((t)&METRIC_ATTR_CUMULATIVE) +#define METRIC_TYPE_TO_STRING(t) \ + (t == METRIC_TYPE_GAUGE) ? "gauge" \ + : (t == METRIC_TYPE_COUNTER) ? "counter" \ + : (t == METRIC_TYPE_COUNTER_FP) ? "counter_fp" \ + : (t == METRIC_TYPE_UP_DOWN) ? "up_down" \ + : (t == METRIC_TYPE_UP_DOWN_FP) ? "up_down_fp" \ + : "unknown" + +#define IS_DOUBLE(t) ((t)&METRIC_ATTR_DOUBLE) +#define IS_MONOTONIC(t) ((t)&METRIC_ATTR_MONOTONIC) -typedef uint64_t counter_t; -typedef double fpcounter_t; typedef double gauge_t; +typedef uint64_t counter_t; typedef int64_t derive_t; union value_u { - counter_t counter; - fpcounter_t fpcounter; gauge_t gauge; + counter_t counter; + double counter_fp; + int64_t up_down; + double up_down_fp; + // For collectd 5 compatiblity. Treated the same as up_down. derive_t derive; }; typedef union value_u value_t; diff --git a/src/daemon/utils_cache.c b/src/daemon/utils_cache.c index ea0a1a7dbd..6b9fe1a6f1 100644 --- a/src/daemon/utils_cache.c +++ b/src/daemon/utils_cache.c @@ -281,6 +281,11 @@ int uc_check_timeout(void) { static int uc_update_rate(metric_t const *m, cache_entry_t *ce) { switch (m->family->type) { + case METRIC_TYPE_GAUGE: { + ce->values_gauge = m->value.gauge; + return 0; + } + case METRIC_TYPE_COUNTER: { // Counter overflows and counter resets are signaled to plugins by resetting // "first_time". Since we can't distinguish between an overflow and a @@ -297,24 +302,29 @@ static int uc_update_rate(metric_t const *m, cache_entry_t *ce) { return 0; } - case METRIC_TYPE_FPCOUNTER: { + case METRIC_TYPE_COUNTER_FP: { // For floating point counters, the logic is slightly different from // integer counters. Floating point counters don't have a (meaningful) // overflow, and we will always assume a counter reset. - if (ce->last_value.fpcounter > m->value.fpcounter) { + if (ce->last_value.counter_fp > m->value.counter_fp) { // counter reset ce->first_time = m->time; ce->first_value = m->value; ce->values_gauge = NAN; return 0; } - gauge_t diff = m->value.fpcounter - ce->last_value.fpcounter; + gauge_t diff = m->value.counter_fp - ce->last_value.counter_fp; ce->values_gauge = diff / CDTIME_T_TO_DOUBLE(m->time - ce->last_time); return 0; } - case METRIC_TYPE_GAUGE: { - ce->values_gauge = m->value.gauge; + case METRIC_TYPE_UP_DOWN: { + ce->values_gauge = (gauge_t)m->value.up_down; + return 0; + } + + case METRIC_TYPE_UP_DOWN_FP: { + ce->values_gauge = (gauge_t)m->value.up_down_fp; return 0; } @@ -465,9 +475,21 @@ int uc_get_rate(metric_t const *m, gauge_t *ret) { if (m == NULL || m->family == NULL || ret == NULL) { return EINVAL; } - if (m->family->type == METRIC_TYPE_GAUGE) { + switch (m->family->type) { + case METRIC_TYPE_GAUGE: *ret = m->value.gauge; return 0; + case METRIC_TYPE_COUNTER: + case METRIC_TYPE_COUNTER_FP: + break; + case METRIC_TYPE_UP_DOWN: + *ret = (gauge_t)m->value.up_down; + return 0; + case METRIC_TYPE_UP_DOWN_FP: + *ret = (gauge_t)m->value.up_down_fp; + return 0; + case METRIC_TYPE_UNTYPED: + return EINVAL; } strbuf_t buf = STRBUF_CREATE; diff --git a/src/daemon/utils_cache.h b/src/daemon/utils_cache.h index f67e4b8aa9..f07605cdd2 100644 --- a/src/daemon/utils_cache.h +++ b/src/daemon/utils_cache.h @@ -77,7 +77,7 @@ typedef struct { } uc_first_metric_result_t; /* uc_first_metric returns the first observed metric value and time. - * For cumulative metrics (METRIC_TYPE_COUNTER and METRIC_TYPE_FPCOUNTER), + * For cumulative metrics (METRIC_TYPE_COUNTER and METRIC_TYPE_COUNTER_FP), * counter resets and counter overflows will reset the value. */ uc_first_metric_result_t uc_first_metric(metric_t const *m); diff --git a/src/daemon/utils_cache_test.c b/src/daemon/utils_cache_test.c index ef2c66fd4d..f5ec1b5bd7 100644 --- a/src/daemon/utils_cache_test.c +++ b/src/daemon/utils_cache_test.c @@ -87,23 +87,59 @@ DEF_TEST(uc_get_rate) { .want = (23. + 18. + 1.) / (110. - 100.), }, { - .name = "fpcounter", - .first_value = (value_t){.fpcounter = 4.2}, - .second_value = (value_t){.fpcounter = 10.2}, + .name = "counter_fp", + .first_value = (value_t){.counter_fp = 4.2}, + .second_value = (value_t){.counter_fp = 10.2}, .first_time = TIME_T_TO_CDTIME_T(100), .second_time = TIME_T_TO_CDTIME_T(110), - .type = METRIC_TYPE_FPCOUNTER, + .type = METRIC_TYPE_COUNTER_FP, .want = (10.2 - 4.2) / (110 - 100), }, { - .name = "fpcounter with reset", - .first_value = (value_t){.fpcounter = 100000.0}, - .second_value = (value_t){.fpcounter = 0.2}, + .name = "counter_fp with reset", + .first_value = (value_t){.counter_fp = 100000.0}, + .second_value = (value_t){.counter_fp = 0.2}, .first_time = TIME_T_TO_CDTIME_T(100), .second_time = TIME_T_TO_CDTIME_T(110), - .type = METRIC_TYPE_FPCOUNTER, + .type = METRIC_TYPE_COUNTER_FP, .want = NAN, }, + { + .name = "up_down", + .first_value = (value_t){.up_down = 10}, + .second_value = (value_t){.up_down = 20}, + .first_time = TIME_T_TO_CDTIME_T(100), + .second_time = TIME_T_TO_CDTIME_T(110), + .type = METRIC_TYPE_UP_DOWN, + .want = 20, + }, + { + .name = "decreasing up_down", + .first_value = (value_t){.up_down = 1000}, + .second_value = (value_t){.up_down = 215}, + .first_time = TIME_T_TO_CDTIME_T(100), + .second_time = TIME_T_TO_CDTIME_T(110), + .type = METRIC_TYPE_UP_DOWN, + .want = 215, + }, + { + .name = "up_down_fp", + .first_value = (value_t){.up_down_fp = 1.0}, + .second_value = (value_t){.up_down_fp = 2.0}, + .first_time = TIME_T_TO_CDTIME_T(100), + .second_time = TIME_T_TO_CDTIME_T(110), + .type = METRIC_TYPE_UP_DOWN_FP, + .want = 2.0, + }, + { + .name = "decreasing up_down_fp", + .first_value = (value_t){.up_down_fp = 100.0}, + .second_value = (value_t){.up_down_fp = 21.5}, + .first_time = TIME_T_TO_CDTIME_T(100), + .second_time = TIME_T_TO_CDTIME_T(110), + .type = METRIC_TYPE_UP_DOWN_FP, + .want = 21.5, + }, }; for (size_t i = 0; i < STATIC_ARRAY_SIZE(cases); i++) { @@ -130,9 +166,11 @@ DEF_TEST(uc_get_rate) { EXPECT_EQ_INT(0, uc_update(&fam)); gauge_t got = 0; EXPECT_EQ_INT(0, uc_get_rate(&m, &got)); - gauge_t want = NAN; - if (fam.type == METRIC_TYPE_GAUGE) { - want = cases[i].first_value.gauge; + gauge_t want = cases[i].first_value.gauge; + if (IS_MONOTONIC(fam.type)) { + want = NAN; + } else if (fam.type == METRIC_TYPE_UP_DOWN) { + want = (gauge_t)cases[i].first_value.up_down; } EXPECT_EQ_DOUBLE(want, got); diff --git a/src/df.c b/src/df.c index 97eac7712e..59d11350b1 100644 --- a/src/df.c +++ b/src/df.c @@ -161,7 +161,7 @@ static int df_read(void) { metric_family_t fam_usage = { .name = "system.filesystem.usage", .unit = "By", - .type = METRIC_TYPE_GAUGE, + .type = METRIC_TYPE_UP_DOWN, }; metric_family_t fam_utilization = { .name = "system.filesystem.utilization", @@ -171,7 +171,7 @@ static int df_read(void) { metric_family_t fam_inode_usage = { .name = "system.filesystem.inodes.usage", .unit = "{inode}", - .type = METRIC_TYPE_GAUGE, + .type = METRIC_TYPE_UP_DOWN, }; metric_family_t fam_inode_utilization = { .name = "system.filesystem.inodes.utilization", @@ -215,7 +215,7 @@ static int df_read(void) { if (!statbuf.f_blocks) continue; - gauge_t blocksize = (gauge_t)BLOCKSIZE(statbuf); + int64_t blocksize = (int64_t)BLOCKSIZE(statbuf); /* * Sanity-check for the values in the struct @@ -240,9 +240,9 @@ static int df_read(void) { if (statbuf.f_blocks < statbuf.f_bfree) statbuf.f_blocks = statbuf.f_bfree; - gauge_t blk_free = (gauge_t)statbuf.f_bavail; - gauge_t blk_reserved = (gauge_t)(statbuf.f_bfree - statbuf.f_bavail); - gauge_t blk_used = (gauge_t)(statbuf.f_blocks - statbuf.f_bfree); + int64_t blk_free = (int64_t)statbuf.f_bavail; + int64_t blk_reserved = (int64_t)(statbuf.f_bfree - statbuf.f_bavail); + int64_t blk_used = (int64_t)(statbuf.f_blocks - statbuf.f_bfree); bool read_only = (statbuf.f_flag & ST_RDONLY) != 0; @@ -254,13 +254,13 @@ static int df_read(void) { if (report_usage) { metric_family_append(&fam_usage, state_label, state_used, - (value_t){.gauge = blk_used * blocksize}, &m); + (value_t){.up_down = blk_used * blocksize}, &m); metric_family_append(&fam_usage, state_label, state_free, - (value_t){.gauge = blk_free * blocksize}, &m); + (value_t){.up_down = blk_free * blocksize}, &m); metric_family_append(&fam_usage, state_label, state_reserved, - (value_t){.gauge = blk_reserved * blocksize}, &m); + (value_t){.up_down = blk_reserved * blocksize}, &m); } if (report_utilization) { @@ -268,13 +268,13 @@ static int df_read(void) { gauge_t f = 1.0 / (gauge_t)statbuf.f_blocks; metric_family_append(&fam_utilization, state_label, state_used, - (value_t){.gauge = blk_used * f}, &m); + (value_t){.gauge = ((gauge_t)blk_used) * f}, &m); metric_family_append(&fam_utilization, state_label, state_free, - (value_t){.gauge = blk_free * f}, &m); + (value_t){.gauge = ((gauge_t)blk_free) * f}, &m); metric_family_append(&fam_utilization, state_label, state_reserved, - (value_t){.gauge = blk_reserved * f}, &m); + (value_t){.gauge = ((gauge_t)blk_reserved) * f}, &m); } /* inode handling */ @@ -285,23 +285,25 @@ static int df_read(void) { if (statbuf.f_files < statbuf.f_ffree) statbuf.f_files = statbuf.f_ffree; - gauge_t inode_free = (gauge_t)statbuf.f_favail; - gauge_t inode_reserved = (gauge_t)(statbuf.f_ffree - statbuf.f_favail); - gauge_t inode_used = (gauge_t)(statbuf.f_files - statbuf.f_ffree); + int64_t inode_free = (int64_t)statbuf.f_favail; + int64_t inode_reserved = (int64_t)(statbuf.f_ffree - statbuf.f_favail); + int64_t inode_used = (int64_t)(statbuf.f_files - statbuf.f_ffree); if (report_utilization) { if (statbuf.f_files > 0) { gauge_t f = 1.0 / (gauge_t)statbuf.f_files; metric_family_append(&fam_inode_utilization, state_label, state_used, - (value_t){.gauge = inode_used * f}, &m); + (value_t){.gauge = ((gauge_t)inode_used) * f}, + &m); metric_family_append(&fam_inode_utilization, state_label, state_free, - (value_t){.gauge = inode_free * f}, &m); + (value_t){.gauge = ((gauge_t)inode_free) * f}, + &m); - metric_family_append(&fam_inode_utilization, state_label, - state_reserved, - (value_t){.gauge = inode_reserved * f}, &m); + metric_family_append( + &fam_inode_utilization, state_label, state_reserved, + (value_t){.gauge = ((gauge_t)inode_reserved) * f}, &m); } else { metric_reset(&m); retval = -1; @@ -310,13 +312,13 @@ static int df_read(void) { } if (report_usage) { metric_family_append(&fam_inode_usage, state_label, state_used, - (value_t){.gauge = inode_used}, &m); + (value_t){.up_down = inode_used}, &m); metric_family_append(&fam_inode_usage, state_label, state_free, - (value_t){.gauge = inode_free}, &m); + (value_t){.up_down = inode_free}, &m); metric_family_append(&fam_inode_usage, state_label, state_reserved, - (value_t){.gauge = inode_reserved}, &m); + (value_t){.up_down = inode_reserved}, &m); } } diff --git a/src/disk.c b/src/disk.c index 21d6688f70..a5dd4f22a1 100644 --- a/src/disk.c +++ b/src/disk.c @@ -389,7 +389,7 @@ static int disk_read(void) { .name = "system.disk.operation_time", .help = "Sum of the time each operation took to complete", .unit = "s", - .type = METRIC_TYPE_FPCOUNTER, + .type = METRIC_TYPE_COUNTER_FP, }; metric_family_t fam_merged = { .name = "system.disk.merged", @@ -401,7 +401,7 @@ static int disk_read(void) { .name = "system.disk.io_time", .help = "Time disk spent activated", .unit = "s", - .type = METRIC_TYPE_FPCOUNTER, + .type = METRIC_TYPE_COUNTER_FP, }; metric_family_t fam_disk_io_weighted_time = { .name = "system.disk.weighted_io_time", @@ -411,7 +411,7 @@ static int disk_read(void) { "this field. This can provide an easy measure of both I/O " "completion time and the backlog that may be accumulating.", .unit = "s", - .type = METRIC_TYPE_FPCOUNTER, + .type = METRIC_TYPE_COUNTER_FP, }; metric_family_t fam_disk_pending_operations = { .name = "system.disk.pending_operations", @@ -592,10 +592,10 @@ static int disk_read(void) { if ((read_time_ns != -1LL) || (write_time_ns != -1LL)) { metric_family_append( &fam_ops_time, direction_label, read_direction, - (value_t){.fpcounter = ((fpcounter_t)read_time_ns) / 1e9}, &m); + (value_t){.counter_fp = ((double)read_time_ns) / 1e9}, &m); metric_family_append( &fam_ops_time, direction_label, write_direction, - (value_t){.fpcounter = ((fpcounter_t)write_time_ns) / 1e9}, &m); + (value_t){.counter_fp = ((double)write_time_ns) / 1e9}, &m); } metric_reset(&m); } @@ -721,11 +721,9 @@ static int disk_read(void) { devstat_compute_etime(&snap_iter->duration[DEVSTAT_WRITE], NULL); if ((read_time_s != 0) || (write_time_s != 0)) { metric_family_append(&fam_ops_time, direction_label, read_direction, - (value_t){.fpcounter = (fpcounter_t)read_time_s}, - &m); + (value_t){.counter_fp = (double)read_time_s}, &m); metric_family_append(&fam_ops_time, direction_label, write_direction, - (value_t){.fpcounter = (fpcounter_t)write_time_s}, - &m); + (value_t){.counter_fp = (double)write_time_s}, &m); } long double busy_time = 0, utilization = 0, total_duration_s = 0; @@ -736,13 +734,13 @@ static int disk_read(void) { DSM_QUEUE_LENGTH, &queue_length, DSM_NONE) != 0) { WARNING("%s", devstat_errbuf); } else { - m.value.fpcounter = (fpcounter_t)busy_time; + m.value.counter_fp = (double)busy_time; metric_family_metric_append(&fam_disk_io_time, m); m.value.gauge = (gauge_t)utilization; metric_family_metric_append(&fam_utilization, m); - m.value.fpcounter = (fpcounter_t)total_duration_s; + m.value.counter_fp = (double)total_duration_s; metric_family_metric_append(&fam_disk_io_weighted_time, m); m.value.gauge = (gauge_t)queue_length; @@ -899,10 +897,10 @@ static int disk_read(void) { if ((read_time_ms != 0) || (write_time_ms != 0)) { metric_family_append( &fam_ops_time, direction_label, read_direction, - (value_t){.fpcounter = ((fpcounter_t)read_time_ms) / 1000}, &m); + (value_t){.counter_fp = ((double)read_time_ms) / 1000}, &m); metric_family_append( &fam_ops_time, direction_label, write_direction, - (value_t){.fpcounter = ((fpcounter_t)write_time_ms) / 1000}, &m); + (value_t){.counter_fp = ((double)write_time_ms) / 1000}, &m); } if (read_merged != 0 || write_merged != 0) { @@ -918,12 +916,12 @@ static int disk_read(void) { } if (io_time_ms != 0) { - m.value.fpcounter = ((fpcounter_t)io_time_ms) / 1000.0; + m.value.counter_fp = ((double)io_time_ms) / 1000.0; metric_family_metric_append(&fam_disk_io_time, m); } if (weighted_time_ms != 0) { - m.value.fpcounter = ((fpcounter_t)weighted_time_ms) / 1000.0; + m.value.counter_fp = ((double)weighted_time_ms) / 1000.0; metric_family_metric_append(&fam_disk_io_weighted_time, m); } @@ -1012,11 +1010,11 @@ static int disk_read(void) { if (strncmp(ksp[i]->ks_class, "disk", strlen("disk")) == 0) { hrtime_t run_time_ns = kio.rtime; - m.value.fpcounter = ((fpcounter_t)run_time_ns) / 1e9; + m.value.counter_fp = ((double)run_time_ns) / 1e9; metric_family_metric_append(&fam_disk_io_time, m); hrtime_t weighted_io_time_ns = kio.rlentime; - m.value.fpcounter = ((fpcounter_t)weighted_io_time_ns) / 1e9; + m.value.counter_fp = ((double)weighted_io_time_ns) / 1e9; metric_family_metric_append(&fam_disk_io_weighted_time, m); uint_t ops_waiting = kio.wcnt; @@ -1110,12 +1108,12 @@ static int disk_read(void) { _system_configuration.Xfrac; derive_t write_time_ns = stat_disk[i].wserv * _system_configuration.Xint / _system_configuration.Xfrac; - metric_family_append( - &fam_ops_time, direction_label, read_direction, - (value_t){.fpcounter = ((fpcounter_t)read_time_ns) / 1e9}, &m); - metric_family_append( - &fam_ops_time, direction_label, write_direction, - (value_t){.fpcounter = ((fpcounter_t)write_time_ns) / 1e9}, &m); + metric_family_append(&fam_ops_time, direction_label, read_direction, + (value_t){.counter_fp = ((double)read_time_ns) / 1e9}, + &m); + metric_family_append(&fam_ops_time, direction_label, write_direction, + (value_t){.counter_fp = ((double)write_time_ns) / 1e9}, + &m); metric_reset(&m); } @@ -1176,8 +1174,8 @@ static int disk_read(void) { metric_family_append(&fam_ops, direction_label, write_direction, (value_t){.counter = drives[i].wxfer}, &m); - m.value.fpcounter = ((fpcounter_t)drives[i].time_sec) + - ((fpcounter_t)drives[i].time_usec) / 1e6; + m.value.counter_fp = + ((double)drives[i].time_sec) + ((double)drives[i].time_usec) / 1e6; metric_family_metric_append(&fam_disk_io_time, m); metric_reset(&m); diff --git a/src/memory.c b/src/memory.c index 46c232a518..412171eb9b 100644 --- a/src/memory.c +++ b/src/memory.c @@ -170,17 +170,17 @@ static int memory_config(oconfig_item_t *ci) /* {{{ */ return 0; } /* }}} int memory_config */ -static int memory_dispatch(gauge_t values[STATE_MAX]) { +static int memory_dispatch(int64_t values[STATE_MAX]) { metric_family_t fam_usage = { .name = "system.memory.usage", .help = "Reports memory in use by state", .unit = "By", - .type = METRIC_TYPE_GAUGE, + .type = METRIC_TYPE_UP_DOWN, }; - gauge_t total = 0; + int64_t total = 0; for (size_t i = 0; i < STATE_MAX; i++) { - if (isnan(values[i])) { + if (values[i] < 0) { continue; } @@ -212,12 +212,11 @@ static int memory_dispatch(gauge_t values[STATE_MAX]) { .name = "system.memory.limit", .help = "Total memory available in the system.", .unit = "By", - .type = METRIC_TYPE_COUNTER, // [sic] should be UpDownCounter - }; - metric_t m = { - .value = (value_t){.derive = (derive_t)total}, + .type = METRIC_TYPE_UP_DOWN, }; - metric_family_metric_append(&fam_limit, m); + metric_family_metric_append(&fam_limit, (metric_t){ + .value.up_down = total, + }); int status = plugin_dispatch_metric_family(&fam_limit); if (status != 0) { @@ -238,12 +237,13 @@ static int memory_dispatch(gauge_t values[STATE_MAX]) { .type = METRIC_TYPE_GAUGE, }; for (size_t i = 0; i < STATE_MAX; i++) { - if (isnan(values[i])) { + if (values[i] < 0) { continue; } + gauge_t ratio = ((gauge_t)values[i]) / ((gauge_t)total); metric_family_append(&fam_util, label_state, memory_type_names[i], - (value_t){.gauge = values[i] / total}, NULL); + (value_t){.gauge = ratio}, NULL); } int status = plugin_dispatch_metric_family(&fam_util); @@ -328,7 +328,7 @@ static int memory_init(void) { return 0; } /* int memory_init */ -static int memory_read_internal(gauge_t values[STATE_MAX]) { +static int memory_read_internal(int64_t values[STATE_MAX]) { #if HAVE_HOST_STATISTICS if (!port_host || !pagesize) { return EINVAL; @@ -364,10 +364,10 @@ static int memory_read_internal(gauge_t values[STATE_MAX]) { * This memory is not being used. */ - values[STATE_WIRED] = (gauge_t)(vm_data.wire_count * pagesize); - values[STATE_ACTIVE] = (gauge_t)(vm_data.active_count * pagesize); - values[STATE_INACTIVE] = (gauge_t)(vm_data.inactive_count * pagesize); - values[STATE_FREE] = (gauge_t)(vm_data.free_count * pagesize); + values[STATE_WIRED] = (int64_t)(vm_data.wire_count * pagesize); + values[STATE_ACTIVE] = (int64_t)(vm_data.active_count * pagesize); + values[STATE_INACTIVE] = (int64_t)(vm_data.inactive_count * pagesize); + values[STATE_FREE] = (int64_t)(vm_data.free_count * pagesize); /* #endif HAVE_HOST_STATISTICS */ #elif HAVE_SYSCTLBYNAME @@ -385,15 +385,15 @@ static int memory_read_internal(gauge_t values[STATE_MAX]) { return errno; } - values[STATE_WIRED] = (gauge_t)(uvmexp.wired * pagesize); - values[STATE_ACTIVE] = (gauge_t)(uvmexp.active * pagesize); - values[STATE_INACTIVE] = (gauge_t)(uvmexp.inactive * pagesize); - values[STATE_FREE] = (gauge_t)(uvmexp.free * pagesize); + values[STATE_WIRED] = (int64_t)(uvmexp.wired * pagesize); + values[STATE_ACTIVE] = (int64_t)(uvmexp.active * pagesize); + values[STATE_INACTIVE] = (int64_t)(uvmexp.inactive * pagesize); + values[STATE_FREE] = (int64_t)(uvmexp.free * pagesize); int64_t accounted = uvmexp.wired + uvmexp.active + uvmexp.inactive + uvmexp.free; if (uvmexp.npages > accounted) { - values[STATE_KERNEL] = (gauge_t)((uvmexp.npages - accounted) * pagesize); + values[STATE_KERNEL] = (int64_t)((uvmexp.npages - accounted) * pagesize); } /* #endif HAVE_SYSCTL && defined(KERNEL_NETBSD) */ @@ -423,7 +423,7 @@ static int memory_read_internal(gauge_t values[STATE_MAX]) { {"vm.stats.vm.v_laundry_count", STATE_LAUNDRY}, }; - gauge_t pagesize = 0; + int64_t pagesize = 0; for (size_t i = 0; i < STATIC_ARRAY_SIZE(metrics); i++) { long value = 0; size_t value_len = sizeof(value); @@ -437,11 +437,11 @@ static int memory_read_internal(gauge_t values[STATE_MAX]) { } if (i == 0) { - pagesize = (gauge_t)value; + pagesize = (int64_t)value; continue; } - values[metrics[i].type] = ((gauge_t)value) * pagesize; + values[metrics[i].type] = ((int64_t)value) * pagesize; } /* for (sysctl_keys) */ #endif /* HAVE_SYSCTL && KERNEL_NETBSD */ /* #endif HAVE_SYSCTLBYNAME */ @@ -454,8 +454,8 @@ static int memory_read_internal(gauge_t values[STATE_MAX]) { return status; } - gauge_t mem_total = 0; - gauge_t mem_not_used = 0; + int64_t mem_total = -1; + int64_t mem_not_used = 0; char buffer[256]; while (fgets(buffer, sizeof(buffer), fh) != NULL) { @@ -465,10 +465,7 @@ static int memory_read_internal(gauge_t values[STATE_MAX]) { continue; } - gauge_t v = 1024.0 * atof(fields[1]); - if (!isfinite(v)) { - continue; - } + int64_t v = 1024 * atoll(fields[1]); if (strcmp(fields[0], "MemTotal:") == 0) { mem_total = v; @@ -493,7 +490,7 @@ static int memory_read_internal(gauge_t values[STATE_MAX]) { WARNING("memory plugin: fclose failed: %s", STRERRNO); } - if (isnan(mem_total) || (mem_total == 0) || (mem_total < mem_not_used)) { + if (mem_total < 0 || mem_total < mem_not_used) { return EINVAL; } @@ -550,12 +547,12 @@ static int memory_read_internal(gauge_t values[STATE_MAX]) { mem_lock = 0; } - values[STATE_USED] = (gauge_t)(mem_used * pagesize); - values[STATE_FREE] = (gauge_t)(mem_free * pagesize); - values[STATE_LOCKED] = (gauge_t)(mem_lock * pagesize); - values[STATE_KERNEL] = (gauge_t)((mem_kern * pagesize) - arcsize); - values[STATE_UNUSED] = (gauge_t)(mem_unus * pagesize); - values[STATE_ARC] = (gauge_t)arcsize; + values[STATE_USED] = mem_used * pagesize; + values[STATE_FREE] = mem_free * pagesize; + values[STATE_LOCKED] = mem_lock * pagesize; + values[STATE_KERNEL] = (mem_kern * pagesize) - arcsize; + values[STATE_UNUSED] = mem_unus * pagesize; + values[STATE_ARC] = arcsize; /* #endif HAVE_LIBKSTAT */ #elif HAVE_SYSCTL && __OpenBSD__ @@ -572,9 +569,9 @@ static int memory_read_internal(gauge_t values[STATE_MAX]) { return errno; } - values[STATE_ACTIVE] = (gauge_t)(vmtotal.t_arm * pagesize); - values[STATE_INACTIVE] = (gauge_t)((vmtotal.t_rm - vmtotal.t_arm) * pagesize); - values[STATE_FREE] = (gauge_t)(vmtotal.t_free * pagesize); + values[STATE_ACTIVE] = (int64_t)(vmtotal.t_arm * pagesize); + values[STATE_INACTIVE] = (int64_t)((vmtotal.t_rm - vmtotal.t_arm) * pagesize); + values[STATE_FREE] = (int64_t)(vmtotal.t_free * pagesize); /* #endif HAVE_SYSCTL && __OpenBSD__ */ #elif HAVE_LIBSTATGRAB @@ -583,9 +580,9 @@ static int memory_read_internal(gauge_t values[STATE_MAX]) { return -1; } - values[STATE_USED] = (gauge_t)ios->used; - values[STATE_CACHED] = (gauge_t)ios->cache; - values[STATE_FREE] = (gauge_t)ios->free; + values[STATE_USED] = (int64_t)ios->used; + values[STATE_CACHED] = (int64_t)ios->cache; + values[STATE_FREE] = (int64_t)ios->free; /* #endif HAVE_LIBSTATGRAB */ #elif HAVE_PERFSTAT @@ -605,10 +602,10 @@ static int memory_read_internal(gauge_t values[STATE_MAX]) { * real_total = real_free + real_inuse * real_inuse = "active" + real_pinned + numperm */ - values[STATE_FREE] = (gauge_t)(pmemory.real_free * pagesize); - values[STATE_CACHED] = (gauge_t)(pmemory.numperm * pagesize); - values[STATE_KERNEL] = (gauge_t)(pmemory.real_system * pagesize); - values[STATE_USED] = (gauge_t)(pmemory.real_process * pagesize); + values[STATE_FREE] = (int64_t)(pmemory.real_free * pagesize); + values[STATE_CACHED] = (int64_t)(pmemory.numperm * pagesize); + values[STATE_KERNEL] = (int64_t)(pmemory.real_system * pagesize); + values[STATE_USED] = (int64_t)(pmemory.real_process * pagesize); #endif /* HAVE_PERFSTAT */ return 0; @@ -616,9 +613,9 @@ static int memory_read_internal(gauge_t values[STATE_MAX]) { static int memory_read(void) /* {{{ */ { - gauge_t values[STATE_MAX] = {0}; + int64_t values[STATE_MAX] = {0}; for (size_t i = 0; i < STATE_MAX; i++) { - values[i] = NAN; + values[i] = -1; } int status = memory_read_internal(values); diff --git a/src/open_telemetry_receiver.cc b/src/open_telemetry_receiver.cc index c0dd114fdf..325b632333 100644 --- a/src/open_telemetry_receiver.cc +++ b/src/open_telemetry_receiver.cc @@ -134,9 +134,6 @@ static grpc::Status unmarshal_data_point(NumberDataPoint dp, .time = NS_TO_CDTIME_T(dp.time_unix_nano()), }; - bool is_cumulative = (agg == AGGREGATION_TEMPORALITY_DELTA || - agg == AGGREGATION_TEMPORALITY_CUMULATIVE); - value_t offset = {0}; if (agg == AGGREGATION_TEMPORALITY_DELTA) { int err = uc_get_value(&m, &offset); @@ -155,19 +152,24 @@ static grpc::Status unmarshal_data_point(NumberDataPoint dp, switch (dp.value_case()) { case NumberDataPoint::kAsDouble: - if (is_cumulative) { - fam->type = METRIC_TYPE_FPCOUNTER; - m.value.fpcounter = dp.as_double(); - break; + if (fam->type == METRIC_TYPE_COUNTER) { + fam->type = METRIC_TYPE_COUNTER_FP; + m.value.counter_fp = offset.counter_fp + dp.as_double(); + } else if (fam->type == METRIC_TYPE_UP_DOWN) { + fam->type = METRIC_TYPE_UP_DOWN_FP; + m.value.up_down_fp = offset.up_down_fp + dp.as_double(); + } else if (fam->type == METRIC_TYPE_GAUGE) { + m.value.gauge = dp.as_double(); } - m.value.gauge = dp.as_double(); break; case NumberDataPoint::kAsInt: - if (is_cumulative) { - m.value.counter = offset.counter + (counter_t)dp.as_int(); - break; + if (fam->type == METRIC_TYPE_COUNTER) { + m.value.counter = offset.counter + (uint64_t)dp.as_int(); + } else if (fam->type == METRIC_TYPE_UP_DOWN) { + m.value.up_down = offset.up_down + dp.as_int(); + } else if (fam->type == METRIC_TYPE_GAUGE) { + m.value.gauge = (double)dp.as_int(); } - m.value.gauge = (gauge_t)dp.as_int(); break; default: return grpc::Status(grpc::StatusCode::INVALID_ARGUMENT, @@ -192,9 +194,11 @@ static grpc::Status unmarshal_data_point(NumberDataPoint dp, } static grpc::Status unmarshal_gauge_metric(Gauge g, metric_family_t *fam) { - for (auto db : g.data_points()) { + fam->type = METRIC_TYPE_GAUGE; + + for (auto dp : g.data_points()) { grpc::Status s = - unmarshal_data_point(db, fam, AGGREGATION_TEMPORALITY_UNSPECIFIED); + unmarshal_data_point(dp, fam, AGGREGATION_TEMPORALITY_UNSPECIFIED); if (!s.ok()) { return s; } @@ -204,18 +208,14 @@ static grpc::Status unmarshal_gauge_metric(Gauge g, metric_family_t *fam) { } static grpc::Status unmarshal_sum_metric(Sum sum, metric_family_t *fam) { - if (!sum.is_monotonic()) { - // TODO(octo): convert to gauge instead? - DEBUG("open_telemetry plugin: non-monotonic sums (aka. UpDownCounters) " - "are unsupported"); - return grpc::Status( - grpc::StatusCode::UNIMPLEMENTED, - "non-monotonic sums (aka. UpDownCounters) are unsupported"); + fam->type = METRIC_TYPE_UP_DOWN; + if (sum.is_monotonic()) { + fam->type = METRIC_TYPE_COUNTER; } - for (auto db : sum.data_points()) { + for (auto dp : sum.data_points()) { grpc::Status s = - unmarshal_data_point(db, fam, sum.aggregation_temporality()); + unmarshal_data_point(dp, fam, sum.aggregation_temporality()); if (!s.ok()) { return s; } @@ -250,7 +250,6 @@ static grpc::Status dispatch_metric(Metric mpb, label_set_t resource, switch (mpb.data_case()) { case Metric::kGauge: { - fam.type = METRIC_TYPE_GAUGE; grpc::Status s = unmarshal_gauge_metric(mpb.gauge(), &fam); if (!s.ok()) { metric_family_metric_reset(&fam); @@ -260,7 +259,6 @@ static grpc::Status dispatch_metric(Metric mpb, label_set_t resource, break; } case Metric::kSum: { - fam.type = METRIC_TYPE_COUNTER; grpc::Status s = unmarshal_sum_metric(mpb.sum(), &fam); if (!s.ok()) { metric_family_metric_reset(&fam); diff --git a/src/utils/cmds/putmetric.c b/src/utils/cmds/putmetric.c index 11969860e6..23020d8984 100644 --- a/src/utils/cmds/putmetric.c +++ b/src/utils/cmds/putmetric.c @@ -47,7 +47,11 @@ static int set_option(metric_t *m, char const *key, char const *value, } else if (strcasecmp("COUNTER", value) == 0) { m->family->type = METRIC_TYPE_COUNTER; } else if (strcasecmp("FPCOUNTER", value) == 0) { - m->family->type = METRIC_TYPE_FPCOUNTER; + m->family->type = METRIC_TYPE_COUNTER_FP; + } else if (strcasecmp("UP_DOWN_COUNTER", value) == 0) { + m->family->type = METRIC_TYPE_UP_DOWN; + } else if (strcasecmp("UP_DOWN_COUNTER_FP", value) == 0) { + m->family->type = METRIC_TYPE_UP_DOWN_FP; } else { return CMD_ERROR; } @@ -247,12 +251,18 @@ int cmd_format_putmetric(strbuf_t *buf, metric_t const *m) { /* {{{ */ case METRIC_TYPE_COUNTER: strbuf_print(buf, " type=COUNTER"); break; - case METRIC_TYPE_FPCOUNTER: + case METRIC_TYPE_COUNTER_FP: strbuf_print(buf, " type=FPCOUNTER"); break; case METRIC_TYPE_GAUGE: strbuf_print(buf, " type=GAUGE"); break; + case METRIC_TYPE_UP_DOWN: + strbuf_print(buf, " type=UP_DOWN_COUNTER"); + break; + case METRIC_TYPE_UP_DOWN_FP: + strbuf_print(buf, " type=UP_DOWN_COUNTER_FP"); + break; case METRIC_TYPE_UNTYPED: /* no op */ break; diff --git a/src/utils/common/common.c b/src/utils/common/common.c index 5ad0328f1f..e55c9f7218 100644 --- a/src/utils/common/common.c +++ b/src/utils/common/common.c @@ -933,115 +933,86 @@ int format_name(char *ret, int ret_len, const char *hostname, } /* int format_name */ int format_values(strbuf_t *buf, metric_t const *m, bool store_rates) { - strbuf_printf(buf, "%.3f", CDTIME_T_TO_DOUBLE(m->time)); - - if (store_rates) { - gauge_t rates = NAN; - int err = uc_get_rate(m, &rates); - if (err) { - ERROR("format_values: uc_get_rate failed: %s", STRERROR(err)); - return err; - } - if (isnan(rates)) { - strbuf_print(buf, ":nan"); - } else { - strbuf_printf(buf, ":" GAUGE_FORMAT, rates); - } - return 0; - } + strbuf_printf(buf, "%.3f:", CDTIME_T_TO_DOUBLE(m->time)); - if (m->family->type == DS_TYPE_DERIVE) { - return strbuf_printf(buf, ":%" PRIi64, m->value.derive); + if (!store_rates) { + return value_marshal_text(buf, m->value, m->family->type); } - switch (m->family->type) { - case METRIC_TYPE_GAUGE: - /* Solaris' printf tends to print NAN as "-nan", breaking unit tests, so we - * introduce a special case here. */ - if (isnan(m->value.gauge)) { - return strbuf_print(buf, ":nan"); - } - return strbuf_printf(buf, ":" GAUGE_FORMAT, m->value.gauge); - case METRIC_TYPE_COUNTER: - return strbuf_printf(buf, ":%" PRIu64, m->value.counter); - case METRIC_TYPE_FPCOUNTER: - return strbuf_printf(buf, ":" GAUGE_FORMAT, m->value.fpcounter); - case METRIC_TYPE_UNTYPED: - break; + gauge_t rates = NAN; + int err = uc_get_rate(m, &rates); + if (err) { + ERROR("format_values: uc_get_rate failed: %s", STRERROR(err)); + return err; } - ERROR("format_values: Unknown metric type: %d", m->family->type); - return EINVAL; + if (isnan(rates)) { + strbuf_print(buf, "nan"); + } else { + strbuf_printf(buf, GAUGE_FORMAT, rates); + } + return 0; } /* }}} int format_values */ -int parse_value(const char *value_orig, value_t *ret_value, int ds_type) { - char *value; +int parse_value(char const *value, value_t *ret_value, metric_type_t type) { char *endptr = NULL; - size_t value_len; - - if (value_orig == NULL) - return EINVAL; - - value = strdup(value_orig); - if (value == NULL) - return ENOMEM; - value_len = strlen(value); - - while ((value_len > 0) && isspace((int)value[value_len - 1])) { - value[value_len - 1] = '\0'; - value_len--; - } + switch (type) { + case METRIC_TYPE_GAUGE: + ret_value->gauge = (gauge_t)strtod(value, &endptr); + break; - switch (ds_type) { - case DS_TYPE_COUNTER: + case METRIC_TYPE_COUNTER: ret_value->counter = (counter_t)strtoull(value, &endptr, 0); break; - case DS_TYPE_GAUGE: - ret_value->gauge = (gauge_t)strtod(value, &endptr); + case METRIC_TYPE_COUNTER_FP: + ret_value->counter_fp = (double)strtod(value, &endptr); break; - case DS_TYPE_DERIVE: - ret_value->derive = (derive_t)strtoll(value, &endptr, 0); + case METRIC_TYPE_UP_DOWN: + ret_value->up_down = (int64_t)strtoll(value, &endptr, 0); break; - default: - sfree(value); - P_ERROR("parse_value: Invalid data source type: %i.", ds_type); - return -1; + case METRIC_TYPE_UP_DOWN_FP: + ret_value->up_down_fp = (double)strtod(value, &endptr); + break; + + case METRIC_TYPE_UNTYPED: + P_ERROR("parse_value: invalid metric type: %d.", type); + return EINVAL; } if (value == endptr) { P_ERROR("parse_value: Failed to parse string as %s: \"%s\".", - DS_TYPE_TO_STRING(ds_type), value); - sfree(value); - return -1; - } else if ((NULL != endptr) && ('\0' != *endptr)) - P_INFO("parse_value: Ignoring trailing garbage \"%s\" after %s value. " - "Input string was \"%s\".", - endptr, DS_TYPE_TO_STRING(ds_type), value_orig); + METRIC_TYPE_TO_STRING(type), value); + return EINVAL; + } + if (NULL != endptr) { + if (*endptr != 0 && !isspace(*endptr)) { + P_INFO("parse_value: Ignoring trailing garbage \"%s\" after %s value. " + "Input string was \"%s\".", + endptr, METRIC_TYPE_TO_STRING(type), value); + } + } - sfree(value); return 0; } /* int parse_value */ -int parse_value_file(char const *path, value_t *ret_value, int ds_type) { - FILE *fh; - char buffer[256]; - - fh = fopen(path, "r"); - if (fh == NULL) - return -1; +int parse_value_file(char const *path, value_t *ret_value, metric_type_t type) { + FILE *fh = fopen(path, "r"); + if (fh == NULL) { + return errno; + } + char buffer[256]; if (fgets(buffer, sizeof(buffer), fh) == NULL) { fclose(fh); - return -1; + return EINVAL; } fclose(fh); - strstripnewline(buffer); - return parse_value(buffer, ret_value, ds_type); + return parse_value(buffer, ret_value, type); } /* int parse_value_file */ #if !HAVE_GETPWNAM_R @@ -1227,9 +1198,6 @@ counter_t counter_diff(counter_t old_value, counter_t new_value) { int rate_to_value(value_t *ret_value, gauge_t rate, /* {{{ */ rate_to_value_state_t *state, metric_type_t type, cdtime_t t) { - gauge_t delta_gauge; - cdtime_t delta_t; - if (type == METRIC_TYPE_GAUGE) { state->last_value.gauge = rate; state->last_time = t; @@ -1241,7 +1209,7 @@ int rate_to_value(value_t *ret_value, gauge_t rate, /* {{{ */ /* Counter can't handle negative rates. Reset "last time" to zero, so that * the next valid rate will re-initialize the structure. */ if ((rate < 0.0) && - (type == METRIC_TYPE_COUNTER || type == METRIC_TYPE_FPCOUNTER)) { + (type == METRIC_TYPE_COUNTER || type == METRIC_TYPE_COUNTER_FP)) { memset(state, 0, sizeof(*state)); return EINVAL; } @@ -1252,19 +1220,12 @@ int rate_to_value(value_t *ret_value, gauge_t rate, /* {{{ */ return EINVAL; } - delta_t = t - state->last_time; - delta_gauge = (rate * CDTIME_T_TO_DOUBLE(delta_t)) + state->residual; + cdtime_t delta_t = t - state->last_time; + gauge_t delta_gauge = (rate * CDTIME_T_TO_DOUBLE(delta_t)) + state->residual; /* Previous value is invalid. */ if (state->last_time == 0) /* {{{ */ { - if (type == DS_TYPE_DERIVE) { - state->last_value.derive = (derive_t)floor(rate); - state->residual = rate - ((gauge_t)state->last_value.derive); - state->last_time = t; - return EAGAIN; - } - switch (type) { case METRIC_TYPE_GAUGE: /* not reached */ @@ -1273,8 +1234,16 @@ int rate_to_value(value_t *ret_value, gauge_t rate, /* {{{ */ state->last_value.counter = (counter_t)rate; state->residual = rate - ((gauge_t)state->last_value.counter); break; - case METRIC_TYPE_FPCOUNTER: - state->last_value.fpcounter = (fpcounter_t)rate; + case METRIC_TYPE_COUNTER_FP: + state->last_value.counter_fp = (double)rate; + state->residual = 0; + break; + case METRIC_TYPE_UP_DOWN: + state->last_value.up_down = (int64_t)floor(rate); + state->residual = rate - ((gauge_t)state->last_value.up_down); + break; + case METRIC_TYPE_UP_DOWN_FP: + state->last_value.up_down_fp = (double)rate; state->residual = 0; break; case METRIC_TYPE_UNTYPED: @@ -1286,29 +1255,31 @@ int rate_to_value(value_t *ret_value, gauge_t rate, /* {{{ */ return EAGAIN; } /* }}} */ - if (type == DS_TYPE_DERIVE) { - derive_t delta_derive = (derive_t)floor(delta_gauge); - state->last_value.derive += delta_derive; - state->residual = delta_gauge - ((gauge_t)delta_derive); - - state->last_time = t; - *ret_value = state->last_value; - return 0; - } - switch (type) { case METRIC_TYPE_GAUGE: /* not reached */ break; case METRIC_TYPE_COUNTER: { - counter_t delta_counter = (counter_t)delta_gauge; - state->last_value.counter += delta_counter; - state->residual = delta_gauge - ((gauge_t)delta_counter); + counter_t delta = (counter_t)delta_gauge; + state->last_value.counter += delta; + state->residual = delta_gauge - ((gauge_t)delta); + break; + } + case METRIC_TYPE_COUNTER_FP: { + double delta = (double)delta_gauge; + state->last_value.counter_fp += delta; + state->residual = 0; break; } - case METRIC_TYPE_FPCOUNTER: { - fpcounter_t delta_counter = (fpcounter_t)delta_gauge; - state->last_value.fpcounter += delta_counter; + case METRIC_TYPE_UP_DOWN: { + int64_t delta = (int64_t)floor(delta_gauge); + state->last_value.up_down += delta; + state->residual = delta_gauge - ((gauge_t)delta); + break; + } + case METRIC_TYPE_UP_DOWN_FP: { + double delta = (double)delta_gauge; + state->last_value.up_down_fp += delta; state->residual = 0; break; } @@ -1324,28 +1295,31 @@ int rate_to_value(value_t *ret_value, gauge_t rate, /* {{{ */ static int calculate_rate(gauge_t *ret_rate, value_t value, metric_type_t type, gauge_t interval, value_to_rate_state_t *state) { - if (type == DS_TYPE_DERIVE) { - derive_t diff = value.derive - state->last_value.derive; - *ret_rate = ((gauge_t)diff) / ((gauge_t)interval); - return 0; - } - switch (type) { - case METRIC_TYPE_GAUGE: { + case METRIC_TYPE_GAUGE: *ret_rate = value.gauge; return 0; - } case METRIC_TYPE_COUNTER: { counter_t diff = counter_diff(state->last_value.counter, value.counter); *ret_rate = ((gauge_t)diff) / ((gauge_t)interval); return 0; } - case METRIC_TYPE_FPCOUNTER: { - if (state->last_value.fpcounter > value.fpcounter) { + case METRIC_TYPE_COUNTER_FP: { + if (state->last_value.counter_fp > value.counter_fp) { *ret_rate = NAN; return EAGAIN; } - fpcounter_t diff = value.fpcounter - state->last_value.fpcounter; + double diff = value.counter_fp - state->last_value.counter_fp; + *ret_rate = ((gauge_t)diff) / ((gauge_t)interval); + return 0; + } + case METRIC_TYPE_UP_DOWN: { + int64_t diff = value.up_down - state->last_value.up_down; + *ret_rate = ((gauge_t)diff) / ((gauge_t)interval); + return 0; + } + case METRIC_TYPE_UP_DOWN_FP: { + double diff = value.up_down_fp - state->last_value.up_down_fp; *ret_rate = ((gauge_t)diff) / ((gauge_t)interval); return 0; } diff --git a/src/utils/common/common.h b/src/utils/common/common.h index a8899343eb..99bbb28909 100644 --- a/src/utils/common/common.h +++ b/src/utils/common/common.h @@ -341,13 +341,13 @@ int format_name(char *ret, int ret_len, const char *hostname, (vl)->type, (vl)->type_instance) int format_values(strbuf_t *buf, metric_t const *m, bool store_rates); -int parse_value(const char *value, value_t *ret_value, int ds_type); +int parse_value(const char *value, value_t *ret_value, metric_type_t type); /* parse_value_file reads "path" and parses its content as an integer or * floating point, depending on "ds_type". On success, the value is stored in * "ret_value" and zero is returned. On failure, a non-zero value is returned. */ -int parse_value_file(char const *path, value_t *ret_value, int ds_type); +int parse_value_file(char const *path, value_t *ret_value, metric_type_t type); #if !HAVE_GETPWNAM_R struct passwd; diff --git a/src/utils/common/common_test.c b/src/utils/common/common_test.c index 80ba765712..2da71b97b5 100644 --- a/src/utils/common/common_test.c +++ b/src/utils/common/common_test.c @@ -360,17 +360,17 @@ DEF_TEST(rate_to_value) { .want_residual = 0.001, }, { - .name = "fpcounter", + .name = "counter_fp", .rate = 1.234, .state = { - .last_value = {.fpcounter = 1000}, + .last_value = {.counter_fp = 1000}, .last_time = TIME_T_TO_CDTIME_T(10), .residual = 0, }, - .type = METRIC_TYPE_FPCOUNTER, + .type = METRIC_TYPE_COUNTER_FP, .time = TIME_T_TO_CDTIME_T(20), - .want = {.fpcounter = 1012.34}, + .want = {.counter_fp = 1012.34}, }, { .name = "derive", @@ -436,17 +436,25 @@ DEF_TEST(rate_to_value) { } switch (cases[i].type) { + case METRIC_TYPE_GAUGE: + EXPECT_EQ_DOUBLE(cases[i].want.gauge, got.gauge); + EXPECT_EQ_DOUBLE(cases[i].want.gauge, state.last_value.gauge); + break; case METRIC_TYPE_COUNTER: EXPECT_EQ_UINT64(cases[i].want.counter, got.counter); EXPECT_EQ_UINT64(cases[i].want.counter, state.last_value.counter); break; - case METRIC_TYPE_GAUGE: - EXPECT_EQ_DOUBLE(cases[i].want.gauge, got.gauge); - EXPECT_EQ_DOUBLE(cases[i].want.gauge, state.last_value.gauge); + case METRIC_TYPE_COUNTER_FP: + EXPECT_EQ_DOUBLE(cases[i].want.counter_fp, got.counter_fp); + EXPECT_EQ_UINT64(cases[i].want.counter_fp, state.last_value.counter_fp); + break; + case METRIC_TYPE_UP_DOWN: + EXPECT_EQ_UINT64(cases[i].want.up_down, got.up_down); + EXPECT_EQ_UINT64(cases[i].want.up_down, state.last_value.up_down); break; - case METRIC_TYPE_FPCOUNTER: - EXPECT_EQ_DOUBLE(cases[i].want.fpcounter, got.fpcounter); - EXPECT_EQ_UINT64(cases[i].want.fpcounter, state.last_value.fpcounter); + case METRIC_TYPE_UP_DOWN_FP: + EXPECT_EQ_DOUBLE(cases[i].want.up_down_fp, got.up_down_fp); + EXPECT_EQ_UINT64(cases[i].want.up_down_fp, state.last_value.up_down_fp); break; case METRIC_TYPE_UNTYPED: LOG(false, "invalid metric type"); @@ -535,30 +543,30 @@ DEF_TEST(value_to_rate) { .want = 10.0, }, { - .name = "fpcounter_t init", + .name = "counter_fp init", .t0 = 0, .t1 = 10, - .type = METRIC_TYPE_FPCOUNTER, - .v0 = {.fpcounter = 0.}, - .v1 = {.fpcounter = 10.}, + .type = METRIC_TYPE_COUNTER_FP, + .v0 = {.counter_fp = 0.}, + .v1 = {.counter_fp = 10.}, .want_err = EAGAIN, }, { - .name = "fpcounter_t increase", + .name = "counter_fp increase", .t0 = 10, .t1 = 20, - .type = METRIC_TYPE_FPCOUNTER, - .v0 = {.fpcounter = 10.}, - .v1 = {.fpcounter = 50.5}, + .type = METRIC_TYPE_COUNTER_FP, + .v0 = {.counter_fp = 10.}, + .v1 = {.counter_fp = 50.5}, .want = (50.5 - 10.) / (20. - 10.), }, { - .name = "fpcounter_t reset", + .name = "counter_fp reset", .t0 = 20, .t1 = 30, - .type = METRIC_TYPE_FPCOUNTER, - .v0 = {.fpcounter = 100.0}, - .v1 = {.fpcounter = 20.0}, + .type = METRIC_TYPE_COUNTER_FP, + .v0 = {.counter_fp = 100.0}, + .v1 = {.counter_fp = 20.0}, .want_err = EAGAIN, }, }; diff --git a/src/utils/format_graphite/format_graphite.c b/src/utils/format_graphite/format_graphite.c index 1f466bf7f8..73e7762212 100644 --- a/src/utils/format_graphite/format_graphite.c +++ b/src/utils/format_graphite/format_graphite.c @@ -44,27 +44,10 @@ static int format_double(strbuf_t *buf, double d) { static int gr_format_values(strbuf_t *buf, metric_t const *m, gauge_t rate, bool store_rate) { - if (m->family->type == METRIC_TYPE_GAUGE) { - rate = m->value.gauge; - } - if (store_rate) { return format_double(buf, rate); } - - switch (m->family->type) { - case METRIC_TYPE_COUNTER: - return strbuf_printf(buf, "%" PRIu64, m->value.counter); - case METRIC_TYPE_FPCOUNTER: - return format_double(buf, m->value.fpcounter); - case METRIC_TYPE_GAUGE: - return format_double(buf, m->value.gauge); - case METRIC_TYPE_UNTYPED: - break; - } - - P_ERROR("gr_format_values: Unknown data source type: %d", m->family->type); - return EINVAL; + return value_marshal_text(buf, m->value, m->family->type); } static int graphite_print_escaped(strbuf_t *buf, char const *s, diff --git a/src/utils/format_influxdb/format_influxdb.c b/src/utils/format_influxdb/format_influxdb.c index 2735352e63..d85408139e 100644 --- a/src/utils/format_influxdb/format_influxdb.c +++ b/src/utils/format_influxdb/format_influxdb.c @@ -73,22 +73,18 @@ static int format_metric_value(strbuf_t *sb, metric_t const *m, return format_metric_rate(sb, m); } - switch (m->family->type) { - case METRIC_TYPE_GAUGE: - if (isnan(m->value.gauge)) { - return EAGAIN; - } - return strbuf_printf(sb, "value=" GAUGE_FORMAT, m->value.gauge); - case METRIC_TYPE_COUNTER: - return strbuf_printf(sb, "value=%" PRIu64 "i", m->value.counter); - case METRIC_TYPE_FPCOUNTER: - return strbuf_printf(sb, "value=" GAUGE_FORMAT, m->value.fpcounter); - case METRIC_TYPE_UNTYPED: - break; + if ((m->family->type == METRIC_TYPE_GAUGE && isnan(m->value.gauge)) || + (m->family->type == METRIC_TYPE_UP_DOWN_FP && + isnan(m->value.up_down_fp))) { + return EAGAIN; } - ERROR("format_influxdb plugin: invalid metric type: %d", m->family->type); - return EINVAL; + int err = strbuf_print(sb, "value="); + ERR_COMBINE(err, value_marshal_text(sb, m->value, m->family->type)); + if (!IS_DOUBLE(m->family->type)) { + ERR_COMBINE(err, strbuf_print(sb, "i")); + } + return err; } static int format_metric_time(strbuf_t *sb, metric_t const *m) { diff --git a/src/utils/format_json/format_json.c b/src/utils/format_json/format_json.c index c65ab97bfd..17caf90261 100644 --- a/src/utils/format_json/format_json.c +++ b/src/utils/format_json/format_json.c @@ -26,12 +26,10 @@ **/ #include "collectd.h" - -#include "utils/format_json/format_json.h" - -#include "plugin.h" +#include "daemon/plugin.h" +#include "daemon/utils_cache.h" #include "utils/common/common.h" -#include "utils_cache.h" +#include "utils/format_json/format_json.h" #include #include @@ -159,6 +157,24 @@ static int format_metric(yajl_gen g, metric_t const *m) { return 0; } +static char const *metric_type_to_string(metric_type_t type) { + switch (type) { + case METRIC_TYPE_GAUGE: + return "GAUGE"; + case METRIC_TYPE_COUNTER: + return "COUNTER"; + case METRIC_TYPE_COUNTER_FP: + return "FPCOUNTER"; + case METRIC_TYPE_UP_DOWN: + return "UP_DOWN_COUNTER"; + case METRIC_TYPE_UP_DOWN_FP: + return "UP_DOWN_COUNTER_FP"; + case METRIC_TYPE_UNTYPED: + break; + } + return "INVALID"; +} + /* json_metric_family that all metrics in ml have the same name and value_type. * * Example: @@ -181,24 +197,8 @@ static int json_metric_family(yajl_gen g, metric_family_t const *fam) { CHECK(json_add_string(g, "name")); CHECK(json_add_string(g, fam->name)); - char const *type = NULL; - switch (fam->type) { - /* TODO(octo): handle store_rates. */ - case METRIC_TYPE_GAUGE: - type = "GAUGE"; - break; - case METRIC_TYPE_COUNTER: - type = "COUNTER"; - break; - case METRIC_TYPE_FPCOUNTER: - type = "FPCOUNTER"; - break; - case METRIC_TYPE_UNTYPED: - type = "UNTYPED"; - break; - } CHECK(json_add_string(g, "type")); - CHECK(json_add_string(g, type)); + CHECK(json_add_string(g, metric_type_to_string(fam->type))); CHECK(json_add_string(g, "metrics")); CHECK(yajl_gen_array_open(g)); @@ -216,6 +216,7 @@ static int json_metric_family(yajl_gen g, metric_family_t const *fam) { return 0; } +/* TODO(octo): handle store_rates. */ int format_json_metric_family(strbuf_t *buf, metric_family_t const *fam, bool store_rates) { if ((buf == NULL) || (fam == NULL)) diff --git a/src/utils/format_json/open_telemetry.c b/src/utils/format_json/open_telemetry.c index 3b5ac6ba3d..447d32a1f2 100644 --- a/src/utils/format_json/open_telemetry.c +++ b/src/utils/format_json/open_telemetry.c @@ -92,17 +92,25 @@ static int number_data_point(yajl_gen g, metric_t const *m) { CHECK(yajl_gen_integer(g, CDTIME_T_TO_NS(m->time))); switch (m->family->type) { + case METRIC_TYPE_GAUGE: + CHECK(json_add_string(g, "asDouble")); + CHECK(yajl_gen_double(g, m->value.gauge)); + break; case METRIC_TYPE_COUNTER: CHECK(json_add_string(g, "asInt")); - CHECK(yajl_gen_integer(g, m->value.counter)); + CHECK(yajl_gen_integer(g, (long long int)m->value.counter)); break; - case METRIC_TYPE_FPCOUNTER: + case METRIC_TYPE_COUNTER_FP: CHECK(json_add_string(g, "asDouble")); - CHECK(yajl_gen_double(g, m->value.fpcounter)); + CHECK(yajl_gen_double(g, m->value.counter_fp)); break; - case METRIC_TYPE_GAUGE: + case METRIC_TYPE_UP_DOWN: + CHECK(json_add_string(g, "asInt")); + CHECK(yajl_gen_integer(g, (long long int)m->value.up_down)); + break; + case METRIC_TYPE_UP_DOWN_FP: CHECK(json_add_string(g, "asDouble")); - CHECK(yajl_gen_double(g, m->value.gauge)); + CHECK(yajl_gen_integer(g, m->value.up_down_fp)); break; case METRIC_TYPE_UNTYPED: ERROR("format_json_open_telemetry: Unexpected metric type: %d", @@ -166,15 +174,17 @@ static int metric(yajl_gen g, metric_family_t const *fam) { } switch (fam->type) { - case METRIC_TYPE_COUNTER: - case METRIC_TYPE_FPCOUNTER: - CHECK(json_add_string(g, "sum")); - CHECK(sum(g, fam)); - break; case METRIC_TYPE_GAUGE: CHECK(json_add_string(g, "gauge")); CHECK(gauge(g, fam)); break; + case METRIC_TYPE_COUNTER: + case METRIC_TYPE_COUNTER_FP: + case METRIC_TYPE_UP_DOWN: + case METRIC_TYPE_UP_DOWN_FP: + CHECK(json_add_string(g, "sum")); + CHECK(sum(g, fam)); + break; case METRIC_TYPE_UNTYPED: ERROR("format_json_open_telemetry: Unexpected metric type: %d", fam->type); return EINVAL; diff --git a/src/utils/format_kairosdb/format_kairosdb.c b/src/utils/format_kairosdb/format_kairosdb.c index d8f92a1950..6a02e1584a 100644 --- a/src/utils/format_kairosdb/format_kairosdb.c +++ b/src/utils/format_kairosdb/format_kairosdb.c @@ -114,12 +114,18 @@ static int json_add_value(yajl_gen g, metric_t const *m, } return 0; } - case METRIC_TYPE_FPCOUNTER: - CHECK(yajl_gen_double(g, m->value.fpcounter)); - return 0; case METRIC_TYPE_COUNTER: CHECK(yajl_gen_integer(g, (long long int)m->value.counter)); return 0; + case METRIC_TYPE_COUNTER_FP: + CHECK(yajl_gen_double(g, m->value.counter_fp)); + return 0; + case METRIC_TYPE_UP_DOWN: + CHECK(yajl_gen_integer(g, (long long int)m->value.up_down)); + return 0; + case METRIC_TYPE_UP_DOWN_FP: + CHECK(yajl_gen_double(g, m->value.up_down_fp)); + return 0; case METRIC_TYPE_UNTYPED: break; } diff --git a/src/utils/format_open_telemetry/format_open_telemetry.cc b/src/utils/format_open_telemetry/format_open_telemetry.cc index 584c2318bc..038b503116 100644 --- a/src/utils/format_open_telemetry/format_open_telemetry.cc +++ b/src/utils/format_open_telemetry/format_open_telemetry.cc @@ -67,13 +67,21 @@ static void metric_to_number_data_point(NumberDataPoint *dp, // A valid metric type is guaranteed by add_metric(). switch (m->family->type) { - case METRIC_TYPE_COUNTER: - dp->set_as_int(m->value.derive); - return; case METRIC_TYPE_GAUGE: - case METRIC_TYPE_FPCOUNTER: dp->set_as_double(m->value.gauge); return; + case METRIC_TYPE_COUNTER: + dp->set_as_int((int64_t)m->value.counter); + return; + case METRIC_TYPE_UP_DOWN: + dp->set_as_int((int64_t)m->value.up_down); + return; + case METRIC_TYPE_COUNTER_FP: + dp->set_as_double(m->value.counter_fp); + return; + case METRIC_TYPE_UP_DOWN_FP: + dp->set_as_double(m->value.up_down_fp); + return; case METRIC_TYPE_UNTYPED: // Fall through. This case signals the compiler that we're checking all // values of the enum. We report an error outside of the switch to also @@ -94,7 +102,7 @@ static void set_sum(Metric *m, metric_family_t const *fam) { } s->set_aggregation_temporality(AGGREGATION_TEMPORALITY_CUMULATIVE); - s->set_is_monotonic(true); + s->set_is_monotonic(IS_MONOTONIC(fam->type)); } static void set_gauge(Metric *m, metric_family_t const *fam) { @@ -125,13 +133,15 @@ static void add_metric(ScopeMetrics *sm, metric_family_t const *fam) { } switch (fam->type) { - case METRIC_TYPE_COUNTER: - case METRIC_TYPE_FPCOUNTER: - set_sum(m, fam); - return; case METRIC_TYPE_GAUGE: set_gauge(m, fam); return; + case METRIC_TYPE_COUNTER: + case METRIC_TYPE_COUNTER_FP: + case METRIC_TYPE_UP_DOWN: + case METRIC_TYPE_UP_DOWN_FP: + set_sum(m, fam); + return; case METRIC_TYPE_UNTYPED: // Never reached, only here to show the compiler we're handling all possible // `metric_type_t` values. diff --git a/src/utils/format_stackdriver/format_stackdriver.c b/src/utils/format_stackdriver/format_stackdriver.c index fe81a04df8..642777b126 100644 --- a/src/utils/format_stackdriver/format_stackdriver.c +++ b/src/utils/format_stackdriver/format_stackdriver.c @@ -141,24 +141,34 @@ static int format_typed_value(yajl_gen gen, metric_t const *m, } break; } - case METRIC_TYPE_FPCOUNTER: { + case METRIC_TYPE_COUNTER: { /* Counter resets are handled in format_time_series(). */ - assert(m->value.fpcounter >= start_value.fpcounter); + assert(m->value.counter >= start_value.counter); - fpcounter_t diff = m->value.fpcounter - start_value.fpcounter; - int status = json_string(gen, "doubleValue") || yajl_gen_double(gen, diff); + counter_t diff = m->value.counter - start_value.counter; + char integer[64] = {0}; + ssnprintf(integer, sizeof(integer), "%" PRIu64, diff); + + int status = json_string(gen, "int64Value") || json_string(gen, integer); if (status != 0) { return status; } break; } - case METRIC_TYPE_COUNTER: { + case METRIC_TYPE_COUNTER_FP: { /* Counter resets are handled in format_time_series(). */ - assert(m->value.counter >= start_value.counter); + assert(m->value.counter_fp >= start_value.counter_fp); - counter_t diff = m->value.counter - start_value.counter; + double diff = m->value.counter_fp - start_value.counter_fp; + int status = json_string(gen, "doubleValue") || yajl_gen_double(gen, diff); + if (status != 0) { + return status; + } + break; + } + case METRIC_TYPE_UP_DOWN: { char integer[64] = {0}; - ssnprintf(integer, sizeof(integer), "%" PRIu64, diff); + ssnprintf(integer, sizeof(integer), "%" PRId64, m->value.up_down); int status = json_string(gen, "int64Value") || json_string(gen, integer); if (status != 0) { @@ -166,6 +176,14 @@ static int format_typed_value(yajl_gen gen, metric_t const *m, } break; } + case METRIC_TYPE_UP_DOWN_FP: { + int status = json_string(gen, "doubleValue") || + yajl_gen_double(gen, m->value.up_down_fp); + if (status != 0) { + return status; + } + break; + } case METRIC_TYPE_UNTYPED: ERROR("format_typed_value: invalid metric type %d.", m->family->type); return EINVAL; @@ -185,9 +203,11 @@ static int format_typed_value(yajl_gen gen, metric_t const *m, static int format_metric_kind(yajl_gen gen, metric_t const *m) { switch (m->family->type) { case METRIC_TYPE_GAUGE: + case METRIC_TYPE_UP_DOWN: + case METRIC_TYPE_UP_DOWN_FP: return json_string(gen, "GAUGE"); case METRIC_TYPE_COUNTER: - case METRIC_TYPE_FPCOUNTER: + case METRIC_TYPE_COUNTER_FP: return json_string(gen, "CUMULATIVE"); case METRIC_TYPE_UNTYPED: break; @@ -206,8 +226,10 @@ static int format_metric_kind(yajl_gen gen, metric_t const *m) { static int format_value_type(yajl_gen gen, metric_t const *m) { switch (m->family->type) { case METRIC_TYPE_GAUGE: - case METRIC_TYPE_FPCOUNTER: + case METRIC_TYPE_COUNTER_FP: + case METRIC_TYPE_UP_DOWN_FP: return json_string(gen, "DOUBLE"); + case METRIC_TYPE_UP_DOWN: case METRIC_TYPE_COUNTER: return json_string(gen, "INT64"); case METRIC_TYPE_UNTYPED: @@ -272,7 +294,7 @@ static int format_time_interval(yajl_gen gen, metric_t const *m, if (status != 0) return status; - if (IS_CUMULATIVE(m->family->type)) { + if (IS_MONOTONIC(m->family->type)) { int status = json_string(gen, "startTime") || json_time(gen, start_time); if (status != 0) return status; @@ -385,12 +407,19 @@ static int format_time_series(yajl_gen gen, metric_t const *m, } break; case METRIC_TYPE_COUNTER: - case METRIC_TYPE_FPCOUNTER: + case METRIC_TYPE_COUNTER_FP: // for cumulative metrics the interval must not be zero. if (start.time == m->time) { return EAGAIN; } break; + case METRIC_TYPE_UP_DOWN: + break; + case METRIC_TYPE_UP_DOWN_FP: + if (!isfinite(m->value.up_down_fp)) { + return EAGAIN; + } + break; case METRIC_TYPE_UNTYPED: ERROR("format_stackdriver: Invalid metric type: %d", m->family->type); return EINVAL; diff --git a/src/utils/format_stackdriver/format_stackdriver_test.c b/src/utils/format_stackdriver/format_stackdriver_test.c index b39913a511..2bb1a98d5e 100644 --- a/src/utils/format_stackdriver/format_stackdriver_test.c +++ b/src/utils/format_stackdriver/format_stackdriver_test.c @@ -50,7 +50,7 @@ DEF_TEST(sd_format_metric_descriptor) { }, { .name = "fpcounter_metric", - .type = METRIC_TYPE_FPCOUNTER, + .type = METRIC_TYPE_COUNTER_FP, .want = "{\"type\":\"custom.googleapis.com/collectd/" "fpcounter_metric\",\"metricKind\":\"CUMULATIVE\"," "\"valueType\":\"DOUBLE\",\"labels\":[]}", diff --git a/src/utils/value_list/value_list.c b/src/utils/value_list/value_list.c index 2ca6692e3f..86fa4ac9da 100644 --- a/src/utils/value_list/value_list.c +++ b/src/utils/value_list/value_list.c @@ -396,8 +396,11 @@ plugin_value_list_to_metric_family(value_list_t const *vl, data_set_t const *ds, fam->name = strdup(buf.ptr); STRBUF_DESTROY(buf); - fam->type = (ds->ds[index].type == DS_TYPE_GAUGE) ? METRIC_TYPE_GAUGE - : METRIC_TYPE_COUNTER; + if (ds->ds[index].type == DS_TYPE_GAUGE) { + fam->type = METRIC_TYPE_UP_DOWN_FP; + } else { + fam->type = METRIC_TYPE_COUNTER; + } metric_t m = { .family = fam, diff --git a/src/utils/value_list/value_list.h b/src/utils/value_list/value_list.h index bad84c275d..12b98ef4f9 100644 --- a/src/utils/value_list/value_list.h +++ b/src/utils/value_list/value_list.h @@ -34,7 +34,7 @@ #define DS_TYPE_COUNTER METRIC_TYPE_COUNTER #define DS_TYPE_GAUGE METRIC_TYPE_GAUGE -#define DS_TYPE_DERIVE (65536 + METRIC_ATTR_CUMULATIVE + 1) +#define DS_TYPE_DERIVE METRIC_TYPE_UP_DOWN #define DS_TYPE_TO_STRING(t) \ (t == DS_TYPE_COUNTER) ? "counter" \ diff --git a/src/write_prometheus.c b/src/write_prometheus.c index 2375308b40..bf4c380007 100644 --- a/src/write_prometheus.c +++ b/src/write_prometheus.c @@ -278,7 +278,7 @@ static void format_metric_family_name(strbuf_t *buf, strbuf_print_restricted(buf, pfam->unit, VALID_NAME_CHARS, '_'); } - if (IS_CUMULATIVE(pfam->type)) { + if (IS_MONOTONIC(pfam->type)) { strbuf_print(buf, "_total"); } } @@ -291,10 +291,12 @@ static void format_metric_family(strbuf_t *buf, char *type = NULL; switch (pfam->type) { case METRIC_TYPE_GAUGE: + case METRIC_TYPE_UP_DOWN: + case METRIC_TYPE_UP_DOWN_FP: type = "gauge"; break; case METRIC_TYPE_COUNTER: - case METRIC_TYPE_FPCOUNTER: + case METRIC_TYPE_COUNTER_FP: type = "counter"; break; case METRIC_TYPE_UNTYPED: @@ -321,11 +323,8 @@ static void format_metric_family(strbuf_t *buf, char const *instance = label_set_get(pm->resource, "service.instance.id"); format_metric(buf, pm, family_name.ptr, job, instance); - - if (pfam->type == METRIC_TYPE_COUNTER) - strbuf_printf(buf, " %" PRIu64, pm->value.counter); - else - strbuf_printf(buf, " " GAUGE_FORMAT, pm->value.gauge); + strbuf_print(buf, " "); + value_marshal_text(buf, pm->value, pfam->type); if (pm->time > 0) { strbuf_printf(buf, " %" PRIi64 "\n", CDTIME_T_TO_MS(pm->time)); diff --git a/src/write_prometheus_test.c b/src/write_prometheus_test.c index 633e625431..e59cd30762 100644 --- a/src/write_prometheus_test.c +++ b/src/write_prometheus_test.c @@ -65,7 +65,7 @@ DEF_TEST(format_metric_family_name) { } cases[] = { { .name = "(lambda).function.executions(#)", - .type = METRIC_TYPE_UNTYPED, + .type = METRIC_TYPE_GAUGE, .want = "lambda_function_executions", }, { @@ -203,7 +203,7 @@ DEF_TEST(format_metric_family) { .pfam = { .name = "unit.test", - .type = METRIC_TYPE_UNTYPED, + .type = METRIC_TYPE_GAUGE, .metrics = &(prometheus_metric_t){ .label = @@ -223,7 +223,7 @@ DEF_TEST(format_metric_family) { .metrics_num = 1, }, .want = "# HELP unit_test\n" - "# TYPE unit_test untyped\n" + "# TYPE unit_test gauge\n" "unit_test{job=\"example.com\",instance=\"\",metric_name=" "\"unit.test\"} 42\n" "\n", @@ -233,7 +233,7 @@ DEF_TEST(format_metric_family) { .pfam = { .name = "unit.test", - .type = METRIC_TYPE_UNTYPED, + .type = METRIC_TYPE_GAUGE, .metrics = &(prometheus_metric_t){ .resource = @@ -256,17 +256,67 @@ DEF_TEST(format_metric_family) { }, .num = 1, }, - .value = - (value_t){ - .gauge = 42, - }, + .value.gauge = 42, + }, + .metrics_num = 1, + }, + // clang-format off + .want = + "# HELP unit_test\n" + "# TYPE unit_test gauge\n" + "unit_test{job=\"service name\",instance=\"service instance id\",metric_name=\"unit.test\"} 42\n" + "\n", + // clang-format on + }, + { + .name = "METRIC_TYPE_COUNTER_FP", + .pfam = + { + .name = "unit_test", + .type = METRIC_TYPE_COUNTER_FP, + .metrics = + (prometheus_metric_t[]){ + {.value.counter_fp = 42.0}, + }, + .metrics_num = 1, + }, + .want = "# HELP unit_test_total\n" + "# TYPE unit_test_total counter\n" + "unit_test_total{job=\"example.com\",instance=\"\"} 42\n" + "\n", + }, + { + .name = "METRIC_TYPE_UP_DOWN", + .pfam = + { + .name = "unit_test", + .type = METRIC_TYPE_UP_DOWN, + .metrics = + (prometheus_metric_t[]){ + {.value.up_down = 42}, + }, + .metrics_num = 1, + }, + .want = "# HELP unit_test\n" + "# TYPE unit_test gauge\n" + "unit_test{job=\"example.com\",instance=\"\"} 42\n" + "\n", + }, + { + .name = "METRIC_TYPE_UP_DOWN_FP", + .pfam = + { + .name = "unit_test", + .type = METRIC_TYPE_UP_DOWN_FP, + .metrics = + (prometheus_metric_t[]){ + {.value.up_down_fp = 42.0}, }, .metrics_num = 1, }, .want = "# HELP unit_test\n" - "# TYPE unit_test untyped\n" - "unit_test{job=\"service name\",instance=\"service instance " - "id\",metric_name=\"unit.test\"} 42\n" + "# TYPE unit_test gauge\n" + "unit_test{job=\"example.com\",instance=\"\"} 42\n" "\n", }, };