11#include < datadog/environment.h>
2+ #include < datadog/logger.h>
23#include < datadog/trace_sampler_config.h>
34#include < datadog/trace_source.h>
45
78
89#include " json.hpp"
910#include " json_serializer.h"
11+ #include " null_logger.h"
1012#include " parse_util.h"
1113#include " stable_config.h"
1214#include " string_util.h"
@@ -156,20 +158,74 @@ Optional<double> stable_config_double(const StableConfig &cfg,
156158 return *result;
157159}
158160
161+ // Parse a stable config JSON string as an array of sampling rules.
162+ // `customize_rule` is a callable that receives (Rule&, const json_rule&) to set
163+ // rule-specific fields beyond the base matcher and sample_rate.
164+ // Returns nullopt on any parse error (stable config errors are non-fatal).
165+ template <typename Rule, typename Json, typename Customize>
166+ Optional<std::vector<Rule>> parse_stable_config_rules (
167+ const StableConfig &cfg, const std::string &key, Logger &logger,
168+ Customize customize_rule) {
169+ auto val = cfg.lookup (key);
170+ if (!val || val->empty ()) return nullopt ;
171+
172+ try {
173+ auto json_rules = Json::parse (*val);
174+ if (!json_rules.is_array ()) {
175+ logger.log_error ([&key](std::ostream &log) {
176+ log << " Unable to parse JSON sampling rules from " << key
177+ << " : expected a JSON array" ;
178+ });
179+ return nullopt ;
180+ }
181+
182+ std::vector<Rule> rules;
183+ for (const auto &json_rule : json_rules) {
184+ auto matcher = from_json (json_rule);
185+ if (matcher.if_error ()) {
186+ logger.log_error ([&key](std::ostream &log) {
187+ log << " Unable to parse JSON sampling rules from " << key
188+ << " : invalid rule matcher" ;
189+ });
190+ return nullopt ;
191+ }
192+
193+ Rule rule{*matcher};
194+ if (auto sr = json_rule.find (" sample_rate" );
195+ sr != json_rule.end () && sr->is_number ()) {
196+ rule.sample_rate = *sr;
197+ }
198+ customize_rule (rule, json_rule);
199+ rules.emplace_back (std::move (rule));
200+ }
201+ return rules;
202+ } catch (...) {
203+ logger.log_error ([&key](std::ostream &log) {
204+ log << " Unable to parse JSON sampling rules from " << key;
205+ });
206+ return nullopt ;
207+ }
208+ }
209+
159210// Try to parse a stable config string value as trace sampling rules JSON.
160- // Returns empty vector on any parse error (stable config errors are non-fatal).
211+ // Returns nullopt on any parse error (stable config errors are non-fatal).
161212Optional<std::vector<TraceSamplerConfig::Rule>> stable_config_sampling_rules (
162- const StableConfig &cfg, const std::string &key) {
213+ const StableConfig &cfg, const std::string &key, Logger &logger ) {
163214 return parse_stable_config_rules<TraceSamplerConfig::Rule, nlohmann::json>(
164- cfg, key, [](TraceSamplerConfig::Rule &, const nlohmann::json &) {});
215+ cfg, key, logger,
216+ [](TraceSamplerConfig::Rule &, const nlohmann::json &) {});
165217}
166218
167219} // namespace
168220
169221TraceSamplerConfig::Rule::Rule (const SpanMatcher &base) : SpanMatcher(base) {}
170222
171223Expected<FinalizedTraceSamplerConfig> finalize_config (
172- const TraceSamplerConfig &config, const StableConfigs *stable_configs) {
224+ const TraceSamplerConfig &config, const StableConfigs *stable_configs,
225+ Logger *logger) {
226+ NullLogger null_logger;
227+ Logger &log = logger ? *logger : static_cast <Logger &>(null_logger);
228+
173229 Expected<TraceSamplerConfig> env_config = load_trace_sampler_env_config ();
174230 if (auto error = env_config.if_error ()) {
175231 return *error;
@@ -184,9 +240,9 @@ Expected<FinalizedTraceSamplerConfig> finalize_config(
184240 Optional<std::vector<TraceSamplerConfig::Rule>> local_rules;
185241 if (stable_configs) {
186242 fleet_rules = stable_config_sampling_rules (stable_configs->fleet ,
187- " DD_TRACE_SAMPLING_RULES" );
243+ " DD_TRACE_SAMPLING_RULES" , log );
188244 local_rules = stable_config_sampling_rules (stable_configs->local ,
189- " DD_TRACE_SAMPLING_RULES" );
245+ " DD_TRACE_SAMPLING_RULES" , log );
190246 }
191247
192248 if (fleet_rules) {
0 commit comments