diff --git a/sample/host.json b/sample/host.json index 61a30fe30a..efa939a168 100644 --- a/sample/host.json +++ b/sample/host.json @@ -9,8 +9,8 @@ "counterThreshold": 0.80 }, "functionTimeout": "00:05:00", - "logger": { - "fileLoggingMode": "always" + "logging": { + "fileLoggingMode": "always" }, "swagger": { "enabled": true diff --git a/src/WebJobs.Script.WebHost/Diagnostics/ILoggingBuilderExtensions.cs b/src/WebJobs.Script.WebHost/Diagnostics/ILoggingBuilderExtensions.cs new file mode 100644 index 0000000000..811ccc8854 --- /dev/null +++ b/src/WebJobs.Script.WebHost/Diagnostics/ILoggingBuilderExtensions.cs @@ -0,0 +1,19 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the MIT License. See License.txt in the project root for license information. + +using Microsoft.Azure.WebJobs.Script.WebHost.Diagnostics; +using Microsoft.Extensions.DependencyInjection; + +namespace Microsoft.Extensions.Logging +{ + public static class ILoggingBuilderExtensions + { + public static void AddWebJobsSystem(this ILoggingBuilder builder) where T : SystemLoggerProvider + { + builder.Services.AddSingleton(); + + // Log all logs to SystemLogger + builder.AddDefaultWebJobsFilters(LogLevel.Trace); + } + } +} diff --git a/src/WebJobs.Script.WebHost/Diagnostics/MetricsEventManager.cs b/src/WebJobs.Script.WebHost/Diagnostics/MetricsEventManager.cs index efc169e1aa..db408169d8 100644 --- a/src/WebJobs.Script.WebHost/Diagnostics/MetricsEventManager.cs +++ b/src/WebJobs.Script.WebHost/Diagnostics/MetricsEventManager.cs @@ -7,7 +7,6 @@ using System.Linq; using System.Threading; using System.Threading.Tasks; -using Microsoft.Azure.WebJobs.Script.Config; using Microsoft.Azure.WebJobs.Script.Description; using Microsoft.Azure.WebJobs.Script.Diagnostics; using Microsoft.Azure.WebJobs.Script.WebHost.Models; @@ -23,16 +22,16 @@ public class MetricsEventManager : IDisposable private readonly IEventGenerator _eventGenerator; private readonly int _functionActivityFlushIntervalSeconds; private readonly Timer _metricsFlushTimer; - private object _functionActivityTrackerLockObject = new object(); + private readonly object _functionActivityTrackerLockObject = new object(); private static string appName; private static string subscriptionId; private bool _disposed; - public MetricsEventManager(ScriptSettingsManager settingsManager, IEventGenerator generator, int functionActivityFlushIntervalSeconds, int metricsFlushIntervalMS = DefaultFlushIntervalMS) + public MetricsEventManager(IEnvironment environment, IEventGenerator generator, int functionActivityFlushIntervalSeconds, int metricsFlushIntervalMS = DefaultFlushIntervalMS) { // we read these in the ctor (not static ctor) since it can change on the fly - appName = GetNormalizedString(settingsManager.AzureWebsiteUniqueSlotName); - subscriptionId = Utility.GetSubscriptionId(settingsManager) ?? string.Empty; + appName = GetNormalizedString(environment.GetAzureWebsiteUniqueSlotName()); + subscriptionId = environment.GetSubscriptionId() ?? string.Empty; _eventGenerator = generator; _functionActivityFlushIntervalSeconds = functionActivityFlushIntervalSeconds; @@ -497,8 +496,7 @@ private List GetMetricsQueueSnapshot() for (int iterator = 0; iterator < currentQueueLength; iterator++) { - FunctionMetrics queueItem; - if (_functionMetricsQueue.TryDequeue(out queueItem)) + if (_functionMetricsQueue.TryDequeue(out FunctionMetrics queueItem)) { queueSnapshot.Add(queueItem); } diff --git a/src/WebJobs.Script.WebHost/Diagnostics/SystemLogger.cs b/src/WebJobs.Script.WebHost/Diagnostics/SystemLogger.cs index de385f9c6a..40fc3bacd3 100644 --- a/src/WebJobs.Script.WebHost/Diagnostics/SystemLogger.cs +++ b/src/WebJobs.Script.WebHost/Diagnostics/SystemLogger.cs @@ -5,7 +5,6 @@ using System.Collections.Generic; using Microsoft.Azure.WebJobs.Host; using Microsoft.Azure.WebJobs.Logging; -using Microsoft.Azure.WebJobs.Script.Config; using Microsoft.Extensions.Logging; namespace Microsoft.Azure.WebJobs.Script.WebHost.Diagnostics @@ -13,19 +12,15 @@ namespace Microsoft.Azure.WebJobs.Script.WebHost.Diagnostics public class SystemLogger : ILogger { private readonly IEventGenerator _eventGenerator; - private readonly ScriptSettingsManager _settingsManager; - private readonly string _appName; - private readonly string _subscriptionId; private readonly string _categoryName; private readonly string _functionName; private readonly bool _isUserFunction; private readonly string _hostInstanceId; + private readonly IEnvironment _environment; - public SystemLogger(string hostInstanceId, string categoryName, IEventGenerator eventGenerator, ScriptSettingsManager settingsManager) + public SystemLogger(string hostInstanceId, string categoryName, IEventGenerator eventGenerator, IEnvironment environment) { - _settingsManager = settingsManager; - _appName = _settingsManager.AzureWebsiteUniqueSlotName; - _subscriptionId = Utility.GetSubscriptionId(settingsManager); + _environment = environment; _eventGenerator = eventGenerator; _categoryName = categoryName ?? string.Empty; _functionName = LogCategories.IsFunctionCategory(_categoryName) ? _categoryName.Split('.')[1] : string.Empty; @@ -74,8 +69,8 @@ public void Log(LogLevel logLevel, EventId eventId, TState state, Except // Apply standard event properties // Note: we must be sure to default any null values to empty string // otherwise the ETW event will fail to be persisted (silently) - string subscriptionId = _subscriptionId ?? string.Empty; - string appName = _appName ?? string.Empty; + string subscriptionId = _environment.GetSubscriptionId() ?? string.Empty; + string appName = _environment.GetAzureWebsiteUniqueSlotName() ?? string.Empty; string source = _categoryName ?? Utility.GetValueFromState(state, ScriptConstants.LogPropertySourceKey); string summary = Sanitizer.Sanitize(formattedMessage) ?? string.Empty; string innerExceptionType = string.Empty; diff --git a/src/WebJobs.Script.WebHost/Diagnostics/SystemLoggerProvider.cs b/src/WebJobs.Script.WebHost/Diagnostics/SystemLoggerProvider.cs index 023e95254d..47522663bd 100644 --- a/src/WebJobs.Script.WebHost/Diagnostics/SystemLoggerProvider.cs +++ b/src/WebJobs.Script.WebHost/Diagnostics/SystemLoggerProvider.cs @@ -3,7 +3,6 @@ using System; using Microsoft.Azure.WebJobs.Logging; -using Microsoft.Azure.WebJobs.Script.Config; using Microsoft.Azure.WebJobs.Script.Rpc; using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging.Abstractions; @@ -14,14 +13,19 @@ namespace Microsoft.Azure.WebJobs.Script.WebHost.Diagnostics public class SystemLoggerProvider : ILoggerProvider { private readonly string _hostInstanceId; - private IEventGenerator _eventGenerator; - private ScriptSettingsManager _settingsManager; + private readonly IEventGenerator _eventGenerator; + private readonly IEnvironment _environment; - public SystemLoggerProvider(IOptions scriptOptions, IEventGenerator eventGenerator, ScriptSettingsManager settingsManager) + public SystemLoggerProvider(IOptions scriptOptions, IEventGenerator eventGenerator, IEnvironment environment) + : this(scriptOptions.Value.InstanceId, eventGenerator, environment) + { + } + + protected SystemLoggerProvider(string hostInstanceId, IEventGenerator eventGenerator, IEnvironment environment) { _eventGenerator = eventGenerator; - _settingsManager = settingsManager; - _hostInstanceId = scriptOptions.Value.InstanceId; + _environment = environment; + _hostInstanceId = hostInstanceId; } public ILogger CreateLogger(string categoryName) @@ -31,7 +35,7 @@ public ILogger CreateLogger(string categoryName) { return NullLogger.Instance; } - return new SystemLogger(_hostInstanceId, categoryName, _eventGenerator, _settingsManager); + return new SystemLogger(_hostInstanceId, categoryName, _eventGenerator, _environment); } private bool IsUserLogCategory(string categoryName) diff --git a/src/WebJobs.Script.WebHost/Diagnostics/WebHostLoggerProviderFactory.cs b/src/WebJobs.Script.WebHost/Diagnostics/WebHostLoggerProviderFactory.cs deleted file mode 100644 index 3a59b39467..0000000000 --- a/src/WebJobs.Script.WebHost/Diagnostics/WebHostLoggerProviderFactory.cs +++ /dev/null @@ -1,25 +0,0 @@ -// Copyright (c) .NET Foundation. All rights reserved. -// Licensed under the MIT License. See License.txt in the project root for license information. - -namespace Microsoft.Azure.WebJobs.Script.WebHost.Diagnostics -{ - //public class WebHostLoggerProviderFactory : DefaultLoggerProviderFactory - //{ - // private readonly IEventGenerator _eventGenerator; - // private readonly ScriptSettingsManager _settingsManager; - - // public WebHostLoggerProviderFactory(IEventGenerator eventGenerator, ScriptSettingsManager settingsManager) - // { - // _eventGenerator = eventGenerator; - // _settingsManager = settingsManager; - // } - - // public override IEnumerable CreateLoggerProviders(string hostInstanceId, ScriptHostOptions scriptConfig, ScriptSettingsManager settingsManager, IMetricsLogger metricsLogger, Func isFileLoggingEnabled, Func isPrimary) - // { - // // TODO: DI (FACAVAL) Review - // //ILoggerProvider systemProvider = new SystemLoggerProvider(hostInstanceId, _eventGenerator, _settingsManager); - - // return base.CreateLoggerProviders(hostInstanceId, scriptConfig, settingsManager, metricsLogger, isFileLoggingEnabled, isPrimary); - // } - //} -} diff --git a/src/WebJobs.Script.WebHost/Diagnostics/WebHostMetricsLogger.cs b/src/WebJobs.Script.WebHost/Diagnostics/WebHostMetricsLogger.cs index 028bf82ef1..22ca30d7bd 100644 --- a/src/WebJobs.Script.WebHost/Diagnostics/WebHostMetricsLogger.cs +++ b/src/WebJobs.Script.WebHost/Diagnostics/WebHostMetricsLogger.cs @@ -2,7 +2,6 @@ // Licensed under the MIT License. See License.txt in the project root for license information. using System; -using Microsoft.Azure.WebJobs.Script.Config; using Microsoft.Azure.WebJobs.Script.Diagnostics; namespace Microsoft.Azure.WebJobs.Script.WebHost.Diagnostics @@ -12,13 +11,8 @@ public class WebHostMetricsLogger : IMetricsLogger, IDisposable private readonly MetricsEventManager _metricsEventManager; private bool disposed = false; - public WebHostMetricsLogger() - : this(ScriptSettingsManager.Instance, new EtwEventGenerator(), 5) - { - } - - public WebHostMetricsLogger(IEventGenerator eventGenerator) - : this(ScriptSettingsManager.Instance, eventGenerator, 5) + public WebHostMetricsLogger(IEnvironment environment, IEventGenerator eventGenerator) + : this(environment, eventGenerator, 5) { } @@ -27,9 +21,9 @@ public WebHostMetricsLogger(MetricsEventManager eventManager) _metricsEventManager = eventManager; } - protected WebHostMetricsLogger(ScriptSettingsManager settingsManager, IEventGenerator eventGenerator, int metricEventIntervalInSeconds) + protected WebHostMetricsLogger(IEnvironment environment, IEventGenerator eventGenerator, int metricEventIntervalInSeconds) { - _metricsEventManager = new MetricsEventManager(settingsManager, eventGenerator, metricEventIntervalInSeconds); + _metricsEventManager = new MetricsEventManager(environment, eventGenerator, metricEventIntervalInSeconds); } public object BeginEvent(string eventName, string functionName = null) @@ -62,7 +56,7 @@ public void EndEvent(MetricEvent metricEvent) } else { - _metricsEventManager.EndEvent((object)metricEvent); + _metricsEventManager.EndEvent(metricEvent); } } diff --git a/src/WebJobs.Script.WebHost/Diagnostics/WebHostSystemLoggerProvider.cs b/src/WebJobs.Script.WebHost/Diagnostics/WebHostSystemLoggerProvider.cs new file mode 100644 index 0000000000..0d7a6dc9ed --- /dev/null +++ b/src/WebJobs.Script.WebHost/Diagnostics/WebHostSystemLoggerProvider.cs @@ -0,0 +1,17 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the MIT License. See License.txt in the project root for license information. + +namespace Microsoft.Azure.WebJobs.Script.WebHost.Diagnostics +{ + /// + /// A SystemLoggerProvider that can be registered at the WebHost level. It logs with an empty InstanceId + /// to make it clear that it is not a part of a JobHost. + /// + public class WebHostSystemLoggerProvider : SystemLoggerProvider + { + public WebHostSystemLoggerProvider(IEventGenerator eventGenerator, IEnvironment environment) + : base(string.Empty, eventGenerator, environment) + { + } + } +} diff --git a/src/WebJobs.Script.WebHost/Program.cs b/src/WebJobs.Script.WebHost/Program.cs index c1c40044f2..0a452e6b85 100644 --- a/src/WebJobs.Script.WebHost/Program.cs +++ b/src/WebJobs.Script.WebHost/Program.cs @@ -6,6 +6,7 @@ using Microsoft.AspNetCore.Hosting; using Microsoft.Azure.WebJobs.Script.WebHost.Configuration; using Microsoft.Azure.WebJobs.Script.WebHost.DependencyInjection; +using Microsoft.Azure.WebJobs.Script.WebHost.Diagnostics; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.Configuration.EnvironmentVariables; using Microsoft.Extensions.DependencyInjection; @@ -18,8 +19,16 @@ public class Program { public static void Main(string[] args) { - BuildWebHost(args) - .RunAsync() + var host = BuildWebHost(args); + + var environment = host.Services.GetService(); + if (environment.IsLinuxContainerEnvironment()) + { + // Linux containers always start out in placeholder mode + environment.SetEnvironmentVariable(EnvironmentSettingNames.AzureWebsitePlaceholderMode, "1"); + } + + host.RunAsync() .Wait(); } @@ -57,7 +66,13 @@ public static IWebHostBuilder CreateWebHostBuilder(string[] args = null) IsLinuxContainerEnvironment = SystemEnvironment.Instance.IsLinuxContainerEnvironment() }); }) - .ConfigureLogging(b => b.ClearProviders()) + .ConfigureLogging((context, loggingBuilder) => + { + loggingBuilder.ClearProviders(); + + loggingBuilder.AddDefaultWebJobsFilters(); + loggingBuilder.AddWebJobsSystem(); + }) .UseStartup(); } } diff --git a/src/WebJobs.Script.WebHost/Startup.cs b/src/WebJobs.Script.WebHost/Startup.cs index f29377dbc0..d84498feca 100644 --- a/src/WebJobs.Script.WebHost/Startup.cs +++ b/src/WebJobs.Script.WebHost/Startup.cs @@ -31,17 +31,9 @@ public void Configure(IApplicationBuilder app, IApplicationLifetime applicationL { if (env.IsDevelopment()) { - loggerFactory.AddConsole(LogLevel.Trace, true); app.UseDeveloperExceptionPage(); } - var environment = app.ApplicationServices.GetService(); - if (environment.IsLinuxContainerEnvironment()) - { - // Linux containers always start out in placeholder mode - environment.SetEnvironmentVariable(EnvironmentSettingNames.AzureWebsitePlaceholderMode, "1"); - } - app.UseWebJobsScriptHost(applicationLifetime); } } diff --git a/src/WebJobs.Script.WebHost/WebScriptHostBuilderExtension.cs b/src/WebJobs.Script.WebHost/WebScriptHostBuilderExtension.cs index 07ee83b37b..d185e36cfa 100644 --- a/src/WebJobs.Script.WebHost/WebScriptHostBuilderExtension.cs +++ b/src/WebJobs.Script.WebHost/WebScriptHostBuilderExtension.cs @@ -12,7 +12,6 @@ using Microsoft.Extensions.DependencyInjection.Extensions; using Microsoft.Extensions.Hosting; using Microsoft.Extensions.Logging; -using Microsoft.Extensions.Options; namespace Microsoft.Azure.WebJobs.Script.WebHost { @@ -41,7 +40,8 @@ public static IHostBuilder AddWebScriptHost(this IHostBuilder builder, IServiceP .ConfigureLogging(loggingBuilder => { loggingBuilder.Services.AddSingleton(); - loggingBuilder.Services.AddSingleton(); + + loggingBuilder.AddWebJobsSystem(); ConfigureRegisteredBuilders(loggingBuilder, rootServiceProvider); }) diff --git a/src/WebJobs.Script/Environment/EnvironmentExtensions.cs b/src/WebJobs.Script/Environment/EnvironmentExtensions.cs index c908cb1487..7759233865 100644 --- a/src/WebJobs.Script/Environment/EnvironmentExtensions.cs +++ b/src/WebJobs.Script/Environment/EnvironmentExtensions.cs @@ -67,6 +67,24 @@ public static string GetAzureWebsiteUniqueSlotName(this IEnvironment environment return name?.ToLowerInvariant(); } + /// + /// Gets a the subscription Id of the current site. + /// + public static string GetSubscriptionId(this IEnvironment environment) + { + string ownerName = environment.GetEnvironmentVariable(AzureWebsiteOwnerName) ?? string.Empty; + if (!string.IsNullOrEmpty(ownerName)) + { + int idx = ownerName.IndexOf('+'); + if (idx > 0) + { + return ownerName.Substring(0, idx); + } + } + + return null; + } + public static bool IsContainerReady(this IEnvironment environment) { return !string.IsNullOrEmpty(environment.GetEnvironmentVariable(AzureWebsiteContainerReady)); diff --git a/src/WebJobs.Script/Extensions/ScriptLoggingBuilderExtensions.cs b/src/WebJobs.Script/Extensions/ScriptLoggingBuilderExtensions.cs new file mode 100644 index 0000000000..b689b9d9de --- /dev/null +++ b/src/WebJobs.Script/Extensions/ScriptLoggingBuilderExtensions.cs @@ -0,0 +1,69 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the MIT License. See License.txt in the project root for license information. + +using System.Linq; +using Microsoft.AspNetCore.Hosting; +using Microsoft.Azure.WebJobs.Script.Configuration; +using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.Hosting; + +namespace Microsoft.Extensions.Logging +{ + public static class ScriptLoggingBuilderExtensions + { + internal static readonly string[] AllowedCategoryPrefixes = new[] + { + "Microsoft.Azure.WebJobs", + "Function", + "Worker", + "Host" + }; + + public static void AddDefaultWebJobsFilters(this ILoggingBuilder builder) + { + builder.SetMinimumLevel(LogLevel.None); + builder.AddFilter((c, l) => Filter(c, l, LogLevel.Information)); + } + + public static void AddDefaultWebJobsFilters(this ILoggingBuilder builder, LogLevel level) where T : ILoggerProvider + { + builder.AddFilter(null, LogLevel.None); + builder.AddFilter((c, l) => Filter(c, l, LogLevel.Trace)); + } + + private static bool Filter(string category, LogLevel actualLevel, LogLevel minLevel) + { + return actualLevel >= minLevel && AllowedCategoryPrefixes.Where(p => category.StartsWith(p)).Any(); + } + + public static void AddConsoleIfEnabled(this ILoggingBuilder builder, HostBuilderContext context) + { + AddConsoleIfEnabled(builder, context.HostingEnvironment.IsDevelopment(), context.Configuration); + } + + public static void AddConsoleIfEnabled(this ILoggingBuilder builder, WebHostBuilderContext context) + { + AddConsoleIfEnabled(builder, context.HostingEnvironment.IsDevelopment(), context.Configuration); + } + + private static void AddConsoleIfEnabled(ILoggingBuilder builder, bool isDevelopment, IConfiguration configuration) + { + // console logging defaults to false, except for self host + bool enableConsole = isDevelopment; + + string consolePath = ConfigurationPath.Combine(ConfigurationSectionNames.JobHost, "Logging", "Console", "IsEnabled"); + IConfigurationSection configSection = configuration.GetSection(consolePath); + + if (configSection.Exists()) + { + // if it has been explicitly configured that value overrides default + enableConsole = configSection.Get(); + } + + if (enableConsole) + { + builder.AddConsole(); + } + } + } +} diff --git a/src/WebJobs.Script/ScriptHostBuilderExtensions.cs b/src/WebJobs.Script/ScriptHostBuilderExtensions.cs index 0d469f1260..95c834b1e6 100644 --- a/src/WebJobs.Script/ScriptHostBuilderExtensions.cs +++ b/src/WebJobs.Script/ScriptHostBuilderExtensions.cs @@ -54,16 +54,15 @@ public static IHostBuilder AddScriptHost(this IHostBuilder builder, ScriptApplic // Host configuration builder.ConfigureLogging((context, loggingBuilder) => { + loggingBuilder.AddDefaultWebJobsFilters(); + string loggingPath = ConfigurationPath.Combine(ConfigurationSectionNames.JobHost, "Logging"); loggingBuilder.AddConfiguration(context.Configuration.GetSection(loggingPath)); loggingBuilder.Services.AddSingleton(); loggingBuilder.Services.AddSingleton(); - if (ConsoleLoggingEnabled(context)) - { - loggingBuilder.AddConsole(c => { c.DisableColors = false; }); - } + loggingBuilder.AddConsoleIfEnabled(context); ConfigureApplicationInsights(context, loggingBuilder); }) @@ -196,26 +195,6 @@ public static IHostBuilder SetAzureFunctionsConfigurationRoot(this IHostBuilder return builder; } - internal static bool ConsoleLoggingEnabled(HostBuilderContext context) - { - // console logging defaults to false, except for self host - bool enableConsole = context.HostingEnvironment.IsDevelopment(); - - if (!enableConsole) - { - string consolePath = ConfigurationPath.Combine(ConfigurationSectionNames.JobHost, "Logging", "Console", "IsEnabled"); - IConfigurationSection configSection = context.Configuration.GetSection(consolePath); - - if (configSection.Exists()) - { - // if it has been explicitly configured that value overrides default - enableConsole = configSection.Get(); - } - } - - return enableConsole; - } - internal static void ConfigureApplicationInsights(HostBuilderContext context, ILoggingBuilder builder) { string appInsightsKey = context.Configuration[EnvironmentSettingNames.AppInsightsInstrumentationKey]; diff --git a/src/WebJobs.Script/Utility.cs b/src/WebJobs.Script/Utility.cs index e7b8d3c328..bdd62feaa1 100644 --- a/src/WebJobs.Script/Utility.cs +++ b/src/WebJobs.Script/Utility.cs @@ -13,6 +13,7 @@ using System.Text; using System.Threading; using System.Threading.Tasks; +using Microsoft.Azure.WebJobs.Host; using Microsoft.Azure.WebJobs.Script.Config; using Microsoft.Azure.WebJobs.Script.Description; using Microsoft.Azure.WebJobs.Script.Rpc; @@ -108,21 +109,6 @@ internal static TimeSpan ComputeBackoff(int exponent, TimeSpan? unit = null, Tim return delay; } - public static string GetSubscriptionId(ScriptSettingsManager settingsManager) - { - string ownerName = settingsManager.GetSetting(EnvironmentSettingNames.AzureWebsiteOwnerName) ?? string.Empty; - if (!string.IsNullOrEmpty(ownerName)) - { - int idx = ownerName.IndexOf('+'); - if (idx > 0) - { - return ownerName.Substring(0, idx); - } - } - - return null; - } - public static string GetInformationalVersion(Type type) => type.Assembly.GetCustomAttribute()?.InformationalVersion ?? string.Empty; @@ -330,16 +316,6 @@ internal static LogLevel ToLogLevel(System.Diagnostics.TraceLevel traceLevel) } } - public static LoggerFilterOptions CreateLoggerFilterOptions() - { - // TODO: Whitelist should be configurable - // Whitelist our log categories to remove large amounts of ASP.NET logs. - var filterOptions = new LoggerFilterOptions(); - filterOptions.AddFilter((category, level) => category.StartsWith($"{ScriptConstants.LogCategoryHost}.") || category.StartsWith($"{ScriptConstants.LogCategoryFunction}.") || category.StartsWith($"{ScriptConstants.LogCategoryWorker}.")); - - return filterOptions; - } - public static bool GetStateBoolValue(IEnumerable> state, string key) { if (state == null) @@ -390,8 +366,7 @@ public static string GetValueFromState(TState state, string key) public static string GetValueFromScope(IDictionary scopeProperties, string key) { - object value; - if (scopeProperties != null && scopeProperties.TryGetValue(key, out value) && value != null) + if (scopeProperties != null && scopeProperties.TryGetValue(key, out object value) && value != null) { return value.ToString(); } diff --git a/test/WebJobs.Script.Tests.Integration/Diagnostics/MetricsEventManagerTests.cs b/test/WebJobs.Script.Tests.Integration/Diagnostics/MetricsEventManagerTests.cs index 052e24640a..4542af96c7 100644 --- a/test/WebJobs.Script.Tests.Integration/Diagnostics/MetricsEventManagerTests.cs +++ b/test/WebJobs.Script.Tests.Integration/Diagnostics/MetricsEventManagerTests.cs @@ -7,7 +7,6 @@ using System.Text; using System.Threading; using System.Threading.Tasks; -using Microsoft.Azure.WebJobs.Script.Config; using Microsoft.Azure.WebJobs.Script.Description; using Microsoft.Azure.WebJobs.Script.Diagnostics; using Microsoft.Azure.WebJobs.Script.WebHost.Diagnostics; @@ -27,11 +26,9 @@ public class MetricsEventManagerTests private readonly WebHostMetricsLogger _metricsLogger; private readonly List _functionExecutionEventArguments; private readonly List _events; - private readonly ScriptSettingsManager _settingsManager; public MetricsEventManagerTests() { - _settingsManager = ScriptSettingsManager.Instance; _functionExecutionEventArguments = new List(); var mockEventGenerator = new Mock(); @@ -74,7 +71,7 @@ public MetricsEventManagerTests() _events.Add(evt); }); - _metricsEventManager = new MetricsEventManager(_settingsManager, mockEventGenerator.Object, MinimumLongRunningDurationInMs / 1000); + _metricsEventManager = new MetricsEventManager(new TestEnvironment(), mockEventGenerator.Object, MinimumLongRunningDurationInMs / 1000); _metricsLogger = new WebHostMetricsLogger(_metricsEventManager); } @@ -379,7 +376,7 @@ public async Task TimerFlush_CalledOnExpectedInterval() { int flushInterval = 10; Mock mockGenerator = new Mock(); - Mock mockEventManager = new Mock(_settingsManager, mockGenerator.Object, flushInterval, flushInterval) { CallBase = true }; + Mock mockEventManager = new Mock(new TestEnvironment(), mockGenerator.Object, flushInterval, flushInterval) { CallBase = true }; MetricsEventManager eventManager = mockEventManager.Object; int numFlushes = 0; @@ -390,7 +387,7 @@ public async Task TimerFlush_CalledOnExpectedInterval() }); // here we're just verifying that we're called multiple times - await TestHelpers.Await(() => numFlushes >= 5, timeout: 2000, pollingInterval: 100, userMessageCallback: () => $"Expected numFlushes >= 5; Actual: {numFlushes}"); + await TestHelpers.Await(() => numFlushes >= 5, timeout: 2000, pollingInterval: 100, userMessageCallback: () => $"Expected numFlushes >= 5; Actual: {numFlushes}"); mockEventManager.VerifyAll(); } @@ -545,11 +542,9 @@ private static async Task AwaitFunctionTasks(List taskList) private static void ValidateFunctionExecutionEventArgumentsList(List list, int noOfFuncExecutions) { - FunctionExecutionEventArguments invalidElement = null; - string errorMessage = null; Assert.True( - ValidateFunctionExecutionEventArgumentsList(list, noOfFuncExecutions, out invalidElement, out errorMessage), + ValidateFunctionExecutionEventArgumentsList(list, noOfFuncExecutions, out FunctionExecutionEventArguments invalidElement, out string errorMessage), string.Format("ErrorMessage:{0} InvalidElement:{1} List:{2}", errorMessage, invalidElement.ToString(), SerializeFunctionExecutionEventArguments(list))); } @@ -617,8 +612,8 @@ private static bool ValidateFunctionExecutionEventArgumentsList(List= MinimumLongRunningDurationInMs diff --git a/test/WebJobs.Script.Tests.Integration/Host/StandbyManagerTests.cs b/test/WebJobs.Script.Tests.Integration/Host/StandbyManagerTests.cs index 45d65c42a0..ec7cce986a 100644 --- a/test/WebJobs.Script.Tests.Integration/Host/StandbyManagerTests.cs +++ b/test/WebJobs.Script.Tests.Integration/Host/StandbyManagerTests.cs @@ -112,6 +112,7 @@ public async Task StandbyMode_EndToEnd_LinuxContainer() var vars = new Dictionary { + { EnvironmentSettingNames.AzureWebsitePlaceholderMode, "1" }, { EnvironmentSettingNames.ContainerName, "TestApp" }, { EnvironmentSettingNames.AzureWebsiteName, "TestApp" }, { EnvironmentSettingNames.ContainerEncryptionKey, encryptionKey }, diff --git a/test/WebJobs.Script.Tests/Configuration/LoggingConfigurationTests.cs b/test/WebJobs.Script.Tests/Configuration/LoggingConfigurationTests.cs index 7ecc1b2473..9ec6ebdc8a 100644 --- a/test/WebJobs.Script.Tests/Configuration/LoggingConfigurationTests.cs +++ b/test/WebJobs.Script.Tests/Configuration/LoggingConfigurationTests.cs @@ -68,17 +68,40 @@ public void Logging_Filters() LoggerFilterOptions filterOptions = host.Services.GetService>().Value; - Assert.Equal(3, filterOptions.Rules.Count); - - Assert.Equal(LogLevel.Trace, filterOptions.Rules[0].LogLevel); - Assert.Equal("Console", filterOptions.Rules[0].ProviderName); - - Assert.Equal(LogLevel.Trace, filterOptions.Rules[1].LogLevel); - Assert.Null(filterOptions.Rules[1].ProviderName); - Assert.Equal("Some.Custom.Category", filterOptions.Rules[1].CategoryName); - - Assert.Equal(LogLevel.Error, filterOptions.Rules[2].LogLevel); - Assert.Null(filterOptions.Rules[2].ProviderName); + Assert.Equal(6, filterOptions.Rules.Count); + + var rules = filterOptions.Rules.ToArray(); + + var rule = rules[0]; + Assert.Null(rule.ProviderName); + Assert.Null(rule.CategoryName); + Assert.Null(rule.LogLevel); + Assert.NotNull(rule.Filter); // The broad "allowed category" filter. + + rule = rules[1]; + Assert.Equal(LogLevel.Trace, rule.LogLevel); + Assert.Equal("Console", rule.ProviderName); + + rule = rules[2]; + Assert.Equal(LogLevel.Trace, rule.LogLevel); + Assert.Null(rule.ProviderName); + Assert.Equal("Some.Custom.Category", rule.CategoryName); + + rule = rules[3]; + Assert.Equal(LogLevel.Error, rule.LogLevel); + Assert.Null(rule.ProviderName); + + rule = rules[4]; + Assert.Equal(typeof(SystemLoggerProvider).FullName, rule.ProviderName); + Assert.Null(rule.CategoryName); + Assert.Equal(LogLevel.None, rule.LogLevel); + Assert.Null(rule.Filter); + + rule = rules[5]; + Assert.Equal(typeof(SystemLoggerProvider).FullName, rule.ProviderName); + Assert.Null(rule.CategoryName); + Assert.Null(rule.LogLevel); + Assert.NotNull(rule.Filter); // The system-specific "allowed category" filter } [Fact] @@ -90,8 +113,28 @@ public void Logging_DefaultsToInformation() LoggerFilterOptions filterOptions = host.Services.GetService>().Value; - Assert.Equal(LogLevel.Information, filterOptions.MinLevel); - Assert.Empty(filterOptions.Rules); + Assert.Equal(LogLevel.None, filterOptions.MinLevel); + + var rules = filterOptions.Rules.ToArray(); + Assert.Equal(3, rules.Length); + + var rule = rules[0]; + Assert.Null(rule.ProviderName); + Assert.Null(rule.CategoryName); + Assert.Null(rule.LogLevel); + Assert.NotNull(rule.Filter); // The broad "allowed category" filter. + + rule = rules[1]; + Assert.Equal(typeof(SystemLoggerProvider).FullName, rule.ProviderName); + Assert.Null(rule.CategoryName); + Assert.Equal(LogLevel.None, rule.LogLevel); + Assert.Null(rule.Filter); + + rule = rules[2]; + Assert.Equal(typeof(SystemLoggerProvider).FullName, rule.ProviderName); + Assert.Null(rule.CategoryName); + Assert.Null(rule.LogLevel); + Assert.NotNull(rule.Filter); // The system-specific "allowed category" filter } [Fact] diff --git a/test/WebJobs.Script.Tests/Eventing/SystemLoggerProviderTests.cs b/test/WebJobs.Script.Tests/Eventing/SystemLoggerProviderTests.cs index db047ab50b..c2325652a6 100644 --- a/test/WebJobs.Script.Tests/Eventing/SystemLoggerProviderTests.cs +++ b/test/WebJobs.Script.Tests/Eventing/SystemLoggerProviderTests.cs @@ -2,7 +2,6 @@ // Licensed under the MIT License. See License.txt in the project root for license information. using System.IO; using Microsoft.Azure.WebJobs.Logging; -using Microsoft.Azure.WebJobs.Script.Config; using Microsoft.Azure.WebJobs.Script.WebHost.Diagnostics; using Microsoft.Extensions.Logging.Abstractions; using Microsoft.Extensions.Options; @@ -12,7 +11,8 @@ namespace Microsoft.Azure.WebJobs.Script.Tests { public class SystemLoggerProviderTests { - private IOptions _options; + private readonly IOptions _options; + private readonly IEnvironment _environment = new TestEnvironment(); public SystemLoggerProviderTests() { @@ -27,7 +27,7 @@ public SystemLoggerProviderTests() [Fact] public void CreateLogger_ReturnsSystemLogger_ForNonUserCategories() { - var provider = new SystemLoggerProvider(_options, null, ScriptSettingsManager.Instance); + var provider = new SystemLoggerProvider(_options, null, _environment); Assert.IsType(provider.CreateLogger(LogCategories.CreateFunctionCategory("TestFunction"))); Assert.IsType(provider.CreateLogger(ScriptConstants.LogCategoryHostGeneral)); @@ -37,7 +37,7 @@ public void CreateLogger_ReturnsSystemLogger_ForNonUserCategories() [Fact] public void CreateLogger_ReturnsNullLogger_ForUserCategory() { - var provider = new SystemLoggerProvider(_options, null, ScriptSettingsManager.Instance); + var provider = new SystemLoggerProvider(_options, null, _environment); Assert.IsType(provider.CreateLogger(LogCategories.CreateFunctionUserCategory("TestFunction"))); } diff --git a/test/WebJobs.Script.Tests/Eventing/SystemLoggerTests.cs b/test/WebJobs.Script.Tests/Eventing/SystemLoggerTests.cs index 5472781750..857b289794 100644 --- a/test/WebJobs.Script.Tests/Eventing/SystemLoggerTests.cs +++ b/test/WebJobs.Script.Tests/Eventing/SystemLoggerTests.cs @@ -4,9 +4,7 @@ using System; using System.Collections.Generic; using Microsoft.Azure.WebJobs.Logging; -using Microsoft.Azure.WebJobs.Script.Config; using Microsoft.Azure.WebJobs.Script.WebHost.Diagnostics; -using Microsoft.Extensions.Configuration; using Microsoft.Extensions.Logging; using Moq; using Xunit; @@ -19,7 +17,6 @@ public class SystemLoggerTests private readonly Mock _mockEventGenerator; private readonly string _websiteName; private readonly string _subscriptionId; - private readonly ScriptSettingsManager _settingsManager; private readonly string _category; private readonly string _functionName = "TestFunction"; private readonly string _hostInstanceId; @@ -32,21 +29,18 @@ public SystemLoggerTests() _mockEventGenerator = new Mock(MockBehavior.Strict); - var configBuilder = ScriptSettingsManager.CreateDefaultConfigurationBuilder() - .AddInMemoryCollection(new Dictionary + var environment = new TestEnvironment(new Dictionary { { EnvironmentSettingNames.AzureWebsiteOwnerName, $"{_subscriptionId}+westuswebspace" }, { EnvironmentSettingNames.AzureWebsiteName, _websiteName }, }); - var config = configBuilder.Build(); - _settingsManager = new ScriptSettingsManager(config); _category = LogCategories.CreateFunctionCategory(_functionName); - _logger = new SystemLogger(_hostInstanceId, _category, _mockEventGenerator.Object, _settingsManager); + _logger = new SystemLogger(_hostInstanceId, _category, _mockEventGenerator.Object, environment); } [Fact] - public void Trace_Verbose_EmitsExpectedEvent() + public void Log_Verbose_EmitsExpectedEvent() { string eventName = string.Empty; string details = string.Empty; @@ -62,7 +56,7 @@ public void Trace_Verbose_EmitsExpectedEvent() } [Fact] - public void Trace_Verbose_LogData_EmitsExpectedEvent() + public void Log_Verbose_LogData_EmitsExpectedEvent() { string eventName = string.Empty; string details = string.Empty; @@ -90,7 +84,7 @@ public void Trace_Verbose_LogData_EmitsExpectedEvent() } [Fact] - public void Trace_Error_EmitsExpectedEvent() + public void Log_Error_EmitsExpectedEvent() { string eventName = string.Empty; string message = "TestMessage"; @@ -107,7 +101,7 @@ public void Trace_Error_EmitsExpectedEvent() } [Fact] - public void Trace_Sanitizes() + public void Log_Sanitizes() { string secretReplacement = "[Hidden Credential]"; string secretString = "{ \"AzureWebJobsStorage\": \"DefaultEndpointsProtocol=https;AccountName=testAccount1;AccountKey=mykey1;EndpointSuffix=core.windows.net\", \"AnotherKey\": \"AnotherValue\" }"; @@ -131,10 +125,10 @@ public void Trace_Sanitizes() } [Fact] - public void Trace_Ignores_FunctionUserCategory() + public void Log_Ignores_FunctionUserCategory() { // Create a logger with the Function.{FunctionName}.User category, which is what determines user logs. - ILogger logger = new SystemLogger(Guid.NewGuid().ToString(), LogCategories.CreateFunctionUserCategory(_functionName), _mockEventGenerator.Object, _settingsManager); + ILogger logger = new SystemLogger(Guid.NewGuid().ToString(), LogCategories.CreateFunctionUserCategory(_functionName), _mockEventGenerator.Object, new TestEnvironment()); logger.LogDebug("TestMessage"); // Make sure it's never been called. @@ -142,7 +136,7 @@ public void Trace_Ignores_FunctionUserCategory() } [Fact] - public void Trace_Ignores_UserLogStateValue() + public void Log_Ignores_UserLogStateValue() { var logState = new Dictionary {