From: Florian Forster Date: Wed, 24 Jan 2024 10:31:13 +0000 (+0100) Subject: format_json: Add the `format_json_metric_identity` function. X-Git-Tag: collectd-6.0.0.rc2~13^2~11 X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=a1f453788bb1b40d26910309d3d5c8fdfbf930a4;p=thirdparty%2Fcollectd.git format_json: Add the `format_json_metric_identity` function. --- diff --git a/src/utils/format_json/format_json.c b/src/utils/format_json/format_json.c index e759d24b3..b4a99343b 100644 --- a/src/utils/format_json/format_json.c +++ b/src/utils/format_json/format_json.c @@ -113,6 +113,19 @@ static int format_time(yajl_gen g, cdtime_t t) /* {{{ */ return 0; } /* }}} int format_time */ +static int format_label_set(yajl_gen g, label_set_t labels) { + CHECK(yajl_gen_map_open(g)); /* BEGIN labels */ + + for (size_t i = 0; i < labels.num; i++) { + label_pair_t *l = labels.ptr + i; + CHECK(json_add_string(g, l->name)); + CHECK(json_add_string(g, l->value)); + } + + CHECK(yajl_gen_map_close(g)); /* END labels */ + return 0; +} + /* TODO(octo): format_metric should export the interval, too. */ /* TODO(octo): Decide whether format_metric should export meta data. */ static int format_metric(yajl_gen g, metric_t const *m) { @@ -120,15 +133,7 @@ static int format_metric(yajl_gen g, metric_t const *m) { if (m->label.num != 0) { CHECK(json_add_string(g, "labels")); - CHECK(yajl_gen_map_open(g)); /* BEGIN labels */ - - for (size_t i = 0; i < m->label.num; i++) { - label_pair_t *l = m->label.ptr + i; - CHECK(json_add_string(g, l->name)); - CHECK(json_add_string(g, l->value)); - } - - CHECK(yajl_gen_map_close(g)); /* END labels */ + CHECK(format_label_set(g, m->label)); } if (m->time != 0) { @@ -321,12 +326,10 @@ static int format_alert(yajl_gen g, notification_t const *n) /* {{{ */ } CHECK(json_add_string(g, "severity")); - CHECK(json_add_string( - g, (n->severity == NOTIF_FAILURE) - ? "FAILURE" - : (n->severity == NOTIF_WARNING) - ? "WARNING" - : (n->severity == NOTIF_OKAY) ? "OKAY" : "UNKNOWN")); + CHECK(json_add_string(g, (n->severity == NOTIF_FAILURE) ? "FAILURE" + : (n->severity == NOTIF_WARNING) ? "WARNING" + : (n->severity == NOTIF_OKAY) ? "OKAY" + : "UNKNOWN")); CHECK(json_add_string(g, "service")); CHECK(json_add_string(g, "collectd")); @@ -430,3 +433,63 @@ int format_json_notification(char *buffer, size_t buffer_size, /* {{{ */ yajl_gen_free(g); return 0; } /* }}} format_json_notification */ + +static int format_metric_identity(yajl_gen g, metric_t const *m) { + CHECK(yajl_gen_map_open(g)); + + CHECK(json_add_string(g, "name")); + CHECK(json_add_string(g, m->family->name)); + + if (m->family->resource.num != 0) { + CHECK(json_add_string(g, "resource")); + CHECK(format_label_set(g, m->family->resource)); + } + + if (m->label.num != 0) { + CHECK(json_add_string(g, "labels")); + CHECK(format_label_set(g, m->label)); + } + + CHECK(yajl_gen_map_close(g)); + return 0; +} + +int format_json_metric_identity(strbuf_t *buf, metric_t const *m) { + if (buf == NULL || m == NULL) { + return EINVAL; + } + +#if HAVE_YAJL_V2 + yajl_gen g = yajl_gen_alloc(NULL); +#else /* !HAVE_YAJL_V2 */ + yajl_gen_config conf = {0}; + yajl_gen g = yajl_gen_alloc(&conf, NULL); +#endif + if (g == NULL) { + return -1; + } + + if (format_metric_identity(g, m) != 0) { + yajl_gen_clear(g); + yajl_gen_free(g); + return -1; + } + + unsigned char const *out; +#if HAVE_YAJL_V2 + size_t out_len; +#else + unsigned int out_len; +#endif + /* copy to output buffer */ + if (yajl_gen_get_buf(g, &out, &out_len) != yajl_gen_status_ok) { + yajl_gen_clear(g); + yajl_gen_free(g); + return -1; + } + strbuf_printn(buf, (char const *)out, (size_t)out_len); + + yajl_gen_clear(g); + yajl_gen_free(g); + return 0; +} diff --git a/src/utils/format_json/format_json.h b/src/utils/format_json/format_json.h index 082273147..a09adb74a 100644 --- a/src/utils/format_json/format_json.h +++ b/src/utils/format_json/format_json.h @@ -52,4 +52,6 @@ int format_json_notification(char *buffer, size_t buffer_size, int format_json_open_telemetry(strbuf_t *buf, resource_metrics_set_t const *set); +int format_json_metric_identity(strbuf_t *buf, metric_t const *m); + #endif /* UTILS_FORMAT_JSON_H */ diff --git a/src/utils/format_json/format_json_test.c b/src/utils/format_json/format_json_test.c index 528be87c0..f775cbec6 100644 --- a/src/utils/format_json/format_json_test.c +++ b/src/utils/format_json/format_json_test.c @@ -346,11 +346,130 @@ DEF_TEST(open_telemetry) { return 0; } +DEF_TEST(format_json_metric_identity) { + struct { + char *name; + label_pair_t *labels; + size_t labels_num; + label_pair_t *rattr; + size_t rattr_num; + char const *want; + } cases[] = { + { + .name = "metric_without_labels", + .want = "{\"name\":\"metric_without_labels\"}", + }, + { + .name = "metric_with_labels", + .labels = + (label_pair_t[]){ + {"sorted", "yes"}, + {"alphabetically", "true"}, + }, + .labels_num = 2, + .want = "{\"name\":\"metric_with_labels\",\"labels\":{" + "\"alphabetically\":\"true\",\"sorted\":\"yes\"}}", + }, + { + .name = "escape_sequences", + .labels = + (label_pair_t[]){ + {"newline", "\n"}, + {"quote", "\""}, + {"tab", "\t"}, + {"cardridge_return", "\r"}, + }, + .labels_num = 4, + .want = + "{\"name\":\"escape_sequences\",\"labels\":{\"cardridge_return\":" + "\"\\r\",\"newline\":\"\\n\",\"quote\":\"\\\"\",\"tab\":\"\\t\"}" + "}", + }, + { + .name = "metric_with_resource", + .rattr = + (label_pair_t[]){ + {"host.name", "example.com"}, + }, + .rattr_num = 1, + .want = "{\"name\":\"metric_with_resource\",\"resource\":{\"host." + "name\":\"example.com\"}}", + }, + { + .name = "metric_with_resource_and_labels", + .rattr = + (label_pair_t[]){ + {"omega", "always"}, + {"alpha", "resources"}, + }, + .rattr_num = 2, + .labels = + (label_pair_t[]){ + {"gamma", "first"}, + {"beta", "come"}, + }, + .labels_num = 2, + .want = "{\"name\":\"metric_with_resource_and_labels\",\"resource\":{" + "\"alpha\":\"resources\",\"omega\":\"always\"},\"labels\":{" + "\"beta\":\"come\",\"gamma\":\"first\"}}", + }, + { + .name = "complex_names.are.quoted", + .rattr = + (label_pair_t[]){ + {"with space", "gets quotes"}, + }, + .rattr_num = 1, + .labels = + (label_pair_t[]){ + {"and \"quotes\" are", "escaped"}, + }, + .labels_num = 1, + .want = "{\"name\":\"complex_names.are.quoted\",\"resource\":{\"with " + "space\":\"gets quotes\"},\"labels\":{\"and \\\"quotes\\\" " + "are\":\"escaped\"}}", + }, + }; + + for (size_t i = 0; i < (sizeof(cases) / sizeof(cases[0])); i++) { + printf("## Case %zu: %s\n", i, cases[i].name); + + metric_family_t fam = { + .name = cases[i].name, + .type = METRIC_TYPE_UNTYPED, + }; + metric_t m = { + .family = &fam, + }; + for (size_t j = 0; j < cases[i].labels_num; j++) { + CHECK_ZERO(metric_label_set(&m, cases[i].labels[j].name, + cases[i].labels[j].value)); + } + for (size_t j = 0; j < cases[i].rattr_num; j++) { + CHECK_ZERO(metric_family_resource_attribute_update( + &fam, cases[i].rattr[j].name, cases[i].rattr[j].value)); + } + + strbuf_t buf = STRBUF_CREATE; + CHECK_ZERO(format_json_metric_identity(&buf, &m)); + + EXPECT_EQ_STR(cases[i].want, buf.ptr); + + STRBUF_DESTROY(buf); + metric_family_metric_reset(&fam); + label_set_reset(&fam.resource); + metric_reset(&m); + } + + return 0; +} + int main(void) { RUN_TEST(notification); RUN_TEST(metric_family); RUN_TEST(metric_family_append); RUN_TEST(open_telemetry); + RUN_TEST(format_json_metric_identity); END_TEST; }