Skip to content

Commit 62d83fb

Browse files
bm1549claude
andcommitted
refactor: extract YAML parser and integrate all configs with stable config precedence
Task 1: Separate the YAML parser from stable_config.cpp into its own module (yaml_parser.h/cpp) with a clean interface that takes a string and returns parsed data without depending on Logger or StableConfig. Add comprehensive parser tests in test_yaml_parser.cpp. Task 2: Update all remaining configs to use the 5-parameter resolve_and_record_config with stable config sources: - DD_TAGS (tags map type) - DD_TRACE_PROPAGATION_STYLE_EXTRACT/INJECT and variants (propagation styles) - DD_TRACE_BAGGAGE_MAX_ITEMS/BYTES (uint64) - DD_TRACE_SAMPLE_RATE, DD_TRACE_RATE_LIMIT (double, via trace_sampler_config) - DD_TRACE_RESOURCE_RENAMING_ENABLED (bool) - DD_TRACE_RESOURCE_RENAMING_ALWAYS_SIMPLIFIED_ENDPOINT (bool) JSON array configs (DD_TRACE_SAMPLING_RULES, DD_SPAN_SAMPLING_RULES) are left on the old path with TODO comments since the YAML parser skips non-scalar values. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
1 parent bdc8d36 commit 62d83fb

12 files changed

Lines changed: 741 additions & 206 deletions

BUILD.bazel

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,8 @@ cc_library(
8383
"src/datadog/tracer.cpp",
8484
"src/datadog/stable_config.cpp",
8585
"src/datadog/stable_config.h",
86+
"src/datadog/yaml_parser.cpp",
87+
"src/datadog/yaml_parser.h",
8688
"src/datadog/tracer_config.cpp",
8789
"src/datadog/version.cpp",
8890
"src/datadog/w3c_propagation.cpp",

CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -211,6 +211,7 @@ target_sources(dd-trace-cpp-objects
211211
src/datadog/tag_propagation.cpp
212212
src/datadog/threaded_event_scheduler.cpp
213213
src/datadog/stable_config.cpp
214+
src/datadog/yaml_parser.cpp
214215
src/datadog/tracer_config.cpp
215216
src/datadog/tracer.cpp
216217
src/datadog/trace_id.cpp

include/datadog/span_sampler_config.h

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ struct SpanSamplerConfig {
3737

3838
class FinalizedSpanSamplerConfig {
3939
friend Expected<FinalizedSpanSamplerConfig> finalize_config(
40-
const SpanSamplerConfig&, Logger&);
40+
const SpanSamplerConfig&, Logger&, const struct StableConfigs*);
4141
friend class FinalizedTracerConfig;
4242

4343
FinalizedSpanSamplerConfig() = default;
@@ -52,8 +52,9 @@ class FinalizedSpanSamplerConfig {
5252
std::unordered_map<ConfigName, std::vector<ConfigMetadata>> metadata;
5353
};
5454

55-
Expected<FinalizedSpanSamplerConfig> finalize_config(const SpanSamplerConfig&,
56-
Logger&);
55+
Expected<FinalizedSpanSamplerConfig> finalize_config(
56+
const SpanSamplerConfig&, Logger&,
57+
const struct StableConfigs* stable_configs = nullptr);
5758

5859
std::string to_string(const FinalizedSpanSamplerConfig::Rule&);
5960

include/datadog/trace_sampler_config.h

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,8 @@
2020
namespace datadog {
2121
namespace tracing {
2222

23+
struct StableConfigs;
24+
2325
struct TraceSamplerRule final {
2426
Rate rate;
2527
SpanMatcher matcher;
@@ -42,7 +44,7 @@ struct TraceSamplerConfig {
4244

4345
class FinalizedTraceSamplerConfig {
4446
friend Expected<FinalizedTraceSamplerConfig> finalize_config(
45-
const TraceSamplerConfig& config);
47+
const TraceSamplerConfig& config, const StableConfigs* stable_configs);
4648
friend class FinalizedTracerConfig;
4749

4850
FinalizedTraceSamplerConfig() = default;
@@ -58,7 +60,8 @@ class FinalizedTraceSamplerConfig {
5860
};
5961

6062
Expected<FinalizedTraceSamplerConfig> finalize_config(
61-
const TraceSamplerConfig& config);
63+
const TraceSamplerConfig& config,
64+
const StableConfigs* stable_configs = nullptr);
6265

6366
} // namespace tracing
6467
} // namespace datadog

src/datadog/span_sampler_config.cpp

Lines changed: 58 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -9,14 +9,15 @@
99

1010
#include "json.hpp"
1111
#include "json_serializer.h"
12+
#include "stable_config.h"
1213

1314
namespace datadog {
1415
namespace tracing {
1516
namespace {
1617

17-
std::string to_string(const std::vector<SpanSamplerConfig::Rule> &rules) {
18+
std::string to_string(const std::vector<SpanSamplerConfig::Rule>& rules) {
1819
nlohmann::json res;
19-
for (const auto &r : rules) {
20+
for (const auto& r : rules) {
2021
nlohmann::json j = r;
2122
j["sample_rate"] = r.sample_rate;
2223
if (r.max_per_second) {
@@ -37,7 +38,7 @@ Expected<std::vector<SpanSamplerConfig::Rule>> parse_rules(StringView rules_raw,
3738

3839
try {
3940
json_rules = nlohmann::json::parse(rules_raw);
40-
} catch (const nlohmann::json::parse_error &error) {
41+
} catch (const nlohmann::json::parse_error& error) {
4142
std::string message;
4243
message += "Unable to parse JSON from ";
4344
append(message, env_var);
@@ -63,9 +64,9 @@ Expected<std::vector<SpanSamplerConfig::Rule>> parse_rules(StringView rules_raw,
6364
const std::unordered_set<std::string> allowed_properties{
6465
"service", "name", "resource", "tags", "sample_rate", "max_per_second"};
6566

66-
for (const auto &json_rule : json_rules) {
67+
for (const auto& json_rule : json_rules) {
6768
auto matcher = from_json(json_rule);
68-
if (auto *error = matcher.if_error()) {
69+
if (auto* error = matcher.if_error()) {
6970
std::string prefix;
7071
prefix += "Unable to create a rule from ";
7172
append(prefix, env_var);
@@ -118,7 +119,7 @@ Expected<std::vector<SpanSamplerConfig::Rule>> parse_rules(StringView rules_raw,
118119
}
119120

120121
// Look for unexpected properties.
121-
for (const auto &[key, value] : json_rule.items()) {
122+
for (const auto& [key, value] : json_rule.items()) {
122123
if (allowed_properties.count(key)) {
123124
continue;
124125
}
@@ -143,14 +144,14 @@ Expected<std::vector<SpanSamplerConfig::Rule>> parse_rules(StringView rules_raw,
143144
return rules;
144145
}
145146

146-
Expected<SpanSamplerConfig> load_span_sampler_env_config(Logger &logger) {
147+
Expected<SpanSamplerConfig> load_span_sampler_env_config(Logger& logger) {
147148
SpanSamplerConfig env_config;
148149

149150
auto rules_env = lookup(environment::DD_SPAN_SAMPLING_RULES);
150151
if (rules_env) {
151152
auto maybe_rules =
152153
parse_rules(*rules_env, name(environment::DD_SPAN_SAMPLING_RULES));
153-
if (auto *error = maybe_rules.if_error()) {
154+
if (auto* error = maybe_rules.if_error()) {
154155
return std::move(*error);
155156
}
156157
env_config.rules = std::move(*maybe_rules);
@@ -174,7 +175,7 @@ Expected<SpanSamplerConfig> load_span_sampler_env_config(Logger &logger) {
174175
} else {
175176
const auto span_rules_file = std::string(*file_env);
176177

177-
const auto file_error = [&](const char *operation) {
178+
const auto file_error = [&](const char* operation) {
178179
std::string message;
179180
message += "Unable to ";
180181
message += operation;
@@ -199,7 +200,7 @@ Expected<SpanSamplerConfig> load_span_sampler_env_config(Logger &logger) {
199200

200201
auto maybe_rules = parse_rules(
201202
rules_stream.str(), name(environment::DD_SPAN_SAMPLING_RULES_FILE));
202-
if (auto *error = maybe_rules.if_error()) {
203+
if (auto* error = maybe_rules.if_error()) {
203204
std::string prefix;
204205
prefix += "With ";
205206
append(prefix, name(environment::DD_SPAN_SAMPLING_RULES_FILE));
@@ -218,10 +219,11 @@ Expected<SpanSamplerConfig> load_span_sampler_env_config(Logger &logger) {
218219

219220
} // namespace
220221

221-
SpanSamplerConfig::Rule::Rule(const SpanMatcher &base) : SpanMatcher(base) {}
222+
SpanSamplerConfig::Rule::Rule(const SpanMatcher& base) : SpanMatcher(base) {}
222223

223224
Expected<FinalizedSpanSamplerConfig> finalize_config(
224-
const SpanSamplerConfig &user_config, Logger &logger) {
225+
const SpanSamplerConfig& user_config, Logger& logger,
226+
const StableConfigs* stable_configs) {
225227
Expected<SpanSamplerConfig> env_config = load_span_sampler_env_config(logger);
226228
if (auto error = env_config.if_error()) {
227229
return *error;
@@ -237,15 +239,52 @@ Expected<FinalizedSpanSamplerConfig> finalize_config(
237239
user_rules = user_config.rules;
238240
}
239241

242+
Optional<std::vector<SpanSamplerConfig::Rule>> fleet_rules;
243+
Optional<std::vector<SpanSamplerConfig::Rule>> local_rules;
244+
if (stable_configs) {
245+
auto parse_span_rules = [](const StableConfig& cfg, const std::string& key)
246+
-> Optional<std::vector<SpanSamplerConfig::Rule>> {
247+
auto val = cfg.lookup(key);
248+
if (!val || val->empty()) return nullopt;
249+
try {
250+
auto json_rules = nlohmann::json::parse(*val);
251+
if (!json_rules.is_array()) return nullopt;
252+
std::vector<SpanSamplerConfig::Rule> rules;
253+
for (const auto& json_rule : json_rules) {
254+
auto matcher = from_json(json_rule);
255+
if (matcher.if_error()) return nullopt;
256+
SpanSamplerConfig::Rule rule{*matcher};
257+
if (auto sr = json_rule.find("sample_rate");
258+
sr != json_rule.end() && sr->is_number()) {
259+
rule.sample_rate = *sr;
260+
}
261+
if (auto mps = json_rule.find("max_per_second");
262+
mps != json_rule.end() && mps->is_number()) {
263+
rule.max_per_second = *mps;
264+
}
265+
rules.emplace_back(std::move(rule));
266+
}
267+
return rules;
268+
} catch (...) {
269+
return nullopt;
270+
}
271+
};
272+
fleet_rules =
273+
parse_span_rules(stable_configs->fleet, "DD_SPAN_SAMPLING_RULES");
274+
local_rules =
275+
parse_span_rules(stable_configs->local, "DD_SPAN_SAMPLING_RULES");
276+
}
277+
240278
std::vector<SpanSamplerConfig::Rule> rules = resolve_and_record_config(
241-
env_rules, user_rules, &result.metadata, ConfigName::SPAN_SAMPLING_RULES,
242-
nullptr, [](const std::vector<SpanSamplerConfig::Rule> &r) {
279+
fleet_rules, env_rules, user_rules, local_rules, &result.metadata,
280+
ConfigName::SPAN_SAMPLING_RULES, nullptr,
281+
[](const std::vector<SpanSamplerConfig::Rule>& r) {
243282
return to_string(r);
244283
});
245284

246-
for (const auto &rule : rules) {
285+
for (const auto& rule : rules) {
247286
auto maybe_rate = Rate::from(rule.sample_rate);
248-
if (auto *error = maybe_rate.if_error()) {
287+
if (auto* error = maybe_rate.if_error()) {
249288
std::string prefix;
250289
prefix +=
251290
"Unable to parse sample_rate in span sampling rule with span "
@@ -272,17 +311,17 @@ Expected<FinalizedSpanSamplerConfig> finalize_config(
272311
}
273312

274313
FinalizedSpanSamplerConfig::Rule finalized;
275-
static_cast<SpanMatcher &>(finalized) = rule;
314+
static_cast<SpanMatcher&>(finalized) = rule;
276315
finalized.sample_rate = *maybe_rate;
277316
finalized.max_per_second = rule.max_per_second;
278317
result.rules.push_back(std::move(finalized));
279318
}
280319
return result;
281320
}
282321

283-
std::string to_string(const FinalizedSpanSamplerConfig::Rule &rule) {
322+
std::string to_string(const FinalizedSpanSamplerConfig::Rule& rule) {
284323
// Get the base class's fields, then add our own.
285-
nlohmann::json result = static_cast<const SpanMatcher &>(rule);
324+
nlohmann::json result = static_cast<const SpanMatcher&>(rule);
286325
result["sample_rate"] = double(rule.sample_rate);
287326
if (rule.max_per_second) {
288327
result["max_per_second"] = *rule.max_per_second;

0 commit comments

Comments
 (0)