LCOV - code coverage report
Current view: top level - datadog - tracer.cpp (source / functions) Hit Total Coverage
Test: filtered.info Lines: 179 182 98.4 %
Date: 2024-01-03 20:30:12 Functions: 13 13 100.0 %

          Line data    Source code
       1             : #include "tracer.h"
       2             : 
       3             : #include <algorithm>
       4             : #include <cassert>
       5             : 
       6             : #include "datadog/runtime_id.h"
       7             : #include "datadog/trace_sampler_config.h"
       8             : #include "datadog_agent.h"
       9             : #include "dict_reader.h"
      10             : #include "environment.h"
      11             : #include "extracted_data.h"
      12             : #include "extraction_util.h"
      13             : #include "hex.h"
      14             : #include "json.hpp"
      15             : #include "logger.h"
      16             : #include "parse_util.h"
      17             : #include "platform_util.h"
      18             : #include "span.h"
      19             : #include "span_config.h"
      20             : #include "span_data.h"
      21             : #include "span_sampler.h"
      22             : #include "tag_propagation.h"
      23             : #include "tags.h"
      24             : #include "trace_sampler.h"
      25             : #include "trace_segment.h"
      26             : #include "tracer_signature.h"
      27             : #include "version.h"
      28             : #include "w3c_propagation.h"
      29             : 
      30             : namespace datadog {
      31             : namespace tracing {
      32             : 
      33         589 : Tracer::Tracer(const FinalizedTracerConfig& config)
      34         589 :     : Tracer(config, default_id_generator(config.trace_id_128_bit)) {}
      35             : 
      36         603 : Tracer::Tracer(const FinalizedTracerConfig& config,
      37         603 :                const std::shared_ptr<const IDGenerator>& generator)
      38         603 :     : logger_(config.logger),
      39         603 :       collector_(/* see constructor body */),
      40         603 :       defaults_(std::make_shared<SpanDefaults>(config.defaults)),
      41         603 :       runtime_id_(config.runtime_id ? *config.runtime_id
      42         603 :                                     : RuntimeID::generate()),
      43         603 :       signature_{runtime_id_, config.defaults.service,
      44         603 :                  config.defaults.environment},
      45         603 :       tracer_telemetry_(std::make_shared<TracerTelemetry>(
      46         603 :           config.report_telemetry, config.clock, logger_, signature_)),
      47         603 :       span_sampler_(
      48         603 :           std::make_shared<SpanSampler>(config.span_sampler, config.clock)),
      49         603 :       generator_(generator),
      50         603 :       clock_(config.clock),
      51         603 :       injection_styles_(config.injection_styles),
      52         603 :       extraction_styles_(config.extraction_styles),
      53         603 :       hostname_(config.report_hostname ? get_hostname() : nullopt),
      54         603 :       tags_header_max_size_(config.tags_header_size),
      55         603 :       config_manager_(config) {
      56         603 :   if (auto* collector =
      57         603 :           std::get_if<std::shared_ptr<Collector>>(&config.collector)) {
      58         174 :     collector_ = *collector;
      59             :   } else {
      60             :     auto& agent_config =
      61         429 :         std::get<FinalizedDatadogAgentConfig>(config.collector);
      62             : 
      63         429 :     auto agent = std::make_shared<DatadogAgent>(agent_config, tracer_telemetry_,
      64         429 :                                                 config.logger, signature_,
      65         429 :                                                 config_manager_);
      66         429 :     collector_ = agent;
      67             : 
      68         429 :     if (tracer_telemetry_->enabled()) {
      69          18 :       agent->send_app_started();
      70             :     }
      71         429 :   }
      72             : 
      73         603 :   if (config.log_on_startup) {
      74         598 :     logger_->log_startup([this](std::ostream& log) {
      75         503 :       log << "DATADOG TRACER CONFIGURATION - " << config_json();
      76         503 :     });
      77             :   }
      78         603 : }
      79             : 
      80         503 : nlohmann::json Tracer::config_json() const {
      81             :   // clang-format off
      82       14084 :   auto config = nlohmann::json::object({
      83             :     {"version", tracer_version_string},
      84        1006 :     {"defaults", to_json(*defaults_)},
      85         503 :     {"runtime_id", runtime_id_.string()},
      86        1006 :     {"collector", collector_->config_json()},
      87        1006 :     {"span_sampler", span_sampler_->config_json()},
      88        1006 :     {"injection_styles", to_json(injection_styles_)},
      89        1006 :     {"extraction_styles", to_json(extraction_styles_)},
      90         503 :     {"tags_header_size", tags_header_max_size_},
      91        1006 :     {"environment_variables", environment::to_json()},
      92        8551 :   });
      93             :   // clang-format on
      94             : 
      95         503 :   config.merge_patch(config_manager_.config_json());
      96             : 
      97         503 :   if (hostname_) {
      98           2 :     config["hostname"] = *hostname_;
      99             :   }
     100             : 
     101         503 :   return config;
     102           0 : }
     103             : 
     104      165580 : Span Tracer::create_span() { return create_span(SpanConfig{}); }
     105             : 
     106       82797 : Span Tracer::create_span(const SpanConfig& config) {
     107       82797 :   auto span_data = std::make_unique<SpanData>();
     108       82797 :   span_data->apply_config(*defaults_, config, clock_);
     109       82797 :   span_data->trace_id = generator_->trace_id(span_data->start);
     110       82797 :   span_data->span_id = span_data->trace_id.low;
     111       82797 :   span_data->parent_id = 0;
     112             : 
     113       82797 :   std::vector<std::pair<std::string, std::string>> trace_tags;
     114       82797 :   if (span_data->trace_id.high) {
     115       82792 :     trace_tags.emplace_back(tags::internal::trace_id_high,
     116      165584 :                             hex_padded(span_data->trace_id.high));
     117             :   }
     118             : 
     119       82797 :   const auto span_data_ptr = span_data.get();
     120       82797 :   tracer_telemetry_->metrics().tracer.trace_segments_created_new.inc();
     121             :   const auto segment = std::make_shared<TraceSegment>(
     122       82797 :       logger_, collector_, tracer_telemetry_,
     123           0 :       config_manager_.get_trace_sampler(), span_sampler_, defaults_,
     124       82797 :       runtime_id_, injection_styles_, hostname_, nullopt /* origin */,
     125       82797 :       tags_header_max_size_, std::move(trace_tags),
     126             :       nullopt /* sampling_decision */, nullopt /* additional_w3c_tracestate */,
     127      165594 :       nullopt /* additional_datadog_w3c_tracestate*/, std::move(span_data));
     128             :   Span span{span_data_ptr, segment,
     129          21 :             [generator = generator_]() { return generator->span_id(); },
     130      165594 :             clock_};
     131      165594 :   return span;
     132       82797 : }
     133             : 
     134         102 : Expected<Span> Tracer::extract_span(const DictReader& reader) {
     135         204 :   return extract_span(reader, SpanConfig{});
     136             : }
     137             : 
     138         113 : Expected<Span> Tracer::extract_span(const DictReader& reader,
     139             :                                     const SpanConfig& config) {
     140         113 :   assert(!extraction_styles_.empty());
     141             : 
     142         113 :   AuditedReader audited_reader{reader};
     143             : 
     144         113 :   auto span_data = std::make_unique<SpanData>();
     145         113 :   std::vector<ExtractedData> extracted_contexts;
     146             : 
     147         270 :   for (const auto style : extraction_styles_) {
     148             :     using Extractor = decltype(&extract_datadog);  // function pointer
     149             :     Extractor extract;
     150         181 :     switch (style) {
     151          89 :       case PropagationStyle::DATADOG:
     152          89 :         extract = &extract_datadog;
     153          89 :         break;
     154          33 :       case PropagationStyle::B3:
     155          33 :         extract = &extract_b3;
     156          33 :         break;
     157          58 :       case PropagationStyle::W3C:
     158          58 :         extract = &extract_w3c;
     159          58 :         break;
     160           1 :       default:
     161           1 :         assert(style == PropagationStyle::NONE);
     162           1 :         extract = &extract_none;
     163             :     }
     164         181 :     audited_reader.entries_found.clear();
     165         181 :     auto data = extract(audited_reader, span_data->tags, *logger_);
     166         181 :     if (auto* error = data.if_error()) {
     167          48 :       return error->with_prefix(
     168          72 :           extraction_error_prefix(style, audited_reader.entries_found));
     169             :     }
     170         157 :     extracted_contexts.push_back(std::move(*data));
     171         157 :     extracted_contexts.back().headers_examined = audited_reader.entries_found;
     172         181 :   }
     173             : 
     174             :   auto [trace_id, parent_id, origin, trace_tags, sampling_priority,
     175             :         additional_w3c_tracestate, additional_datadog_w3c_tracestate, style,
     176          89 :         headers_examined] = merge(extracted_contexts);
     177             : 
     178             :   // Some information might be missing.
     179             :   // Here are the combinations considered:
     180             :   //
     181             :   // - no trace ID and no parent ID
     182             :   //     - this means there's no span to extract
     183             :   // - parent ID and no trace ID
     184             :   //     - error
     185             :   // - trace ID and no parent ID
     186             :   //     - if origin is set, then we're extracting a root span
     187             :   //         - the idea is that "synthetics" might have started a trace without
     188             :   //           producing a root span
     189             :   //     - if origin is _not_ set, then it's an error
     190             :   // - trace ID and parent ID means we're extracting a child span
     191             :   // - if trace ID is zero, then that's an error.
     192             : 
     193          89 :   if (!trace_id && !parent_id) {
     194           6 :     return Error{Error::NO_SPAN_TO_EXTRACT,
     195             :                  "There's neither a trace ID nor a parent span ID to extract."}
     196           3 :         .with_prefix(extraction_error_prefix(style, headers_examined));
     197             :   }
     198          86 :   if (!trace_id) {
     199           2 :     std::string message;
     200             :     message +=
     201           2 :         "There's no trace ID to extract, but there is a parent span ID: ";
     202           2 :     message += std::to_string(*parent_id);
     203           6 :     return Error{Error::MISSING_TRACE_ID, std::move(message)}.with_prefix(
     204           6 :         extraction_error_prefix(style, headers_examined));
     205           2 :   }
     206          84 :   if (!parent_id && !origin) {
     207           2 :     std::string message;
     208             :     message +=
     209           2 :         "There's no parent span ID to extract, but there is a trace ID: ";
     210           2 :     message += "[hexadecimal = ";
     211           2 :     message += trace_id->hex_padded();
     212           2 :     if (trace_id->high == 0) {
     213           2 :       message += ", decimal = ";
     214           2 :       message += std::to_string(trace_id->low);
     215             :     }
     216           2 :     message += ']';
     217           6 :     return Error{Error::MISSING_PARENT_SPAN_ID, std::move(message)}.with_prefix(
     218           6 :         extraction_error_prefix(style, headers_examined));
     219           2 :   }
     220             : 
     221          82 :   if (!parent_id) {
     222             :     // We have a trace ID, but not parent ID.  We're meant to be the root, and
     223             :     // whoever called us already created a trace ID for us (to correlate with
     224             :     // whatever they're doing).
     225           6 :     parent_id = 0;
     226             :   }
     227             : 
     228          82 :   assert(parent_id);
     229          82 :   assert(trace_id);
     230             : 
     231          82 :   if (*trace_id == 0) {
     232           8 :     return Error{Error::ZERO_TRACE_ID,
     233             :                  "extracted zero value for trace ID, which is invalid"}
     234           4 :         .with_prefix(extraction_error_prefix(style, headers_examined));
     235             :   }
     236             : 
     237             :   // We're done extracting fields.  Now create the span.
     238             :   // This is similar to what we do in `create_span`.
     239          78 :   span_data->apply_config(*defaults_, config, clock_);
     240          78 :   span_data->span_id = generator_->span_id();
     241          78 :   span_data->trace_id = *trace_id;
     242          78 :   span_data->parent_id = *parent_id;
     243             : 
     244          78 :   if (span_data->trace_id.high) {
     245             :     // The trace ID has some bits set in the higher 64 bits. Set the
     246             :     // corresponding `trace_id_high` tag, so that the Datadog backend is aware
     247             :     // of those bits.
     248             :     //
     249             :     // First, though, if the `trace_id_high` tag is already set and has a
     250             :     // bogus value or a value inconsistent with the trace ID, tag an error.
     251          15 :     const auto hex_high = hex_padded(span_data->trace_id.high);
     252          15 :     const auto extant = std::find_if(
     253           7 :         trace_tags.begin(), trace_tags.end(), [&](const auto& pair) {
     254           7 :           return pair.first == tags::internal::trace_id_high;
     255             :         });
     256          15 :     if (extant == trace_tags.end()) {
     257           8 :       trace_tags.emplace_back(tags::internal::trace_id_high, hex_high);
     258             :     } else {
     259             :       // There is already a `trace_id_high` tag. `hex_high` is its proper
     260             :       // value. Check if the extant value is malformed or different from
     261             :       // `hex_high`. In either case, tag an error and overwrite the tag with
     262             :       // `hex_high`.
     263           7 :       const Optional<std::uint64_t> high = parse_trace_id_high(extant->second);
     264           7 :       if (!high) {
     265           4 :         span_data->tags[tags::internal::propagation_error] =
     266           8 :             "malformed_tid " + extant->second;
     267           4 :         extant->second = hex_high;
     268           3 :       } else if (*high != span_data->trace_id.high) {
     269           2 :         span_data->tags[tags::internal::propagation_error] =
     270           4 :             "inconsistent_tid " + extant->second;
     271           2 :         extant->second = hex_high;
     272             :       }
     273             :     }
     274          15 :   }
     275             : 
     276          78 :   Optional<SamplingDecision> sampling_decision;
     277          78 :   if (sampling_priority) {
     278          43 :     SamplingDecision decision;
     279          43 :     decision.priority = *sampling_priority;
     280             :     // `decision.mechanism` is null.  We might be able to infer it once we
     281             :     // extract `trace_tags`, but we would have no use for it, so we won't.
     282          43 :     decision.origin = SamplingDecision::Origin::EXTRACTED;
     283             : 
     284          43 :     sampling_decision = decision;
     285             :   }
     286             : 
     287          78 :   const auto span_data_ptr = span_data.get();
     288          78 :   tracer_telemetry_->metrics().tracer.trace_segments_created_continued.inc();
     289             :   const auto segment = std::make_shared<TraceSegment>(
     290          78 :       logger_, collector_, tracer_telemetry_,
     291           0 :       config_manager_.get_trace_sampler(), span_sampler_, defaults_,
     292          78 :       runtime_id_, injection_styles_, hostname_, std::move(origin),
     293          78 :       tags_header_max_size_, std::move(trace_tags),
     294          78 :       std::move(sampling_decision), std::move(additional_w3c_tracestate),
     295         156 :       std::move(additional_datadog_w3c_tracestate), std::move(span_data));
     296             :   Span span{span_data_ptr, segment,
     297          20 :             [generator = generator_]() { return generator->span_id(); },
     298         234 :             clock_};
     299          78 :   return span;
     300         113 : }
     301             : 
     302          10 : Expected<Span> Tracer::extract_or_create_span(const DictReader& reader) {
     303          20 :   return extract_or_create_span(reader, SpanConfig{});
     304             : }
     305             : 
     306          10 : Expected<Span> Tracer::extract_or_create_span(const DictReader& reader,
     307             :                                               const SpanConfig& config) {
     308          10 :   auto maybe_span = extract_span(reader, config);
     309          10 :   if (!maybe_span && maybe_span.error().code == Error::NO_SPAN_TO_EXTRACT) {
     310           1 :     return create_span(config);
     311             :   }
     312           9 :   return maybe_span;
     313          10 : }
     314             : 
     315             : }  // namespace tracing
     316             : }  // namespace datadog

Generated by: LCOV version 1.16