diff --git a/src/WebJobs.Script.WebHost/Diagnostics/AzureMonitorDiagnosticLogger.cs b/src/WebJobs.Script.WebHost/Diagnostics/AzureMonitorDiagnosticLogger.cs index 4e1f0a5a40..bc08d3ff60 100644 --- a/src/WebJobs.Script.WebHost/Diagnostics/AzureMonitorDiagnosticLogger.cs +++ b/src/WebJobs.Script.WebHost/Diagnostics/AzureMonitorDiagnosticLogger.cs @@ -29,7 +29,7 @@ public class AzureMonitorDiagnosticLogger : ILogger private readonly IEventGenerator _eventGenerator; private readonly IEnvironment _environment; private readonly IExternalScopeProvider _scopeProvider; - private AppServiceOptions _appServiceOptions; + private IOptionsMonitor _appServiceOptionsMonitor; public AzureMonitorDiagnosticLogger(string category, string hostInstanceId, IEventGenerator eventGenerator, IEnvironment environment, IExternalScopeProvider scopeProvider, HostNameProvider hostNameProvider, IOptionsMonitor appServiceOptionsMonitor) @@ -40,10 +40,7 @@ public AzureMonitorDiagnosticLogger(string category, string hostInstanceId, IEve _environment = environment ?? throw new ArgumentNullException(nameof(environment)); _scopeProvider = scopeProvider ?? throw new ArgumentNullException(nameof(scopeProvider)); _hostNameProvider = hostNameProvider ?? throw new ArgumentNullException(nameof(hostNameProvider)); - _ = appServiceOptionsMonitor ?? throw new ArgumentNullException(nameof(appServiceOptionsMonitor)); - - appServiceOptionsMonitor.OnChange(newOptions => _appServiceOptions = newOptions); - _appServiceOptions = appServiceOptionsMonitor.CurrentValue; + _appServiceOptionsMonitor = appServiceOptionsMonitor ?? throw new ArgumentNullException(nameof(appServiceOptionsMonitor)); _roleInstance = _environment.GetInstanceId(); @@ -54,6 +51,10 @@ public AzureMonitorDiagnosticLogger(string category, string hostInstanceId, IEve public bool IsEnabled(LogLevel logLevel) { + if (_environment.IsConsumptionOnLegion() && !_appServiceOptionsMonitor.CurrentValue.IsAzureMonitorLoggingEnabled) + { + return false; + } // We want to instantiate this Logger in placeholder mode to warm it up, but do not want to log anything. return !string.IsNullOrEmpty(_hostNameProvider.Value) && !_environment.IsPlaceholderModeEnabled(); } @@ -107,7 +108,7 @@ public void Log(LogLevel logLevel, EventId eventId, TState state, Except using (JsonTextWriter writer = new JsonTextWriter(sw) { Formatting = Formatting.None }) { writer.WriteStartObject(); - WritePropertyIfNotNull(writer, "appName", _appServiceOptions.AppName); + WritePropertyIfNotNull(writer, "appName", _appServiceOptionsMonitor.CurrentValue.AppName); WritePropertyIfNotNull(writer, "roleInstance", _roleInstance); WritePropertyIfNotNull(writer, "message", formattedMessage); WritePropertyIfNotNull(writer, "category", _category); diff --git a/src/WebJobs.Script.WebHost/WebScriptHostBuilderExtension.cs b/src/WebJobs.Script.WebHost/WebScriptHostBuilderExtension.cs index a43a855098..2fbb66c6e9 100644 --- a/src/WebJobs.Script.WebHost/WebScriptHostBuilderExtension.cs +++ b/src/WebJobs.Script.WebHost/WebScriptHostBuilderExtension.cs @@ -25,11 +25,13 @@ using Microsoft.Azure.WebJobs.Script.WebHost.Management; using Microsoft.Azure.WebJobs.Script.WebHost.Middleware; using Microsoft.Azure.WebJobs.Script.WebHost.Storage; +using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection.Extensions; using Microsoft.Extensions.Hosting; using Microsoft.Extensions.Logging; using Microsoft.Extensions.Options; +using static Microsoft.Azure.WebJobs.Script.Utility; namespace Microsoft.Azure.WebJobs.Script.WebHost { @@ -91,6 +93,17 @@ public static IHostBuilder AddWebScriptHost(this IHostBuilder builder, IServiceP loggingBuilder.AddWebJobsSystem(); if (environment.IsAzureMonitorEnabled()) { + if (environment.IsConsumptionOnLegion()) + { + loggingBuilder.Services.AddOptions() + .Configure>((filters, options) => + { + filters.AddFilter((category, level) => + { + return options.CurrentValue.IsAzureMonitorLoggingEnabled; + }); + }); + } loggingBuilder.Services.AddSingleton(); } diff --git a/src/WebJobs.Script/Config/AppServiceOptions.cs b/src/WebJobs.Script/Config/AppServiceOptions.cs index 5410f66983..899b89571c 100644 --- a/src/WebJobs.Script/Config/AppServiceOptions.cs +++ b/src/WebJobs.Script/Config/AppServiceOptions.cs @@ -12,5 +12,7 @@ public class AppServiceOptions public string RuntimeSiteName { get; set; } public string SlotName { get; set; } + + public bool IsAzureMonitorLoggingEnabled { get; set; } } } diff --git a/src/WebJobs.Script/Config/AppServiceOptionsSetup.cs b/src/WebJobs.Script/Config/AppServiceOptionsSetup.cs index b67f758cd4..932c97521d 100644 --- a/src/WebJobs.Script/Config/AppServiceOptionsSetup.cs +++ b/src/WebJobs.Script/Config/AppServiceOptionsSetup.cs @@ -2,6 +2,8 @@ // Licensed under the MIT License. See License.txt in the project root for license information. using Microsoft.Extensions.Options; +using static Microsoft.Azure.WebJobs.Script.EnvironmentSettingNames; +using static Microsoft.Azure.WebJobs.Script.Utility; namespace Microsoft.Azure.WebJobs.Script.Configuration { @@ -20,6 +22,7 @@ public void Configure(AppServiceOptions options) options.SubscriptionId = _environment.GetSubscriptionId() ?? string.Empty; options.RuntimeSiteName = _environment.GetRuntimeSiteName() ?? string.Empty; options.SlotName = _environment.GetSlotName() ?? string.Empty; + options.IsAzureMonitorLoggingEnabled = _environment.IsAzureMonitorEnabled(); } } } \ No newline at end of file diff --git a/src/WebJobs.Script/Environment/EnvironmentExtensions.cs b/src/WebJobs.Script/Environment/EnvironmentExtensions.cs index fb41d1be38..a1611530aa 100644 --- a/src/WebJobs.Script/Environment/EnvironmentExtensions.cs +++ b/src/WebJobs.Script/Environment/EnvironmentExtensions.cs @@ -11,6 +11,7 @@ using Microsoft.Azure.WebJobs.Script.Description; using Microsoft.Azure.WebJobs.Script.Workers.Rpc; using static Microsoft.Azure.WebJobs.Script.EnvironmentSettingNames; +using static Microsoft.Azure.WebJobs.Script.Utility; namespace Microsoft.Azure.WebJobs.Script { @@ -69,13 +70,7 @@ public static bool IsEasyAuthEnabled(this IEnvironment environment) /// public static bool IsAzureMonitorEnabled(this IEnvironment environment) { - string value = environment.GetEnvironmentVariable(AzureMonitorCategories); - if (value == null) - { - return true; - } - string[] categories = value.Split(','); - return categories.Contains(ScriptConstants.AzureMonitorTraceCategory); + return IsAzureMonitorLoggingEnabled(environment.GetEnvironmentVariable(AzureMonitorCategories)); } /// @@ -337,7 +332,7 @@ public static bool IsLinuxConsumptionOnAtlas(this IEnvironment environment) string.IsNullOrEmpty(environment.GetEnvironmentVariable(LegionServiceHost)); } - private static bool IsConsumptionOnLegion(this IEnvironment environment) + public static bool IsConsumptionOnLegion(this IEnvironment environment) { return !environment.IsAppService() && (!string.IsNullOrEmpty(environment.GetEnvironmentVariable(ContainerName)) || diff --git a/src/WebJobs.Script/ScriptConstants.cs b/src/WebJobs.Script/ScriptConstants.cs index 838707abd0..427e3c55ed 100644 --- a/src/WebJobs.Script/ScriptConstants.cs +++ b/src/WebJobs.Script/ScriptConstants.cs @@ -227,6 +227,7 @@ public static class ScriptConstants public const string ExtendedPlatformChannelNameUpper = "EXTENDED"; public const string AzureMonitorTraceCategory = "FunctionAppLogs"; + public const string DefaultAzureMonitorCategories = "None"; public const string KubernetesManagedAppName = "K8SE_APP_NAME"; public const string KubernetesManagedAppNamespace = "K8SE_APP_NAMESPACE"; diff --git a/src/WebJobs.Script/Utility.cs b/src/WebJobs.Script/Utility.cs index ff1ce8cf10..3e0cee1824 100644 --- a/src/WebJobs.Script/Utility.cs +++ b/src/WebJobs.Script/Utility.cs @@ -1139,6 +1139,22 @@ public static bool TryReadAsBool(IDictionary properties, string return result = false; } + public static bool IsAzureMonitorLoggingEnabled(string azureMonitorcategoriesSubscribed) + { + if (azureMonitorcategoriesSubscribed == null) + { + return true; + } + if (string.Equals(ScriptConstants.DefaultAzureMonitorCategories, azureMonitorcategoriesSubscribed, StringComparison.Ordinal)) + { + // Default value for the env variable is None. + // This is set when customer does not subscribe any category. + return false; + } + string[] categories = azureMonitorcategoriesSubscribed.Split(','); + return categories.Contains(ScriptConstants.AzureMonitorTraceCategory); + } + private class FilteredExpandoObjectConverter : ExpandoObjectConverter { public override bool CanWrite => true; diff --git a/test/WebJobs.Script.Tests/Eventing/DiagnosticLoggerTests.cs b/test/WebJobs.Script.Tests/Eventing/DiagnosticLoggerTests.cs index 4b6f474d66..87ee868c15 100644 --- a/test/WebJobs.Script.Tests/Eventing/DiagnosticLoggerTests.cs +++ b/test/WebJobs.Script.Tests/Eventing/DiagnosticLoggerTests.cs @@ -206,35 +206,30 @@ public void Log_Sanitizes() Assert.True(JToken.DeepEquals(actual, expected), $"Actual: {actual.ToString()}{Environment.NewLine}Expected: {expected.ToString()}"); } - [Fact] - public void Log_DisabledIfPlaceholder() + [Theory] + [InlineData(EnvironmentSettingNames.AzureWebsitePlaceholderMode, "1", false, false, false)] // Placeholder + [InlineData(EnvironmentSettingNames.AzureWebsitePlaceholderMode, "1", true, false, false)] // Placeholder + [InlineData(EnvironmentSettingNames.AzureWebsiteHostName, null, false, false, false)] // NoSiteName + [InlineData(EnvironmentSettingNames.AzureWebsiteHostName, null, true, false, false)] // NoSiteName + [InlineData(EnvironmentSettingNames.AzureWebsiteHostName, "host", false, false, true)] + [InlineData(EnvironmentSettingNames.AzureWebsiteHostName, "host", true, false, false)] + [InlineData(EnvironmentSettingNames.AzureWebsiteHostName, "host", true, true, true)] + public void Log_IsEnabled(string envVariableName, string envVariableVale, bool isConsumptionOnLegion, bool isAzureMonitorEnabled, bool isDisabled) { - string message = "TestMessage"; string functionInvocationId = Guid.NewGuid().ToString(); string activityId = Guid.NewGuid().ToString(); - _environment.SetEnvironmentVariable(EnvironmentSettingNames.AzureWebsitePlaceholderMode, "1"); - - _logger.LogInformation(message); - - Assert.False(_logger.IsEnabled(LogLevel.Information)); - _mockEventGenerator.Verify(m => m.LogAzureMonitorDiagnosticLogEvent(It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny()), Times.Never); - } - - [Fact] - public void Log_DisabledIfNoSiteName() - { - string message = "TestMessage"; - string functionInvocationId = Guid.NewGuid().ToString(); - string activityId = Guid.NewGuid().ToString(); - _environment.SetEnvironmentVariable(EnvironmentSettingNames.AzureWebsiteHostName, null); - - // Recreate the logger was we cache the site name in the constructor - ILogger logger = new AzureMonitorDiagnosticLogger(_category, _hostInstanceId, _mockEventGenerator.Object, _environment, new LoggerExternalScopeProvider(), _hostNameProvider, _appServiceOptionsWrapper); + _environment.SetEnvironmentVariable(envVariableName, envVariableVale); + if (isConsumptionOnLegion) + { + _environment.SetEnvironmentVariable(EnvironmentSettingNames.AzureWebsiteInstanceId, string.Empty); + _environment.SetEnvironmentVariable(EnvironmentSettingNames.ContainerName, "containername"); + _environment.SetEnvironmentVariable(EnvironmentSettingNames.LegionServiceHost, "legionhost"); + } - logger.LogInformation(message); + _appServiceOptionsWrapper.CurrentValue.IsAzureMonitorLoggingEnabled = isAzureMonitorEnabled; - Assert.False(logger.IsEnabled(LogLevel.Information)); + Assert.Equal(isDisabled, _logger.IsEnabled(LogLevel.Information)); _mockEventGenerator.Verify(m => m.LogAzureMonitorDiagnosticLogEvent(It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny()), Times.Never); } diff --git a/test/WebJobs.Script.Tests/Extensions/EnvironmentExtensionsTests.cs b/test/WebJobs.Script.Tests/Extensions/EnvironmentExtensionsTests.cs index b14244af17..9ea8bcf5d5 100644 --- a/test/WebJobs.Script.Tests/Extensions/EnvironmentExtensionsTests.cs +++ b/test/WebJobs.Script.Tests/Extensions/EnvironmentExtensionsTests.cs @@ -62,6 +62,8 @@ public void IsVMSS_RetrunsExpectedResult(string roleInstanceId, bool expected) [InlineData(null, true)] [InlineData("", false)] [InlineData("Foo,FunctionAppLogs,Bar", true)] + [InlineData("FunctionAppLogs", true)] + [InlineData("None", false)] [InlineData("Foo,Bar", false)] public void IsAzureMonitorEnabled_ReturnsExpectedResult(string value, bool expected) {