diff --git a/src/datadog/config_manager.cpp b/src/datadog/config_manager.cpp index a13ac346..b5f6498f 100644 --- a/src/datadog/config_manager.cpp +++ b/src/datadog/config_manager.cpp @@ -9,13 +9,20 @@ ConfigManager::ConfigManager(const FinalizedTracerConfig& config) : clock_(config.clock), default_trace_sampler_( std::make_shared(config.trace_sampler, clock_)), - current_trace_sampler_(default_trace_sampler_) {} + current_trace_sampler_(default_trace_sampler_), + default_span_defaults_(std::make_shared(config.defaults)), + current_span_defaults_(default_span_defaults_) {} std::shared_ptr ConfigManager::get_trace_sampler() { std::lock_guard lock(mutex_); return current_trace_sampler_; } +std::shared_ptr ConfigManager::get_span_defaults() { + std::lock_guard lock(mutex_); + return current_span_defaults_; +} + void ConfigManager::update(const ConfigUpdate& conf) { std::lock_guard lock(mutex_); @@ -30,16 +37,28 @@ void ConfigManager::update(const ConfigUpdate& conf) { } else { current_trace_sampler_ = default_trace_sampler_; } + + if (conf.tags) { + auto new_span_defaults = + std::make_shared(*current_span_defaults_); + new_span_defaults->tags = std::move(*conf.tags); + + current_span_defaults_ = new_span_defaults; + } else { + current_span_defaults_ = default_span_defaults_; + } } void ConfigManager::reset() { std::lock_guard lock(mutex_); current_trace_sampler_ = default_trace_sampler_; + current_span_defaults_ = default_span_defaults_; } nlohmann::json ConfigManager::config_json() const { std::lock_guard lock(mutex_); return nlohmann::json{ + {"default", to_json(*current_span_defaults_)}, {"trace_sampler", current_trace_sampler_->config_json()}}; } diff --git a/src/datadog/config_manager.h b/src/datadog/config_manager.h index 1b48a6a8..e14ffee0 100644 --- a/src/datadog/config_manager.h +++ b/src/datadog/config_manager.h @@ -10,6 +10,7 @@ #include "clock.h" #include "config_update.h" #include "json.hpp" +#include "span_defaults.h" #include "tracer_config.h" namespace datadog { @@ -21,12 +22,18 @@ class ConfigManager { std::shared_ptr default_trace_sampler_; std::shared_ptr current_trace_sampler_; + std::shared_ptr default_span_defaults_; + std::shared_ptr current_span_defaults_; + public: ConfigManager(const FinalizedTracerConfig& config); // Return the `TraceSampler` consistent with the most recent configuration. std::shared_ptr get_trace_sampler(); + // Return the `SpanDefaults` consistent with the most recent configuration. + std::shared_ptr get_span_defaults(); + // Apply the specified `conf` update. void update(const ConfigUpdate& conf); diff --git a/src/datadog/config_update.h b/src/datadog/config_update.h index 57818c3e..41aac51e 100644 --- a/src/datadog/config_update.h +++ b/src/datadog/config_update.h @@ -1,5 +1,7 @@ #pragma once +#include + #include "optional" #include "trace_sampler_config.h" @@ -13,6 +15,7 @@ namespace tracing { // remote configuration value. struct ConfigUpdate { Optional trace_sampler; + Optional> tags; }; } // namespace tracing diff --git a/src/datadog/remote_config.cpp b/src/datadog/remote_config.cpp index 89982a42..746aada2 100644 --- a/src/datadog/remote_config.cpp +++ b/src/datadog/remote_config.cpp @@ -29,6 +29,7 @@ namespace { // within the array. enum CapabilitiesFlag : uint64_t { APM_TRACING_SAMPLE_RATE = 1 << 12, + APM_TRACING_TAGS = 1 << 15 }; constexpr std::array capabilities_byte_array( @@ -43,11 +44,36 @@ constexpr std::array capabilities_byte_array( } constexpr std::array k_apm_capabilities = - capabilities_byte_array(APM_TRACING_SAMPLE_RATE); + capabilities_byte_array(APM_TRACING_SAMPLE_RATE | APM_TRACING_TAGS); constexpr StringView k_apm_product = "APM_TRACING"; constexpr StringView k_apm_product_path_substring = "/APM_TRACING/"; +Expected> parse_tags( + const std::vector& list_of_tags) { + std::unordered_map tags; + + // Within a tag, the key and value are separated by a colon (":"). + for (const StringView& token : list_of_tags) { + const auto separator = std::find(token.begin(), token.end(), ':'); + if (separator == token.end()) { + std::string message; + message += "Unable to parse a key/value from the tag text \""; + append(message, token); + message += + "\" because it does not contain the separator character \":\"."; + return Error{Error::TAG_MISSING_SEPARATOR, std::move(message)}; + } + + std::string key{token.begin(), separator}; + std::string value{separator + 1, token.end()}; + // If there are duplicate values, then the last one wins. + tags.insert_or_assign(std::move(key), std::move(value)); + } + + return tags; +} + ConfigUpdate parse_dynamic_config(const nlohmann::json& j) { ConfigUpdate config_update; @@ -59,6 +85,15 @@ ConfigUpdate parse_dynamic_config(const nlohmann::json& j) { config_update.trace_sampler = trace_sampler_cfg; } + if (auto tags_it = j.find("tracing_tags"); tags_it != j.cend()) { + auto parsed_tags = parse_tags(*tags_it); + if (parsed_tags.if_error()) { + // TODO: report to telemetry + } else { + config_update.tags = std::move(*parsed_tags); + } + } + return config_update; } diff --git a/src/datadog/tracer.cpp b/src/datadog/tracer.cpp index 9ca6a35a..54c55b96 100644 --- a/src/datadog/tracer.cpp +++ b/src/datadog/tracer.cpp @@ -38,7 +38,6 @@ Tracer::Tracer(const FinalizedTracerConfig& config, : logger_(config.logger), config_manager_(std::make_shared(config)), collector_(/* see constructor body */), - defaults_(std::make_shared(config.defaults)), runtime_id_(config.runtime_id ? *config.runtime_id : RuntimeID::generate()), signature_{runtime_id_, config.defaults.service, @@ -83,7 +82,6 @@ nlohmann::json Tracer::config_json() const { // clang-format off auto config = nlohmann::json::object({ {"version", tracer_version_string}, - {"defaults", to_json(*defaults_)}, {"runtime_id", runtime_id_.string()}, {"collector", collector_->config_json()}, {"span_sampler", span_sampler_->config_json()}, @@ -106,8 +104,9 @@ nlohmann::json Tracer::config_json() const { Span Tracer::create_span() { return create_span(SpanConfig{}); } Span Tracer::create_span(const SpanConfig& config) { + auto defaults = config_manager_->get_span_defaults(); auto span_data = std::make_unique(); - span_data->apply_config(*defaults_, config, clock_); + span_data->apply_config(*defaults, config, clock_); span_data->trace_id = generator_->trace_id(span_data->start); span_data->span_id = span_data->trace_id.low; span_data->parent_id = 0; @@ -122,7 +121,7 @@ Span Tracer::create_span(const SpanConfig& config) { tracer_telemetry_->metrics().tracer.trace_segments_created_new.inc(); const auto segment = std::make_shared( logger_, collector_, tracer_telemetry_, - config_manager_->get_trace_sampler(), span_sampler_, defaults_, + config_manager_->get_trace_sampler(), span_sampler_, defaults, runtime_id_, sampling_delegation_enabled_, false /* sampling_decision_was_delegated_to_me */, injection_styles_, hostname_, nullopt /* origin */, tags_header_max_size_, @@ -241,7 +240,8 @@ Expected Tracer::extract_span(const DictReader& reader, // We're done extracting fields. Now create the span. // This is similar to what we do in `create_span`. - span_data->apply_config(*defaults_, config, clock_); + span_data->apply_config(*config_manager_->get_span_defaults(), config, + clock_); span_data->span_id = generator_->span_id(); span_data->trace_id = *trace_id; span_data->parent_id = *parent_id; @@ -293,8 +293,9 @@ Expected Tracer::extract_span(const DictReader& reader, tracer_telemetry_->metrics().tracer.trace_segments_created_continued.inc(); const auto segment = std::make_shared( logger_, collector_, tracer_telemetry_, - config_manager_->get_trace_sampler(), span_sampler_, defaults_, - runtime_id_, sampling_delegation_enabled_, delegate_sampling_decision, + config_manager_->get_trace_sampler(), span_sampler_, + config_manager_->get_span_defaults(), runtime_id_, + sampling_delegation_enabled_, delegate_sampling_decision, injection_styles_, hostname_, std::move(origin), tags_header_max_size_, std::move(trace_tags), std::move(sampling_decision), std::move(additional_w3c_tracestate), diff --git a/src/datadog/tracer.h b/src/datadog/tracer.h index 57942909..7fc5cb24 100644 --- a/src/datadog/tracer.h +++ b/src/datadog/tracer.h @@ -37,7 +37,6 @@ class Tracer { std::shared_ptr logger_; std::shared_ptr config_manager_; std::shared_ptr collector_; - std::shared_ptr defaults_; RuntimeID runtime_id_; TracerSignature signature_; std::shared_ptr tracer_telemetry_; diff --git a/test/test_remote_config.cpp b/test/test_remote_config.cpp index 0c002a53..984d64f5 100644 --- a/test/test_remote_config.cpp +++ b/test/test_remote_config.cpp @@ -166,7 +166,7 @@ REMOTE_CONFIG_TEST("response processing") { "target_files": [ { "path": "foo/APM_TRACING/30", - "raw": "eyAiaWQiOiAiODI3ZWFjZjhkYmMzYWIxNDM0ZDMyMWNiODFkZmJmN2FmZTY1NGE0YjYxMTFjZjE2NjBiNzFjY2Y4OTc4MTkzOCIsICJyZXZpc2lvbiI6IDE2OTgxNjcxMjYwNjQsICJzY2hlbWFfdmVyc2lvbiI6ICJ2MS4wLjAiLCAiYWN0aW9uIjogImVuYWJsZSIsICJsaWJfY29uZmlnIjogeyAibGlicmFyeV9sYW5ndWFnZSI6ICJhbGwiLCAibGlicmFyeV92ZXJzaW9uIjogImxhdGVzdCIsICJzZXJ2aWNlX25hbWUiOiAidGVzdHN2YyIsICJlbnYiOiAidGVzdCIsICJ0cmFjaW5nX2VuYWJsZWQiOiB0cnVlLCAidHJhY2luZ19zYW1wbGluZ19yYXRlIjogMC42IH0sICJzZXJ2aWNlX3RhcmdldCI6IHsgInNlcnZpY2UiOiAidGVzdHN2YyIsICJlbnYiOiAidGVzdCIgfSB9" + "raw": "eyAiaWQiOiAiODI3ZWFjZjhkYmMzYWIxNDM0ZDMyMWNiODFkZmJmN2FmZTY1NGE0YjYxMTFjZjE2NjBiNzFjY2Y4OTc4MTkzOCIsICJyZXZpc2lvbiI6IDE2OTgxNjcxMjYwNjQsICJzY2hlbWFfdmVyc2lvbiI6ICJ2MS4wLjAiLCAiYWN0aW9uIjogImVuYWJsZSIsICJsaWJfY29uZmlnIjogeyAibGlicmFyeV9sYW5ndWFnZSI6ICJhbGwiLCAibGlicmFyeV92ZXJzaW9uIjogImxhdGVzdCIsICJzZXJ2aWNlX25hbWUiOiAidGVzdHN2YyIsICJlbnYiOiAidGVzdCIsICJ0cmFjaW5nX2VuYWJsZWQiOiB0cnVlLCAidHJhY2luZ19zYW1wbGluZ19yYXRlIjogMC42LCAidHJhY2luZ190YWdzIjogWyJoZWxsbzp3b3JsZCIsICJmb286YmFyIl0gfSwgInNlcnZpY2VfdGFyZ2V0IjogeyAic2VydmljZSI6ICJ0ZXN0c3ZjIiwgImVudiI6ICJ0ZXN0IiB9IH0=" } ] })"; @@ -180,10 +180,13 @@ REMOTE_CONFIG_TEST("response processing") { REQUIRE(!response_json.is_discarded()); const auto old_trace_sampler = config_manager->get_trace_sampler(); + const auto old_span_defaults = config_manager->get_span_defaults(); rc.process_response(response_json); const auto new_trace_sampler = config_manager->get_trace_sampler(); + const auto new_span_defaults = config_manager->get_span_defaults(); CHECK(new_trace_sampler != old_trace_sampler); + CHECK(new_span_defaults != old_span_defaults); SECTION("reset confguration") { SECTION(