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
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 @@ -121,6 +122,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
2 changes: 1 addition & 1 deletion bin/check-format
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
#!/bin/sh

find binding/ examples/ fuzz/ include/ src/ test/ -type f \( -name '*.h' -o -name '*.cpp' \) -print0 | \
xargs -0 clang-format-14 --style=file --dry-run -Werror
xargs -0 clang-format --style=file --dry-run -Werror
4 changes: 2 additions & 2 deletions bin/cmake-build
Original file line number Diff line number Diff line change
Expand Up @@ -6,5 +6,5 @@ cd "$(dirname "$0")"/..

mkdir -p .build
cd .build
cmake .. "$@"
make -j "$(nproc)"
cmake -B . .. -DCMAKE_EXPORT_COMPILE_COMMANDS=ON "$@"
cmake --build . -j
1 change: 0 additions & 1 deletion examples/http-server/proxy/proxy.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@

#include <csignal>
#include <iostream>
#include <optional>
#include <string_view>

#include "datadog/cerr_logger.h"
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
31 changes: 31 additions & 0 deletions include/datadog/propagation_behavior_extract.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
#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"

namespace datadog {
namespace tracing {

enum class PropagationBehaviorExtract {
// Propagate extract4ed 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
// `Error::NO_SPAN_TO_EXTRACT`)
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
42 changes: 42 additions & 0 deletions src/datadog/propagation_behavior_extract.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
#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";
}
}

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
13 changes: 5 additions & 8 deletions src/datadog/span.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -166,10 +166,8 @@ void Span::set_source(Source source) {
}

void Span::add_link(const Span& linked, const SpanLinkAttributes& attrs) {
SpanLink link;
link.trace_id = linked.trace_id();
link.span_id = linked.id();
link.attributes = attrs;
Optional<std::string> tracestate = nullopt;
Optional<std::uint32_t> flags = nullopt;

// Capture injected headers (tracestate, traceparent flags) into a map.
struct : DictWriter {
Expand All @@ -180,21 +178,20 @@ void Span::add_link(const Span& linked, const SpanLinkAttributes& attrs) {
} w;
linked.inject(w);
if (auto it = w.map.find("tracestate"); it != w.map.end()) {
link.tracestate = it->second;
tracestate = it->second;
}
if (auto it = w.map.find("traceparent"); it != w.map.end()) {
const auto& tp = it->second;
const auto pos = tp.rfind('-');
if (pos != std::string::npos && pos + 1 < tp.size()) {
try {
link.flags = static_cast<std::uint32_t>(
flags = static_cast<std::uint32_t>(
std::stoul(tp.substr(pos + 1), nullptr, 16));
} catch (...) {
}
}
}

data_->span_links.push_back(std::move(link));
add_link(SpanLink{linked.trace_id(), linked.id(), tracestate, attrs, flags});
}

void Span::add_link(const SpanLink& link) { data_->span_links.push_back(link); }
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 @@ -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
106 changes: 71 additions & 35 deletions src/datadog/tracer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,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 +87,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 +175,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 +232,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 +427,63 @@ 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

auto link_attributes = SpanLinkAttributes{};
link_attributes.emplace("reason", "propagation_behavior_extract");
link_attributes.emplace("context_headers", "todo");

auto tracestate =
extracted_contexts[PropagationStyle::W3C].tracestate_full;

auto restarted_span = create_span(config);
restarted_span.add_link(SpanLink{span_data->trace_id, span_data->span_id,
tracestate, link_attributes,
nullopt}); // TODO: flags
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
Loading