Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
52 commits
Select commit Hold shift + click to select a range
e40dd5d
Implement stable session identifier headers for telemetry
khanayan123 Mar 25, 2026
3fab2f5
Resync supported-configurations.json via config-inversion tool
khanayan123 Mar 25, 2026
d805351
Fix config-inversion: output null default for _DD_ROOT_CPP_SESSION_ID
khanayan123 Mar 25, 2026
1f8a254
Match registry: _DD_ROOT_CPP_SESSION_ID implementation B, default ""
khanayan123 Mar 25, 2026
468dd13
Revert to implementation A with null default for _DD_ROOT_CPP_SESSION_ID
khanayan123 Mar 25, 2026
f124ca3
Merge branch 'main' into ayan.khan/stable-session-id-headers
khanayan123 Mar 26, 2026
c106643
Update include/datadog/environment.h
khanayan123 Mar 31, 2026
25fb7ea
fix: address PR review — use empty string default, move env lookup
khanayan123 Mar 31, 2026
d323be4
fix: revert config-inversion changes, no longer needed with "" default
khanayan123 Mar 31, 2026
3d9252b
fix: revert unrelated formatting, deduplicate session headers, fix Wi…
khanayan123 Mar 31, 2026
b3c0f73
Update test/telemetry/test_telemetry.cpp
khanayan123 Mar 31, 2026
b673a7e
fix: include request_headers in MockHTTPClient::clear()
khanayan123 Mar 31, 2026
0b7fd49
fix: address review feedback from xlamorlette
khanayan123 Apr 2, 2026
64223a6
fix: move config registry link to top of file, remove inline link
khanayan123 Apr 2, 2026
2453d16
chore: remove inline comment on _DD_ROOT_CPP_SESSION_ID
khanayan123 Apr 2, 2026
d3c3b39
refactor: simplify telemetry init() using 3-param TracerSignature con…
khanayan123 Apr 2, 2026
a571544
Merge remote-tracking branch 'origin/main' into ayan.khan/stable-sess…
khanayan123 Apr 2, 2026
d47c926
style: fix clang-format violation in telemetry.cpp
khanayan123 Apr 2, 2026
b8a48ff
fix: address review feedback from xlamorlette
khanayan123 Apr 2, 2026
ccd3fa2
refactor: replace env var with singleton for root session ID
khanayan123 Apr 3, 2026
2dfc89d
fix: store session_id in local var, simplify app_started() via send_p…
khanayan123 Apr 3, 2026
cff8726
refactor: move root session ID singleton to internal header
khanayan123 Apr 3, 2026
c7a17b2
fix: add root_session_id.h to Bazel BUILD file
khanayan123 Apr 3, 2026
ed1638c
refactor: move root session ID singleton internals to .cpp file
khanayan123 Apr 3, 2026
78b1a06
chore: revert formatting-only changes in environment.cpp
khanayan123 Apr 6, 2026
315f841
chore: revert formatting-only changes in tracer_config.cpp
khanayan123 Apr 6, 2026
212fc67
chore: revert unnecessary changes in test_remote_config.cpp
khanayan123 Apr 6, 2026
b22ce9f
Revert "chore: revert unnecessary changes in test_remote_config.cpp"
khanayan123 Apr 6, 2026
bf96df0
chore: revert test_remote_config.cpp to main (no functional changes n…
khanayan123 Apr 6, 2026
b518f63
chore: restore original TracerSignature style in test_telemetry.cpp
khanayan123 Apr 6, 2026
165fadb
chore: revert unnecessary changes in test_datadog_agent.cpp
khanayan123 Apr 6, 2026
7133928
Merge branch 'main' into ayan.khan/stable-session-id-headers
khanayan123 Apr 6, 2026
e347fa6
fix: replace std::call_once with mutex+bool guard in root session sin…
khanayan123 Apr 6, 2026
881addf
simplify: use static local singleton pattern for root session ID
khanayan123 Apr 6, 2026
2ecd828
fix: add mutex to root session ID singleton for thread safety
khanayan123 Apr 6, 2026
a8ed9b8
fix: use std::atomic for root session ID singleton guard
khanayan123 Apr 6, 2026
675ee33
revert: restore original app_started() implementation
khanayan123 Apr 6, 2026
69eb64c
refactor: simplify root session ID to single get_or_init() singleton
khanayan123 Apr 7, 2026
58602f5
test: add root_session_id singleton test
khanayan123 Apr 7, 2026
d37f5f4
refactor: move get_or_init into TracerSignature 3-param constructor
khanayan123 Apr 7, 2026
94b81ca
fix: fix fork test to compare against actual singleton value
khanayan123 Apr 7, 2026
6664e2a
fix: acquire root_session_id from singleton in session header tests
khanayan123 Apr 7, 2026
b7ade60
revert: move get_or_init back to tracer.cpp, remove tracer_signature.cpp
khanayan123 Apr 7, 2026
20467e1
style: fix clang-format violations in test files
khanayan123 Apr 7, 2026
d808a0a
chore: remove test_root_session_id.cpp
khanayan123 Apr 7, 2026
dd3b4f8
chore: remove unnecessary comments
khanayan123 Apr 7, 2026
f6f2ffb
fix: address review feedback
khanayan123 Apr 7, 2026
1abb0ce
fix: remove extra spaces in test Telemetry init alignment
khanayan123 Apr 7, 2026
96a40da
style: restore clang-format alignment in test_telemetry.cpp
khanayan123 Apr 7, 2026
2ba87be
style: rename sig to tracer_signature to fix clang-format alignment
khanayan123 Apr 7, 2026
8b810cc
feat: re-add root_session_id to TracerConfig for integration seeding
khanayan123 Apr 8, 2026
e61e812
fix: match field order in TracerConfig/FinalizedTracerConfig, remove …
khanayan123 Apr 10, 2026
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 BUILD.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ cc_library(
"src/datadog/propagation_style.cpp",
"src/datadog/random.cpp",
"src/datadog/random.h",
"src/datadog/root_session_id.h",
"src/datadog/rate.cpp",
"src/datadog/remote_config/product.cpp",
"src/datadog/remote_config/remote_config.cpp",
Expand Down
2 changes: 2 additions & 0 deletions include/datadog/environment.h
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@ namespace environment {

// Central registry for supported environment variables.
// All configurations must be registered here.
// See also:
// https://feature-parity.us1.prod.dog/configurations?viewType=configurations
//
// This registry is the single source of truth for:
// - env variable name allowlist (`include/datadog/environment.h`)
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 @@ -189,6 +189,11 @@ struct TracerConfig {
// This option is ignored if `resource_renaming_enabled` is not `true`.
Optional<bool> resource_renaming_always_simplified_endpoint;

// Root session ID for stable telemetry correlation across forked workers.
// Integrations (nginx, httpd, kong) should set this in the master process
// before workers fork so all Tracers share the same root.
Optional<std::string> root_session_id;
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: we should have the same order for the fields in TracerConfig and FinalizedTracerConfig.


/// A mapping of process-specific tags used to uniquely identify processes.
///
/// The `process_tags` map allows associating arbitrary string-based keys and
Expand Down Expand Up @@ -225,6 +230,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
18 changes: 14 additions & 4 deletions include/datadog/tracer_signature.h
Original file line number Diff line number Diff line change
Expand Up @@ -33,17 +33,27 @@ 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)
: runtime_id(id),
default_service(std::move(service)),
default_environment(std::move(environment)),

TracerSignature(RuntimeID runtime_id, std::string default_service,
Comment thread
xlamorlette-datadog marked this conversation as resolved.
std::string default_environment)
: TracerSignature(runtime_id, runtime_id.string(),
std::move(default_service),
std::move(default_environment)) {}

TracerSignature(RuntimeID runtime_id, std::string root_session_id,
std::string default_service, std::string default_environment)
: runtime_id(runtime_id),
root_session_id(std::move(root_session_id)),
default_service(std::move(default_service)),
default_environment(std::move(default_environment)),
library_version(tracer_version),
library_language("cpp"),
library_language_version(DD_TRACE_STRINGIFY(__cplusplus), 6) {}
Expand Down
12 changes: 12 additions & 0 deletions src/datadog/root_session_id.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
#pragma once

#include <string>

namespace datadog::tracing::root_session_id {

inline const std::string& get_or_init(const std::string& runtime_id) {
static const std::string id = runtime_id;
return id;
}

} // namespace datadog::tracing::root_session_id
19 changes: 16 additions & 3 deletions src/datadog/telemetry/telemetry_impl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,15 @@ HTTPClient::URL make_telemetry_endpoint(HTTPClient::URL url) {
return url;
}

void set_session_headers(DictWriter& headers,
const tracing::TracerSignature& signature) {
const auto& session_id = signature.runtime_id.string();
headers.set("DD-Session-ID", session_id);
if (signature.root_session_id != session_id) {
headers.set("DD-Root-Session-ID", signature.root_session_id);
}
}

void cancel_tasks(std::vector<tracing::EventScheduler::Cancel>& tasks) {
for (auto& cancel_task : tasks) {
cancel_task();
Expand Down Expand Up @@ -309,13 +318,15 @@ 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,
&signature = 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");
set_session_headers(headers, signature);
if (debug_enabled) {
headers.set("DD-Telemetry-Debug-Enabled", "true");
}
Expand Down Expand Up @@ -363,14 +374,16 @@ 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,
&signature =
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);
set_session_headers(headers, signature);
if (debug_enabled) {
headers.set("DD-Telemetry-Debug-Enabled", "true");
}
Expand Down
7 changes: 5 additions & 2 deletions src/datadog/tracer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
#include "msgpack.h"
#include "platform_util.h"
#include "random.h"
#include "root_session_id.h"
#include "span_data.h"
#include "span_sampler.h"
#include "tags.h"
Expand All @@ -48,8 +49,10 @@ 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_,
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: I agree one should generalize the usage of braces initialization calls, but here I think we should favor consistency with existing code

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

up

root_session_id::get_or_init(
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 Down
6 changes: 2 additions & 4 deletions src/datadog/tracer_config.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -403,10 +403,8 @@ Expected<FinalizedTracerConfig> finalize_config(const TracerConfig &user_config,
final_config.injection_styles.erase(it);
}

if (user_config.runtime_id) {
final_config.runtime_id = user_config.runtime_id;
}

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;

auto agent_finalized =
Expand Down
5 changes: 4 additions & 1 deletion test/mocks/http_clients.h
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,10 @@ struct MockHTTPClient : public HTTPClient {
ErrorHandler on_error_;
std::string request_body;

void clear() { request_body = ""; }
void clear() {
request_body = "";
request_headers.items.clear();
}

Expected<void> post(
const URL&, HeadersSetter set_headers, std::string body,
Expand Down
61 changes: 61 additions & 0 deletions test/telemetry/test_telemetry.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -366,6 +366,67 @@ 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");
Comment thread
zacharycmontoya marked this conversation as resolved.

SECTION("root process: DD-Session-ID present, DD-Root-Session-ID absent") {
auto session_rid = RuntimeID::generate();
const TracerSignature tracer_signature(session_rid, "testsvc", "test");

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

auto it = client->request_headers.items.find("DD-Session-ID");
REQUIRE(it != client->request_headers.items.end());
CHECK(it->second == session_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 session_rid = RuntimeID::generate();
auto root_rid = RuntimeID::generate();
const TracerSignature tracer_signature(session_rid, root_rid.string(),
"testsvc", "test");

Telemetry telemetry{
*finalize_config(), tracer_signature, 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 == session_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") {
Comment thread
xlamorlette-datadog marked this conversation as resolved.
auto session_rid = RuntimeID::generate();
auto root_rid = RuntimeID::generate();
const TracerSignature tracer_signature(session_rid, root_rid.string(),
"testsvc", "test");

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

client->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 == session_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 Down
Loading