]> git.ipfire.org Git - thirdparty/collectd.git/commitdiff
format_json: Add the `format_json_metric_identity` function.
authorFlorian Forster <octo@collectd.org>
Wed, 24 Jan 2024 10:31:13 +0000 (11:31 +0100)
committerFlorian Forster <octo@collectd.org>
Thu, 25 Jan 2024 19:53:29 +0000 (20:53 +0100)
src/utils/format_json/format_json.c
src/utils/format_json/format_json.h
src/utils/format_json/format_json_test.c

index e759d24b3343c0080664d72a4f4019c4a0f05a75..b4a99343b9573ad05edc6b83cbc07ca628df63d4 100644 (file)
@@ -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;
+}
index 0822731474127a45c33ac3adfa67d6a8b46a894e..a09adb74a1ec4ac18e817fdd2238cebeb39ff1e9 100644 (file)
@@ -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 */
index 528be87c0bfdb3688675beeece4e48b8889a1bbe..f775cbec64ca310da95ba7771527abf4ad968614 100644 (file)
@@ -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;
 }