]> git.ipfire.org Git - thirdparty/collectd.git/commitdiff
New utility: resource_metrics.
authorFlorian Forster <octo@collectd.org>
Sat, 16 Dec 2023 07:26:26 +0000 (08:26 +0100)
committerFlorian Forster <octo@collectd.org>
Mon, 18 Dec 2023 22:28:18 +0000 (23:28 +0100)
Resource metrics allows plugin to stage metric families, grouped by
resource. This is designed to work well with plugins that export the
OpenTelemetry protocol.

src/utils/resource_metrics/resource_metrics.c [new file with mode: 0644]
src/utils/resource_metrics/resource_metrics.h [new file with mode: 0644]

diff --git a/src/utils/resource_metrics/resource_metrics.c b/src/utils/resource_metrics/resource_metrics.c
new file mode 100644 (file)
index 0000000..e808030
--- /dev/null
@@ -0,0 +1,202 @@
+/**
+ * collectd - src/utils/resource_metrics/resource_metrics.c
+ * Copyright (C) 2023       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"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ *
+ * Authors:
+ *   Florian octo Forster <octo at collectd.org>
+ **/
+
+#include "utils/resource_metrics/resource_metrics.h"
+#include "daemon/plugin.h"
+#include "utils/common/common.h"
+
+static int resource_metrics_compare(resource_metrics_t const *a,
+                                    resource_metrics_t const *b) {
+  return label_set_compare(a->resource, b->resource);
+}
+
+static resource_metrics_t *lookup_resource(resource_metrics_set_t *set,
+                                           label_set_t resource) {
+  resource_metrics_t key = {
+      .resource = resource,
+  };
+
+  return bsearch(&key, set->ptr, set->num, sizeof(*set->ptr),
+                 (void *)resource_metrics_compare);
+}
+
+static int insert_resource(resource_metrics_set_t *set, label_set_t resource) {
+  resource_metrics_t *rm =
+      realloc(set->ptr, (set->num + 1) * sizeof(*set->ptr));
+  if (rm == NULL) {
+    return ENOMEM;
+  }
+  set->ptr = rm;
+
+  rm = set->ptr + set->num;
+  memset(rm, 0, sizeof(*rm));
+
+  int status = label_set_clone(&rm->resource, resource);
+  if (status != 0) {
+    return ENOMEM;
+  }
+  set->num++;
+
+  qsort(set->ptr, set->num, sizeof(*set->ptr),
+        (void *)resource_metrics_compare);
+  return 0;
+}
+
+static resource_metrics_t *
+lookup_or_insert_resource(resource_metrics_set_t *set, label_set_t resource) {
+  resource_metrics_t *ret = lookup_resource(set, resource);
+  if (ret != NULL) {
+    return ret;
+  }
+
+  int status = insert_resource(set, resource);
+  if (status != 0) {
+    ERROR("resource_metrics: insert_resource failed: %s", STRERROR(status));
+    return NULL;
+  }
+
+  return lookup_resource(set, resource);
+}
+
+static int compare_family_by_name(metric_family_t **a, metric_family_t **b) {
+  return strcmp((*a)->name, (*b)->name);
+}
+
+static metric_family_t *lookup_family(resource_metrics_t *rm,
+                                      metric_family_t const *fam) {
+  return bsearch(&fam, rm->families, rm->families_num, sizeof(*rm->families),
+                 (void *)compare_family_by_name);
+}
+
+static int insert_family(resource_metrics_t *rm, metric_family_t const *fam) {
+  metric_family_t **tmp =
+      realloc(rm->families, (rm->families_num + 1) * sizeof(*rm->families));
+  if (tmp == NULL) {
+    return ENOMEM;
+  }
+  rm->families = tmp;
+
+  /* NOTE: metric_family_clone also copies the resource attributes. This is not
+   * strictly required, since we have these attributes in rm. We keep the copies
+   * for now for the sake of simplicity. If memory consumption is a problem,
+   * this could be de-duplicated, at the cost of more complicated memory
+   * management. */
+  rm->families[rm->families_num] = metric_family_clone(fam);
+  if (rm->families[rm->families_num] == NULL) {
+    return errno;
+  }
+  rm->families_num++;
+
+  return 0;
+}
+
+static metric_family_t *lookup_or_insert_family(resource_metrics_t *rm,
+                                                metric_family_t const *fam) {
+  metric_family_t *ret = lookup_family(rm, fam);
+  if (ret != NULL) {
+    return ret;
+  }
+
+  int status = insert_family(rm, fam);
+  if (status != 0) {
+    ERROR("resource_metrics: insert_family failed: %s", STRERROR(status));
+    return NULL;
+  }
+
+  return lookup_family(rm, fam);
+}
+
+static int compare_metrics(metric_t const *a, metric_t const *b) {
+  return label_set_compare(a->label, b->label);
+}
+
+static bool metric_exists(metric_family_t const *fam, metric_t const *m) {
+  metric_family_t *found =
+      bsearch(m, fam->metric.ptr, fam->metric.num, sizeof(*fam->metric.ptr),
+              (void *)compare_metrics);
+  return found != NULL;
+}
+
+static int insert_metrics(metric_family_t *fam, metric_list_t metrics) {
+  for (size_t i = 0; i < metrics.num; i++) {
+    if (metric_exists(fam, metrics.ptr + i)) {
+      return EEXIST;
+    }
+  }
+
+  int last_err = 0;
+  for (size_t i = 0; i < metrics.num; i++) {
+    int status = metric_family_metric_append(fam, metrics.ptr[i]);
+    if (status != 0) {
+      ERROR("resource_metrics: metric_family_metric_append failed: %s", STRERROR(status));
+      /* DO NOT RETURN: the metric list may be unsorted */
+      last_err = status;
+    }
+  }
+
+  qsort(fam->metric.ptr, fam->metric.num, sizeof(*fam->metric.ptr),
+        (void *)compare_metrics);
+  return last_err;
+}
+
+int resource_metrics_add(resource_metrics_set_t *set,
+                         metric_family_t const *fam) {
+  if (set == NULL || fam == NULL) {
+    return EINVAL;
+  }
+
+  resource_metrics_t *rm = lookup_or_insert_resource(set, fam->resource);
+  if (rm == NULL) {
+    return -1;
+  }
+
+  metric_family_t *staged_fam = lookup_or_insert_family(rm, fam);
+  if (staged_fam == NULL) {
+    return -1;
+  }
+
+  return insert_metrics(staged_fam, fam->metric);
+}
+
+static void resource_reset(resource_metrics_t *rm) {
+  label_set_reset(&rm->resource);
+
+  for (size_t i = 0; i < rm->families_num; i++) {
+    metric_family_free(rm->families[i]);
+    rm->families[i] = NULL;
+  }
+  free(rm->families);
+  rm->families = NULL;
+  rm->families_num = 0;
+}
+
+void resource_metrics_reset(resource_metrics_set_t *set) {
+  for (size_t i = 0; i < set->num; i++) {
+    resource_reset(&set->ptr[i]);
+  }
+  set->ptr = NULL;
+  set->num = 0;
+}
diff --git a/src/utils/resource_metrics/resource_metrics.h b/src/utils/resource_metrics/resource_metrics.h
new file mode 100644 (file)
index 0000000..26293bd
--- /dev/null
@@ -0,0 +1,54 @@
+/**
+ * collectd - src/utils/resource_metrics/resource_metrics.h
+ * Copyright (C) 2023       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"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ *
+ * Authors:
+ *   Florian octo Forster <octo at collectd.org>
+ **/
+
+#ifndef UTILS_RESOURCE_METRICS_H
+#define UTILS_RESOURCE_METRICS_H 1
+
+#include "collectd.h"
+#include "daemon/metric.h"
+
+typedef struct {
+  label_set_t resource;
+
+  metric_family_t **families;
+  size_t families_num;
+} resource_metrics_t;
+
+typedef struct {
+  resource_metrics_t *ptr;
+  size_t num;
+} resource_metrics_set_t;
+
+/* resource_metrics_add adds a metric family to the resource metrics set.
+ * If any metric within the metric family is already part of the resource
+ * metrics set, the function will return EEXIST and rm remains unmodified. */
+int resource_metrics_add(resource_metrics_set_t *rm, metric_family_t const *fam);
+
+/* resource_metrics_reset frees all the memory held inside the set. set itself
+ * is not freed and can be reused afterwards. */
+void resource_metrics_reset(resource_metrics_set_t *set);
+
+#endif