Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
18 commits
Select commit Hold shift + click to select a range
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
2 changes: 2 additions & 0 deletions BUILD.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ cc_library(
"src/datadog/parse_util.cpp",
"src/datadog/parse_util.h",
"src/datadog/platform_util.h",
"src/datadog/propagation_behavior_extract.cpp",
"src/datadog/propagation_style.cpp",
"src/datadog/random.cpp",
"src/datadog/random.h",
Expand Down Expand Up @@ -122,6 +123,7 @@ cc_library(
"include/datadog/logger.h",
"include/datadog/null_collector.h",
"include/datadog/optional.h",
"include/datadog/propagation_behavior_extract.h",
"include/datadog/propagation_style.h",
"include/datadog/rate.h",
"include/datadog/remote_config/capability.h",
Expand Down
2 changes: 2 additions & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -151,6 +151,7 @@ target_sources(dd-trace-cpp-objects
include/datadog/logger.h
include/datadog/null_collector.h
include/datadog/optional.h
include/datadog/propagation_behavior_extract.h
include/datadog/propagation_style.h
include/datadog/rate.h
include/datadog/runtime_id.h
Expand Down Expand Up @@ -196,6 +197,7 @@ target_sources(dd-trace-cpp-objects
src/datadog/logger.cpp
src/datadog/msgpack.cpp
src/datadog/parse_util.cpp
src/datadog/propagation_behavior_extract.cpp
src/datadog/propagation_style.cpp
src/datadog/random.cpp
src/datadog/rate.cpp
Expand Down
1 change: 1 addition & 0 deletions include/datadog/config.h
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ enum class ConfigName : char {
TAGS,
EXTRACTION_STYLES,
INJECTION_STYLES,
PROPAGATION_BEHAVIOR_EXTRACT,
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 @@ -52,6 +52,7 @@ namespace environment {
ENV_DEFAULT_RESOLVED_IN_CODE("Defaults to process name when unset.")) \
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_STYLE_EXTRACT, ARRAY, \
"datadog,tracecontext,baggage") \
MACRO(DD_TRACE_PROPAGATION_STYLE_INJECT, ARRAY, \
Expand Down
36 changes: 36 additions & 0 deletions include/datadog/propagation_behavior_extract.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
#pragma once

// This component provides an `enum class`, `PropagationBehaviorExtract`, that
// indicates a trace context extraction or injection format to be used.
// `TracerConfig` has one `std::vector<PropagationStyle>` for extraction and
// another for injection. See `tracer_config.h`.

#include "optional.h"
#include "string_view.h"

// Undefine legacy Windows macro so it can be a value in the enum
#ifdef IGNORE
#undef IGNORE
#endif

namespace datadog {
namespace tracing {

enum class PropagationBehaviorExtract {
// Propagate extracted context normally
CONTINUE,
// Restart a new trace (new sampling decision, no parent)
// Reference previous trace through a span-link
RESTART,
// Discard entirely incoming context
IGNORE,
};

StringView to_string_view(PropagationBehaviorExtract behavior);

// defaults to CONTINUE if empty or unsupported
Optional<PropagationBehaviorExtract> parse_propagation_behavior_extract(
StringView text);

} // namespace tracing
} // namespace datadog
5 changes: 5 additions & 0 deletions include/datadog/tracer.h
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
#include "expected.h"
#include "id_generator.h"
#include "optional.h"
#include "propagation_behavior_extract.h"
#include "span.h"
#include "span_config.h"
#include "tracer_config.h"
Expand Down Expand Up @@ -45,6 +46,7 @@ class Tracer {
Clock clock_;
std::vector<PropagationStyle> injection_styles_;
std::vector<PropagationStyle> extraction_styles_;
PropagationBehaviorExtract propagation_behavior_extract_;
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 Expand Up @@ -75,6 +77,9 @@ class Tracer {
// `config`. If there is no tracing information in `reader`, then return an
// error with code `Error::NO_SPAN_TO_EXTRACT`. If a failure occurs, then
// return an error with some other code.
// Depending of the propagation_behavior_restart config, it can continue the
// trace, restart a new trace (with link), or discard the span (returning
// the same result as if there was no tracing information)
Expected<Span> extract_span(const DictReader& reader);
Expected<Span> extract_span(const DictReader& reader,
const SpanConfig& config);
Expand Down
11 changes: 11 additions & 0 deletions include/datadog/tracer_config.h
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
#include "datadog_agent_config.h"
#include "expected.h"
#include "http_endpoint_calculation_mode.h"
#include "propagation_behavior_extract.h"
#include "propagation_style.h"
#include "runtime_id.h"
#include "span_defaults.h"
Expand Down Expand Up @@ -107,6 +108,14 @@ struct TracerConfig {
// environment variables.
Optional<std::vector<PropagationStyle>> extraction_styles;

// `propagation_behavior_extract` indicates how to handle incoming trace
// context. `continue`: default behavior, all trace contexts are propagated.
// `restart`: restart a new trace (new sampling decision) with a span link to
// the remote one. baggage is propagated. `ignore`: discard entirely any
// existing trace context and start a new trace. Overridden by
// DD_TRACE_PROPAGATION_BEHAVIOR_EXTRACT
Optional<PropagationBehaviorExtract> propagation_behavior_extract;

// `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 @@ -224,6 +233,8 @@ class FinalizedTracerConfig final {
std::vector<PropagationStyle> injection_styles;
std::vector<PropagationStyle> extraction_styles;

PropagationBehaviorExtract propagation_behavior_extract;

bool report_hostname;
std::size_t tags_header_size;
std::shared_ptr<Logger> logger;
Expand Down
1 change: 1 addition & 0 deletions src/datadog/extracted_data.h
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ struct ExtractedData {
Optional<std::string> additional_datadog_w3c_tracestate;
// `style` is the extraction style used to obtain this `ExtractedData`. It's
// for diagnostics.
Optional<std::string> tracestate_full;
Optional<PropagationStyle> style;
// `headers_examined` are the name/value pairs of HTTP headers (or equivalent
// request meta-data) that were looked up and had values during the
Expand Down
44 changes: 44 additions & 0 deletions src/datadog/propagation_behavior_extract.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
#include <datadog/propagation_behavior_extract.h>

#include <cassert>

#include "json.hpp"
#include "string_util.h"

namespace datadog {
namespace tracing {

StringView to_string_view(PropagationBehaviorExtract behavior) {
switch (behavior) {
case PropagationBehaviorExtract::CONTINUE:
return "continue";
case PropagationBehaviorExtract::RESTART:
return "restart";
case PropagationBehaviorExtract::IGNORE:
return "ignore";
default:
std::abort();
}
}

nlohmann::json to_json(PropagationBehaviorExtract behavior) {
return to_string_view(behavior);
}

Optional<PropagationBehaviorExtract> parse_propagation_behavior_extract(
StringView text) {
auto token = std::string{text};
to_lower(token);

if (token == "continue" || token.empty()) {
return PropagationBehaviorExtract::CONTINUE;
} else if (token == "restart") {
return PropagationBehaviorExtract::RESTART;
} else if (token == "ignore") {
return PropagationBehaviorExtract::IGNORE;
}
return nullopt;
}

} // namespace tracing
} // namespace datadog
2 changes: 2 additions & 0 deletions src/datadog/telemetry/telemetry_impl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,8 @@ std::string to_string(datadog::tracing::ConfigName name) {
return "trace_propagation_style_extract";
case ConfigName::INJECTION_STYLES:
return "trace_propagation_style_inject";
case ConfigName::PROPAGATION_BEHAVIOR_EXTRACT:
return "trace_propagation_behavior_extract";
case ConfigName::STARTUP_LOGS:
return "trace_startup_logs_enabled";
case ConfigName::REPORT_TELEMETRY:
Expand Down
120 changes: 85 additions & 35 deletions src/datadog/tracer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
#include "root_session_id.h"
#include "span_data.h"
#include "span_sampler.h"
#include "string_util.h"
#include "tags.h"
#include "telemetry_metrics.h"
#include "trace_sampler.h"
Expand Down Expand Up @@ -61,6 +62,7 @@ Tracer::Tracer(const FinalizedTracerConfig& config,
clock_(config.clock),
injection_styles_(config.injection_styles),
extraction_styles_(config.extraction_styles),
propagation_behavior_extract_(config.propagation_behavior_extract),
tags_header_max_size_(config.tags_header_size),
baggage_opts_(config.baggage_opts),
baggage_injection_enabled_(false),
Expand All @@ -86,10 +88,12 @@ Tracer::Tracer(const FinalizedTracerConfig& config,
collector_ = agent;
}

for (const auto style : extraction_styles_) {
if (style == PropagationStyle::BAGGAGE) {
baggage_extraction_enabled_ = true;
break;
if (propagation_behavior_extract_ != PropagationBehaviorExtract::IGNORE) {
for (const auto style : extraction_styles_) {
if (style == PropagationStyle::BAGGAGE) {
baggage_extraction_enabled_ = true;
break;
}
}
}

Expand Down Expand Up @@ -172,7 +176,7 @@ void Tracer::store_config(

// clang-format off
msgpack::pack_map(
buffer,
buffer,
"schema_version", [&](auto& buffer) { msgpack::pack_integer(buffer, std::uint64_t(2)); return Expected<void>{}; },
"runtime_id", [&](auto& buffer) { return msgpack::pack_string(buffer, runtime_id_.string()); },
"tracer_version", [&](auto& buffer) { return msgpack::pack_string(buffer, signature_.library_version); },
Expand Down Expand Up @@ -229,6 +233,13 @@ Expected<Span> Tracer::extract_span(const DictReader& reader) {

Expected<Span> Tracer::extract_span(const DictReader& reader,
const SpanConfig& config) {
// ignore: Discard incoming context, new span with new sampling decision
if (propagation_behavior_extract_ == PropagationBehaviorExtract::IGNORE) {
return Error{
Error::NO_SPAN_TO_EXTRACT,
"Ignoring context extraction (propagation_behavior_extract=ignore)"};
}

assert(!extraction_styles_.empty());

AuditedReader audited_reader{reader};
Expand Down Expand Up @@ -417,37 +428,76 @@ Expected<Span> Tracer::extract_span(const DictReader& reader,
merged_context.trace_tags.erase(found);
}

// When APM Tracing is disabled, the incoming sampling decision MAY be
// overridden based on locally generated spans. As such, the received sampling
// decision is intentionally ignored, and the tracer is expected to make its
// own decision in accordance with the locally enabled product configuration.
Optional<SamplingDecision> sampling_decision;
if (tracing_enabled_ && merged_context.sampling_priority) {
SamplingDecision decision;
decision.priority = *merged_context.sampling_priority;
// `decision.mechanism` is null. We might be able to infer it once we
// extract `trace_tags`, but we would have no use for it, so we won't.
decision.origin = SamplingDecision::Origin::EXTRACTED;

sampling_decision = decision;
}
switch (propagation_behavior_extract_) {
case PropagationBehaviorExtract::CONTINUE: {
// When APM Tracing is disabled, the incoming sampling decision MAY be
// overridden based on locally generated spans. As such, the received
// sampling decision is intentionally ignored, and the tracer is expected
// to make its own decision in accordance with the locally enabled product
// configuration.
Optional<SamplingDecision> sampling_decision;
if (tracing_enabled_ && merged_context.sampling_priority) {
SamplingDecision decision;
decision.priority = *merged_context.sampling_priority;
// `decision.mechanism` is null. We might be able to infer it once we
// extract `trace_tags`, but we would have no use for it, so we won't.
decision.origin = SamplingDecision::Origin::EXTRACTED;
sampling_decision = decision;
}

const auto span_data_ptr = span_data.get();
telemetry::counter::increment(metrics::tracer::trace_segments_created,
{"new_continued:continued"});
const auto segment = std::make_shared<TraceSegment>(
logger_, collector_, config_manager_->trace_sampler(), span_sampler_,
config_manager_->span_defaults(), config_manager_, runtime_id_,
injection_styles_, hostname_, std::move(merged_context.origin),
tags_header_max_size_, std::move(merged_context.trace_tags),
std::move(sampling_decision),
std::move(merged_context.additional_w3c_tracestate),
std::move(merged_context.additional_datadog_w3c_tracestate),
std::move(span_data), resource_renaming_mode_, tracing_enabled_);
Span span{span_data_ptr, segment,
[generator = generator_]() { return generator->span_id(); },
clock_};
return span;
const auto span_data_ptr = span_data.get();
telemetry::counter::increment(metrics::tracer::trace_segments_created,
{"new_continued:continued"});
const auto segment = std::make_shared<TraceSegment>(
logger_, collector_, config_manager_->trace_sampler(), span_sampler_,
config_manager_->span_defaults(), config_manager_, runtime_id_,
injection_styles_, hostname_, std::move(merged_context.origin),
tags_header_max_size_, std::move(merged_context.trace_tags),
std::move(sampling_decision),
std::move(merged_context.additional_w3c_tracestate),
std::move(merged_context.additional_datadog_w3c_tracestate),
std::move(span_data), resource_renaming_mode_, tracing_enabled_);

Span span{span_data_ptr, segment,
[generator = generator_]() { return generator->span_id(); },
clock_};
return span;
}
case PropagationBehaviorExtract::RESTART: {
// restart: create a new trace, with a span link to the previous one

std::string context_headers{to_string_view(*first_style_with_trace_id)};
to_lower(context_headers);
auto link_attributes = SpanLinkAttributes{};
link_attributes.emplace("reason", "propagation_behavior_extract");
link_attributes.emplace("context_headers", std::move(context_headers));

Optional<std::string> tracestate;
if (const auto w3c = extracted_contexts.find(PropagationStyle::W3C);
w3c != extracted_contexts.end() &&
w3c->second.trace_id == span_data->trace_id) {
tracestate = w3c->second.tracestate_full;
}

Optional<std::uint32_t> flags =
merged_context.sampling_priority
? Optional<std::uint32_t>(
*merged_context.sampling_priority > 0 ? 1u : 0u)
: nullopt;

auto restarted_span = create_span(config);
Comment thread
MilanGarnier marked this conversation as resolved.
restarted_span.add_link(
SpanContext{span_data->trace_id, span_data->parent_id, tracestate,
flags},
link_attributes);
return restarted_span;
}
default:
// Should be unreachable
return Error{
Error::NO_SPAN_TO_EXTRACT,
"Ignoring context extraction (propagation_behavior_extract=ignore)"};
}
}

Span Tracer::extract_or_create_span(const DictReader& reader) {
Expand Down
Loading