From b709d390dd73bf903add409f88704b7501c0ed6a Mon Sep 17 00:00:00 2001 From: Anna Date: Tue, 23 Dec 2025 16:46:49 +0100 Subject: [PATCH 1/6] minor env adjustments --- .gitignore | 3 +++ bin/format | 1 + 2 files changed, 4 insertions(+) diff --git a/.gitignore b/.gitignore index 31457e72..60c94744 100644 --- a/.gitignore +++ b/.gitignore @@ -9,3 +9,6 @@ dist/ out/ MODULE.bazel.lock .vscode +.cache/ +.cursor/ +.DS_Store \ No newline at end of file diff --git a/bin/format b/bin/format index 6a5d66ac..d99ed544 100755 --- a/bin/format +++ b/bin/format @@ -64,6 +64,7 @@ docker_clang_format() { process_arg "$arg" done | xargs -0 \ docker run \ + --platform linux/amd64 \ --interactive \ --rm \ --volume /etc/passwd:/etc/passwd:ro \ From a5180891a5708a3a8f59d4fc014ace13d8b78d2e Mon Sep 17 00:00:00 2001 From: Anna Date: Tue, 23 Dec 2025 16:48:36 +0100 Subject: [PATCH 2/6] struct changes to accomodate for reporting all sources --- include/datadog/telemetry/product.h | 3 ++- src/datadog/telemetry/telemetry_impl.cpp | 9 +++++---- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/include/datadog/telemetry/product.h b/include/datadog/telemetry/product.h index e7dd7102..61437063 100644 --- a/include/datadog/telemetry/product.h +++ b/include/datadog/telemetry/product.h @@ -5,6 +5,7 @@ #include #include +#include namespace datadog::telemetry { @@ -30,7 +31,7 @@ struct Product final { /// Optional error message related to the product status. tracing::Optional error_message; /// Map of configuration settings for the product. - std::unordered_map + std::unordered_map> configurations; }; diff --git a/src/datadog/telemetry/telemetry_impl.cpp b/src/datadog/telemetry/telemetry_impl.cpp index 2089bebd..d9464dd7 100644 --- a/src/datadog/telemetry/telemetry_impl.cpp +++ b/src/datadog/telemetry/telemetry_impl.cpp @@ -587,11 +587,12 @@ std::string Telemetry::app_started_payload() { for (const auto& product : config_.products) { auto& configurations = product.configurations; - for (const auto& [_, config_metadata] : configurations) { + for (const auto& [_, config_metadatas] : configurations) { // if (config_metadata.value.empty()) continue; - - configuration_json.emplace_back( - generate_configuration_field(config_metadata)); + for (const auto& config_metadata : config_metadatas) { + configuration_json.emplace_back( + generate_configuration_field(config_metadata)); + } } /// NOTE(@dmehala): Telemetry API is tightly related to APM tracing and From dfed24215bb53d0913f9d76b6540bc975bc099aa Mon Sep 17 00:00:00 2001 From: Anna Date: Tue, 23 Dec 2025 16:49:26 +0100 Subject: [PATCH 3/6] compute all sources in vectors and pick overload for it --- include/datadog/config.h | 68 +++++++++++- src/datadog/trace_sampler_config.cpp | 10 +- src/datadog/tracer_config.cpp | 156 +++++++++++++-------------- 3 files changed, 147 insertions(+), 87 deletions(-) diff --git a/include/datadog/config.h b/include/datadog/config.h index bfff6059..6398b9b6 100644 --- a/include/datadog/config.h +++ b/include/datadog/config.h @@ -1,5 +1,11 @@ #pragma once +#include +#include +#include +#include +#include + #include "error.h" #include "optional.h" @@ -56,14 +62,72 @@ struct ConfigMetadata { : name(n), value(std::move(v)), origin(orig), error(std::move(err)) {} }; +// Return the chosen configuration value, in order of precedence: `from_env`, +// `from_user`, and `fallback`. +// This overload directly populates both telemetry_configs[config_name] and +// metadata[config_name] using the stringified value, with all values found, +// from lowest to highest precedence. Returns the chosen value directly. +// +// The fallback parameter is optional and defaults to nullptr. +template +Value pick(const Optional& from_env, const Optional& from_user, + std::unordered_map>* + telemetry_configs, + std::unordered_map* metadata, + ConfigName config_name, DefaultValue fallback = nullptr, + Stringifier to_string_fn = nullptr) { + auto stringify = [&](const Value& v) -> std::string { + if constexpr (!std::is_same_v) { + return to_string_fn(v); // use provided function + } else if constexpr (std::is_constructible_v) { + return std::string(v); // default behaviour (works for string-like types) + } else { + static_assert(!std::is_same_v, + "Non-string types require a stringifier function"); + return ""; // unreachable + } + }; + + std::vector telemetry_entries; + Optional chosen_value; + + auto add_entry = [&](ConfigMetadata::Origin origin, const Value& val) { + std::string val_str = stringify(val); + telemetry_entries.emplace_back( + ConfigMetadata{config_name, val_str, origin}); + chosen_value = val; + }; + + // Add DEFAULT entry if fallback was provided (detected by type) + if constexpr (!std::is_same_v) { + add_entry(ConfigMetadata::Origin::DEFAULT, fallback); + } + + if (from_user) { + add_entry(ConfigMetadata::Origin::CODE, *from_user); + } + + if (from_env) { + add_entry(ConfigMetadata::Origin::ENVIRONMENT_VARIABLE, *from_env); + } + + (*telemetry_configs)[config_name] = std::move(telemetry_entries); + if (!(*telemetry_configs)[config_name].empty()) { + (*metadata)[config_name] = (*telemetry_configs)[config_name].back(); + } + + return chosen_value.value_or(Value{}); +} + // Return a pair containing the configuration origin and value of a // configuration value chosen from one of the specified `from_env`, // `from_config`, and `fallback`. This function defines the relative precedence // among configuration values originating from the environment, programmatic // configuration, and default configuration. template -std::pair pick(const Optional &from_env, - const Optional &from_user, +std::pair pick(const Optional& from_env, + const Optional& from_user, DefaultValue fallback) { if (from_env) { return {ConfigMetadata::Origin::ENVIRONMENT_VARIABLE, *from_env}; diff --git a/src/datadog/trace_sampler_config.cpp b/src/datadog/trace_sampler_config.cpp index b29d799f..97a68d00 100644 --- a/src/datadog/trace_sampler_config.cpp +++ b/src/datadog/trace_sampler_config.cpp @@ -225,10 +225,12 @@ Expected finalize_config( result.rules.emplace_back(std::move(finalized_rule)); } - const auto [origin, max_per_second] = - pick(env_config->max_per_second, config.max_per_second, 100); - result.metadata[ConfigName::TRACE_SAMPLING_LIMIT] = ConfigMetadata( - ConfigName::TRACE_SAMPLING_LIMIT, std::to_string(max_per_second), origin); + std::unordered_map> + telemetry_configs_tmp; + double max_per_second = pick( + env_config->max_per_second, config.max_per_second, &telemetry_configs_tmp, + &result.metadata, ConfigName::TRACE_SAMPLING_LIMIT, 100.0, + [](const double &d) { return std::to_string(d); }); const auto allowed_types = {FP_NORMAL, FP_SUBNORMAL}; if (!(max_per_second > 0) || diff --git a/src/datadog/tracer_config.cpp b/src/datadog/tracer_config.cpp index 970367c9..932c41e4 100644 --- a/src/datadog/tracer_config.cpp +++ b/src/datadog/tracer_config.cpp @@ -8,13 +8,13 @@ #include #include +#include "datadog/optional.h" #include "datadog_agent.h" #include "json.hpp" #include "null_logger.h" #include "parse_util.h" #include "platform_util.h" #include "string_util.h" -#include "tags.h" #include "threaded_event_scheduler.h" namespace datadog { @@ -284,81 +284,81 @@ Expected finalize_config(const TracerConfig &user_config, final_config.clock = clock; final_config.logger = logger; - ConfigMetadata::Origin origin; + std::unordered_map> + all_sources_configs; - std::tie(origin, final_config.defaults.service) = - pick(env_config->service, user_config.service, ""); - - if (final_config.defaults.service.empty()) { - final_config.defaults.service = get_process_name(); - } - - final_config.metadata[ConfigName::SERVICE_NAME] = ConfigMetadata( - ConfigName::SERVICE_NAME, final_config.defaults.service, origin); + // DD_SERVICE + final_config.defaults.service = pick( + env_config->service, user_config.service, &all_sources_configs, + &final_config.metadata, ConfigName::SERVICE_NAME, get_process_name()); + // Service type final_config.defaults.service_type = value_or(env_config->service_type, user_config.service_type, "web"); // DD_ENV - std::tie(origin, final_config.defaults.environment) = - pick(env_config->environment, user_config.environment, ""); - final_config.metadata[ConfigName::SERVICE_ENV] = ConfigMetadata( - ConfigName::SERVICE_ENV, final_config.defaults.environment, origin); + final_config.defaults.environment = pick( + env_config->environment, user_config.environment, &all_sources_configs, + &final_config.metadata, ConfigName::SERVICE_ENV); // DD_VERSION - std::tie(origin, final_config.defaults.version) = - pick(env_config->version, user_config.version, ""); - final_config.metadata[ConfigName::SERVICE_VERSION] = ConfigMetadata( - ConfigName::SERVICE_VERSION, final_config.defaults.version, origin); + final_config.defaults.version = + pick(env_config->version, user_config.version, &all_sources_configs, + &final_config.metadata, ConfigName::SERVICE_VERSION); + // Span name final_config.defaults.name = value_or(env_config->name, user_config.name, ""); // DD_TAGS - std::tie(origin, final_config.defaults.tags) = - pick(env_config->tags, user_config.tags, - std::unordered_map{}); - final_config.metadata[ConfigName::TAGS] = ConfigMetadata( - ConfigName::TAGS, join_tags(final_config.defaults.tags), origin); + final_config.defaults.tags = + pick(env_config->tags, user_config.tags, &all_sources_configs, + &final_config.metadata, ConfigName::TAGS, + std::unordered_map{}, + [](const auto &tags) { return join_tags(tags); }); // Extraction Styles const std::vector default_propagation_styles{ PropagationStyle::DATADOG, PropagationStyle::W3C, PropagationStyle::BAGGAGE}; - std::tie(origin, final_config.extraction_styles) = + final_config.extraction_styles = pick(env_config->extraction_styles, user_config.extraction_styles, - default_propagation_styles); + &all_sources_configs, &final_config.metadata, + ConfigName::EXTRACTION_STYLES, default_propagation_styles, + [](const std::vector &styles) { + return join_propagation_styles(styles); + }); + if (final_config.extraction_styles.empty()) { return Error{Error::MISSING_SPAN_EXTRACTION_STYLE, "At least one extraction style must be specified."}; } - final_config.metadata[ConfigName::EXTRACTION_STYLES] = ConfigMetadata( - ConfigName::EXTRACTION_STYLES, - join_propagation_styles(final_config.extraction_styles), origin); // Injection Styles - std::tie(origin, final_config.injection_styles) = + final_config.injection_styles = pick(env_config->injection_styles, user_config.injection_styles, - default_propagation_styles); + &all_sources_configs, &final_config.metadata, + ConfigName::INJECTION_STYLES, default_propagation_styles, + [](const std::vector &styles) { + return join_propagation_styles(styles); + }); + if (final_config.injection_styles.empty()) { return Error{Error::MISSING_SPAN_INJECTION_STYLE, "At least one injection style must be specified."}; } - final_config.metadata[ConfigName::INJECTION_STYLES] = ConfigMetadata( - ConfigName::INJECTION_STYLES, - join_propagation_styles(final_config.injection_styles), origin); // Startup Logs - std::tie(origin, final_config.log_on_startup) = - pick(env_config->log_on_startup, user_config.log_on_startup, true); - final_config.metadata[ConfigName::STARTUP_LOGS] = ConfigMetadata( - ConfigName::STARTUP_LOGS, to_string(final_config.log_on_startup), origin); + final_config.log_on_startup = pick( + env_config->log_on_startup, user_config.log_on_startup, + &all_sources_configs, &final_config.metadata, ConfigName::STARTUP_LOGS, + true, [](const bool &b) { return to_string(b); }); // Report traces - std::tie(origin, final_config.report_traces) = - pick(env_config->report_traces, user_config.report_traces, true); - final_config.metadata[ConfigName::REPORT_TRACES] = ConfigMetadata( - ConfigName::REPORT_TRACES, to_string(final_config.report_traces), origin); + final_config.report_traces = pick( + env_config->report_traces, user_config.report_traces, + &all_sources_configs, &final_config.metadata, ConfigName::REPORT_TRACES, + true, [](const bool &b) { return to_string(b); }); // Report hostname final_config.report_hostname = @@ -369,12 +369,11 @@ Expected finalize_config(const TracerConfig &user_config, env_config->max_tags_header_size, user_config.max_tags_header_size, 512); // 128b Trace IDs - std::tie(origin, final_config.generate_128bit_trace_ids) = + final_config.generate_128bit_trace_ids = pick(env_config->generate_128bit_trace_ids, - user_config.generate_128bit_trace_ids, true); - final_config.metadata[ConfigName::GENEREATE_128BIT_TRACE_IDS] = - ConfigMetadata(ConfigName::GENEREATE_128BIT_TRACE_IDS, - to_string(final_config.generate_128bit_trace_ids), origin); + user_config.generate_128bit_trace_ids, &all_sources_configs, + &final_config.metadata, ConfigName::GENEREATE_128BIT_TRACE_IDS, true, + [](const bool &b) { return to_string(b); }); // Integration name & version final_config.integration_name = value_or( @@ -383,18 +382,19 @@ Expected finalize_config(const TracerConfig &user_config, value_or(env_config->integration_version, user_config.integration_version, tracer_version); - // Baggage - std::tie(origin, final_config.baggage_opts.max_items) = - pick(env_config->baggage_max_items, user_config.baggage_max_items, 64); - final_config.metadata[ConfigName::TRACE_BAGGAGE_MAX_ITEMS] = ConfigMetadata( - ConfigName::TRACE_BAGGAGE_MAX_ITEMS, - std::to_string(final_config.baggage_opts.max_items), origin); - - std::tie(origin, final_config.baggage_opts.max_bytes) = - pick(env_config->baggage_max_bytes, user_config.baggage_max_bytes, 8192); - final_config.metadata[ConfigName::TRACE_BAGGAGE_MAX_BYTES] = ConfigMetadata( - ConfigName::TRACE_BAGGAGE_MAX_BYTES, - std::to_string(final_config.baggage_opts.max_bytes), origin); + // Baggage - max items + final_config.baggage_opts.max_items = + pick(env_config->baggage_max_items, user_config.baggage_max_items, + &all_sources_configs, &final_config.metadata, + ConfigName::TRACE_BAGGAGE_MAX_ITEMS, 64UL, + [](const size_t &i) { return std::to_string(i); }); + + // Baggage - max bytes + final_config.baggage_opts.max_bytes = + pick(env_config->baggage_max_bytes, user_config.baggage_max_bytes, + &all_sources_configs, &final_config.metadata, + ConfigName::TRACE_BAGGAGE_MAX_BYTES, 8192UL, + [](const size_t &i) { return std::to_string(i); }); if (final_config.baggage_opts.max_items <= 0 || final_config.baggage_opts.max_bytes < 3) { @@ -453,39 +453,33 @@ Expected finalize_config(const TracerConfig &user_config, final_config.telemetry = std::move(*telemetry_final_config); final_config.telemetry.products.emplace_back(telemetry::Product{ telemetry::Product::Name::tracing, true, tracer_version, nullopt, - nullopt, final_config.metadata}); + nullopt, all_sources_configs}); } else { return std::move(telemetry_final_config.error()); } // APM Tracing Enabled - std::tie(origin, final_config.tracing_enabled) = - pick(env_config->tracing_enabled, user_config.tracing_enabled, true); - final_config.metadata[ConfigName::APM_TRACING_ENABLED] = - ConfigMetadata(ConfigName::APM_TRACING_ENABLED, - to_string(final_config.tracing_enabled), origin); + final_config.tracing_enabled = + pick(env_config->tracing_enabled, user_config.tracing_enabled, + &all_sources_configs, &final_config.metadata, + ConfigName::APM_TRACING_ENABLED, true, + [](const bool &b) { return to_string(b); }); { // Resource Renaming Enabled - bool resource_renaming_enabled; - std::tie(origin, resource_renaming_enabled) = - pick(env_config->resource_renaming_enabled, - user_config.resource_renaming_enabled, false); - - final_config.metadata[ConfigName::TRACE_RESOURCE_RENAMING_ENABLED] = - ConfigMetadata(ConfigName::TRACE_RESOURCE_RENAMING_ENABLED, - to_string(resource_renaming_enabled), origin); + const bool resource_renaming_enabled = pick( + env_config->resource_renaming_enabled, + user_config.resource_renaming_enabled, &all_sources_configs, + &final_config.metadata, ConfigName::TRACE_RESOURCE_RENAMING_ENABLED, + false, [](const bool &b) { return to_string(b); }); // Resource Renaming Always Simplified Endpoint - bool resource_renaming_always_simplified_endpoint; - std::tie(origin, resource_renaming_always_simplified_endpoint) = + const bool resource_renaming_always_simplified_endpoint = pick(env_config->resource_renaming_always_simplified_endpoint, - user_config.resource_renaming_always_simplified_endpoint, false); - final_config.metadata - [ConfigName::TRACE_RESOURCE_RENAMING_ALWAYS_SIMPLIFIED_ENDPOINT] = - ConfigMetadata( - ConfigName::TRACE_RESOURCE_RENAMING_ALWAYS_SIMPLIFIED_ENDPOINT, - to_string(resource_renaming_always_simplified_endpoint), origin); + user_config.resource_renaming_always_simplified_endpoint, + &all_sources_configs, &final_config.metadata, + ConfigName::TRACE_RESOURCE_RENAMING_ALWAYS_SIMPLIFIED_ENDPOINT, + false, [](const bool &b) { return to_string(b); }); if (!resource_renaming_enabled) { final_config.resource_renaming_mode = From 51faecf04c9b65488b943444805f168e892f2edf Mon Sep 17 00:00:00 2001 From: Anna Date: Tue, 23 Dec 2025 16:50:22 +0100 Subject: [PATCH 4/6] add new test for telemetry configs vector with precedence --- test/test_tracer_config.cpp | 70 +++++++++++++++++++++++++++++++++++++ 1 file changed, 70 insertions(+) diff --git a/test/test_tracer_config.cpp b/test/test_tracer_config.cpp index 712f98d8..b08af36c 100644 --- a/test/test_tracer_config.cpp +++ b/test/test_tracer_config.cpp @@ -1463,3 +1463,73 @@ TRACER_CONFIG_TEST("baggage") { false); } } + +TRACER_CONFIG_TEST("telemetry products contain configuration precedence") { + // Verifies that finalize_config produces telemetry products with + // configurations in the correct precedence order. Each configuration source + // (DEFAULT, CODE, ENVIRONMENT_VARIABLE) is tracked separately and will + // receive sequential seq_id values (1, 2, 3, etc.) when sent to telemetry. + + SECTION("all three sources: DEFAULT -> CODE -> ENVIRONMENT_VARIABLE") { + TracerConfig config; + config.service = "code-service"; + EnvGuard env{"DD_SERVICE", "env-service"}; + + auto finalized = finalize_config(config); + REQUIRE(finalized); + + const auto& configs = finalized->telemetry.products[0].configurations.at( + ConfigName::SERVICE_NAME); + + // All three sources in order (will receive seq_id 1, 2, 3) + REQUIRE(configs.size() == 3); + CHECK(configs[0].origin == ConfigMetadata::Origin::DEFAULT); + CHECK(configs[1].origin == ConfigMetadata::Origin::CODE); + CHECK(configs[1].value == "code-service"); + CHECK(configs[2].origin == ConfigMetadata::Origin::ENVIRONMENT_VARIABLE); + CHECK(configs[2].value == "env-service"); + + // Metadata contains final value (highest precedence) + CHECK(finalized->metadata.at(ConfigName::SERVICE_NAME).value == + "env-service"); + } + + SECTION("two sources: CODE -> ENVIRONMENT_VARIABLE (no default)") { + TracerConfig config; + config.service = "test"; + config.environment = "dev"; + EnvGuard env{"DD_ENV", "prod"}; + + auto finalized = finalize_config(config); + REQUIRE(finalized); + + const auto& configs = finalized->telemetry.products[0].configurations.at( + ConfigName::SERVICE_ENV); + + // Two sources (will receive seq_id 1, 2) + REQUIRE(configs.size() == 2); + CHECK(configs[0].origin == ConfigMetadata::Origin::CODE); + CHECK(configs[0].value == "dev"); + CHECK(configs[1].origin == ConfigMetadata::Origin::ENVIRONMENT_VARIABLE); + CHECK(configs[1].value == "prod"); + + CHECK(finalized->metadata.at(ConfigName::SERVICE_ENV).value == "prod"); + } + + SECTION("single source: CODE only") { + TracerConfig config; + config.service = "test"; + config.version = "1.2.3"; + + auto finalized = finalize_config(config); + REQUIRE(finalized); + + const auto& configs = finalized->telemetry.products[0].configurations.at( + ConfigName::SERVICE_VERSION); + + // Single source (will receive seq_id 1) + REQUIRE(configs.size() == 1); + CHECK(configs[0].origin == ConfigMetadata::Origin::CODE); + CHECK(configs[0].value == "1.2.3"); + } +} From 8e33b54986ca7ce83f5a9327192f911a0da768ca Mon Sep 17 00:00:00 2001 From: Anna Date: Tue, 23 Dec 2025 16:51:29 +0100 Subject: [PATCH 5/6] adapt telemetry test --- test/telemetry/test_telemetry.cpp | 54 +++++++++++++++++++++++-------- 1 file changed, 40 insertions(+), 14 deletions(-) diff --git a/test/telemetry/test_telemetry.cpp b/test/telemetry/test_telemetry.cpp index 31ffc9cb..83590773 100644 --- a/test/telemetry/test_telemetry.cpp +++ b/test/telemetry/test_telemetry.cpp @@ -191,11 +191,16 @@ TELEMETRY_IMPLEMENTATION_TEST("Tracer telemetry lifecycle") { product.name = Product::Name::tracing; product.enabled = true; product.version = tracer_version; - product.configurations = std::unordered_map{ - {ConfigName::SERVICE_NAME, - ConfigMetadata(ConfigName::SERVICE_NAME, "foo", - ConfigMetadata::Origin::CODE)}, - }; + product.configurations = + std::unordered_map>{ + {ConfigName::SERVICE_NAME, + {ConfigMetadata(ConfigName::SERVICE_NAME, "default-service", + ConfigMetadata::Origin::DEFAULT), + ConfigMetadata(ConfigName::SERVICE_NAME, "code-service", + ConfigMetadata::Origin::CODE), + ConfigMetadata(ConfigName::SERVICE_NAME, "env-service", + ConfigMetadata::Origin::ENVIRONMENT_VARIABLE)}}, + }; Configuration cfg; cfg.products.emplace_back(std::move(product)); @@ -218,18 +223,38 @@ TELEMETRY_IMPLEMENTATION_TEST("Tracer telemetry lifecycle") { auto cfg_payload = app_started_payload["payload"]["configuration"]; REQUIRE(cfg_payload.is_array()); - REQUIRE(cfg_payload.size() == 1); + REQUIRE(cfg_payload.size() == 3); + // Each configuration source is sent with an incrementing seq_id // clang-format off - const auto expected_conf = nlohmann::json({ - {"name", "service"}, - {"value", "foo"}, - {"seq_id", 1}, - {"origin", "code"}, - }); + const std::unordered_map expected_configs{ + {"default-service", nlohmann::json({ + {"name", "service"}, + {"value", "default-service"}, + {"seq_id", 1}, + {"origin", "default"}, + })}, + {"code-service", nlohmann::json({ + {"name", "service"}, + {"value", "code-service"}, + {"seq_id", 2}, + {"origin", "code"}, + })}, + {"env-service", nlohmann::json({ + {"name", "service"}, + {"value", "env-service"}, + {"seq_id", 3}, + {"origin", "env_var"}, + })}, + }; // clang-format on - CHECK(cfg_payload[0] == expected_conf); + // Verify all three configuration sources are present + for (const auto& conf : cfg_payload) { + auto value = conf["value"].get(); + REQUIRE(expected_configs.count(value) == 1); + CHECK(conf == expected_configs.at(value)); + } SECTION("generates a configuration change event") { SECTION("empty configuration do not generate a valid payload") { @@ -268,7 +293,8 @@ TELEMETRY_IMPLEMENTATION_TEST("Tracer telemetry lifecycle") { nlohmann::json{ {"name", "service"}, {"value", "increase seq_id"}, - {"seq_id", 2}, + {"seq_id", + 4}, // seq_id 4 (after DEFAULT=1, CODE=2, ENV=3) {"origin", "env_var"}, }, }, From 382e80faa81e353100104c43834c17c32ee0412b Mon Sep 17 00:00:00 2001 From: Anna Date: Tue, 23 Dec 2025 19:43:17 +0100 Subject: [PATCH 6/6] Answer to comments --- bin/format | 1 - include/datadog/config.h | 46 +++++++++--- src/datadog/trace_sampler_config.cpp | 2 +- src/datadog/tracer_config.cpp | 105 ++++++++++++++------------- 4 files changed, 88 insertions(+), 66 deletions(-) diff --git a/bin/format b/bin/format index d99ed544..6a5d66ac 100755 --- a/bin/format +++ b/bin/format @@ -64,7 +64,6 @@ docker_clang_format() { process_arg "$arg" done | xargs -0 \ docker run \ - --platform linux/amd64 \ --interactive \ --rm \ --volume /etc/passwd:/etc/passwd:ro \ diff --git a/include/datadog/config.h b/include/datadog/config.h index 6398b9b6..c1a70319 100644 --- a/include/datadog/config.h +++ b/include/datadog/config.h @@ -62,21 +62,43 @@ struct ConfigMetadata { : name(n), value(std::move(v)), origin(orig), error(std::move(err)) {} }; -// Return the chosen configuration value, in order of precedence: `from_env`, -// `from_user`, and `fallback`. -// This overload directly populates both telemetry_configs[config_name] and -// metadata[config_name] using the stringified value, with all values found, -// from lowest to highest precedence. Returns the chosen value directly. +// Returns the final configuration value using the following +// precedence order: environment > user code > default, and populates two maps: +// 1. `telemetry_configs`: Records ALL configuration sources that were provided, +// ordered from lowest to highest precedence. +// 2. `metadata`: Records ONLY the winning configuration value (highest +// precedence). Template Parameters: +// Value: The type of the configuration value +// Stringifier: Optional function type to convert Value to string +// (defaults to std::nullptr_t, which uses string construction) +// DefaultValue: Type of the fallback value (defaults to std::nullptr_t) // -// The fallback parameter is optional and defaults to nullptr. +// Parameters: +// from_env: Optional value from environment variables (highest precedence) +// from_user: Optional value from user code (middle precedence) +// telemetry_configs: Output map that will be populated with all config +// sources found for this config_name, in precedence order +// metadata: Output map that will be populated with the winning config value +// for this config_name +// config_name: The configuration parameter name identifier +// fallback: Optional default value (lowest precedence). Pass nullptr to +// indicate no default. +// to_string_fn: Optional custom function to convert Value to string. +// Required for non-string types. For string-like types, uses +// default string construction if not provided. +// +// Returns: +// The chosen configuration value based on precedence, or Value{} if no value +// was provided. template -Value pick(const Optional& from_env, const Optional& from_user, - std::unordered_map>* - telemetry_configs, - std::unordered_map* metadata, - ConfigName config_name, DefaultValue fallback = nullptr, - Stringifier to_string_fn = nullptr) { +Value resolve_and_record_config( + const Optional& from_env, const Optional& from_user, + std::unordered_map>* + telemetry_configs, + std::unordered_map* metadata, + ConfigName config_name, DefaultValue fallback = nullptr, + Stringifier to_string_fn = nullptr) { auto stringify = [&](const Value& v) -> std::string { if constexpr (!std::is_same_v) { return to_string_fn(v); // use provided function diff --git a/src/datadog/trace_sampler_config.cpp b/src/datadog/trace_sampler_config.cpp index 97a68d00..2182c0ab 100644 --- a/src/datadog/trace_sampler_config.cpp +++ b/src/datadog/trace_sampler_config.cpp @@ -227,7 +227,7 @@ Expected finalize_config( std::unordered_map> telemetry_configs_tmp; - double max_per_second = pick( + double max_per_second = resolve_and_record_config( env_config->max_per_second, config.max_per_second, &telemetry_configs_tmp, &result.metadata, ConfigName::TRACE_SAMPLING_LIMIT, 100.0, [](const double &d) { return std::to_string(d); }); diff --git a/src/datadog/tracer_config.cpp b/src/datadog/tracer_config.cpp index 932c41e4..7c9af617 100644 --- a/src/datadog/tracer_config.cpp +++ b/src/datadog/tracer_config.cpp @@ -288,7 +288,7 @@ Expected finalize_config(const TracerConfig &user_config, all_sources_configs; // DD_SERVICE - final_config.defaults.service = pick( + final_config.defaults.service = resolve_and_record_config( env_config->service, user_config.service, &all_sources_configs, &final_config.metadata, ConfigName::SERVICE_NAME, get_process_name()); @@ -297,37 +297,37 @@ Expected finalize_config(const TracerConfig &user_config, value_or(env_config->service_type, user_config.service_type, "web"); // DD_ENV - final_config.defaults.environment = pick( + final_config.defaults.environment = resolve_and_record_config( env_config->environment, user_config.environment, &all_sources_configs, &final_config.metadata, ConfigName::SERVICE_ENV); // DD_VERSION - final_config.defaults.version = - pick(env_config->version, user_config.version, &all_sources_configs, - &final_config.metadata, ConfigName::SERVICE_VERSION); + final_config.defaults.version = resolve_and_record_config( + env_config->version, user_config.version, &all_sources_configs, + &final_config.metadata, ConfigName::SERVICE_VERSION); // Span name final_config.defaults.name = value_or(env_config->name, user_config.name, ""); // DD_TAGS - final_config.defaults.tags = - pick(env_config->tags, user_config.tags, &all_sources_configs, - &final_config.metadata, ConfigName::TAGS, - std::unordered_map{}, - [](const auto &tags) { return join_tags(tags); }); + final_config.defaults.tags = resolve_and_record_config( + env_config->tags, user_config.tags, &all_sources_configs, + &final_config.metadata, ConfigName::TAGS, + std::unordered_map{}, + [](const auto &tags) { return join_tags(tags); }); // Extraction Styles const std::vector default_propagation_styles{ PropagationStyle::DATADOG, PropagationStyle::W3C, PropagationStyle::BAGGAGE}; - final_config.extraction_styles = - pick(env_config->extraction_styles, user_config.extraction_styles, - &all_sources_configs, &final_config.metadata, - ConfigName::EXTRACTION_STYLES, default_propagation_styles, - [](const std::vector &styles) { - return join_propagation_styles(styles); - }); + final_config.extraction_styles = resolve_and_record_config( + env_config->extraction_styles, user_config.extraction_styles, + &all_sources_configs, &final_config.metadata, + ConfigName::EXTRACTION_STYLES, default_propagation_styles, + [](const std::vector &styles) { + return join_propagation_styles(styles); + }); if (final_config.extraction_styles.empty()) { return Error{Error::MISSING_SPAN_EXTRACTION_STYLE, @@ -335,13 +335,13 @@ Expected finalize_config(const TracerConfig &user_config, } // Injection Styles - final_config.injection_styles = - pick(env_config->injection_styles, user_config.injection_styles, - &all_sources_configs, &final_config.metadata, - ConfigName::INJECTION_STYLES, default_propagation_styles, - [](const std::vector &styles) { - return join_propagation_styles(styles); - }); + final_config.injection_styles = resolve_and_record_config( + env_config->injection_styles, user_config.injection_styles, + &all_sources_configs, &final_config.metadata, + ConfigName::INJECTION_STYLES, default_propagation_styles, + [](const std::vector &styles) { + return join_propagation_styles(styles); + }); if (final_config.injection_styles.empty()) { return Error{Error::MISSING_SPAN_INJECTION_STYLE, @@ -349,13 +349,13 @@ Expected finalize_config(const TracerConfig &user_config, } // Startup Logs - final_config.log_on_startup = pick( + final_config.log_on_startup = resolve_and_record_config( env_config->log_on_startup, user_config.log_on_startup, &all_sources_configs, &final_config.metadata, ConfigName::STARTUP_LOGS, true, [](const bool &b) { return to_string(b); }); // Report traces - final_config.report_traces = pick( + final_config.report_traces = resolve_and_record_config( env_config->report_traces, user_config.report_traces, &all_sources_configs, &final_config.metadata, ConfigName::REPORT_TRACES, true, [](const bool &b) { return to_string(b); }); @@ -369,11 +369,11 @@ Expected finalize_config(const TracerConfig &user_config, env_config->max_tags_header_size, user_config.max_tags_header_size, 512); // 128b Trace IDs - final_config.generate_128bit_trace_ids = - pick(env_config->generate_128bit_trace_ids, - user_config.generate_128bit_trace_ids, &all_sources_configs, - &final_config.metadata, ConfigName::GENEREATE_128BIT_TRACE_IDS, true, - [](const bool &b) { return to_string(b); }); + final_config.generate_128bit_trace_ids = resolve_and_record_config( + env_config->generate_128bit_trace_ids, + user_config.generate_128bit_trace_ids, &all_sources_configs, + &final_config.metadata, ConfigName::GENEREATE_128BIT_TRACE_IDS, true, + [](const bool &b) { return to_string(b); }); // Integration name & version final_config.integration_name = value_or( @@ -383,18 +383,18 @@ Expected finalize_config(const TracerConfig &user_config, tracer_version); // Baggage - max items - final_config.baggage_opts.max_items = - pick(env_config->baggage_max_items, user_config.baggage_max_items, - &all_sources_configs, &final_config.metadata, - ConfigName::TRACE_BAGGAGE_MAX_ITEMS, 64UL, - [](const size_t &i) { return std::to_string(i); }); + final_config.baggage_opts.max_items = resolve_and_record_config( + env_config->baggage_max_items, user_config.baggage_max_items, + &all_sources_configs, &final_config.metadata, + ConfigName::TRACE_BAGGAGE_MAX_ITEMS, 64UL, + [](const size_t &i) { return std::to_string(i); }); // Baggage - max bytes - final_config.baggage_opts.max_bytes = - pick(env_config->baggage_max_bytes, user_config.baggage_max_bytes, - &all_sources_configs, &final_config.metadata, - ConfigName::TRACE_BAGGAGE_MAX_BYTES, 8192UL, - [](const size_t &i) { return std::to_string(i); }); + final_config.baggage_opts.max_bytes = resolve_and_record_config( + env_config->baggage_max_bytes, user_config.baggage_max_bytes, + &all_sources_configs, &final_config.metadata, + ConfigName::TRACE_BAGGAGE_MAX_BYTES, 8192UL, + [](const size_t &i) { return std::to_string(i); }); if (final_config.baggage_opts.max_items <= 0 || final_config.baggage_opts.max_bytes < 3) { @@ -459,15 +459,15 @@ Expected finalize_config(const TracerConfig &user_config, } // APM Tracing Enabled - final_config.tracing_enabled = - pick(env_config->tracing_enabled, user_config.tracing_enabled, - &all_sources_configs, &final_config.metadata, - ConfigName::APM_TRACING_ENABLED, true, - [](const bool &b) { return to_string(b); }); + final_config.tracing_enabled = resolve_and_record_config( + env_config->tracing_enabled, user_config.tracing_enabled, + &all_sources_configs, &final_config.metadata, + ConfigName::APM_TRACING_ENABLED, true, + [](const bool &b) { return to_string(b); }); { // Resource Renaming Enabled - const bool resource_renaming_enabled = pick( + const bool resource_renaming_enabled = resolve_and_record_config( env_config->resource_renaming_enabled, user_config.resource_renaming_enabled, &all_sources_configs, &final_config.metadata, ConfigName::TRACE_RESOURCE_RENAMING_ENABLED, @@ -475,11 +475,12 @@ Expected finalize_config(const TracerConfig &user_config, // Resource Renaming Always Simplified Endpoint const bool resource_renaming_always_simplified_endpoint = - pick(env_config->resource_renaming_always_simplified_endpoint, - user_config.resource_renaming_always_simplified_endpoint, - &all_sources_configs, &final_config.metadata, - ConfigName::TRACE_RESOURCE_RENAMING_ALWAYS_SIMPLIFIED_ENDPOINT, - false, [](const bool &b) { return to_string(b); }); + resolve_and_record_config( + env_config->resource_renaming_always_simplified_endpoint, + user_config.resource_renaming_always_simplified_endpoint, + &all_sources_configs, &final_config.metadata, + ConfigName::TRACE_RESOURCE_RENAMING_ALWAYS_SIMPLIFIED_ENDPOINT, + false, [](const bool &b) { return to_string(b); }); if (!resource_renaming_enabled) { final_config.resource_renaming_mode =