Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,11 @@

### Features Added

* Added `EnableTraceBasedLogSampler` property to `AzureMonitorOptions` to enable
filtering logs based on trace sampling decisions, reducing log volume while
maintaining trace-log correlation.
([#53441](https://github.com/Azure/azure-sdk-for-net/pull/53441))

### Breaking Changes

### Bugs Fixed
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ public AzureMonitorOptions() { }
public Azure.Core.TokenCredential Credential { get { throw null; } set { } }
public bool DisableOfflineStorage { get { throw null; } set { } }
public bool EnableLiveMetrics { get { throw null; } set { } }
public bool EnableTraceBasedLogsSampler { get { throw null; } set { } }
public float SamplingRatio { get { throw null; } set { } }
public string StorageDirectory { get { throw null; } set { } }
public double? TracesPerSecond { get { throw null; } set { } }
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ public AzureMonitorOptions() { }
public Azure.Core.TokenCredential Credential { get { throw null; } set { } }
public bool DisableOfflineStorage { get { throw null; } set { } }
public bool EnableLiveMetrics { get { throw null; } set { } }
public bool EnableTraceBasedLogsSampler { get { throw null; } set { } }
public float SamplingRatio { get { throw null; } set { } }
public string StorageDirectory { get { throw null; } set { } }
public double? TracesPerSecond { get { throw null; } set { } }
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
<Project Sdk="Microsoft.NET.Sdk">
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<Description>An OpenTelemetry .NET distro that exports to Azure Monitor</Description>
<AssemblyTitle>AzureMonitor OpenTelemetry ASP.NET Core Distro</AssemblyTitle>
Expand All @@ -24,10 +24,10 @@
<!-- Depending on monthly deliverables, we may switch between PackageReference or ProjectReference. Keeping both here to make the switch easier. -->

<!-- FOR PUBLIC RELEASES, MUST USE PackageReference. THIS REQUIRES A STAGGERED RELEASE IF SHIPPING A NEW EXPORTER. -->
<PackageReference Include="Azure.Monitor.OpenTelemetry.Exporter" VersionOverride="1.5.0-beta.1" />
<!--<PackageReference Include="Azure.Monitor.OpenTelemetry.Exporter" VersionOverride="1.5.0-beta.1" />-->

<!-- FOR LOCAL DEV, ProjectReference IS PREFERRED. -->
<!--<ProjectReference Include="..\..\Azure.Monitor.OpenTelemetry.Exporter\src\Azure.Monitor.OpenTelemetry.Exporter.csproj" />-->
<ProjectReference Include="..\..\Azure.Monitor.OpenTelemetry.Exporter\src\Azure.Monitor.OpenTelemetry.Exporter.csproj" />
</ItemGroup>

<!-- Shared sources from Azure.Core -->
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,16 @@ public class AzureMonitorOptions : ClientOptions
/// </summary>
public bool EnableLiveMetrics { get; set; } = true;

/// <summary>
/// Enables or disables filtering logs based on trace sampling decisions.
/// </summary>
/// <remarks>
/// When enabled, only logs associated with sampled traces are exported.
/// Logs without trace context are always exported.
/// This reduces log volume while maintaining trace-log correlation.
/// </remarks>
public bool EnableTraceBasedLogsSampler { get; set; } = true;

/// <summary>
/// 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.
Expand Down Expand Up @@ -86,27 +96,13 @@ internal void SetValueToExporterOptions(AzureMonitorExporterOptions exporterOpti
exporterOptions.TracesPerSecond = TracesPerSecond;
exporterOptions.StorageDirectory = StorageDirectory;
exporterOptions.EnableLiveMetrics = EnableLiveMetrics;
exporterOptions.EnableTraceBasedLogsSampler = EnableTraceBasedLogsSampler;
if (Transport != null)
{
exporterOptions.Transport = Transport;
}
exporterOptions.Diagnostics.IsDistributedTracingEnabled = Diagnostics.IsDistributedTracingEnabled;
exporterOptions.Diagnostics.IsLoggingEnabled = Diagnostics.IsLoggingEnabled;
}

//internal void SetValueToLiveMetricsOptions(AzureMonitorLiveMetricsOptions liveMetricsOptions)
//{
// liveMetricsOptions.ConnectionString = ConnectionString;
// liveMetricsOptions.Credential = Credential;
// liveMetricsOptions.EnableLiveMetrics = EnableLiveMetrics;

// if (Transport != null)
// {
// liveMetricsOptions.Transport = Transport;
// }

// liveMetricsOptions.Diagnostics.IsDistributedTracingEnabled = Diagnostics.IsDistributedTracingEnabled;
// liveMetricsOptions.Diagnostics.IsLoggingEnabled = Diagnostics.IsLoggingEnabled;
//}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,174 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.

using System;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Options;
using Azure.Monitor.OpenTelemetry.Exporter;
using Xunit;

namespace Azure.Monitor.OpenTelemetry.AspNetCore.Tests
{
public class AzureMonitorOptionsTests
{
private const string TestConnectionString = "InstrumentationKey=00000000-0000-0000-0000-000000000000";

[Fact]
public void AzureMonitorOptions_EnableTraceBasedLogsSampler_DefaultValue_IsTrue()
{
// Arrange & Act
var options = new AzureMonitorOptions();

// Assert
Assert.True(options.EnableTraceBasedLogsSampler);
}

[Fact]
public void AzureMonitorOptions_EnableTraceBasedLogsSampler_CanBeDisabled()
{
// Arrange & Act
var options = new AzureMonitorOptions
{
EnableTraceBasedLogsSampler = false
};

// Assert
Assert.False(options.EnableTraceBasedLogsSampler);
}

[Fact]
public void AzureMonitorOptions_SetValueToExporterOptions_CopiesEnableTraceBasedLogsSampler()
{
// Arrange
var azureMonitorOptions = new AzureMonitorOptions
{
ConnectionString = TestConnectionString,
EnableTraceBasedLogsSampler = false
};

var exporterOptions = new AzureMonitorExporterOptions();

// Act
azureMonitorOptions.SetValueToExporterOptions(exporterOptions);

// Assert
Assert.False(exporterOptions.EnableTraceBasedLogsSampler);
Assert.Equal(TestConnectionString, exporterOptions.ConnectionString);
}

[Fact]
public void UseAzureMonitor_DefaultEnableTraceBasedLogsSampler()
{
// Arrange
var serviceCollection = new ServiceCollection();

// Act
serviceCollection.AddOpenTelemetry()
.UseAzureMonitor(options =>
{
options.ConnectionString = TestConnectionString;
options.DisableOfflineStorage = true;
});

var serviceProvider = serviceCollection.BuildServiceProvider();

// Assert
var azureMonitorOptions = serviceProvider.GetRequiredService<IOptionsMonitor<AzureMonitorOptions>>()
.Get(Options.DefaultName);

Assert.True(azureMonitorOptions.EnableTraceBasedLogsSampler);

var exporterOptions = serviceProvider.GetRequiredService<IOptionsMonitor<AzureMonitorExporterOptions>>()
.Get(Options.DefaultName);

Assert.True(exporterOptions.EnableTraceBasedLogsSampler);
}

[Fact]
public void UseAzureMonitor_CanDisableTraceBasedLogsSampler()
{
// Arrange
var serviceCollection = new ServiceCollection();

// Act
serviceCollection.AddOpenTelemetry()
.UseAzureMonitor(options =>
{
options.ConnectionString = TestConnectionString;
options.EnableTraceBasedLogsSampler = false;
options.DisableOfflineStorage = true;
});

var serviceProvider = serviceCollection.BuildServiceProvider();

// Assert
var azureMonitorOptions = serviceProvider.GetRequiredService<IOptionsMonitor<AzureMonitorOptions>>()
.Get(Options.DefaultName);

Assert.False(azureMonitorOptions.EnableTraceBasedLogsSampler);

var exporterOptions = serviceProvider.GetRequiredService<IOptionsMonitor<AzureMonitorExporterOptions>>()
.Get(Options.DefaultName);

Assert.False(exporterOptions.EnableTraceBasedLogsSampler);
}

[Fact]
public void UseAzureMonitor_EnableTraceBasedLogsSampler_PropagatesCorrectly()
{
// Arrange
var serviceCollection = new ServiceCollection();

// Act - Test with true
serviceCollection.AddOpenTelemetry()
.UseAzureMonitor(options =>
{
options.ConnectionString = TestConnectionString;
options.EnableTraceBasedLogsSampler = true;
options.DisableOfflineStorage = true;
});

var serviceProvider = serviceCollection.BuildServiceProvider();

// Assert
var azureMonitorOptions = serviceProvider.GetRequiredService<IOptionsMonitor<AzureMonitorOptions>>()
.Get(Options.DefaultName);

var exporterOptions = serviceProvider.GetRequiredService<IOptionsMonitor<AzureMonitorExporterOptions>>()
.Get(Options.DefaultName);

Assert.Equal(azureMonitorOptions.EnableTraceBasedLogsSampler, exporterOptions.EnableTraceBasedLogsSampler);
Assert.True(exporterOptions.EnableTraceBasedLogsSampler);
}

[Fact]
public void UseAzureMonitor_WithoutConfiguration_UsesDefaultValue()
{
// Arrange
var serviceCollection = new ServiceCollection();

// Act
serviceCollection.AddOpenTelemetry()
.UseAzureMonitor(options =>
{
options.ConnectionString = TestConnectionString;
options.DisableOfflineStorage = true;

// Not setting EnableTraceBasedLogsSampler, should use default
});

var serviceProvider = serviceCollection.BuildServiceProvider();

// Assert
var azureMonitorOptions = serviceProvider.GetRequiredService<IOptionsMonitor<AzureMonitorOptions>>()
.Get(Options.DefaultName);

var exporterOptions = serviceProvider.GetRequiredService<IOptionsMonitor<AzureMonitorExporterOptions>>()
.Get(Options.DefaultName);

// Both should be true (default value)
Assert.True(azureMonitorOptions.EnableTraceBasedLogsSampler);
Assert.True(exporterOptions.EnableTraceBasedLogsSampler);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ public void VerifyConfigure_Default()
Assert.False(azureMonitorOptions.DisableOfflineStorage);
Assert.Equal(1.0F, azureMonitorOptions.SamplingRatio);
Assert.Null(azureMonitorOptions.StorageDirectory);
Assert.True(azureMonitorOptions.EnableTraceBasedLogsSampler);
}

#if NET
Expand All @@ -39,7 +40,8 @@ public void VerifyConfigure_ViaJson()
""ConnectionString"" : ""testJsonValue"",
""DisableOfflineStorage"" : ""true"",
""SamplingRatio"" : 0.5,
""StorageDirectory"" : ""testJsonValue""
""StorageDirectory"" : ""testJsonValue"",
""EnableTraceBasedLogsSampler"" : ""true""
}}";

var configuration = new ConfigurationBuilder()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -401,7 +401,6 @@ public static void EvaluateLoggerProvider(IServiceProvider serviceProvider, bool

var secondProcessor = valueField.GetValue(secondNode);
Assert.NotNull(secondProcessor);
Assert.Contains("BatchLogRecordExportProcessor", secondProcessor.GetType().Name);

var exporterProperty = secondProcessor.GetType().GetProperty("Exporter", BindingFlags.NonPublic | BindingFlags.Instance);
Assert.NotNull(exporterProperty);
Expand All @@ -412,9 +411,6 @@ public static void EvaluateLoggerProvider(IServiceProvider serviceProvider, bool
}
else
{
// When LiveMetrics is disabled, processor should be a BatchLogRecordExportProcessor
Assert.Contains("BatchLogRecordExportProcessor", processor.GetType().Name);

var exporterProperty = processor.GetType().GetProperty("Exporter", BindingFlags.NonPublic | BindingFlags.Instance);
Assert.NotNull(exporterProperty);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,11 @@
* Enabled resource metrics export by default.
([#53432](https://github.com/Azure/azure-sdk-for-net/pull/53432))

* Added `EnableTraceBasedLogSampler` property to `AzureMonitorExporterOptions`
to enable filtering logs based on trace sampling decisions, reducing log
volume while maintaining trace-log correlation.
([#53441](https://github.com/Azure/azure-sdk-for-net/pull/53441))

### Breaking Changes

### Bugs Fixed
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ public AzureMonitorExporterOptions(Azure.Monitor.OpenTelemetry.Exporter.AzureMon
public Azure.Core.TokenCredential Credential { get { throw null; } set { } }
public bool DisableOfflineStorage { get { throw null; } set { } }
public bool EnableLiveMetrics { get { throw null; } set { } }
public bool EnableTraceBasedLogsSampler { get { throw null; } set { } }
public float SamplingRatio { get { throw null; } set { } }
public string StorageDirectory { get { throw null; } set { } }
public double? TracesPerSecond { get { throw null; } set { } }
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ public AzureMonitorExporterOptions(Azure.Monitor.OpenTelemetry.Exporter.AzureMon
public Azure.Core.TokenCredential Credential { get { throw null; } set { } }
public bool DisableOfflineStorage { get { throw null; } set { } }
public bool EnableLiveMetrics { get { throw null; } set { } }
public bool EnableTraceBasedLogsSampler { get { throw null; } set { } }
public float SamplingRatio { get { throw null; } set { } }
public string StorageDirectory { get { throw null; } set { } }
public double? TracesPerSecond { get { throw null; } set { } }
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -203,7 +203,12 @@ public static OpenTelemetryLoggerOptions AddAzureMonitorLogExporter(
options.Credential ??= credential;
}

return loggerOptions.AddProcessor(new BatchLogRecordExportProcessor(new AzureMonitorLogExporter(options)));
var exporter = new AzureMonitorLogExporter(options);
BaseProcessor<LogRecord> processor = options.EnableTraceBasedLogsSampler
? new LogFilteringProcessor(exporter)
: new BatchLogRecordExportProcessor(exporter);

return loggerOptions.AddProcessor(processor);
}

/// <summary>
Expand Down Expand Up @@ -271,7 +276,10 @@ public static LoggerProviderBuilder AddAzureMonitorLogExporter(
sp.EnsureNoUseAzureMonitorExporterRegistrations();

// TODO: Do we need provide an option to alter BatchExportLogRecordProcessorOptions?
return new BatchLogRecordExportProcessor(new AzureMonitorLogExporter(exporterOptions));
var exporter = new AzureMonitorLogExporter(exporterOptions);
return exporterOptions.EnableTraceBasedLogsSampler
? new LogFilteringProcessor(exporter)
: new BatchLogRecordExportProcessor(exporter);
});
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,16 @@ public enum ServiceVersion
/// </remarks>
public bool EnableLiveMetrics { get; set; } = true;

/// <summary>
/// Enables or disables filtering logs based on trace sampling decisions.
/// </summary>
/// <remarks>
/// When enabled, only logs associated with sampled traces are exported.
/// Logs without trace context are always exported.
/// This reduces log volume while maintaining trace-log correlation.
/// </remarks>
public bool EnableTraceBasedLogsSampler { get; set; } = true;

/// <summary>
/// Internal flag to control if Statsbeat is enabled.
/// </summary>
Expand Down
Loading
Loading