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_FPCOUNTER:
+ return strbuf_printf(buf, GAUGE_FORMAT, v.fpcounter);
+ case METRIC_TYPE_UP_DOWN_COUNTER:
+ return strbuf_printf(buf, "%" PRId64, v.up_down_counter);
+ case METRIC_TYPE_UP_DOWN_COUNTER_FP:
+ if (isnan(v.up_down_counter_fp)) {
+ return strbuf_print(buf, "nan");
+ }
+ return strbuf_printf(buf, GAUGE_FORMAT, v.up_down_counter_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) {
#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_FPCOUNTER are monotonically increasing floating point counts.
+ // The rate of change is meaningful, the absolute value is not.
+ METRIC_TYPE_FPCOUNTER =
+ METRIC_ATTR_DOUBLE | METRIC_ATTR_CUMULATIVE | METRIC_ATTR_MONOTONIC,
+ // METRIC_TYPE_UP_DOWN_COUNTER are absolute integer metrics that can
+ // (meaningfully) be summed up. Examples are filesystem space used and
+ // physical memory.
+ METRIC_TYPE_UP_DOWN_COUNTER = METRIC_ATTR_CUMULATIVE,
+ // METRIC_TYPE_UP_DOWN_COUNTER_FP are absolute floating point metrics that can
+ // (meaningfully) be summed up.
+ METRIC_TYPE_UP_DOWN_COUNTER_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_FPCOUNTER) ? "fpcounter" \
+ : (t == METRIC_TYPE_UP_DOWN_COUNTER) ? "up_down_counter" \
+ : (t == METRIC_TYPE_UP_DOWN_COUNTER_FP) ? "up_down_counter_fp" \
+ : "unknown"
+#define IS_DOUBLE(t) ((t)&METRIC_ATTR_DOUBLE)
+#define IS_MONOTONIC(t) ((t)&METRIC_ATTR_MONOTONIC)
+
+typedef double gauge_t;
typedef uint64_t counter_t;
typedef double fpcounter_t;
-typedef double gauge_t;
typedef int64_t derive_t;
+typedef int64_t up_down_counter_t;
+typedef double up_down_counter_fp_t;
union value_u {
+ gauge_t gauge;
counter_t counter;
fpcounter_t fpcounter;
- gauge_t gauge;
+ up_down_counter_t up_down_counter;
+ up_down_counter_fp_t up_down_counter_fp;
+ // For collectd 5 compatiblity. Treated the same as up_down_counter.
derive_t derive;
};
typedef union value_u value_t;
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
return 0;
}
- case METRIC_TYPE_GAUGE: {
- ce->values_gauge = m->value.gauge;
+ case METRIC_TYPE_UP_DOWN_COUNTER: {
+ ce->values_gauge = (gauge_t) m->value.up_down_counter;
+ return 0;
+ }
+
+ case METRIC_TYPE_UP_DOWN_COUNTER_FP: {
+ ce->values_gauge = (gauge_t) m->value.up_down_counter_fp;
return 0;
}
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_FPCOUNTER:
+ break;
+ case METRIC_TYPE_UP_DOWN_COUNTER:
+ *ret = (gauge_t)m->value.up_down_counter;
+ return 0;
+ case METRIC_TYPE_UP_DOWN_COUNTER_FP:
+ *ret = (gauge_t)m->value.up_down_counter_fp;
+ return 0;
+ case METRIC_TYPE_UNTYPED:
+ return EINVAL;
}
strbuf_t buf = STRBUF_CREATE;
.type = METRIC_TYPE_FPCOUNTER,
.want = NAN,
},
+ {
+ .name = "up_down_counter",
+ .first_value = (value_t){.up_down_counter = 10},
+ .second_value = (value_t){.up_down_counter = 20},
+ .first_time = TIME_T_TO_CDTIME_T(100),
+ .second_time = TIME_T_TO_CDTIME_T(110),
+ .type = METRIC_TYPE_UP_DOWN_COUNTER,
+ .want = 20,
+ },
+ {
+ .name = "decreasing up_down_counter",
+ .first_value = (value_t){.up_down_counter = 1000},
+ .second_value = (value_t){.up_down_counter = 215},
+ .first_time = TIME_T_TO_CDTIME_T(100),
+ .second_time = TIME_T_TO_CDTIME_T(110),
+ .type = METRIC_TYPE_UP_DOWN_COUNTER,
+ .want = 215,
+ },
+ {
+ .name = "up_down_counter_fp",
+ .first_value = (value_t){.up_down_counter_fp = 1.0},
+ .second_value = (value_t){.up_down_counter_fp = 2.0},
+ .first_time = TIME_T_TO_CDTIME_T(100),
+ .second_time = TIME_T_TO_CDTIME_T(110),
+ .type = METRIC_TYPE_UP_DOWN_COUNTER_FP,
+ .want = 2.0,
+ },
+ {
+ .name = "decreasing up_down_counter_fp",
+ .first_value = (value_t){.up_down_counter_fp = 100.0},
+ .second_value = (value_t){.up_down_counter_fp = 21.5},
+ .first_time = TIME_T_TO_CDTIME_T(100),
+ .second_time = TIME_T_TO_CDTIME_T(110),
+ .type = METRIC_TYPE_UP_DOWN_COUNTER_FP,
+ .want = 21.5,
+ },
};
for (size_t i = 0; i < STATIC_ARRAY_SIZE(cases); i++) {
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_COUNTER) {
+ want = (gauge_t)cases[i].first_value.up_down_counter;
}
EXPECT_EQ_DOUBLE(want, got);
m->family->type = METRIC_TYPE_COUNTER;
} else if (strcasecmp("FPCOUNTER", value) == 0) {
m->family->type = METRIC_TYPE_FPCOUNTER;
+ } else if (strcasecmp("UP_DOWN_COUNTER", value) == 0) {
+ m->family->type = METRIC_TYPE_UP_DOWN_COUNTER;
+ } else if (strcasecmp("UP_DOWN_COUNTER_FP", value) == 0) {
+ m->family->type = METRIC_TYPE_UP_DOWN_COUNTER_FP;
} else {
return CMD_ERROR;
}
case METRIC_TYPE_GAUGE:
strbuf_print(buf, " type=GAUGE");
break;
+ case METRIC_TYPE_UP_DOWN_COUNTER:
+ strbuf_print(buf, " type=UP_DOWN_COUNTER");
+ break;
+ case METRIC_TYPE_UP_DOWN_COUNTER_FP:
+ strbuf_print(buf, " type=UP_DOWN_COUNTER_FP");
+ break;
case METRIC_TYPE_UNTYPED:
/* no op */
break;
} /* 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_FPCOUNTER:
+ ret_value->fpcounter = (fpcounter_t)strtod(value, &endptr);
break;
- case DS_TYPE_DERIVE:
- ret_value->derive = (derive_t)strtoll(value, &endptr, 0);
+ case METRIC_TYPE_UP_DOWN_COUNTER:
+ ret_value->up_down_counter = (up_down_counter_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_COUNTER_FP:
+ ret_value->up_down_counter_fp =
+ (up_down_counter_fp_t)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
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;
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 */
state->last_value.fpcounter = (fpcounter_t)rate;
state->residual = 0;
break;
+ case METRIC_TYPE_UP_DOWN_COUNTER:
+ state->last_value.up_down_counter = (up_down_counter_t)floor(rate);
+ state->residual = rate - ((gauge_t)state->last_value.up_down_counter);
+ break;
+ case METRIC_TYPE_UP_DOWN_COUNTER_FP:
+ state->last_value.up_down_counter_fp = (up_down_counter_fp_t)rate;
+ state->residual = 0;
+ break;
case METRIC_TYPE_UNTYPED:
ERROR("rate_to_value: invalid metric type: %d", type);
return EINVAL;
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_FPCOUNTER: {
- fpcounter_t delta_counter = (fpcounter_t)delta_gauge;
- state->last_value.fpcounter += delta_counter;
+ fpcounter_t delta = (fpcounter_t)delta_gauge;
+ state->last_value.fpcounter += delta;
+ state->residual = 0;
+ break;
+ }
+ case METRIC_TYPE_UP_DOWN_COUNTER: {
+ up_down_counter_t delta = (up_down_counter_t)floor(delta_gauge);
+ state->last_value.up_down_counter += delta;
+ state->residual = delta_gauge - ((gauge_t)delta);
+ break;
+ }
+ case METRIC_TYPE_UP_DOWN_COUNTER_FP: {
+ up_down_counter_fp_t delta = (up_down_counter_fp_t)delta_gauge;
+ state->last_value.up_down_counter_fp += delta;
state->residual = 0;
break;
}
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);
*ret_rate = ((gauge_t)diff) / ((gauge_t)interval);
return 0;
}
+ case METRIC_TYPE_UP_DOWN_COUNTER: {
+ up_down_counter_t diff =
+ value.up_down_counter - state->last_value.up_down_counter;
+ *ret_rate = ((gauge_t)diff) / ((gauge_t)interval);
+ return 0;
+ }
+ case METRIC_TYPE_UP_DOWN_COUNTER_FP: {
+ up_down_counter_fp_t diff =
+ value.up_down_counter_fp - state->last_value.up_down_counter_fp;
+ *ret_rate = ((gauge_t)diff) / ((gauge_t)interval);
+ return 0;
+ }
case METRIC_TYPE_UNTYPED:
break;
}
(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;
}
switch (cases[i].type) {
- 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);
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_FPCOUNTER:
EXPECT_EQ_DOUBLE(cases[i].want.fpcounter, got.fpcounter);
EXPECT_EQ_UINT64(cases[i].want.fpcounter, state.last_value.fpcounter);
break;
+ case METRIC_TYPE_UP_DOWN_COUNTER:
+ EXPECT_EQ_UINT64(cases[i].want.up_down_counter, got.up_down_counter);
+ EXPECT_EQ_UINT64(cases[i].want.up_down_counter, state.last_value.up_down_counter);
+ break;
+ case METRIC_TYPE_UP_DOWN_COUNTER_FP:
+ EXPECT_EQ_DOUBLE(cases[i].want.up_down_counter_fp, got.up_down_counter_fp);
+ EXPECT_EQ_UINT64(cases[i].want.up_down_counter_fp, state.last_value.up_down_counter_fp);
+ break;
case METRIC_TYPE_UNTYPED:
LOG(false, "invalid metric type");
break;
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,
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_COUNTER_FP &&
+ isnan(m->value.up_down_counter_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) {
**/
#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 <yajl/yajl_common.h>
#include <yajl/yajl_gen.h>
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_FPCOUNTER:
+ return "FPCOUNTER";
+ case METRIC_TYPE_UP_DOWN_COUNTER:
+ return "UP_DOWN_COUNTER";
+ case METRIC_TYPE_UP_DOWN_COUNTER_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:
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));
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))
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:
CHECK(json_add_string(g, "asDouble"));
CHECK(yajl_gen_double(g, m->value.fpcounter));
break;
- case METRIC_TYPE_GAUGE:
+ case METRIC_TYPE_UP_DOWN_COUNTER:
+ CHECK(json_add_string(g, "asInt"));
+ CHECK(yajl_gen_integer(g, (long long int)m->value.up_down_counter));
+ break;
+ case METRIC_TYPE_UP_DOWN_COUNTER_FP:
CHECK(json_add_string(g, "asDouble"));
- CHECK(yajl_gen_double(g, m->value.gauge));
+ CHECK(yajl_gen_integer(g, m->value.up_down_counter_fp));
break;
case METRIC_TYPE_UNTYPED:
ERROR("format_json_open_telemetry: Unexpected metric type: %d",
}
switch (fam->type) {
+ case METRIC_TYPE_GAUGE:
+ CHECK(json_add_string(g, "gauge"));
+ CHECK(gauge(g, fam));
+ break;
case METRIC_TYPE_COUNTER:
case METRIC_TYPE_FPCOUNTER:
+ case METRIC_TYPE_UP_DOWN_COUNTER:
+ case METRIC_TYPE_UP_DOWN_COUNTER_FP:
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_UNTYPED:
ERROR("format_json_open_telemetry: Unexpected metric type: %d", fam->type);
return EINVAL;
}
return 0;
}
+ case METRIC_TYPE_COUNTER:
+ CHECK(yajl_gen_integer(g, (long long int)m->value.counter));
+ 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));
+ case METRIC_TYPE_UP_DOWN_COUNTER:
+ CHECK(yajl_gen_integer(g, (long long int)m->value.up_down_counter));
+ return 0;
+ case METRIC_TYPE_UP_DOWN_COUNTER_FP:
+ CHECK(yajl_gen_double(g, m->value.up_down_counter_fp));
return 0;
case METRIC_TYPE_UNTYPED:
break;
// A valid metric type is guaranteed by add_metric().
switch (m->family->type) {
+ case METRIC_TYPE_GAUGE:
+ dp->set_as_double(m->value.gauge);
+ return;
case METRIC_TYPE_COUNTER:
- dp->set_as_int(m->value.derive);
+ dp->set_as_int((int64_t)m->value.counter);
+ return;
+ case METRIC_TYPE_UP_DOWN_COUNTER:
+ dp->set_as_int((int64_t)m->value.up_down_counter);
return;
- case METRIC_TYPE_GAUGE:
case METRIC_TYPE_FPCOUNTER:
- dp->set_as_double(m->value.gauge);
+ dp->set_as_double(m->value.fpcounter);
+ return;
+ case METRIC_TYPE_UP_DOWN_COUNTER_FP:
+ dp->set_as_double(m->value.up_down_counter_fp);
return;
case METRIC_TYPE_UNTYPED:
// Fall through. This case signals the compiler that we're checking all
}
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) {
}
switch (fam->type) {
+ case METRIC_TYPE_GAUGE:
+ set_gauge(m, fam);
+ return;
case METRIC_TYPE_COUNTER:
case METRIC_TYPE_FPCOUNTER:
+ case METRIC_TYPE_UP_DOWN_COUNTER:
+ case METRIC_TYPE_UP_DOWN_COUNTER_FP:
set_sum(m, fam);
return;
- case METRIC_TYPE_GAUGE:
- set_gauge(m, fam);
- return;
case METRIC_TYPE_UNTYPED:
// Never reached, only here to show the compiler we're handling all possible
// `metric_type_t` values.
}
break;
}
+ case METRIC_TYPE_COUNTER: {
+ /* Counter resets are handled in format_time_series(). */
+ assert(m->value.counter >= start_value.counter);
+
+ 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_FPCOUNTER: {
/* Counter resets are handled in format_time_series(). */
assert(m->value.fpcounter >= start_value.fpcounter);
}
break;
}
- case METRIC_TYPE_COUNTER: {
- /* Counter resets are handled in format_time_series(). */
- assert(m->value.counter >= start_value.counter);
-
- counter_t diff = m->value.counter - start_value.counter;
+ case METRIC_TYPE_UP_DOWN_COUNTER: {
char integer[64] = {0};
- ssnprintf(integer, sizeof(integer), "%" PRIu64, diff);
+ ssnprintf(integer, sizeof(integer), "%" PRId64, m->value.up_down_counter);
int status = json_string(gen, "int64Value") || json_string(gen, integer);
if (status != 0) {
}
break;
}
+ case METRIC_TYPE_UP_DOWN_COUNTER_FP: {
+ int status = json_string(gen, "doubleValue") ||
+ yajl_gen_double(gen, m->value.up_down_counter_fp);
+ if (status != 0) {
+ return status;
+ }
+ break;
+ }
case METRIC_TYPE_UNTYPED:
ERROR("format_typed_value: invalid metric type %d.", m->family->type);
return EINVAL;
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_COUNTER:
+ case METRIC_TYPE_UP_DOWN_COUNTER_FP:
return json_string(gen, "GAUGE");
case METRIC_TYPE_COUNTER:
case METRIC_TYPE_FPCOUNTER:
switch (m->family->type) {
case METRIC_TYPE_GAUGE:
case METRIC_TYPE_FPCOUNTER:
+ case METRIC_TYPE_UP_DOWN_COUNTER_FP:
return json_string(gen, "DOUBLE");
+ case METRIC_TYPE_UP_DOWN_COUNTER:
case METRIC_TYPE_COUNTER:
return json_string(gen, "INT64");
case METRIC_TYPE_UNTYPED:
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;
return EAGAIN;
}
break;
+ case METRIC_TYPE_UP_DOWN_COUNTER:
+ break;
+ case METRIC_TYPE_UP_DOWN_COUNTER_FP:
+ if (!isfinite(m->value.up_down_counter_fp)) {
+ return EAGAIN;
+ }
+ break;
case METRIC_TYPE_UNTYPED:
ERROR("format_stackdriver: Invalid metric type: %d", m->family->type);
return EINVAL;
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_COUNTER_FP;
+ } else {
+ fam->type = METRIC_TYPE_COUNTER;
+ }
metric_t m = {
.family = fam,
#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_COUNTER
#define DS_TYPE_TO_STRING(t) \
(t == DS_TYPE_COUNTER) ? "counter" \
strbuf_print_restricted(buf, pfam->unit, VALID_NAME_CHARS, '_');
}
- if (IS_CUMULATIVE(pfam->type)) {
+ if (IS_MONOTONIC(pfam->type)) {
strbuf_print(buf, "_total");
}
}
char *type = NULL;
switch (pfam->type) {
case METRIC_TYPE_GAUGE:
+ case METRIC_TYPE_UP_DOWN_COUNTER:
+ case METRIC_TYPE_UP_DOWN_COUNTER_FP:
type = "gauge";
break;
case METRIC_TYPE_COUNTER:
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));
} cases[] = {
{
.name = "(lambda).function.executions(#)",
- .type = METRIC_TYPE_UNTYPED,
+ .type = METRIC_TYPE_GAUGE,
.want = "lambda_function_executions",
},
{
.pfam =
{
.name = "unit.test",
- .type = METRIC_TYPE_UNTYPED,
+ .type = METRIC_TYPE_GAUGE,
.metrics =
&(prometheus_metric_t){
.label =
.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",
.pfam =
{
.name = "unit.test",
- .type = METRIC_TYPE_UNTYPED,
+ .type = METRIC_TYPE_GAUGE,
.metrics =
&(prometheus_metric_t){
.resource =
},
.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_FPCOUNTER",
+ .pfam =
+ {
+ .name = "unit_test",
+ .type = METRIC_TYPE_FPCOUNTER,
+ .metrics =
+ (prometheus_metric_t[]){
+ {.value.fpcounter = 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_COUNTER",
+ .pfam =
+ {
+ .name = "unit_test",
+ .type = METRIC_TYPE_UP_DOWN_COUNTER,
+ .metrics =
+ (prometheus_metric_t[]){
+ {.value.up_down_counter = 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_COUNTER_FP",
+ .pfam =
+ {
+ .name = "unit_test",
+ .type = METRIC_TYPE_UP_DOWN_COUNTER_FP,
+ .metrics =
+ (prometheus_metric_t[]){
+ {.value.up_down_counter_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",
},
};