LCOV - code coverage report
Current view: top level - datadog - tracer_config.cpp (source / functions) Hit Total Coverage
Test: filtered.info Lines: 209 212 98.6 %
Date: 2024-01-03 20:30:12 Functions: 14 14 100.0 %

          Line data    Source code
       1             : #include "tracer_config.h"
       2             : 
       3             : #include <algorithm>
       4             : #include <cassert>
       5             : #include <cctype>
       6             : #include <cstddef>
       7             : #include <string>
       8             : #include <unordered_map>
       9             : #include <vector>
      10             : 
      11             : #include "cerr_logger.h"
      12             : #include "datadog_agent.h"
      13             : #include "environment.h"
      14             : #include "json.hpp"
      15             : #include "null_collector.h"
      16             : #include "parse_util.h"
      17             : #include "string_view.h"
      18             : 
      19             : namespace datadog {
      20             : namespace tracing {
      21             : namespace {
      22             : 
      23          29 : bool falsy(StringView text) {
      24          29 :   auto lower = std::string{text};
      25          29 :   to_lower(lower);
      26          58 :   return lower == "0" || lower == "false" || lower == "no";
      27          29 : }
      28             : 
      29             : // List items are separated by an optional comma (",") and any amount of
      30             : // whitespace.
      31             : // Leading and trailing whitespace is ignored.
      32          57 : std::vector<StringView> parse_list(StringView input) {
      33             :   using uchar = unsigned char;
      34             : 
      35          57 :   input = strip(input);
      36          57 :   std::vector<StringView> items;
      37          57 :   if (input.empty()) {
      38           1 :     return items;
      39             :   }
      40             : 
      41          56 :   const char *const end = input.end();
      42             : 
      43          56 :   const char *current = input.begin();
      44             :   const char *begin_delim;
      45             :   do {
      46             :     const char *begin_item =
      47         195 :         std::find_if(current, end, [](uchar ch) { return !std::isspace(ch); });
      48          87 :     begin_delim = std::find_if(begin_item, end, [](uchar ch) {
      49         356 :       return std::isspace(ch) || ch == ',';
      50             :     });
      51             : 
      52          87 :     items.emplace_back(begin_item, std::size_t(begin_delim - begin_item));
      53             : 
      54          87 :     const char *end_delim = std::find_if(
      55          46 :         begin_delim, end, [](uchar ch) { return !std::isspace(ch); });
      56             : 
      57          87 :     if (end_delim != end && *end_delim == ',') {
      58          20 :       ++end_delim;
      59             :     }
      60             : 
      61          87 :     current = end_delim;
      62          87 :   } while (begin_delim != end);
      63             : 
      64          56 :   return items;
      65           0 : }
      66             : 
      67          50 : Expected<std::vector<PropagationStyle>> parse_propagation_styles(
      68             :     StringView input) {
      69          50 :   std::vector<PropagationStyle> styles;
      70             : 
      71          63 :   const auto last_is_duplicate = [&]() -> Optional<Error> {
      72          63 :     assert(!styles.empty());
      73             : 
      74             :     const auto dupe =
      75          63 :         std::find(styles.begin(), styles.end() - 1, styles.back());
      76          63 :     if (dupe == styles.end() - 1) {
      77          60 :       return nullopt;  // no duplicate
      78             :     }
      79             : 
      80           3 :     std::string message;
      81           3 :     message += "The propagation style ";
      82           3 :     message += to_json(styles.back()).dump();
      83           3 :     message += " is duplicated in: ";
      84           3 :     append(message, input);
      85           3 :     return Error{Error::DUPLICATE_PROPAGATION_STYLE, std::move(message)};
      86           3 :   };
      87             : 
      88             :   // Style names are separated by spaces, or a comma, or some combination.
      89         110 :   for (const StringView &item : parse_list(input)) {
      90          66 :     if (const auto style = parse_propagation_style(item)) {
      91          63 :       styles.push_back(*style);
      92             :     } else {
      93           3 :       std::string message;
      94           3 :       message += "Unsupported propagation style \"";
      95           3 :       append(message, item);
      96           3 :       message += "\" in list \"";
      97           3 :       append(message, input);
      98             :       message +=
      99           3 :           "\".  The following styles are supported: Datadog, B3, tracecontext.";
     100           3 :       return Error{Error::UNKNOWN_PROPAGATION_STYLE, std::move(message)};
     101           3 :     }
     102             : 
     103          63 :     if (auto maybe_error = last_is_duplicate()) {
     104           3 :       return *maybe_error;
     105          63 :     }
     106          50 :   }
     107             : 
     108          44 :   return styles;
     109          50 : }
     110             : 
     111           7 : Expected<std::unordered_map<std::string, std::string>> parse_tags(
     112             :     StringView input) {
     113           7 :   std::unordered_map<std::string, std::string> tags;
     114             : 
     115             :   // Within a tag, the key and value are separated by a colon (":").
     116          16 :   for (const StringView &token : parse_list(input)) {
     117          11 :     const auto separator = std::find(token.begin(), token.end(), ':');
     118          11 :     if (separator == token.end()) {
     119           2 :       std::string message;
     120           2 :       message += "Unable to parse a key/value from the tag text \"";
     121           2 :       append(message, token);
     122             :       message +=
     123             :           "\" because it does not contain the separator character \":\".  "
     124           2 :           "Error occurred in list of tags \"";
     125           2 :       append(message, input);
     126           2 :       message += "\".";
     127           2 :       return Error{Error::TAG_MISSING_SEPARATOR, std::move(message)};
     128           2 :     }
     129           9 :     std::string key{token.begin(), separator};
     130           9 :     std::string value{separator + 1, token.end()};
     131             :     // If there are duplicate values, then the last one wins.
     132           9 :     tags.insert_or_assign(std::move(key), std::move(value));
     133          16 :   }
     134             : 
     135           5 :   return tags;
     136           7 : }
     137             : 
     138             : // Return a `std::vector<PropagationStyle>` parsed from the specified `env_var`.
     139             : // If `env_var` is not in the environment, return `nullopt`. If an error occurs,
     140             : // throw an `Error`.
     141        3587 : Optional<std::vector<PropagationStyle>> styles_from_env(
     142             :     environment::Variable env_var) {
     143        3587 :   const auto styles_env = lookup(env_var);
     144        3587 :   if (!styles_env) {
     145        3537 :     return {};
     146             :   }
     147             : 
     148          50 :   auto styles = parse_propagation_styles(*styles_env);
     149          50 :   if (auto *error = styles.if_error()) {
     150           6 :     std::string prefix;
     151           6 :     prefix += "Unable to parse ";
     152           6 :     append(prefix, name(env_var));
     153           6 :     prefix += " environment variable: ";
     154           6 :     throw error->with_prefix(prefix);
     155           6 :   }
     156          44 :   return *styles;
     157          50 : }
     158             : 
     159          20 : std::string json_quoted(StringView text) {
     160          20 :   std::string unquoted;
     161          20 :   assign(unquoted, text);
     162          60 :   return nlohmann::json(std::move(unquoted)).dump();
     163          20 : }
     164             : 
     165         718 : Expected<void> finalize_propagation_styles(FinalizedTracerConfig &result,
     166             :                                            const TracerConfig &config,
     167             :                                            Logger &logger) {
     168             :   namespace env = environment;
     169             :   // Print a warning if a questionable combination of environment variables is
     170             :   // defined.
     171         718 :   const auto ts = env::DD_TRACE_PROPAGATION_STYLE;
     172         718 :   const auto tse = env::DD_TRACE_PROPAGATION_STYLE_EXTRACT;
     173         718 :   const auto se = env::DD_PROPAGATION_STYLE_EXTRACT;
     174         718 :   const auto tsi = env::DD_TRACE_PROPAGATION_STYLE_INJECT;
     175         718 :   const auto si = env::DD_PROPAGATION_STYLE_INJECT;
     176             :   // clang-format off
     177             :   /*
     178             :            ts    tse   se    tsi   si
     179             :            ---   ---   ---   ---   ---
     180             :     ts  |  x     warn  warn  warn  warn
     181             :         |
     182             :     tse |  x     x     warn  ok    ok
     183             :         |
     184             :     se  |  x     x     x     ok    ok
     185             :         |
     186             :     tsi |  x     x     x     x     warn
     187             :         |
     188             :     si  |  x     x     x     x     x
     189             :   */
     190             :   // In each pair, the first would be overridden by the second.
     191         718 :   const std::pair<env::Variable, env::Variable> questionable_combinations[] = {
     192             :            {ts, tse}, {ts, se},  {ts, tsi}, {ts, si},
     193             : 
     194             :                       {se, tse}, /* ok */   /* ok */
     195             : 
     196             :                                  /* ok */   /* ok */
     197             : 
     198             :                                             {si, tsi},
     199             :   };
     200             :   // clang-format on
     201             : 
     202          10 :   const auto warn_message = [](StringView name, StringView value,
     203             :                                StringView name_override,
     204             :                                StringView value_override) {
     205          10 :     std::string message;
     206          10 :     message += "Both the environment variables ";
     207          10 :     append(message, name);
     208          10 :     message += "=";
     209          10 :     message += json_quoted(value);
     210          10 :     message += " and ";
     211          10 :     append(message, name_override);
     212          10 :     message += "=";
     213          10 :     message += json_quoted(value_override);
     214          10 :     message += " are defined. ";
     215          10 :     append(message, name_override);
     216          10 :     message += " will take precedence.";
     217          10 :     return message;
     218           0 :   };
     219             : 
     220        5026 :   for (const auto &[var, var_override] : questionable_combinations) {
     221        4308 :     const auto value = lookup(var);
     222        4308 :     if (!value) {
     223        4298 :       continue;
     224             :     }
     225          39 :     const auto value_override = lookup(var_override);
     226          39 :     if (!value_override) {
     227          29 :       continue;
     228             :     }
     229          10 :     const auto var_name = name(var);
     230          10 :     const auto var_name_override = name(var_override);
     231          10 :     logger.log_error(Error{
     232             :         Error::MULTIPLE_PROPAGATION_STYLE_ENVIRONMENT_VARIABLES,
     233          10 :         warn_message(var_name, *value, var_name_override, *value_override)});
     234             :   }
     235             : 
     236             :   // Parse the propagation styles from the configuration and/or from the
     237             :   // environment.
     238             :   // Exceptions make this section simpler.
     239             :   try {
     240         718 :     const auto global_styles = styles_from_env(env::DD_TRACE_PROPAGATION_STYLE);
     241             :     result.extraction_styles =
     242        1434 :         value_or(styles_from_env(env::DD_TRACE_PROPAGATION_STYLE_EXTRACT),
     243         717 :                  styles_from_env(env::DD_PROPAGATION_STYLE_EXTRACT),
     244        1435 :                  global_styles, config.extraction_styles);
     245             :     result.injection_styles =
     246        1429 :         value_or(styles_from_env(env::DD_TRACE_PROPAGATION_STYLE_INJECT),
     247         722 :                  styles_from_env(env::DD_PROPAGATION_STYLE_INJECT),
     248        1429 :                  global_styles, config.injection_styles);
     249         724 :   } catch (Error &error) {
     250           6 :     return std::move(error);
     251           6 :   }
     252             : 
     253         712 :   if (result.extraction_styles.empty()) {
     254           2 :     return Error{Error::MISSING_SPAN_EXTRACTION_STYLE,
     255           1 :                  "At least one extraction style must be specified."};
     256             :   }
     257         711 :   if (result.injection_styles.empty()) {
     258           2 :     return Error{Error::MISSING_SPAN_INJECTION_STYLE,
     259           1 :                  "At least one injection style must be specified."};
     260             :   }
     261             : 
     262         710 :   return {};
     263             : }
     264             : 
     265             : }  // namespace
     266             : 
     267         785 : Expected<FinalizedTracerConfig> finalize_config(const TracerConfig &config) {
     268         785 :   return finalize_config(config, default_clock);
     269             : }
     270             : 
     271         796 : Expected<FinalizedTracerConfig> finalize_config(const TracerConfig &config,
     272             :                                                 const Clock &clock) {
     273         796 :   FinalizedTracerConfig result;
     274             : 
     275         796 :   result.clock = clock;
     276         796 :   result.defaults = config.defaults;
     277             : 
     278         796 :   if (auto service_env = lookup(environment::DD_SERVICE)) {
     279           1 :     assign(result.defaults.service, *service_env);
     280             :   }
     281         796 :   if (result.defaults.service.empty()) {
     282           1 :     return Error{Error::SERVICE_NAME_REQUIRED, "Service name is required."};
     283             :   }
     284             : 
     285         795 :   if (auto environment_env = lookup(environment::DD_ENV)) {
     286           1 :     assign(result.defaults.environment, *environment_env);
     287             :   }
     288         795 :   if (auto version_env = lookup(environment::DD_VERSION)) {
     289           1 :     assign(result.defaults.version, *version_env);
     290             :   }
     291             : 
     292         795 :   if (auto tags_env = lookup(environment::DD_TAGS)) {
     293           7 :     auto tags = parse_tags(*tags_env);
     294           7 :     if (auto *error = tags.if_error()) {
     295           2 :       std::string prefix;
     296           2 :       prefix += "Unable to parse ";
     297           2 :       append(prefix, name(environment::DD_TAGS));
     298           2 :       prefix += " environment variable: ";
     299           2 :       return error->with_prefix(prefix);
     300           2 :     }
     301           5 :     result.defaults.tags = std::move(*tags);
     302           7 :   }
     303             : 
     304         793 :   if (config.logger) {
     305         619 :     result.logger = config.logger;
     306             :   } else {
     307         174 :     result.logger = std::make_shared<CerrLogger>();
     308             :   }
     309             : 
     310         793 :   result.log_on_startup = config.log_on_startup;
     311         793 :   if (auto startup_env = lookup(environment::DD_TRACE_STARTUP_LOGS)) {
     312           9 :     result.log_on_startup = !falsy(*startup_env);
     313             :   }
     314             : 
     315         793 :   bool report_traces = config.report_traces;
     316         793 :   if (auto enabled_env = lookup(environment::DD_TRACE_ENABLED)) {
     317           8 :     report_traces = !falsy(*enabled_env);
     318             :   }
     319             : 
     320         793 :   if (!report_traces) {
     321           5 :     result.collector = std::make_shared<NullCollector>();
     322         788 :   } else if (!config.collector) {
     323         619 :     auto finalized = finalize_config(config.agent, result.logger, clock);
     324         619 :     if (auto *error = finalized.if_error()) {
     325           8 :       return std::move(*error);
     326             :     }
     327         611 :     result.collector = *finalized;
     328         619 :   } else {
     329         169 :     result.collector = config.collector;
     330             :   }
     331             : 
     332         785 :   bool report_telemetry = config.report_telemetry;
     333         785 :   if (auto enabled_env =
     334         785 :           lookup(environment::DD_INSTRUMENTATION_TELEMETRY_ENABLED)) {
     335           0 :     report_telemetry = !falsy(*enabled_env);
     336             :   }
     337         785 :   result.report_telemetry = report_telemetry;
     338             : 
     339         785 :   if (auto trace_sampler_config = finalize_config(config.trace_sampler)) {
     340         743 :     result.trace_sampler = std::move(*trace_sampler_config);
     341             :   } else {
     342          42 :     return std::move(trace_sampler_config.error());
     343         785 :   }
     344             : 
     345         743 :   if (auto span_sampler_config =
     346         743 :           finalize_config(config.span_sampler, *result.logger)) {
     347         718 :     result.span_sampler = std::move(*span_sampler_config);
     348             :   } else {
     349          25 :     return std::move(span_sampler_config.error());
     350         743 :   }
     351             : 
     352             :   auto maybe_error =
     353         718 :       finalize_propagation_styles(result, config, *result.logger);
     354         718 :   if (!maybe_error) {
     355           8 :     return maybe_error.error();
     356             :   }
     357             : 
     358         710 :   result.report_hostname = config.report_hostname;
     359         710 :   result.tags_header_size = config.tags_header_size;
     360             : 
     361         710 :   if (auto enabled_env =
     362         710 :           lookup(environment::DD_TRACE_128_BIT_TRACEID_GENERATION_ENABLED)) {
     363          12 :     result.trace_id_128_bit = !falsy(*enabled_env);
     364             :   } else {
     365         698 :     result.trace_id_128_bit = config.trace_id_128_bit;
     366             :   }
     367             : 
     368         710 :   result.runtime_id = config.runtime_id;
     369             : 
     370         710 :   return result;
     371         796 : }
     372             : 
     373             : }  // namespace tracing
     374             : }  // namespace datadog

Generated by: LCOV version 1.16