feat: support Remote Config sampling rules (#116) · DataDog/dd-trace-cpp@0ada79d
@@ -6,18 +6,81 @@
6677namespace datadog {
88namespace tracing {
9+namespace {
10+11+using Rules =
12+ std::unordered_map<SpanMatcher, TraceSamplerRate, SpanMatcher::Hash>;
13+14+Expected<Rules> parse_trace_sampling_rules(const nlohmann::json& json_rules) {
15+ Rules parsed_rules;
16+17+ std::string type = json_rules.type_name();
18+if (type != "array") {
19+ std::string message;
20+return Error{Error::TRACE_SAMPLING_RULES_WRONG_TYPE, std::move(message)};
21+ }
22+23+for (const auto& json_rule : json_rules) {
24+auto matcher = SpanMatcher::from_json(json_rule);
25+if (auto* error = matcher.if_error()) {
26+ std::string prefix;
27+return error->with_prefix(prefix);
28+ }
29+30+ TraceSamplerRate rate;
31+if (auto sample_rate = json_rule.find("sample_rate");
32+ sample_rate != json_rule.end()) {
33+ type = sample_rate->type_name();
34+if (type != "number") {
35+ std::string message;
36+return Error{Error::TRACE_SAMPLING_RULES_SAMPLE_RATE_WRONG_TYPE,
37+std::move(message)};
38+ }
39+40+auto maybe_rate = Rate::from(*sample_rate);
41+if (auto error = maybe_rate.if_error()) {
42+return *error;
43+ }
44+45+ rate.value = *maybe_rate;
46+ }
47+48+if (auto provenance_it = json_rule.find("provenance");
49+ provenance_it != json_rule.cend()) {
50+if (!provenance_it->is_string()) {
51+ std::string message;
52+return Error{Error::TRACE_SAMPLING_RULES_SAMPLE_RATE_WRONG_TYPE,
53+std::move(message)};
54+ }
55+56+auto provenance = provenance_it->get<std::string_view>();
57+if (provenance == "customer") {
58+ rate.mechanism = SamplingMechanism::REMOTE_RULE;
59+ } else if (provenance == "dynamic") {
60+ rate.mechanism = SamplingMechanism::REMOTE_ADAPTIVE_RULE;
61+ }
62+ }
63+64+ parsed_rules.emplace(std::move(*matcher), std::move(rate));
65+ }
66+67+return parsed_rules;
68+}
69+70+} // namespace
9711072ConfigManager::ConfigManager(const FinalizedTracerConfig& config)
1173 : clock_(config.clock),
1274default_metadata_(config.metadata),
1375 trace_sampler_(
1476 std::make_shared<TraceSampler>(config.trace_sampler, clock_)),
77+ rules_(config.trace_sampler.rules),
1578 span_defaults_(std::make_shared<SpanDefaults>(config.defaults)),
1679 report_traces_(config.report_traces) {}
17801881std::shared_ptr<TraceSampler> ConfigManager::trace_sampler() {
1982 std::lock_guard<std::mutex> lock(mutex_);
20-return trace_sampler_.value();
83+return trace_sampler_;
2184}
22852386std::shared_ptr<const SpanDefaults> ConfigManager::span_defaults() {
@@ -35,32 +98,48 @@ std::vector<ConfigMetadata> ConfigManager::update(const ConfigUpdate& conf) {
35983699 std::lock_guard<std::mutex> lock(mutex_);
37100101+decltype(rules_) rules;
102+38103if (!conf.trace_sampling_rate) {
39-reset_config(ConfigName::TRACE_SAMPLING_RATE, trace_sampler_, metadata);
104+auto found = default_metadata_.find(ConfigName::TRACE_SAMPLING_RATE);
105+if (found != default_metadata_.cend()) {
106+ metadata.push_back(found->second);
107+ }
40108 } else {
41109 ConfigMetadata trace_sampling_metadata(
42110 ConfigName::TRACE_SAMPLING_RATE,
43111to_string(*conf.trace_sampling_rate, 1),
44112 ConfigMetadata::Origin::REMOTE_CONFIG);
4511346- TraceSamplerConfig trace_sampler_cfg;
47- trace_sampler_cfg.sample_rate = *conf.trace_sampling_rate;
114+auto rate = Rate::from(*conf.trace_sampling_rate);
115+ rules[catch_all] = TraceSamplerRate{*rate, SamplingMechanism::RULE};
116+117+ metadata.emplace_back(std::move(trace_sampling_metadata));
118+ }
4811949-auto finalized_trace_sampler_cfg = finalize_config(trace_sampler_cfg);
50-if (auto error = finalized_trace_sampler_cfg.if_error()) {
51- trace_sampling_metadata.error = *error;
120+if (!conf.trace_sampling_rules) {
121+auto found = default_metadata_.find(ConfigName::TRACE_SAMPLING_RULES);
122+if (found != default_metadata_.cend()) {
123+ metadata.emplace_back(found->second);
52124 }
125+ } else {
126+ ConfigMetadata trace_sampling_rules_metadata(
127+ ConfigName::TRACE_SAMPLING_RULES, conf.trace_sampling_rules->dump(),
128+ ConfigMetadata::Origin::REMOTE_CONFIG);
5312954-auto trace_sampler =
55- std::make_shared<TraceSampler>(*finalized_trace_sampler_cfg, clock_);
130+auto maybe_rules = parse_trace_sampling_rules(*conf.trace_sampling_rules);
131+if (auto error = maybe_rules.if_error()) {
132+ trace_sampling_rules_metadata.error = std::move(*error);
133+ } else {
134+ rules.merge(*maybe_rules);
135+ }
5613657-// This reset rate limiting and `TraceSampler` has no `operator==`.
58-// TODO: Instead of creating another `TraceSampler`, we should
59-// update the default sampling rate.
60- trace_sampler_ = std::move(trace_sampler);
61- metadata.emplace_back(std::move(trace_sampling_metadata));
137+ metadata.emplace_back(std::move(trace_sampling_rules_metadata));
62138 }
63139140+ rules.insert(rules_.cbegin(), rules_.cend());
141+ trace_sampler_->set_rules(rules);
142+64143if (!conf.tags) {
65144reset_config(ConfigName::TAGS, span_defaults_, metadata);
66145 } else {
@@ -109,10 +188,9 @@ std::vector<ConfigMetadata> ConfigManager::reset() { return update({}); }
109188110189nlohmann::json ConfigManager::config_json() const {
111190 std::lock_guard<std::mutex> lock(mutex_);
112-return nlohmann::json{
113- {"defaults", to_json(*span_defaults_.value())},
114- {"trace_sampler", trace_sampler_.value()->config_json()},
115- {"report_traces", report_traces_.value()}};
191+return nlohmann::json{{"defaults", to_json(*span_defaults_.value())},
192+ {"trace_sampler", trace_sampler_->config_json()},
193+ {"report_traces", report_traces_.value()}};
116194}
117195118196} // namespace tracing