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
|