diff --git a/tracer/src/Datadog.Trace/Debugger/DebuggerFactory.cs b/tracer/src/Datadog.Trace/Debugger/DebuggerFactory.cs index e8b649593f30..18806b73355d 100644 --- a/tracer/src/Datadog.Trace/Debugger/DebuggerFactory.cs +++ b/tracer/src/Datadog.Trace/Debugger/DebuggerFactory.cs @@ -72,6 +72,7 @@ private static IDogStatsd GetDogStatsd(TracerSettings tracerSettings, string ser tracerSettings.Manager.InitialMutableSettings, tracerSettings.Manager.InitialExporterSettings, includeDefaultTags: false, + tracerSettings.PropagateProcessTags ? ProcessTags.TagsList : [], prefix: DebuggerSettings.DebuggerMetricPrefix); } diff --git a/tracer/src/Datadog.Trace/DogStatsd/StatsdFactory.cs b/tracer/src/Datadog.Trace/DogStatsd/StatsdFactory.cs index 37f25162c6b2..42a51aa6c83d 100644 --- a/tracer/src/Datadog.Trace/DogStatsd/StatsdFactory.cs +++ b/tracer/src/Datadog.Trace/DogStatsd/StatsdFactory.cs @@ -18,19 +18,19 @@ internal static class StatsdFactory { private static readonly IDatadogLogger Log = DatadogLogging.GetLoggerFor(typeof(StatsdFactory)); - internal static IDogStatsd CreateDogStatsdClient(MutableSettings settings, ExporterSettings exporter, bool includeDefaultTags, string? prefix = null) + internal static IDogStatsd CreateDogStatsdClient(MutableSettings settings, ExporterSettings exporter, bool includeDefaultTags, IList processTags, string? prefix = null) { + var customTagCount = settings.GlobalTags.Count; + var tagsCount = (includeDefaultTags ? 5 + customTagCount : 0) + processTags.Count; + var constantTags = new List(tagsCount); if (includeDefaultTags) { - var customTagCount = settings.GlobalTags.Count; - var constantTags = new List(5 + customTagCount) - { - "lang:.NET", - $"lang_interpreter:{FrameworkDescription.Instance.Name}", - $"lang_version:{FrameworkDescription.Instance.ProductVersion}", - $"tracer_version:{TracerConstants.AssemblyVersion}", - $"{Tags.RuntimeId}:{Tracer.RuntimeId}" - }; + constantTags.Add("lang:.NET"); + constantTags.Add($"lang_interpreter:{FrameworkDescription.Instance.Name}"); + constantTags.Add($"lang_version:{FrameworkDescription.Instance.ProductVersion}"); + constantTags.Add($"tracer_version:{TracerConstants.AssemblyVersion}"); + constantTags.Add($"{Tags.RuntimeId}:{Tracer.RuntimeId}"); + // update count above if adding new tags if (customTagCount > 0) { @@ -43,11 +43,14 @@ internal static IDogStatsd CreateDogStatsdClient(MutableSettings settings, Expor constantTags.Add($"{key}:{value}"); } } + } - return CreateDogStatsdClient(settings, exporter, constantTags, prefix); + if (processTags.Count > 0) + { + constantTags.AddRange(processTags); } - return CreateDogStatsdClient(settings, exporter, constantTags: null, prefix); + return CreateDogStatsdClient(settings, exporter, constantTags, prefix); } private static IDogStatsd CreateDogStatsdClient(MutableSettings settings, ExporterSettings exporter, List? constantTags, string? prefix = null) diff --git a/tracer/src/Datadog.Trace/DogStatsd/StatsdManager.cs b/tracer/src/Datadog.Trace/DogStatsd/StatsdManager.cs index 3d455cb149ab..f34330006406 100644 --- a/tracer/src/Datadog.Trace/DogStatsd/StatsdManager.cs +++ b/tracer/src/Datadog.Trace/DogStatsd/StatsdManager.cs @@ -6,6 +6,7 @@ #nullable enable using System; +using System.Collections.Generic; using System.Threading; using System.Threading.Tasks; using Datadog.Trace.Configuration; @@ -33,12 +34,13 @@ public StatsdManager(TracerSettings tracerSettings) } // Internal for testing - internal StatsdManager(TracerSettings tracerSettings, Func statsdFactory) + internal StatsdManager(TracerSettings tracerSettings, Func, StatsdClientHolder> statsdFactory) { // The initial factory, assuming there's no updates _factory = () => statsdFactory( tracerSettings.Manager.InitialMutableSettings, - tracerSettings.Manager.InitialExporterSettings); + tracerSettings.Manager.InitialExporterSettings, + tracerSettings.PropagateProcessTags ? ProcessTags.TagsList : []); // We don't create a new client unless we need one, and we rely on consumers of the manager to tell us when it's needed _current = null; @@ -60,7 +62,8 @@ internal StatsdManager(TracerSettings tracerSettings, Func statsdFactory( c.UpdatedMutable ?? c.PreviousMutable, - c.UpdatedExporter ?? c.PreviousExporter)); + c.UpdatedExporter ?? c.PreviousExporter, + tracerSettings.PropagateProcessTags ? ProcessTags.TagsList : [])); // check if we actually need to do an update or if noone is using the client yet if (Volatile.Read(ref _isRequiredMask) != 0) @@ -173,8 +176,10 @@ internal static bool HasImpactingChanges(TracerSettings.SettingsManager.SettingC return hasChanges; } - private static StatsdClientHolder CreateClient(MutableSettings settings, ExporterSettings exporter) - => new(StatsdFactory.CreateDogStatsdClient(settings, exporter, includeDefaultTags: true)); + private static StatsdClientHolder CreateClient(MutableSettings settings, ExporterSettings exporter, IList processTags) + { + return new StatsdClientHolder(StatsdFactory.CreateDogStatsdClient(settings, exporter, includeDefaultTags: true, processTags)); + } private void EnsureClient(bool ensureCreated, bool forceRecreate) { diff --git a/tracer/src/Datadog.Trace/ProcessTags.cs b/tracer/src/Datadog.Trace/ProcessTags.cs index 9e65d4a37425..a48525f8f377 100644 --- a/tracer/src/Datadog.Trace/ProcessTags.cs +++ b/tracer/src/Datadog.Trace/ProcessTags.cs @@ -7,10 +7,10 @@ using System; using System.Collections.Generic; +using System.Collections.ObjectModel; using System.IO; using Datadog.Trace.Configuration; using Datadog.Trace.Processors; -using Datadog.Trace.Util; namespace Datadog.Trace; @@ -21,7 +21,7 @@ internal static class ProcessTags public const string EntrypointWorkdir = "entrypoint.workdir"; // two views on the same data - public static readonly List TagsList = GetTagsList(); + public static readonly ReadOnlyCollection TagsList = GetTagsList().AsReadOnly(); public static readonly string SerializedTags = GetSerializedTagsFromList(TagsList); private static List GetTagsList() @@ -53,7 +53,7 @@ private static void AddNormalizedTag(this List tags, string key, string? tags.Add($"{key}:{normalizedValue}"); } - private static string GetSerializedTagsFromList(List tags) + private static string GetSerializedTagsFromList(IEnumerable tags) { return string.Join(",", tags); } diff --git a/tracer/src/Datadog.Trace/RemoteConfigurationManagement/Protocol/RcmClientTracer.cs b/tracer/src/Datadog.Trace/RemoteConfigurationManagement/Protocol/RcmClientTracer.cs index 7a8ade5c158d..9797854eb134 100644 --- a/tracer/src/Datadog.Trace/RemoteConfigurationManagement/Protocol/RcmClientTracer.cs +++ b/tracer/src/Datadog.Trace/RemoteConfigurationManagement/Protocol/RcmClientTracer.cs @@ -10,15 +10,13 @@ using System.Linq; using Datadog.Trace.Vendors.Newtonsoft.Json; -#nullable enable - namespace Datadog.Trace.RemoteConfigurationManagement.Protocol { internal sealed class RcmClientTracer { // Don't change this constructor - it's used by Newtonsoft.JSON for deserialization // and that can mean the provided properties are not _really_ nullable, even though we "require" them to be - public RcmClientTracer(string runtimeId, string tracerVersion, string service, string env, string? appVersion, List tags, List? processTags) + public RcmClientTracer(string runtimeId, string tracerVersion, string service, string env, string? appVersion, List tags, IList? processTags) { RuntimeId = runtimeId; Language = TracerConstants.Language; @@ -46,7 +44,7 @@ public RcmClientTracer(string runtimeId, string tracerVersion, string service, s public string? Service { get; } [JsonProperty("process_tags")] - public List? ProcessTags { get; } + public IList? ProcessTags { get; } [JsonProperty("extra_services")] public string[]? ExtraServices { get; set; } @@ -60,7 +58,7 @@ public RcmClientTracer(string runtimeId, string tracerVersion, string service, s [JsonProperty("tags")] public List Tags { get; } - public static RcmClientTracer Create(string runtimeId, string tracerVersion, string service, string env, string? appVersion, ReadOnlyDictionary globalTags, List? processTags) + public static RcmClientTracer Create(string runtimeId, string tracerVersion, string service, string env, string? appVersion, ReadOnlyDictionary globalTags, IList? processTags) => new(runtimeId, tracerVersion, service, env, appVersion, GetTags(env, service, globalTags), processTags); private static List GetTags(string? environment, string? serviceVersion, ReadOnlyDictionary? globalTags) diff --git a/tracer/src/Datadog.Trace/RemoteConfigurationManagement/RemoteConfigurationManager.cs b/tracer/src/Datadog.Trace/RemoteConfigurationManagement/RemoteConfigurationManager.cs index 8eaa6aed3ad7..58e2d008819a 100644 --- a/tracer/src/Datadog.Trace/RemoteConfigurationManagement/RemoteConfigurationManager.cs +++ b/tracer/src/Datadog.Trace/RemoteConfigurationManagement/RemoteConfigurationManager.cs @@ -42,7 +42,7 @@ private RemoteConfigurationManager( TimeSpan pollInterval, IGitMetadataTagsProvider gitMetadataTagsProvider, IRcmSubscriptionManager subscriptionManager, - List? processTags) + IList? processTags) { _discoveryService = discoveryService; _pollInterval = pollInterval; diff --git a/tracer/test/Datadog.Trace.ClrProfiler.IntegrationTests/RuntimeMetricsTests.cs b/tracer/test/Datadog.Trace.ClrProfiler.IntegrationTests/RuntimeMetricsTests.cs index 2d01f7ef0993..6856dd5fbfc9 100644 --- a/tracer/test/Datadog.Trace.ClrProfiler.IntegrationTests/RuntimeMetricsTests.cs +++ b/tracer/test/Datadog.Trace.ClrProfiler.IntegrationTests/RuntimeMetricsTests.cs @@ -82,6 +82,60 @@ public async Task NamedPipesSubmitsMetrics() await RunTest(); } + [SkippableFact] + [Trait("Category", "EndToEnd")] + [Trait("RunOnWindows", "True")] + public async Task ProcessTagsIncludedInMetrics_WhenEnabled() + { + SkipOn.Platform(SkipOn.PlatformValue.MacOs); + EnvironmentHelper.EnableDefaultTransport(); + SetEnvironmentVariable("DD_RUNTIME_METRICS_ENABLED", "1"); + SetEnvironmentVariable("DD_EXPERIMENTAL_PROPAGATE_PROCESS_TAGS_ENABLED", "1"); + SetEnvironmentVariable("DD_SERVICE", "Samples.RuntimeMetrics"); + + using var agent = EnvironmentHelper.GetMockAgent(useStatsD: true); + using var processResult = await RunSampleAndWaitForExit(agent); + var requests = agent.StatsdRequests; + + Assert.True(requests.Count > 0, "No metrics received"); + + var metrics = requests.SelectMany(x => x.Split('\n')).ToList(); + + // Verify process tags are present in the metrics + // Process tags include: entrypoint.basedir, entrypoint.workdir, and optionally entrypoint.name + metrics + .Should() + .OnlyContain(s => s.Contains("entrypoint.basedir:"), "entrypoint.basedir process tag should be present") + .And.OnlyContain(s => s.Contains("entrypoint.workdir:"), "entrypoint.workdir process tag should be present"); + } + + [SkippableFact] + [Trait("Category", "EndToEnd")] + [Trait("RunOnWindows", "True")] + public async Task ProcessTagsNotIncludedInMetrics_WhenDisabled() + { + SkipOn.Platform(SkipOn.PlatformValue.MacOs); + EnvironmentHelper.EnableDefaultTransport(); + SetEnvironmentVariable("DD_RUNTIME_METRICS_ENABLED", "1"); + SetEnvironmentVariable("DD_EXPERIMENTAL_PROPAGATE_PROCESS_TAGS_ENABLED", "0"); + SetEnvironmentVariable("DD_SERVICE", "Samples.RuntimeMetrics"); + + using var agent = EnvironmentHelper.GetMockAgent(useStatsD: true); + using var processResult = await RunSampleAndWaitForExit(agent); + var requests = agent.StatsdRequests; + + Assert.True(requests.Count > 0, "No metrics received"); + + var metrics = requests.SelectMany(x => x.Split('\n')).ToList(); + + // Verify process tags are NOT present in the metrics when disabled + metrics + .Should() + .NotContain(s => s.Contains("entrypoint.basedir:"), "entrypoint.basedir should not be present when process tags are disabled") + .And.NotContain(s => s.Contains("entrypoint.workdir:"), "entrypoint.workdir should not be present when process tags are disabled") + .And.NotContain(s => s.Contains("entrypoint.name:"), "entrypoint.name should not be present when process tags are disabled"); + } + private async Task RunTest() { var inputServiceName = "12_$#Samples.$RuntimeMetrics"; diff --git a/tracer/test/Datadog.Trace.Tests/DogStatsDTests.cs b/tracer/test/Datadog.Trace.Tests/DogStatsDTests.cs index 487b80ffcb86..530b8b5b26d4 100644 --- a/tracer/test/Datadog.Trace.Tests/DogStatsDTests.cs +++ b/tracer/test/Datadog.Trace.Tests/DogStatsDTests.cs @@ -139,6 +139,7 @@ public void CanCreateDogStatsD_UDP_FromTraceAgentSettings(string agentUri, strin settings.Manager.InitialMutableSettings, settings.Manager.InitialExporterSettings, includeDefaultTags: true, + processTags: ["a:b", "c:d"], prefix: null); // If there's an error during configuration, we get a no-op instance, so using this as a test @@ -168,6 +169,7 @@ public void CanCreateDogStatsD_NamedPipes_FromTraceAgentSettings() settings.Manager.InitialMutableSettings, settings.Manager.InitialExporterSettings, includeDefaultTags: true, + processTags: ["a:b", "c:d"], prefix: null); // If there's an error during configuration, we get a no-op instance, so using this as a test @@ -202,6 +204,7 @@ public void CanCreateDogStatsD_UDS_FromTraceAgentSettings() settings.Manager.InitialMutableSettings, settings.Manager.InitialExporterSettings, includeDefaultTags: true, + processTags: ["a:b", "c:d"], prefix: null); // If there's an error during configuration, we get a no-op instance, so using this as a test @@ -234,6 +237,7 @@ public void CanCreateDogStatsD_UDS_FallsBackToUdp_FromTraceAgentSettings() settings.Manager.InitialMutableSettings, settings.Manager.InitialExporterSettings, includeDefaultTags: true, + processTags: ["a:b", "c:d"], prefix: null); // If there's an error during configuration, we get a no-op instance, so using this as a test diff --git a/tracer/test/Datadog.Trace.Tests/DogStatsd/StatsdManagerTests.cs b/tracer/test/Datadog.Trace.Tests/DogStatsd/StatsdManagerTests.cs index 4dc61ae82271..2c6e56b1247e 100644 --- a/tracer/test/Datadog.Trace.Tests/DogStatsd/StatsdManagerTests.cs +++ b/tracer/test/Datadog.Trace.Tests/DogStatsd/StatsdManagerTests.cs @@ -111,7 +111,7 @@ public void HasImpactingChanges_WhenMutableChangesGlobalTags() public void InitialState_ClientNotCreated() { var clientCount = 0; - using var manager = new StatsdManager(new TracerSettings(), (_, _) => + using var manager = new StatsdManager(new TracerSettings(), (_, _, _) => { Interlocked.Increment(ref clientCount); return new(new MockStatsdClient()); @@ -127,7 +127,7 @@ public void InitialState_ClientNotCreated() public void SetRequired_CreatesClient() { var clientCount = 0; - using var manager = new StatsdManager(new TracerSettings(), (_, _) => + using var manager = new StatsdManager(new TracerSettings(), (_, _, _) => { Interlocked.Increment(ref clientCount); return new(new MockStatsdClient()); @@ -144,7 +144,7 @@ public void SetRequired_CreatesClient() public void SetRequired_False_DisposesClient() { var holder = new StatsdManager.StatsdClientHolder(new MockStatsdClient()); - using var manager = new StatsdManager(new TracerSettings(), (_, _) => holder); + using var manager = new StatsdManager(new TracerSettings(), (_, _, _) => holder); manager.SetRequired(StatsdConsumer.RuntimeMetricsWriter, true); using (manager.TryGetClientLease()) @@ -160,7 +160,7 @@ public void SetRequired_False_DisposesClient() public void MultipleConsumers_AllRequire_SingleClient() { var clientCount = 0; - using var manager = new StatsdManager(new TracerSettings(), (_, _) => + using var manager = new StatsdManager(new TracerSettings(), (_, _, _) => { Interlocked.Increment(ref clientCount); return new(new MockStatsdClient()); @@ -178,7 +178,7 @@ public void MultipleConsumers_PartialUnrequire_KeepsClient() { var clientCount = 0; var holder = new StatsdManager.StatsdClientHolder(new MockStatsdClient()); - using var manager = new StatsdManager(new TracerSettings(), (_, _) => + using var manager = new StatsdManager(new TracerSettings(), (_, _, _) => { Interlocked.Increment(ref clientCount); return holder; @@ -198,7 +198,7 @@ public void MultipleConsumers_AllUnrequire_DisposesClient() { var clientCount = 0; var holder = new StatsdManager.StatsdClientHolder(new MockStatsdClient()); - using var manager = new StatsdManager(new TracerSettings(), (_, _) => + using var manager = new StatsdManager(new TracerSettings(), (_, _, _) => { Interlocked.Increment(ref clientCount); return holder; @@ -223,7 +223,7 @@ public void MultipleConsumers_ReRequire_CreatesNewClient() { var clientCount = 0; StatsdManager.StatsdClientHolder holder = null; - using var manager = new StatsdManager(new TracerSettings(), (_, _) => + using var manager = new StatsdManager(new TracerSettings(), (_, _, _) => { Interlocked.Increment(ref clientCount); var newClient = new StatsdManager.StatsdClientHolder(new MockStatsdClient()); @@ -245,7 +245,7 @@ public void MultipleConsumers_ReRequire_CreatesNewClient() public void Lease_ProvidesAccessToClient() { var holder = new StatsdManager.StatsdClientHolder(new MockStatsdClient()); - using var manager = new StatsdManager(new TracerSettings(), (_, _) => holder); + using var manager = new StatsdManager(new TracerSettings(), (_, _, _) => holder); manager.SetRequired(StatsdConsumer.RuntimeMetricsWriter, true); using var lease = manager.TryGetClientLease(); @@ -257,7 +257,7 @@ public void Lease_ProvidesAccessToClient() public void MultipleLeasesSimultaneously_ShareSameClient() { var holder = new StatsdManager.StatsdClientHolder(new MockStatsdClient()); - using var manager = new StatsdManager(new TracerSettings(), (_, _) => holder); + using var manager = new StatsdManager(new TracerSettings(), (_, _, _) => holder); manager.SetRequired(StatsdConsumer.RuntimeMetricsWriter, true); using var lease1 = manager.TryGetClientLease(); @@ -273,7 +273,7 @@ public void MultipleLeasesSimultaneously_ShareSameClient() public void DisposingLease_DoesNotDisposeClient_WhileOtherLeasesActive() { var holder = new StatsdManager.StatsdClientHolder(new MockStatsdClient()); - using var manager = new StatsdManager(new TracerSettings(), (_, _) => holder); + using var manager = new StatsdManager(new TracerSettings(), (_, _, _) => holder); manager.SetRequired(StatsdConsumer.RuntimeMetricsWriter, true); var lease1 = manager.TryGetClientLease(); @@ -291,7 +291,7 @@ public void DisposingLease_DoesNotDisposeClient_WhileOtherLeasesActive() public void NeverReturnsDisposedClient() { StatsdManager.StatsdClientHolder holder = null; - using var manager = new StatsdManager(new TracerSettings(), (_, _) => + using var manager = new StatsdManager(new TracerSettings(), (_, _, _) => { var newClient = new StatsdManager.StatsdClientHolder(new MockStatsdClient()); Volatile.Write(ref holder, newClient); @@ -321,7 +321,7 @@ public void NeverReturnsDisposedClient() public void ReferenceCountingPreventsDisposalWhileLeasesActive() { var holder = new StatsdManager.StatsdClientHolder(new MockStatsdClient()); - using var manager = new StatsdManager(new TracerSettings(), (_, _) => holder); + using var manager = new StatsdManager(new TracerSettings(), (_, _, _) => holder); manager.SetRequired(StatsdConsumer.RuntimeMetricsWriter, true); var lease = manager.TryGetClientLease(); @@ -338,7 +338,7 @@ public void ReferenceCountingPreventsDisposalWhileLeasesActive() public void Dispose_WithActiveLease_DisposesAfterLeaseReleased() { var holder = new StatsdManager.StatsdClientHolder(new MockStatsdClient()); - var manager = new StatsdManager(new TracerSettings(), (_, _) => holder); + var manager = new StatsdManager(new TracerSettings(), (_, _, _) => holder); manager.SetRequired(StatsdConsumer.RuntimeMetricsWriter, true); var lease = manager.TryGetClientLease(); @@ -358,7 +358,7 @@ public void SettingsUpdate_RecreatesClient_WhenRequired() { var clientCount = 0; var tracerSettings = new TracerSettings(); - using var manager = new StatsdManager(tracerSettings, (_, _) => + using var manager = new StatsdManager(tracerSettings, (_, _, _) => { Interlocked.Increment(ref clientCount); return new(new MockStatsdClient()); @@ -389,7 +389,7 @@ public void SettingsUpdate_OldLeaseContinuesWorkingWithOldClient() var tracerSettings = new TracerSettings(); StatsdManager.StatsdClientHolder holder = null; - using var manager = new StatsdManager(tracerSettings, (_, _) => + using var manager = new StatsdManager(tracerSettings, (_, _, _) => { var newClient = new StatsdManager.StatsdClientHolder(new MockStatsdClient()); Volatile.Write(ref holder, newClient); @@ -426,7 +426,7 @@ public void SettingsUpdate_DoesNotRecreateClient_WhenNotRequired() { var tracerSettings = new TracerSettings(); var clientCount = 0; - using var manager = new StatsdManager(tracerSettings, (_, _) => + using var manager = new StatsdManager(tracerSettings, (_, _, _) => { Interlocked.Increment(ref clientCount); return new(new MockStatsdClient()); @@ -448,7 +448,7 @@ public void SettingsUpdate_DoesNotRecreateClient_WhenSettingsDontChange() { var tracerSettings = new TracerSettings(); var clientCount = 0; - using var manager = new StatsdManager(tracerSettings, (_, _) => + using var manager = new StatsdManager(tracerSettings, (_, _, _) => { Interlocked.Increment(ref clientCount); return new(new MockStatsdClient()); @@ -471,7 +471,7 @@ public void SettingsUpdate_DoesNotRecreateClient_WhenRelevantSettingsDontChange( { var tracerSettings = new TracerSettings(); var clientCount = 0; - using var manager = new StatsdManager(tracerSettings, (_, _) => + using var manager = new StatsdManager(tracerSettings, (_, _, _) => { Interlocked.Increment(ref clientCount); return new(new MockStatsdClient()); @@ -494,7 +494,7 @@ public void SettingsUpdate_DoesNotRecreateClient_WhenRelevantSettingsDontChange( public void ConcurrentLeaseAcquisition_AllSucceed() { var holder = new StatsdManager.StatsdClientHolder(new MockStatsdClient()); - using var manager = new StatsdManager(new TracerSettings(), (_, _) => holder); + using var manager = new StatsdManager(new TracerSettings(), (_, _, _) => holder); manager.SetRequired(StatsdConsumer.RuntimeMetricsWriter, true); var leases = new ConcurrentQueue(); @@ -519,7 +519,7 @@ public async Task ConcurrentLeaseAcquisitionAndDisposal_ThreadSafe() { var clientCount = 0; var holder = new StatsdManager.StatsdClientHolder(new MockStatsdClient()); - using var manager = new StatsdManager(new TracerSettings(), (_, _) => + using var manager = new StatsdManager(new TracerSettings(), (_, _, _) => { Interlocked.Increment(ref clientCount); return holder; @@ -567,7 +567,7 @@ public async Task ConcurrentLeaseAcquisitionAndDisposal_ThreadSafe() public void ConcurrentSetRequired_ThreadSafe() { var clientCount = 0; - using var manager = new StatsdManager(new TracerSettings(), (_, _) => + using var manager = new StatsdManager(new TracerSettings(), (_, _, _) => { Interlocked.Increment(ref clientCount); return new(new MockStatsdClient()); @@ -593,7 +593,7 @@ public async Task ConcurrentSettingsUpdateAndLeaseAcquisition_ThreadSafe() { var tracerSettings = new TracerSettings(); var clientCount = 0; - using var manager = new StatsdManager(tracerSettings, (_, _) => + using var manager = new StatsdManager(tracerSettings, (_, _, _) => { Interlocked.Increment(ref clientCount); return new(new MockStatsdClient()); @@ -647,7 +647,7 @@ public async Task ConcurrentSettingsUpdateAndLeaseAcquisition_ThreadSafe() public async Task ConcurrentLeaseDisposalDuringClientRecreation_ThreadSafe() { var holders = new ConcurrentQueue(); - using var manager = new StatsdManager(new TracerSettings(), (_, _) => + using var manager = new StatsdManager(new TracerSettings(), (_, _, _) => { var client = new StatsdManager.StatsdClientHolder(new MockStatsdClient()); holders.Enqueue(client); @@ -694,7 +694,7 @@ public async Task ConcurrentLeaseDisposalDuringClientRecreation_ThreadSafe() public void MultipleTransitionsBetweenRequiredAndNotRequired() { var holders = new List(); - using var manager = new StatsdManager(new TracerSettings(), (_, _) => + using var manager = new StatsdManager(new TracerSettings(), (_, _, _) => { var client = new StatsdManager.StatsdClientHolder(new MockStatsdClient()); holders.Add(client); @@ -716,7 +716,7 @@ public void MultipleTransitionsBetweenRequiredAndNotRequired() [Fact] public void Dispose_MultipleTimes_IsSafe() { - using var manager = new StatsdManager(new TracerSettings(), (_, _) => new(new MockStatsdClient())); + using var manager = new StatsdManager(new TracerSettings(), (_, _, _) => new(new MockStatsdClient())); manager.SetRequired(StatsdConsumer.RuntimeMetricsWriter, true); manager.Dispose(); @@ -724,10 +724,46 @@ public void Dispose_MultipleTimes_IsSafe() manager.Dispose(); } + [Fact] + public void ProcessTags_PassedToFactory_WhenEnabled() + { + IList capturedProcessTags = null; + var settings = TracerSettings.Create(new() { { ConfigurationKeys.PropagateProcessTags, true } }); + using var manager = new StatsdManager(settings, (_, _, processTags) => + { + capturedProcessTags = processTags; + return new(new MockStatsdClient()); + }); + + manager.SetRequired(StatsdConsumer.RuntimeMetricsWriter, true); + + capturedProcessTags.Should().NotBeNull(); + capturedProcessTags.Should().NotBeEmpty("process tags should be passed to factory when enabled"); + // Verify the format is key:value + capturedProcessTags.Should().AllSatisfy(tag => tag.Should().Contain(":")); + } + + [Fact] + public void ProcessTags_NotPassedToFactory_WhenDisabled() + { + IList capturedProcessTags = null; + var settings = TracerSettings.Create(new() { { ConfigurationKeys.PropagateProcessTags, false } }); + using var manager = new StatsdManager(settings, (_, _, processTags) => + { + capturedProcessTags = processTags; + return new(new MockStatsdClient()); + }); + + manager.SetRequired(StatsdConsumer.RuntimeMetricsWriter, true); + + capturedProcessTags.Should().NotBeNull(); + capturedProcessTags.Should().BeEmpty("process tags should not be passed to factory when disabled"); + } + [Fact] public void DefaultLease_CanDisposeSafely() { - using var manager = new StatsdManager(new TracerSettings(), (_, _) => new(new MockStatsdClient())); + using var manager = new StatsdManager(new TracerSettings(), (_, _, _) => new(new MockStatsdClient())); var lease = manager.TryGetClientLease(); @@ -739,7 +775,7 @@ public void DefaultLease_CanDisposeSafely() public void DisposingLease_MultipleTimes_DoesNotDisposeStatsDMultipleTimes() { var holder = new StatsdManager.StatsdClientHolder(new MockStatsdClient()); - using var manager = new StatsdManager(new TracerSettings(), (_, _) => holder); + using var manager = new StatsdManager(new TracerSettings(), (_, _, _) => holder); manager.SetRequired(StatsdConsumer.RuntimeMetricsWriter, true); var lease = manager.TryGetClientLease(); diff --git a/tracer/test/Datadog.Trace.Tests/RuntimeMetrics/RuntimeEventListenerTests.cs b/tracer/test/Datadog.Trace.Tests/RuntimeMetrics/RuntimeEventListenerTests.cs index 2701dc39d087..2c834617bd86 100644 --- a/tracer/test/Datadog.Trace.Tests/RuntimeMetrics/RuntimeEventListenerTests.cs +++ b/tracer/test/Datadog.Trace.Tests/RuntimeMetrics/RuntimeEventListenerTests.cs @@ -151,7 +151,7 @@ public void UpdateStatsdOnReinitialization() var settings = TracerSettings.Create(new() { { ConfigurationKeys.ServiceName, "original" } }); var statsdManager = new StatsdManager( settings, - (m, e) => new(m.ServiceName == "original" ? originalStatsd.Object : newStatsd.Object)); + (m, e, p) => new(m.ServiceName == "original" ? originalStatsd.Object : newStatsd.Object)); using var listener = new RuntimeEventListener(statsdManager, TimeSpan.FromSeconds(1)); using var writer = new RuntimeMetricsWriter(statsdManager, TimeSpan.FromSeconds(1), false);