From: Florian Forster Date: Tue, 19 Dec 2023 13:04:44 +0000 (+0100) Subject: resource: Add an option to configure resource attributes. X-Git-Tag: 6.0.0-rc0~25^2~4 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=c6f055486ce56301e23a73de75f1f67fb5537298;p=thirdparty%2Fcollectd.git resource: Add an option to configure resource attributes. --- diff --git a/src/collectd.conf.in b/src/collectd.conf.in index 15bb7cffd..8b7fa7987 100644 --- a/src/collectd.conf.in +++ b/src/collectd.conf.in @@ -17,6 +17,10 @@ #PluginDir "@libdir@/@PACKAGE_NAME@" #TypesDB "@prefix@/share/@PACKAGE_NAME@/types.db" +# +# Attribute "custom.key" "custom.value" +# + #----------------------------------------------------------------------------# # When enabled, plugins are loaded automatically with the default options # # when an appropriate block is encountered. # diff --git a/src/collectd.conf.pod b/src/collectd.conf.pod index e9f6a5ac4..737acb282 100644 --- a/src/collectd.conf.pod +++ b/src/collectd.conf.pod @@ -328,6 +328,51 @@ the daemon should try to figure out the "fully qualified domain name", FQDN. This is done using a lookup of the name returned by C. This option is enabled by default. +=item B I + +The B block defined the I to use for local +metrics such as CPU usage, i.e. metrics that were not received from a remote +system or service. + +The recommended way to configure resource attributes is by following the +OpenTelemetry Semantic Conventions, +L. + +The I argument determines which default attributes are automatically +populated. B options inside the block can be used to add additional +attributes or overwrite existing ones. + +Valid values for I are: + +=over 4 + +=item B (default) + +For use with computing instances (e.g. physical and virtual machines). Will +automatically populate the C, C, and C +attributes. + +For more information, refer to +L. + +=item B + +Minimal defaults for maximum flexibility. Only the C attribute is +populated. + +=back + +Inside the B block, the following configuration options are valid: + +=over 4 + +=item B I I + +Adds a resource attribute or updates an existing one. If I is the empty +string, the attribute is deleted. + +=back + =item B I =item B I diff --git a/src/daemon/configfile.c b/src/daemon/configfile.c index b36c69eff..b9bf8d637 100644 --- a/src/daemon/configfile.c +++ b/src/daemon/configfile.c @@ -29,11 +29,13 @@ #include "liboconfig/oconfig.h" -#include "configfile.h" -#include "filter_chain.h" -#include "plugin.h" -#include "types_list.h" +#include "daemon/configfile.h" +#include "daemon/filter_chain.h" +#include "daemon/plugin.h" +#include "daemon/resource.h" +#include "daemon/types_list.h" #include "utils/common/common.h" +#include "utils/strbuf/strbuf.h" #if HAVE_WORDEXP_H #include @@ -87,6 +89,7 @@ static int dispatch_value_typesdb(oconfig_item_t *ci); static int dispatch_value_plugindir(oconfig_item_t *ci); static int dispatch_loadplugin(oconfig_item_t *ci); static int dispatch_block_plugin(oconfig_item_t *ci); +static int dispatch_resource(oconfig_item_t *ci); /* * Private variables @@ -94,10 +97,13 @@ static int dispatch_block_plugin(oconfig_item_t *ci); static cf_callback_t *first_callback; static cf_complex_callback_t *complex_callback_head; -static cf_value_map_t cf_value_map[] = {{"TypesDB", dispatch_value_typesdb}, - {"PluginDir", dispatch_value_plugindir}, - {"LoadPlugin", dispatch_loadplugin}, - {"Plugin", dispatch_block_plugin}}; +static cf_value_map_t cf_value_map[] = { + {"TypesDB", dispatch_value_typesdb}, + {"PluginDir", dispatch_value_plugindir}, + {"LoadPlugin", dispatch_loadplugin}, + {"Plugin", dispatch_block_plugin}, + {"Resource", dispatch_resource}, +}; static int cf_value_map_num = STATIC_ARRAY_SIZE(cf_value_map); static cf_global_option_t cf_global_options[] = { @@ -316,6 +322,74 @@ static int dispatch_loadplugin(oconfig_item_t *ci) { return ret_val; } /* int dispatch_value_loadplugin */ +static int format_config_value(strbuf_t *buf, oconfig_value_t v) { + switch (v.type) { + case OCONFIG_TYPE_STRING: + return strbuf_print(buf, v.value.string); + case OCONFIG_TYPE_NUMBER: + return strbuf_printf(buf, "%g", v.value.number); + case OCONFIG_TYPE_BOOLEAN: + return strbuf_print(buf, v.value.boolean ? "true" : "false"); + default: + ERROR("configfile: Unexpected value type: %d", v.type); + return -1; + } +} + +static int dispatch_resource(oconfig_item_t *ci) { + char type[128] = {0}; + int status = cf_util_get_string_buffer(ci, type, sizeof(type)); + if (status != 0) { + return status; + } + + status = resource_attributes_init(type); + if (status != 0) { + return status; + } + + for (int i = 0; i < ci->children_num; ++i) { + oconfig_item_t *child = ci->children + i; + + if (strcasecmp("Attribute", child->key) == 0) { + if (child->values_num != 2) { + ERROR("configfile: The \"%s\" option requires two values, got %d.", + child->key, child->values_num); + return EINVAL; + } + if (child->values[0].type != OCONFIG_TYPE_STRING) { + ERROR("configfile: The first value of the \"%s\" option (the attribute " + "name) must be a string.", + child->key); + return EINVAL; + } + char const *key = child->values[0].value.string; + + strbuf_t value = STRBUF_CREATE; + int status = format_config_value(&value, ci->values[1]); + if (status != 0) { + STRBUF_DESTROY(value); + return status; + } + + status = resource_attribute_update(key, value.ptr); + if (status != 0) { + ERROR("configfile: default_resource_attributes_update failed: %s", + STRERROR(status)); + STRBUF_DESTROY(value); + return status; + } + + STRBUF_DESTROY(value); + } else { + ERROR("configfile: The option \"%s\" is not valid within \"%s\" blocks.", + child->key, ci->key); + return EINVAL; + } + } + return 0; +} /* int dispatch_resource */ + static int dispatch_value(oconfig_item_t *ci) { int ret = 0; diff --git a/src/daemon/resource.c b/src/daemon/resource.c index 6a132bd3e..eebb98b3f 100644 --- a/src/daemon/resource.c +++ b/src/daemon/resource.c @@ -104,7 +104,7 @@ static int machine_id(void) { return ENOENT; } -static void init_default_resource(void) { +static void resource_host_init(void) { if (default_resource.num != 0) { return; } @@ -115,8 +115,34 @@ static void init_default_resource(void) { machine_id(); } +static void resource_generic_init(void) { + if (default_resource.num != 0) { + return; + } + + otel_service_name(); + otel_resource_attributes(); +} + +int resource_attributes_init(char const *type) { + if (strcasecmp("Host", type) == 0) { + resource_host_init(); + return 0; + } else if (strcasecmp("Generic", type) == 0) { + resource_generic_init(); + return 0; + } + ERROR("resource: The resource type \"%s\" is unknown.", type); + return ENOENT; +} + +int resource_attribute_update(char const *key, char const *value) { + resource_host_init(); + return label_set_add(&default_resource, key, value); +} + label_set_t default_resource_attributes(void) { - init_default_resource(); + resource_host_init(); return default_resource; } diff --git a/src/daemon/resource.h b/src/daemon/resource.h index fb94153ef..179a74d0a 100644 --- a/src/daemon/resource.h +++ b/src/daemon/resource.h @@ -30,6 +30,14 @@ #include "collectd.h" #include "daemon/metric.h" +/* resource_attributes_init sets default resource attributes depending on + * "type". Returns ENOENT if type is not known. */ +int resource_attributes_init(char const *type); + +/* resource_attribute_update adds a gobal resource attribute. If an + * attribute of the same name already exists, it is overwritten. */ +int resource_attribute_update(char const *key, char const *value); + label_set_t default_resource_attributes(void); #endif