Skip to content

Commit 28e2b0c

Browse files
committed
feat: extract/inject datadog specific tracestate
Add support for W3C datadog tracestate field `p`.
1 parent e576445 commit 28e2b0c

File tree

8 files changed

+102
-32
lines changed

8 files changed

+102
-32
lines changed

src/datadog/extracted_data.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,10 @@ struct ExtractedData {
2323
bool delegate_sampling_decision = false;
2424
Optional<int> sampling_priority;
2525
// If this `ExtractedData` was created on account of `PropagationStyle::W3C`,
26+
// then `datadog_w3c_parent_id` contains the parts of the "tracestate"
27+
// refering to the latest datadog parent ID.
28+
Optional<std::string> datadog_w3c_parent_id;
29+
// If this `ExtractedData` was created on account of `PropagationStyle::W3C`,
2630
// then `additional_w3c_tracestate` contains the parts of the "tracestate"
2731
// header that are not the "dd" (Datadog) entry. If there are no other parts,
2832
// then `additional_w3c_tracestate` is null.

src/datadog/extraction_util.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -295,6 +295,7 @@ ExtractedData merge(const std::vector<ExtractedData>& contexts) {
295295
});
296296

297297
if (other != contexts.end()) {
298+
result.datadog_w3c_parent_id = other->datadog_w3c_parent_id;
298299
result.additional_w3c_tracestate = other->additional_w3c_tracestate;
299300
result.additional_datadog_w3c_tracestate =
300301
other->additional_datadog_w3c_tracestate;

src/datadog/trace_segment.cpp

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -418,10 +418,11 @@ bool TraceSegment::inject(DictWriter& writer, const SpanData& span,
418418
writer.set(
419419
"traceparent",
420420
encode_traceparent(span.trace_id, span.span_id, sampling_priority));
421-
writer.set("tracestate",
422-
encode_tracestate(sampling_priority, origin_, trace_tags,
423-
additional_datadog_w3c_tracestate_,
424-
additional_w3c_tracestate_));
421+
writer.set(
422+
"tracestate",
423+
encode_tracestate(span.span_id, sampling_priority, origin_,
424+
trace_tags, additional_datadog_w3c_tracestate_,
425+
additional_w3c_tracestate_));
425426
break;
426427
default:
427428
assert(style == PropagationStyle::NONE);

src/datadog/tracer.cpp

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -175,7 +175,7 @@ Expected<Span> Tracer::extract_span(const DictReader& reader,
175175
}
176176

177177
auto [trace_id, parent_id, origin, trace_tags, delegate_sampling_decision,
178-
sampling_priority, additional_w3c_tracestate,
178+
sampling_priority, datadog_w3c_parent_id, additional_w3c_tracestate,
179179
additional_datadog_w3c_tracestate, style, headers_examined] =
180180
merge(extracted_contexts);
181181

@@ -277,6 +277,10 @@ Expected<Span> Tracer::extract_span(const DictReader& reader,
277277
}
278278
}
279279

280+
if (datadog_w3c_parent_id) {
281+
span_data->tags["_dd.parent_id"] = *datadog_w3c_parent_id;
282+
}
283+
280284
Optional<SamplingDecision> sampling_decision;
281285
if (sampling_priority) {
282286
SamplingDecision decision;

src/datadog/w3c_propagation.cpp

Lines changed: 24 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -221,6 +221,21 @@ void parse_datadog_tracestate(ExtractedData& result, StringView datadog_value) {
221221
(*result.sampling_priority > 0) == (priority > 0)) {
222222
result.sampling_priority = priority;
223223
}
224+
} else if (key == "p") {
225+
if (value.size() != 16) {
226+
// chaff!
227+
pair_begin = pair_end == end ? end : pair_end + 1;
228+
continue;
229+
}
230+
231+
const auto maybe_id = parse_int(value, 16);
232+
if (!maybe_id || *maybe_id == 0) {
233+
// chaff!
234+
pair_begin = pair_end == end ? end : pair_end + 1;
235+
continue;
236+
}
237+
238+
result.datadog_w3c_parent_id = std::string(value);
224239
} else if (starts_with(key, "t.")) {
225240
// The part of the key that follows "t." is the name of a trace tag,
226241
// except without the "_dd.p." prefix.
@@ -326,11 +341,14 @@ std::string encode_traceparent(TraceID trace_id, std::uint64_t span_id,
326341
}
327342

328343
std::string encode_datadog_tracestate(
329-
int sampling_priority, const Optional<std::string>& origin,
344+
uint64_t span_id, int sampling_priority,
345+
const Optional<std::string>& origin,
330346
const std::vector<std::pair<std::string, std::string>>& trace_tags,
331347
const Optional<std::string>& additional_datadog_w3c_tracestate) {
332348
std::string result = "dd=s:";
333349
result += std::to_string(sampling_priority);
350+
result += ";p:";
351+
result += hex_padded(span_id);
334352

335353
if (origin) {
336354
result += ";o:";
@@ -382,12 +400,14 @@ std::string encode_datadog_tracestate(
382400
}
383401

384402
std::string encode_tracestate(
385-
int sampling_priority, const Optional<std::string>& origin,
403+
uint64_t span_id, int sampling_priority,
404+
const Optional<std::string>& origin,
386405
const std::vector<std::pair<std::string, std::string>>& trace_tags,
387406
const Optional<std::string>& additional_datadog_w3c_tracestate,
388407
const Optional<std::string>& additional_w3c_tracestate) {
389-
std::string result = encode_datadog_tracestate(
390-
sampling_priority, origin, trace_tags, additional_datadog_w3c_tracestate);
408+
std::string result =
409+
encode_datadog_tracestate(span_id, sampling_priority, origin, trace_tags,
410+
additional_datadog_w3c_tracestate);
391411

392412
if (additional_w3c_tracestate) {
393413
result += ',';

src/datadog/w3c_propagation.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,8 @@ std::string encode_traceparent(TraceID trace_id, std::uint64_t span_id,
3737

3838
// Return a value for the "tracestate" header containing the specified fields.
3939
std::string encode_tracestate(
40-
int sampling_priority, const Optional<std::string>& origin,
40+
uint64_t span_id, int sampling_priority,
41+
const Optional<std::string>& origin,
4142
const std::vector<std::pair<std::string, std::string>>& trace_tags,
4243
const Optional<std::string>& additional_datadog_w3c_tracestate,
4344
const Optional<std::string>& additional_w3c_tracestate);

test/test_span.cpp

Lines changed: 18 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -660,6 +660,7 @@ TEST_CASE("injecting W3C tracestate header") {
660660
// - sampling priority
661661
// - origin
662662
// - trace tags
663+
// - parent id
663664
// - extra fields (extracted from W3C)
664665
// - all of the above
665666
// - character substitutions:
@@ -704,79 +705,79 @@ TEST_CASE("injecting W3C tracestate header") {
704705
{__LINE__, "sampling priority",
705706
{{"x-datadog-trace-id", "1"}, {"x-datadog-parent-id", "1"},
706707
{"x-datadog-sampling-priority", "2"}},
707-
"dd=s:2"},
708+
"dd=s:2;p:$parent_id"},
708709

709710
{__LINE__, "origin",
710711
{{"x-datadog-trace-id", "1"}, {"x-datadog-parent-id", "1"},
711712
{"x-datadog-origin", "France"}},
712713
// The "s:-1" comes from the 0% sample rate.
713-
"dd=s:-1;o:France"},
714+
"dd=s:-1;p:$parent_id;o:France"},
714715

715716
{__LINE__, "trace tags",
716717
{{"x-datadog-trace-id", "1"}, {"x-datadog-parent-id", "1"},
717718
{"x-datadog-tags", "_dd.p.foo=x,_dd.p.bar=y,ignored=wrong_prefix"}},
718719
// The "s:-1" comes from the 0% sample rate.
719-
"dd=s:-1;t.foo:x;t.bar:y"},
720+
"dd=s:-1;p:$parent_id;t.foo:x;t.bar:y"},
720721

721722
{__LINE__, "extra fields",
722723
{{"traceparent", traceparent_drop}, {"tracestate", "dd=foo:bar;boing:boing"}},
723724
// The "s:0" comes from the sampling decision in `traceparent_drop`.
724-
"dd=s:0;foo:bar;boing:boing"},
725+
"dd=s:0;p:$parent_id;foo:bar;boing:boing"},
725726

726727
{__LINE__, "all of the above",
727728
{{"traceparent", traceparent_drop},
728729
{"tracestate", "dd=o:France;t.foo:x;t.bar:y;foo:bar;boing:boing"}},
729730
// The "s:0" comes from the sampling decision in `traceparent_drop`.
730-
"dd=s:0;o:France;t.foo:x;t.bar:y;foo:bar;boing:boing"},
731+
"dd=s:0;p:$parent_id;o:France;t.foo:x;t.bar:y;foo:bar;boing:boing"},
731732

732733
{__LINE__, "replace invalid characters in origin",
733734
{{"x-datadog-trace-id", "1"}, {"x-datadog-parent-id", "1"},
734735
{"x-datadog-origin", "France, is a country=nation; so is 台北."}},
735736
// The "s:-1" comes from the 0% sample rate.
736-
"dd=s:-1;o:France_ is a country~nation_ so is ______."},
737+
"dd=s:-1;p:$parent_id;o:France_ is a country~nation_ so is ______."},
737738

738739
{__LINE__, "replace invalid characters in trace tag key",
739740
{{"x-datadog-trace-id", "1"}, {"x-datadog-parent-id", "1"},
740741
{"x-datadog-tags", "_dd.p.a;d台北x =foo,_dd.p.ok=bar"}},
741742
// The "s:-1" comes from the 0% sample rate.
742-
"dd=s:-1;t.a_d______x_:foo;t.ok:bar"},
743+
"dd=s:-1;p:$parent_id;t.a_d______x_:foo;t.ok:bar"},
743744

744745
{__LINE__, "replace invalid characters in trace tag value",
745746
{{"x-datadog-trace-id", "1"}, {"x-datadog-parent-id", "1"},
746747
{"x-datadog-tags", "_dd.p.wacky=hello fr~d; how are คุณ?"}},
747748
// The "s:-1" comes from the 0% sample rate.
748-
"dd=s:-1;t.wacky:hello fr_d_ how are _________?"},
749+
"dd=s:-1;p:$parent_id;t.wacky:hello fr_d_ how are _________?"},
749750

750751
{__LINE__, "replace equal signs with tildes in trace tag value",
751752
{{"x-datadog-trace-id", "1"}, {"x-datadog-parent-id", "1"},
752753
{"x-datadog-tags", "_dd.p.base64_thingy=d2Fra2EhIHdhaw=="}},
753754
// The "s:-1" comes from the 0% sample rate.
754-
"dd=s:-1;t.base64_thingy:d2Fra2EhIHdhaw~~"},
755+
"dd=s:-1;p:$parent_id;t.base64_thingy:d2Fra2EhIHdhaw~~"},
755756

756757
{__LINE__, "oversized origin truncates it and subsequent fields",
757758
{{"x-datadog-trace-id", "1"}, {"x-datadog-parent-id", "1"},
758759
{"x-datadog-origin", "long cat is looooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooong"},
759760
{"x-datadog-tags", "_dd.p.foo=bar,_dd.p.honk=honk"}},
760761
// The "s:-1" comes from the 0% sample rate.
761-
"dd=s:-1"},
762+
"dd=s:-1;p:$parent_id"},
762763

763764
{__LINE__, "oversized trace tag truncates it and subsequent fields",
764765
{{"x-datadog-trace-id", "1"}, {"x-datadog-parent-id", "1"},
765766
{"x-datadog-tags", "_dd.p.foo=bar,_dd.p.long_cat_is=looooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooong,_dd.p.lost=forever"}},
766767
// The "s:-1" comes from the 0% sample rate.
767-
"dd=s:-1;t.foo:bar"},
768+
"dd=s:-1;p:$parent_id;t.foo:bar"},
768769

769770
{__LINE__, "oversized extra field truncates itself and subsequent fields",
770771
{{"traceparent", traceparent_drop},
771772
{"tracestate", "dd=foo:bar;long_cat_is:looooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooong;lost:forever"}},
772773
// The "s:0" comes from the sampling decision in `traceparent_drop`.
773-
"dd=s:0;foo:bar"},
774+
"dd=s:0;p:$parent_id;foo:bar"},
774775

775776
{__LINE__, "non-Datadog tracestate",
776777
{{"traceparent", traceparent_drop},
777778
{"tracestate", "foo=bar,boing=boing"}},
778779
// The "s:0" comes from the sampling decision in `traceparent_drop`.
779-
"dd=s:0,foo=bar,boing=boing"},
780+
"dd=s:0;p:$parent_id,foo=bar,boing=boing"},
780781
}));
781782
// clang-format on
782783

@@ -794,9 +795,13 @@ TEST_CASE("injecting W3C tracestate header") {
794795
span->inject(writer);
795796

796797
CAPTURE(writer.items);
798+
// TODO: substitue parent ID in tracestate
797799
const auto found = writer.items.find("tracestate");
798800
REQUIRE(found != writer.items.end());
799801

802+
test_case.expected_tracestate.replace(
803+
test_case.expected_tracestate.find("$parent_id"),
804+
sizeof("$parent_id") - 1, hex_padded(span->id()));
800805
REQUIRE(found->second == test_case.expected_tracestate);
801806

802807
REQUIRE(logger->error_count() == 0);

test/test_tracer.cpp

Lines changed: 43 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -736,6 +736,7 @@ TEST_CASE("span extraction") {
736736
std::vector<std::pair<std::string, std::string>> expected_trace_tags = {};
737737
Optional<std::string> expected_additional_w3c_tracestate = {};
738738
Optional<std::string> expected_additional_datadog_w3c_tracestate = {};
739+
Optional<std::string> expected_datadog_w3c_parent_id = {};
739740
};
740741

741742
static const std::string traceparent_prefix =
@@ -826,14 +827,28 @@ TEST_CASE("span extraction") {
826827
nullopt, // expected_additional_w3c_tracestate
827828
"foo:bar;baz:bam"}, // expected_additional_datadog_w3c_tracestate
828829

829-
{__LINE__, "origin, trace tags, and extra fields",
830+
{__LINE__, "origin, trace tags, parent, and extra fields",
830831
traceparent_drop, // traceparent
831-
"dd=o:France;t.foo:thing1;t.bar:thing2;x:wow;y:wow", // tracestate
832+
"dd=o:France;p:00000000000d69ac;t.foo:thing1;t.bar:thing2;x:wow;y:wow", // tracestate
832833
0, // expected_sampling_priority
833834
"France", // expected_origin
834835
{{"_dd.p.foo", "thing1"}, {"_dd.p.bar", "thing2"}}, // expected_trace_tags
835836
nullopt, // expected_additional_w3c_tracestate
836-
"x:wow;y:wow"}, // expected_additional_datadog_w3c_tracestate
837+
"x:wow;y:wow", // expected_additional_datadog_w3c_tracestate
838+
"00000000000d69ac", // expected_datadog_w3c_parent_id
839+
},
840+
841+
{__LINE__, "dd parent id is malformed (1/2)",
842+
traceparent_drop, // traceparent
843+
"dd=p:XxDDOGxX", // tracestate
844+
0, // expected_sampling_priority
845+
},
846+
847+
{__LINE__, "dd parent id is malformed (1/2)",
848+
traceparent_drop, // traceparent
849+
"dd=p:a4c928f8ad6d444b", // tracestate
850+
0, // expected_sampling_priority
851+
},
837852

838853
{__LINE__, "origin with escaped equal sign",
839854
traceparent_drop, // traceparent
@@ -921,11 +936,30 @@ TEST_CASE("span extraction") {
921936
test_case.expected_additional_w3c_tracestate);
922937
REQUIRE(extracted->additional_datadog_w3c_tracestate ==
923938
test_case.expected_additional_datadog_w3c_tracestate);
939+
REQUIRE(extracted->datadog_w3c_parent_id ==
940+
test_case.expected_datadog_w3c_parent_id);
924941

925942
REQUIRE(logger.entries.empty());
926943
REQUIRE(span_tags.empty());
927944
}
928945

946+
SECTION("_dd.parent_id") {
947+
auto finalized_config = finalize_config(config);
948+
REQUIRE(finalized_config);
949+
Tracer tracer{*finalized_config};
950+
951+
std::unordered_map<std::string, std::string> headers;
952+
headers["traceparent"] =
953+
"00-4bf92f3577b34da6a3ce929d0e0e4736-00f067aa0ba902b7-01";
954+
headers["tracestate"] = "dd=s:1;p:000000000000002a;foo:bar,lol=wut";
955+
MockDictReader reader{headers};
956+
const auto span = tracer.extract_span(reader);
957+
958+
auto parent_id_tag = span->lookup_tag("_dd.parent_id");
959+
REQUIRE(parent_id_tag);
960+
CHECK(*parent_id_tag == "000000000000002a");
961+
}
962+
929963
SECTION("x-datadog-tags") {
930964
auto finalized_config = finalize_config(config);
931965
REQUIRE(finalized_config);
@@ -1119,7 +1153,7 @@ TEST_CASE("128-bit trace IDs") {
11191153
// trace ID in the traceparent.
11201154
// It will be ignored, and the resulting _dd.p.tid value will be consistent
11211155
// with the higher part of the trace ID in traceparent: "deadbeefdeadbeef".
1122-
headers["tracestate"] = "dd=t.tid:" + tid;
1156+
headers["tracestate"] = "dd=t.tid:" + tid + ";p:0000000000000001";
11231157
MockDictReader reader{headers};
11241158
const auto span = tracer.extract_span(reader);
11251159
CAPTURE(logger->entries);
@@ -1629,7 +1663,7 @@ TEST_CASE("heterogeneous extraction") {
16291663
{{"traceparent", "00-4bf92f3577b34da6a3ce929d0e0e4736-00f067aa0ba902b7-01"},
16301664
{"tracestate", "dd=foo:bar,lol=wut"}},
16311665
{{"traceparent", "00-4bf92f3577b34da6a3ce929d0e0e4736-000000000000002a-01"},
1632-
{"tracestate", "dd=s:1;foo:bar,lol=wut"}}},
1666+
{"tracestate", "dd=s:1;p:000000000000002a;foo:bar,lol=wut"}}},
16331667

16341668
{__LINE__, "tracestate from subsequent style",
16351669
{PropagationStyle::DATADOG, PropagationStyle::W3C},
@@ -1639,7 +1673,7 @@ TEST_CASE("heterogeneous extraction") {
16391673
{"traceparent", "00-00000000000000000000000000000030-0000000000000040-01"},
16401674
{"tracestate", "competitor=stuff,dd=o:Nebraska;s:1;ah:choo"}}, // origin is different
16411675
{{"traceparent", "00-00000000000000000000000000000030-000000000000002a-01"},
1642-
{"tracestate", "dd=s:2;o:Kansas;ah:choo,competitor=stuff"}}},
1676+
{"tracestate", "dd=s:2;p:000000000000002a;o:Kansas;ah:choo,competitor=stuff"}}},
16431677

16441678
{__LINE__, "ignore interlopers",
16451679
{PropagationStyle::DATADOG, PropagationStyle::B3, PropagationStyle::W3C},
@@ -1652,7 +1686,7 @@ TEST_CASE("heterogeneous extraction") {
16521686
{"traceparent", "00-00000000000000000000000000000030-0000000000000040-01"},
16531687
{"tracestate", "competitor=stuff,dd=o:Nebraska;s:1;ah:choo"}},
16541688
{{"traceparent", "00-00000000000000000000000000000030-000000000000002a-01"},
1655-
{"tracestate", "dd=s:2;o:Kansas;ah:choo,competitor=stuff"}}},
1689+
{"tracestate", "dd=s:2;p:000000000000002a;o:Kansas;ah:choo,competitor=stuff"}}},
16561690

16571691
{__LINE__, "don't take tracestate if trace ID doesn't match",
16581692
{PropagationStyle::DATADOG, PropagationStyle::W3C},
@@ -1662,7 +1696,7 @@ TEST_CASE("heterogeneous extraction") {
16621696
{"traceparent", "00-00000000000000000000000000000031-0000000000000040-01"},
16631697
{"tracestate", "competitor=stuff,dd=o:Nebraska;s:1;ah:choo"}},
16641698
{{"traceparent", "00-00000000000000000000000000000030-000000000000002a-01"},
1665-
{"tracestate", "dd=s:2;o:Kansas"}}},
1699+
{"tracestate", "dd=s:2;p:000000000000002a;o:Kansas"}}},
16661700

16671701
{__LINE__, "don't take tracestate if W3C extraction isn't configured",
16681702
{PropagationStyle::DATADOG, PropagationStyle::B3},
@@ -1672,7 +1706,7 @@ TEST_CASE("heterogeneous extraction") {
16721706
{"traceparent", "00-00000000000000000000000000000030-0000000000000040-01"},
16731707
{"tracestate", "competitor=stuff,dd=o:Nebraska;s:1;ah:choo"}},
16741708
{{"traceparent", "00-00000000000000000000000000000030-000000000000002a-01"},
1675-
{"tracestate", "dd=s:2;o:Kansas"}}},
1709+
{"tracestate", "dd=s:2;p:000000000000002a;o:Kansas"}}},
16761710
}));
16771711
// clang-format on
16781712

0 commit comments

Comments
 (0)