Skip to content

Commit 9b2ac56

Browse files
authored
pass Datadog's internal shared tests for 128-bit trace IDs (#41)
* don't propagate invalid _dd.p.tid * "_dd.propagation_error:malformed_tid xxxxx" * 128-bit trace IDs contain unix timestamp * always include _dd.p.tid with 128-bit trace IDs * manual.keep and manual.drop are not special * note when _dd.p.tid disagrees with trace ID * note when _dd.p.tid cannot be parsed * work around the poorly named "-Wunused-lambda-capture" * capture unit test case context * work around a spurious compiler warning * Permit unused-lambda-capture on Clang only. It doesn't exist in GCC.
1 parent 0536a16 commit 9b2ac56

File tree

9 files changed

+190
-54
lines changed

9 files changed

+190
-54
lines changed

CMakeLists.txt

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,12 @@ set(CMAKE_CXX_EXTENSIONS OFF)
2121
# <https://gcc.gnu.org/bugzilla/show_bug.cgi?id=108088>.
2222
add_compile_options(-Wno-error=free-nonheap-object)
2323

24+
# This warning has a false positive with clang. See
25+
# <https://stackoverflow.com/questions/52416362>.
26+
if (CMAKE_CXX_COMPILER_ID STREQUAL "Clang")
27+
add_compile_options(-Wno-error=unused-lambda-capture)
28+
endif()
29+
2430
# If we're building with clang, then use the libc++ version of the standard
2531
# library instead of libstdc++. Better coverage of build configurations.
2632
#

src/datadog/id_generator.cpp

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
#include "id_generator.h"
22

33
#include <bitset>
4+
#include <chrono>
45

56
#include "random.h"
67

@@ -15,11 +16,17 @@ class DefaultIDGenerator : public IDGenerator {
1516
explicit DefaultIDGenerator(bool trace_id_128_bit)
1617
: trace_id_128_bit_(trace_id_128_bit) {}
1718

18-
TraceID trace_id() const override {
19+
TraceID trace_id(const TimePoint& start) const override {
1920
TraceID result;
2021
result.low = random_uint64();
2122
if (trace_id_128_bit_) {
22-
result.high = random_uint64();
23+
// Highest 32 bits contain a unix timestamp (the trace start time).
24+
const auto since_epoch = start.wall.time_since_epoch();
25+
const auto seconds =
26+
std::chrono::duration_cast<std::chrono::seconds>(since_epoch).count();
27+
// The farthest we'll go back is the unix epoch.
28+
const std::uint64_t unsigned_seconds = seconds < 0 ? 0 : seconds;
29+
result.high = unsigned_seconds << 32;
2330
} else {
2431
// In 64-bit mode, zero the most significant bit for compatibility with
2532
// older tracers that can't accept values above

src/datadog/id_generator.h

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
#include <cstdint>
1313
#include <memory>
1414

15+
#include "clock.h"
1516
#include "trace_id.h"
1617

1718
namespace datadog {
@@ -21,8 +22,10 @@ class IDGenerator {
2122
public:
2223
virtual ~IDGenerator() = default;
2324

25+
// Generate a span ID.
2426
virtual std::uint64_t span_id() const = 0;
25-
virtual TraceID trace_id() const = 0;
27+
// Generate a trace ID for a trace having the specified `start` time.
28+
virtual TraceID trace_id(const TimePoint& start) const = 0;
2629
};
2730

2831
std::shared_ptr<const IDGenerator> default_id_generator(bool trace_id_128_bit);

src/datadog/tags.cpp

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,6 @@ const std::string service_name = "service.name";
1111
const std::string span_type = "span.type";
1212
const std::string operation_name = "operation";
1313
const std::string resource_name = "resource.name";
14-
const std::string manual_keep = "manual.keep";
15-
const std::string manual_drop = "manual.drop";
1614
const std::string version = "version";
1715

1816
namespace internal {

src/datadog/tags.h

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,8 +16,6 @@ extern const std::string service_name;
1616
extern const std::string span_type;
1717
extern const std::string operation_name;
1818
extern const std::string resource_name;
19-
extern const std::string manual_keep;
20-
extern const std::string manual_drop;
2119
extern const std::string version;
2220

2321
namespace internal {

src/datadog/tracer.cpp

Lines changed: 31 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -54,11 +54,13 @@ void handle_trace_tags(StringView trace_tags, ExtractedData& result,
5454
error->with_prefix("Unable to parse high bits of the trace ID in "
5555
"Datadog style from the "
5656
"\"_dd.p.tid\" trace tag: "));
57-
span_tags[tags::internal::propagation_error] = "decoding_error";
57+
span_tags[tags::internal::propagation_error] = "malformed_tid " + value;
58+
continue;
5859
}
59-
// Note that this assumes the lower 64 bits of the trace ID have already
60-
// been extracted (i.e. we look for X-Datadog-Trace-ID first).
60+
6161
if (result.trace_id) {
62+
// Note that this assumes the lower 64 bits of the trace ID have already
63+
// been extracted (i.e. we look for X-Datadog-Trace-ID first).
6264
result.trace_id->high = *high;
6365
}
6466
}
@@ -269,7 +271,7 @@ Span Tracer::create_span(const SpanConfig& config) {
269271
auto span_data = std::make_unique<SpanData>();
270272
span_data->apply_config(*defaults_, config, clock_);
271273
std::vector<std::pair<std::string, std::string>> trace_tags;
272-
span_data->trace_id = generator_->trace_id();
274+
span_data->trace_id = generator_->trace_id(span_data->start);
273275
if (span_data->trace_id.high) {
274276
trace_tags.emplace_back(tags::internal::trace_id_high,
275277
hex(span_data->trace_id.high));
@@ -392,6 +394,31 @@ Expected<Span> Tracer::extract_span(const DictReader& reader,
392394
span_data->trace_id = *trace_id;
393395
span_data->parent_id = *parent_id;
394396

397+
if (span_data->trace_id.high) {
398+
// The trace ID has some bits set in the higher 64 bits. Set the
399+
// corresponding `trace_id_high` tag, so that the Datadog backend is aware
400+
// of those bits.
401+
//
402+
// First, though, if the `trace_id_high` tag is already set and has a bogus
403+
// value or a value inconsistent with the trace ID, tag an error.
404+
const std::string hex_high = hex(span_data->trace_id.high);
405+
const auto extant = std::find_if(
406+
trace_tags.begin(), trace_tags.end(), [&](const auto& pair) {
407+
return pair.first == tags::internal::trace_id_high;
408+
});
409+
if (extant == trace_tags.end()) {
410+
trace_tags.emplace_back(tags::internal::trace_id_high, hex_high);
411+
} else if (!parse_uint64(extant->second, 16)) {
412+
span_data->tags[tags::internal::propagation_error] =
413+
"malformed_tid " + extant->second;
414+
extant->second = hex_high;
415+
} else if (extant->second != hex_high) {
416+
span_data->tags[tags::internal::propagation_error] =
417+
"inconsistent_tid " + extant->second;
418+
extant->second = hex_high;
419+
}
420+
}
421+
395422
Optional<SamplingDecision> sampling_decision;
396423
if (sampling_priority) {
397424
SamplingDecision decision;

src/datadog/w3c_propagation.cpp

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -214,12 +214,6 @@ void parse_datadog_tracestate(ExtractedData& result, StringView datadog_value) {
214214
const auto tag_suffix = key.substr(2);
215215
std::string tag_name = "_dd.p.";
216216
append(tag_name, tag_suffix);
217-
// The "_dd.p.tid" trace tag is ignored in this context, since its value
218-
// is better inferred from the higher 64 bits of the trace ID.
219-
if (tag_name == "_dd.p.tid") {
220-
pair_begin = pair_end == end ? end : pair_end + 1;
221-
continue;
222-
}
223217
// The tag value was encoded with all '=' replaced by '~'. Undo that
224218
// transformation.
225219
std::string decoded_value{value};

test/test_span.cpp

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -373,7 +373,7 @@ TEST_CASE("injection") {
373373
struct Generator : public IDGenerator {
374374
const std::uint64_t id;
375375
explicit Generator(std::uint64_t id) : id(id) {}
376-
TraceID trace_id() const override { return TraceID(id); }
376+
TraceID trace_id(const TimePoint&) const override { return TraceID(id); }
377377
std::uint64_t span_id() const override { return id; }
378378
};
379379
Tracer tracer{*finalized_config, std::make_shared<Generator>(42)};
@@ -478,7 +478,7 @@ TEST_CASE("injecting W3C traceparent header") {
478478
struct Generator : public IDGenerator {
479479
const std::uint64_t id;
480480
explicit Generator(std::uint64_t id) : id(id) {}
481-
TraceID trace_id() const override { return TraceID(id); }
481+
TraceID trace_id(const TimePoint&) const override { return TraceID(id); }
482482
std::uint64_t span_id() const override { return id; }
483483
};
484484
Tracer tracer{*finalized_config,
@@ -515,7 +515,7 @@ TEST_CASE("injecting W3C traceparent header") {
515515
struct Generator : public IDGenerator {
516516
const std::uint64_t id;
517517
explicit Generator(std::uint64_t id) : id(id) {}
518-
TraceID trace_id() const override { return TraceID(id); }
518+
TraceID trace_id(const TimePoint&) const override { return TraceID(id); }
519519
std::uint64_t span_id() const override { return id; }
520520
};
521521
Tracer tracer{*finalized_config, std::make_shared<Generator>(expected_id)};
@@ -715,7 +715,7 @@ TEST_CASE("128-bit trace ID injection") {
715715

716716
public:
717717
explicit MockIDGenerator(TraceID trace_id) : trace_id_(trace_id) {}
718-
TraceID trace_id() const override { return trace_id_; }
718+
TraceID trace_id(const TimePoint&) const override { return trace_id_; }
719719
// `span_id` won't be called, because root spans use the lower part of
720720
// `trace_id` for the span ID.
721721
std::uint64_t span_id() const override { return 42; }

0 commit comments

Comments
 (0)