From 5e2352caa9136fa7016989886f134b9274230b4f Mon Sep 17 00:00:00 2001 From: Vishwesh Bankwar Date: Fri, 9 Feb 2024 13:30:08 -0800 Subject: [PATCH] Add log filtering processor distro (#41665) * draft * fix pooling issue * file header * clean up * Add test * rmv using * refactor * log * rmv unused * fix test * changelog * feedback * refactor * fix test * Update Packages.Data.props --------- Co-authored-by: Timothy Mothra --- .../CHANGELOG.md | 6 +++ .../src/AzureMonitorAspNetCoreEventSource.cs | 12 ++++++ .../src/LogFilteringProcessor.cs | 24 +++++++++++ .../src/OpenTelemetryBuilderExtensions.cs | 26 +++++++++++- .../E2EAzureMonitorDistroTests.cs | 42 +++++++++++++++++++ 5 files changed, 109 insertions(+), 1 deletion(-) create mode 100644 sdk/monitor/Azure.Monitor.OpenTelemetry.AspNetCore/src/LogFilteringProcessor.cs diff --git a/sdk/monitor/Azure.Monitor.OpenTelemetry.AspNetCore/CHANGELOG.md b/sdk/monitor/Azure.Monitor.OpenTelemetry.AspNetCore/CHANGELOG.md index cc19b4d7ec171..e5bab2b2a3a25 100644 --- a/sdk/monitor/Azure.Monitor.OpenTelemetry.AspNetCore/CHANGELOG.md +++ b/sdk/monitor/Azure.Monitor.OpenTelemetry.AspNetCore/CHANGELOG.md @@ -13,6 +13,12 @@ property can be set to `false` to disable live metrics. ([#41872](https://github.com/Azure/azure-sdk-for-net/pull/41872)) +- Added an experimental feature for logs emitted within an active tracing + context to follow the Activity's sampling decision. The feature can be enabled + by setting `OTEL_DOTNET_AZURE_MONITOR_EXPERIMENTAL_ENABLE_LOG_SAMPLING` + environment variable to `true`. + ([#41665](https://github.com/Azure/azure-sdk-for-net/pull/41665)) + ### Breaking Changes ### Bugs Fixed diff --git a/sdk/monitor/Azure.Monitor.OpenTelemetry.AspNetCore/src/AzureMonitorAspNetCoreEventSource.cs b/sdk/monitor/Azure.Monitor.OpenTelemetry.AspNetCore/src/AzureMonitorAspNetCoreEventSource.cs index 11aa7d65bd1a1..00e375019bd50 100644 --- a/sdk/monitor/Azure.Monitor.OpenTelemetry.AspNetCore/src/AzureMonitorAspNetCoreEventSource.cs +++ b/sdk/monitor/Azure.Monitor.OpenTelemetry.AspNetCore/src/AzureMonitorAspNetCoreEventSource.cs @@ -51,6 +51,15 @@ public void ConfigureFailed(Exception ex) } } + [NonEvent] + public void GetEnvironmentVariableFailed(string envVarName, Exception ex) + { + if (IsEnabled(EventLevel.Error)) + { + GetEnvironmentVariableFailed(envVarName, ex.FlattenException().ToInvariantString()); + } + } + [Event(1, Message = "Failed to configure AzureMonitorOptions using the connection string from environment variables due to an exception: {0}", Level = EventLevel.Error)] public void ConfigureFailed(string exceptionMessage) => WriteEvent(1, exceptionMessage); @@ -62,5 +71,8 @@ public void ConfigureFailed(Exception ex) [Event(4, Message = "Vendor instrumentation added for: {0}.", Level = EventLevel.Verbose)] public void VendorInstrumentationAdded(string packageName) => WriteEvent(4, packageName); + + [Event(5, Message = "Failed to Read environment variable {0}, exception: {1}", Level = EventLevel.Error)] + public void GetEnvironmentVariableFailed(string envVarName, string exceptionMessage) => WriteEvent(5, envVarName, exceptionMessage); } } diff --git a/sdk/monitor/Azure.Monitor.OpenTelemetry.AspNetCore/src/LogFilteringProcessor.cs b/sdk/monitor/Azure.Monitor.OpenTelemetry.AspNetCore/src/LogFilteringProcessor.cs new file mode 100644 index 0000000000000..ede796dcb060d --- /dev/null +++ b/sdk/monitor/Azure.Monitor.OpenTelemetry.AspNetCore/src/LogFilteringProcessor.cs @@ -0,0 +1,24 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +using OpenTelemetry.Logs; +using OpenTelemetry; + +namespace Azure.Monitor.OpenTelemetry.AspNetCore +{ + internal class LogFilteringProcessor : BatchLogRecordExportProcessor + { + public LogFilteringProcessor(BaseExporter exporter) + : base(exporter) + { + } + + public override void OnEnd(LogRecord logRecord) + { + if (logRecord.SpanId == default || logRecord.TraceFlags == ActivityTraceFlags.Recorded) + { + base.OnEnd(logRecord); + } + } + } +} diff --git a/sdk/monitor/Azure.Monitor.OpenTelemetry.AspNetCore/src/OpenTelemetryBuilderExtensions.cs b/sdk/monitor/Azure.Monitor.OpenTelemetry.AspNetCore/src/OpenTelemetryBuilderExtensions.cs index b2ca06d7a1bca..707847e5d4d1f 100644 --- a/sdk/monitor/Azure.Monitor.OpenTelemetry.AspNetCore/src/OpenTelemetryBuilderExtensions.cs +++ b/sdk/monitor/Azure.Monitor.OpenTelemetry.AspNetCore/src/OpenTelemetryBuilderExtensions.cs @@ -27,6 +27,8 @@ public static class OpenTelemetryBuilderExtensions { private const string SqlClientInstrumentationPackageName = "OpenTelemetry.Instrumentation.SqlClient"; + private const string EnableLogSamplingEnvVar = "OTEL_DOTNET_AZURE_MONITOR_EXPERIMENTAL_ENABLE_LOG_SAMPLING"; + /// /// Configures Azure Monitor for logging, distributed tracing, and metrics. /// @@ -137,7 +139,29 @@ public static OpenTelemetryBuilder UseAzureMonitor(this OpenTelemetryBuilder bui builder.Services.AddOptions() .Configure>((loggingOptions, azureOptions) => { - loggingOptions.AddAzureMonitorLogExporter(o => azureOptions.Get(Options.DefaultName).SetValueToExporterOptions(o)); + var azureMonitorOptions = azureOptions.Get(Options.DefaultName); + + bool enableLogSampling = false; + try + { + var enableLogSamplingEnvVar = Environment.GetEnvironmentVariable(EnableLogSamplingEnvVar); + bool.TryParse(enableLogSamplingEnvVar, out enableLogSampling); + } + catch (Exception ex) + { + AzureMonitorAspNetCoreEventSource.Log.GetEnvironmentVariableFailed(EnableLogSamplingEnvVar, ex); + } + + if (enableLogSampling) + { + var azureMonitorExporterOptions = new AzureMonitorExporterOptions(); + azureMonitorOptions.SetValueToExporterOptions(azureMonitorExporterOptions); + loggingOptions.AddProcessor(new LogFilteringProcessor(new AzureMonitorLogExporter(azureMonitorExporterOptions))); + } + else + { + loggingOptions.AddAzureMonitorLogExporter(o => azureMonitorOptions.SetValueToExporterOptions(o)); + } }); // Register a configuration action so that when diff --git a/sdk/monitor/Azure.Monitor.OpenTelemetry.AspNetCore/tests/Azure.Monitor.OpenTelemetry.AspNetCore.Tests/E2EAzureMonitorDistroTests.cs b/sdk/monitor/Azure.Monitor.OpenTelemetry.AspNetCore/tests/Azure.Monitor.OpenTelemetry.AspNetCore.Tests/E2EAzureMonitorDistroTests.cs index 84b311b5a8a71..25dec56b68f9a 100644 --- a/sdk/monitor/Azure.Monitor.OpenTelemetry.AspNetCore/tests/Azure.Monitor.OpenTelemetry.AspNetCore.Tests/E2EAzureMonitorDistroTests.cs +++ b/sdk/monitor/Azure.Monitor.OpenTelemetry.AspNetCore/tests/Azure.Monitor.OpenTelemetry.AspNetCore.Tests/E2EAzureMonitorDistroTests.cs @@ -16,6 +16,9 @@ using System.Collections.Generic; using System.Linq; using Xunit.Abstractions; +using OpenTelemetry.Logs; +using OpenTelemetry; +using System.Reflection; namespace Azure.Monitor.OpenTelemetry.AspNetCore.Tests { @@ -77,6 +80,45 @@ public async Task ValidateTelemetryExport() // TODO: This test needs to assert telemetry content. (ie: sample rate) } + [Theory] + [InlineData(null)] + [InlineData("true")] + [InlineData("True")] + [InlineData("False")] + [InlineData("false")] + public void ValidateLogFilteringProcessorIsAddedToLoggerProvider(string enableLogSampling) + { + try + { + Environment.SetEnvironmentVariable("OTEL_DOTNET_AZURE_MONITOR_EXPERIMENTAL_ENABLE_LOG_SAMPLING", enableLogSampling); + + var sv = new ServiceCollection(); + sv.AddOpenTelemetry().UseAzureMonitor(o => o.ConnectionString = "InstrumentationKey=00000000-0000-0000-0000-000000000000"); + + var sp = sv.BuildServiceProvider(); + var loggerProvider = sp.GetRequiredService(); + var sdkProvider = typeof(OpenTelemetryLoggerProvider).GetField("Provider", BindingFlags.Instance | BindingFlags.NonPublic)?.GetValue(loggerProvider); + var processor = sdkProvider?.GetType().GetProperty("Processor", BindingFlags.Instance | BindingFlags.Public)?.GetMethod?.Invoke(sdkProvider, null); + + Assert.NotNull(processor); + + if (enableLogSampling != null && enableLogSampling.Equals("true" , StringComparison.OrdinalIgnoreCase)) + { + Assert.True(processor is LogFilteringProcessor); + Assert.True(processor is BatchLogRecordExportProcessor); + } + else + { + Assert.True(processor is not LogFilteringProcessor); + Assert.True(processor is BatchLogRecordExportProcessor); + } + } + finally + { + Environment.SetEnvironmentVariable("OTEL_DOTNET_AZURE_MONITOR_EXPERIMENTAL_ENABLE_LOG_SAMPLING", null); + } + } + private void WaitForRequest(MockTransport transport) { SpinWait.SpinUntil(