diff --git a/release_notes.md b/release_notes.md index c8ed5dca88..105beea245 100644 --- a/release_notes.md +++ b/release_notes.md @@ -4,4 +4,5 @@ --> *Please note we have reached end-of-life (EOL) support for v3.x.* For more information on supported runtime versions, please see [here.](https://learn.microsoft.com/en-us/azure/azure-functions/functions-versions?tabs=v4&pivots=programming-language-csharp) -- Disable all non host startup logs for v3.x (#10399) \ No newline at end of file +- Disable all non host startup logs for v3.x (#10399) +- Add hosting config (`DisableHostLogs`) to enable/disable v3.x logs (#10552) \ No newline at end of file diff --git a/src/WebJobs.Script.WebHost/Diagnostics/ILoggingBuilderExtensions.cs b/src/WebJobs.Script.WebHost/Diagnostics/ILoggingBuilderExtensions.cs index 811ccc8854..2f4505a3a0 100644 --- a/src/WebJobs.Script.WebHost/Diagnostics/ILoggingBuilderExtensions.cs +++ b/src/WebJobs.Script.WebHost/Diagnostics/ILoggingBuilderExtensions.cs @@ -8,12 +8,12 @@ namespace Microsoft.Extensions.Logging { public static class ILoggingBuilderExtensions { - public static void AddWebJobsSystem(this ILoggingBuilder builder) where T : SystemLoggerProvider + public static void AddWebJobsSystem(this ILoggingBuilder builder, bool restrictHostLogs = false) where T : SystemLoggerProvider { builder.Services.AddSingleton(); // Log all logs to SystemLogger - builder.AddDefaultWebJobsFilters(LogLevel.Trace); + builder.AddDefaultWebJobsFilters(LogLevel.Trace, restrictHostLogs); } } } diff --git a/src/WebJobs.Script.WebHost/WebScriptHostBuilderExtension.cs b/src/WebJobs.Script.WebHost/WebScriptHostBuilderExtension.cs index 5a0035c753..d2b4d407fa 100644 --- a/src/WebJobs.Script.WebHost/WebScriptHostBuilderExtension.cs +++ b/src/WebJobs.Script.WebHost/WebScriptHostBuilderExtension.cs @@ -75,9 +75,12 @@ public static IHostBuilder AddWebScriptHost(this IHostBuilder builder, IServiceP }) .ConfigureLogging(loggingBuilder => { + var hostingConfigOptions = rootServiceProvider.GetService>(); + var restrictHostLogs = RestrictHostLogs(hostingConfigOptions.Value); + loggingBuilder.Services.AddSingleton(); + loggingBuilder.AddWebJobsSystem(restrictHostLogs); - loggingBuilder.AddWebJobsSystem(); if (environment.IsAzureMonitorEnabled()) { loggingBuilder.Services.AddSingleton(); @@ -170,6 +173,12 @@ private static void ConfigureRegisteredBuilders(TBuilder builder, ISer } } + private static bool RestrictHostLogs(FunctionsHostingConfigOptions options) + { + // Feature flag should take precedence over the host configuration + return !FeatureFlags.IsEnabled(ScriptConstants.FeatureFlagEnableHostLogs) && options.RestrictHostLogs; + } + /// /// Used internally to register Newtonsoft formatters with our ScriptHost. /// diff --git a/src/WebJobs.Script/Config/FunctionsHostingConfigOptions.cs b/src/WebJobs.Script/Config/FunctionsHostingConfigOptions.cs index f389c57cc1..633e8064be 100644 --- a/src/WebJobs.Script/Config/FunctionsHostingConfigOptions.cs +++ b/src/WebJobs.Script/Config/FunctionsHostingConfigOptions.cs @@ -85,6 +85,22 @@ public bool SwtIssuerEnabled } } + /// + /// Gets or sets a value indicating whether non-critical logs should be disabled in the host. + /// + public bool RestrictHostLogs + { + get + { + return GetFeatureOrDefault(ScriptConstants.HostingConfigRestrictHostLogs, "0") == "1"; + } + + set + { + _features[ScriptConstants.HostingConfigRestrictHostLogs] = value ? "1" : "0"; + } + } + /// /// Gets feature by name. /// diff --git a/src/WebJobs.Script/Extensions/ScriptLoggingBuilderExtensions.cs b/src/WebJobs.Script/Extensions/ScriptLoggingBuilderExtensions.cs index f1cc8586c5..54e7542d13 100644 --- a/src/WebJobs.Script/Extensions/ScriptLoggingBuilderExtensions.cs +++ b/src/WebJobs.Script/Extensions/ScriptLoggingBuilderExtensions.cs @@ -6,7 +6,6 @@ using System.Linq; using Microsoft.AspNetCore.Hosting; using Microsoft.Azure.WebJobs.Script; -using Microsoft.Azure.WebJobs.Script.Config; using Microsoft.Azure.WebJobs.Script.Configuration; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.Hosting; @@ -15,17 +14,25 @@ namespace Microsoft.Extensions.Logging { public static class ScriptLoggingBuilderExtensions { - private static ConcurrentDictionary _filteredCategoryCache = new ConcurrentDictionary(); + private static ConcurrentDictionary _filteredCategoryCache = new (); + private static ImmutableArray _allowedLogCategoryPrefixes = ImmutableArray.Empty; - public static ILoggingBuilder AddDefaultWebJobsFilters(this ILoggingBuilder builder) + // For testing only + internal static ImmutableArray AllowedSystemLogPrefixes => _allowedLogCategoryPrefixes; + + public static ILoggingBuilder AddDefaultWebJobsFilters(this ILoggingBuilder builder, bool restrictHostLogs = false) { + SetSystemLogCategoryPrefixes(restrictHostLogs); + builder.SetMinimumLevel(LogLevel.None); builder.AddFilter((c, l) => Filter(c, l, LogLevel.Information)); return builder; } - public static ILoggingBuilder AddDefaultWebJobsFilters(this ILoggingBuilder builder, LogLevel level) where T : ILoggerProvider + public static ILoggingBuilder AddDefaultWebJobsFilters(this ILoggingBuilder builder, LogLevel level, bool restrictHostLogs = false) where T : ILoggerProvider { + SetSystemLogCategoryPrefixes(restrictHostLogs); + builder.AddFilter(null, LogLevel.None); builder.AddFilter((c, l) => Filter(c, l, level)); return builder; @@ -38,11 +45,18 @@ internal static bool Filter(string category, LogLevel actualLevel, LogLevel minL private static bool IsFiltered(string category) { - ImmutableArray systemLogCategoryPrefixes = FeatureFlags.IsEnabled(ScriptConstants.FeatureFlagEnableHostLogs) - ? ScriptConstants.SystemLogCategoryPrefixes - : ScriptConstants.RestrictedSystemLogCategoryPrefixes; + return _filteredCategoryCache.GetOrAdd(category, c => _allowedLogCategoryPrefixes.Any(p => c.StartsWith(p))); + } + + private static void SetSystemLogCategoryPrefixes(bool restrictHostLogs) + { + var previous = _allowedLogCategoryPrefixes; + _allowedLogCategoryPrefixes = restrictHostLogs ? ScriptConstants.RestrictedSystemLogCategoryPrefixes : ScriptConstants.SystemLogCategoryPrefixes; - return _filteredCategoryCache.GetOrAdd(category, c => systemLogCategoryPrefixes.Any(p => category.StartsWith(p))); + if (!previous.IsDefault && !previous.SequenceEqual(_allowedLogCategoryPrefixes)) + { + _filteredCategoryCache.Clear(); + } } public static void AddConsoleIfEnabled(this ILoggingBuilder builder, HostBuilderContext context) diff --git a/src/WebJobs.Script/ScriptConstants.cs b/src/WebJobs.Script/ScriptConstants.cs index 5f0d49e344..3b1cebce28 100644 --- a/src/WebJobs.Script/ScriptConstants.cs +++ b/src/WebJobs.Script/ScriptConstants.cs @@ -127,6 +127,7 @@ public static class ScriptConstants public const string FeatureFlagEnableHostLogs = "EnableHostLogs"; public const string HostingConfigSwtAuthenticationEnabled = "SwtAuthenticationEnabled"; public const string HostingConfigSwtIssuerEnabled = "SwtIssuerEnabled"; + public const string HostingConfigRestrictHostLogs = "RestrictHostLogs"; public const string SiteAzureFunctionsUriFormat = "https://{0}.azurewebsites.net/azurefunctions"; public const string ScmSiteUriFormat = "https://{0}.scm.azurewebsites.net"; diff --git a/test/TestFunctions/TestFunctions.csproj b/test/TestFunctions/TestFunctions.csproj index 7d48bd8571..99d7e34c06 100644 --- a/test/TestFunctions/TestFunctions.csproj +++ b/test/TestFunctions/TestFunctions.csproj @@ -6,7 +6,7 @@ false - true + false diff --git a/test/WebJobs.Script.Tests.Integration/EventHubs/EventHubsEndToEndTestsBase.cs b/test/WebJobs.Script.Tests.Integration/EventHubs/EventHubsEndToEndTestsBase.cs index 180dcfc8b7..977dda03dc 100644 --- a/test/WebJobs.Script.Tests.Integration/EventHubs/EventHubsEndToEndTestsBase.cs +++ b/test/WebJobs.Script.Tests.Integration/EventHubs/EventHubsEndToEndTestsBase.cs @@ -21,7 +21,7 @@ public EventHubsEndToEndTestsBase(TestFixture fixture) _fixture = fixture; } - [Fact] + [Fact(Skip = "SAS authentication has been disabled for the namespace.")] public async Task EventHub() { // Event Hub needs the following environment vars: diff --git a/test/WebJobs.Script.Tests.Integration/Management/InstanceManagerTests.cs b/test/WebJobs.Script.Tests.Integration/Management/InstanceManagerTests.cs index c782cb7b5d..5de2c6fae4 100644 --- a/test/WebJobs.Script.Tests.Integration/Management/InstanceManagerTests.cs +++ b/test/WebJobs.Script.Tests.Integration/Management/InstanceManagerTests.cs @@ -356,7 +356,7 @@ public async Task ValidateContext_InvalidZipUrl_WebsiteUseZip_ReturnsError() { var environmentSettings = new Dictionary() { - { EnvironmentSettingNames.AzureWebsiteZipDeployment, "http://invalid.com/invalid/dne" } + { EnvironmentSettingNames.AzureWebsiteZipDeployment, "http://invalid.test/invalid/dne" } }; var environment = new TestEnvironment(); @@ -439,7 +439,7 @@ public async Task ValidateContext_Succeeds_For_WebsiteUseZip_Only() { var environment = new Dictionary() { - { EnvironmentSettingNames.AzureWebsiteZipDeployment, "http://microsoft.com" } + { EnvironmentSettingNames.AzureWebsiteZipDeployment, "https://valid-zip.test" } }; var assignmentContext = new HostAssignmentContext { @@ -449,7 +449,19 @@ public async Task ValidateContext_Succeeds_For_WebsiteUseZip_Only() IsWarmupRequest = false }; - string error = await _instanceManager.ValidateContext(assignmentContext); + var handlerMock = new Mock(); + handlerMock.Protected().Setup>("SendAsync", + ItExpr.IsAny(), + ItExpr.IsAny()).ReturnsAsync(new HttpResponseMessage + { + StatusCode = HttpStatusCode.OK, + }); + + var instanceManager = new InstanceManager(_optionsFactory, TestHelpers.CreateHttpClientFactory(handlerMock.Object).CreateClient(), + _scriptWebEnvironment, _environment, _loggerFactory.CreateLogger(), + new TestMetricsLogger(), null, _runFromPackageHandler, _packageDownloadHandler.Object); + + string error = await instanceManager.ValidateContext(assignmentContext); Assert.Null(error); string[] expectedOutputLines = @@ -539,8 +551,8 @@ public async Task ValidateContext_Succeeds_For_WebsiteUseZip_With_ScmPackageDefi { var environment = new Dictionary() { - { EnvironmentSettingNames.AzureWebsiteZipDeployment, "http://microsoft.com" }, - { EnvironmentSettingNames.ScmRunFromPackage, "http://microsoft.com" } + { EnvironmentSettingNames.AzureWebsiteZipDeployment, "https://valid.test" }, + { EnvironmentSettingNames.ScmRunFromPackage, "https://valid.tests" } }; var assignmentContext = new HostAssignmentContext { @@ -550,7 +562,19 @@ public async Task ValidateContext_Succeeds_For_WebsiteUseZip_With_ScmPackageDefi IsWarmupRequest = false }; - string error = await _instanceManager.ValidateContext(assignmentContext); + var handlerMock = new Mock(); + handlerMock.Protected().Setup>("SendAsync", + ItExpr.IsAny(), + ItExpr.IsAny()).ReturnsAsync(new HttpResponseMessage + { + StatusCode = HttpStatusCode.OK, + }); + + var instanceManager = new InstanceManager(_optionsFactory, TestHelpers.CreateHttpClientFactory(handlerMock.Object).CreateClient(), + _scriptWebEnvironment, _environment, _loggerFactory.CreateLogger(), + new TestMetricsLogger(), null, _runFromPackageHandler, _packageDownloadHandler.Object); + + string error = await instanceManager.ValidateContext(assignmentContext); Assert.Null(error); string[] expectedOutputLines = diff --git a/test/WebJobs.Script.Tests.Integration/WebHostEndToEnd/SamplesEndToEndTests_Node.cs b/test/WebJobs.Script.Tests.Integration/WebHostEndToEnd/SamplesEndToEndTests_Node.cs index 50061eb9d9..759f8fc0d8 100644 --- a/test/WebJobs.Script.Tests.Integration/WebHostEndToEnd/SamplesEndToEndTests_Node.cs +++ b/test/WebJobs.Script.Tests.Integration/WebHostEndToEnd/SamplesEndToEndTests_Node.cs @@ -39,7 +39,7 @@ public SamplesEndToEndTests_Node(TestFixture fixture) _settingsManager = ScriptSettingsManager.Instance; } - [Fact] + [Fact(Skip = "SAS authentication has been disabled for the namespace.")] public async Task EventHubTrigger() { // write 3 events diff --git a/test/WebJobs.Script.Tests.Shared/TestHelpers.cs b/test/WebJobs.Script.Tests.Shared/TestHelpers.cs index 2b01586f2f..9166f6ae22 100644 --- a/test/WebJobs.Script.Tests.Shared/TestHelpers.cs +++ b/test/WebJobs.Script.Tests.Shared/TestHelpers.cs @@ -23,6 +23,7 @@ using Microsoft.Extensions.Hosting; using Microsoft.Extensions.Options; using Microsoft.WebJobs.Script.Tests; +using Moq; using Xunit.Abstractions; namespace Microsoft.Azure.WebJobs.Script.Tests @@ -475,5 +476,19 @@ public static IAzureStorageProvider GetAzureStorageProvider(IConfiguration confi var azureStorageProvider = tempHost.Services.GetRequiredService(); return azureStorageProvider; } + + /// + /// Mock an HttpClientFactory and its CreateClient functionality. + /// + /// Some tests pass a mock HttpHandler into their HttpClient. + /// IHttpClientFactory. + public static IHttpClientFactory CreateHttpClientFactory(HttpMessageHandler handler = null) + { + var httpClient = handler == null ? new HttpClient() : new HttpClient(handler); + var mockFactory = new Mock(); + mockFactory.Setup(m => m.CreateClient(It.IsAny())) + .Returns(httpClient); + return mockFactory.Object; + } } } \ No newline at end of file diff --git a/test/WebJobs.Script.Tests.Shared/TestHostBuilderExtensions.cs b/test/WebJobs.Script.Tests.Shared/TestHostBuilderExtensions.cs index a1f55a1442..24ca6c2f7c 100644 --- a/test/WebJobs.Script.Tests.Shared/TestHostBuilderExtensions.cs +++ b/test/WebJobs.Script.Tests.Shared/TestHostBuilderExtensions.cs @@ -29,13 +29,13 @@ namespace Microsoft.WebJobs.Script.Tests { public static class TestHostBuilderExtensions { - public static IHostBuilder ConfigureDefaultTestWebScriptHost(this IHostBuilder builder, Action configure = null, bool runStartupHostedServices = false) + public static IHostBuilder ConfigureDefaultTestWebScriptHost(this IHostBuilder builder, Action configure = null, bool runStartupHostedServices = false, IEnvironment environment = null) { - return builder.ConfigureDefaultTestWebScriptHost(null, configure, runStartupHostedServices); + return builder.ConfigureDefaultTestWebScriptHost(null, configure, runStartupHostedServices, environment: environment); } public static IHostBuilder ConfigureDefaultTestWebScriptHost(this IHostBuilder builder, Action configureWebJobs, - Action configure = null, bool runStartupHostedServices = false, Action configureRootServices = null) + Action configure = null, bool runStartupHostedServices = false, Action configureRootServices = null, IEnvironment environment = null) { var webHostOptions = new ScriptApplicationHostOptions() { @@ -48,9 +48,18 @@ public static IHostBuilder ConfigureDefaultTestWebScriptHost(this IHostBuilder b // Register root services var services = new ServiceCollection(); + + if (environment is not null) + { + services.AddSingleton(environment); + } + else + { + services.AddSingleton(SystemEnvironment.Instance); + } + AddMockedSingleton(services); AddMockedSingleton(services); - AddMockedSingleton(services); AddMockedSingleton(services); AddMockedSingleton(services); AddMockedSingleton(services); diff --git a/test/WebJobs.Script.Tests/Configuration/DefaultDependencyValidatorTests.cs b/test/WebJobs.Script.Tests/Configuration/DefaultDependencyValidatorTests.cs index 8bfda63aab..e9fa0f9422 100644 --- a/test/WebJobs.Script.Tests/Configuration/DefaultDependencyValidatorTests.cs +++ b/test/WebJobs.Script.Tests/Configuration/DefaultDependencyValidatorTests.cs @@ -36,27 +36,28 @@ public async Task Validator_AllValid() [Fact] public async Task Validator_InvalidServices_LogsError() { - SystemEnvironment.Instance.SetEnvironmentVariable(EnvironmentSettingNames.AzureWebJobsFeatureFlags, ScriptConstants.FeatureFlagEnableHostLogs); - - LogMessage invalidServicesMessage = await RunTest(configureJobHost: s => + using (new TestScopedEnvironmentVariable(EnvironmentSettingNames.AzureWebJobsFeatureFlags, ScriptConstants.FeatureFlagEnableHostLogs)) { - s.AddSingleton(); - s.AddSingleton(); - s.AddSingleton(new MyMetricsLogger()); - - // Try removing system logger - var descriptor = s.Single(p => p.ImplementationType == typeof(SystemLoggerProvider)); - s.Remove(descriptor); - }); - - Assert.NotNull(invalidServicesMessage); - - IEnumerable messageLines = invalidServicesMessage.Exception.Message.Split(Environment.NewLine, StringSplitOptions.RemoveEmptyEntries).Select(p => p.Trim()); - Assert.Equal(5, messageLines.Count()); - Assert.Contains(messageLines, p => p.StartsWith("[Invalid]") && p.EndsWith(typeof(MyHostedService).AssemblyQualifiedName)); - Assert.Contains(messageLines, p => p.StartsWith("[Invalid]") && p.EndsWith(typeof(MyScriptEventManager).AssemblyQualifiedName)); - Assert.Contains(messageLines, p => p.StartsWith("[Invalid]") && p.EndsWith(typeof(MyMetricsLogger).AssemblyQualifiedName)); - Assert.Contains(messageLines, p => p.StartsWith("[Missing]") && p.EndsWith(typeof(SystemLoggerProvider).AssemblyQualifiedName)); + LogMessage invalidServicesMessage = await RunTest(configureJobHost: s => + { + s.AddSingleton(); + s.AddSingleton(); + s.AddSingleton(new MyMetricsLogger()); + + // Try removing system logger + var descriptor = s.Single(p => p.ImplementationType == typeof(SystemLoggerProvider)); + s.Remove(descriptor); + }); + + Assert.NotNull(invalidServicesMessage); + + IEnumerable messageLines = invalidServicesMessage.Exception.Message.Split(Environment.NewLine, StringSplitOptions.RemoveEmptyEntries).Select(p => p.Trim()); + Assert.Equal(5, messageLines.Count()); + Assert.Contains(messageLines, p => p.StartsWith("[Invalid]") && p.EndsWith(typeof(MyHostedService).AssemblyQualifiedName)); + Assert.Contains(messageLines, p => p.StartsWith("[Invalid]") && p.EndsWith(typeof(MyScriptEventManager).AssemblyQualifiedName)); + Assert.Contains(messageLines, p => p.StartsWith("[Invalid]") && p.EndsWith(typeof(MyMetricsLogger).AssemblyQualifiedName)); + Assert.Contains(messageLines, p => p.StartsWith("[Missing]") && p.EndsWith(typeof(SystemLoggerProvider).AssemblyQualifiedName)); + } } [Fact] diff --git a/test/WebJobs.Script.Tests/Configuration/FunctionsHostingConfigOptionsTest.cs b/test/WebJobs.Script.Tests/Configuration/FunctionsHostingConfigOptionsTest.cs index aa72b0fd22..de80967ec5 100644 --- a/test/WebJobs.Script.Tests/Configuration/FunctionsHostingConfigOptionsTest.cs +++ b/test/WebJobs.Script.Tests/Configuration/FunctionsHostingConfigOptionsTest.cs @@ -135,14 +135,35 @@ public void SwtIssuerEnabled_ReturnsExpectedValue() Assert.False(options.SwtIssuerEnabled); } - internal static IHostBuilder GetScriptHostBuilder(string fileName, string fileContent) + [Fact] + public void RestrictHostLogs_ReturnsExpectedValue() + { + FunctionsHostingConfigOptions options = new FunctionsHostingConfigOptions(); + + // defaults to false + Assert.False(options.RestrictHostLogs); + + // returns true when explicitly enabled + options.Features[ScriptConstants.HostingConfigRestrictHostLogs] = "1"; + Assert.True(options.RestrictHostLogs); + + // returns false when disabled + options.Features[ScriptConstants.HostingConfigRestrictHostLogs] = "0"; + Assert.False(options.RestrictHostLogs); + } + + internal static IHostBuilder GetScriptHostBuilder(string fileName, string fileContent, IEnvironment environment = null) { if (!string.IsNullOrEmpty(fileContent)) { File.WriteAllText(fileName, fileContent); } - TestEnvironment environment = new TestEnvironment(); + if (environment is null) + { + environment = new TestEnvironment(); + } + environment.SetEnvironmentVariable(EnvironmentSettingNames.FunctionsPlatformConfigFilePath, fileName); IHost webHost = new HostBuilder() @@ -161,7 +182,7 @@ internal static IHostBuilder GetScriptHostBuilder(string fileName, string fileCo { services.AddSingleton(); }) - .ConfigureDefaultTestWebScriptHost(null, configureRootServices: (services) => + .ConfigureDefaultTestWebScriptHost(null, environment: environment, configureRootServices: (services) => { services.AddSingleton(webHost.Services.GetService>()); services.AddSingleton(webHost.Services.GetService>()); @@ -170,7 +191,7 @@ internal static IHostBuilder GetScriptHostBuilder(string fileName, string fileCo public class TestService { - public TestService(IOptions options, IOptionsMonitor monitor) + public TestService(IOptions options, IOptionsMonitor monitor) { Options = options; Monitor = monitor; @@ -180,9 +201,9 @@ public TestService(IOptions options, IOpti }); } - public IOptions Options { get; set; } + public IOptions Options { get; set; } - public IOptionsMonitor Monitor { get; set; } + public IOptionsMonitor Monitor { get; set; } } } } diff --git a/test/WebJobs.Script.Tests/Configuration/RestrictHostLogsTests.cs b/test/WebJobs.Script.Tests/Configuration/RestrictHostLogsTests.cs new file mode 100644 index 0000000000..932db3b6f7 --- /dev/null +++ b/test/WebJobs.Script.Tests/Configuration/RestrictHostLogsTests.cs @@ -0,0 +1,75 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the MIT License. See License.txt in the project root for license information. + +using System.Collections.Immutable; +using System.IO; +using System.Reflection; +using System.Threading.Tasks; +using Microsoft.Azure.WebJobs.Script.Config; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Hosting; +using Microsoft.Extensions.Logging; +using WebJobs.Script.Tests; +using Xunit; +using static Microsoft.Azure.WebJobs.Script.Tests.Configuration.FunctionsHostingConfigOptionsTest; + +namespace Microsoft.Azure.WebJobs.Script.Tests.Configuration +{ + public class RestrictHostLogsTests : IAsyncLifetime + { + public Task InitializeAsync() + { + return Task.CompletedTask; + } + + public Task DisposeAsync() + { + // Reset the static _allowedLogCategoryPrefixes field after each test to the default value + typeof(ScriptLoggingBuilderExtensions) + .GetField("_allowedLogCategoryPrefixes", BindingFlags.Static | BindingFlags.NonPublic) + .SetValue(null, default(ImmutableArray)); + + SystemEnvironment.Instance.SetEnvironmentVariable(EnvironmentSettingNames.AzureWebJobsFeatureFlags, null); + return Task.CompletedTask; + } + + [Theory] + [InlineData(true, false, true)] // RestrictHostLogs is true, FeatureFlag is not set, should result in **restricted** logs. + [InlineData(false, true, false)] // RestrictHostLogs is false, FeatureFlag is set, should result in unrestricted logs. + [InlineData(true, true, false)] // RestrictHostLogs is true, FeatureFlag is set, should result in unrestricted logs. + [InlineData(false, false, false)] // RestrictHostLogs is false, FeatureFlag is not set, should result in unrestricted log. This is the default behaviour of the host. + public async Task RestirctHostLogs_SetsCorrectSystemLogPrefix(bool restrictHostLogs, bool setFeatureFlag, bool shouldResultInRestrictedSystemLogs) + { + using (TempDirectory tempDir = new TempDirectory()) + { + string fileName = Path.Combine(tempDir.Path, "settings.txt"); + string fileContent = restrictHostLogs ? $"{ScriptConstants.HostingConfigRestrictHostLogs}=true" : string.Empty; // the default value is false + + if (setFeatureFlag) + { + SystemEnvironment.Instance.SetEnvironmentVariable(EnvironmentSettingNames.AzureWebJobsFeatureFlags, ScriptConstants.FeatureFlagEnableHostLogs); + } + + IHost host = GetScriptHostBuilder(fileName, fileContent).Build(); + var testService = host.Services.GetService(); + + await host.StartAsync(); + await Task.Delay(1000); + + Assert.Equal(restrictHostLogs, testService.Options.Value.RestrictHostLogs); + Assert.Equal(setFeatureFlag, FeatureFlags.IsEnabled(ScriptConstants.FeatureFlagEnableHostLogs, SystemEnvironment.Instance)); + + if (shouldResultInRestrictedSystemLogs) + { + Assert.Equal(ScriptConstants.RestrictedSystemLogCategoryPrefixes, ScriptLoggingBuilderExtensions.AllowedSystemLogPrefixes); + } + else + { + Assert.Equal(ScriptConstants.SystemLogCategoryPrefixes, ScriptLoggingBuilderExtensions.AllowedSystemLogPrefixes); + } + + await host.StopAsync(); + } + } + } +}