Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions include/datadog/config.h
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ enum class ConfigName : char {
EXTRACTION_STYLES,
INJECTION_STYLES,
PROPAGATION_BEHAVIOR_EXTRACT,
PROPAGATION_EXTRACT_FIRST,
STARTUP_LOGS,
REPORT_TELEMETRY,
DELEGATE_SAMPLING,
Expand Down
1 change: 1 addition & 0 deletions include/datadog/environment.h
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@ namespace environment {
MACRO(DD_SPAN_SAMPLING_RULES, ARRAY, "[]") \
MACRO(DD_SPAN_SAMPLING_RULES_FILE, STRING, "") \
MACRO(DD_TRACE_PROPAGATION_BEHAVIOR_EXTRACT, STRING, "continue") \
MACRO(DD_TRACE_PROPAGATION_EXTRACT_FIRST, BOOLEAN, false) \
MACRO(DD_TRACE_PROPAGATION_STYLE_EXTRACT, ARRAY, \
"datadog,tracecontext,baggage") \
MACRO(DD_TRACE_PROPAGATION_STYLE_INJECT, ARRAY, \
Expand Down
1 change: 1 addition & 0 deletions include/datadog/tracer.h
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ class Tracer {
std::vector<PropagationStyle> injection_styles_;
std::vector<PropagationStyle> extraction_styles_;
PropagationBehaviorExtract propagation_behavior_extract_;
bool propagation_extract_first_;
Optional<std::string> hostname_;
std::size_t tags_header_max_size_;
// Store the tracer configuration in an in-memory file, allowing it to be
Expand Down
6 changes: 6 additions & 0 deletions include/datadog/tracer_config.h
Original file line number Diff line number Diff line change
Expand Up @@ -116,6 +116,11 @@ struct TracerConfig {
// DD_TRACE_PROPAGATION_BEHAVIOR_EXTRACT
Optional<PropagationBehaviorExtract> propagation_behavior_extract;

// `propagation_extract_first` indicates whether extraction stops after the
// first successful trace context. Overridden by
// DD_TRACE_PROPAGATION_EXTRACT_FIRST.
Optional<bool> propagation_extract_first;

// `report_hostname` indicates whether the tracer will include the result of
// `gethostname` with traces sent to the collector.
Optional<bool> report_hostname;
Expand Down Expand Up @@ -234,6 +239,7 @@ class FinalizedTracerConfig final {
std::vector<PropagationStyle> extraction_styles;

PropagationBehaviorExtract propagation_behavior_extract;
bool propagation_extract_first;

bool report_hostname;
std::size_t tags_header_size;
Expand Down
2 changes: 2 additions & 0 deletions src/datadog/telemetry/telemetry_impl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,8 @@ std::string to_string(datadog::tracing::ConfigName name) {
return "trace_propagation_style_inject";
case ConfigName::PROPAGATION_BEHAVIOR_EXTRACT:
return "trace_propagation_behavior_extract";
case ConfigName::PROPAGATION_EXTRACT_FIRST:
return "trace_propagation_extract_first";
case ConfigName::STARTUP_LOGS:
return "trace_startup_logs_enabled";
case ConfigName::REPORT_TELEMETRY:
Expand Down
8 changes: 7 additions & 1 deletion src/datadog/tracer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,7 @@ Tracer::Tracer(const FinalizedTracerConfig& config,
injection_styles_(config.injection_styles),
extraction_styles_(config.extraction_styles),
propagation_behavior_extract_(config.propagation_behavior_extract),
propagation_extract_first_(config.propagation_extract_first),
tags_header_max_size_(config.tags_header_size),
baggage_opts_(config.baggage_opts),
baggage_injection_enabled_(false),
Expand Down Expand Up @@ -309,7 +310,8 @@ Expected<Span> Tracer::extract_span(const DictReader& reader,
telemetry::counter::increment(metrics::tracer::trace_context::extracted,
{extracted_tag});

if (!first_style_with_trace_id && data->trace_id.has_value()) {
const bool extracted_trace_context = data->trace_id.has_value();
if (!first_style_with_trace_id && extracted_trace_context) {
first_style_with_trace_id = style;
}

Expand All @@ -319,6 +321,10 @@ Expected<Span> Tracer::extract_span(const DictReader& reader,

data->headers_examined = audited_reader.entries_found;
extracted_contexts.emplace(style, std::move(*data));

if (propagation_extract_first_ && extracted_trace_context) {
break;
}
}

ExtractedData merged_context;
Expand Down
11 changes: 11 additions & 0 deletions src/datadog/tracer_config.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -243,6 +243,11 @@ Expected<TracerConfig> load_tracer_env_config(Logger &logger) {
propagation_behavior_extract.value());
}

if (auto propagation_extract_first =
lookup(environment::DD_TRACE_PROPAGATION_EXTRACT_FIRST)) {
env_cfg.propagation_extract_first = !falsy(*propagation_extract_first);
}

try {
const auto global_styles =
styles_from_env(environment::DD_TRACE_PROPAGATION_STYLE);
Expand Down Expand Up @@ -421,6 +426,12 @@ Expected<FinalizedTracerConfig> finalize_config(const TracerConfig &user_config,
return std::string{to_string_view(behavior)};
});

final_config.propagation_extract_first = resolve_and_record_config(
env_config->propagation_extract_first,
user_config.propagation_extract_first, &final_config.metadata,
ConfigName::PROPAGATION_EXTRACT_FIRST, false,
[](const bool &value) { return to_string(value); });

final_config.runtime_id = user_config.runtime_id;
final_config.root_session_id = user_config.root_session_id;
final_config.process_tags = user_config.process_tags;
Expand Down
7 changes: 7 additions & 0 deletions supported-configurations.json
Original file line number Diff line number Diff line change
Expand Up @@ -210,6 +210,13 @@
"type": "string"
}
],
"DD_TRACE_PROPAGATION_EXTRACT_FIRST": [
{
"default": "false",
"implementation": "A",
"type": "boolean"
}
],
"DD_TRACE_PROPAGATION_STYLE": [
{
"default": "datadog,tracecontext,baggage",
Expand Down
46 changes: 46 additions & 0 deletions test/test_tracer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@
#include <type_traits>
#include <utility>

#include "common/environment.h"
#include "matchers.h"
#include "mocks/collectors.h"
#include "mocks/dict_readers.h"
Expand Down Expand Up @@ -70,6 +71,9 @@ TEST_TRACER("tracer span defaults") {
config.name = "test.thing";
config.tags = {{"some.thing", "thing value"},
{"another.thing", "another value"}};
// The test does not cover telemetry. Disabling it prevents an asynchronous
// request to a local agent from racing the logger assertion below.
config.telemetry.enabled = false;

const auto collector = std::make_shared<MockCollector>();
config.collector = collector;
Expand Down Expand Up @@ -1545,6 +1549,32 @@ TEST_TRACER("span extraction") {
}
}

TEST_TRACER(
"extract first tries later styles after an unsuccessful extraction") {
const datadog::test::EnvGuard guard{"DD_TRACE_PROPAGATION_EXTRACT_FIRST",
"true"};
TracerConfig config;
config.service = "testsvc";
config.collector = std::make_shared<NullCollector>();
config.telemetry.enabled = false;
config.extraction_styles = std::vector<PropagationStyle>{
PropagationStyle::DATADOG, PropagationStyle::W3C};

const auto finalized_config = finalize_config(config);
REQUIRE(finalized_config);
Tracer tracer{*finalized_config};

const std::unordered_map<std::string, std::string> headers{
{"traceparent",
"00-00000000000000000000000000000001-0000000000000002-01"},
};

MockDictReader reader{headers};
const auto span = tracer.extract_span(reader);
REQUIRE(span);
CHECK(span->parent_id() == 2);
}

TEST_TRACER("continue extraction resumes the extracted trace") {
TracerConfig config;
config.service = "testsvc";
Expand Down Expand Up @@ -2019,6 +2049,7 @@ TEST_TRACER("heterogeneous extraction") {
std::vector<PropagationStyle> injection_styles;
std::unordered_map<std::string, std::string> extracted_headers;
std::unordered_map<std::string, std::string> expected_injected_headers;
bool extract_first = false;
};

// clang-format off
Expand All @@ -2041,6 +2072,17 @@ TEST_TRACER("heterogeneous extraction") {
{{"traceparent", "00-00000000000000000000000000000030-000000000000002a-01"},
{"tracestate", "dd=s:2;p:000000000000002a;o:Kansas;ah:choo,competitor=stuff"}}},

{__LINE__, "extract first ignores tracestate from subsequent style",
{PropagationStyle::DATADOG, PropagationStyle::W3C},
{PropagationStyle::W3C},
{{"x-datadog-trace-id", "48"}, {"x-datadog-parent-id", "64"},
{"x-datadog-origin", "Kansas"}, {"x-datadog-sampling-priority", "2"},
{"traceparent", "00-00000000000000000000000000000030-0000000000000040-01"},
{"tracestate", "competitor=stuff,dd=o:Nebraska;s:1;ah:choo"}},
{{"traceparent", "00-00000000000000000000000000000030-000000000000002a-01"},
{"tracestate", "dd=s:2;p:000000000000002a;o:Kansas"}},
true},

{__LINE__, "ignore interlopers",
{PropagationStyle::DATADOG, PropagationStyle::B3, PropagationStyle::W3C},
{PropagationStyle::W3C},
Expand Down Expand Up @@ -2087,8 +2129,12 @@ TEST_TRACER("heterogeneous extraction") {
config.service = "testsvc";
config.extraction_styles = test_case.extraction_styles;
config.injection_styles = test_case.injection_styles;
config.telemetry.enabled = false;
config.logger = std::make_shared<NullLogger>();

const datadog::test::EnvGuard extract_first{
"DD_TRACE_PROPAGATION_EXTRACT_FIRST",
test_case.extract_first ? "true" : "false"};
auto finalized_config = finalize_config(config);
REQUIRE(finalized_config);
Tracer tracer{*finalized_config, std::make_shared<MockIDGenerator>()};
Expand Down
49 changes: 49 additions & 0 deletions test/test_tracer_config.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1378,6 +1378,55 @@ TRACER_CONFIG_TEST("TracerConfig propagation behavior extract") {
}
}

TRACER_CONFIG_TEST("TracerConfig propagation extract first") {
TracerConfig config;
config.service = "testsvc";

const auto extract_first_metadata =
[](const FinalizedTracerConfig& finalized) {
return finalized.metadata.at(ConfigName::PROPAGATION_EXTRACT_FIRST);
};

SECTION("defaults to false") {
const auto finalized = finalize_config(config);
REQUIRE(finalized);
CHECK_FALSE(finalized->propagation_extract_first);

const auto& metadata = extract_first_metadata(*finalized);
REQUIRE(metadata.size() == 1);
CHECK(metadata.back().origin == ConfigMetadata::Origin::DEFAULT);
CHECK(metadata.back().value == "false");
}

SECTION("uses programmatic configuration") {
config.propagation_extract_first = true;

const auto finalized = finalize_config(config);
REQUIRE(finalized);
CHECK(finalized->propagation_extract_first);

const auto& metadata = extract_first_metadata(*finalized);
REQUIRE(metadata.size() == 2);
CHECK(metadata.back().origin == ConfigMetadata::Origin::CODE);
CHECK(metadata.back().value == "true");
}

SECTION("environment configuration overrides programmatic configuration") {
const EnvGuard guard{"DD_TRACE_PROPAGATION_EXTRACT_FIRST", "true"};
config.propagation_extract_first = false;

const auto finalized = finalize_config(config);
REQUIRE(finalized);
CHECK(finalized->propagation_extract_first);

const auto& metadata = extract_first_metadata(*finalized);
REQUIRE(metadata.size() == 3);
CHECK(metadata.back().origin ==
ConfigMetadata::Origin::ENVIRONMENT_VARIABLE);
CHECK(metadata.back().value == "true");
}
}

TRACER_CONFIG_TEST("configure 128-bit trace IDs") {
TracerConfig config;
config.service = "testsvc";
Expand Down
Loading