#include "collectd.h"
#include "plugin.h"
-#include "utils/common/common.h"
#include "utils/avltree/avltree.h"
-
+#include "utils/common/common.h"
#include "utils/format_graphite/format_graphite.h"
#include "utils_cache.h"
/* Utils functions to format data sets in graphite format.
* Largely taken from write_graphite.c as it remains the same formatting */
-static int gr_format_values(char *ret, size_t ret_len, const metric_t *metric_p,
- gauge_t rate) {
- size_t offset = 0;
- int status;
-
- memset(ret, 0, ret_len);
-
-#define BUFFER_ADD(...) \
- do { \
- status = snprintf(ret + offset, ret_len - offset, __VA_ARGS__); \
- if (status < 1) { \
- return -1; \
- } else if (((size_t)status) >= (ret_len - offset)) { \
- return -1; \
- } else \
- offset += ((size_t)status); \
- } while (0)
-
- if (metric_p->value_type == DS_TYPE_GAUGE)
- BUFFER_ADD(GAUGE_FORMAT, metric_p->value.gauge);
+static int gr_format_values(strbuf_t *buf, const metric_t *m, gauge_t rate) {
+ if (m->value_type == DS_TYPE_GAUGE)
+ return strbuf_printf(buf, GAUGE_FORMAT, m->value.gauge);
else if (rate != -1)
- BUFFER_ADD("%f", rate);
- else if (metric_p->value_type == DS_TYPE_COUNTER)
- BUFFER_ADD("%" PRIu64, (uint64_t)metric_p->value.counter);
- else if (metric_p->value_type == DS_TYPE_DERIVE)
- BUFFER_ADD("%" PRIi64, metric_p->value.derive);
- else {
- P_ERROR("gr_format_values: Unknown data source type: %i",
- metric_p->value_type);
- return -1;
- }
-
-#undef BUFFER_ADD
-
- return 0;
+ return strbuf_printf(buf, "%f", rate);
+ else if (m->value_type == DS_TYPE_COUNTER)
+ return strbuf_printf(buf, "%" PRIu64, (uint64_t)m->value.counter);
+ else if (m->value_type == DS_TYPE_DERIVE)
+ return strbuf_printf(buf, "%" PRIi64, m->value.derive);
+
+ P_ERROR("gr_format_values: Unknown data source type: %d", m->value_type);
+ return EINVAL;
}
-static void gr_copy_escape_part(char *dst, const char *src, size_t dst_len,
- char escape_char, bool preserve_separator) {
- if (src == NULL)
- return;
-
- for (size_t i = 0; i < dst_len; i++) {
- if (src[i] == 0) {
- dst[i] = 0;
- break;
- }
-
- if ((!preserve_separator && (src[i] == '.')) || ((src[i] == '/')) ||
- isspace((int)src[i]) || iscntrl((int)src[i]))
- dst[i] = escape_char;
- else
- dst[i] = src[i];
+static int graphite_print_escaped(strbuf_t *buf, char const *s,
+ char escape_char) {
+ if ((buf == NULL) || (s == NULL)) {
+ return EINVAL;
}
-}
-static int gr_format_name_tagged(char *ret, int ret_len,
- metric_t const *metric_p, char const *prefix,
- char const *postfix, char const escape_char,
- unsigned int flags) {
- int starting_len = ret_len;
- if (prefix == NULL)
- prefix = "";
-
- if (postfix == NULL)
- postfix = "";
-
- memset(ret, 0, ret_len);
-
- int tmp_str_len = 0;
- tmp_str_len = strlen(prefix);
- if (tmp_str_len < ret_len) {
- snprintf(ret, tmp_str_len + 1, "%s", prefix);
- ret += tmp_str_len; /* This is the location of the trailing nul */
- ret_len -= tmp_str_len;
- } else {
- snprintf(ret, ret_len, "%s", prefix);
- return starting_len;
- }
-
- tmp_str_len = strlen(metric_p->identity->name);
- if (tmp_str_len < ret_len) {
- gr_copy_escape_part(ret, metric_p->identity->name, tmp_str_len + 1,
- escape_char, 1);
- ret += tmp_str_len;
- ret_len -= tmp_str_len;
- } else {
- gr_copy_escape_part(ret, metric_p->identity->name, ret_len, escape_char, 1);
- return starting_len;
- }
+ size_t s_len = strlen(s);
+ while (s_len > 0) {
+ size_t valid_len = strcspn(s, GRAPHITE_FORBIDDEN);
+ if (valid_len == s_len) {
+ return strbuf_print(buf, s);
+ }
+ if (valid_len != 0) {
+ char tmp[valid_len + 1];
+ strncpy(tmp, s, valid_len);
+ tmp[valid_len] = 0;
+ int status = strbuf_print(buf, tmp);
+ if (status != 0) {
+ return status;
+ }
- tmp_str_len = strlen(postfix);
- if (tmp_str_len < ret_len) {
- snprintf(ret, tmp_str_len + 1, "%s", postfix);
- ret += tmp_str_len; /* This is the location of the trailing nul */
- ret_len -= tmp_str_len;
- } else {
- snprintf(ret, ret_len, "%s", postfix);
- return starting_len;
- }
+ s += valid_len;
+ s_len -= valid_len;
+ continue;
+ }
- if (metric_p->identity->root_p != NULL) {
- c_avl_iterator_t *iter_p = c_avl_get_iterator(metric_p->identity->root_p);
- if (iter_p != NULL) {
- char *key_p = NULL;
- char *value_p = NULL;
- while ((c_avl_iterator_next(iter_p, (void **)&key_p,
- (void **)&value_p)) == 0) {
- if ((key_p != NULL) && (value_p != NULL)) {
- tmp_str_len = strlen(key_p) + strlen(value_p) + 2;
- if (tmp_str_len < ret_len) {
- snprintf(ret, tmp_str_len + 1, ";%s=%s", key_p, value_p);
- ret += tmp_str_len;
- ret_len -= tmp_str_len;
- } else {
- snprintf(ret, ret_len, ";%s=%s", key_p, value_p);
- return starting_len;
- }
- }
- }
- c_avl_iterator_destroy(iter_p);
+ char tmp[2] = {escape_char, 0};
+ int status = strbuf_print(buf, tmp);
+ if (status != 0) {
+ return status;
}
+
+ s++;
+ s_len--;
}
- return starting_len - ret_len; /* Characters appended */
+ return 0;
}
-static int gr_format_name(char *ret, int ret_len, metric_t const *metric_p,
- char const *prefix, char const *postfix,
- char const escape_char, unsigned int flags) {
- int starting_len = ret_len;
- if (prefix == NULL)
- prefix = "";
-
- if (postfix == NULL)
- postfix = "";
-
- memset(ret, 0, ret_len);
-
- int tmp_str_len = 0;
- tmp_str_len = strlen(prefix);
- if (tmp_str_len < ret_len) {
- snprintf(ret, tmp_str_len + 1, "%s", prefix);
- ret += tmp_str_len; /* This is the location of the trailing nul */
- ret_len -= tmp_str_len;
- } else {
- snprintf(ret, ret_len, "%s", prefix);
- return starting_len;
+static int gr_format_name(strbuf_t *buf, metric_t const *m, char const *prefix,
+ char const *suffix, char const escape_char,
+ unsigned int flags) {
+ if (prefix != NULL) {
+ strbuf_print(buf, prefix);
}
-
- tmp_str_len = strlen(metric_p->identity->name);
- if (tmp_str_len < ret_len) {
- gr_copy_escape_part(ret, metric_p->identity->name, tmp_str_len + 1,
- escape_char, 1);
- ret += tmp_str_len;
- ret_len -= tmp_str_len;
- } else {
- gr_copy_escape_part(ret, metric_p->identity->name, ret_len, escape_char, 1);
- return starting_len;
+ graphite_print_escaped(buf, m->identity->name, escape_char);
+ if (suffix != NULL) {
+ strbuf_print(buf, suffix);
}
- tmp_str_len = strlen(postfix);
- if (tmp_str_len < ret_len) {
- snprintf(ret, tmp_str_len + 1, "%s", postfix);
- ret += tmp_str_len; /* This is the location of the trailing nul */
- ret_len -= tmp_str_len;
- } else {
- snprintf(ret, ret_len, "%s", postfix);
- return starting_len;
+ c_avl_iterator_t *iter = c_avl_get_iterator(m->identity->labels);
+ char *k = NULL, *v = NULL;
+ while ((c_avl_iterator_next(iter, (void **)&k, (void **)&v)) == 0) {
+ strbuf_print(buf, ".");
+ graphite_print_escaped(buf, k, escape_char);
+ strbuf_print(buf, (flags & GRAPHITE_SEPARATE_INSTANCES) ? "." : "=");
+ graphite_print_escaped(buf, v, escape_char);
}
+ c_avl_iterator_destroy(iter);
- if (metric_p->identity->root_p != NULL) {
- c_avl_iterator_t *iter_p = c_avl_get_iterator(metric_p->identity->root_p);
- if (iter_p != NULL) {
- char *key_p = NULL;
- char *value_p = NULL;
- while ((c_avl_iterator_next(iter_p, (void **)&key_p,
- (void **)&value_p)) == 0) {
- if ((key_p != NULL) && (value_p != NULL)) {
- tmp_str_len = strlen(value_p) + 1;
- if (tmp_str_len < ret_len) {
- snprintf(ret, tmp_str_len + 1, ";%s", value_p);
- ret += tmp_str_len;
- ret_len -= tmp_str_len;
- } else {
- snprintf(ret, ret_len, ";%s", value_p);
- return starting_len;
- }
- }
- }
- c_avl_iterator_destroy(iter_p);
- }
- }
-
- return starting_len - ret_len; /* Characters appended */
-}
-
-static void escape_graphite_string(char *buffer, char escape_char) {
- assert(strchr(GRAPHITE_FORBIDDEN, escape_char) == NULL);
-
- for (char *head = buffer + strcspn(buffer, GRAPHITE_FORBIDDEN); *head != '\0';
- head += strcspn(head, GRAPHITE_FORBIDDEN))
- *head = escape_char;
+ return 0;
}
-int format_graphite(char *buffer, size_t buffer_size, metric_t const *metric_p,
- char const *prefix, char const *postfix,
- char const escape_char, unsigned int flags) {
- int status = 0;
- int buffer_pos = 0;
-
+int format_graphite(strbuf_t *buf, metric_t const *m, char const *prefix,
+ char const *postfix, char const escape_char,
+ unsigned int flags) {
gauge_t rate = -1;
if (flags & GRAPHITE_STORE_RATES) {
- status = uc_get_rate(metric_p, &rate);
+ int status = uc_get_rate(m, &rate);
if (status != 0) {
P_ERROR("format_graphite: error with uc_get_rate");
return -1;
}
}
- char key[10 * DATA_MAX_NAME_LEN];
- char values[512];
- size_t message_len;
- char message[1024];
+ /* format:
+ * <name> <value> <time> '\r' '\n'
+ */
- /* Copy the identifier to `key' and escape it. */
- if (flags & GRAPHITE_USE_TAGS) {
- status = gr_format_name_tagged(key, sizeof(key), metric_p, prefix, postfix,
- escape_char, flags);
- if (status != 0) {
- P_ERROR("format_graphite: error with gr_format_name_tagged");
- return status;
- }
- } else {
- status = gr_format_name(key, sizeof(key), metric_p, prefix, postfix,
- escape_char, flags);
- if (status != 0) {
- P_ERROR("format_graphite: error with gr_format_name");
- return status;
- }
- }
+ gr_format_name(buf, m, prefix, postfix, escape_char, flags);
- escape_graphite_string(key, escape_char);
+ strbuf_print(buf, " ");
/* Convert the values to an ASCII representation and put that into
* `values'. */
- status = gr_format_values(values, sizeof(values), metric_p, rate);
+ int status = gr_format_values(buf, m, rate);
if (status != 0) {
P_ERROR("format_graphite: error with gr_format_values");
return status;
}
- /* Compute the graphite command */
- message_len =
- (size_t)snprintf(message, sizeof(message), "%s %s %u\r\n", key, values,
- (unsigned int)CDTIME_T_TO_TIME_T(metric_p->time));
- if (message_len >= sizeof(message)) {
- P_ERROR("format_graphite: message buffer too small: "
- "Need %" PRIsz " bytes.",
- message_len + 1);
- return -ENOMEM;
- }
-
- /* Append it in case we got multiple data set */
- if ((buffer_pos + message_len) >= buffer_size) {
- P_ERROR("format_graphite: target buffer too small");
- return -ENOMEM;
- }
- memcpy((void *)(buffer + buffer_pos), message, message_len);
- buffer_pos += message_len;
- buffer[buffer_pos] = '\0';
-
- return status;
+ return strbuf_printf(buf, " %lu\r\n",
+ (unsigned long)CDTIME_T_TO_TIME_T(m->time));
} /* int format_graphite */
/**
* collectd - src/utils_format_graphite_test.c
- * Copyright (C) 2016 Florian octo Forster
+ * Copyright (C) 2016-2020 Florian octo Forster
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
#include "utils/common/common.h" /* for STATIC_ARRAY_SIZE */
#include "utils/format_graphite/format_graphite.h"
-static data_set_t ds_single = {
- .type = "single",
- .ds_num = 1,
- .ds = &(data_source_t){"value", DS_TYPE_GAUGE, NAN, NAN},
-};
-
-/*
-static data_set_t ds_double = {
- .type = "double",
- .ds_num = 2,
- .ds =
- (data_source_t[]){
- {"one", DS_TYPE_DERIVE, 0, NAN}, {"two", DS_TYPE_DERIVE, 0, NAN},
- },
-};
-*/
-
DEF_TEST(metric_name) {
struct {
- const char *plugin_instance;
- const char *type_instance;
- const char *prefix;
- const char *suffix;
+ char const *name;
+ size_t labels_num;
+ char const **keys;
+ char const **values;
+ value_t value;
+ int value_type;
unsigned int flags;
- const char *want_name;
+ const char *want;
} cases[] = {
{
- .want_name = "example@com.test.single",
- },
- /* plugin and type instances */
- {
- .plugin_instance = "foo",
- .type_instance = "bar",
- .want_name = "example@com.test-foo.single-bar",
- },
- {
- .plugin_instance = NULL,
- .type_instance = "bar",
- .want_name = "example@com.test.single-bar",
- },
- {
- .plugin_instance = "foo",
- .type_instance = NULL,
- .want_name = "example@com.test-foo.single",
+ .name = "unit_test",
+ .value = (value_t){.gauge = 42},
+ .value_type = DS_TYPE_GAUGE,
+ .want = "unit_test 42",
},
- /* special chars */
{
- .plugin_instance = "foo (test)",
- .type_instance = "test: \"hello\"",
- .want_name = "example@com.test-foo@@test@.single-test@@@hello@",
+ .name = "test_with_label",
+ .labels_num = 2,
+ .keys = (char const *[]){"beta", "alpha"},
+ .values = (char const *[]){"second", "first"},
+ .value = (value_t){.derive = -9223372036854775807LL},
+ .value_type = DS_TYPE_DERIVE,
+ .want =
+ "test_with_label.alpha=first.beta=second -9223372036854775807",
},
- /* test escaping comma */
{
- .plugin_instance = "foo (test)",
- .type_instance = "test,123,",
- .want_name = "example@com.test-foo@@test@.single-test@123@",
- },
- /* flag GRAPHITE_SEPARATE_INSTANCES */
- {
- .plugin_instance = "foo",
- .type_instance = "bar",
+ .name = "separate_instances_test",
+ .labels_num = 2,
+ .keys = (char const *[]){"beta", "alpha"},
+ .values = (char const *[]){"second", "first"},
+ .value = (value_t){.derive = 9223372036854775807LL},
+ .value_type = DS_TYPE_DERIVE,
.flags = GRAPHITE_SEPARATE_INSTANCES,
- .want_name = "example@com.test.foo.single.bar",
- },
- /* flag GRAPHITE_ALWAYS_APPEND_DS */
- {
- .plugin_instance = "foo",
- .type_instance = "bar",
- .flags = GRAPHITE_ALWAYS_APPEND_DS,
- .want_name = "example@com.test-foo.single-bar.value",
+ .want = "separate_instances_test.alpha.first.beta.second "
+ "9223372036854775807",
},
- /* flag GRAPHITE_PRESERVE_SEPARATOR */
{
- .plugin_instance = "f.o.o",
- .type_instance = "b.a.r",
- .flags = 0,
- .want_name = "example@com.test-f@o@o.single-b@a@r",
+ .name = "escaped:metric_name",
+ .value = (value_t){.gauge = NAN},
+ .value_type = DS_TYPE_GAUGE,
+ .want = "escaped_metric_name nan",
},
{
- .plugin_instance = "f.o.o",
- .type_instance = "b.a.r",
- .flags = GRAPHITE_PRESERVE_SEPARATOR,
- .want_name = "example.com.test-f.o.o.single-b.a.r",
+ .name = "escaped_label_value",
+ .labels_num = 2,
+ .keys = (char const *[]){"beta", "alpha"},
+ .values = (char const *[]){"second value", "first/value"},
+ .value = (value_t){.counter = 18446744073709551615LLU},
+ .value_type = DS_TYPE_COUNTER,
+ .want = "escaped_label_value.alpha=first_value.beta=second_value "
+ "18446744073709551615",
},
- /* prefix and suffix */
- {
- .prefix = "foo.",
- .suffix = ".bar",
- .want_name = "foo.example@com.bar.test.single",
- },
- {
- .prefix = NULL,
- .suffix = ".bar",
- .want_name = "example@com.bar.test.single",
- },
- {
- .prefix = "foo.",
- .suffix = NULL,
- .want_name = "foo.example@com.test.single",
- },
- /* flag GRAPHITE_USE_TAGS */
- {.flags = GRAPHITE_USE_TAGS,
- .want_name = "test.single;host=example.com;plugin=test;type=single"},
- {.plugin_instance = "f.o.o",
- .type_instance = "b.a.r",
- .flags = GRAPHITE_USE_TAGS,
- .want_name = "test.single;host=example.com;plugin=test;plugin_instance="
- "f.o.o;type=single;type_instance=b.a.r"},
- {.flags = GRAPHITE_USE_TAGS ^ GRAPHITE_ALWAYS_APPEND_DS,
- .want_name = "test.single.value;host=example.com;plugin=test;type="
- "single;ds_name=value"},
- {.plugin_instance = "foo",
- .type_instance = "foo",
- .flags = GRAPHITE_USE_TAGS ^ GRAPHITE_DROP_DUPE_FIELDS,
- .want_name = "test.single;host=example.com;plugin=test;plugin_instance="
- "foo;type=single"},
};
for (size_t i = 0; i < STATIC_ARRAY_SIZE(cases); i++) {
- value_list_t vl = {
- .values = &(value_t){.gauge = 42},
- .values_len = 1,
- .time = TIME_T_TO_CDTIME_T_STATIC(1480063672),
- .interval = TIME_T_TO_CDTIME_T_STATIC(10),
- .host = "example.com",
- .plugin = "test",
- .type = "single",
+ identity_t *id;
+ CHECK_NOT_NULL(id = identity_create(cases[i].name));
+ for (size_t j = 0; j < cases[i].labels_num; j++) {
+ CHECK_ZERO(identity_add_label(id, cases[i].keys[j], cases[i].values[j]));
+ }
+
+ metric_t m = {
+ .identity = id,
+ .value = cases[i].value,
+ .value_type = cases[i].value_type,
+ .time = 1710200311404036096, /* 1592748157.125 */
};
+ strbuf_t buf = STRBUF_CREATE;
+ EXPECT_EQ_INT(0, format_graphite(&buf, &m, "", "", '_', cases[i].flags));
+
strbuf_t want = STRBUF_CREATE;
- if (cases[i].want_name != NULL) {
- strbuf_print(&want, cases[i].want_name);
- strbuf_print(&want, " 42 1480063672\r\n");
+ if (cases[i].want != NULL) {
+ strbuf_print(&want, cases[i].want);
+ strbuf_print(&want, " 1480063672\r\n");
}
+ EXPECT_EQ_STR(want.ptr, buf.ptr);
- if (cases[i].plugin_instance != NULL)
- sstrncpy(vl.plugin_instance, cases[i].plugin_instance,
- sizeof(vl.plugin_instance));
- if (cases[i].type_instance != NULL)
- sstrncpy(vl.type_instance, cases[i].type_instance,
- sizeof(vl.type_instance));
-
- char got[1024];
- EXPECT_EQ_INT(0, format_graphite(got, sizeof(got), &ds_single, &vl,
- cases[i].prefix, cases[i].suffix, '@',
- cases[i].flags));
- EXPECT_EQ_STR(want.ptr, got);
STRBUF_DESTROY(want);
+ STRBUF_DESTROY(buf);
+ identity_destroy(id);
}
return 0;
}
-DEF_TEST(null_termination) {
- value_list_t vl = {
- .values = &(value_t){.gauge = 1337},
- .values_len = 1,
- .time = TIME_T_TO_CDTIME_T_STATIC(1480063672),
- .interval = TIME_T_TO_CDTIME_T_STATIC(10),
- .host = "example.com",
- .plugin = "test",
- .type = "single",
- };
- char const *want = "example_com.test.single 1337 1480063672\r\n";
-
- char buffer[128];
- for (size_t i = 0; i < sizeof(buffer); i++)
- buffer[i] = (char)i;
-
- EXPECT_EQ_INT(0, format_graphite(buffer, sizeof(buffer), &ds_single, &vl,
- NULL, NULL, '_', 0));
- EXPECT_EQ_STR(want, buffer);
- EXPECT_EQ_INT(0, buffer[strlen(want)]);
- for (size_t i = strlen(want) + 1; i < sizeof(buffer); i++)
- EXPECT_EQ_INT((int)i, (int)buffer[i]);
-
- return 0;
-}
-
int main(void) {
RUN_TEST(metric_name);
- RUN_TEST(null_termination);
END_TEST;
}