]> git.ipfire.org Git - thirdparty/collectd.git/commitdiff
write_prometheus plugin: Refactor the internal data structures.
authorFlorian Forster <octo@collectd.org>
Mon, 19 Feb 2024 22:17:41 +0000 (23:17 +0100)
committerFlorian Forster <octo@collectd.org>
Tue, 20 Feb 2024 15:00:59 +0000 (16:00 +0100)
(Ab)Using the `metric_t` and `metric_family_t` data structures provided by the
daemon made memory management quite hard and therefore brittle.

This introduces types specific to the *write_prometheus plugin* that store the
resource attributes with the metric, not the family.

Makefile.am
src/write_prometheus.c
src/write_prometheus_test.c

index 3a13c3042d7e802eb4b229c4cc318fa0db00dfa5..7232f90d433b7adb87228b7c7dbbf4c70996a0fd 100644 (file)
@@ -2379,8 +2379,7 @@ write_prometheus_la_CPPFLAGS = $(AM_CPPFLAGS) $(BUILD_WITH_LIBMICROHTTPD_CPPFLAG
 write_prometheus_la_LDFLAGS = $(PLUGIN_LDFLAGS) $(BUILD_WITH_LIBMICROHTTPD_LDFLAGS)
 write_prometheus_la_LIBADD = $(BUILD_WITH_LIBMICROHTTPD_LIBS)
 
-test_plugin_write_prometheus_SOURCES = src/write_prometheus_test.c \
-                                      src/write_prometheus.c
+test_plugin_write_prometheus_SOURCES = src/write_prometheus_test.c
 test_plugin_write_prometheus_CPPFLAGS = $(AM_CPPFLAGS) $(BUILD_WITH_LIBMICROHTTPD_CPPFLAGS)
 test_plugin_write_prometheus_LDFLAGS = $(PLUGIN_LDFLAGS) $(BUILD_WITH_LIBMICROHTTPD_LDFLAGS)
 test_plugin_write_prometheus_LDADD = libplugin_mock.la libavltree.la \
index c15fce52c4a73840592cc8bf8116ce0658ac6728..707877243ed4ed5d4a04ac0efdab56b2301c6ff1 100644 (file)
 // instrument-name = ALPHA 0*254 ("_" / "." / "-" / "/" / ALPHA / DIGIT)
 #define VALID_NAME_CHARS VALID_LABEL_CHARS ":"
 
-#define RESOURCE_LABEL_PREFIX "resource_"
+typedef struct {
+  label_set_t resource;
+  label_set_t label;
+
+  value_t value;
+  cdtime_t time;
+  cdtime_t interval;
+} prometheus_metric_t;
+
+typedef struct {
+  char *name;
+  char *help;
+  char *unit;
+  metric_type_t type;
+
+  prometheus_metric_t *metrics;
+  size_t metrics_num;
+} prometheus_metric_family_t;
 
 static c_avl_tree_t *prom_metrics;
 static pthread_mutex_t prom_metrics_lock = PTHREAD_MUTEX_INITIALIZER;
@@ -202,10 +219,10 @@ static int format_label_set(strbuf_t *buf, label_set_t labels, char const *job,
   return status;
 }
 
-static int format_metric(strbuf_t *buf, metric_t const *m,
+static int format_metric(strbuf_t *buf, prometheus_metric_t const *pm,
                          char const *metric_family_name, char const *job,
                          char const *instance) {
-  if ((buf == NULL) || (m == NULL)) {
+  if ((buf == NULL) || (pm == NULL)) {
     return EINVAL;
   }
 
@@ -213,12 +230,12 @@ static int format_metric(strbuf_t *buf, metric_t const *m,
    * not replace any characters. */
   int status =
       strbuf_print_restricted(buf, metric_family_name, VALID_NAME_CHARS, '_');
-  if (job == NULL && instance == NULL && m->label.num == 0) {
+  if (job == NULL && instance == NULL && pm->label.num == 0) {
     return status;
   }
 
   status = status || strbuf_print(buf, "{");
-  status = status || format_label_set(buf, m->label, job, instance);
+  status = status || format_label_set(buf, pm->label, job, instance);
   status = status || strbuf_print(buf, "}");
 
   return status;
@@ -230,13 +247,13 @@ static int format_metric(strbuf_t *buf, metric_t const *m,
  * multiple underscores into one underscore.
  *
  * Visible for testing */
-void format_metric_family_name(strbuf_t *buf, metric_family_t const *fam) {
-  size_t name_len = strlen(fam->name);
+void format_metric_family_name(strbuf_t *buf, prometheus_metric_family_t const *pfam) {
+  size_t name_len = strlen(pfam->name);
   char name[name_len + 1];
   memset(name, 0, sizeof(name));
 
   strbuf_t namebuf = STRBUF_CREATE_FIXED(name, sizeof(name));
-  strbuf_print_restricted(&namebuf, fam->name, VALID_NAME_CHARS, '_');
+  strbuf_print_restricted(&namebuf, pfam->name, VALID_NAME_CHARS, '_');
   STRBUF_DESTROY(namebuf);
 
   bool skip_underscore = true;
@@ -259,26 +276,27 @@ void format_metric_family_name(strbuf_t *buf, metric_family_t const *fam) {
 
   strbuf_print(buf, name);
 
-  unit_map_t const *unit = unit_map_lookup(fam->unit);
+  unit_map_t const *unit = unit_map_lookup(pfam->unit);
   if (unit != NULL) {
     strbuf_printf(buf, "_%s", unit->prometheus);
-  } else if (fam->unit != NULL && fam->unit[0] != '{') {
+  } else if (pfam->unit != NULL && pfam->unit[0] != '{') {
     strbuf_print(buf, "_");
-    strbuf_print_restricted(buf, fam->unit, VALID_NAME_CHARS, '_');
+    strbuf_print_restricted(buf, pfam->unit, VALID_NAME_CHARS, '_');
   }
 
-  if (IS_CUMULATIVE(fam->type)) {
+  if (IS_CUMULATIVE(pfam->type)) {
     strbuf_print(buf, "_total");
   }
 }
 
 /* visible for testing */
-void format_metric_family(strbuf_t *buf, metric_family_t const *prom_fam) {
-  if (prom_fam->metric.num == 0)
+void format_metric_family(strbuf_t *buf,
+                          prometheus_metric_family_t const *pfam) {
+  if (pfam->metrics_num == 0)
     return;
 
   char *type = NULL;
-  switch (prom_fam->type) {
+  switch (pfam->type) {
   case METRIC_TYPE_GAUGE:
     type = "gauge";
     break;
@@ -295,31 +313,29 @@ void format_metric_family(strbuf_t *buf, metric_family_t const *prom_fam) {
   }
 
   strbuf_t family_name = STRBUF_CREATE;
-  format_metric_family_name(&family_name, prom_fam);
+  format_metric_family_name(&family_name, pfam);
 
-  if (prom_fam->help == NULL)
+  if (pfam->help == NULL)
     strbuf_printf(buf, "# HELP %s\n", family_name.ptr);
   else
-    strbuf_printf(buf, "# HELP %s %s\n", family_name.ptr, prom_fam->help);
+    strbuf_printf(buf, "# HELP %s %s\n", family_name.ptr, pfam->help);
   strbuf_printf(buf, "# TYPE %s %s\n", family_name.ptr, type);
 
-  for (size_t i = 0; i < prom_fam->metric.num; i++) {
-    metric_t *m = &prom_fam->metric.ptr[i];
+  for (size_t i = 0; i < pfam->metrics_num; i++) {
+    prometheus_metric_t *pm = pfam->metrics + i;
 
-    // Note: m->family != prom_fam
-    char const *job = label_set_get(m->family->resource, "service.name");
-    char const *instance =
-        label_set_get(m->family->resource, "service.instance.id");
+    char const *job = label_set_get(pm->resource, "service.name");
+    char const *instance = label_set_get(pm->resource, "service.instance.id");
 
-    format_metric(buf, m, family_name.ptr, job, instance);
+    format_metric(buf, pm, family_name.ptr, job, instance);
 
-    if (prom_fam->type == METRIC_TYPE_COUNTER)
-      strbuf_printf(buf, " %" PRIu64, m->value.counter);
+    if (pfam->type == METRIC_TYPE_COUNTER)
+      strbuf_printf(buf, " %" PRIu64, pm->value.counter);
     else
-      strbuf_printf(buf, " " GAUGE_FORMAT, m->value.gauge);
+      strbuf_printf(buf, " " GAUGE_FORMAT, pm->value.gauge);
 
-    if (m->time > 0) {
-      strbuf_printf(buf, " %" PRIi64 "\n", CDTIME_T_TO_MS(m->time));
+    if (pm->time > 0) {
+      strbuf_printf(buf, " %" PRIi64 "\n", CDTIME_T_TO_MS(pm->time));
     } else {
       strbuf_printf(buf, "\n");
     }
@@ -386,15 +402,15 @@ static void target_info_reset(target_info_t *ti) {
  * https://github.com/OpenObservability/OpenMetrics/blob/main/specification/OpenMetrics.md#supporting-target-metadata-in-both-push-based-and-pull-based-systems
  * for more details. */
 /* visible for testing */
-void target_info(strbuf_t *buf, metric_family_t const **families,
+void target_info(strbuf_t *buf, prometheus_metric_family_t const **families,
                  size_t families_num) {
   target_info_t ti = {0};
 
   for (size_t i = 0; i < families_num; i++) {
-    metric_family_t const *fam = families[i];
-    for (size_t j = 0; j < fam->metric.num; j++) {
-      metric_t const *m = fam->metric.ptr + j;
-      target_info_add(&ti, m->family->resource);
+    prometheus_metric_family_t const *pfam = families[i];
+    for (size_t j = 0; j < pfam->metrics_num; j++) {
+      prometheus_metric_t const *pm = pfam->metrics + j;
+      target_info_add(&ti, pm->resource);
     }
   }
 
@@ -440,13 +456,13 @@ void target_info(strbuf_t *buf, metric_family_t const **families,
 }
 
 static void format_metric_families(strbuf_t *buf,
-                                   metric_family_t const **families,
+                                   prometheus_metric_family_t const **families,
                                    size_t families_num) {
   target_info(buf, families, families_num);
 
   for (size_t i = 0; i < families_num; i++) {
-    metric_family_t const *fam = families[i];
-    format_metric_family(buf, fam);
+    prometheus_metric_family_t const *pfam = families[i];
+    format_metric_family(buf, pfam);
   }
 }
 
@@ -455,17 +471,16 @@ void format_text(strbuf_t *buf) {
   pthread_mutex_lock(&prom_metrics_lock);
 
   size_t families_num = (size_t)c_avl_size(prom_metrics);
-  metric_family_t const *families[families_num];
+  prometheus_metric_family_t const *families[families_num];
   memset(families, 0, sizeof(families));
 
   char *unused = NULL;
-  metric_family_t *prom_fam = NULL;
+  prometheus_metric_family_t *pfam = NULL;
   c_avl_iterator_t *iter = c_avl_get_iterator(prom_metrics);
   for (size_t i = 0;
-       c_avl_iterator_next(iter, (void *)&unused, (void *)&prom_fam) == 0;
-       i++) {
+       c_avl_iterator_next(iter, (void *)&unused, (void *)&pfam) == 0; i++) {
     assert(i < families_num);
-    families[i] = prom_fam;
+    families[i] = pfam;
   }
   c_avl_iterator_destroy(iter);
 
@@ -521,131 +536,145 @@ static MHD_RESULT http_handler(__attribute__((unused)) void *cls,
 /* metric_cmp compares two metrics. It's prototype makes it easy to use with
  * qsort(3) and bsearch(3). */
 static int prom_metric_cmp(void const *a, void const *b) {
-  metric_t const *m_a = (metric_t *)a;
-  metric_t const *m_b = (metric_t *)b;
+  prometheus_metric_t const *pma = (prometheus_metric_t const *)a;
+  prometheus_metric_t const *pmb = (prometheus_metric_t const *)b;
 
-  int cmp = label_set_compare(m_a->family->resource, m_b->family->resource);
+  int cmp = label_set_compare(pma->resource, pmb->resource);
   if (cmp) {
     return cmp;
   }
 
-  return label_set_compare(m_a->label, m_b->label);
+  return label_set_compare(pma->label, pmb->label);
 }
 
-static void prom_resource_attr_free(metric_family_t *attr) {
-  if (attr == NULL) {
-    return;
-  }
-
-  label_set_reset(&attr->resource);
-  sfree(attr);
+static prometheus_metric_t to_prometheus_metric(metric_t m) {
+  return (prometheus_metric_t){
+      .resource = m.family->resource,
+      .label = m.label,
+      .value = m.value,
+      .time = m.time,
+      .interval = m.interval,
+  };
 }
 
-static metric_family_t *prom_resource_attr_clone(metric_family_t const *fam) {
-  metric_family_t *attr = calloc(1, sizeof(*attr));
-  if (attr == NULL) {
-    return NULL;
-  }
-
-  int err = label_set_clone(&attr->resource, fam->resource);
-  if (err) {
-    prom_resource_attr_free(attr);
-    return NULL;
+static int prom_metric_family_metric_append(prometheus_metric_family_t *pfam,
+                                            prometheus_metric_t pm) {
+  prometheus_metric_t *ptr =
+      realloc(pfam->metrics, sizeof(*pfam->metrics) * (pfam->metrics_num + 1));
+  if (ptr == NULL) {
+    return ENOMEM;
   }
 
-  return attr;
-}
+  pfam->metrics = ptr;
+  ptr = pfam->metrics + pfam->metrics_num;
 
-static int prom_metric_family_metric_append(metric_family_t *fam,
-                                            metric_t const *m) {
-  metric_t copy = *m;
+  *ptr = (prometheus_metric_t){
+      .value = pm.value,
+      .time = pm.time,
+      .interval = pm.interval,
+  };
 
-  copy.family = prom_resource_attr_clone(m->family);
-  if (copy.family == NULL) {
-    return ENOMEM;
+  int err = label_set_clone(&ptr->resource, pm.resource);
+  if (err) {
+    return err;
   }
 
-  int err = metric_list_append(&fam->metric, copy);
+  err = label_set_clone(&ptr->label, pm.label);
   if (err) {
-    prom_resource_attr_free(copy.family);
+    label_set_reset(&ptr->resource);
     return err;
   }
 
+  pfam->metrics_num++;
+
+  /* Sort the metrics so we can use binary search. */
+  qsort(pfam->metrics, pfam->metrics_num, sizeof(*pfam->metrics),
+        prom_metric_cmp);
+
   return 0;
 }
 
-static void prom_metric_reset(metric_t *m) {
-  prom_resource_attr_free(m->family);
-  metric_reset(m);
+static void prom_metric_reset(prometheus_metric_t *pm) {
+  label_set_reset(&pm->resource);
+  label_set_reset(&pm->label);
 }
 
-static int prom_metric_family_metric_delete(metric_family_t *fam,
-                                            metric_t const *m) {
-  if ((fam == NULL) || (m == NULL)) {
+static int prom_metric_family_metric_delete(prometheus_metric_family_t *pfam,
+                                            prometheus_metric_t pm) {
+  if (pfam == NULL) {
     return EINVAL;
   }
+
   size_t i;
-  for (i = 0; i < fam->metric.num; i++) {
-    if (prom_metric_cmp(m, &fam->metric.ptr[i]) == 0)
+  for (i = 0; i < pfam->metrics_num; i++) {
+    if (prom_metric_cmp(&pm, pfam->metrics + i) == 0)
       break;
   }
 
-  if (i >= fam->metric.num)
+  if (i >= pfam->metrics_num)
     return ENOENT;
 
-  prom_metric_reset(&fam->metric.ptr[i]);
+  prom_metric_reset(&pfam->metrics[i]);
 
-  if ((fam->metric.num - 1) > i)
-    memmove(&fam->metric.ptr[i], &fam->metric.ptr[i + 1],
-            ((fam->metric.num - 1) - i) * sizeof(fam->metric.ptr[i]));
+  if ((pfam->metrics_num - 1) > i) {
+    memmove(&pfam->metrics[i], &pfam->metrics[i + 1],
+            ((pfam->metrics_num - 1) - i) * sizeof(pfam->metrics[i]));
+  }
 
-  fam->metric.num--;
+  pfam->metrics_num--;
 
-  if (fam->metric.num == 0) {
-    sfree(fam->metric.ptr);
-    fam->metric.ptr = NULL;
+  if (pfam->metrics_num == 0) {
+    sfree(pfam->metrics);
+    pfam->metrics = NULL;
     return 0;
   }
 
-  metric_t *tmp =
-      realloc(fam->metric.ptr, fam->metric.num * sizeof(*fam->metric.ptr));
+  prometheus_metric_t *tmp =
+      realloc(pfam->metrics, pfam->metrics_num * sizeof(*pfam->metrics));
   if (tmp != NULL)
-    fam->metric.ptr = tmp;
+    pfam->metrics = tmp;
 
   return 0;
 }
 
-static void prom_metric_family_free(metric_family_t *prom_fam) {
-  for (size_t i = 0; i < prom_fam->metric.num; i++) {
-    metric_t *m = prom_fam->metric.ptr + i;
-    prom_resource_attr_free(m->family);
-    m->family = NULL;
+static void prom_metric_family_free(prometheus_metric_family_t *pfam) {
+  for (size_t i = 0; i < pfam->metrics_num; i++) {
+    prometheus_metric_t *m = pfam->metrics + i;
+    prom_metric_reset(m);
   }
 
-  metric_family_free(prom_fam);
+  sfree(pfam->metrics);
+  pfam->metrics_num = 0;
+
+  sfree(pfam->name);
+  sfree(pfam->help);
+  sfree(pfam->unit);
+
+  sfree(pfam);
 }
 
-static metric_family_t *prom_metric_family_clone(metric_family_t const *fam) {
-  metric_family_t *copy = calloc(1, sizeof(*copy));
-  if (copy == NULL) {
+static prometheus_metric_family_t *
+prom_metric_family_clone(metric_family_t const *fam) {
+  prometheus_metric_family_t *pfam = calloc(1, sizeof(*pfam));
+  if (pfam == NULL) {
     return NULL;
   }
-  copy->type = fam->type;
+  pfam->type = fam->type;
 
-  copy->name = strdup(fam->name);
-  if (copy->name == NULL) {
-    sfree(copy);
+  pfam->name = strdup(fam->name);
+  if (pfam->name == NULL) {
+    sfree(pfam);
     return NULL;
   }
 
   if (fam->help != NULL) {
-    copy->help = strdup(fam->help);
+    pfam->help = strdup(fam->help);
   }
   if (fam->unit != NULL) {
-    copy->unit = strdup(fam->unit);
+    pfam->unit = strdup(fam->unit);
   }
 
-  return copy;
+  return pfam;
 }
 
 static void prom_logger(__attribute__((unused)) void *arg, char const *fmt,
@@ -829,7 +858,7 @@ void free_metrics(void) {
   }
 
   char *name = NULL;
-  metric_family_t *prom_fam = NULL;
+  prometheus_metric_family_t *prom_fam = NULL;
   while (c_avl_pick(prom_metrics, (void *)&name, (void *)&prom_fam) == 0) {
     assert(name == prom_fam->name);
     name = NULL;
@@ -863,45 +892,42 @@ int prom_write(metric_family_t const *fam,
                __attribute__((unused)) user_data_t *ud) {
   pthread_mutex_lock(&prom_metrics_lock);
 
-  metric_family_t *prom_fam = NULL;
-  if (c_avl_get(prom_metrics, fam->name, (void *)&prom_fam) != 0) {
-    prom_fam = prom_metric_family_clone(fam);
-    if (prom_fam == NULL) {
+  prometheus_metric_family_t *pfam = NULL;
+  if (c_avl_get(prom_metrics, fam->name, (void *)&pfam) != 0) {
+    pfam = prom_metric_family_clone(fam);
+    if (pfam == NULL) {
       ERROR("write_prometheus plugin: Clone metric \"%s\" failed.", fam->name);
       pthread_mutex_unlock(&prom_metrics_lock);
       return ENOMEM;
     }
 
-    int err = c_avl_insert(prom_metrics, prom_fam->name, prom_fam);
+    int err = c_avl_insert(prom_metrics, pfam->name, pfam);
     if (err) {
-      ERROR("write_prometheus plugin: Adding \"%s\" failed.", prom_fam->name);
-      metric_family_free(prom_fam);
+      ERROR("write_prometheus plugin: Adding \"%s\" failed.", pfam->name);
+      prom_metric_family_free(pfam);
       pthread_mutex_unlock(&prom_metrics_lock);
       return err;
     }
   }
 
   for (size_t i = 0; i < fam->metric.num; i++) {
-    metric_t const *m = &fam->metric.ptr[i];
+    prometheus_metric_t pm = to_prometheus_metric(fam->metric.ptr[i]);
 
-    metric_t *mmatch = bsearch(m, prom_fam->metric.ptr, prom_fam->metric.num,
-                               sizeof(*prom_fam->metric.ptr), prom_metric_cmp);
+    metric_t *mmatch = bsearch(&pm, pfam->metrics, pfam->metrics_num,
+                               sizeof(*pfam->metrics), prom_metric_cmp);
     if (mmatch == NULL) {
-      prom_metric_family_metric_append(prom_fam, m);
-      /* Sort the metrics so that lookup is fast. */
-      qsort(prom_fam->metric.ptr, prom_fam->metric.num,
-            sizeof(*prom_fam->metric.ptr), prom_metric_cmp);
+      prom_metric_family_metric_append(pfam, pm);
       continue;
     }
 
-    mmatch->value = m->value;
+    mmatch->value = pm.value;
 
     /* Prometheus has a globally configured timeout after which metrics are
      * considered stale. This causes problems when metrics have an interval
      * exceeding that limit. We emulate the behavior of "pushgateway" and
      * *not* send a timestamp value â€“ Prometheus will fill in the current
      * time. */
-    if (m->interval > staleness_delta) {
+    if (pm.interval > staleness_delta) {
       static c_complain_t long_metric = C_COMPLAIN_INIT_STATIC;
       c_complain(LOG_NOTICE, &long_metric,
                  "write_prometheus plugin: You have metrics with an interval "
@@ -912,7 +938,7 @@ int prom_write(metric_family_t const *fam,
 
       mmatch->time = 0;
     } else {
-      mmatch->time = m->time;
+      mmatch->time = pm.time;
     }
   }
 
@@ -925,21 +951,22 @@ static int prom_missing(metric_family_t const *fam,
 
   pthread_mutex_lock(&prom_metrics_lock);
 
-  metric_family_t *prom_fam = NULL;
-  if (c_avl_get(prom_metrics, fam->name, (void *)&prom_fam) != 0) {
+  prometheus_metric_family_t *pfam = NULL;
+  if (c_avl_get(prom_metrics, fam->name, (void *)&pfam) != 0) {
     pthread_mutex_unlock(&prom_metrics_lock);
     return 0;
   }
 
   for (size_t i = 0; i < fam->metric.num; i++) {
-    metric_t const *m = &fam->metric.ptr[i];
+    prometheus_metric_t pm = to_prometheus_metric(fam->metric.ptr[i]);
 
-    metric_t *mmatch = bsearch(m, prom_fam->metric.ptr, prom_fam->metric.num,
-                               sizeof(*prom_fam->metric.ptr), prom_metric_cmp);
+    prometheus_metric_t *mmatch =
+        bsearch(&pm, pfam->metrics, pfam->metrics_num, sizeof(*pfam->metrics),
+                prom_metric_cmp);
     if (mmatch == NULL)
       continue;
 
-    int status = prom_metric_family_metric_delete(prom_fam, m);
+    int status = prom_metric_family_metric_delete(pfam, pm);
     if (status != 0) {
       ERROR("write_prometheus plugin: Deleting a metric in family \"%s\" "
             "failed with status %d",
@@ -947,15 +974,15 @@ static int prom_missing(metric_family_t const *fam,
       continue;
     }
 
-    if (prom_fam->metric.num == 0) {
-      int status = c_avl_remove(prom_metrics, prom_fam->name, NULL, NULL);
+    if (pfam->metrics_num == 0) {
+      int status = c_avl_remove(prom_metrics, pfam->name, NULL, NULL);
       if (status != 0) {
         ERROR("write_prometheus plugin: Deleting metric family \"%s\" failed "
               "with status %d",
-              prom_fam->name, status);
+              pfam->name, status);
         continue;
       }
-      metric_family_free(prom_fam);
+      prom_metric_family_free(pfam);
       break;
     }
   }
index fea4d3abd5b52a02085f9021cb78300c14632bd1..2edf244f7a098b9844bcb7b83fd020b2f4d66f3a 100644 (file)
  */
 
 #include "collectd.h"
-
-#include "daemon/metric.h"
 #include "testing.h"
-#include "utils/common/common.h"
 
-int format_label_name(strbuf_t *buf, char const *name);
+#include "write_prometheus.c" /* sic */
 
 DEF_TEST(format_label_name) {
   // Test cases are based on:
@@ -57,7 +54,6 @@ DEF_TEST(format_label_name) {
 
   return 0;
 }
-void format_metric_family_name(strbuf_t *buf, metric_family_t const *fam);
 
 DEF_TEST(format_metric_family_name) {
   // Test cases are based on:
@@ -123,13 +119,13 @@ DEF_TEST(format_metric_family_name) {
     printf("# Case %zu: %s\n", i, cases[i].name);
     strbuf_t got = STRBUF_CREATE;
 
-    metric_family_t fam = {
+    prometheus_metric_family_t pfam = {
         .name = cases[i].name,
         .type = cases[i].type,
         .unit = cases[i].unit,
     };
 
-    format_metric_family_name(&got, &fam);
+    format_metric_family_name(&got, &pfam);
     EXPECT_EQ_STR(cases[i].want, got.ptr);
 
     STRBUF_DESTROY(got);
@@ -138,17 +134,15 @@ DEF_TEST(format_metric_family_name) {
   return 0;
 }
 
-void format_metric_family(strbuf_t *buf, metric_family_t const *prom_fam);
-
 DEF_TEST(format_metric_family) {
   struct {
     char const *name;
-    metric_family_t fam;
+    prometheus_metric_family_t pfam;
     char const *want;
   } cases[] = {
       {
           .name = "metrics is empty",
-          .fam =
+          .pfam =
               {
                   .name = "unit.test",
               },
@@ -156,21 +150,18 @@ DEF_TEST(format_metric_family) {
       },
       {
           .name = "metric without labels",
-          .fam =
+          .pfam =
               {
                   .name = "unit.test",
                   .type = METRIC_TYPE_COUNTER,
-                  .metric =
-                      {
-                          .ptr =
-                              &(metric_t){
-                                  .value =
-                                      (value_t){
-                                          .counter = 42,
-                                      },
+                  .metrics =
+                      &(prometheus_metric_t){
+                          .value =
+                              (value_t){
+                                  .counter = 42,
                               },
-                          .num = 1,
                       },
+                  .metrics_num = 1,
               },
           .want = "# HELP unit_test_total\n"
                   "# TYPE unit_test_total counter\n"
@@ -179,30 +170,27 @@ DEF_TEST(format_metric_family) {
       },
       {
           .name = "metric with one label",
-          .fam =
+          .pfam =
               {
                   .name = "unittest",
                   .type = METRIC_TYPE_GAUGE,
-                  .metric =
-                      {
-                          .ptr =
-                              &(metric_t){
-                                  .label =
-                                      {
-                                          .ptr =
-                                              &(label_pair_t){
-                                                  .name = "foo",
-                                                  .value = "bar",
-                                              },
-                                          .num = 1,
-                                      },
-                                  .value =
-                                      (value_t){
-                                          .gauge = 42,
+                  .metrics =
+                      &(prometheus_metric_t){
+                          .label =
+                              {
+                                  .ptr =
+                                      &(label_pair_t){
+                                          .name = "foo",
+                                          .value = "bar",
                                       },
+                                  .num = 1,
+                              },
+                          .value =
+                              (value_t){
+                                  .gauge = 42,
                               },
-                          .num = 1,
                       },
+                  .metrics_num = 1,
               },
           .want = "# HELP unittest\n"
                   "# TYPE unittest gauge\n"
@@ -211,30 +199,27 @@ DEF_TEST(format_metric_family) {
       },
       {
           .name = "invalid characters are replaced",
-          .fam =
+          .pfam =
               {
                   .name = "unit.test",
                   .type = METRIC_TYPE_UNTYPED,
-                  .metric =
-                      {
-                          .ptr =
-                              &(metric_t){
-                                  .label =
-                                      {
-                                          .ptr =
-                                              &(label_pair_t){
-                                                  .name = "metric.name",
-                                                  .value = "unit.test",
-                                              },
-                                          .num = 1,
-                                      },
-                                  .value =
-                                      (value_t){
-                                          .gauge = 42,
+                  .metrics =
+                      &(prometheus_metric_t){
+                          .label =
+                              {
+                                  .ptr =
+                                      &(label_pair_t){
+                                          .name = "metric.name",
+                                          .value = "unit.test",
                                       },
+                                  .num = 1,
+                              },
+                          .value =
+                              (value_t){
+                                  .gauge = 42,
                               },
-                          .num = 1,
                       },
+                  .metrics_num = 1,
               },
           .want = "# HELP unit_test\n"
                   "# TYPE unit_test untyped\n"
@@ -243,40 +228,38 @@ DEF_TEST(format_metric_family) {
       },
       {
           .name = "most resource attributes are ignored",
-          .fam =
+          .pfam =
               {
                   .name = "unit.test",
                   .type = METRIC_TYPE_UNTYPED,
-                  .resource =
-                      {
-                          .ptr =
-                              (label_pair_t[]){
-                                  {"service.instance.id",
-                                   "service instance id"},
-                                  {"service.name", "service name"},
-                                  {"zzz.all.other.attributes", "are ignored"},
-                              },
-                          .num = 3,
-                      },
-                  .metric =
-                      {
-                          .ptr =
-                              &(metric_t){
-                                  .label =
-                                      {
-                                          .ptr =
-                                              (label_pair_t[]){
-                                                  {"metric.name", "unit.test"},
-                                              },
-                                          .num = 1,
+                  .metrics =
+                      &(prometheus_metric_t){
+                          .resource =
+                              {
+                                  .ptr =
+                                      (label_pair_t[]){
+                                          {"service.instance.id",
+                                           "service instance id"},
+                                          {"service.name", "service name"},
+                                          {"zzz.all.other.attributes",
+                                           "are ignored"},
                                       },
-                                  .value =
-                                      (value_t){
-                                          .gauge = 42,
+                                  .num = 3,
+                              },
+                          .label =
+                              {
+                                  .ptr =
+                                      (label_pair_t[]){
+                                          {"metric.name", "unit.test"},
                                       },
+                                  .num = 1,
+                              },
+                          .value =
+                              (value_t){
+                                  .gauge = 42,
                               },
-                          .num = 1,
                       },
+                  .metrics_num = 1,
               },
           .want = "# HELP unit_test\n"
                   "# TYPE unit_test untyped\n"
@@ -288,14 +271,9 @@ DEF_TEST(format_metric_family) {
 
   for (size_t i = 0; i < STATIC_ARRAY_SIZE(cases); i++) {
     printf("# Case %zu: %s\n", i, cases[i].name);
-    strbuf_t got = STRBUF_CREATE;
 
-    metric_family_t *fam = &cases[i].fam;
-    for (size_t j = 0; j < fam->metric.num; j++) {
-      fam->metric.ptr[j].family = fam;
-    }
-
-    format_metric_family(&got, &cases[i].fam);
+    strbuf_t got = STRBUF_CREATE;
+    format_metric_family(&got, &cases[i].pfam);
     EXPECT_EQ_STR(cases[i].want, got.ptr);
 
     STRBUF_DESTROY(got);
@@ -304,9 +282,6 @@ DEF_TEST(format_metric_family) {
   return 0;
 }
 
-void target_info(strbuf_t *buf, metric_family_t const **families,
-                 size_t families_num);
-
 DEF_TEST(target_info) {
   struct {
     char const *name;
@@ -422,19 +397,17 @@ DEF_TEST(target_info) {
   for (size_t i = 0; i < STATIC_ARRAY_SIZE(cases); i++) {
     printf("# Case %zu: %s\n", i, cases[i].name);
 
-    metric_family_t mfams[cases[i].resources_num];
-    metric_t ms[cases[i].resources_num];
+    prometheus_metric_t pms[cases[i].resources_num];
     for (size_t j = 0; j < cases[i].resources_num; j++) {
-      mfams[j] = (metric_family_t){.resource = cases[i].resources[j]};
-      ms[j] = (metric_t){.family = &mfams[j]};
+      pms[j] = (prometheus_metric_t){.resource = cases[i].resources[j]};
     }
-    metric_family_t fam = {
-        .metric.ptr = ms,
-        .metric.num = cases[i].resources_num,
+    prometheus_metric_family_t pfam = {
+        .metrics= pms,
+        .metrics_num = cases[i].resources_num,
     };
 
     strbuf_t got = STRBUF_CREATE;
-    target_info(&got, (metric_family_t const *[]){&fam}, 1);
+    target_info(&got, (prometheus_metric_family_t const *[]){&pfam}, 1);
     EXPECT_EQ_STR(cases[i].want, got.ptr);
 
     STRBUF_DESTROY(got);
@@ -443,11 +416,6 @@ DEF_TEST(target_info) {
   return 0;
 }
 
-void format_text(strbuf_t *buf);
-int prom_write(metric_family_t const *fam, user_data_t *ud);
-int alloc_metrics(void);
-void free_metrics(void);
-
 DEF_TEST(end_to_end) {
   hostname_set("example.com");