From: Florian Forster Date: Sat, 16 Dec 2023 07:26:26 +0000 (+0100) Subject: New utility: resource_metrics. X-Git-Tag: 6.0.0-rc0~34^2~6 X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=286974d80cce1d4b759afa6b71bef0b1b7f31e4a;p=thirdparty%2Fcollectd.git New utility: resource_metrics. Resource metrics allows plugin to stage metric families, grouped by resource. This is designed to work well with plugins that export the OpenTelemetry protocol. --- diff --git a/src/utils/resource_metrics/resource_metrics.c b/src/utils/resource_metrics/resource_metrics.c new file mode 100644 index 000000000..e80803057 --- /dev/null +++ b/src/utils/resource_metrics/resource_metrics.c @@ -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 + **/ + +#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 index 000000000..26293bdbc --- /dev/null +++ b/src/utils/resource_metrics/resource_metrics.h @@ -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 + **/ + +#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