diff --git a/.publicApi/Microsoft.ApplicationInsights.dll/Stable/PublicAPI.Unshipped.txt b/.publicApi/Microsoft.ApplicationInsights.dll/Stable/PublicAPI.Unshipped.txt index 02fa764ab..862a7bb6f 100644 --- a/.publicApi/Microsoft.ApplicationInsights.dll/Stable/PublicAPI.Unshipped.txt +++ b/.publicApi/Microsoft.ApplicationInsights.dll/Stable/PublicAPI.Unshipped.txt @@ -283,7 +283,6 @@ override Microsoft.ApplicationInsights.Metrics.MetricIdentifier.Equals(object ot override Microsoft.ApplicationInsights.Metrics.MetricIdentifier.GetHashCode() -> int override Microsoft.ApplicationInsights.Metrics.MetricIdentifier.ToString() -> string static Microsoft.ApplicationInsights.Extensibility.TelemetryConfiguration.CreateDefault() -> Microsoft.ApplicationInsights.Extensibility.TelemetryConfiguration -static Microsoft.ApplicationInsights.Extensibility.TelemetryConfiguration.CreateFromConfiguration(string config) -> Microsoft.ApplicationInsights.Extensibility.TelemetryConfiguration static Microsoft.ApplicationInsights.Metrics.MetricIdentifier.DefaultMetricNamespace.get -> string static Microsoft.ApplicationInsights.Metrics.MetricIdentifier.DefaultMetricNamespace.set -> void static Microsoft.ApplicationInsights.TelemetryClientExtensions.StartOperation(this Microsoft.ApplicationInsights.TelemetryClient telemetryClient, string operationName) -> Microsoft.ApplicationInsights.Extensibility.IOperationHolder diff --git a/BASE/Test/Microsoft.ApplicationInsights.Test/Microsoft.ApplicationInsights.Tests/TelemetryConfigurationOtelSdkDisabledTests.cs b/BASE/Test/Microsoft.ApplicationInsights.Test/Microsoft.ApplicationInsights.Tests/TelemetryConfigurationOtelSdkDisabledTests.cs new file mode 100644 index 000000000..1c66b8eb1 --- /dev/null +++ b/BASE/Test/Microsoft.ApplicationInsights.Test/Microsoft.ApplicationInsights.Tests/TelemetryConfigurationOtelSdkDisabledTests.cs @@ -0,0 +1,107 @@ +namespace Microsoft.ApplicationInsights.Tests +{ + using System; + using System.Collections.Generic; + using System.Diagnostics; + using System.Linq; + using Microsoft.ApplicationInsights.DataContracts; + using Microsoft.ApplicationInsights.Extensibility; + using Microsoft.Extensions.Logging; + using OpenTelemetry; + using OpenTelemetry.Logs; + using OpenTelemetry.Metrics; + using OpenTelemetry.Trace; + using Xunit; + + /// + /// Tests for DisableTelemetry property in TelemetryConfiguration. + /// Verifies that when DisableTelemetry is set to true, the OTEL_SDK_DISABLED environment variable + /// is set and telemetry data does not flow through the OpenTelemetry SDK. + /// + [Collection("TelemetryClientTests")] + public class TelemetryConfigurationOtelSdkDisabledTests + { + private const string OtelSdkDisabledEnvVar = "OTEL_SDK_DISABLED"; + + [Fact] + public void WhenDisableTelemetryIsTrue_TelemetryDataIsNotExported() + { + // Save original value to restore after test + var originalEnvValue = Environment.GetEnvironmentVariable(OtelSdkDisabledEnvVar); + + try + { + // Arrange + var logItems = new List(); + var activityItems = new List(); + var configuration = new TelemetryConfiguration(); + configuration.ConnectionString = "InstrumentationKey=" + Guid.NewGuid().ToString(); + configuration.DisableTelemetry = true; + configuration.ConfigureOpenTelemetryBuilder(b => b + .WithLogging(l => l.AddInMemoryExporter(logItems)) + .WithTracing(t => t.AddInMemoryExporter(activityItems))); + + var telemetryClient = new TelemetryClient(configuration); + + // Act + telemetryClient.TrackEvent("TestEvent"); + telemetryClient.TrackTrace("TestTrace"); + telemetryClient.TrackDependency("HTTP", "example.com", "GET /api", DateTimeOffset.Now, TimeSpan.FromMilliseconds(100), true); + telemetryClient.Flush(); + + // Assert - No data should be exported when telemetry is disabled + Assert.Empty(logItems); + Assert.Empty(activityItems); + Assert.Equal("true", Environment.GetEnvironmentVariable(OtelSdkDisabledEnvVar)); + + // Cleanup + configuration.Dispose(); + } + finally + { + // Restore original environment variable value + Environment.SetEnvironmentVariable(OtelSdkDisabledEnvVar, originalEnvValue); + } + } + + [Fact] + public void WhenDisableTelemetryIsFalse_TelemetryDataIsExported() + { + // Save original value to restore after test + var originalEnvValue = Environment.GetEnvironmentVariable(OtelSdkDisabledEnvVar); + + try + { + // Arrange + var logItems = new List(); + var configuration = new TelemetryConfiguration(); + configuration.SamplingRatio = 1.0f; + configuration.ConnectionString = "InstrumentationKey=" + Guid.NewGuid().ToString(); + configuration.DisableTelemetry = false; + configuration.ConfigureOpenTelemetryBuilder(b => b + .WithLogging(l => l.AddInMemoryExporter(logItems))); + + var telemetryClient = new TelemetryClient(configuration); + + // Act + telemetryClient.TrackEvent("TestEvent"); + telemetryClient.Flush(); + + // Assert - Data should be exported when telemetry is enabled + Assert.NotEmpty(logItems); + var logRecord = logItems.FirstOrDefault(l => + l.Attributes != null && l.Attributes.Any(a => + a.Key == "microsoft.custom_event.name" && a.Value?.ToString() == "TestEvent")); + Assert.NotNull(logRecord); + + // Cleanup + configuration.Dispose(); + } + finally + { + // Restore original environment variable value + Environment.SetEnvironmentVariable(OtelSdkDisabledEnvVar, originalEnvValue); + } + } + } +} diff --git a/BASE/src/Microsoft.ApplicationInsights/Extensibility/TelemetryConfiguration.cs b/BASE/src/Microsoft.ApplicationInsights/Extensibility/TelemetryConfiguration.cs index fc882f6f9..f7ee9bd79 100644 --- a/BASE/src/Microsoft.ApplicationInsights/Extensibility/TelemetryConfiguration.cs +++ b/BASE/src/Microsoft.ApplicationInsights/Extensibility/TelemetryConfiguration.cs @@ -226,22 +226,6 @@ public static TelemetryConfiguration CreateDefault() return DefaultInstance.Value; } - /// - /// Creates a new instance loaded from the specified configuration. - /// - /// An xml serialized configuration. - /// Throws if the config value is null or empty. - public static TelemetryConfiguration CreateFromConfiguration(string config) - { - if (string.IsNullOrWhiteSpace(config)) - { - throw new ArgumentNullException(nameof(config)); - } - - var configuration = new TelemetryConfiguration(); - return configuration; - } - /// /// Allows extending the OpenTelemetry builder configuration. /// @@ -365,6 +349,8 @@ internal OpenTelemetrySdk Build() return this.openTelemetrySdk; } + Environment.SetEnvironmentVariable("OTEL_SDK_DISABLED", this.disableTelemetry ? "true" : "false"); + this.openTelemetrySdk = OpenTelemetrySdk.Create(builder => { this.builderConfiguration(builder); diff --git a/BASE/src/Microsoft.ApplicationInsights/TelemetryClientExtensions.cs b/BASE/src/Microsoft.ApplicationInsights/TelemetryClientExtensions.cs index b56750862..b510a279d 100644 --- a/BASE/src/Microsoft.ApplicationInsights/TelemetryClientExtensions.cs +++ b/BASE/src/Microsoft.ApplicationInsights/TelemetryClientExtensions.cs @@ -224,7 +224,7 @@ public static IOperationHolder StartOperation( if (activity == null) { - throw new ArgumentNullException(nameof(activity)); + return null; } // if already started activity, we just link it — not create a new one diff --git a/CHANGELOG.md b/CHANGELOG.md index 2f6d948f5..2bf75e1e4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,7 +3,6 @@ ## Unreleased - [The JavaScript in `Microsoft.ApplicationInsights.AspNetCore` has been updated to version 10.](https://github.com/microsoft/ApplicationInsights-dotnet/pull/3078) - - [Centralized package version management](https://github.com/microsoft/ApplicationInsights-dotnet/pull/3080) - [Update OpenTelemetry dependencies](https://github.com/microsoft/ApplicationInsights-dotnet/pull/3080) - OpenTelemetry 1.15.0 @@ -12,7 +11,8 @@ - OpenTelemetry.Instrumentation.Http 1.15.0 - OpenTelemetry.Instrumentation.SqlClient 1.15.0-rc.1 - OpenTelemetry.Resources.Azure 1.15.0-beta.1 - +- [Added support to disable telemetry via TelemetryConfiguration.DisableTelemetry](https://github.com/microsoft/ApplicationInsights-dotnet/pull/3084) + ## Version 3.0.0-beta2 ### Added - [Automatic configuration binding from "ApplicationInsights" section in appsettings.json for both AspNetCore and WorkerService packages with configuration precedence: environment variables > explicit configuration > appsettings.json](https://github.com/microsoft/ApplicationInsights-dotnet/pull/3064) diff --git a/NETCORE/src/Microsoft.ApplicationInsights.AspNetCore/Extensions/ApplicationInsightsExtensions.cs b/NETCORE/src/Microsoft.ApplicationInsights.AspNetCore/Extensions/ApplicationInsightsExtensions.cs index fe737ad05..e82a25d9c 100644 --- a/NETCORE/src/Microsoft.ApplicationInsights.AspNetCore/Extensions/ApplicationInsightsExtensions.cs +++ b/NETCORE/src/Microsoft.ApplicationInsights.AspNetCore/Extensions/ApplicationInsightsExtensions.cs @@ -9,6 +9,7 @@ using Microsoft.ApplicationInsights.AspNetCore; using Microsoft.ApplicationInsights.AspNetCore.Extensibility.Implementation.Tracing; using Microsoft.ApplicationInsights.AspNetCore.Extensions; + using Microsoft.ApplicationInsights.Extensibility; using Microsoft.ApplicationInsights.Extensibility.Implementation.Tracing; using Microsoft.ApplicationInsights.Internals; using Microsoft.Extensions.Configuration; @@ -188,9 +189,15 @@ internal static IOpenTelemetryBuilder UseApplicationInsightsTelemetry(this IOpen // Configure Azure Monitor Exporter with connection string and sampling from ApplicationInsightsServiceOptions builder.Services.AddOptions() - .Configure>((exporterOptions, aiOptions) => + .Configure, TelemetryConfiguration, IConfiguration>((exporterOptions, aiOptions, telemetryConfig, config) => { var serviceOptions = aiOptions.Value; + + // Set OTEL_SDK_DISABLED in configuration if DisableTelemetry is true + if (telemetryConfig.DisableTelemetry) + { + config["OTEL_SDK_DISABLED"] = "true"; + } // Copy connection string to Azure Monitor Exporter if (!string.IsNullOrEmpty(serviceOptions.ConnectionString)) diff --git a/NETCORE/src/Microsoft.ApplicationInsights.WorkerService/ApplicationInsightsExtensions.cs b/NETCORE/src/Microsoft.ApplicationInsights.WorkerService/ApplicationInsightsExtensions.cs index 2455cb978..0389d7e84 100644 --- a/NETCORE/src/Microsoft.ApplicationInsights.WorkerService/ApplicationInsightsExtensions.cs +++ b/NETCORE/src/Microsoft.ApplicationInsights.WorkerService/ApplicationInsightsExtensions.cs @@ -6,6 +6,7 @@ using System.Reflection; using Azure.Monitor.OpenTelemetry.Exporter; using Microsoft.ApplicationInsights; + using Microsoft.ApplicationInsights.Extensibility; using Microsoft.ApplicationInsights.Extensibility.Implementation.Tracing; using Microsoft.ApplicationInsights.Internals; using Microsoft.ApplicationInsights.WorkerService; @@ -183,10 +184,16 @@ internal static IOpenTelemetryBuilder UseApplicationInsightsTelemetry(this IOpen // Configure Azure Monitor Exporter with connection string and sampling from ApplicationInsightsServiceOptions builder.Services.AddOptions() - .Configure>((exporterOptions, aiOptions) => + .Configure, TelemetryConfiguration, IConfiguration>((exporterOptions, aiOptions, telemetryConfig, config) => { var serviceOptions = aiOptions.Value; + // Set OTEL_SDK_DISABLED in configuration if DisableTelemetry is true + if (telemetryConfig.DisableTelemetry) + { + config["OTEL_SDK_DISABLED"] = "true"; + } + // Copy connection string to Azure Monitor Exporter if (!string.IsNullOrEmpty(serviceOptions.ConnectionString)) { diff --git a/NETCORE/src/Shared/Extensions/ApplicationInsightsExtensions.cs b/NETCORE/src/Shared/Extensions/ApplicationInsightsExtensions.cs index a3831e669..ea53b68fc 100644 --- a/NETCORE/src/Shared/Extensions/ApplicationInsightsExtensions.cs +++ b/NETCORE/src/Shared/Extensions/ApplicationInsightsExtensions.cs @@ -92,7 +92,8 @@ private static void AddTelemetryConfigAndClient(IServiceCollection services) { services.AddOptions(); - // Register TelemetryConfiguration as singleton with factory that creates it for DI scenarios + // Register TelemetryConfiguration singleton with factory that creates it for DI scenarios + // We use a factory to ensure skipDefaultBuilderConfiguration: true is passed services.AddSingleton(provider => { var options = provider.GetRequiredService>().Value; @@ -105,6 +106,19 @@ private static void AddTelemetryConfigAndClient(IServiceCollection services) { configuration.ConnectionString = options.ConnectionString; } + + // Apply any Configure callbacks + var configureOptions = provider.GetServices>(); + foreach (var configure in configureOptions) + { + configure.Configure(configuration); + } + + var postConfigureOptions = provider.GetServices>(); + foreach (var postConfigure in postConfigureOptions) + { + postConfigure.PostConfigure(Options.DefaultName, configuration); + } return configuration; }); diff --git a/docs/concepts.md b/docs/concepts.md index d921f5860..0919f29f6 100644 --- a/docs/concepts.md +++ b/docs/concepts.md @@ -58,3 +58,36 @@ Please review our full guide on [Telemetry correlation in Application Insights]( Sampling is the recommended way to reduce telemetry traffic, data costs, and storage costs, while preserving a statistically correct analysis of application data. Please review our full guide on [Sampling in Application Insights](https://docs.microsoft.com/azure/azure-monitor/app/sampling). + + +## Disabling Telemetry + +You can disable all telemetry collection using the `DisableTelemetry` property: + +```C# +var configuration = TelemetryConfiguration.CreateDefault(); +configuration.ConnectionString = "InstrumentationKey=00000000-0000-0000-0000-000000000000"; +configuration.DisableTelemetry = true; +var tc = new TelemetryClient(configuration); +``` + +When `DisableTelemetry` is set to `true`, the SDK internally sets the `OTEL_SDK_DISABLED` environment variable to `true` before building the OpenTelemetry SDK. This causes the OpenTelemetry SDK (version 1.15.0+) to return no-op implementations for all telemetry signals, preventing any telemetry data from being collected or exported. + +**Note:** The `DisableTelemetry` property must be set before the first `TelemetryClient` is created, as the OpenTelemetry SDK is built at that time. + +### Disabling Telemetry in DI Scenarios (ASP.NET Core / Worker Service) + +For dependency injection scenarios, you must configure `DisableTelemetry` **before** calling `AddApplicationInsightsTelemetry()`: + +```C# +public void ConfigureServices(IServiceCollection services) +{ + // Configure DisableTelemetry BEFORE AddApplicationInsightsTelemetry + services.Configure(tc => tc.DisableTelemetry = true); + + // Add and initialize the Application Insights SDK + services.AddApplicationInsightsTelemetry(); +} +``` + +This ensures the `OTEL_SDK_DISABLED` environment variable is set before the OpenTelemetry SDK is initialized. diff --git a/examples/AspNetCoreWebApp/Startup.cs b/examples/AspNetCoreWebApp/Startup.cs index d1f58b5c8..76876c379 100644 --- a/examples/AspNetCoreWebApp/Startup.cs +++ b/examples/AspNetCoreWebApp/Startup.cs @@ -1,5 +1,6 @@ namespace AspNetCoreWebApp { + using Microsoft.ApplicationInsights.Extensibility; using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Hosting; using Microsoft.Extensions.Configuration; @@ -26,6 +27,8 @@ public void ConfigureServices(IServiceCollection services) });*/ + // services.Configure(tc => tc.DisableTelemetry = true); + // Add and initialize the Application Insights SDK. services.AddApplicationInsightsTelemetry(); /*services.AddApplicationInsightsTelemetry(new ApplicationInsightsServiceOptions