feat: support Remote Config sampling rules (#116) · DataDog/dd-trace-cpp@0ada79d

@@ -6,18 +6,81 @@

6677

namespace datadog {

88

namespace 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

9711072

ConfigManager::ConfigManager(const FinalizedTracerConfig& config)

1173

: clock_(config.clock),

1274

default_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) {}

17801881

std::shared_ptr<TraceSampler> ConfigManager::trace_sampler() {

1982

std::lock_guard<std::mutex> lock(mutex_);

20-

return trace_sampler_.value();

83+

return trace_sampler_;

2184

}

22852386

std::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+38103

if (!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,

43111

to_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+64143

if (!conf.tags) {

65144

reset_config(ConfigName::TAGS, span_defaults_, metadata);

66145

} else {

@@ -109,10 +188,9 @@ std::vector<ConfigMetadata> ConfigManager::reset() { return update({}); }

109188110189

nlohmann::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