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
|