Skip to content
Merged
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
dc79c61
feat: add DD-Session-ID and DD-Root-Session-ID telemetry headers
khanayan123 Mar 25, 2026
d47e797
fix: sort using directives alphabetically (SA1210)
khanayan123 Mar 25, 2026
2804636
fix: correct using directive order (SA1210) - TestHelpers before Util
khanayan123 Mar 25, 2026
4351aa0
Merge branch 'master' into ayan.khan/stable-session-id-headers
khanayan123 Mar 26, 2026
642b76b
Initialize root session ID at tracer startup
khanayan123 Mar 26, 2026
d7ec963
Address PR review comments
khanayan123 Mar 27, 2026
6385c47
Optimize TelemetryHttpHeaderNames per review feedback
khanayan123 Mar 30, 2026
3406b39
Add test for inherited root session ID
khanayan123 Mar 31, 2026
9eff4f3
Merge branch 'master' into ayan.khan/stable-session-id-headers
khanayan123 Mar 31, 2026
8f98088
Update tracer/src/Datadog.Trace/Telemetry/TelemetryHttpHeaderNames.cs
khanayan123 Mar 31, 2026
7782691
Update tracer/src/Datadog.Trace/Util/RuntimeId.cs
khanayan123 Mar 31, 2026
38678c1
Update tracer/test/Datadog.Trace.Tests/Util/RuntimeIdTests.cs
khanayan123 Mar 31, 2026
56e6aea
Revert stray blank line in TracerManager.cs
khanayan123 Mar 31, 2026
98cca06
Use EnvironmentConfigurationSource to read root session ID
khanayan123 Mar 31, 2026
604f9ba
fix: use TestingAndPrivateOnly attribute instead of InternalForTesting
khanayan123 Mar 31, 2026
64d70b5
Merge branch 'master' into ayan.khan/stable-session-id-headers
khanayan123 Mar 31, 2026
4f3274a
fix: add missing using for TestingAndPrivateOnlyAttribute
khanayan123 Mar 31, 2026
fbb50f6
fix: add missing using for EnvironmentRestorerAttribute
khanayan123 Mar 31, 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
4 changes: 4 additions & 0 deletions tracer/src/Datadog.Trace/ClrProfiler/Instrumentation.cs
Original file line number Diff line number Diff line change
Expand Up @@ -307,6 +307,10 @@ internal static void InitializeNoNativeParts(ref RefStopwatch sw)
}
#endif

// Eagerly initialize the root session ID so child processes
// inherit it even if spawned before the first telemetry flush.
_ = RuntimeId.GetRootSessionId();

try
{
// ensure global instance is created if it's not already
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1580,6 +1580,15 @@ supportedConfigurations:
documentation: |-
Configuration key for whether telemetry metrics should be sent.
<see cref="Datadog.Trace.Telemetry.TelemetrySettings.MetricsEnabled"/>
_DD_ROOT_DOTNET_SESSION_ID:
Copy link
Copy Markdown
Member

@lucaspimentel lucaspimentel Mar 26, 2026

Choose a reason for hiding this comment

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

Why the leading underscore? _DD... instead of DD..? Is that something we're doing now for internal env vars? The RFC says DD_ROOT_<LIB>_SESSION_ID.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Agreed, this seems non standard

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Prefixing environment variables with _DD is convention we use in dd-trace-py to mark a configuration as internal/private. All implementations currently follow this spec, the implementation plan was out of date but I just pushed a fix.

Ideally we'd use _DD to be consistent across SDKs but this isn't a big deal for us. We would like to do what's best for the .NET library and ideally this PR would introduce minimal changes to library internals. From a backend perspective as long as the expected headers sent by the telemetry client we should be good.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

If that's the standard and it follows the spec and the other implementations, then yeah, go for it. I only asked because I had not seen it before and I didn't see in the RFC. Thanks!

(I wonder though it we have places where we look for DD_* env vars that would need to be updated. Probably not production code, but maybe tests or scripts that list all DD env vars for troubleshooting.)

Copy link
Copy Markdown
Contributor Author

@khanayan123 khanayan123 Mar 27, 2026

Choose a reason for hiding this comment

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

The _DD prefix was established by the Python implementation (dd-trace-py#16839).

The implementation proposal/parent RFC only mandates the HTTP header names (DD-Session-ID, DD-Root-Session-ID) the env var naming (DD_ROOT_LIB_SESSION_ID) is mentioned only in the "Process Spawning: Fork & Exec Support" appendix as an implementation detail.

The underscore prefix signals this is internal: we don't want to expose it as a public API, and keeping it private gives us flexibility to replace the propagation mechanism in a minor version without a breaking change.

That said, this is purely an internal convention if the DD_ prefix is preferred for the DOTNET implementation, I can update it. It's not customer-facing either way.

Copy link
Copy Markdown
Contributor Author

@khanayan123 khanayan123 Mar 31, 2026

Choose a reason for hiding this comment

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

As per discussion on slack and after updating the implementation proposal we will keep _DD_ROOT_LIB_SESSION_ID as the naming convention across SDKs for this env var

- implementation: A
type: string
default: null
product: Telemetry
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

I think this needs to be marked as a "platform key" so that it can be read directly from the environment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Unfortunately, anything starting with _DD_ has to be controlled by ConfigRegistry, hence the constraint in the analyzer

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Any resolution on this @anna-git @andrewlock? Since this is purely an internal implementation detail (the RFC only mandates the HTTP header names, not the env var), I'm happy to rename it to whatever works best with DOTNET standards whether that's dropping the _DD prefix to allow it to be a platform key, or keeping the current approach

Copy link
Copy Markdown
Contributor Author

@khanayan123 khanayan123 Mar 30, 2026

Choose a reason for hiding this comment

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

Resolved per discussion with @anna-git , this should stay as a config key in ConfigRegistry (not a platform key). Platform keys are for non-Datadog config keys. Since this is ours (_DD_ prefix), ConfigRegistry is the right place, which is how it's currently implemented.

const_name: RootSessionId
documentation: |-
Internal env var for propagating the root session ID to child processes.
Set automatically by the tracer at init time; not user-configurable.
DD_TEST_MANAGEMENT_ATTEMPT_TO_FIX_RETRIES:
- implementation: A
type: int
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,12 @@ internal static partial class ConfigurationKeys
{
internal static class Telemetry
{
/// <summary>
/// Internal env var for propagating the root session ID to child processes.
/// Set automatically by the tracer at init time; not user-configurable.
/// </summary>
public const string RootSessionId = "_DD_ROOT_DOTNET_SESSION_ID";

/// <summary>
/// SSI variable that provides a unique identifier for the instrumentation installation.
/// Used for tracking and correlation purposes in telemetry.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,12 @@ internal static partial class ConfigurationKeys
{
internal static class Telemetry
{
/// <summary>
/// Internal env var for propagating the root session ID to child processes.
/// Set automatically by the tracer at init time; not user-configurable.
/// </summary>
public const string RootSessionId = "_DD_ROOT_DOTNET_SESSION_ID";

/// <summary>
/// SSI variable that provides a unique identifier for the instrumentation installation.
/// Used for tracking and correlation purposes in telemetry.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,12 @@ internal static partial class ConfigurationKeys
{
internal static class Telemetry
{
/// <summary>
/// Internal env var for propagating the root session ID to child processes.
/// Set automatically by the tracer at init time; not user-configurable.
/// </summary>
public const string RootSessionId = "_DD_ROOT_DOTNET_SESSION_ID";

/// <summary>
/// SSI variable that provides a unique identifier for the instrumentation installation.
/// Used for tracking and correlation purposes in telemetry.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,12 @@ internal static partial class ConfigurationKeys
{
internal static class Telemetry
{
/// <summary>
/// Internal env var for propagating the root session ID to child processes.
/// Set automatically by the tracer at init time; not user-configurable.
/// </summary>
public const string RootSessionId = "_DD_ROOT_DOTNET_SESSION_ID";

/// <summary>
/// SSI variable that provides a unique identifier for the instrumentation installation.
/// Used for tracking and correlation purposes in telemetry.
Expand Down
3 changes: 3 additions & 0 deletions tracer/src/Datadog.Trace/Telemetry/TelemetryConstants.cs
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,9 @@ internal static class TelemetryConstants
public const string ClientLibraryLanguageHeader = "DD-Client-Library-Language";
public const string ClientLibraryVersionHeader = "DD-Client-Library-Version";

public const string SessionIdHeader = "DD-Session-ID";
public const string RootSessionIdHeader = "DD-Root-Session-ID";

public const string CloudProviderHeader = "DD-Cloud-Provider";
public const string CloudResourceTypeHeader = "DD-Cloud-Resource-Type";
public const string CloudResourceIdentifierHeader = "DD-Cloud-Resource-Identifier";
Expand Down
83 changes: 65 additions & 18 deletions tracer/src/Datadog.Trace/Telemetry/TelemetryHttpHeaderNames.cs
Original file line number Diff line number Diff line change
@@ -1,56 +1,103 @@
// <copyright file="TelemetryHttpHeaderNames.cs" company="Datadog">
// <copyright file="TelemetryHttpHeaderNames.cs" company="Datadog">
// Unless explicitly stated otherwise all files in this repository are licensed under the Apache 2 License.
// This product includes software developed at Datadog (https://www.datadoghq.com/). Copyright 2017 Datadog, Inc.
// </copyright>

using System;
using System.Collections.Generic;
using System.Threading;
using Datadog.Trace.HttpOverStreams;
using Datadog.Trace.Util;

namespace Datadog.Trace.Telemetry
{
internal static class TelemetryHttpHeaderNames
{
private static string _httpSerializedDefaultAgentHeaders;

/// <summary>
/// Returns <see cref="GetDefaultAgentHeaders"/>, in the format <c>Key: Value\r\n</c>. For use in HTTP headers
/// Gets the default agent headers in the format <c>Key: Value\r\n</c>. For use in HTTP headers.
/// Lazily initialized because session headers depend on runtime values.
/// </summary>
internal const string HttpSerializedDefaultAgentHeaders =
$"{TelemetryConstants.ClientLibraryLanguageHeader}: {TracerConstants.Language}" + DatadogHttpValues.CrLf +
$"{TelemetryConstants.ClientLibraryVersionHeader}: {TracerConstants.AssemblyVersion}" + DatadogHttpValues.CrLf +
$"{HttpHeaderNames.TracingEnabled}: false" + DatadogHttpValues.CrLf;
internal static string HttpSerializedDefaultAgentHeaders =>
LazyInitializer.EnsureInitialized(ref _httpSerializedDefaultAgentHeaders, BuildSerializedAgentHeaders);

/// <summary>
/// Gets the default constant headers that should be added to any request to the agent
/// </summary>
internal static KeyValuePair<string, string>[] GetDefaultAgentHeaders()
=>
[
new(TelemetryConstants.ClientLibraryLanguageHeader, TracerConstants.Language),
new(TelemetryConstants.ClientLibraryVersionHeader, TracerConstants.AssemblyVersion),
new(HttpHeaderNames.TracingEnabled, "false") // don't add automatic instrumentation to requests directed to the agent
];
{
var sessionId = RuntimeId.Get();
var rootSessionId = RuntimeId.GetRootSessionId();
var includeRoot = rootSessionId != sessionId;
var headerCount = includeRoot ? 5 : 4;

var headers = new KeyValuePair<string, string>[headerCount];
headers[0] = new(TelemetryConstants.ClientLibraryLanguageHeader, TracerConstants.Language);
headers[1] = new(TelemetryConstants.ClientLibraryVersionHeader, TracerConstants.AssemblyVersion);
headers[2] = new(HttpHeaderNames.TracingEnabled, "false"); // don't add automatic instrumentation to requests directed to the agent
headers[3] = new(TelemetryConstants.SessionIdHeader, sessionId);

if (includeRoot)
{
headers[4] = new(TelemetryConstants.RootSessionIdHeader, rootSessionId);
}

return headers;
}

/// <summary>
/// Gets the default constant headers that should be added to any request to the direct telemetry intake
/// </summary>
internal static KeyValuePair<string, string>[] GetDefaultIntakeHeaders(TelemetrySettings.AgentlessSettings settings)
{
var headerCount = settings.Cloud is null ? 4 : 7;
var sessionId = RuntimeId.Get();
var rootSessionId = RuntimeId.GetRootSessionId();
var includeRoot = rootSessionId != sessionId;
var baseCount = settings.Cloud is null ? 5 : 8;
var headerCount = includeRoot ? baseCount + 1 : baseCount;

var headers = new KeyValuePair<string, string>[headerCount];

headers[0] = new(TelemetryConstants.ClientLibraryLanguageHeader, TracerConstants.Language);
headers[1] = new(TelemetryConstants.ClientLibraryVersionHeader, TracerConstants.AssemblyVersion);
headers[2] = new(HttpHeaderNames.TracingEnabled, "false"); // don't add automatic instrumentation to requests directed to the agent
headers[2] = new(HttpHeaderNames.TracingEnabled, "false");
headers[3] = new(TelemetryConstants.ApiKeyHeader, settings.ApiKey);
headers[4] = new(TelemetryConstants.SessionIdHeader, sessionId);

var index = 5;
if (settings.Cloud is { } cloud)
{
headers[4] = new(TelemetryConstants.CloudProviderHeader, cloud.Provider);
headers[5] = new(TelemetryConstants.CloudResourceTypeHeader, cloud.ResourceType);
headers[6] = new(TelemetryConstants.CloudResourceIdentifierHeader, cloud.ResourceIdentifier);
headers[index++] = new(TelemetryConstants.CloudProviderHeader, cloud.Provider);
headers[index++] = new(TelemetryConstants.CloudResourceTypeHeader, cloud.ResourceType);
headers[index++] = new(TelemetryConstants.CloudResourceIdentifierHeader, cloud.ResourceIdentifier);
}

if (includeRoot)
{
headers[index] = new(TelemetryConstants.RootSessionIdHeader, rootSessionId);
}

return headers;
}

private static string BuildSerializedAgentHeaders()
{
var sessionId = RuntimeId.Get();
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

There's another call to RuntimeId.Get() two lines above that we may want to consolidate.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Done

var rootSessionId = RuntimeId.GetRootSessionId();

if (rootSessionId != sessionId)
{
return $"{TelemetryConstants.ClientLibraryLanguageHeader}: {TracerConstants.Language}" + DatadogHttpValues.CrLf +
$"{TelemetryConstants.ClientLibraryVersionHeader}: {TracerConstants.AssemblyVersion}" + DatadogHttpValues.CrLf +
$"{HttpHeaderNames.TracingEnabled}: false" + DatadogHttpValues.CrLf +
$"{TelemetryConstants.SessionIdHeader}: {sessionId}" + DatadogHttpValues.CrLf +
$"{TelemetryConstants.RootSessionIdHeader}: {rootSessionId}" + DatadogHttpValues.CrLf;
}

return $"{TelemetryConstants.ClientLibraryLanguageHeader}: {TracerConstants.Language}" + DatadogHttpValues.CrLf +
$"{TelemetryConstants.ClientLibraryVersionHeader}: {TracerConstants.AssemblyVersion}" + DatadogHttpValues.CrLf +
$"{HttpHeaderNames.TracingEnabled}: false" + DatadogHttpValues.CrLf +
$"{TelemetryConstants.SessionIdHeader}: {sessionId}" + DatadogHttpValues.CrLf;
}
}
}
25 changes: 25 additions & 0 deletions tracer/src/Datadog.Trace/Util/RuntimeId.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,17 +5,27 @@

using System;
using System.Threading;
using Datadog.Trace.Configuration;
using Datadog.Trace.Configuration.Telemetry;
using Datadog.Trace.Logging;
using Datadog.Trace.SourceGenerators;
using Datadog.Trace.Telemetry;

namespace Datadog.Trace.Util
{
internal static class RuntimeId
{
private static readonly IDatadogLogger Log = DatadogLogging.GetLoggerFor(typeof(RuntimeId));
private static string _runtimeId;
private static string _rootSessionId;

public static string Get() => LazyInitializer.EnsureInitialized(ref _runtimeId, () => GetImpl());

public static string GetRootSessionId() => LazyInitializer.EnsureInitialized(ref _rootSessionId, () => GetRootSessionIdImpl());

[TestingAndPrivateOnly]
internal static void ResetForTests() => _rootSessionId = null;

private static string GetImpl()
{
if (NativeLoader.TryGetRuntimeIdFromNative(out var runtimeId))
Expand All @@ -29,5 +39,20 @@ private static string GetImpl()

return guid;
}

private static string GetRootSessionIdImpl()
{
var config = new ConfigurationBuilder(new EnvironmentConfigurationSource(), TelemetryFactory.Config);
var inherited = config.WithKeys(ConfigurationKeys.Telemetry.RootSessionId).AsString();
if (!string.IsNullOrEmpty(inherited))
{
Log.Debug("Inherited root session ID from parent: {RootSessionId}", inherited);
return inherited;
}

var rootId = Get();
EnvironmentHelpers.SetEnvironmentVariable(ConfigurationKeys.Telemetry.RootSessionId, rootId);
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

For clarity, this simply won't work in many cases, such that this whole approach is potentially flawed...

  • On Unix, environment changes are not seen by native processes. There's no way to remediate this.
  • Users may well call Process.Start() with a custom env var block, in which case this won't be inherited.
  • If UseShellExecute=true is set when starting a new process, this won't work.

The "good" news is that starting new dotnet processes is not a standard pattern in general, so hopefully it just doesn't really matter...

Copy link
Copy Markdown
Contributor

@anna-git anna-git Mar 27, 2026

Choose a reason for hiding this comment

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

as we already instrument ProcessStart , maybe the RootSessionId could be passed down via the instrumentation setting some additional args or something 🤔

Copy link
Copy Markdown
Member

@lucaspimentel lucaspimentel Mar 27, 2026

Choose a reason for hiding this comment

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

starting new dotnet processes is not a standard pattern

Yet we have a whole instrumentation for it. 😉

tracer/src/Datadog.Trace/ClrProfiler/AutoInstrumentation/Process

(edit: I had not refreshed to see Anna's comment before I posted mine 😅 )

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

@lucaspimentel @anna-git @andrewlock Good point about the Process instrumentation.

Would you prefer we inject _DD_ROOT_DOTNET_SESSION_ID directly into the child process's ProcessStartInfo.Environment via the existing ClrProfiler/AutoInstrumentation/Process hooks, rather than relying on Environment.SetEnvironmentVariable()? And would that address the Unix limitations Andrew mentioned and handle cases where the user provides a custom env block or sets UseShellExecute=true?

Copy link
Copy Markdown
Contributor Author

@khanayan123 khanayan123 Mar 27, 2026

Choose a reason for hiding this comment

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

I think since dotnet already instruments process start it might make sense to utilize that instead of env vars, we implemented it in a similar way for Node

Copy link
Copy Markdown
Contributor Author

@khanayan123 khanayan123 Mar 30, 2026

Choose a reason for hiding this comment

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

Resolved per @andrewlock's recommendation, we'll keep SetEnvironmentVariable() and accept it won't give 100% coverage. Process instrumentation is too risky for this. Spawning child .NET processes is rare enough that this is acceptable.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

as we already instrument ProcessStart , maybe the RootSessionId could be passed down via the instrumentation setting some additional args or something 🤔

FWIW, I consider this to be very risky for breaking customers. I just don't see the value here as being worth the trade-off tbh

return rootId;
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
using Datadog.Trace.Telemetry;
using Datadog.Trace.Telemetry.Transports;
using Datadog.Trace.TestHelpers;
using Datadog.Trace.Util;
using FluentAssertions;
using FluentAssertions.Execution;
using Xunit;
Expand Down Expand Up @@ -116,6 +117,12 @@ public async Task SetsRequiredHeaders(bool agentless, bool useCloudAgentless)
{ "DD-Client-Library-Version", TracerConstants.AssemblyVersion },
};

// DD-Session-ID is always present and equals the runtime ID
allExpected.Add(TelemetryConstants.SessionIdHeader, RuntimeId.Get());

// DD-Root-Session-ID is absent when rootSessionId == runtimeId (normal process)
// We can't assert absence in the loop below, so we check it separately after

if (ContainerMetadata.Instance.ContainerId is { } containerId)
{
allExpected.Add(AgentHttpHeaderNames.ContainerId, containerId);
Expand Down Expand Up @@ -149,6 +156,12 @@ public async Task SetsRequiredHeaders(bool agentless, bool useCloudAgentless)
}
}

// DD-Root-Session-ID should be absent in a normal (non-child) process
if (RuntimeId.GetRootSessionId() == RuntimeId.Get())
{
headers.AllKeys.Should().NotContain(TelemetryConstants.RootSessionIdHeader);
}

// should have either content-length or chunked encoding
headers.AllKeys.Should()
.Contain(s => s.Equals("Content-Type", StringComparison.OrdinalIgnoreCase)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@ public class ConfigurationTests
"DD_CIVISIBILITY_CODE_COVERAGE_MODE",
"DD_CIVISIBILITY_AUTO_INSTRUMENTATION_PROVIDER",
// Internal env vars that we only ever read from environment
"_DD_ROOT_DOTNET_SESSION_ID",
"DD_INTERNAL_TRACE_NATIVE_ENGINE_PATH",
"DD_INTERNAL_PROFILING_NATIVE_ENGINE_PATH",
"DD_DOTNET_TRACER_HOME",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,7 @@ public async Task ShouldContainRequiredHeaders(bool debugEnabled, [Combinatorial
{ "DD-Telemetry-API-Version", TelemetryConstants.ApiVersionV2 },
{ "DD-Telemetry-Request-Type", "my-request-type" },
{ "Datadog-Container-ID", "my-container-id" },
{ "Datadog-Entity-ID", "my-entity-id" }
{ "Datadog-Entity-ID", "my-entity-id" },
};
if (debugEnabled)
{
Expand Down
47 changes: 47 additions & 0 deletions tracer/test/Datadog.Trace.Tests/Util/RuntimeIdTests.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
// <copyright file="RuntimeIdTests.cs" company="Datadog">
// Unless explicitly stated otherwise all files in this repository are licensed under the Apache 2 License.
// This product includes software developed at Datadog (https://www.datadoghq.com/). Copyright 2017 Datadog, Inc.
// </copyright>

using System;
using Datadog.Trace.Configuration;
using Datadog.Trace.TestHelpers;
using Datadog.Trace.Util;
using FluentAssertions;
using Xunit;

namespace Datadog.Trace.Tests.Util
{
[EnvironmentRestorer(ConfigurationKeys.Telemetry.RootSessionId)]
public class RuntimeIdTests
{
[Fact]
public void RootSessionId_UsesRuntimeIdWhenNotInherited_AndInheritsWhenSet()
{
try
{
// When no env var is set, root session ID should default to runtime ID
RuntimeId.ResetForTests();
Environment.SetEnvironmentVariable(ConfigurationKeys.Telemetry.RootSessionId, null);

var rootSessionId = RuntimeId.GetRootSessionId();
rootSessionId.Should().Be(RuntimeId.Get());

// When env var is pre-set (simulating a child process), root session ID
// should return the inherited value instead of the current runtime ID
var inherited = "inherited-root-session-id";
RuntimeId.ResetForTests();
Environment.SetEnvironmentVariable(ConfigurationKeys.Telemetry.RootSessionId, inherited);

RuntimeId.GetRootSessionId().Should().Be(inherited);
RuntimeId.GetRootSessionId().Should().NotBe(RuntimeId.Get());
}
finally
{
RuntimeId.ResetForTests();
Environment.SetEnvironmentVariable(ConfigurationKeys.Telemetry.RootSessionId, null);
RuntimeId.GetRootSessionId();
}
}
}
}
Loading