src/daemon/globals.h \
src/daemon/plugin.c \
src/daemon/plugin.h \
+ src/daemon/resource.c \
+ src/daemon/resource.h \
src/daemon/types_list.c \
src/daemon/types_list.h \
src/daemon/utils_cache.c \
#define VALID_LABEL_CHARS \
"abcdefghijklmnopqrstuvwxyz" \
"ABCDEFGHIJKLMNOPQRSTUVWXYZ" \
- "0123456789_"
+ "0123456789_.-"
+
/* Metric names must match the regex `[a-zA-Z_:][a-zA-Z0-9_:]*` */
-#define VALID_NAME_CHARS VALID_LABEL_CHARS ":"
+// instrument-name = ALPHA 0*254 ("_" / "." / "-" / "/" / ALPHA / DIGIT)
+#define VALID_NAME_CHARS VALID_LABEL_CHARS "/"
+
+#define RESOURCE_LABEL_PREFIX "resource:"
int value_marshal_text(strbuf_t *buf, value_t v, metric_type_t type) {
switch (type) {
((label_pair_t const *)b)->name);
}
-static label_pair_t *label_set_read(label_set_t labels, char const *name) {
+static label_pair_t *label_set_read(label_set_t const *labels,
+ char const *name) {
if (name == NULL) {
errno = EINVAL;
return NULL;
.name = name,
};
- label_pair_t *ret = bsearch(&label, labels.ptr, labels.num,
- sizeof(*labels.ptr), label_pair_compare);
+ label_pair_t *ret = bsearch(&label, labels->ptr, labels->num,
+ sizeof(*labels->ptr), label_pair_compare);
if (ret == NULL) {
errno = ENOENT;
return NULL;
return ret;
}
-static int label_set_create(label_set_t *labels, char const *name,
- char const *value) {
+int label_set_add(label_set_t *labels, char const *name, char const *value) {
if ((labels == NULL) || (name == NULL) || (value == NULL)) {
return EINVAL;
}
return EINVAL;
}
- if (label_set_read(*labels, name) != NULL) {
+ if (label_set_read(labels, name) != NULL) {
return EEXIST;
}
errno = 0;
return 0;
}
-static void label_set_reset(label_set_t *labels) {
+static int label_set_update(label_set_t *labels, char const *name,
+ char const *value) {
+ if ((labels == NULL) || (name == NULL)) {
+ return EINVAL;
+ }
+
+ label_pair_t *label = label_set_read(labels, name);
+ if ((label == NULL) && (errno != ENOENT)) {
+ return errno;
+ }
+ errno = 0;
+
+ if (label == NULL) {
+ if ((value == NULL) || strlen(value) == 0) {
+ return 0;
+ }
+ return label_set_add(labels, name, value);
+ }
+
+ if ((value == NULL) || strlen(value) == 0) {
+ return label_set_delete(labels, label);
+ }
+
+ char *new_value = strdup(value);
+ if (new_value == NULL) {
+ return errno;
+ }
+
+ free(label->value);
+ label->value = new_value;
+
+ return 0;
+}
+
+void label_set_reset(label_set_t *labels) {
if (labels == NULL) {
return;
}
labels->num = 0;
}
-static int label_set_clone(label_set_t *dest, label_set_t src) {
+int label_set_clone(label_set_t *dest, label_set_t src) {
if (src.num == 0) {
return 0;
}
}
label_set_reset(&m->label);
+ label_set_reset(&m->resource);
meta_data_destroy(m->meta);
memset(m, 0, sizeof(*m));
return 0;
}
+static int format_label_set(strbuf_t *buf, label_set_t const *labels,
+ char const *prefix, bool first_label) {
+ int status = 0;
+ for (size_t i = 0; i < labels->num; i++) {
+ if (!first_label) {
+ status = status || strbuf_print(buf, ",");
+ }
+
+ status = status || strbuf_print(buf, prefix);
+ status = status || strbuf_print(buf, labels->ptr[i].name);
+ status = status || strbuf_print(buf, "=\"");
+ status = status || strbuf_print_escaped(buf, labels->ptr[i].value,
+ "\\\"\n\r\t", '\\');
+ status = status || strbuf_print(buf, "\"");
+ first_label = false;
+ }
+ return status;
+}
+
int metric_identity(strbuf_t *buf, metric_t const *m) {
if ((buf == NULL) || (m == NULL) || (m->family == NULL)) {
return EINVAL;
}
int status = strbuf_print(buf, m->family->name);
- if (m->label.num == 0) {
+ if (m->resource.num == 0 && m->label.num == 0) {
return status;
}
status = status || strbuf_print(buf, "{");
- for (size_t i = 0; i < m->label.num; i++) {
- if (i != 0) {
- status = status || strbuf_print(buf, ",");
- }
- status = status || strbuf_print(buf, m->label.ptr[i].name);
- status = status || strbuf_print(buf, "=\"");
- status = status || strbuf_print_escaped(buf, m->label.ptr[i].value,
- "\\\"\n\r\t", '\\');
- status = status || strbuf_print(buf, "\"");
- }
+ bool first_label = true;
+ status = status || format_label_set(buf, &m->resource, RESOURCE_LABEL_PREFIX,
+ first_label);
+
+ first_label = (m->resource.num == 0);
+ status = status || format_label_set(buf, &m->label, "", first_label);
return status || strbuf_print(buf, "}");
}
int metric_label_set(metric_t *m, char const *name, char const *value) {
- if ((m == NULL) || (name == NULL)) {
+ if (m == NULL) {
return EINVAL;
}
- label_pair_t *label = label_set_read(m->label, name);
- if ((label == NULL) && (errno != ENOENT)) {
- return errno;
- }
- errno = 0;
-
- if (label == NULL) {
- if ((value == NULL) || strlen(value) == 0) {
- return 0;
- }
- return label_set_create(&m->label, name, value);
- }
-
- if ((value == NULL) || strlen(value) == 0) {
- return label_set_delete(&m->label, label);
- }
+ return label_set_update(&m->label, name, value);
+}
- char *new_value = strdup(value);
- if (new_value == NULL) {
- return errno;
+int metric_resource_attribute_update(metric_t *m, char const *name,
+ char const *value) {
+ if (m == NULL) {
+ return EINVAL;
}
- free(label->value);
- label->value = new_value;
-
- return 0;
+ return label_set_update(&m->resource, name, value);
}
char const *metric_label_get(metric_t const *m, char const *name) {
return NULL;
}
- label_pair_t *set = label_set_read(m->label, name);
+ label_pair_t *set = label_set_read(&m->label, name);
if (set == NULL) {
return NULL;
}
.interval = src.ptr[i].interval,
};
- int status = label_set_clone(&ret.ptr[i].label, src.ptr[i].label);
+ int status = label_set_clone(&ret.ptr[i].resource, src.ptr[i].resource);
+ if (status != 0) {
+ metric_list_reset(&ret);
+ return status;
+ }
+
+ status = label_set_clone(&ret.ptr[i].label, src.ptr[i].label);
if (status != 0) {
metric_list_reset(&ret);
return status;
while ((ptr[0] == '{') || (ptr[0] == ',')) {
ptr++;
+ bool is_resource_label =
+ strncmp(ptr, RESOURCE_LABEL_PREFIX, strlen(RESOURCE_LABEL_PREFIX)) == 0;
+ if (is_resource_label) {
+ ptr += strlen(RESOURCE_LABEL_PREFIX);
+ }
+
size_t key_len = strspn(ptr, VALID_LABEL_CHARS);
if (key_len == 0) {
ret = EINVAL;
/* one metric is added to the family by metric_family_unmarshal_text. */
assert(fam->metric.num >= 1);
- status = metric_label_set(m, key, value.ptr);
+ if (is_resource_label) {
+ status = label_set_add(&m->resource, key, value.ptr);
+ } else {
+ status = metric_label_set(m, key, value.ptr);
+ }
STRBUF_DESTROY(value);
if (status != 0) {
ret = status;
size_t num;
} label_set_t;
+/* label_set_clone copies all the laels in src into dest. If dest contains
+ * any labels prior to calling label_set_clone, the associated memory is
+ * leaked. */
+int label_set_clone(label_set_t *dest, label_set_t src);
+
+/* label_set_add adds a label to the label set. If a label with name already
+ * exists, EEXIST is returned. The set of labels is sorted by label name. */
+int label_set_add(label_set_t *labels, char const *name, char const *value);
+
+/* label_set_reset frees all the memory referenced by the label set and
+ * initializes the label set to zero. */
+void label_set_reset(label_set_t *labels);
+
/*
* Metric
*/
metric_family_t *family; /* backreference for family->name and family->type */
label_set_t label;
+ label_set_t resource;
+
value_t value;
cdtime_t time; /* TODO(octo): use ms or µs instead? */
cdtime_t interval;
- /* TODO(octo): target labels */
meta_data_t *meta;
} metric_t;
* label that does not exist is *not* an error. */
int metric_label_set(metric_t *m, char const *name, char const *value);
+/* metric_resource_attribute_update adds, updates, or deleted a resource
+ * attribute. If "value" is NULL or the empty string, the attribute is removed.
+ * Removing an attribute that does not exist is *not* an error. */
+int metric_resource_attribute_update(metric_t *m, char const *name,
+ char const *value);
+
/* metric_label_get efficiently looks up and returns the value of the "name"
* label. If a label does not exist, NULL is returned and errno is set to
* ENOENT. The returned pointer may not be valid after a subsequent call to
char *name;
label_pair_t *labels;
size_t labels_num;
+ label_pair_t *rattr;
+ size_t rattr_num;
char const *want;
} cases[] = {
{
.want = "escape_sequences{cardridge_return=\"\\r\",newline=\"\\n\","
"quote=\"\\\"\",tab=\"\\t\"}",
},
+ {
+ .name = "metric_with_resource",
+ .rattr =
+ (label_pair_t[]){
+ {"host.name", "example.com"},
+ },
+ .rattr_num = 1,
+ .want = "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 =
+ "metric_with_resource_and_labels{resource:alpha=\"resources\","
+ "resource:omega=\"always\",beta=\"come\",gamma=\"first\"}",
+ },
};
for (size_t i = 0; i < (sizeof(cases) / sizeof(cases[0])); i++) {
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_resource_attribute_update(&m, cases[i].rattr[j].name,
+ cases[i].rattr[j].value));
+ }
strbuf_t buf = STRBUF_CREATE;
CHECK_ZERO(metric_identity(&buf, &m));
#include "configfile.h"
#include "filter_chain.h"
#include "plugin.h"
+#include "resource.h"
#include "utils/avltree/avltree.h"
#include "utils/common/common.h"
#include "utils/heap/heap.h"
return 0;
} /* int plugin_dispatch_values_internal */
+static void set_default_resource_attributes(metric_t *m) {
+ if (m->resource.num > 0) {
+ return;
+ }
+
+ label_set_clone(&m->resource, default_resource_attributes());
+}
+
EXPORT int plugin_dispatch_metric_family(metric_family_t const *fam) {
if ((fam == NULL) || (fam->metric.num == 0)) {
return EINVAL;
cdtime_t interval = plugin_get_interval();
for (size_t i = 0; i < fam_copy->metric.num; i++) {
- if (fam_copy->metric.ptr[i].time == 0) {
- fam_copy->metric.ptr[i].time = time;
+ metric_t *m = fam_copy->metric.ptr + i;
+ if (m->time == 0) {
+ m->time = time;
}
- if (fam_copy->metric.ptr[i].interval == 0) {
- fam_copy->metric.ptr[i].interval = interval;
+ if (m->interval == 0) {
+ m->interval = interval;
}
- /* TODO(octo): set target labels here. */
+ set_default_resource_attributes(m);
}
int status = plugin_dispatch_metric_internal(fam_copy);
--- /dev/null
+/**
+ * collectd - src/daemon/resource.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 "collectd.h"
+#include "daemon/resource.h"
+
+#include "utils/common/common.h"
+
+static label_set_t default_resource;
+
+static void otel_service_name(void) {
+ char *sn = getenv("OTEL_SERVICE_NAME");
+ if (sn != NULL) {
+ label_set_add(&default_resource, "service.name", sn);
+ return;
+ }
+
+ strbuf_t buf = STRBUF_CREATE;
+ strbuf_print(&buf, "unknown_service:" PACKAGE_NAME);
+ label_set_add(&default_resource, "service.name", buf.ptr);
+ STRBUF_DESTROY(buf);
+}
+
+static void otel_resource_attributes(void) {
+ char *ra = getenv("OTEL_RESOURCE_ATTRIBUTES");
+ if (ra == NULL) {
+ return;
+ }
+
+ size_t tmp_sz = strlen(ra) + 1;
+ char tmp[tmp_sz];
+ sstrncpy(tmp, ra, sizeof(tmp));
+
+ char *str = &tmp[0];
+ char *saveptr = NULL;
+ char *key;
+ while ((key = strtok_r(str, ",", &saveptr)) != NULL) {
+ str = NULL;
+ char *value = strchr(key, '=');
+ if (value == NULL) {
+ continue;
+ }
+ *value = 0;
+ value++;
+
+ label_set_add(&default_resource, key, value);
+ }
+}
+
+static void host_name(void) {
+ if (strlen(hostname_g) > 0) {
+ label_set_add(&default_resource, "host.name", hostname_g);
+ }
+}
+
+static int machine_id(void) {
+ char *files[] = {
+ "/etc/machine-id",
+ "/etc/hostid",
+ "/var/lib/dbus/machine-id",
+ };
+
+ for (size_t i = 0; i < STATIC_ARRAY_SIZE(files); i++) {
+ char *f = files[i];
+ if (access(f, R_OK) != 0) {
+ continue;
+ }
+
+ char buf[1024] = {0};
+ ssize_t status = read_text_file_contents(f, buf, sizeof(buf));
+ if (status <= 0) {
+ NOTICE("machine_id: reading \"%s\" failed: %zd", f, status);
+ continue;
+ }
+ strstripnewline(buf);
+
+ label_set_add(&default_resource, "host.id", buf);
+ return 0;
+ }
+
+ return ENOENT;
+}
+
+static void init_default_resource(void) {
+ if (default_resource.num != 0) {
+ return;
+ }
+
+ otel_service_name();
+ otel_resource_attributes();
+ host_name();
+ machine_id();
+}
+
+label_set_t default_resource_attributes(void) {
+ init_default_resource();
+
+ return default_resource;
+}
--- /dev/null
+/**
+ * collectd - src/daemon/resource.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>
+ **/
+
+#ifndef DAEMON_RESOURCE_H
+#define DAEMON_RESOURCE_H 1
+
+#include "collectd.h"
+#include "daemon/metric.h"
+
+label_set_t default_resource_attributes(void);
+
+#endif