diff --git a/examples/http-server/common/tracingutil.h b/examples/http-server/common/tracingutil.h index 7bc8aefc..e0a7cea2 100644 --- a/examples/http-server/common/tracingutil.h +++ b/examples/http-server/common/tracingutil.h @@ -16,7 +16,12 @@ class HeaderWriter final : public datadog::tracing::DictWriter { explicit HeaderWriter(httplib::Headers& headers) : headers_(headers) {} void set(std::string_view key, std::string_view value) override { - headers_.emplace(key, value); + auto found = headers_.find(std::string(key)); + if (found == headers_.cend()) { + headers_.emplace(key, value); + } else { + found->second = value; + } } }; diff --git a/examples/http-server/proxy/proxy.cpp b/examples/http-server/proxy/proxy.cpp index 451c4031..5d100374 100644 --- a/examples/http-server/proxy/proxy.cpp +++ b/examples/http-server/proxy/proxy.cpp @@ -49,37 +49,38 @@ int main() { auto forward_handler = [&tracer, &upstream_client]( const httplib::Request& req, httplib::Response& res) { - auto span = tracer.create_span(); - span.set_name("forward.request"); - span.set_resource_name(req.method + " " + req.path); - span.set_tag("network.origin.ip", req.remote_addr); - span.set_tag("network.origin.port", std::to_string(req.remote_port)); - span.set_tag("http.url_details.path", req.target); - span.set_tag("http.route", req.path); - span.set_tag("http.method", req.method); + tracingutil::HeaderReader reader(req.headers); + auto span = tracer.extract_or_create_span(reader); + span->set_name("forward.request"); + span->set_resource_name(req.method + " " + req.path); + span->set_tag("network.origin.ip", req.remote_addr); + span->set_tag("network.origin.port", std::to_string(req.remote_port)); + span->set_tag("http.url_details.path", req.target); + span->set_tag("http.route", req.path); + span->set_tag("http.method", req.method); httplib::Error er; httplib::Request forward_request(req); forward_request.path = req.target; tracingutil::HeaderWriter writer(forward_request.headers); - span.inject(writer); + span->inject(writer); upstream_client.send(forward_request, res, er); if (er != httplib::Error::Success) { res.status = 500; - span.set_error_message(httplib::to_string(er)); + span->set_error_message(httplib::to_string(er)); std::cerr << "Error occurred while proxying request: " << req.target << "\n"; } else { tracingutil::HeaderReader reader(res.headers); - auto status = span.read_sampling_delegation_response(reader); + auto status = span->read_sampling_delegation_response(reader); if (auto error = status.if_error()) { std::cerr << error << "\n"; } } - span.set_tag("http.status_code", std::to_string(res.status)); + span->set_tag("http.status_code", std::to_string(res.status)); }; httplib::Server server; diff --git a/src/datadog/extracted_data.h b/src/datadog/extracted_data.h index 3d36a3fb..a28b0c16 100644 --- a/src/datadog/extracted_data.h +++ b/src/datadog/extracted_data.h @@ -23,6 +23,10 @@ struct ExtractedData { bool delegate_sampling_decision = false; Optional sampling_priority; // If this `ExtractedData` was created on account of `PropagationStyle::W3C`, + // then `datadog_w3c_parent_id` contains the parts of the "tracestate" + // refering to the latest datadog parent ID. + Optional datadog_w3c_parent_id; + // If this `ExtractedData` was created on account of `PropagationStyle::W3C`, // then `additional_w3c_tracestate` contains the parts of the "tracestate" // header that are not the "dd" (Datadog) entry. If there are no other parts, // then `additional_w3c_tracestate` is null. diff --git a/src/datadog/extraction_util.cpp b/src/datadog/extraction_util.cpp index 10fdf92c..0e57e840 100644 --- a/src/datadog/extraction_util.cpp +++ b/src/datadog/extraction_util.cpp @@ -295,6 +295,7 @@ ExtractedData merge(const std::vector& contexts) { }); if (other != contexts.end()) { + result.datadog_w3c_parent_id = other->datadog_w3c_parent_id; result.additional_w3c_tracestate = other->additional_w3c_tracestate; result.additional_datadog_w3c_tracestate = other->additional_datadog_w3c_tracestate; diff --git a/src/datadog/tags.cpp b/src/datadog/tags.cpp index b139fa06..32e1d4d5 100644 --- a/src/datadog/tags.cpp +++ b/src/datadog/tags.cpp @@ -1,7 +1,5 @@ #include "tags.h" -#include "string_util.h" - namespace datadog { namespace tracing { namespace tags { @@ -32,11 +30,10 @@ const std::string process_id = "process_id"; const std::string language = "language"; const std::string runtime_id = "runtime-id"; const std::string sampling_decider = "_dd.is_sampling_decider"; +const std::string w3c_parent_id = "_dd.parent_id"; } // namespace internal -bool is_internal(StringView tag_name) { return starts_with(tag_name, "_dd."); } - } // namespace tags } // namespace tracing } // namespace datadog diff --git a/src/datadog/tags.h b/src/datadog/tags.h index d05e33a2..63f5020f 100644 --- a/src/datadog/tags.h +++ b/src/datadog/tags.h @@ -5,6 +5,7 @@ #include +#include "string_util.h" #include "string_view.h" namespace datadog { @@ -36,11 +37,14 @@ extern const std::string process_id; extern const std::string language; extern const std::string runtime_id; extern const std::string sampling_decider; +extern const std::string w3c_parent_id; } // namespace internal // Return whether the specified `tag_name` is reserved for use internal to this // library. -bool is_internal(StringView tag_name); +inline bool is_internal(StringView tag_name) { + return starts_with(tag_name, "_dd."); +} } // namespace tags } // namespace tracing diff --git a/src/datadog/trace_segment.cpp b/src/datadog/trace_segment.cpp index 639411ca..e2506f7d 100644 --- a/src/datadog/trace_segment.cpp +++ b/src/datadog/trace_segment.cpp @@ -418,10 +418,11 @@ bool TraceSegment::inject(DictWriter& writer, const SpanData& span, writer.set( "traceparent", encode_traceparent(span.trace_id, span.span_id, sampling_priority)); - writer.set("tracestate", - encode_tracestate(sampling_priority, origin_, trace_tags, - additional_datadog_w3c_tracestate_, - additional_w3c_tracestate_)); + writer.set( + "tracestate", + encode_tracestate(span.span_id, sampling_priority, origin_, + trace_tags, additional_datadog_w3c_tracestate_, + additional_w3c_tracestate_)); break; default: assert(style == PropagationStyle::NONE); diff --git a/src/datadog/tracer.cpp b/src/datadog/tracer.cpp index 583ecd74..e6287e43 100644 --- a/src/datadog/tracer.cpp +++ b/src/datadog/tracer.cpp @@ -175,7 +175,7 @@ Expected Tracer::extract_span(const DictReader& reader, } auto [trace_id, parent_id, origin, trace_tags, delegate_sampling_decision, - sampling_priority, additional_w3c_tracestate, + sampling_priority, datadog_w3c_parent_id, additional_w3c_tracestate, additional_datadog_w3c_tracestate, style, headers_examined] = merge(extracted_contexts); @@ -277,6 +277,10 @@ Expected Tracer::extract_span(const DictReader& reader, } } + if (datadog_w3c_parent_id) { + span_data->tags[tags::internal::w3c_parent_id] = *datadog_w3c_parent_id; + } + Optional sampling_decision; if (sampling_priority) { SamplingDecision decision; diff --git a/src/datadog/w3c_propagation.cpp b/src/datadog/w3c_propagation.cpp index 9e2dd16a..e8d96d18 100644 --- a/src/datadog/w3c_propagation.cpp +++ b/src/datadog/w3c_propagation.cpp @@ -221,6 +221,14 @@ void parse_datadog_tracestate(ExtractedData& result, StringView datadog_value) { (*result.sampling_priority > 0) == (priority > 0)) { result.sampling_priority = priority; } + } else if (key == "p") { + if (value.size() != 16) { + // chaff! + pair_begin = pair_end == end ? end : pair_end + 1; + continue; + } + + result.datadog_w3c_parent_id = std::string(value); } else if (starts_with(key, "t.")) { // The part of the key that follows "t." is the name of a trace tag, // except without the "_dd.p." prefix. @@ -300,6 +308,7 @@ Expected extract_w3c( return result; } + result.datadog_w3c_parent_id = "0000000000000000"; extract_tracestate(result, headers); return result; @@ -326,11 +335,14 @@ std::string encode_traceparent(TraceID trace_id, std::uint64_t span_id, } std::string encode_datadog_tracestate( - int sampling_priority, const Optional& origin, + uint64_t span_id, int sampling_priority, + const Optional& origin, const std::vector>& trace_tags, const Optional& additional_datadog_w3c_tracestate) { std::string result = "dd=s:"; result += std::to_string(sampling_priority); + result += ";p:"; + result += hex_padded(span_id); if (origin) { result += ";o:"; @@ -382,12 +394,14 @@ std::string encode_datadog_tracestate( } std::string encode_tracestate( - int sampling_priority, const Optional& origin, + uint64_t span_id, int sampling_priority, + const Optional& origin, const std::vector>& trace_tags, const Optional& additional_datadog_w3c_tracestate, const Optional& additional_w3c_tracestate) { - std::string result = encode_datadog_tracestate( - sampling_priority, origin, trace_tags, additional_datadog_w3c_tracestate); + std::string result = + encode_datadog_tracestate(span_id, sampling_priority, origin, trace_tags, + additional_datadog_w3c_tracestate); if (additional_w3c_tracestate) { result += ','; diff --git a/src/datadog/w3c_propagation.h b/src/datadog/w3c_propagation.h index dd4f6f25..5ab06b24 100644 --- a/src/datadog/w3c_propagation.h +++ b/src/datadog/w3c_propagation.h @@ -37,7 +37,8 @@ std::string encode_traceparent(TraceID trace_id, std::uint64_t span_id, // Return a value for the "tracestate" header containing the specified fields. std::string encode_tracestate( - int sampling_priority, const Optional& origin, + uint64_t span_id, int sampling_priority, + const Optional& origin, const std::vector>& trace_tags, const Optional& additional_datadog_w3c_tracestate, const Optional& additional_w3c_tracestate); diff --git a/test/test_span.cpp b/test/test_span.cpp index f6aee85a..1fb3eb00 100644 --- a/test/test_span.cpp +++ b/test/test_span.cpp @@ -660,6 +660,7 @@ TEST_CASE("injecting W3C tracestate header") { // - sampling priority // - origin // - trace tags + // - parent id // - extra fields (extracted from W3C) // - all of the above // - character substitutions: @@ -704,79 +705,79 @@ TEST_CASE("injecting W3C tracestate header") { {__LINE__, "sampling priority", {{"x-datadog-trace-id", "1"}, {"x-datadog-parent-id", "1"}, {"x-datadog-sampling-priority", "2"}}, - "dd=s:2"}, + "dd=s:2;p:$parent_id"}, {__LINE__, "origin", {{"x-datadog-trace-id", "1"}, {"x-datadog-parent-id", "1"}, {"x-datadog-origin", "France"}}, // The "s:-1" comes from the 0% sample rate. - "dd=s:-1;o:France"}, + "dd=s:-1;p:$parent_id;o:France"}, {__LINE__, "trace tags", {{"x-datadog-trace-id", "1"}, {"x-datadog-parent-id", "1"}, {"x-datadog-tags", "_dd.p.foo=x,_dd.p.bar=y,ignored=wrong_prefix"}}, // The "s:-1" comes from the 0% sample rate. - "dd=s:-1;t.foo:x;t.bar:y"}, + "dd=s:-1;p:$parent_id;t.foo:x;t.bar:y"}, {__LINE__, "extra fields", {{"traceparent", traceparent_drop}, {"tracestate", "dd=foo:bar;boing:boing"}}, // The "s:0" comes from the sampling decision in `traceparent_drop`. - "dd=s:0;foo:bar;boing:boing"}, + "dd=s:0;p:$parent_id;foo:bar;boing:boing"}, {__LINE__, "all of the above", {{"traceparent", traceparent_drop}, {"tracestate", "dd=o:France;t.foo:x;t.bar:y;foo:bar;boing:boing"}}, // The "s:0" comes from the sampling decision in `traceparent_drop`. - "dd=s:0;o:France;t.foo:x;t.bar:y;foo:bar;boing:boing"}, + "dd=s:0;p:$parent_id;o:France;t.foo:x;t.bar:y;foo:bar;boing:boing"}, {__LINE__, "replace invalid characters in origin", {{"x-datadog-trace-id", "1"}, {"x-datadog-parent-id", "1"}, {"x-datadog-origin", "France, is a country=nation; so is 台北."}}, // The "s:-1" comes from the 0% sample rate. - "dd=s:-1;o:France_ is a country~nation_ so is ______."}, + "dd=s:-1;p:$parent_id;o:France_ is a country~nation_ so is ______."}, {__LINE__, "replace invalid characters in trace tag key", {{"x-datadog-trace-id", "1"}, {"x-datadog-parent-id", "1"}, {"x-datadog-tags", "_dd.p.a;d台北x =foo,_dd.p.ok=bar"}}, // The "s:-1" comes from the 0% sample rate. - "dd=s:-1;t.a_d______x_:foo;t.ok:bar"}, + "dd=s:-1;p:$parent_id;t.a_d______x_:foo;t.ok:bar"}, {__LINE__, "replace invalid characters in trace tag value", {{"x-datadog-trace-id", "1"}, {"x-datadog-parent-id", "1"}, {"x-datadog-tags", "_dd.p.wacky=hello fr~d; how are คุณ?"}}, // The "s:-1" comes from the 0% sample rate. - "dd=s:-1;t.wacky:hello fr_d_ how are _________?"}, + "dd=s:-1;p:$parent_id;t.wacky:hello fr_d_ how are _________?"}, {__LINE__, "replace equal signs with tildes in trace tag value", {{"x-datadog-trace-id", "1"}, {"x-datadog-parent-id", "1"}, {"x-datadog-tags", "_dd.p.base64_thingy=d2Fra2EhIHdhaw=="}}, // The "s:-1" comes from the 0% sample rate. - "dd=s:-1;t.base64_thingy:d2Fra2EhIHdhaw~~"}, + "dd=s:-1;p:$parent_id;t.base64_thingy:d2Fra2EhIHdhaw~~"}, {__LINE__, "oversized origin truncates it and subsequent fields", {{"x-datadog-trace-id", "1"}, {"x-datadog-parent-id", "1"}, {"x-datadog-origin", "long cat is looooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooong"}, {"x-datadog-tags", "_dd.p.foo=bar,_dd.p.honk=honk"}}, // The "s:-1" comes from the 0% sample rate. - "dd=s:-1"}, + "dd=s:-1;p:$parent_id"}, {__LINE__, "oversized trace tag truncates it and subsequent fields", {{"x-datadog-trace-id", "1"}, {"x-datadog-parent-id", "1"}, {"x-datadog-tags", "_dd.p.foo=bar,_dd.p.long_cat_is=looooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooong,_dd.p.lost=forever"}}, // The "s:-1" comes from the 0% sample rate. - "dd=s:-1;t.foo:bar"}, + "dd=s:-1;p:$parent_id;t.foo:bar"}, {__LINE__, "oversized extra field truncates itself and subsequent fields", {{"traceparent", traceparent_drop}, {"tracestate", "dd=foo:bar;long_cat_is:looooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooong;lost:forever"}}, // The "s:0" comes from the sampling decision in `traceparent_drop`. - "dd=s:0;foo:bar"}, + "dd=s:0;p:$parent_id;foo:bar"}, {__LINE__, "non-Datadog tracestate", {{"traceparent", traceparent_drop}, {"tracestate", "foo=bar,boing=boing"}}, // The "s:0" comes from the sampling decision in `traceparent_drop`. - "dd=s:0,foo=bar,boing=boing"}, + "dd=s:0;p:$parent_id,foo=bar,boing=boing"}, })); // clang-format on @@ -797,6 +798,9 @@ TEST_CASE("injecting W3C tracestate header") { const auto found = writer.items.find("tracestate"); REQUIRE(found != writer.items.end()); + test_case.expected_tracestate.replace( + test_case.expected_tracestate.find("$parent_id"), + sizeof("$parent_id") - 1, hex_padded(span->id())); REQUIRE(found->second == test_case.expected_tracestate); REQUIRE(logger->error_count() == 0); diff --git a/test/test_tracer.cpp b/test/test_tracer.cpp index 987a7ba2..f3b04c72 100644 --- a/test/test_tracer.cpp +++ b/test/test_tracer.cpp @@ -736,162 +736,328 @@ TEST_CASE("span extraction") { std::vector> expected_trace_tags = {}; Optional expected_additional_w3c_tracestate = {}; Optional expected_additional_datadog_w3c_tracestate = {}; + Optional expected_datadog_w3c_parent_id = {}; }; static const std::string traceparent_prefix = "00-00000000000000000000000000000001-0000000000000001-0"; static const std::string traceparent_drop = traceparent_prefix + "0"; static const std::string traceparent_keep = traceparent_prefix + "1"; - // clang-format off + auto test_case = GENERATE(values({ - {__LINE__, "no tracestate", - traceparent_drop, // traceparent - nullopt, // tracestate - 0}, // expected_sampling_priority + { + __LINE__, + "no tracestate", + traceparent_drop, // traceparent + nullopt, // tracestate + 0, // expected_sampling_priority + nullopt, // expected_origin + {}, // expected_trace_tags, + nullopt, // expected_additional_w3c_tracestate, + nullopt, // expected_additional_datadog_w3c_tracestate, + "0000000000000000", // expected_datadog_w3c_parent_id, + }, - {__LINE__, "empty tracestate", - traceparent_drop, // traceparent - "", // tracestate - 0}, // expected_sampling_priority + { + __LINE__, + "empty tracestate", + traceparent_drop, // traceparent + "", // tracestate + 0, // expected_sampling_priority + nullopt, // expected_origin + {}, // expected_trace_tags, + nullopt, // expected_additional_w3c_tracestate, + nullopt, // expected_additional_datadog_w3c_tracestate, + "0000000000000000", // expected_datadog_w3c_parent_id, + }, + + { + __LINE__, + "no dd entry", + traceparent_drop, // traceparent + "foo=hello,@thingy/thing=wah;wah;wah", // tracestate + 0, // expected_sampling_priority + nullopt, // expected_origin + {}, // expected_trace_tags + "foo=hello,@thingy/thing=wah;wah;wah", // expected_additional_w3c_tracestate + nullopt, // expected_additional_datadog_w3c_tracestate + "0000000000000000", // expected_datadog_w3c_parent_id, + }, + + { + __LINE__, + "empty entry", + traceparent_drop, // traceparent + "foo=hello,,bar=thing", // tracestate + 0, // expected_sampling_priority + nullopt, // expected_origin + {}, // expected_trace_tags + "foo=hello,,bar=thing", // expected_additional_w3c_tracestate + nullopt, // expected_additional_datadog_w3c_tracestate + "0000000000000000", // expected_datadog_w3c_parent_id, + }, + + { + __LINE__, + "malformed entry", + traceparent_drop, // traceparent + "foo=hello,chicken,bar=thing", // tracestate + 0, // expected_sampling_priority + nullopt, // expected_origin + {}, // expected_trace_tags + "foo=hello,chicken,bar=thing", // expected_additional_w3c_tracestate + nullopt, // expected_additional_datadog_w3c_tracestate + "0000000000000000", // expected_datadog_w3c_parent_id, + }, + + { + __LINE__, + "stuff before dd entry", + traceparent_drop, // traceparent + "foo=hello,bar=baz,dd=", // tracestate + 0, // expected_sampling_priority + nullopt, // expected_origin + {}, // expected_trace_tags + "foo=hello,bar=baz", // expected_additional_w3c_tracestate + nullopt, // expected_additional_datadog_w3c_tracestate + "0000000000000000", // expected_datadog_w3c_parent_id, + }, + + { + __LINE__, + "stuff after dd entry", + traceparent_drop, // traceparent + "dd=,foo=hello,bar=baz", // tracestate + 0, // expected_sampling_priority + nullopt, // expected_origin + {}, // expected_trace_tags + "foo=hello,bar=baz", // expected_additional_w3c_tracestate + nullopt, // expected_additional_datadog_w3c_tracestate + "0000000000000000", // expected_datadog_w3c_parent_id, + }, + + { + __LINE__, + "stuff before and after dd entry", + traceparent_drop, // traceparent + "chicken=yes,nuggets=yes,dd=,foo=hello,bar=baz", // tracestate + 0, // expected_sampling_priority + nullopt, // expected_origin + {}, // expected_trace_tags + "chicken=yes,nuggets=yes,foo=hello,bar=baz", // expected_additional_w3c_tracestate + nullopt, // expected_additional_datadog_w3c_tracestate + "0000000000000000", // expected_datadog_w3c_parent_id, + }, + + { + __LINE__, + "dd entry with empty subentries", + traceparent_drop, // traceparent + "dd=foo:bar;;;;;baz:bam;;;", // tracestate + 0, // expected_sampling_priority + nullopt, // expected_origin + {}, // expected_trace_tags + nullopt, // expected_additional_w3c_tracestate + "foo:bar;baz:bam", // expected_additional_datadog_w3c_tracestate + "0000000000000000", // expected_datadog_w3c_parent_id, + }, + + { + __LINE__, + "dd entry with malformed subentries", + traceparent_drop, // traceparent + "dd=foo:bar;chicken;chicken;baz:bam;chicken", // tracestate + 0, // expected_sampling_priority + nullopt, // expected_origin + {}, // expected_trace_tags + nullopt, // expected_additional_w3c_tracestate + "foo:bar;baz:bam", // expected_additional_datadog_w3c_tracestate + "0000000000000000", // expected_datadog_w3c_parent_id, + }, + + { + __LINE__, + "origin, trace tags, parent, and extra fields", + traceparent_drop, // traceparent + "dd=o:France;p:00000000000d69ac;t.foo:thing1;t.bar:thing2;x:wow;y:" + "wow", // tracestate + 0, // expected_sampling_priority + "France", // expected_origin + {{"_dd.p.foo", "thing1"}, + {"_dd.p.bar", "thing2"}}, // expected_trace_tags + nullopt, // expected_additional_w3c_tracestate + "x:wow;y:wow", // expected_additional_datadog_w3c_tracestate + "00000000000d69ac", // expected_datadog_w3c_parent_id + }, + + { + __LINE__, + "dd parent id is malformed", + traceparent_drop, // traceparent + "dd=p:XxDDOGxX", // tracestate + 0, // expected_sampling_priority + nullopt, // expected_origin + {}, // expected_trace_tags + nullopt, // expected_additional_w3c_tracestate + nullopt, // expected_additional_datadog_w3c_tracestate + "0000000000000000", // expected_datadog_w3c_parent_id, + }, + { + __LINE__, + "dd parent id is propagated even if not valid", + traceparent_drop, // traceparent + "dd=p:yu7C0o3AOmbOcfXw", // tracestate + 0, // expected_sampling_priority + nullopt, // expected_origin + {}, // expected_trace_tags + nullopt, // expected_additional_w3c_tracestate + nullopt, // expected_additional_datadog_w3c_tracestate + "yu7C0o3AOmbOcfXw", // expected_datadog_w3c_parent_id, + }, + + { + __LINE__, + "origin with escaped equal sign", + traceparent_drop, // traceparent + "dd=o:France~country", // tracestate + 0, // expected_sampling_priority + "France=country", // expected_origin + {}, // expected_trace_tags + nullopt, // expected_additional_w3c_tracestate + nullopt, // expected_additional_datadog_w3c_tracestate + "0000000000000000", // expected_datadog_w3c_parent_id, + }, + + { + __LINE__, + "traceparent and tracestate sampling agree (1/4)", + traceparent_drop, // traceparent + "dd=s:0", // tracestate + 0, // expected_sampling_priority + nullopt, // expected_origin + {}, // expected_trace_tags + nullopt, // expected_additional_w3c_tracestate + nullopt, // expected_additional_datadog_w3c_tracestate + "0000000000000000", // expected_datadog_w3c_parent_id, + }, + + { + __LINE__, + "traceparent and tracestate sampling agree (2/4)", + traceparent_drop, // traceparent + "dd=s:-1", // tracestate + -1, // expected_sampling_priority + nullopt, // expected_origin + {}, // expected_trace_tags + nullopt, // expected_additional_w3c_tracestate + nullopt, // expected_additional_datadog_w3c_tracestate + "0000000000000000", // expected_datadog_w3c_parent_id, + }, + + { + __LINE__, + "traceparent and tracestate sampling agree (3/4)", + traceparent_keep, // traceparent + "dd=s:1", // tracestate + 1, // expected_sampling_priority + nullopt, // expected_origin + {}, // expected_trace_tags + nullopt, // expected_additional_w3c_tracestate + nullopt, // expected_additional_datadog_w3c_tracestate + "0000000000000000", // expected_datadog_w3c_parent_id, + }, + + { + __LINE__, + "traceparent and tracestate sampling agree (4/4)", + traceparent_keep, // traceparent + "dd=s:2", // tracestate + 2, // expected_sampling_priority + nullopt, // expected_origin + {}, // expected_trace_tags + nullopt, // expected_additional_w3c_tracestate + nullopt, // expected_additional_datadog_w3c_tracestate + "0000000000000000", // expected_datadog_w3c_parent_id, + }, + + { + __LINE__, + "traceparent and tracestate sampling disagree (1/4)", + traceparent_drop, // traceparent + "dd=s:1", // tracestate + 0, // expected_sampling_priority + nullopt, // expected_origin + {}, // expected_trace_tags + nullopt, // expected_additional_w3c_tracestate + nullopt, // expected_additional_datadog_w3c_tracestate + "0000000000000000", // expected_datadog_w3c_parent_id, + }, + + { + __LINE__, + "traceparent and tracestate sampling disagree (2/4)", + traceparent_drop, // traceparent + "dd=s:2", // tracestate + 0, // expected_sampling_priority + nullopt, // expected_origin + {}, // expected_trace_tags + nullopt, // expected_additional_w3c_tracestate + nullopt, // expected_additional_datadog_w3c_tracestate + "0000000000000000", // expected_datadog_w3c_parent_id, + }, + + { + __LINE__, + "traceparent and tracestate sampling disagree (3/4)", + traceparent_keep, // traceparent + "dd=s:0", // tracestate + 1, // expected_sampling_priority + nullopt, // expected_origin + {}, // expected_trace_tags + nullopt, // expected_additional_w3c_tracestate + nullopt, // expected_additional_datadog_w3c_tracestate + "0000000000000000", // expected_datadog_w3c_parent_id, + }, + + { + __LINE__, + "traceparent and tracestate sampling disagree (4/4)", + traceparent_keep, // traceparent + "dd=s:-1", // tracestate + 1, // expected_sampling_priority + nullopt, // expected_origin + {}, // expected_trace_tags + nullopt, // expected_additional_w3c_tracestate + nullopt, // expected_additional_datadog_w3c_tracestate + "0000000000000000", // expected_datadog_w3c_parent_id, + }, - {__LINE__, "no dd entry", - traceparent_drop, // traceparent - "foo=hello,@thingy/thing=wah;wah;wah", // tracestate - 0, // expected_sampling_priority - nullopt, // expected_origin - {}, // expected_trace_tags - "foo=hello,@thingy/thing=wah;wah;wah", // expected_additional_w3c_tracestate - nullopt}, // expected_additional_datadog_w3c_tracestate - - {__LINE__, "empty entry", - traceparent_drop, // traceparent - "foo=hello,,bar=thing", // tracestate - 0, // expected_sampling_priority - nullopt, // expected_origin - {}, // expected_trace_tags - "foo=hello,,bar=thing", // expected_additional_w3c_tracestate - nullopt}, // expected_additional_datadog_w3c_tracestate - - {__LINE__, "malformed entry", - traceparent_drop, // traceparent - "foo=hello,chicken,bar=thing", // tracestate - 0, // expected_sampling_priority - nullopt, // expected_origin - {}, // expected_trace_tags - "foo=hello,chicken,bar=thing", // expected_additional_w3c_tracestate - nullopt}, // expected_additional_datadog_w3c_tracestate - - {__LINE__, "stuff before dd entry", - traceparent_drop, // traceparent - "foo=hello,bar=baz,dd=", // tracestate - 0, // expected_sampling_priority - nullopt, // expected_origin - {}, // expected_trace_tags - "foo=hello,bar=baz", // expected_additional_w3c_tracestate - nullopt}, // expected_additional_datadog_w3c_tracestate - - {__LINE__, "stuff after dd entry", - traceparent_drop, // traceparent - "dd=,foo=hello,bar=baz", // tracestate - 0, // expected_sampling_priority - nullopt, // expected_origin - {}, // expected_trace_tags - "foo=hello,bar=baz", // expected_additional_w3c_tracestate - nullopt}, // expected_additional_datadog_w3c_tracestate - - {__LINE__, "stuff before and after dd entry", - traceparent_drop, // traceparent - "chicken=yes,nuggets=yes,dd=,foo=hello,bar=baz", // tracestate - 0, // expected_sampling_priority - nullopt, // expected_origin - {}, // expected_trace_tags - "chicken=yes,nuggets=yes,foo=hello,bar=baz", // expected_additional_w3c_tracestate - nullopt}, // expected_additional_datadog_w3c_tracestate - - {__LINE__, "dd entry with empty subentries", - traceparent_drop, // traceparent - "dd=foo:bar;;;;;baz:bam;;;", // tracestate - 0, // expected_sampling_priority - nullopt, // expected_origin - {}, // expected_trace_tags - nullopt, // expected_additional_w3c_tracestate - "foo:bar;baz:bam"}, // expected_additional_datadog_w3c_tracestate - - {__LINE__, "dd entry with malformed subentries", - traceparent_drop, // traceparent - "dd=foo:bar;chicken;chicken;baz:bam;chicken", // tracestate - 0, // expected_sampling_priority - nullopt, // expected_origin - {}, // expected_trace_tags - nullopt, // expected_additional_w3c_tracestate - "foo:bar;baz:bam"}, // expected_additional_datadog_w3c_tracestate - - {__LINE__, "origin, trace tags, and extra fields", - traceparent_drop, // traceparent - "dd=o:France;t.foo:thing1;t.bar:thing2;x:wow;y:wow", // tracestate - 0, // expected_sampling_priority - "France", // expected_origin - {{"_dd.p.foo", "thing1"}, {"_dd.p.bar", "thing2"}}, // expected_trace_tags - nullopt, // expected_additional_w3c_tracestate - "x:wow;y:wow"}, // expected_additional_datadog_w3c_tracestate - - {__LINE__, "origin with escaped equal sign", - traceparent_drop, // traceparent - "dd=o:France~country", // tracestate - 0, // expected_sampling_priority - "France=country"}, // expected_origin - - {__LINE__, "traceparent and tracestate sampling agree (1/4)", - traceparent_drop, // traceparent - "dd=s:0", // tracestate - 0}, // expected_sampling_priority - - {__LINE__, "traceparent and tracestate sampling agree (2/4)", - traceparent_drop, // traceparent - "dd=s:-1", // tracestate - -1}, // expected_sampling_priority - - {__LINE__, "traceparent and tracestate sampling agree (3/4)", - traceparent_keep, // traceparent - "dd=s:1", // tracestate - 1}, // expected_sampling_priority - - {__LINE__, "traceparent and tracestate sampling agree (4/4)", - traceparent_keep, // traceparent - "dd=s:2", // tracestate - 2}, // expected_sampling_priority - - {__LINE__, "traceparent and tracestate sampling disagree (1/4)", - traceparent_drop, // traceparent - "dd=s:1", // tracestate - 0}, // expected_sampling_priority - - {__LINE__, "traceparent and tracestate sampling disagree (2/4)", - traceparent_drop, // traceparent - "dd=s:2", // tracestate - 0}, // expected_sampling_priority - - {__LINE__, "traceparent and tracestate sampling disagree (3/4)", - traceparent_keep, // traceparent - "dd=s:0", // tracestate - 1}, // expected_sampling_priority - - {__LINE__, "traceparent and tracestate sampling disagree (4/4)", - traceparent_keep, // traceparent - "dd=s:-1", // tracestate - 1}, // expected_sampling_priority - - {__LINE__, "invalid sampling priority (1/2)", - traceparent_drop, // traceparent - "dd=s:oops", // tracestate - 0}, // expected_sampling_priority - - {__LINE__, "invalid sampling priority (2/2)", - traceparent_keep, // traceparent - "dd=s:oops", // tracestate - 1}, // expected_sampling_priority + { + __LINE__, + "invalid sampling priority (1/2)", + traceparent_drop, // traceparent + "dd=s:oops", // tracestate + 0, // expected_sampling_priority + nullopt, // expected_origin + {}, // expected_trace_tags + nullopt, // expected_additional_w3c_tracestate + nullopt, // expected_additional_datadog_w3c_tracestate + "0000000000000000", // expected_datadog_w3c_parent_id, + }, + + { + __LINE__, + "invalid sampling priority (2/2)", + traceparent_keep, // traceparent + "dd=s:oops", // tracestate + 1, // expected_sampling_priority + nullopt, // expected_origin + {}, // expected_trace_tags + nullopt, // expected_additional_w3c_tracestate + nullopt, // expected_additional_datadog_w3c_tracestate + "0000000000000000", // expected_datadog_w3c_parent_id, + }, })); - // clang-format on CAPTURE(test_case.name); CAPTURE(test_case.line); @@ -921,11 +1087,30 @@ TEST_CASE("span extraction") { test_case.expected_additional_w3c_tracestate); REQUIRE(extracted->additional_datadog_w3c_tracestate == test_case.expected_additional_datadog_w3c_tracestate); + REQUIRE(extracted->datadog_w3c_parent_id == + test_case.expected_datadog_w3c_parent_id); REQUIRE(logger.entries.empty()); REQUIRE(span_tags.empty()); } + SECTION("_dd.parent_id") { + auto finalized_config = finalize_config(config); + REQUIRE(finalized_config); + Tracer tracer{*finalized_config}; + + std::unordered_map headers; + headers["traceparent"] = + "00-4bf92f3577b34da6a3ce929d0e0e4736-00f067aa0ba902b7-01"; + headers["tracestate"] = "dd=s:1;p:000000000000002a;foo:bar,lol=wut"; + MockDictReader reader{headers}; + const auto span = tracer.extract_span(reader); + + auto parent_id_tag = span->lookup_tag("_dd.parent_id"); + REQUIRE(parent_id_tag); + CHECK(*parent_id_tag == "000000000000002a"); + } + SECTION("x-datadog-tags") { auto finalized_config = finalize_config(config); REQUIRE(finalized_config); @@ -1119,7 +1304,7 @@ TEST_CASE("128-bit trace IDs") { // trace ID in the traceparent. // It will be ignored, and the resulting _dd.p.tid value will be consistent // with the higher part of the trace ID in traceparent: "deadbeefdeadbeef". - headers["tracestate"] = "dd=t.tid:" + tid; + headers["tracestate"] = "dd=t.tid:" + tid + ";p:0000000000000001"; MockDictReader reader{headers}; const auto span = tracer.extract_span(reader); CAPTURE(logger->entries); @@ -1629,7 +1814,7 @@ TEST_CASE("heterogeneous extraction") { {{"traceparent", "00-4bf92f3577b34da6a3ce929d0e0e4736-00f067aa0ba902b7-01"}, {"tracestate", "dd=foo:bar,lol=wut"}}, {{"traceparent", "00-4bf92f3577b34da6a3ce929d0e0e4736-000000000000002a-01"}, - {"tracestate", "dd=s:1;foo:bar,lol=wut"}}}, + {"tracestate", "dd=s:1;p:000000000000002a;foo:bar,lol=wut"}}}, {__LINE__, "tracestate from subsequent style", {PropagationStyle::DATADOG, PropagationStyle::W3C}, @@ -1639,7 +1824,7 @@ TEST_CASE("heterogeneous extraction") { {"traceparent", "00-00000000000000000000000000000030-0000000000000040-01"}, {"tracestate", "competitor=stuff,dd=o:Nebraska;s:1;ah:choo"}}, // origin is different {{"traceparent", "00-00000000000000000000000000000030-000000000000002a-01"}, - {"tracestate", "dd=s:2;o:Kansas;ah:choo,competitor=stuff"}}}, + {"tracestate", "dd=s:2;p:000000000000002a;o:Kansas;ah:choo,competitor=stuff"}}}, {__LINE__, "ignore interlopers", {PropagationStyle::DATADOG, PropagationStyle::B3, PropagationStyle::W3C}, @@ -1652,7 +1837,7 @@ TEST_CASE("heterogeneous extraction") { {"traceparent", "00-00000000000000000000000000000030-0000000000000040-01"}, {"tracestate", "competitor=stuff,dd=o:Nebraska;s:1;ah:choo"}}, {{"traceparent", "00-00000000000000000000000000000030-000000000000002a-01"}, - {"tracestate", "dd=s:2;o:Kansas;ah:choo,competitor=stuff"}}}, + {"tracestate", "dd=s:2;p:000000000000002a;o:Kansas;ah:choo,competitor=stuff"}}}, {__LINE__, "don't take tracestate if trace ID doesn't match", {PropagationStyle::DATADOG, PropagationStyle::W3C}, @@ -1662,7 +1847,7 @@ TEST_CASE("heterogeneous extraction") { {"traceparent", "00-00000000000000000000000000000031-0000000000000040-01"}, {"tracestate", "competitor=stuff,dd=o:Nebraska;s:1;ah:choo"}}, {{"traceparent", "00-00000000000000000000000000000030-000000000000002a-01"}, - {"tracestate", "dd=s:2;o:Kansas"}}}, + {"tracestate", "dd=s:2;p:000000000000002a;o:Kansas"}}}, {__LINE__, "don't take tracestate if W3C extraction isn't configured", {PropagationStyle::DATADOG, PropagationStyle::B3}, @@ -1672,7 +1857,7 @@ TEST_CASE("heterogeneous extraction") { {"traceparent", "00-00000000000000000000000000000030-0000000000000040-01"}, {"tracestate", "competitor=stuff,dd=o:Nebraska;s:1;ah:choo"}}, {{"traceparent", "00-00000000000000000000000000000030-000000000000002a-01"}, - {"tracestate", "dd=s:2;o:Kansas"}}}, + {"tracestate", "dd=s:2;p:000000000000002a;o:Kansas"}}}, })); // clang-format on