diff --git a/sdk/monitor/Azure.Monitor.OpenTelemetry.AspNetCore/CHANGELOG.md b/sdk/monitor/Azure.Monitor.OpenTelemetry.AspNetCore/CHANGELOG.md index 5a95a693feb9..376b2a95bfb2 100644 --- a/sdk/monitor/Azure.Monitor.OpenTelemetry.AspNetCore/CHANGELOG.md +++ b/sdk/monitor/Azure.Monitor.OpenTelemetry.AspNetCore/CHANGELOG.md @@ -6,6 +6,28 @@ ### Breaking Changes +* **Default Sampler Changed**: The default sampling behavior has been changed from + `ApplicationInsightsSampler` with 100% sampling (all traces sampled) to + `RateLimitedSampler` with 5.0 traces per second. This change significantly + reduces telemetry volume for high-traffic applications and provides better + cost optimization out of the box. + **Impact**: Applications with more than 5 requests per second will see fewer + traces exported by default. + **Migration**: To maintain the previous behavior (100% sampling), explicitly + configure the sampler: + + ```csharp + // Option 1: Set SamplingRatio and clear TracesPerSecond + builder.Services.AddOpenTelemetry() + .UseAzureMonitor(options => + { + options.SamplingRatio = 1.0f; + options.TracesPerSecond = null; + }); + // Option 2: Use environment variables + // OTEL_TRACES_SAMPLER=microsoft.fixed_percentage + // OTEL_TRACES_SAMPLER_ARG=1.0 + ``` ### Bugs Fixed * Fixed an issue where Azure Container Apps instances were showing VM instance GUIDs diff --git a/sdk/monitor/Azure.Monitor.OpenTelemetry.AspNetCore/README.md b/sdk/monitor/Azure.Monitor.OpenTelemetry.AspNetCore/README.md index 07f046fbac09..135226c0ee25 100644 --- a/sdk/monitor/Azure.Monitor.OpenTelemetry.AspNetCore/README.md +++ b/sdk/monitor/Azure.Monitor.OpenTelemetry.AspNetCore/README.md @@ -130,17 +130,46 @@ Note that the `Credential` property is optional. If it is not set, Azure Monitor ### Advanced configuration -#### Customizing Sampling Percentage +#### Customizing Sampling Behavior -When using the Azure Monitor Distro, the sampling percentage for telemetry data is set to 100% (1.0F) by default. For example, let's say you want to set the sampling percentage to 50%. You can achieve this by modifying the code as follows: +The Azure Monitor Distro uses **rate-limited sampling** by default, collecting up to **5.0 traces per second**. This provides cost-effective telemetry collection for most applications while maintaining observability. +To customize the sampling behavior: + +**Option 1: Set the rate limited sampler to use a configured traces per second** +``` C# +builder.Services.AddOpenTelemetry().UseAzureMonitor(options => +{ + options.TracesPerSecond = 10.0; // Collect up to 10 traces per second +}); +``` + +**Option 2: Switch to percentage-based sampling** ``` C# builder.Services.AddOpenTelemetry().UseAzureMonitor(options => { - options.SamplingRatio = 0.5F; + options.SamplingRatio = 0.5F; // Sample 50% of traces + options.TracesPerSecond = null; // Disable rate-limited sampling }); ``` +**Option 3: Use environment variables** +For rate-limited sampling: + +``` +OTEL_TRACES_SAMPLER=microsoft.rate_limited +OTEL_TRACES_SAMPLER_ARG=10 +``` + +For percentage-based sampling: + +``` +OTEL_TRACES_SAMPLER=microsoft.fixed_percentage +OTEL_TRACES_SAMPLER_ARG=0.5 +``` + +**Note**: When both `TracesPerSecond` and `SamplingRatio` are configured, `TracesPerSecond` takes precedence. + #### Adding Custom ActivitySource to Traces ```C# diff --git a/sdk/monitor/Azure.Monitor.OpenTelemetry.AspNetCore/src/AzureMonitorOptions.cs b/sdk/monitor/Azure.Monitor.OpenTelemetry.AspNetCore/src/AzureMonitorOptions.cs index 8c12e41e59ae..e152e65924b2 100644 --- a/sdk/monitor/Azure.Monitor.OpenTelemetry.AspNetCore/src/AzureMonitorOptions.cs +++ b/sdk/monitor/Azure.Monitor.OpenTelemetry.AspNetCore/src/AzureMonitorOptions.cs @@ -61,7 +61,6 @@ public class AzureMonitorOptions : ClientOptions /// /// Gets or sets the ratio of telemetry items to be sampled. The value must be between 0.0F and 1.0F, inclusive. /// For example, specifying 0.4 means that 40% of traces are sampled and 60% are dropped. - /// The default value is 1.0F, indicating that all telemetry items are sampled. /// public float SamplingRatio { get; set; } = 1.0F; @@ -70,7 +69,7 @@ public class AzureMonitorOptions : ClientOptions /// For example, specifying 0.5 means one request every two seconds. /// When both TracesPerSecond and SamplingRatio are specified, TracesPerSecond takes precedence. /// - public double? TracesPerSecond { get; set; } + public double? TracesPerSecond { get; set; } = 5.0; /// /// Override the default directory for offline storage. diff --git a/sdk/monitor/Azure.Monitor.OpenTelemetry.AspNetCore/src/DefaultAzureMonitorOptions.cs b/sdk/monitor/Azure.Monitor.OpenTelemetry.AspNetCore/src/DefaultAzureMonitorOptions.cs index 9a54cf8ae102..0e32c9243a20 100644 --- a/sdk/monitor/Azure.Monitor.OpenTelemetry.AspNetCore/src/DefaultAzureMonitorOptions.cs +++ b/sdk/monitor/Azure.Monitor.OpenTelemetry.AspNetCore/src/DefaultAzureMonitorOptions.cs @@ -28,7 +28,13 @@ public void Configure(AzureMonitorOptions options) { if (_configuration != null) { - _configuration.GetSection(AzureMonitorSectionFromConfig).Bind(options); + var azureMonitorSection = _configuration.GetSection(AzureMonitorSectionFromConfig); + azureMonitorSection.Bind(options); + if (azureMonitorSection["TracesPerSecond"] == null && azureMonitorSection["SamplingRatio"] != null) + { + // This is so user does not have to explicitly set TracesPerSecond to null in config to use fixed-percentage sampling. + options.TracesPerSecond = null; + } // IConfiguration can read from EnvironmentVariables or InMemoryCollection if configured to do so. var connectionStringFromIConfig = _configuration[EnvironmentVariableConstants.APPLICATIONINSIGHTS_CONNECTION_STRING]; @@ -97,6 +103,7 @@ private static void ConfigureSamplingOptions(string? samplerType, string? sample if (ratio >= 0.0 && ratio <= 1.0) { options.SamplingRatio = (float)ratio; + options.TracesPerSecond = null; // Explicitly set to null to use fixed-percentage sampling } else { diff --git a/sdk/monitor/Azure.Monitor.OpenTelemetry.AspNetCore/tests/Azure.Monitor.OpenTelemetry.AspNetCore.Tests/DefaultAzureMonitorOptionsSamplerTests.cs b/sdk/monitor/Azure.Monitor.OpenTelemetry.AspNetCore/tests/Azure.Monitor.OpenTelemetry.AspNetCore.Tests/DefaultAzureMonitorOptionsSamplerTests.cs index 77e862ca7806..1b81402cf263 100644 --- a/sdk/monitor/Azure.Monitor.OpenTelemetry.AspNetCore/tests/Azure.Monitor.OpenTelemetry.AspNetCore.Tests/DefaultAzureMonitorOptionsSamplerTests.cs +++ b/sdk/monitor/Azure.Monitor.OpenTelemetry.AspNetCore/tests/Azure.Monitor.OpenTelemetry.AspNetCore.Tests/DefaultAzureMonitorOptionsSamplerTests.cs @@ -60,8 +60,8 @@ public void Configure_Sampler_InvalidArgs_Ignored() var configurator = new DefaultAzureMonitorOptions(configuration); var options = new AzureMonitorOptions(); configurator.Configure(options); - Assert.Equal(1.0f, options.SamplingRatio); // default - Assert.Null(options.TracesPerSecond); + Assert.Equal(1.0f, options.SamplingRatio); + Assert.Equal(5.0, options.TracesPerSecond); // default value retained // invalid negative rate configValues = new List> @@ -73,7 +73,7 @@ public void Configure_Sampler_InvalidArgs_Ignored() configurator = new DefaultAzureMonitorOptions(configuration); options = new AzureMonitorOptions(); configurator.Configure(options); - Assert.Null(options.TracesPerSecond); + Assert.Equal(5.0, options.TracesPerSecond); // default value retained Assert.Equal(1.0f, options.SamplingRatio); } diff --git a/sdk/monitor/Azure.Monitor.OpenTelemetry.AspNetCore/tests/Azure.Monitor.OpenTelemetry.AspNetCore.Tests/DefaultAzureMonitorOptionsTests.cs b/sdk/monitor/Azure.Monitor.OpenTelemetry.AspNetCore/tests/Azure.Monitor.OpenTelemetry.AspNetCore.Tests/DefaultAzureMonitorOptionsTests.cs index 5240d5b9d5e2..a71ec3547a61 100644 --- a/sdk/monitor/Azure.Monitor.OpenTelemetry.AspNetCore/tests/Azure.Monitor.OpenTelemetry.AspNetCore.Tests/DefaultAzureMonitorOptionsTests.cs +++ b/sdk/monitor/Azure.Monitor.OpenTelemetry.AspNetCore/tests/Azure.Monitor.OpenTelemetry.AspNetCore.Tests/DefaultAzureMonitorOptionsTests.cs @@ -28,6 +28,7 @@ public void VerifyConfigure_Default() Assert.Null(azureMonitorOptions.ConnectionString); Assert.False(azureMonitorOptions.DisableOfflineStorage); Assert.Equal(1.0F, azureMonitorOptions.SamplingRatio); + Assert.Equal(5.0, azureMonitorOptions.TracesPerSecond); Assert.Null(azureMonitorOptions.StorageDirectory); Assert.True(azureMonitorOptions.EnableTraceBasedLogsSampler); } @@ -57,6 +58,35 @@ public void VerifyConfigure_ViaJson() Assert.Equal("testJsonValue", azureMonitorOptions.ConnectionString); Assert.True(azureMonitorOptions.DisableOfflineStorage); Assert.Equal(0.5F, azureMonitorOptions.SamplingRatio); + Assert.Null(azureMonitorOptions.TracesPerSecond); + Assert.Equal("testJsonValue", azureMonitorOptions.StorageDirectory); + } + + [Fact] + public void VerifyConfigure_TracesPerSecond_ViaJson() + { + var appSettings = @"{""AzureMonitor"":{ + ""ConnectionString"" : ""testJsonValue"", + ""DisableOfflineStorage"" : ""true"", + ""TracesPerSecond"" : 6.0, + ""StorageDirectory"" : ""testJsonValue"", + ""EnableTraceBasedLogsSampler"" : ""true"" + }}"; + + var configuration = new ConfigurationBuilder() + .AddJsonStream(new MemoryStream(Encoding.UTF8.GetBytes(appSettings))) + .Build(); + + var defaultAzureMonitorOptions = new DefaultAzureMonitorOptions(configuration); + + var azureMonitorOptions = new AzureMonitorOptions(); + + defaultAzureMonitorOptions.Configure(azureMonitorOptions); + + Assert.Equal("testJsonValue", azureMonitorOptions.ConnectionString); + Assert.True(azureMonitorOptions.DisableOfflineStorage); + Assert.Equal(1.0F, azureMonitorOptions.SamplingRatio); + Assert.Equal(6.0, azureMonitorOptions.TracesPerSecond); Assert.Equal("testJsonValue", azureMonitorOptions.StorageDirectory); } diff --git a/sdk/monitor/Azure.Monitor.OpenTelemetry.AspNetCore/tests/Azure.Monitor.OpenTelemetry.AspNetCore.Tests/E2ETests/AspNetCoreInstrumentationTests.cs b/sdk/monitor/Azure.Monitor.OpenTelemetry.AspNetCore/tests/Azure.Monitor.OpenTelemetry.AspNetCore.Tests/E2ETests/AspNetCoreInstrumentationTests.cs index b5301b7ab0bc..ce125ac1f085 100644 --- a/sdk/monitor/Azure.Monitor.OpenTelemetry.AspNetCore/tests/Azure.Monitor.OpenTelemetry.AspNetCore.Tests/E2ETests/AspNetCoreInstrumentationTests.cs +++ b/sdk/monitor/Azure.Monitor.OpenTelemetry.AspNetCore/tests/Azure.Monitor.OpenTelemetry.AspNetCore.Tests/E2ETests/AspNetCoreInstrumentationTests.cs @@ -71,6 +71,8 @@ public void AspNetCoreRequestsAreCapturedCorrectly(string path, string? queryStr { x.EnableLiveMetrics = false; x.ConnectionString = testConnectionString; + x.SamplingRatio = 1.0f; // Ensure 100% sampling for tests + x.TracesPerSecond = null; // Disable rate limited sampler }) .WithTracing(x => x.AddInMemoryExporter(activities)) // Custom resources must be added AFTER AzureMonitor to override the included ResourceDetectors. diff --git a/sdk/monitor/Azure.Monitor.OpenTelemetry.AspNetCore/tests/Azure.Monitor.OpenTelemetry.AspNetCore.Tests/E2ETests/HttpClientInstrumentationTests.cs b/sdk/monitor/Azure.Monitor.OpenTelemetry.AspNetCore/tests/Azure.Monitor.OpenTelemetry.AspNetCore.Tests/E2ETests/HttpClientInstrumentationTests.cs index bc3d74547fbc..077a18d9c9dd 100644 --- a/sdk/monitor/Azure.Monitor.OpenTelemetry.AspNetCore/tests/Azure.Monitor.OpenTelemetry.AspNetCore.Tests/E2ETests/HttpClientInstrumentationTests.cs +++ b/sdk/monitor/Azure.Monitor.OpenTelemetry.AspNetCore/tests/Azure.Monitor.OpenTelemetry.AspNetCore.Tests/E2ETests/HttpClientInstrumentationTests.cs @@ -58,7 +58,12 @@ public async Task HttpRequestsAreCapturedCorrectly(string? queryString, int expe var serviceCollection = new ServiceCollection(); serviceCollection.AddOpenTelemetry() - .UseAzureMonitor(x => x.ConnectionString = testConnectionString) + .UseAzureMonitor(x => + { + x.ConnectionString = testConnectionString; + x.SamplingRatio = 1.0f; // Ensure 100% sampling for tests + x.TracesPerSecond = null; // Disable rate limited sampler + }) .WithTracing(x => x.AddInMemoryExporter(activities)) // Custom resources must be added AFTER AzureMonitor to override the included ResourceDetectors. .ConfigureResource(x => x.AddAttributes(SharedTestVars.TestResourceAttributes)); diff --git a/sdk/monitor/Azure.Monitor.OpenTelemetry.AspNetCore/tests/Azure.Monitor.OpenTelemetry.AspNetCore.Tests/E2ETests/SqlClientInstrumentationTests.cs b/sdk/monitor/Azure.Monitor.OpenTelemetry.AspNetCore/tests/Azure.Monitor.OpenTelemetry.AspNetCore.Tests/E2ETests/SqlClientInstrumentationTests.cs index 21cb43fff8aa..8cc2dc0d346d 100644 --- a/sdk/monitor/Azure.Monitor.OpenTelemetry.AspNetCore/tests/Azure.Monitor.OpenTelemetry.AspNetCore.Tests/E2ETests/SqlClientInstrumentationTests.cs +++ b/sdk/monitor/Azure.Monitor.OpenTelemetry.AspNetCore/tests/Azure.Monitor.OpenTelemetry.AspNetCore.Tests/E2ETests/SqlClientInstrumentationTests.cs @@ -72,7 +72,12 @@ public async Task SqlClientErrorsAreCollectedSuccessfully( var activities = new List(); var serviceCollection = new ServiceCollection(); serviceCollection.AddOpenTelemetry() - .UseAzureMonitor(x => x.ConnectionString = testConnectionString) + .UseAzureMonitor(x => + { + x.ConnectionString = testConnectionString; + x.SamplingRatio = 1.0f; // Ensure 100% sampling for tests + x.TracesPerSecond = null; // Disable rate limiting sampler + }) .WithTracing(x => x.AddInMemoryExporter(activities)) // Custom resources must be added AFTER AzureMonitor to override the included ResourceDetectors. .ConfigureResource(x => x.AddAttributes(SharedTestVars.TestResourceAttributes)); @@ -172,7 +177,12 @@ public async Task SqlClientCallsAreCollectedSuccessfully( var activities = new List(); var serviceCollection = new ServiceCollection(); serviceCollection.AddOpenTelemetry() - .UseAzureMonitor(x => x.ConnectionString = testConnectionString) + .UseAzureMonitor(x => + { + x.ConnectionString = testConnectionString; + x.SamplingRatio = 1.0f; // Ensure 100% sampling for tests + x.TracesPerSecond = null; // Disable rate limited sampler + }) .WithTracing(x => x.AddInMemoryExporter(activities)) // Custom resources must be added AFTER AzureMonitor to override the included ResourceDetectors. .ConfigureResource(x => x.AddAttributes(SharedTestVars.TestResourceAttributes)); diff --git a/sdk/monitor/Azure.Monitor.OpenTelemetry.AspNetCore/tests/Azure.Monitor.OpenTelemetry.AspNetCore.Tests/InitializationTests.cs b/sdk/monitor/Azure.Monitor.OpenTelemetry.AspNetCore/tests/Azure.Monitor.OpenTelemetry.AspNetCore.Tests/InitializationTests.cs index f8e969532cbd..fce82c6ee220 100644 --- a/sdk/monitor/Azure.Monitor.OpenTelemetry.AspNetCore/tests/Azure.Monitor.OpenTelemetry.AspNetCore.Tests/InitializationTests.cs +++ b/sdk/monitor/Azure.Monitor.OpenTelemetry.AspNetCore/tests/Azure.Monitor.OpenTelemetry.AspNetCore.Tests/InitializationTests.cs @@ -300,7 +300,8 @@ public static void EvaluateTracerProvider(IServiceProvider serviceProvider, bool Assert.Equal(expectedProfilingSessionTraceProcessor, variables.foundProfilingSessionTraceProcessor); // Validate Sampler - EvaluationHelper.EvaluateTracerProviderSampler(serviceProvider, false); + // The default TracesPerSecond is 5.0, so we expect RateLimitedSampler by default + EvaluationHelper.EvaluateTracerProviderSampler(serviceProvider, true); // Validate Instrumentations var instrumentationsProperty = tracerProvider.GetType().GetProperty("Instrumentations", BindingFlags.NonPublic | BindingFlags.Instance); diff --git a/sdk/monitor/Azure.Monitor.OpenTelemetry.Exporter/CHANGELOG.md b/sdk/monitor/Azure.Monitor.OpenTelemetry.Exporter/CHANGELOG.md index 119befa383e6..0e9c365b9ef3 100644 --- a/sdk/monitor/Azure.Monitor.OpenTelemetry.Exporter/CHANGELOG.md +++ b/sdk/monitor/Azure.Monitor.OpenTelemetry.Exporter/CHANGELOG.md @@ -5,6 +5,27 @@ ### Features Added ### Breaking Changes +* **Default Sampler Changed**: The default sampling behavior has been changed from + `ApplicationInsightsSampler` with 100% sampling (all traces sampled) to + `RateLimitedSampler` with 5.0 traces per second. This change significantly + reduces telemetry volume for high-traffic applications and provides better + cost optimization out of the box. + **Impact**: Applications with more than 5 requests per second will see fewer + traces exported by default. + **Migration**: To maintain the previous behavior (100% sampling), explicitly + configure the sampler: + ```csharp + // Option 1: Set SamplingRatio and clear TracesPerSecond + builder.Services.AddOpenTelemetry() + .UseAzureMonitorExporter(options => + { + options.SamplingRatio = 1.0f; + options.TracesPerSecond = null; + }); + // Option 2: Use environment variables + // OTEL_TRACES_SAMPLER=microsoft.fixed_percentage + // OTEL_TRACES_SAMPLER_ARG=1.0 + ``` ### Bugs Fixed diff --git a/sdk/monitor/Azure.Monitor.OpenTelemetry.Exporter/README.md b/sdk/monitor/Azure.Monitor.OpenTelemetry.Exporter/README.md index eabf983c16ed..6464b15bfc34 100644 --- a/sdk/monitor/Azure.Monitor.OpenTelemetry.Exporter/README.md +++ b/sdk/monitor/Azure.Monitor.OpenTelemetry.Exporter/README.md @@ -150,6 +150,38 @@ Some key concepts for OpenTelemetry include: - [Sampling](https://github.com/open-telemetry/opentelemetry-specification/blob/master/specification/trace/sdk.md#sampling): Sampling is a mechanism to control the noise and overhead introduced by OpenTelemetry by reducing the number of samples of traces collected and sent to the backend. + **Default Sampling**: By default, the Azure Monitor Exporter uses `RateLimitedSampler` with a rate of 5.0 traces per second. This provides cost-effective telemetry collection for most applications. To change the sampling behavior: + + - **For rate-limited sampling**: Set `TracesPerSecond` in `AzureMonitorExporterOptions`: + + ```csharp + .AddAzureMonitorTraceExporter(options => { + options.TracesPerSecond = 10.0; // Sample 10 traces per second + }) + ``` + + - **For percentage-based sampling**: Set `SamplingRatio` and clear `TracesPerSecond`: + + ```csharp + .AddAzureMonitorTraceExporter(options => { + options.SamplingRatio = 0.5f; // Sample 50% of traces + options.TracesPerSecond = null; + }) + ``` + + - **Via environment variables**: + + ``` + OTEL_TRACES_SAMPLER=microsoft.rate_limited + OTEL_TRACES_SAMPLER_ARG=10 + ``` + + or + + ``` + OTEL_TRACES_SAMPLER=microsoft.fixed_percentage + OTEL_TRACES_SAMPLER_ARG=0.5 + ``` - [Metric Signal](https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/overview.md#metric-signal): OpenTelemetry allows to record raw measurements or metrics with predefined aggregation and a set of attributes (dimensions). diff --git a/sdk/monitor/Azure.Monitor.OpenTelemetry.Exporter/src/AzureMonitorExporterOptions.cs b/sdk/monitor/Azure.Monitor.OpenTelemetry.Exporter/src/AzureMonitorExporterOptions.cs index 5e8907575b19..935539a6ab61 100644 --- a/sdk/monitor/Azure.Monitor.OpenTelemetry.Exporter/src/AzureMonitorExporterOptions.cs +++ b/sdk/monitor/Azure.Monitor.OpenTelemetry.Exporter/src/AzureMonitorExporterOptions.cs @@ -36,7 +36,6 @@ public class AzureMonitorExporterOptions : ClientOptions /// /// Gets or sets the ratio of telemetry items to be sampled. The value must be between 0.0F and 1.0F, inclusive. /// For example, specifying 0.4 means that 40% of traces are sampled and 60% are dropped. - /// The default value is 1.0F, indicating that all telemetry items are sampled. /// public float SamplingRatio { get; set; } = 1.0F; @@ -45,7 +44,7 @@ public class AzureMonitorExporterOptions : ClientOptions /// For example, specifying 0.5 means one request every two seconds. /// When both TracesPerSecond and SamplingRatio are specified, TracesPerSecond takes precedence. /// - public double? TracesPerSecond { get; set; } + public double? TracesPerSecond { get; set; } = 5.0; /// /// The of the Azure Monitor ingestion API. diff --git a/sdk/monitor/Azure.Monitor.OpenTelemetry.Exporter/src/DefaultAzureMonitorExporterOptions.cs b/sdk/monitor/Azure.Monitor.OpenTelemetry.Exporter/src/DefaultAzureMonitorExporterOptions.cs index 8d2090868430..8165102f34a3 100644 --- a/sdk/monitor/Azure.Monitor.OpenTelemetry.Exporter/src/DefaultAzureMonitorExporterOptions.cs +++ b/sdk/monitor/Azure.Monitor.OpenTelemetry.Exporter/src/DefaultAzureMonitorExporterOptions.cs @@ -31,7 +31,13 @@ public void Configure(AzureMonitorExporterOptions options) { if (_configuration != null) { - BindIConfigurationOptions(_configuration, options); + var azureMonitorSection = _configuration.GetSection(AzureMonitorExporterSectionFromConfig); + azureMonitorSection.Bind(options); + if (azureMonitorSection["TracesPerSecond"] == null && azureMonitorSection["SamplingRatio"] != null) + { + // This is so user does not have to explicitly set TracesPerSecond to null in config to use fixed-percentage sampling. + options.TracesPerSecond = null; + } // IConfiguration can read from EnvironmentVariables or InMemoryCollection if configured to do so. var connectionStringFromIConfig = _configuration[EnvironmentVariableConstants.APPLICATIONINSIGHTS_CONNECTION_STRING]; @@ -100,6 +106,7 @@ private static void ConfigureSamplingOptions(string? samplerType, string? sample if (ratio >= 0.0 && ratio <= 1.0) { options.SamplingRatio = (float)ratio; + options.TracesPerSecond = null; // Explicitly set to null to use fixed-percentage sampling } else { diff --git a/sdk/monitor/Azure.Monitor.OpenTelemetry.Exporter/tests/Azure.Monitor.OpenTelemetry.Exporter.Tests/DefaultAzureMonitorExporterOptionsTests.cs b/sdk/monitor/Azure.Monitor.OpenTelemetry.Exporter/tests/Azure.Monitor.OpenTelemetry.Exporter.Tests/DefaultAzureMonitorExporterOptionsTests.cs index 4ccc2bd238db..dfcb30acf94d 100644 --- a/sdk/monitor/Azure.Monitor.OpenTelemetry.Exporter/tests/Azure.Monitor.OpenTelemetry.Exporter.Tests/DefaultAzureMonitorExporterOptionsTests.cs +++ b/sdk/monitor/Azure.Monitor.OpenTelemetry.Exporter/tests/Azure.Monitor.OpenTelemetry.Exporter.Tests/DefaultAzureMonitorExporterOptionsTests.cs @@ -110,7 +110,7 @@ public void Configure_Sampler_InvalidArgs_Ignored() // Assert - defaults unchanged Assert.Equal(1.0f, options.SamplingRatio); - Assert.Null(options.TracesPerSecond); + Assert.Equal(5.0, options.TracesPerSecond); // Default value retained // Now test invalid negative rate_limited configValues = new List> @@ -126,7 +126,7 @@ public void Configure_Sampler_InvalidArgs_Ignored() defaultConfigurator.Configure(options); // Assert - Assert.Null(options.TracesPerSecond); + Assert.Equal(5.0, options.TracesPerSecond); // Default value retained Assert.Equal(1.0f, options.SamplingRatio); } finally @@ -203,5 +203,55 @@ public void Configure_Sampler_EnvironmentVariable_Overrides_IConfiguration() Environment.SetEnvironmentVariable("OTEL_TRACES_SAMPLER_ARG", prevSamplerArg); } } + + [Fact] + public void Configure_SamplingRatio_ViaJson_WithoutTracesPerSecond_SetsTracesPerSecondToNull_And_SetsSamplingRatio() + { + // Arrange - simulate appsettings.json with only SamplingRatio set (using InMemoryCollection with section key format) + var configValues = new List> + { + new("AzureMonitorExporter:ConnectionString", "testConnectionString"), + new("AzureMonitorExporter:SamplingRatio", "0.5"), + }; + + var configuration = new ConfigurationBuilder() + .AddInMemoryCollection(configValues) + .Build(); + + var defaultConfigurator = new DefaultAzureMonitorExporterOptions(configuration); + var options = new AzureMonitorExporterOptions(); + + // Act + defaultConfigurator.Configure(options); + + // Assert + Assert.Equal(0.5f, options.SamplingRatio); + Assert.Null(options.TracesPerSecond); + } + + [Fact] + public void Configure_TracesPerSecond_ViaJson_WithoutSamplingRatio_SetsTracesPerSecond() + { + // Arrange - simulate appsettings.json with only TracesPerSecond set (using InMemoryCollection with section key format) + var configValues = new List> + { + new("AzureMonitorExporter:ConnectionString", "testConnectionString"), + new("AzureMonitorExporter:TracesPerSecond", "10"), + }; + + var configuration = new ConfigurationBuilder() + .AddInMemoryCollection(configValues) + .Build(); + + var defaultConfigurator = new DefaultAzureMonitorExporterOptions(configuration); + var options = new AzureMonitorExporterOptions(); + + // Act + defaultConfigurator.Configure(options); + + // Assert + Assert.Equal(1.0f, options.SamplingRatio); // default unchanged + Assert.Equal(10d, options.TracesPerSecond); + } } }