From e67e038333f74d75f1bbd2183453467f85dc8490 Mon Sep 17 00:00:00 2001 From: Steven Bouwkamp Date: Fri, 16 Aug 2024 18:52:13 -0400 Subject: [PATCH 1/6] Add ability to disable ActivitySources This allows for users to pass in a comma separated list of Activity Source names (glob-pattern supported) that should not be listened to by the Datadog Tracer. --- .../Activity/ActivityListenerHandler.cs | 30 ++++-- .../Handlers/ActivityHandlersRegister.cs | 5 + .../Handlers/DisableActivityHandler.cs | 96 +++++++++++++++++++ .../Configuration/ConfigurationKeys.cs | 7 ++ .../Configuration/ImmutableTracerSettings.cs | 7 ++ .../Configuration/TracerSettings.cs | 10 ++ .../NetActivitySdkTests.cs | 1 + .../Samples.NetActivitySdk/Program.cs | 26 ++++- .../Properties/launchSettings.json | 3 +- 9 files changed, 177 insertions(+), 8 deletions(-) create mode 100644 tracer/src/Datadog.Trace/Activity/Handlers/DisableActivityHandler.cs diff --git a/tracer/src/Datadog.Trace/Activity/ActivityListenerHandler.cs b/tracer/src/Datadog.Trace/Activity/ActivityListenerHandler.cs index ea3a8d0b36f3..45174a73f2bf 100644 --- a/tracer/src/Datadog.Trace/Activity/ActivityListenerHandler.cs +++ b/tracer/src/Datadog.Trace/Activity/ActivityListenerHandler.cs @@ -48,16 +48,34 @@ public static bool OnShouldListenTo(T source) { if (source is IActivitySource activitySource) { - if (handler.ShouldListenTo(sName, activitySource.Version) && HandlerBySource.TryAdd(sName, handler)) + if (handler.ShouldListenTo(sName, activitySource.Version)) { - Log.Debug("ActivityListenerHandler: {SourceName} will be handled by {Handler}.", sName, handler); - return true; + if (handler is DisableActivityHandler) + { + Log.Information("ActivityListenerHandler: {SourceName} will not be listened to by the .NET Tracer.", sName); + return false; + } + + if (HandlerBySource.TryAdd(sName, handler)) + { + Log.Debug("ActivityListenerHandler: {SourceName} will be handled by {Handler}.", sName, handler); + return true; + } } } - else if (handler.ShouldListenTo(sName, null) && HandlerBySource.TryAdd(sName, handler)) + else if (handler.ShouldListenTo(sName, null)) { - Log.Debug("ActivityListenerHandler: {SourceName} will be handled by {Handler}.", sName, handler); - return true; + if (handler is DisableActivityHandler) + { + Log.Information("ActivityListenerHandler: {SourceName} will not be listened to by the .NET Tracer.", sName); + return false; + } + + if (HandlerBySource.TryAdd(sName, handler)) + { + Log.Debug("ActivityListenerHandler: {SourceName} will be handled by {Handler}.", sName, handler); + return true; + } } } diff --git a/tracer/src/Datadog.Trace/Activity/Handlers/ActivityHandlersRegister.cs b/tracer/src/Datadog.Trace/Activity/Handlers/ActivityHandlersRegister.cs index f9f762685cf9..c753f6b2dfd3 100644 --- a/tracer/src/Datadog.Trace/Activity/Handlers/ActivityHandlersRegister.cs +++ b/tracer/src/Datadog.Trace/Activity/Handlers/ActivityHandlersRegister.cs @@ -9,12 +9,17 @@ namespace Datadog.Trace.Activity.Handlers { internal static class ActivityHandlersRegister { + // Disable Activity Handler does not listen to the activity source at all. + internal static readonly DisableActivityHandler DisableHandler = new(); + // Ignore Activity Handler catches existing integrations that also emits activities. internal static readonly IgnoreActivityHandler IgnoreHandler = new(); // Activity handlers in order, the first handler where ShouldListenTo returns true will always handle that source. internal static readonly IActivityHandler[] Handlers = { + DisableHandler, + IgnoreHandler, // Azure Service Bus handlers diff --git a/tracer/src/Datadog.Trace/Activity/Handlers/DisableActivityHandler.cs b/tracer/src/Datadog.Trace/Activity/Handlers/DisableActivityHandler.cs new file mode 100644 index 000000000000..e7115a478cbf --- /dev/null +++ b/tracer/src/Datadog.Trace/Activity/Handlers/DisableActivityHandler.cs @@ -0,0 +1,96 @@ +// +// 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. +// + +#nullable enable + +using System.Collections.Generic; +using System.Text.RegularExpressions; +using Datadog.Trace.Activity.DuckTypes; +using Datadog.Trace.Sampling; + +namespace Datadog.Trace.Activity.Handlers +{ + internal class DisableActivityHandler : IActivityHandler + { + private List? _disabledSourceNameGlobs = null; + private bool _disableAll = false; + + public void ActivityStarted(string sourceName, T activity) + where T : IActivity + { + // do nothing; this should not be called + } + + public void ActivityStopped(string sourceName, T activity) + where T : IActivity + { + // do nothing; this should not be called + } + + /// + /// Determines whether will "listen" to . + /// + /// Note that "listen" in this case means that the created ActivityListener will not subscribe to the ActivitySource. + /// + /// + /// when the Tracer will disable the ActivitySource; otherwise + public bool ShouldListenTo(string sourceName, string? version) + { + if (_disableAll) + { + return true; // "*" was specified as a pattern, short circuit to disable all + } + + _disabledSourceNameGlobs ??= PopulateGlobs(); + if (_disabledSourceNameGlobs.Count == 0) + { + return false; // no glob patterns specified, sourceName will not be disabled + } + + foreach (var regex in _disabledSourceNameGlobs) + { + if (regex.IsMatch(sourceName)) + { + return true; // disable ActivitySource of "sourceName" from being listened to by the tracer + } + } + + var toDisable = Tracer.Instance.Settings.DisabledActivitySources; + if (toDisable is null || toDisable.Length == 0) + { + return false; + } + + // sources were specified to be disabled, but this sourceName didn't match any of them + return false; // sourceName will _not_ be disabled + } + + private List PopulateGlobs() + { + var globs = new List(); + var toDisable = Tracer.Instance.Settings.DisabledActivitySources; + if (toDisable is null || toDisable.Length == 0) + { + return globs; + } + + foreach (var disabledSourceNameGlob in toDisable) + { + // HACK: using RegexBuilder here even though it isn't _really_ for this + var globRegex = RegexBuilder.Build(disabledSourceNameGlob, SamplingRulesFormat.Glob, RegexBuilder.DefaultTimeout); + // handle special case where a "*" pattern will be null + if (globRegex is null) + { + _disableAll = true; + return []; + } + + globs.Add(globRegex); + } + + return globs; + } + } +} diff --git a/tracer/src/Datadog.Trace/Configuration/ConfigurationKeys.cs b/tracer/src/Datadog.Trace/Configuration/ConfigurationKeys.cs index b6d641bbfd98..a4a3b92880eb 100644 --- a/tracer/src/Datadog.Trace/Configuration/ConfigurationKeys.cs +++ b/tracer/src/Datadog.Trace/Configuration/ConfigurationKeys.cs @@ -99,6 +99,13 @@ internal static partial class ConfigurationKeys /// public const string DisabledIntegrations = "DD_DISABLED_INTEGRATIONS"; + /// + /// Configuration key for a list of ActivitySource names (supports globbing) that will be disabled. + /// Default is empty (all ActivitySources will be subscribed to by default). + /// Supports multiple values separated with commas. + /// + public const string DisabledActivitySources = "DD_DISABLED_ACTIVITY_SOURCES"; + /// /// Configuration key for enabling or disabling default Analytics. /// diff --git a/tracer/src/Datadog.Trace/Configuration/ImmutableTracerSettings.cs b/tracer/src/Datadog.Trace/Configuration/ImmutableTracerSettings.cs index 97385d6bb52b..ad69af3f0472 100644 --- a/tracer/src/Datadog.Trace/Configuration/ImmutableTracerSettings.cs +++ b/tracer/src/Datadog.Trace/Configuration/ImmutableTracerSettings.cs @@ -107,6 +107,7 @@ internal ImmutableTracerSettings(TracerSettings settings, bool unusedParamNotToU SpanSamplingRules = settings.SpanSamplingRules; _globalSamplingRate = settings.GlobalSamplingRateInternal; IntegrationsInternal = new ImmutableIntegrationSettingsCollection(settings.IntegrationsInternal, settings.DisabledIntegrationNamesInternal); + DisabledActivitySources = settings.DisabledActivitySources; _headerTags = new ReadOnlyDictionary(settings.HeaderTagsInternal); GrpcTagsInternal = new ReadOnlyDictionary(settings.GrpcTagsInternal); IpHeader = settings.IpHeader; @@ -338,6 +339,12 @@ internal ImmutableTracerSettings(TracerSettings settings, bool unusedParamNotToU [GeneratePublicApi(PublicApiUsage.ImmutableTracerSettings_Integrations_Get)] internal ImmutableIntegrationSettingsCollection IntegrationsInternal { get; } + /// + /// Gets the comma separated string of disabled ActivitySources that will not be handled at all. + /// + /// + internal string[] DisabledActivitySources { get; } + /// /// Gets the global tags, which are applied to all s. /// diff --git a/tracer/src/Datadog.Trace/Configuration/TracerSettings.cs b/tracer/src/Datadog.Trace/Configuration/TracerSettings.cs index a1f6c79ed598..304405b103be 100644 --- a/tracer/src/Datadog.Trace/Configuration/TracerSettings.cs +++ b/tracer/src/Datadog.Trace/Configuration/TracerSettings.cs @@ -176,6 +176,10 @@ _ when x.ToBoolean() is { } boolean => boolean, DisabledIntegrationNamesInternal = new HashSet(disabledIntegrationNames, StringComparer.OrdinalIgnoreCase); + var disabledActivitySources = config.WithKeys(ConfigurationKeys.DisabledActivitySources).AsString(); + + DisabledActivitySources = !string.IsNullOrEmpty(disabledActivitySources) ? TrimSplitString(disabledActivitySources, commaSeparator) : []; + IntegrationsInternal = new IntegrationSettingsCollection(source, unusedParamNotToUsePublicApi: false); ExporterInternal = new ExporterSettings(source, _telemetry); @@ -604,6 +608,12 @@ _ when x.ToBoolean() is { } boolean => boolean, [ConfigKey(ConfigurationKeys.DisabledIntegrations)] internal HashSet DisabledIntegrationNamesInternal { get; set; } + /// + /// Gets the names of disabled ActivitySources. + /// + /// + internal string[] DisabledActivitySources { get; } + /// /// Gets or sets the transport settings that dictate how the tracer connects to the agent. /// diff --git a/tracer/test/Datadog.Trace.ClrProfiler.IntegrationTests/NetActivitySdkTests.cs b/tracer/test/Datadog.Trace.ClrProfiler.IntegrationTests/NetActivitySdkTests.cs index 4c067ee68dbc..ad3cc21609c1 100644 --- a/tracer/test/Datadog.Trace.ClrProfiler.IntegrationTests/NetActivitySdkTests.cs +++ b/tracer/test/Datadog.Trace.ClrProfiler.IntegrationTests/NetActivitySdkTests.cs @@ -60,6 +60,7 @@ public NetActivitySdkTests(ITestOutputHelper output) public async Task SubmitsTraces() { SetEnvironmentVariable("DD_TRACE_OTEL_ENABLED", "true"); + SetEnvironmentVariable("DD_DISABLED_ACTIVITY_SOURCES", "Disabled.By.ExactMatch,*.By.Glob*"); using (var telemetry = this.ConfigureTelemetry()) using (var agent = EnvironmentHelper.GetMockAgent()) diff --git a/tracer/test/test-applications/integrations/Samples.NetActivitySdk/Program.cs b/tracer/test/test-applications/integrations/Samples.NetActivitySdk/Program.cs index 570b3da3c05b..de70c64132a6 100644 --- a/tracer/test/test-applications/integrations/Samples.NetActivitySdk/Program.cs +++ b/tracer/test/test-applications/integrations/Samples.NetActivitySdk/Program.cs @@ -8,6 +8,9 @@ namespace NetActivitySdk; public static class Program { private static ActivitySource _source; + private static ActivitySource _ignored; + private static ActivitySource _disabledGlob; + private static ActivitySource _disabledExact; private static string SpanLinkTraceId1; private static string SpanLinkTraceId2; @@ -19,12 +22,15 @@ public static async Task Main(string[] args) { Console.WriteLine($"SpanId 1: {SpanLinkSpanId1} SpanId 2: {SpanLinkSpanId2}"); _source = new ActivitySource("Samples.NetActivitySdk"); + _ignored = new ActivitySource("Microsoft.AspNetCore"); // this is an ignored source + _disabledGlob = new ActivitySource("Disabled.By.GlobPattern"); + _disabledExact = new ActivitySource("Disabled.By.ExactMatch"); var activityListener = new ActivityListener { //ActivityStarted = activity => Console.WriteLine($"{activity.DisplayName} - Started"), ActivityStopped = activity => PrintSpanStoppedInformation(activity), - ShouldListenTo = _ => true, + ShouldListenTo = source => { return !source.Name.Contains("Disabled"); }, // return true for all except Disabled ones Sample = (ref ActivityCreationOptions options) => ActivitySamplingResult.AllData }; @@ -51,6 +57,8 @@ public static async Task Main(string[] args) RunManuallyUpdatedStartTime(); // 3 spans (50 total) + RunIgnoredAndDisabledSources(); // 0 spans (50 total) + await Task.Delay(1000); } @@ -208,6 +216,22 @@ private static void RunManuallyUpdatedStartTime() } } + private static void RunIgnoredAndDisabledSources() + { + using var ignoredSpan = _ignored.StartActivity("Ignored - SHOULD NOT BE IN SNAPSHOT") ?? throw new InvalidOperationException("Ignored Activity was null - was it disabled?"); + using var disabledSpanGlob = _disabledGlob.StartActivity("DisabledGlob - SHOULD BE NULL AND NOT IN SNAPSHOT"); + if (disabledSpanGlob is not null) + { + throw new InvalidOperationException("DisabledGlob Activity wasn't null"); + } + + using var disabledSpanExact = _disabledExact.StartActivity("DisabledExact - SHOULD BE NULL AND NOT IN SNAPSHOT"); + if (disabledSpanExact is not null) + { + throw new InvalidOperationException("DisabledExact Activity wasn't null"); + } + } + private static void RunActivityUpdate() { using var errorSpan = _source.StartActivity("ErrorSpan"); diff --git a/tracer/test/test-applications/integrations/Samples.NetActivitySdk/Properties/launchSettings.json b/tracer/test/test-applications/integrations/Samples.NetActivitySdk/Properties/launchSettings.json index 2d8c71555b27..ff1948f6ddf5 100644 --- a/tracer/test/test-applications/integrations/Samples.NetActivitySdk/Properties/launchSettings.json +++ b/tracer/test/test-applications/integrations/Samples.NetActivitySdk/Properties/launchSettings.json @@ -17,7 +17,8 @@ "DD_DOTNET_TRACER_HOME": "$(SolutionDir)shared\\bin\\monitoring-home", "DD_TRACE_OTEL_ENABLED": "true", - "DD_TRACE_DEBUG": "true" + "DD_TRACE_DEBUG": "true", + "DD_DISABLED_ACTIVITY_SOURCES": "Disabled.By.ExactMatch,*.By.Glob*" }, "nativeDebugging": false }, From 277351dba85837f689fe113e47de856b171a7cdf Mon Sep 17 00:00:00 2001 From: Steven Bouwkamp Date: Thu, 5 Sep 2024 15:01:17 -0400 Subject: [PATCH 2/6] Use active voice --- tracer/src/Datadog.Trace/Activity/ActivityListenerHandler.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tracer/src/Datadog.Trace/Activity/ActivityListenerHandler.cs b/tracer/src/Datadog.Trace/Activity/ActivityListenerHandler.cs index 45174a73f2bf..04cc79e485f8 100644 --- a/tracer/src/Datadog.Trace/Activity/ActivityListenerHandler.cs +++ b/tracer/src/Datadog.Trace/Activity/ActivityListenerHandler.cs @@ -52,7 +52,7 @@ public static bool OnShouldListenTo(T source) { if (handler is DisableActivityHandler) { - Log.Information("ActivityListenerHandler: {SourceName} will not be listened to by the .NET Tracer.", sName); + Log.Information("ActivityListenerHandler: The .NET Tracer will not listen to {SourceName}.", sName); return false; } @@ -67,7 +67,7 @@ public static bool OnShouldListenTo(T source) { if (handler is DisableActivityHandler) { - Log.Information("ActivityListenerHandler: {SourceName} will not be listened to by the .NET Tracer.", sName); + Log.Information("ActivityListenerHandler: The .NET Tracer will not listen to {SourceName}.", sName); return false; } From 8c314e6a64d9d145ab1dc59bbbaf8a933d95e8c9 Mon Sep 17 00:00:00 2001 From: Steven Bouwkamp Date: Thu, 5 Sep 2024 15:07:33 -0400 Subject: [PATCH 3/6] Change to "DD_TRACE_DISABLED_ACTIVITY_SOURCES" --- tracer/src/Datadog.Trace/Configuration/ConfigurationKeys.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tracer/src/Datadog.Trace/Configuration/ConfigurationKeys.cs b/tracer/src/Datadog.Trace/Configuration/ConfigurationKeys.cs index a4a3b92880eb..922dae852e74 100644 --- a/tracer/src/Datadog.Trace/Configuration/ConfigurationKeys.cs +++ b/tracer/src/Datadog.Trace/Configuration/ConfigurationKeys.cs @@ -104,7 +104,7 @@ internal static partial class ConfigurationKeys /// Default is empty (all ActivitySources will be subscribed to by default). /// Supports multiple values separated with commas. /// - public const string DisabledActivitySources = "DD_DISABLED_ACTIVITY_SOURCES"; + public const string DisabledActivitySources = "DD_TRACE_DISABLED_ACTIVITY_SOURCES"; /// /// Configuration key for enabling or disabling default Analytics. From 8d2f356f1f20841d77946038304287420658eeca Mon Sep 17 00:00:00 2001 From: Steven Bouwkamp Date: Thu, 5 Sep 2024 16:01:49 -0400 Subject: [PATCH 4/6] Manually update config_norm_rules copy --- tracer/test/Datadog.Trace.Tests/Telemetry/config_norm_rules.json | 1 + 1 file changed, 1 insertion(+) diff --git a/tracer/test/Datadog.Trace.Tests/Telemetry/config_norm_rules.json b/tracer/test/Datadog.Trace.Tests/Telemetry/config_norm_rules.json index d6b28db73736..8dab73a71099 100644 --- a/tracer/test/Datadog.Trace.Tests/Telemetry/config_norm_rules.json +++ b/tracer/test/Datadog.Trace.Tests/Telemetry/config_norm_rules.json @@ -529,6 +529,7 @@ "DD_IAST_TRUNCATION_MAX_VALUE_LENGTH": "iast_truncation_max_value_length", "DD_IAST_DB_ROWS_TO_TAINT": "iast_db_rows_to_taint", "DD_IAST_COOKIE_FILTER_PATTERN": "iast_cookie_filter_pattern", + "DD_TRACE_DISABLED_ACTIVITY_SOURCES": "dd_trace_disabled_activity_sources", "DD_TRACE_STARTUP_LOGS": "trace_startup_logs_enabled", "DD_MAX_LOGFILE_SIZE": "trace_log_file_max_size", "DD_TRACE_LOGGING_RATE": "trace_log_rate", From 436bd00aa5e1ec1dbba32a9e873d695058dbd7e3 Mon Sep 17 00:00:00 2001 From: Steven Bouwkamp Date: Fri, 6 Sep 2024 13:20:28 -0400 Subject: [PATCH 5/6] Fix tests --- .../NetActivitySdkTests.cs | 2 +- .../Samples.NetActivitySdk/Properties/launchSettings.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/tracer/test/Datadog.Trace.ClrProfiler.IntegrationTests/NetActivitySdkTests.cs b/tracer/test/Datadog.Trace.ClrProfiler.IntegrationTests/NetActivitySdkTests.cs index ad3cc21609c1..069140c6a99b 100644 --- a/tracer/test/Datadog.Trace.ClrProfiler.IntegrationTests/NetActivitySdkTests.cs +++ b/tracer/test/Datadog.Trace.ClrProfiler.IntegrationTests/NetActivitySdkTests.cs @@ -60,7 +60,7 @@ public NetActivitySdkTests(ITestOutputHelper output) public async Task SubmitsTraces() { SetEnvironmentVariable("DD_TRACE_OTEL_ENABLED", "true"); - SetEnvironmentVariable("DD_DISABLED_ACTIVITY_SOURCES", "Disabled.By.ExactMatch,*.By.Glob*"); + SetEnvironmentVariable("DD_TRACE_DISABLED_ACTIVITY_SOURCES", "Disabled.By.ExactMatch,*.By.Glob*"); using (var telemetry = this.ConfigureTelemetry()) using (var agent = EnvironmentHelper.GetMockAgent()) diff --git a/tracer/test/test-applications/integrations/Samples.NetActivitySdk/Properties/launchSettings.json b/tracer/test/test-applications/integrations/Samples.NetActivitySdk/Properties/launchSettings.json index ff1948f6ddf5..c7132c48d4cb 100644 --- a/tracer/test/test-applications/integrations/Samples.NetActivitySdk/Properties/launchSettings.json +++ b/tracer/test/test-applications/integrations/Samples.NetActivitySdk/Properties/launchSettings.json @@ -18,7 +18,7 @@ "DD_DOTNET_TRACER_HOME": "$(SolutionDir)shared\\bin\\monitoring-home", "DD_TRACE_OTEL_ENABLED": "true", "DD_TRACE_DEBUG": "true", - "DD_DISABLED_ACTIVITY_SOURCES": "Disabled.By.ExactMatch,*.By.Glob*" + "DD_TRACE_DISABLED_ACTIVITY_SOURCES": "Disabled.By.ExactMatch,*.By.Glob*" }, "nativeDebugging": false }, From f4e4f3648ecd943b927700bc48853bea4b0c476b Mon Sep 17 00:00:00 2001 From: Steven Bouwkamp Date: Fri, 11 Oct 2024 14:15:39 -0400 Subject: [PATCH 6/6] Remove redundant check --- .../Activity/Handlers/DisableActivityHandler.cs | 6 ------ 1 file changed, 6 deletions(-) diff --git a/tracer/src/Datadog.Trace/Activity/Handlers/DisableActivityHandler.cs b/tracer/src/Datadog.Trace/Activity/Handlers/DisableActivityHandler.cs index e7115a478cbf..a2551a1b83e2 100644 --- a/tracer/src/Datadog.Trace/Activity/Handlers/DisableActivityHandler.cs +++ b/tracer/src/Datadog.Trace/Activity/Handlers/DisableActivityHandler.cs @@ -57,12 +57,6 @@ public bool ShouldListenTo(string sourceName, string? version) } } - var toDisable = Tracer.Instance.Settings.DisabledActivitySources; - if (toDisable is null || toDisable.Length == 0) - { - return false; - } - // sources were specified to be disabled, but this sourceName didn't match any of them return false; // sourceName will _not_ be disabled }