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) {
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) {
}
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"));
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;
+}
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 */
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;
}