LCOV - code coverage report
Current view: top level - datadog - trace_sampler_config.cpp (source / functions) Hit Total Coverage
Test: filtered.info Lines: 144 145 99.3 %
Date: 2024-01-03 20:30:12 Functions: 3 3 100.0 %

          Line data    Source code
       1             : #include "trace_sampler_config.h"
       2             : 
       3             : #include <cmath>
       4             : #include <unordered_set>
       5             : 
       6             : #include "environment.h"
       7             : #include "json.hpp"
       8             : #include "parse_util.h"
       9             : 
      10             : namespace datadog {
      11             : namespace tracing {
      12             : 
      13           4 : TraceSamplerConfig::Rule::Rule(const SpanMatcher &base) : SpanMatcher(base) {}
      14             : 
      15         787 : Expected<FinalizedTraceSamplerConfig> finalize_config(
      16             :     const TraceSamplerConfig &config) {
      17         787 :   FinalizedTraceSamplerConfig result;
      18             : 
      19         787 :   std::vector<TraceSamplerConfig::Rule> rules = config.rules;
      20             : 
      21         787 :   if (auto rules_env = lookup(environment::DD_TRACE_SAMPLING_RULES)) {
      22          12 :     rules.clear();
      23          12 :     nlohmann::json json_rules;
      24             :     try {
      25          14 :       json_rules = nlohmann::json::parse(*rules_env);
      26           2 :     } catch (const nlohmann::json::parse_error &error) {
      27           2 :       std::string message;
      28           2 :       message += "Unable to parse JSON from ";
      29           2 :       append(message, name(environment::DD_TRACE_SAMPLING_RULES));
      30           2 :       message += " value ";
      31           2 :       append(message, *rules_env);
      32           2 :       message += ": ";
      33           2 :       message += error.what();
      34           4 :       return Error{Error::TRACE_SAMPLING_RULES_INVALID_JSON,
      35           4 :                    std::move(message)};
      36           2 :     }
      37             : 
      38          10 :     std::string type = json_rules.type_name();
      39          10 :     if (type != "array") {
      40           1 :       std::string message;
      41           1 :       message += "Trace sampling rules must be an array, but ";
      42           1 :       append(message, name(environment::DD_TRACE_SAMPLING_RULES));
      43           1 :       message += " has JSON type \"";
      44           1 :       message += type;
      45           1 :       message += "\": ";
      46           1 :       append(message, *rules_env);
      47           1 :       return Error{Error::TRACE_SAMPLING_RULES_WRONG_TYPE, std::move(message)};
      48           1 :     }
      49             : 
      50             :     const std::unordered_set<std::string> allowed_properties{
      51          72 :         "service", "name", "resource", "tags", "sample_rate"};
      52             : 
      53          11 :     for (const auto &json_rule : json_rules) {
      54          10 :       auto matcher = SpanMatcher::from_json(json_rule);
      55          10 :       if (auto *error = matcher.if_error()) {
      56           6 :         std::string prefix;
      57           6 :         prefix += "Unable to create a rule from ";
      58           6 :         append(prefix, name(environment::DD_TRACE_SAMPLING_RULES));
      59           6 :         prefix += " value ";
      60           6 :         append(prefix, *rules_env);
      61           6 :         prefix += ": ";
      62           6 :         return error->with_prefix(prefix);
      63           6 :       }
      64             : 
      65           4 :       TraceSamplerConfig::Rule rule{*matcher};
      66             : 
      67           4 :       auto sample_rate = json_rule.find("sample_rate");
      68           4 :       if (sample_rate != json_rule.end()) {
      69           2 :         type = sample_rate->type_name();
      70           2 :         if (type != "number") {
      71           1 :           std::string message;
      72           1 :           message += "Unable to parse a rule from ";
      73           1 :           append(message, name(environment::DD_TRACE_SAMPLING_RULES));
      74           1 :           message += " value ";
      75           1 :           append(message, *rules_env);
      76           1 :           message += ".  The \"sample_rate\" property of the rule ";
      77           1 :           message += json_rule.dump();
      78           1 :           message += " is not a number, but instead has type \"";
      79           1 :           message += type;
      80           1 :           message += "\".";
      81           2 :           return Error{Error::TRACE_SAMPLING_RULES_SAMPLE_RATE_WRONG_TYPE,
      82           2 :                        std::move(message)};
      83           1 :         }
      84           1 :         rule.sample_rate = *sample_rate;
      85             :       }
      86             : 
      87             :       // Look for unexpected properties.
      88           8 :       for (const auto &[key, value] : json_rule.items()) {
      89           6 :         if (allowed_properties.count(key)) {
      90           5 :           continue;
      91             :         }
      92           1 :         std::string message;
      93           1 :         message += "Unexpected property \"";
      94           1 :         message += key;
      95           1 :         message += "\" having value ";
      96           1 :         message += value.dump();
      97           1 :         message += " in trace sampling rule ";
      98           1 :         message += json_rule.dump();
      99           1 :         message += ".  Error occurred while parsing ";
     100           1 :         append(message, name(environment::DD_TRACE_SAMPLING_RULES));
     101           1 :         message += ": ";
     102           1 :         append(message, *rules_env);
     103           2 :         return Error{Error::TRACE_SAMPLING_RULES_UNKNOWN_PROPERTY,
     104           2 :                      std::move(message)};
     105           5 :       }
     106             : 
     107           2 :       rules.emplace_back(std::move(rule));
     108          12 :     }
     109          29 :   }
     110             : 
     111         787 :   for (const auto &rule : rules) {
     112          17 :     auto maybe_rate = Rate::from(rule.sample_rate);
     113          17 :     if (auto *error = maybe_rate.if_error()) {
     114           6 :       std::string prefix;
     115             :       prefix +=
     116             :           "Unable to parse sample_rate in trace sampling rule with root span "
     117           6 :           "pattern ";
     118           6 :       prefix += rule.to_json().dump();
     119           6 :       prefix += ": ";
     120           6 :       return error->with_prefix(prefix);
     121           6 :     }
     122             : 
     123          11 :     FinalizedTraceSamplerConfig::Rule finalized;
     124          11 :     static_cast<SpanMatcher &>(finalized) = rule;
     125          11 :     finalized.sample_rate = *maybe_rate;
     126          11 :     result.rules.push_back(std::move(finalized));
     127          17 :   }
     128             : 
     129         770 :   auto sample_rate = config.sample_rate;
     130         770 :   if (auto sample_rate_env = lookup(environment::DD_TRACE_SAMPLE_RATE)) {
     131          12 :     auto maybe_sample_rate = parse_double(*sample_rate_env);
     132          12 :     if (auto *error = maybe_sample_rate.if_error()) {
     133           8 :       std::string prefix;
     134           8 :       prefix += "While parsing ";
     135           8 :       append(prefix, name(environment::DD_TRACE_SAMPLE_RATE));
     136           8 :       prefix += ": ";
     137           8 :       return error->with_prefix(prefix);
     138           8 :     }
     139           4 :     sample_rate = *maybe_sample_rate;
     140          12 :   }
     141             : 
     142             :   // If `sample_rate` was specified, then it translates to a "catch-all" rule
     143             :   // appended to the end of `rules`.  First, though, we have to make sure the
     144             :   // sample rate is valid.
     145         762 :   if (sample_rate) {
     146          59 :     auto maybe_rate = Rate::from(*sample_rate);
     147          59 :     if (auto *error = maybe_rate.if_error()) {
     148           4 :       return error->with_prefix(
     149           2 :           "Unable to parse overall sample_rate for trace sampling: ");
     150             :     }
     151             : 
     152          57 :     FinalizedTraceSamplerConfig::Rule catch_all;
     153          57 :     catch_all.sample_rate = *maybe_rate;
     154          57 :     result.rules.push_back(std::move(catch_all));
     155          59 :   }
     156             : 
     157         760 :   auto max_per_second = config.max_per_second;
     158         760 :   if (auto limit_env = lookup(environment::DD_TRACE_RATE_LIMIT)) {
     159          11 :     auto maybe_max_per_second = parse_double(*limit_env);
     160          11 :     if (auto *error = maybe_max_per_second.if_error()) {
     161           8 :       std::string prefix;
     162           8 :       prefix += "While parsing ";
     163           8 :       append(prefix, name(environment::DD_TRACE_RATE_LIMIT));
     164           8 :       prefix += ": ";
     165           8 :       return error->with_prefix(prefix);
     166           8 :     }
     167           3 :     max_per_second = *maybe_max_per_second;
     168          11 :   }
     169             : 
     170         752 :   const auto allowed_types = {FP_NORMAL, FP_SUBNORMAL};
     171        1498 :   if (!(max_per_second > 0) ||
     172         746 :       std::find(std::begin(allowed_types), std::end(allowed_types),
     173        2244 :                 std::fpclassify(max_per_second)) == std::end(allowed_types)) {
     174           7 :     std::string message;
     175             :     message +=
     176             :         "Trace sampling max_per_second must be greater than zero, but the "
     177           7 :         "following value was given: ";
     178           7 :     message += std::to_string(config.max_per_second);
     179           7 :     return Error{Error::MAX_PER_SECOND_OUT_OF_RANGE, std::move(message)};
     180           7 :   }
     181         745 :   result.max_per_second = max_per_second;
     182             : 
     183         745 :   return result;
     184         787 : }
     185             : 
     186          17 : nlohmann::json to_json(const FinalizedTraceSamplerConfig::Rule &rule) {
     187             :   // Get the base class's fields, then add our own.
     188          17 :   auto result = static_cast<const SpanMatcher &>(rule).to_json();
     189          17 :   result["sample_rate"] = double(rule.sample_rate);
     190          17 :   return result;
     191           0 : }
     192             : 
     193             : }  // namespace tracing
     194             : }  // namespace datadog

Generated by: LCOV version 1.16