-
Notifications
You must be signed in to change notification settings - Fork 19
feat: support Remote Config sampling rules #116
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
de92b21
4b87ef7
fbf61ef
0f0027d
908a852
9b2aadb
9545d64
be97f19
23bb27c
0a61f4b
3df7c9f
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -6,18 +6,81 @@ | |
|
|
||
| namespace datadog { | ||
| namespace tracing { | ||
| namespace { | ||
|
|
||
| using Rules = | ||
| std::unordered_map<SpanMatcher, TraceSamplerRate, SpanMatcher::Hash>; | ||
|
|
||
| Expected<Rules> parse_trace_sampling_rules(const nlohmann::json& json_rules) { | ||
| Rules parsed_rules; | ||
|
|
||
| std::string type = json_rules.type_name(); | ||
| if (type != "array") { | ||
| std::string message; | ||
| return Error{Error::TRACE_SAMPLING_RULES_WRONG_TYPE, std::move(message)}; | ||
| } | ||
|
|
||
| for (const auto& json_rule : json_rules) { | ||
| auto matcher = SpanMatcher::from_json(json_rule); | ||
| if (auto* error = matcher.if_error()) { | ||
| std::string prefix; | ||
| return error->with_prefix(prefix); | ||
| } | ||
|
|
||
| TraceSamplerRate rate; | ||
| if (auto sample_rate = json_rule.find("sample_rate"); | ||
| sample_rate != json_rule.end()) { | ||
| type = sample_rate->type_name(); | ||
| if (type != "number") { | ||
| std::string message; | ||
| return Error{Error::TRACE_SAMPLING_RULES_SAMPLE_RATE_WRONG_TYPE, | ||
| std::move(message)}; | ||
| } | ||
|
|
||
| auto maybe_rate = Rate::from(*sample_rate); | ||
| if (auto error = maybe_rate.if_error()) { | ||
| return *error; | ||
| } | ||
|
|
||
| rate.value = *maybe_rate; | ||
| } | ||
|
|
||
| if (auto provenance_it = json_rule.find("provenance"); | ||
| provenance_it != json_rule.cend()) { | ||
| if (!provenance_it->is_string()) { | ||
| std::string message; | ||
| return Error{Error::TRACE_SAMPLING_RULES_SAMPLE_RATE_WRONG_TYPE, | ||
| std::move(message)}; | ||
| } | ||
|
|
||
| auto provenance = provenance_it->get<std::string_view>(); | ||
| if (provenance == "customer") { | ||
| rate.mechanism = SamplingMechanism::REMOTE_RULE; | ||
| } else if (provenance == "dynamic") { | ||
| rate.mechanism = SamplingMechanism::REMOTE_ADAPTIVE_RULE; | ||
| } | ||
| } | ||
|
|
||
| parsed_rules.emplace(std::move(*matcher), std::move(rate)); | ||
| } | ||
|
|
||
| return parsed_rules; | ||
| } | ||
|
|
||
| } // namespace | ||
|
|
||
| ConfigManager::ConfigManager(const FinalizedTracerConfig& config) | ||
| : clock_(config.clock), | ||
| default_metadata_(config.metadata), | ||
| trace_sampler_( | ||
| std::make_shared<TraceSampler>(config.trace_sampler, clock_)), | ||
| rules_(config.trace_sampler.rules), | ||
| span_defaults_(std::make_shared<SpanDefaults>(config.defaults)), | ||
| report_traces_(config.report_traces) {} | ||
|
|
||
| std::shared_ptr<TraceSampler> ConfigManager::trace_sampler() { | ||
| std::lock_guard<std::mutex> lock(mutex_); | ||
| return trace_sampler_.value(); | ||
| return trace_sampler_; | ||
| } | ||
|
|
||
| std::shared_ptr<const SpanDefaults> ConfigManager::span_defaults() { | ||
|
|
@@ -35,32 +98,48 @@ std::vector<ConfigMetadata> ConfigManager::update(const ConfigUpdate& conf) { | |
|
|
||
| std::lock_guard<std::mutex> lock(mutex_); | ||
|
|
||
| decltype(rules_) rules; | ||
|
|
||
| if (!conf.trace_sampling_rate) { | ||
| reset_config(ConfigName::TRACE_SAMPLING_RATE, trace_sampler_, metadata); | ||
| auto found = default_metadata_.find(ConfigName::TRACE_SAMPLING_RATE); | ||
| if (found != default_metadata_.cend()) { | ||
| metadata.push_back(found->second); | ||
| } | ||
| } else { | ||
| ConfigMetadata trace_sampling_metadata( | ||
| ConfigName::TRACE_SAMPLING_RATE, | ||
| to_string(*conf.trace_sampling_rate, 1), | ||
| ConfigMetadata::Origin::REMOTE_CONFIG); | ||
|
|
||
| TraceSamplerConfig trace_sampler_cfg; | ||
| trace_sampler_cfg.sample_rate = *conf.trace_sampling_rate; | ||
| auto rate = Rate::from(*conf.trace_sampling_rate); | ||
| rules[catch_all] = TraceSamplerRate{*rate, SamplingMechanism::RULE}; | ||
|
|
||
| metadata.emplace_back(std::move(trace_sampling_metadata)); | ||
| } | ||
|
|
||
| auto finalized_trace_sampler_cfg = finalize_config(trace_sampler_cfg); | ||
| if (auto error = finalized_trace_sampler_cfg.if_error()) { | ||
| trace_sampling_metadata.error = *error; | ||
| if (!conf.trace_sampling_rules) { | ||
| auto found = default_metadata_.find(ConfigName::TRACE_SAMPLING_RULES); | ||
| if (found != default_metadata_.cend()) { | ||
| metadata.emplace_back(found->second); | ||
| } | ||
| } else { | ||
| ConfigMetadata trace_sampling_rules_metadata( | ||
| ConfigName::TRACE_SAMPLING_RULES, conf.trace_sampling_rules->dump(), | ||
| ConfigMetadata::Origin::REMOTE_CONFIG); | ||
|
|
||
| auto trace_sampler = | ||
| std::make_shared<TraceSampler>(*finalized_trace_sampler_cfg, clock_); | ||
| auto maybe_rules = parse_trace_sampling_rules(*conf.trace_sampling_rules); | ||
| if (auto error = maybe_rules.if_error()) { | ||
| trace_sampling_rules_metadata.error = std::move(*error); | ||
| } else { | ||
| rules.merge(*maybe_rules); | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'm looking at the docs for this and if I'm understanding correctly, items already in
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Nope, good catch. |
||
| } | ||
|
|
||
| // This reset rate limiting and `TraceSampler` has no `operator==`. | ||
| // TODO: Instead of creating another `TraceSampler`, we should | ||
| // update the default sampling rate. | ||
| trace_sampler_ = std::move(trace_sampler); | ||
| metadata.emplace_back(std::move(trace_sampling_metadata)); | ||
| metadata.emplace_back(std::move(trace_sampling_rules_metadata)); | ||
| } | ||
|
|
||
| rules.insert(rules_.cbegin(), rules_.cend()); | ||
| trace_sampler_->set_rules(rules); | ||
|
|
||
| if (!conf.tags) { | ||
| reset_config(ConfigName::TAGS, span_defaults_, metadata); | ||
| } else { | ||
|
|
@@ -109,10 +188,9 @@ std::vector<ConfigMetadata> ConfigManager::reset() { return update({}); } | |
|
|
||
| nlohmann::json ConfigManager::config_json() const { | ||
| std::lock_guard<std::mutex> lock(mutex_); | ||
| return nlohmann::json{ | ||
| {"defaults", to_json(*span_defaults_.value())}, | ||
| {"trace_sampler", trace_sampler_.value()->config_json()}, | ||
| {"report_traces", report_traces_.value()}}; | ||
| return nlohmann::json{{"defaults", to_json(*span_defaults_.value())}, | ||
| {"trace_sampler", trace_sampler_->config_json()}, | ||
| {"report_traces", report_traces_.value()}}; | ||
| } | ||
|
|
||
| } // namespace tracing | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -16,7 +16,12 @@ namespace { | |
| std::string to_string(const std::vector<SpanSamplerConfig::Rule> &rules) { | ||
| nlohmann::json res; | ||
| for (const auto &r : rules) { | ||
| res.emplace_back(r.to_json()); | ||
| auto j = r.to_json(); | ||
| j["sample_rate"] = r.sample_rate; | ||
| if (r.max_per_second) { | ||
| j["max_per_second"] = *r.max_per_second; | ||
| } | ||
| res.emplace_back(std::move(j)); | ||
|
Comment on lines
+19
to
+24
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Curious to know, as they are being set in
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
|
||
| } | ||
|
|
||
| return res.dump(); | ||
|
|
||
Uh oh!
There was an error while loading. Please reload this page.