This is done using a lookup of the name returned by C<gethostname>. This option
is enabled by default.
+=item B<Resource> I<Type>
+
+The B<Resource> block defined the I<Resource Attributes> 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<https://opentelemetry.io/docs/specs/semconv/resource/>.
+
+The I<Type> argument determines which default attributes are automatically
+populated. B<Attribute> options inside the block can be used to add additional
+attributes or overwrite existing ones.
+
+Valid values for I<Type> are:
+
+=over 4
+
+=item B<Host> (default)
+
+For use with computing instances (e.g. physical and virtual machines). Will
+automatically populate the C<service.name>, C<host.name>, and C<host.id>
+attributes.
+
+For more information, refer to
+L<https://opentelemetry.io/docs/specs/semconv/resource/host/>.
+
+=item B<Generic>
+
+Minimal defaults for maximum flexibility. Only the C<service.name> attribute is
+populated.
+
+=back
+
+Inside the B<Resource> block, the following configuration options are valid:
+
+=over 4
+
+=item B<Attribute> I<Name> I<Value>
+
+Adds a resource attribute or updates an existing one. If I<Value> is the empty
+string, the attribute is deleted.
+
+=back
+
=item B<PreCacheChain> I<ChainName>
=item B<PostCacheChain> I<ChainName>
#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 <wordexp.h>
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
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[] = {
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;
return ENOENT;
}
-static void init_default_resource(void) {
+static void resource_host_init(void) {
if (default_resource.num != 0) {
return;
}
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;
}