Skip to content
Open
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
9 changes: 7 additions & 2 deletions include/datadog/environment.h
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,8 @@ namespace environment {
MACRO(DD_APM_TRACING_ENABLED, BOOLEAN, true) \
MACRO(DD_TRACE_RESOURCE_RENAMING_ENABLED, BOOLEAN, false) \
MACRO(DD_TRACE_RESOURCE_RENAMING_ALWAYS_SIMPLIFIED_ENDPOINT, BOOLEAN, false) \
MACRO(DD_EXTERNAL_ENV, STRING, "")
MACRO(DD_EXTERNAL_ENV, STRING, "") \
MACRO(_DD_ROOT_CPP_SESSION_ID, STRING, nullptr)

#define ENV_DEFAULT_RESOLVED_IN_CODE(X) X
#define WITH_COMMA(ARG, TYPE, DEFAULT_VALUE) ARG,
Expand All @@ -95,7 +96,7 @@ enum Variable { DD_LIST_ENVIRONMENT_VARIABLES(WITH_COMMA) };
#define QUOTED_WITH_COMMA(ARG, TYPE, DEFAULT_VALUE) \
WITH_COMMA(QUOTED(ARG), TYPE, DEFAULT_VALUE)

inline const char *const variable_names[] = {
inline const char* const variable_names[] = {
DD_LIST_ENVIRONMENT_VARIABLES(QUOTED_WITH_COMMA)};

#undef QUOTED
Expand All @@ -110,6 +111,10 @@ StringView name(Variable variable);
// `nullopt` if that variable is not set in the environment.
Optional<StringView> lookup(Variable variable);

// Set the specified environment `variable` to `value`. Does not overwrite if
// already set (equivalent to setenv(..., 0) on POSIX).
void set(Variable variable, StringView value);

std::string to_json();

} // namespace environment
Expand Down
1 change: 1 addition & 0 deletions include/datadog/tracer_config.h
Original file line number Diff line number Diff line change
Expand Up @@ -225,6 +225,7 @@ class FinalizedTracerConfig final {
bool log_on_startup;
bool generate_128bit_trace_ids;
Optional<RuntimeID> runtime_id;
Optional<std::string> root_session_id;
Clock clock;
std::string integration_name;
std::string integration_version;
Expand Down
5 changes: 4 additions & 1 deletion include/datadog/tracer_signature.h
Original file line number Diff line number Diff line change
Expand Up @@ -33,15 +33,18 @@ namespace tracing {

struct TracerSignature {
RuntimeID runtime_id;
std::string root_session_id;
std::string default_service;
std::string default_environment;
std::string library_version;
StringView library_language;
StringView library_language_version;

TracerSignature() = delete;
TracerSignature(RuntimeID id, std::string service, std::string environment)
TracerSignature(RuntimeID id, std::string root_session, std::string service,
std::string environment)
: runtime_id(id),
root_session_id(std::move(root_session)),
default_service(std::move(service)),
default_environment(std::move(environment)),
library_version(tracer_version),
Expand Down
17 changes: 13 additions & 4 deletions src/datadog/environment.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -11,19 +11,28 @@ namespace environment {
StringView name(Variable variable) { return variable_names[variable]; }

Optional<StringView> lookup(Variable variable) {
const char *name = variable_names[variable];
const char *value = std::getenv(name);
const char* name = variable_names[variable];
const char* value = std::getenv(name);
if (!value) {
return nullopt;
}
return StringView{value};
}

void set(Variable variable, StringView value) {
const char* name = variable_names[variable];
#ifdef _WIN32
_putenv_s(name, value.data());
#else
setenv(name, value.data(), /*overwrite=*/0);
#endif
}

std::string to_json() {
auto result = nlohmann::json::object({});

for (const char *name : variable_names) {
if (const char *value = std::getenv(name)) {
for (const char* name : variable_names) {
if (const char* value = std::getenv(name)) {
result[name] = value;
}
}
Expand Down
11 changes: 7 additions & 4 deletions src/datadog/telemetry/telemetry.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -53,10 +53,13 @@ void init(FinalizedConfiguration configuration,
std::shared_ptr<tracing::HTTPClient> client,
std::shared_ptr<tracing::EventScheduler> event_scheduler,
tracing::HTTPClient::URL agent_url, tracing::Clock clock) {
instance(Ctor_param{configuration,
tracing::TracerSignature(tracing::RuntimeID::generate(),
tracing::get_process_name(), ""),
logger, client, event_scheduler, agent_url, clock});
auto runtime_id = tracing::RuntimeID::generate();
auto root_session_id = runtime_id.string();
instance(Ctor_param{
configuration,
tracing::TracerSignature(runtime_id, std::move(root_session_id),
tracing::get_process_name(), ""),
logger, client, event_scheduler, agent_url, clock});
}

void init(FinalizedConfiguration configuration,
Expand Down
15 changes: 12 additions & 3 deletions src/datadog/telemetry/telemetry_impl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -302,13 +302,18 @@ void Telemetry::app_started() {
auto payload = app_started_payload();

auto on_headers = [payload_size = payload.size(),
debug_enabled = config_.debug](DictWriter& headers) {
debug_enabled = config_.debug,
&sig = tracer_signature_](DictWriter& headers) {
headers.set("Content-Type", "application/json");
headers.set("Content-Length", std::to_string(payload_size));
headers.set("DD-Telemetry-API-Version", "v2");
headers.set("DD-Client-Library-Language", "cpp");
headers.set("DD-Client-Library-Version", tracer_version);
headers.set("DD-Telemetry-Request-Type", "app-started");
headers.set("DD-Session-ID", sig.runtime_id.string());
if (sig.root_session_id != sig.runtime_id.string()) {
headers.set("DD-Root-Session-ID", sig.root_session_id);
}
if (debug_enabled) {
headers.set("DD-Telemetry-Debug-Enabled", "true");
}
Expand Down Expand Up @@ -356,14 +361,18 @@ void Telemetry::app_closing() {

void Telemetry::send_payload(StringView request_type, std::string payload) {
auto set_telemetry_headers = [request_type, payload_size = payload.size(),
debug_enabled =
config_.debug](DictWriter& headers) {
debug_enabled = config_.debug,
&sig = tracer_signature_](DictWriter& headers) {
headers.set("Content-Type", "application/json");
headers.set("Content-Length", std::to_string(payload_size));
headers.set("DD-Telemetry-API-Version", "v2");
headers.set("DD-Client-Library-Language", "cpp");
headers.set("DD-Client-Library-Version", tracer_version);
headers.set("DD-Telemetry-Request-Type", request_type);
headers.set("DD-Session-ID", sig.runtime_id.string());
if (sig.root_session_id != sig.runtime_id.string()) {
headers.set("DD-Root-Session-ID", sig.root_session_id);
}
if (debug_enabled) {
headers.set("DD-Telemetry-Debug-Enabled", "true");
}
Expand Down
8 changes: 6 additions & 2 deletions src/datadog/tracer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -48,8 +48,9 @@ Tracer::Tracer(const FinalizedTracerConfig& config,
: logger_(config.logger),
runtime_id_(config.runtime_id ? *config.runtime_id
: RuntimeID::generate()),
signature_{runtime_id_, config.defaults.service,
config.defaults.environment},
signature_{runtime_id_,
config.root_session_id.value_or(runtime_id_.string()),
config.defaults.service, config.defaults.environment},
config_manager_(std::make_shared<ConfigManager>(config)),
collector_(/* see constructor body */),
span_sampler_(
Expand All @@ -64,6 +65,9 @@ Tracer::Tracer(const FinalizedTracerConfig& config,
baggage_extraction_enabled_(false),
tracing_enabled_(config.tracing_enabled),
resource_renaming_mode_(config.resource_renaming_mode) {
environment::set(environment::_DD_ROOT_CPP_SESSION_ID,
signature_.root_session_id);

telemetry::init(config.telemetry, signature_, logger_, config.http_client,
config.event_scheduler, config.agent_url);
if (config.report_hostname) {
Expand Down
4 changes: 4 additions & 0 deletions src/datadog/tracer_config.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -407,6 +407,10 @@ Expected<FinalizedTracerConfig> finalize_config(const TracerConfig &user_config,
final_config.runtime_id = user_config.runtime_id;
}

if (auto val = lookup(environment::_DD_ROOT_CPP_SESSION_ID)) {
final_config.root_session_id = std::string{*val};
}

final_config.process_tags = user_config.process_tags;

auto agent_finalized =
Expand Down
7 changes: 7 additions & 0 deletions supported-configurations.json
Original file line number Diff line number Diff line change
Expand Up @@ -279,6 +279,13 @@
"implementation": "A",
"type": "STRING"
}
],
"_DD_ROOT_CPP_SESSION_ID": [
{
"default": null,
"implementation": "A",
"type": "STRING"
}
]
},
"version": "2"
Expand Down
8 changes: 6 additions & 2 deletions test/remote_config/test_remote_config.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -47,8 +47,10 @@ auto logger = std::make_shared<NullLogger>();

REMOTE_CONFIG_TEST("initial state payload") {
// Verify the initial payload structure for a remote configuration instance.
auto runtime_id = RuntimeID::generate();
const TracerSignature tracer_signature{
/* runtime_id = */ RuntimeID::generate(),
/* runtime_id = */ runtime_id,
/* root_session_id = */ runtime_id.string(),
/* service = */ "testsvc",
/* environment = */ "test"};

Expand Down Expand Up @@ -92,8 +94,10 @@ REMOTE_CONFIG_TEST("initial state payload") {
// TODO: test all combination of product and capabilities generation

REMOTE_CONFIG_TEST("response processing") {
auto runtime_id = RuntimeID::generate();
const TracerSignature tracer_signature{
/* runtime_id = */ RuntimeID::generate(),
/* runtime_id = */ runtime_id,
/* root_session_id = */ runtime_id.string(),
/* service = */ "testsvc",
/* environment = */ "test"};

Expand Down
71 changes: 68 additions & 3 deletions test/telemetry/test_telemetry.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -89,8 +89,10 @@ TELEMETRY_IMPLEMENTATION_TEST("Tracer telemetry lifecycle") {
auto client = std::make_shared<MockHTTPClient>();
auto scheduler = std::make_shared<FakeEventScheduler>();

auto runtime_id = RuntimeID::generate();
const TracerSignature tracer_signature{
/* runtime_id = */ RuntimeID::generate(),
/* runtime_id = */ runtime_id,
/* root_session_id = */ runtime_id.string(),
/* service = */ "testsvc",
/* environment = */ "test"};

Expand Down Expand Up @@ -353,6 +355,65 @@ TELEMETRY_IMPLEMENTATION_TEST("Tracer telemetry lifecycle") {
}
}

TELEMETRY_IMPLEMENTATION_TEST("session ID headers") {
auto logger = std::make_shared<MockLogger>();
auto client = std::make_shared<MockHTTPClient>();
auto scheduler = std::make_shared<FakeEventScheduler>();
auto url = HTTPClient::URL::parse("http://localhost:8000");

SECTION("root process: DD-Session-ID present, DD-Root-Session-ID absent") {
auto rid = RuntimeID::generate();
const TracerSignature sig{rid, rid.string(), "testsvc", "test"};

Telemetry telemetry{*finalize_config(), sig, logger, client,
scheduler, *url};

auto it = client->request_headers.items.find("DD-Session-ID");
REQUIRE(it != client->request_headers.items.end());
CHECK(it->second == rid.string());

CHECK(client->request_headers.items.find("DD-Root-Session-ID") ==
client->request_headers.items.end());
}

SECTION("child process: DD-Root-Session-ID present when different") {
auto rid = RuntimeID::generate();
auto root_rid = RuntimeID::generate();
const TracerSignature sig{rid, root_rid.string(), "testsvc", "test"};

Telemetry telemetry{*finalize_config(), sig, logger, client,
scheduler, *url};

auto session_it = client->request_headers.items.find("DD-Session-ID");
REQUIRE(session_it != client->request_headers.items.end());
CHECK(session_it->second == rid.string());

auto root_it = client->request_headers.items.find("DD-Root-Session-ID");
REQUIRE(root_it != client->request_headers.items.end());
CHECK(root_it->second == root_rid.string());
}

SECTION("heartbeat includes session headers") {
auto rid = RuntimeID::generate();
auto root_rid = RuntimeID::generate();
const TracerSignature sig{rid, root_rid.string(), "testsvc", "test"};

Telemetry telemetry{*finalize_config(), sig, logger, client,
scheduler, *url};

client->request_headers.items.clear();
scheduler->trigger_heartbeat();

auto session_it = client->request_headers.items.find("DD-Session-ID");
REQUIRE(session_it != client->request_headers.items.end());
CHECK(session_it->second == rid.string());

auto root_it = client->request_headers.items.find("DD-Root-Session-ID");
REQUIRE(root_it != client->request_headers.items.end());
CHECK(root_it->second == root_rid.string());
}
}

TELEMETRY_IMPLEMENTATION_TEST("Tracer telemetry API") {
const Clock clock = [] {
TimePoint result;
Expand All @@ -364,8 +425,10 @@ TELEMETRY_IMPLEMENTATION_TEST("Tracer telemetry API") {
auto client = std::make_shared<MockHTTPClient>();
auto scheduler = std::make_shared<FakeEventScheduler>();

auto runtime_id = RuntimeID::generate();
const TracerSignature tracer_signature{
/* runtime_id = */ RuntimeID::generate(),
/* runtime_id = */ runtime_id,
/* root_session_id = */ runtime_id.string(),
/* service = */ "testsvc",
/* environment = */ "test"};

Expand Down Expand Up @@ -869,8 +932,10 @@ TELEMETRY_IMPLEMENTATION_TEST("Tracer telemetry configuration") {
auto client = std::make_shared<MockHTTPClient>();
auto scheduler = std::make_shared<FakeEventScheduler>();

auto runtime_id = RuntimeID::generate();
const TracerSignature tracer_signature{
/* runtime_id = */ RuntimeID::generate(),
/* runtime_id = */ runtime_id,
/* root_session_id = */ runtime_id.string(),
/* service = */ "testsvc",
/* environment = */ "test"};

Expand Down
3 changes: 2 additions & 1 deletion test/test_datadog_agent.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -199,7 +199,8 @@ DATADOG_AGENT_TEST("Remote Configuration") {
auto finalized = finalize_config(config);
REQUIRE(finalized);

const TracerSignature signature(RuntimeID::generate(), "testsvc", "test");
auto rid = RuntimeID::generate();
const TracerSignature signature(rid, rid.string(), "testsvc", "test");

// TODO: set telemetry mock
auto config_manager = std::make_shared<ConfigManager>(*finalized);
Expand Down
9 changes: 7 additions & 2 deletions tools/config-inversion/main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -11,12 +11,17 @@ namespace fs = std::filesystem;
namespace env = datadog::tracing::environment;

template <typename T>
std::string to_string_any(const T& value) {
nlohmann::json to_json_default(const T& value) {
std::ostringstream oss;
oss << value;
return oss.str();
}

template <>
nlohmann::json to_json_default(std::nullptr_t const&) {
return nullptr;
}

nlohmann::json build_configuration() {
nlohmann::json j;
j["version"] = "2";
Expand All @@ -31,7 +36,7 @@ nlohmann::json build_configuration() {
#define X(NAME, TYPE, DEFAULT_VALUE) \
do { \
auto obj = nlohmann::json::object(); \
obj["default"] = to_string_any(DEFAULT_VALUE); \
obj["default"] = to_json_default(DEFAULT_VALUE); \
obj["implementation"] = "A"; \
obj["type"] = QUOTED(TYPE); \
supported_configurations[QUOTED(NAME)] = nlohmann::json::array({obj}); \
Expand Down
Loading