From b0d9e28d305011c2625bce48354e26aa2a3888bf Mon Sep 17 00:00:00 2001 From: Pavel Krymets Date: Fri, 19 May 2017 15:09:24 -0700 Subject: [PATCH 01/20] Logging in DI --- ...AzureAppServicesLoggerFactoryExtensions.cs | 20 +- .../ConsoleLoggerFactoryExtensions.cs | 9 +- .../DebugLoggerFactoryExtensions.cs | 9 +- .../EventLoggerFactoryExtensions.cs | 13 +- .../EventSourceLoggerFactoryExtensions.cs | 13 +- .../XunitLoggerFactoryExtensions.cs | 4 +- .../TraceSourceFactoryExtensions.cs | 53 +- .../LogMessageFilter.cs | 4 + src/Microsoft.Extensions.Logging/Logger.cs | 122 +- .../LoggerFactory.cs | 1082 ++++++++--------- .../LoggerFactory1.cs | 146 +++ .../LoggerFilterOptions.cs | 11 + .../LoggerFilterOptionsConfigurationSetup.cs | 105 ++ .../LoggerFilterRule.cs | 23 + .../LoggerRuleSelector.cs | 78 ++ .../LoggingServiceCollectionExtensions.cs | 19 + .../Microsoft.Extensions.Logging.csproj | 2 + .../StaticFilterOptionsMonitor.cs | 20 + .../LoggerFactoryTest.cs | 27 +- 19 files changed, 1063 insertions(+), 697 deletions(-) create mode 100644 src/Microsoft.Extensions.Logging/LogMessageFilter.cs create mode 100644 src/Microsoft.Extensions.Logging/LoggerFactory1.cs create mode 100644 src/Microsoft.Extensions.Logging/LoggerFilterOptions.cs create mode 100644 src/Microsoft.Extensions.Logging/LoggerFilterOptionsConfigurationSetup.cs create mode 100644 src/Microsoft.Extensions.Logging/LoggerFilterRule.cs create mode 100644 src/Microsoft.Extensions.Logging/LoggerRuleSelector.cs create mode 100644 src/Microsoft.Extensions.Logging/StaticFilterOptionsMonitor.cs diff --git a/src/Microsoft.Extensions.Logging.AzureAppServices/AzureAppServicesLoggerFactoryExtensions.cs b/src/Microsoft.Extensions.Logging.AzureAppServices/AzureAppServicesLoggerFactoryExtensions.cs index 9f7c7c62..644b77b9 100644 --- a/src/Microsoft.Extensions.Logging.AzureAppServices/AzureAppServicesLoggerFactoryExtensions.cs +++ b/src/Microsoft.Extensions.Logging.AzureAppServices/AzureAppServicesLoggerFactoryExtensions.cs @@ -2,6 +2,7 @@ // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System; +using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Logging.AzureAppServices; using Microsoft.Extensions.Logging.AzureAppServices.Internal; @@ -15,25 +16,30 @@ public static class AzureAppServicesLoggerFactoryExtensions /// /// Adds an Azure Web Apps diagnostics logger. /// - /// The extension method argument - public static LoggerFactory AddAzureWebAppDiagnostics(this LoggerFactory factory) + /// The extension method argument + public static IServiceCollection AddAzureWebAppDiagnostics(this IServiceCollection collection) { - return AddAzureWebAppDiagnostics(factory, new AzureAppServicesDiagnosticsSettings()); + return AddAzureWebAppDiagnostics(collection, null); } /// /// Adds an Azure Web Apps diagnostics logger. /// - /// The extension method argument + /// The extension method argument /// The setting object to configure loggers. - public static LoggerFactory AddAzureWebAppDiagnostics(this LoggerFactory factory, AzureAppServicesDiagnosticsSettings settings) + public static IServiceCollection AddAzureWebAppDiagnostics(this IServiceCollection collection, AzureAppServicesDiagnosticsSettings settings) { if (WebAppContext.Default.IsRunningInAzureWebApp) { // Only add the provider if we're in Azure WebApp. That cannot change once the apps started - factory.AddProvider("AzureAppServices", new AzureAppServicesDiagnosticsLoggerProvider(WebAppContext.Default, settings)); + collection.AddSingleton(WebAppContext.Default); + collection.AddSingleton(); + if (settings != null) + { + collection.AddSingleton(settings); + } } - return factory; + return collection; } /// diff --git a/src/Microsoft.Extensions.Logging.Console/ConsoleLoggerFactoryExtensions.cs b/src/Microsoft.Extensions.Logging.Console/ConsoleLoggerFactoryExtensions.cs index baade3b8..5c2c6319 100644 --- a/src/Microsoft.Extensions.Logging.Console/ConsoleLoggerFactoryExtensions.cs +++ b/src/Microsoft.Extensions.Logging.Console/ConsoleLoggerFactoryExtensions.cs @@ -3,6 +3,7 @@ using System; using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Logging.Console; namespace Microsoft.Extensions.Logging @@ -12,11 +13,11 @@ public static class ConsoleLoggerExtensions /// /// Adds a console logger named 'Console' to the factory. /// - /// The to use. - public static LoggerFactory AddConsole(this LoggerFactory factory) + /// The to use. + public static IServiceCollection AddConsole(this IServiceCollection collection) { - factory.AddProvider("Console", new ConsoleLoggerProvider(factory.Configuration)); - return factory; + collection.AddSingleton(); + return collection; } /// diff --git a/src/Microsoft.Extensions.Logging.Debug/DebugLoggerFactoryExtensions.cs b/src/Microsoft.Extensions.Logging.Debug/DebugLoggerFactoryExtensions.cs index 74224362..a436247f 100644 --- a/src/Microsoft.Extensions.Logging.Debug/DebugLoggerFactoryExtensions.cs +++ b/src/Microsoft.Extensions.Logging.Debug/DebugLoggerFactoryExtensions.cs @@ -2,6 +2,7 @@ // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System; +using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Logging.Debug; namespace Microsoft.Extensions.Logging @@ -14,11 +15,11 @@ public static class DebugLoggerFactoryExtensions /// /// Adds a debug logger named 'Debug' to the factory. /// - /// The extension method argument. - public static LoggerFactory AddDebug(this LoggerFactory factory) + /// The extension method argument. + public static IServiceCollection AddDebug(this IServiceCollection serviceCollection) { - factory.AddProvider("Debug", new DebugLoggerProvider()); - return factory; + serviceCollection.AddSingleton(); + return serviceCollection; } /// diff --git a/src/Microsoft.Extensions.Logging.EventLog/EventLoggerFactoryExtensions.cs b/src/Microsoft.Extensions.Logging.EventLog/EventLoggerFactoryExtensions.cs index 3af41254..8295fac2 100644 --- a/src/Microsoft.Extensions.Logging.EventLog/EventLoggerFactoryExtensions.cs +++ b/src/Microsoft.Extensions.Logging.EventLog/EventLoggerFactoryExtensions.cs @@ -2,6 +2,7 @@ // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System; +using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Logging.EventLog; namespace Microsoft.Extensions.Logging @@ -14,17 +15,17 @@ public static class EventLoggerFactoryExtensions /// /// Adds an event logger named 'EventLog' to the factory. /// - /// The extension method argument. - public static LoggerFactory AddEventLog(this LoggerFactory factory) + /// The extension method argument. + public static IServiceCollection AddEventLog(this IServiceCollection serviceCollection) { - if (factory == null) + if (serviceCollection == null) { - throw new ArgumentNullException(nameof(factory)); + throw new ArgumentNullException(nameof(serviceCollection)); } - factory.AddProvider("EventLog", new EventLogLoggerProvider()); + serviceCollection.AddSingleton(); - return factory; + return serviceCollection; } /// diff --git a/src/Microsoft.Extensions.Logging.EventSource/EventSourceLoggerFactoryExtensions.cs b/src/Microsoft.Extensions.Logging.EventSource/EventSourceLoggerFactoryExtensions.cs index 417f94ff..56556a65 100644 --- a/src/Microsoft.Extensions.Logging.EventSource/EventSourceLoggerFactoryExtensions.cs +++ b/src/Microsoft.Extensions.Logging.EventSource/EventSourceLoggerFactoryExtensions.cs @@ -2,6 +2,7 @@ // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System; +using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Logging.EventSource; namespace Microsoft.Extensions.Logging @@ -14,18 +15,18 @@ public static class EventSourceLoggerFactoryExtensions /// /// Adds an event logger named 'EventSource' to the factory. /// - /// The extension method argument. - public static LoggerFactory AddEventSourceLogger(this LoggerFactory factory) + /// The extension method argument. + public static IServiceCollection AddEventSourceLogger(this IServiceCollection serviceCollection) { - if (factory == null) + if (serviceCollection == null) { - throw new ArgumentNullException(nameof(factory)); + throw new ArgumentNullException(nameof(serviceCollection)); } var loggerProvider = LoggingEventSource.Instance.CreateLoggerProvider(); - factory.AddProvider("EventSource", loggerProvider); + serviceCollection.AddSingleton(loggerProvider); - return factory; + return serviceCollection; } /// diff --git a/src/Microsoft.Extensions.Logging.Testing/XunitLoggerFactoryExtensions.cs b/src/Microsoft.Extensions.Logging.Testing/XunitLoggerFactoryExtensions.cs index dc778add..f154e9bd 100644 --- a/src/Microsoft.Extensions.Logging.Testing/XunitLoggerFactoryExtensions.cs +++ b/src/Microsoft.Extensions.Logging.Testing/XunitLoggerFactoryExtensions.cs @@ -11,13 +11,13 @@ public static class XunitLoggerFactoryExtensions { public static LoggerFactory AddXunit(this LoggerFactory loggerFactory, ITestOutputHelper output) { - loggerFactory.AddProvider("Xunit", new XunitLoggerProvider(output)); + loggerFactory.AddProvider(new XunitLoggerProvider(output)); return loggerFactory; } public static LoggerFactory AddXunit(this LoggerFactory loggerFactory, ITestOutputHelper output, LogLevel minLevel) { - loggerFactory.AddProvider("Xunit", new XunitLoggerProvider(output, minLevel)); + loggerFactory.AddProvider(new XunitLoggerProvider(output, minLevel)); return loggerFactory; } diff --git a/src/Microsoft.Extensions.Logging.TraceSource/TraceSourceFactoryExtensions.cs b/src/Microsoft.Extensions.Logging.TraceSource/TraceSourceFactoryExtensions.cs index e71409ed..d7cbf62d 100644 --- a/src/Microsoft.Extensions.Logging.TraceSource/TraceSourceFactoryExtensions.cs +++ b/src/Microsoft.Extensions.Logging.TraceSource/TraceSourceFactoryExtensions.cs @@ -3,6 +3,7 @@ using System; using System.Diagnostics; +using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Logging.TraceSource; namespace Microsoft.Extensions.Logging @@ -12,15 +13,15 @@ public static class TraceSourceFactoryExtensions /// /// Adds a TraceSource logger named 'TraceSource' to the factory. /// - /// The to use. + /// The to use. /// The name of the to use. - public static LoggerFactory AddTraceSource( - this LoggerFactory factory, + public static IServiceCollection AddTraceSource( + this IServiceCollection collection, string switchName) { - if (factory == null) + if (collection == null) { - throw new ArgumentNullException(nameof(factory)); + throw new ArgumentNullException(nameof(collection)); } if (switchName == null) @@ -28,23 +29,23 @@ public static LoggerFactory AddTraceSource( throw new ArgumentNullException(nameof(switchName)); } - return factory.AddTraceSource(new SourceSwitch(switchName)); + return collection.AddTraceSource(new SourceSwitch(switchName)); } /// /// Adds a TraceSource logger named 'TraceSource' to the factory. /// - /// The to use. + /// The to use. /// The name of the to use. /// The to use. - public static LoggerFactory AddTraceSource( - this LoggerFactory factory, + public static IServiceCollection AddTraceSource( + this IServiceCollection collection, string switchName, TraceListener listener) { - if (factory == null) + if (collection == null) { - throw new ArgumentNullException(nameof(factory)); + throw new ArgumentNullException(nameof(collection)); } if (switchName == null) @@ -57,21 +58,21 @@ public static LoggerFactory AddTraceSource( throw new ArgumentNullException(nameof(listener)); } - return factory.AddTraceSource(new SourceSwitch(switchName), listener); + return collection.AddTraceSource(new SourceSwitch(switchName), listener); } /// /// Adds a TraceSource logger named 'TraceSource' to the factory. /// - /// The to use. + /// The to use. /// The to use. - public static LoggerFactory AddTraceSource( - this LoggerFactory factory, + public static IServiceCollection AddTraceSource( + this IServiceCollection collection, SourceSwitch sourceSwitch) { - if (factory == null) + if (collection == null) { - throw new ArgumentNullException(nameof(factory)); + throw new ArgumentNullException(nameof(collection)); } if (sourceSwitch == null) @@ -79,25 +80,25 @@ public static LoggerFactory AddTraceSource( throw new ArgumentNullException(nameof(sourceSwitch)); } - factory.AddProvider("TraceSource", new TraceSourceLoggerProvider(sourceSwitch)); + collection.AddSingleton(new TraceSourceLoggerProvider(sourceSwitch)); - return factory; + return collection; } /// /// Adds a TraceSource logger named 'TraceSource' to the factory. /// - /// The to use. + /// The to use. /// The to use. /// The to use. - public static LoggerFactory AddTraceSource( - this LoggerFactory factory, + public static IServiceCollection AddTraceSource( + this IServiceCollection collection, SourceSwitch sourceSwitch, TraceListener listener) { - if (factory == null) + if (collection == null) { - throw new ArgumentNullException(nameof(factory)); + throw new ArgumentNullException(nameof(collection)); } if (sourceSwitch == null) @@ -110,9 +111,9 @@ public static LoggerFactory AddTraceSource( throw new ArgumentNullException(nameof(listener)); } - factory.AddProvider("TraceSource", new TraceSourceLoggerProvider(sourceSwitch, listener)); + collection.AddSingleton(new TraceSourceLoggerProvider(sourceSwitch, listener)); - return factory; + return collection; } /// diff --git a/src/Microsoft.Extensions.Logging/LogMessageFilter.cs b/src/Microsoft.Extensions.Logging/LogMessageFilter.cs new file mode 100644 index 00000000..c6a7bfa2 --- /dev/null +++ b/src/Microsoft.Extensions.Logging/LogMessageFilter.cs @@ -0,0 +1,4 @@ +namespace Microsoft.Extensions.Logging +{ + public delegate bool LogMessageFilter(string loggerType, string categoryName, LogLevel level); +} \ No newline at end of file diff --git a/src/Microsoft.Extensions.Logging/Logger.cs b/src/Microsoft.Extensions.Logging/Logger.cs index b320883a..c9c5f4cd 100644 --- a/src/Microsoft.Extensions.Logging/Logger.cs +++ b/src/Microsoft.Extensions.Logging/Logger.cs @@ -9,59 +9,19 @@ namespace Microsoft.Extensions.Logging { internal class Logger : ILogger { - private readonly LoggerFactory _loggerFactory; - private readonly string _categoryName; - private LoggerInformation[] _loggers; - private readonly Func _categoryFilter; - - public Logger(LoggerFactory loggerFactory, string categoryName, Func categoryFilter) - { - _loggerFactory = loggerFactory; - _categoryName = categoryName; - - var providers = loggerFactory.GetProviders(); - if (providers.Length > 0) - { - _loggers = new LoggerInformation[providers.Length]; - for (var index = 0; index < providers.Length; index++) - { - _loggers[index] = new LoggerInformation - { - Logger = providers[index].Key.CreateLogger(categoryName), - // Order of preference - // 1. Custom Name - // 2. Provider FullName - ProviderNames = new List - { - providers[index].Value, - providers[index].Key.GetType().FullName - } - }; - } - } - - _categoryFilter = categoryFilter; - } + public LoggerInformation[] Loggers { get; set; } public void Log(LogLevel logLevel, EventId eventId, TState state, Exception exception, Func formatter) { - if (_loggers == null) + if (Loggers == null) { return; } List exceptions = null; - foreach (var loggerInfo in _loggers) + foreach (var loggerInfo in Loggers) { - // TODO: Try to noop if no filters set - if (!_categoryFilter(loggerInfo.ProviderNames[0], logLevel) || - !_categoryFilter(loggerInfo.ProviderNames[1], logLevel)) - { - continue; - } - - // checks config and filters set on the LoggerFactory - if (!_loggerFactory.IsEnabled(loggerInfo.ProviderNames, _categoryName, logLevel)) + if (!loggerInfo.IsEnabled(logLevel)) { continue; } @@ -90,24 +50,17 @@ public void Log(LogLevel logLevel, EventId eventId, TState state, Except public bool IsEnabled(LogLevel logLevel) { - if (_loggers == null) + if (Loggers == null) { return false; } List exceptions = null; - foreach (var loggerInfo in _loggers) + foreach (var loggerInfo in Loggers) { - if (!_categoryFilter(loggerInfo.ProviderNames[0], logLevel) || - !_categoryFilter(loggerInfo.ProviderNames[1], logLevel)) + if (!loggerInfo.IsEnabled(logLevel)) { - continue; - } - - // checks config and filters set on the LoggerFactory - if (!_loggerFactory.IsEnabled(loggerInfo.ProviderNames, _categoryName, logLevel)) - { - continue; + return false; } try @@ -140,17 +93,17 @@ public bool IsEnabled(LogLevel logLevel) public IDisposable BeginScope(TState state) { - if (_loggers == null) + if (Loggers == null) { return NullScope.Instance; } - if (_loggers.Length == 1) + if (Loggers.Length == 1) { - return _loggers[0].Logger.BeginScope(state); + return Loggers[0].Logger.BeginScope(state); } - var loggers = _loggers; + var loggers = Loggers; var scope = new Scope(loggers.Length); List exceptions = null; @@ -181,33 +134,6 @@ public IDisposable BeginScope(TState state) return scope; } - internal void AddProvider(string providerName, ILoggerProvider provider) - { - var logger = provider.CreateLogger(_categoryName); - int logIndex; - if (_loggers == null) - { - logIndex = 0; - _loggers = new LoggerInformation[1]; - } - else - { - logIndex = _loggers.Length; - Array.Resize(ref _loggers, logIndex + 1); - } - _loggers[logIndex] = new LoggerInformation - { - Logger = logger, - // Order of preference - // 1. Custom Name - // 2. Provider FullName - ProviderNames = new List - { - providerName, - provider.GetType().FullName - } - }; - } private class Scope : IDisposable { @@ -275,10 +201,30 @@ internal void Add(IDisposable disposable) } } - private struct LoggerInformation + public struct LoggerInformation { public ILogger Logger { get; set; } - public List ProviderNames { get; set; } + + public string Category { get; set; } + + public LogLevel? MinLevel { get; set; } + + public LogMessageFilter Filter { get; set; } + + public bool IsEnabled(LogLevel level) + { + if (MinLevel != null && level < MinLevel) + { + return false; + } + + if (Filter != null) + { + return Filter(Logger.GetType().FullName, Category, level); + } + + return true; + } } } } \ No newline at end of file diff --git a/src/Microsoft.Extensions.Logging/LoggerFactory.cs b/src/Microsoft.Extensions.Logging/LoggerFactory.cs index 06de9be5..ce3a8f9c 100644 --- a/src/Microsoft.Extensions.Logging/LoggerFactory.cs +++ b/src/Microsoft.Extensions.Logging/LoggerFactory.cs @@ -1,541 +1,541 @@ -// Copyright (c) .NET Foundation. All rights reserved. -// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. - -using System; -using System.Collections.Generic; -using System.Linq; -using Microsoft.Extensions.Configuration; -using Microsoft.Extensions.Primitives; - -namespace Microsoft.Extensions.Logging -{ - /// - /// Summary description for LoggerFactory - /// - public class LoggerFactory : ILoggerFactory - { - private readonly Dictionary _loggers = new Dictionary(StringComparer.Ordinal); - private KeyValuePair[] _providers = new KeyValuePair[0]; - private readonly object _sync = new object(); - private volatile bool _disposed; - private IConfiguration _configuration; - private IChangeToken _changeToken; - private IDisposable _changeTokenRegistration; - private Dictionary _defaultFilter; - private Func _genericFilters; - private Dictionary> _providerFilters = new Dictionary>(); - private Dictionary> _categoryFilters = new Dictionary>(); - - private static readonly Func _trueFilter = (providerName, category, level) => true; - private static readonly Func _categoryTrueFilter = (n, l) => true; - - public LoggerFactory() - { - _genericFilters = _trueFilter; - } - - public LoggerFactory(IConfiguration configuration) - : this() - { - if (configuration == null) - { - throw new ArgumentNullException(nameof(configuration)); - } - - UseConfiguration(configuration); - } - - /// - /// Replaces the used for filtering. - /// - /// The new configuration to use. - /// The so that additional calls can be chained. - public LoggerFactory UseConfiguration(IConfiguration configuration) - { - if (configuration == _configuration) - { - return this; - } - - // unregister the previous configuration callback if there was one - _changeTokenRegistration?.Dispose(); - - _configuration = configuration; - - if (configuration == null) - { - _changeToken = null; - _changeTokenRegistration = null; - } - else - { - _changeToken = _configuration.GetReloadToken(); - _changeTokenRegistration = _changeToken?.RegisterChangeCallback(OnConfigurationReload, null); - } - - LoadDefaultConfigValues(); - - return this; - } - - public ILogger CreateLogger(string categoryName) - { - if (CheckDisposed()) - { - throw new ObjectDisposedException(nameof(LoggerFactory)); - } - - Logger logger; - lock (_sync) - { - if (!_loggers.TryGetValue(categoryName, out logger)) - { - Func filter = _categoryTrueFilter; - foreach (var prefix in GetKeyPrefixes(categoryName)) - { - if (_categoryFilters.TryGetValue(prefix, out var categoryFilter)) - { - var previousFilter = filter; - filter = (providerName, level) => - { - if (previousFilter(providerName, level)) - { - return categoryFilter(providerName, level); - } - - return false; - }; - } - } - logger = new Logger(this, categoryName, filter); - _loggers[categoryName] = logger; - } - } - - return logger; - } - - public void AddProvider(ILoggerProvider provider) - { - // REVIEW: Should we do the name resolution for our providers like this? - var name = string.Empty; - switch (provider.GetType().FullName) - { - case "Microsoft.Extensions.Logging.ConsoleLoggerProvider": - name = "Console"; - break; - case "Microsoft.Extensions.Logging.DebugLoggerProvider": - name = "Debug"; - break; - case "Microsoft.Extensions.Logging.AzureAppServices.Internal.AzureAppServicesDiagnosticsLoggerProvider": - name = "AzureAppServices"; - break; - case "Microsoft.Extensions.Logging.EventLog.EventLogLoggerProvider": - name = "EventLog"; - break; - case "Microsoft.Extensions.Logging.TraceSource.TraceSourceLoggerProvider": - name = "TraceSource"; - break; - case "Microsoft.Extensions.Logging.EventSource.EventSourceLoggerProvider": - name = "EventSource"; - break; - } - - AddProvider(name, provider); - } - - public void AddProvider(string providerName, ILoggerProvider provider) - { - if (CheckDisposed()) - { - throw new ObjectDisposedException(nameof(LoggerFactory)); - } - - lock (_sync) - { - _providers = _providers.Concat(new[] { new KeyValuePair(provider, providerName) }).ToArray(); - - foreach (var logger in _loggers) - { - logger.Value.AddProvider(providerName, provider); - } - } - } - - /// - /// Adds a filter that applies to and with the given - /// . - /// - /// The name of the provider. - /// The name of the logger category. - /// The filter that applies to logs for and . - /// Returning true means allow log through, false means reject log. - /// The so that additional calls can be chained. - public LoggerFactory AddFilter(string providerName, string categoryName, Func filter) - { - if (filter == null) - { - throw new ArgumentNullException(nameof(filter)); - } - - lock (_sync) - { - if (_categoryFilters.TryGetValue(categoryName, out var previousFilter)) - { - _categoryFilters[categoryName] = (currentProviderName, level) => - { - if (previousFilter(currentProviderName, level)) - { - if (string.Equals(providerName, currentProviderName)) - { - return filter(level); - } - - return true; - } - - return false; - }; - } - else - { - _categoryFilters[categoryName] = (currentProviderName, level) => - { - if (string.Equals(providerName, currentProviderName)) - { - return filter(level); - } - - return true; - }; - } - } - - return this; - } - - /// - /// Adds a filter that applies to with the given . - /// - /// The name of the provider. - /// The filter that applies to logs for . - /// The string argument is the category being logged to. - /// Returning true means allow log through, false means reject log. - /// The so that additional calls can be chained. - public LoggerFactory AddFilter(string providerName, Func filter) - { - if (filter == null) - { - throw new ArgumentNullException(nameof(filter)); - } - - lock (_sync) - { - if (_providerFilters.TryGetValue(providerName, out var value)) - { - _providerFilters[providerName] = (categoryName, level) => - { - if (value(categoryName, level)) - { - return filter(categoryName, level); - } - - return false; - }; - } - else - { - _providerFilters[providerName] = (category, level) => filter(category, level); - } - } - - return this; - } - - /// - /// Adds a filter that applies to all logs. - /// - /// The filter that applies to logs. - /// The first string is the provider name and the second string is the category name being logged to. - /// Returning true means allow log through, false means reject log. - /// The so that additional calls can be chained. - public LoggerFactory AddFilter(Func filter) - { - if (filter == null) - { - throw new ArgumentNullException(nameof(filter)); - } - - lock (_sync) - { - var previousFilters = _genericFilters; - _genericFilters = (providerName, category, level) => - { - if (previousFilters(providerName, category, level)) - { - return filter(providerName, category, level); - } - - return false; - }; - } - - return this; - } - - /// - /// Adds a filter to all logs. - /// - /// The filter that applies to logs. - /// The key is the category and the is the minimum level allowed. - /// The so that additional calls can be chained. - public LoggerFactory AddFilter(IDictionary filter) - { - if (filter == null) - { - throw new ArgumentNullException(nameof(filter)); - } - - lock (_sync) - { - foreach (var kvp in filter) - { - if (_categoryFilters.TryGetValue(kvp.Key, out var currentFilter)) - { - _categoryFilters[kvp.Key] = (providerName, level) => - { - if (currentFilter(providerName, level)) - { - return level >= kvp.Value; - } - - return false; - }; - } - else - { - _categoryFilters[kvp.Key] = (providerName, level) => level >= kvp.Value; - } - } - } - - return this; - } - - /// - /// Adds a filter that applies to and , allowing logs with the given - /// minimum or higher. - /// - /// The name of the provider. - /// The name of the logger category. - /// The minimum that logs from - /// and are allowed. - public LoggerFactory AddFilter(string providerName, string categoryName, LogLevel minLevel) - { - return AddFilter(providerName, categoryName, level => level >= minLevel); - } - - /// - /// Adds a filter that applies to with the given - /// . - /// - /// The name of the provider. - /// The filter that applies to logs for . - /// Returning true means allow log through, false means reject log. - public LoggerFactory AddFilter(string providerName, Func filter) - { - if (filter == null) - { - throw new ArgumentNullException(nameof(filter)); - } - - // Using 'Default' for the category name means this filter will apply for all category names - return AddFilter(providerName, "Default", filter); - } - - // TODO: Figure out how to do this better, perhaps a new IConfigurableLogger interface? - public IConfiguration Configuration => _configuration; - - internal KeyValuePair[] GetProviders() - { - return _providers; - } - - internal bool IsEnabled(List providerNames, string categoryName, LogLevel currentLevel) - { - if (_genericFilters != _trueFilter || _providerFilters.Count > 0) - { - foreach (var providerName in providerNames) - { - if (string.IsNullOrEmpty(providerName)) - { - continue; - } - - if (_providerFilters.TryGetValue(providerName, out var filter)) - { - if (!filter(categoryName, currentLevel)) - { - return false; - } - } - - if (_genericFilters != _trueFilter) - { - // filters from factory.AddFilter(Func) - if (!_genericFilters(providerName, categoryName, currentLevel)) - { - return false; - } - } - } - } - - if (_configuration != null) - { - // need to loop over this separately because _filters can apply to multiple providerNames - // but the configuration prefers early providerNames and will early out if a match is found - foreach (var providerName in providerNames) - { - // TODO: Caching? - var logLevelSection = _configuration.GetSection($"{providerName}:LogLevel"); - if (logLevelSection != null) - { - foreach (var prefix in GetKeyPrefixes(categoryName)) - { - if (TryGetSwitch(logLevelSection[prefix], out var configLevel)) - { - return currentLevel >= configLevel; - } - } - } - } - } - - if (_defaultFilter == null) - { - return true; - } - - // get a local reference to the filter so that if the config is reloaded then `_defaultFilter` - // doesn't change while we are accessing it - var localDefaultFilter = _defaultFilter; - - // No specific filter for this logger, check defaults - foreach (var prefix in GetKeyPrefixes(categoryName)) - { - if (localDefaultFilter.TryGetValue(prefix, out var defaultLevel)) - { - return currentLevel >= defaultLevel; - } - } - - return true; - } - - private void OnConfigurationReload(object state) - { - _changeToken = _configuration.GetReloadToken(); - try - { - LoadDefaultConfigValues(); - } - catch (Exception /*ex*/) - { - // TODO: Can we do anything? - //Console.WriteLine($"Error while loading configuration changes.{Environment.NewLine}{ex}"); - } - finally - { - // The token will change each time it reloads, so we need to register again. - _changeTokenRegistration = _changeToken.RegisterChangeCallback(OnConfigurationReload, null); - } - } - - private static bool TryGetSwitch(string value, out LogLevel level) - { - if (string.IsNullOrEmpty(value)) - { - level = LogLevel.None; - return false; - } - else if (Enum.TryParse(value, true, out level)) - { - return true; - } - else - { - var message = $"Configuration value '{value}' is not supported."; - throw new InvalidOperationException(message); - } - } - - private static IEnumerable GetKeyPrefixes(string name) - { - while (!string.IsNullOrEmpty(name)) - { - yield return name; - var lastIndexOfDot = name.LastIndexOf('.'); - if (lastIndexOfDot == -1) - { - yield return "Default"; - break; - } - name = name.Substring(0, lastIndexOfDot); - } - } - - private void LoadDefaultConfigValues() - { - var replacementDefaultFilters = new Dictionary(); - if (_configuration == null) - { - _defaultFilter = replacementDefaultFilters; - return; - } - - var logLevelSection = _configuration.GetSection("LogLevel"); - - if (logLevelSection != null) - { - foreach (var section in logLevelSection.AsEnumerable(true)) - { - if (TryGetSwitch(section.Value, out var level)) - { - replacementDefaultFilters[section.Key] = level; - } - } - } - - _defaultFilter = replacementDefaultFilters; - } - - /// - /// Check if the factory has been disposed. - /// - /// True when as been called - protected virtual bool CheckDisposed() => _disposed; - - public void Dispose() - { - if (!_disposed) - { - _disposed = true; - - _changeTokenRegistration?.Dispose(); - - foreach (var provider in _providers) - { - try - { - provider.Key.Dispose(); - } - catch - { - // Swallow exceptions on dispose - } - } - } - } - } -} \ No newline at end of file +//// Copyright (c) .NET Foundation. All rights reserved. +//// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +//using System; +//using System.Collections.Generic; +//using System.Linq; +//using Microsoft.Extensions.Configuration; +//using Microsoft.Extensions.Primitives; + +//namespace Microsoft.Extensions.Logging +//{ +// /// +// /// Summary description for LoggerFactory +// /// +// public class LoggerFactory : ILoggerFactory +// { +// private readonly Dictionary _loggers = new Dictionary(StringComparer.Ordinal); +// private KeyValuePair[] _providers = new KeyValuePair[0]; +// private readonly object _sync = new object(); +// private volatile bool _disposed; +// private IConfiguration _configuration; +// private IChangeToken _changeToken; +// private IDisposable _changeTokenRegistration; +// private Dictionary _defaultFilter; +// private Func _genericFilters; +// private Dictionary> _providerFilters = new Dictionary>(); +// private Dictionary> _categoryFilters = new Dictionary>(); + +// private static readonly Func _trueFilter = (providerName, category, level) => true; +// private static readonly Func _categoryTrueFilter = (n, l) => true; + +// public LoggerFactory() +// { +// _genericFilters = _trueFilter; +// } + +// public LoggerFactory(IConfiguration configuration) +// : this() +// { +// if (configuration == null) +// { +// throw new ArgumentNullException(nameof(configuration)); +// } + +// UseConfiguration(configuration); +// } + +// /// +// /// Replaces the used for filtering. +// /// +// /// The new configuration to use. +// /// The so that additional calls can be chained. +// public LoggerFactory UseConfiguration(IConfiguration configuration) +// { +// if (configuration == _configuration) +// { +// return this; +// } + +// // unregister the previous configuration callback if there was one +// _changeTokenRegistration?.Dispose(); + +// _configuration = configuration; + +// if (configuration == null) +// { +// _changeToken = null; +// _changeTokenRegistration = null; +// } +// else +// { +// _changeToken = _configuration.GetReloadToken(); +// _changeTokenRegistration = _changeToken?.RegisterChangeCallback(OnConfigurationReload, null); +// } + +// LoadDefaultConfigValues(); + +// return this; +// } + +// public ILogger CreateLogger(string categoryName) +// { +// if (CheckDisposed()) +// { +// throw new ObjectDisposedException(nameof(LoggerFactory)); +// } + +// Logger logger; +// lock (_sync) +// { +// if (!_loggers.TryGetValue(categoryName, out logger)) +// { +// Func filter = _categoryTrueFilter; +// foreach (var prefix in GetKeyPrefixes(categoryName)) +// { +// if (_categoryFilters.TryGetValue(prefix, out var categoryFilter)) +// { +// var previousFilter = filter; +// filter = (providerName, level) => +// { +// if (previousFilter(providerName, level)) +// { +// return categoryFilter(providerName, level); +// } + +// return false; +// }; +// } +// } +// logger = new Logger(this, categoryName, filter); +// _loggers[categoryName] = logger; +// } +// } + +// return logger; +// } + +// public void AddProvider(ILoggerProvider provider) +// { +// // REVIEW: Should we do the name resolution for our providers like this? +// var name = string.Empty; +// switch (provider.GetType().FullName) +// { +// case "Microsoft.Extensions.Logging.ConsoleLoggerProvider": +// name = "Console"; +// break; +// case "Microsoft.Extensions.Logging.DebugLoggerProvider": +// name = "Debug"; +// break; +// case "Microsoft.Extensions.Logging.AzureAppServices.Internal.AzureAppServicesDiagnosticsLoggerProvider": +// name = "AzureAppServices"; +// break; +// case "Microsoft.Extensions.Logging.EventLog.EventLogLoggerProvider": +// name = "EventLog"; +// break; +// case "Microsoft.Extensions.Logging.TraceSource.TraceSourceLoggerProvider": +// name = "TraceSource"; +// break; +// case "Microsoft.Extensions.Logging.EventSource.EventSourceLoggerProvider": +// name = "EventSource"; +// break; +// } + +// AddProvider(name, provider); +// } + +// public void AddProvider(string providerName, ILoggerProvider provider) +// { +// if (CheckDisposed()) +// { +// throw new ObjectDisposedException(nameof(LoggerFactory)); +// } + +// lock (_sync) +// { +// _providers = _providers.Concat(new[] { new KeyValuePair(provider, providerName) }).ToArray(); + +// foreach (var logger in _loggers) +// { +// logger.Value.AddProvider(providerName, provider); +// } +// } +// } + +// /// +// /// Adds a filter that applies to and with the given +// /// . +// /// +// /// The name of the provider. +// /// The name of the logger category. +// /// The filter that applies to logs for and . +// /// Returning true means allow log through, false means reject log. +// /// The so that additional calls can be chained. +// public LoggerFactory AddFilter(string providerName, string categoryName, Func filter) +// { +// if (filter == null) +// { +// throw new ArgumentNullException(nameof(filter)); +// } + +// lock (_sync) +// { +// if (_categoryFilters.TryGetValue(categoryName, out var previousFilter)) +// { +// _categoryFilters[categoryName] = (currentProviderName, level) => +// { +// if (previousFilter(currentProviderName, level)) +// { +// if (string.Equals(providerName, currentProviderName)) +// { +// return filter(level); +// } + +// return true; +// } + +// return false; +// }; +// } +// else +// { +// _categoryFilters[categoryName] = (currentProviderName, level) => +// { +// if (string.Equals(providerName, currentProviderName)) +// { +// return filter(level); +// } + +// return true; +// }; +// } +// } + +// return this; +// } + +// /// +// /// Adds a filter that applies to with the given . +// /// +// /// The name of the provider. +// /// The filter that applies to logs for . +// /// The string argument is the category being logged to. +// /// Returning true means allow log through, false means reject log. +// /// The so that additional calls can be chained. +// public LoggerFactory AddFilter(string providerName, Func filter) +// { +// if (filter == null) +// { +// throw new ArgumentNullException(nameof(filter)); +// } + +// lock (_sync) +// { +// if (_providerFilters.TryGetValue(providerName, out var value)) +// { +// _providerFilters[providerName] = (categoryName, level) => +// { +// if (value(categoryName, level)) +// { +// return filter(categoryName, level); +// } + +// return false; +// }; +// } +// else +// { +// _providerFilters[providerName] = (category, level) => filter(category, level); +// } +// } + +// return this; +// } + +// /// +// /// Adds a filter that applies to all logs. +// /// +// /// The filter that applies to logs. +// /// The first string is the provider name and the second string is the category name being logged to. +// /// Returning true means allow log through, false means reject log. +// /// The so that additional calls can be chained. +// public LoggerFactory AddFilter(Func filter) +// { +// if (filter == null) +// { +// throw new ArgumentNullException(nameof(filter)); +// } + +// lock (_sync) +// { +// var previousFilters = _genericFilters; +// _genericFilters = (providerName, category, level) => +// { +// if (previousFilters(providerName, category, level)) +// { +// return filter(providerName, category, level); +// } + +// return false; +// }; +// } + +// return this; +// } + +// /// +// /// Adds a filter to all logs. +// /// +// /// The filter that applies to logs. +// /// The key is the category and the is the minimum level allowed. +// /// The so that additional calls can be chained. +// public LoggerFactory AddFilter(IDictionary filter) +// { +// if (filter == null) +// { +// throw new ArgumentNullException(nameof(filter)); +// } + +// lock (_sync) +// { +// foreach (var kvp in filter) +// { +// if (_categoryFilters.TryGetValue(kvp.Key, out var currentFilter)) +// { +// _categoryFilters[kvp.Key] = (providerName, level) => +// { +// if (currentFilter(providerName, level)) +// { +// return level >= kvp.Value; +// } + +// return false; +// }; +// } +// else +// { +// _categoryFilters[kvp.Key] = (providerName, level) => level >= kvp.Value; +// } +// } +// } + +// return this; +// } + +// /// +// /// Adds a filter that applies to and , allowing logs with the given +// /// minimum or higher. +// /// +// /// The name of the provider. +// /// The name of the logger category. +// /// The minimum that logs from +// /// and are allowed. +// public LoggerFactory AddFilter(string providerName, string categoryName, LogLevel minLevel) +// { +// return AddFilter(providerName, categoryName, level => level >= minLevel); +// } + +// /// +// /// Adds a filter that applies to with the given +// /// . +// /// +// /// The name of the provider. +// /// The filter that applies to logs for . +// /// Returning true means allow log through, false means reject log. +// public LoggerFactory AddFilter(string providerName, Func filter) +// { +// if (filter == null) +// { +// throw new ArgumentNullException(nameof(filter)); +// } + +// // Using 'Default' for the category name means this filter will apply for all category names +// return AddFilter(providerName, "Default", filter); +// } + +// // TODO: Figure out how to do this better, perhaps a new IConfigurableLogger interface? +// public IConfiguration Configuration => _configuration; + +// internal KeyValuePair[] GetProviders() +// { +// return _providers; +// } + +// internal bool IsEnabled(List providerNames, string categoryName, LogLevel currentLevel) +// { +// if (_genericFilters != _trueFilter || _providerFilters.Count > 0) +// { +// foreach (var providerName in providerNames) +// { +// if (string.IsNullOrEmpty(providerName)) +// { +// continue; +// } + +// if (_providerFilters.TryGetValue(providerName, out var filter)) +// { +// if (!filter(categoryName, currentLevel)) +// { +// return false; +// } +// } + +// if (_genericFilters != _trueFilter) +// { +// // filters from factory.AddFilter(Func) +// if (!_genericFilters(providerName, categoryName, currentLevel)) +// { +// return false; +// } +// } +// } +// } + +// if (_configuration != null) +// { +// // need to loop over this separately because _filters can apply to multiple providerNames +// // but the configuration prefers early providerNames and will early out if a match is found +// foreach (var providerName in providerNames) +// { +// // TODO: Caching? +// var logLevelSection = _configuration.GetSection($"{providerName}:LogLevel"); +// if (logLevelSection != null) +// { +// foreach (var prefix in GetKeyPrefixes(categoryName)) +// { +// if (TryGetSwitch(logLevelSection[prefix], out var configLevel)) +// { +// return currentLevel >= configLevel; +// } +// } +// } +// } +// } + +// if (_defaultFilter == null) +// { +// return true; +// } + +// // get a local reference to the filter so that if the config is reloaded then `_defaultFilter` +// // doesn't change while we are accessing it +// var localDefaultFilter = _defaultFilter; + +// // No specific filter for this logger, check defaults +// foreach (var prefix in GetKeyPrefixes(categoryName)) +// { +// if (localDefaultFilter.TryGetValue(prefix, out var defaultLevel)) +// { +// return currentLevel >= defaultLevel; +// } +// } + +// return true; +// } + +// private void OnConfigurationReload(object state) +// { +// _changeToken = _configuration.GetReloadToken(); +// try +// { +// LoadDefaultConfigValues(); +// } +// catch (Exception /*ex*/) +// { +// // TODO: Can we do anything? +// //Console.WriteLine($"Error while loading configuration changes.{Environment.NewLine}{ex}"); +// } +// finally +// { +// // The token will change each time it reloads, so we need to register again. +// _changeTokenRegistration = _changeToken.RegisterChangeCallback(OnConfigurationReload, null); +// } +// } + +// private static bool TryGetSwitch(string value, out LogLevel level) +// { +// if (string.IsNullOrEmpty(value)) +// { +// level = LogLevel.None; +// return false; +// } +// else if (Enum.TryParse(value, true, out level)) +// { +// return true; +// } +// else +// { +// var message = $"Configuration value '{value}' is not supported."; +// throw new InvalidOperationException(message); +// } +// } + +// private static IEnumerable GetKeyPrefixes(string name) +// { +// while (!string.IsNullOrEmpty(name)) +// { +// yield return name; +// var lastIndexOfDot = name.LastIndexOf('.'); +// if (lastIndexOfDot == -1) +// { +// yield return "Default"; +// break; +// } +// name = name.Substring(0, lastIndexOfDot); +// } +// } + +// private void LoadDefaultConfigValues() +// { +// var replacementDefaultFilters = new Dictionary(); +// if (_configuration == null) +// { +// _defaultFilter = replacementDefaultFilters; +// return; +// } + +// var logLevelSection = _configuration.GetSection("LogLevel"); + +// if (logLevelSection != null) +// { +// foreach (var section in logLevelSection.AsEnumerable(true)) +// { +// if (TryGetSwitch(section.Value, out var level)) +// { +// replacementDefaultFilters[section.Key] = level; +// } +// } +// } + +// _defaultFilter = replacementDefaultFilters; +// } + +// /// +// /// Check if the factory has been disposed. +// /// +// /// True when as been called +// protected virtual bool CheckDisposed() => _disposed; + +// public void Dispose() +// { +// if (!_disposed) +// { +// _disposed = true; + +// _changeTokenRegistration?.Dispose(); + +// foreach (var provider in _providers) +// { +// try +// { +// provider.Key.Dispose(); +// } +// catch +// { +// // Swallow exceptions on dispose +// } +// } +// } +// } +// } +//} \ No newline at end of file diff --git a/src/Microsoft.Extensions.Logging/LoggerFactory1.cs b/src/Microsoft.Extensions.Logging/LoggerFactory1.cs new file mode 100644 index 00000000..3ac5e243 --- /dev/null +++ b/src/Microsoft.Extensions.Logging/LoggerFactory1.cs @@ -0,0 +1,146 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using Microsoft.Extensions.Options; + +namespace Microsoft.Extensions.Logging +{ + public class LoggerFactory : ILoggerFactory + { + private static readonly LoggerRuleSelector RuleSelector = new LoggerRuleSelector(); + + private readonly Dictionary _loggers = new Dictionary(StringComparer.Ordinal); + + private readonly List _providers; + private readonly object _sync = new object(); + private volatile bool _disposed; + private IDisposable _changeTokenRegistration; + private LoggerFilterOptions _filterOptions; + + public LoggerFactory() : this(new LoggerFilterOptions()) + { + } + + public LoggerFactory(LoggerFilterOptions filterOptions) : this(Enumerable.Empty(), new StaticFilterOptionsMonitor(filterOptions)) + { + } + + public LoggerFactory(IEnumerable providers, IOptionsMonitor filterOption) + { + _providers = providers.ToList(); + _changeTokenRegistration = filterOption.OnChange(RefreshFilters); + RefreshFilters(filterOption.CurrentValue); + } + + private void RefreshFilters(LoggerFilterOptions filterOptions) + { + lock (_sync) + { + _filterOptions = filterOptions; + foreach (var logger in _loggers) + { + var loggerInformation = logger.Value.Loggers; + var categoryName = logger.Key; + + ApplyRules(loggerInformation, categoryName, 0, loggerInformation.Length); + } + } + } + + public ILogger CreateLogger(string categoryName) + { + lock (_sync) + { + Logger logger; + + if (!_loggers.TryGetValue(categoryName, out logger)) + { + + logger = new Logger() + { + Loggers = CreateLoggers(categoryName) + }; + _loggers[categoryName] = logger; + } + + return logger; + } + } + + void ILoggerFactory.AddProvider(ILoggerProvider provider) + { + lock (_sync) + { + _providers.Add(provider); + foreach (var logger in _loggers) + { + var loggerInformation = logger.Value.Loggers; + var categoryName = logger.Key; + + Array.Resize(ref loggerInformation, loggerInformation.Length + 1); + var newLoggerIndex = loggerInformation.Length - 1; + loggerInformation[newLoggerIndex].Logger = provider.CreateLogger(categoryName); + + ApplyRules(loggerInformation, categoryName, newLoggerIndex, 1); + } + } + } + + private Logger.LoggerInformation[] CreateLoggers(string categoryName) + { + Logger.LoggerInformation[] loggers = new Logger.LoggerInformation[_providers.Count]; + for (int i = 0; i < _providers.Count; i++) + { + loggers[i].Logger = _providers[i].CreateLogger(categoryName); + } + + ApplyRules(loggers, categoryName, 0, loggers.Length); + return loggers; + } + + private void ApplyRules(Logger.LoggerInformation[] loggers, string categoryName, int start, int count) + { + for (var index = start; index < start + count; index++) + { + ref var loggerInformation = ref loggers[index]; + + RuleSelector.Select(_filterOptions, + loggerInformation.Logger.GetType().FullName, + categoryName, + out var minLevel, + out var filter); + + loggerInformation.MinLevel = minLevel; + loggerInformation.Filter = filter; + } + } + + /// + /// Check if the factory has been disposed. + /// + /// True when as been called + protected virtual bool CheckDisposed() => _disposed; + + public void Dispose() + { + if (!_disposed) + { + _disposed = true; + + _changeTokenRegistration?.Dispose(); + + foreach (var provider in _providers) + { + try + { + provider.Dispose(); + } + catch + { + // Swallow exceptions on dispose + } + } + } + } + } +} \ No newline at end of file diff --git a/src/Microsoft.Extensions.Logging/LoggerFilterOptions.cs b/src/Microsoft.Extensions.Logging/LoggerFilterOptions.cs new file mode 100644 index 00000000..ca417901 --- /dev/null +++ b/src/Microsoft.Extensions.Logging/LoggerFilterOptions.cs @@ -0,0 +1,11 @@ +using System.Collections.Generic; + +namespace Microsoft.Extensions.Logging +{ + public class LoggerFilterOptions + { + public LogLevel MinLevel { get; set; } = LogLevel.Information; + + public ICollection Rules { get; } = new List(); + } +} \ No newline at end of file diff --git a/src/Microsoft.Extensions.Logging/LoggerFilterOptionsConfigurationSetup.cs b/src/Microsoft.Extensions.Logging/LoggerFilterOptionsConfigurationSetup.cs new file mode 100644 index 00000000..e7ab18da --- /dev/null +++ b/src/Microsoft.Extensions.Logging/LoggerFilterOptionsConfigurationSetup.cs @@ -0,0 +1,105 @@ +using System; +using System.Collections.Generic; +using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.Options; + +namespace Microsoft.Extensions.Logging +{ + // TODO: Reload support + public class LoggerFilterOptionsConfigurationSetup : IConfigureOptions + { + private readonly IConfiguration _configuration; + + public LoggerFilterOptionsConfigurationSetup(IConfiguration configuration) + { + _configuration = configuration; + } + + public void Configure(LoggerFilterOptions options) + { + LoadDefaultConfigValues(options.Rules); + } + + private void LoadDefaultConfigValues(ICollection rules) + { + if (_configuration == null) + { + return; + } + + foreach (var configurationSection in _configuration.GetChildren()) + { + if (configurationSection.Key == "LogLevel") + { + // Load global category defaults + LoadRules(rules, configurationSection, null); + } + else + { + var logLevelSection = configurationSection.GetSection("LogLevel"); + if (logLevelSection != null) + { + // Load logger specific rules + var logger = ExpandLoggerAlias(configurationSection.Key); + LoadRules(rules, configurationSection, logger); + } + } + } + } + + private static void LoadRules(ICollection rules, IConfigurationSection configurationSection, string logger) + { + foreach (var section in configurationSection.AsEnumerable(true)) + { + if (TryGetSwitch(section.Value, out var level)) + { + rules.Add(new LoggerFilterRule(logger, section.Key, level, null)); + } + } + } + + public string ExpandLoggerAlias(string name) + { + switch (name) + { + case "Console": + name = "Microsoft.Extensions.Logging.ConsoleLoggerProvider"; + break; + case "Debug": + name = "Microsoft.Extensions.Logging.DebugLoggerProvider"; + break; + case "AzureAppServices": + name = "Microsoft.Extensions.Logging.AzureAppServices.Internal.AzureAppServicesDiagnosticsLoggerProvider"; + break; + case "EventLog": + name = "Microsoft.Extensions.Logging.EventLog.EventLogLoggerProvider"; + break; + case "TraceSource": + name = "Microsoft.Extensions.Logging.TraceSource.TraceSourceLoggerProvider"; + break; + case "EventSource": + name = "Microsoft.Extensions.Logging.EventSource.EventSourceLoggerProvider"; + break; + } + + return name; + } + + private static bool TryGetSwitch(string value, out LogLevel level) + { + if (string.IsNullOrEmpty(value)) + { + level = LogLevel.None; + return false; + } + else if (Enum.TryParse(value, true, out level)) + { + return true; + } + else + { + throw new InvalidOperationException($"Configuration value '{value}' is not supported."); + } + } + } +} \ No newline at end of file diff --git a/src/Microsoft.Extensions.Logging/LoggerFilterRule.cs b/src/Microsoft.Extensions.Logging/LoggerFilterRule.cs new file mode 100644 index 00000000..a8419632 --- /dev/null +++ b/src/Microsoft.Extensions.Logging/LoggerFilterRule.cs @@ -0,0 +1,23 @@ +using System; + +namespace Microsoft.Extensions.Logging +{ + public class LoggerFilterRule + { + public LoggerFilterRule(string loggerType, string categoryName, LogLevel? logLevel, LogMessageFilter filter) + { + LoggerType = loggerType; + CategoryName = categoryName; + LogLevel = logLevel; + Filter = filter; + } + + public string LoggerType { get; } + + public string CategoryName { get; } + + public LogLevel? LogLevel { get; } + + public LogMessageFilter Filter { get; } + } +} \ No newline at end of file diff --git a/src/Microsoft.Extensions.Logging/LoggerRuleSelector.cs b/src/Microsoft.Extensions.Logging/LoggerRuleSelector.cs new file mode 100644 index 00000000..416379bf --- /dev/null +++ b/src/Microsoft.Extensions.Logging/LoggerRuleSelector.cs @@ -0,0 +1,78 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Runtime.InteropServices; +using Microsoft.Extensions.Options; + +namespace Microsoft.Extensions.Logging +{ + public class LoggerRuleSelector + { + public void Select(LoggerFilterOptions options, string logger, string category, out LogLevel? minLevel, out LogMessageFilter filter) + { + minLevel = null; + filter = null; + + // Filter rule selection: + // 1. Select rules for current logger type, if there is none, select ones without logger type specified + // 2. Select rules with longest matching categories + // 3. If there is only one rule use it's level and filter + // 4. If there are multiple rules combine them using AND operator + // 5. If there are no applicable rules use global minimal level + + var loggerSpecificRules = options.Rules.Where(rule => rule.LoggerType == logger).ToList(); + if (!loggerSpecificRules.Any()) + { + loggerSpecificRules = options.Rules.Where(rule => string.IsNullOrEmpty(rule.LoggerType)).ToList(); + } + + if (loggerSpecificRules.Any()) + { + var categorySpecificRules = loggerSpecificRules + .Where(rule => !string.IsNullOrEmpty(rule.CategoryName) && category.StartsWith(rule.CategoryName, StringComparison.OrdinalIgnoreCase)) + .GroupBy(rule => rule.CategoryName.Length) + .OrderByDescending(group => group.Key) + .FirstOrDefault() + ?.ToList(); + + if (categorySpecificRules?.Any() != true) + { + categorySpecificRules = loggerSpecificRules.Where(rule => string.IsNullOrEmpty(rule.CategoryName)).ToList(); + } + + if (categorySpecificRules.Any()) + { + if (categorySpecificRules.Count == 1) + { + var loggerFilterRule = categorySpecificRules.Single(); + filter = loggerFilterRule.Filter; + minLevel = loggerFilterRule.LogLevel; + } + else + { + // Combine rules, for min level we take maximum of all rules + // for filter delegated we take firs if there is only one or apply AND operator to all + minLevel = categorySpecificRules.Max(rule => rule.LogLevel); + filter = (type, c, level) => + { + foreach (var loggerFilterRule in categorySpecificRules) + { + if (!loggerFilterRule.Filter(type, c, level)) + { + return false; + } + } + + return true; + }; + } + } + else + { + // If there are no rules fallback to global min level + minLevel = options.MinLevel; + } + } + } + } +} \ No newline at end of file diff --git a/src/Microsoft.Extensions.Logging/LoggingServiceCollectionExtensions.cs b/src/Microsoft.Extensions.Logging/LoggingServiceCollectionExtensions.cs index d3335492..dab76a66 100644 --- a/src/Microsoft.Extensions.Logging/LoggingServiceCollectionExtensions.cs +++ b/src/Microsoft.Extensions.Logging/LoggingServiceCollectionExtensions.cs @@ -2,8 +2,10 @@ // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System; +using Microsoft.Extensions.Configuration; using Microsoft.Extensions.Logging; using Microsoft.Extensions.DependencyInjection.Extensions; +using Microsoft.Extensions.Options; namespace Microsoft.Extensions.DependencyInjection { @@ -16,8 +18,20 @@ public static class LoggingServiceCollectionExtensions /// Adds logging services to the specified . /// /// The to add services to. + /// The instance to use fol filter configuration /// The so that additional calls can be chained. public static IServiceCollection AddLogging(this IServiceCollection services) + { + return AddLogging(services, null); + } + + /// + /// Adds logging services to the specified . + /// + /// The to add services to. + /// The instance to use fol filter configuration + /// The so that additional calls can be chained. + public static IServiceCollection AddLogging(this IServiceCollection services, IConfiguration configuration) { if (services == null) { @@ -27,6 +41,11 @@ public static IServiceCollection AddLogging(this IServiceCollection services) services.TryAdd(ServiceDescriptor.Singleton()); services.TryAdd(ServiceDescriptor.Singleton(typeof(ILogger<>), typeof(Logger<>))); + if (configuration != null) + { + services.TryAdd(ServiceDescriptor.Singleton>(new LoggerFilterOptionsConfigurationSetup(configuration))); + services.TryAdd(ServiceDescriptor.Singleton>(new ConfigurationChangeTokenSource(configuration))); + } return services; } } diff --git a/src/Microsoft.Extensions.Logging/Microsoft.Extensions.Logging.csproj b/src/Microsoft.Extensions.Logging/Microsoft.Extensions.Logging.csproj index 31ac32bc..2d0d3432 100644 --- a/src/Microsoft.Extensions.Logging/Microsoft.Extensions.Logging.csproj +++ b/src/Microsoft.Extensions.Logging/Microsoft.Extensions.Logging.csproj @@ -17,6 +17,8 @@ + + diff --git a/src/Microsoft.Extensions.Logging/StaticFilterOptionsMonitor.cs b/src/Microsoft.Extensions.Logging/StaticFilterOptionsMonitor.cs new file mode 100644 index 00000000..448f21d3 --- /dev/null +++ b/src/Microsoft.Extensions.Logging/StaticFilterOptionsMonitor.cs @@ -0,0 +1,20 @@ +using System; +using Microsoft.Extensions.Options; + +namespace Microsoft.Extensions.Logging +{ + internal class StaticFilterOptionsMonitor : IOptionsMonitor + { + public StaticFilterOptionsMonitor(LoggerFilterOptions currentValue) + { + CurrentValue = currentValue; + } + + public IDisposable OnChange(Action listener) + { + return null; + } + + public LoggerFilterOptions CurrentValue { get; } + } +} \ No newline at end of file diff --git a/test/Microsoft.Extensions.Logging.Test/LoggerFactoryTest.cs b/test/Microsoft.Extensions.Logging.Test/LoggerFactoryTest.cs index 969a8a9c..8bd812f1 100644 --- a/test/Microsoft.Extensions.Logging.Test/LoggerFactoryTest.cs +++ b/test/Microsoft.Extensions.Logging.Test/LoggerFactoryTest.cs @@ -88,20 +88,21 @@ public void Dispose_ThrowException_SwallowsException() .Verify(p => p.Dispose(), Times.Once()); } - [Fact] - public void UseConfiguration_RegistersChangeCallback() - { - // Arrange - var factory = new LoggerFactory(); - var changeToken = new Mock(); - var configuration = new Mock(); - configuration.Setup(c => c.GetReloadToken()).Returns(changeToken.Object); + // TODO: Replace with reload test + //[Fact] + //public void UseConfiguration_RegistersChangeCallback() + //{ + // // Arrange + // var factory = new LoggerFactory(); + // var changeToken = new Mock(); + // var configuration = new Mock(); + // configuration.Setup(c => c.GetReloadToken()).Returns(changeToken.Object); - // Act - factory.UseConfiguration(configuration.Object); + // // Act + // factory.UseConfiguration(configuration.Object); - // Assert - changeToken.Verify(c => c.RegisterChangeCallback(It.IsAny>(), It.IsAny()), Times.Once); - } + // // Assert + // changeToken.Verify(c => c.RegisterChangeCallback(It.IsAny>(), It.IsAny()), Times.Once); + //} } } From 3aaa06d0bf2f82ef7aeac22c99f6e070bdff4952 Mon Sep 17 00:00:00 2001 From: Pavel Krymets Date: Mon, 22 May 2017 16:55:48 -0700 Subject: [PATCH 02/20] All things in the world --- build/common.props | 2 + samples/SampleApp/Program.cs | 26 +- samples/SampleApp/SampleApp.csproj | 1 + ...AzureAppServicesLoggerFactoryExtensions.cs | 7 +- .../ConsoleLoggerFactoryExtensions.cs | 40 +++ .../ConsoleLoggerProvider.cs | 56 ++-- .../DebugLoggerFactoryExtensions.cs | 10 +- .../EventLoggerFactoryExtensions.cs | 29 +- .../EventSourceLoggerFactoryExtensions.cs | 14 +- .../AssemblyTestLog.cs | 16 +- ...icrosoft.Extensions.Logging.Testing.csproj | 1 + .../XunitLoggerFactoryExtensions.cs | 15 +- .../TraceSourceFactoryExtensions.cs | 2 + ...figurationLoggerFilterConfigureOptions.cs} | 7 +- src/Microsoft.Extensions.Logging/Logger.cs | 4 +- .../LoggerFactory1.cs | 44 ++- .../LoggerFilterOptionsExtensions.cs | 60 ++++ .../LoggerRuleSelector.cs | 6 + .../LoggingServiceCollectionExtensions.cs | 7 +- .../EventSourceLoggerTest.cs | 63 ++-- ...Extensions.Logging.EventSource.Test.csproj | 1 + .../ConsoleLoggerTest.cs | 18 - .../LoggerFactoryBuilder.cs | 54 +++ .../LoggerFactoryTest.cs | 15 +- .../LoggerFilterTest.cs | 316 +++++------------- .../LoggerTest.cs | 44 ++- .../TraceSourceLoggerTest.cs | 19 +- .../TraceSourceScopeTest.cs | 6 +- ...ft.Extensions.Logging.Testing.Tests.csproj | 1 + .../XunitLoggerProviderTest.cs | 23 +- 30 files changed, 497 insertions(+), 410 deletions(-) rename src/Microsoft.Extensions.Logging/{LoggerFilterOptionsConfigurationSetup.cs => ConfigurationLoggerFilterConfigureOptions.cs} (92%) create mode 100644 src/Microsoft.Extensions.Logging/LoggerFilterOptionsExtensions.cs create mode 100644 test/Microsoft.Extensions.Logging.Test/LoggerFactoryBuilder.cs diff --git a/build/common.props b/build/common.props index 2e376efe..ed9d38a9 100644 --- a/build/common.props +++ b/build/common.props @@ -10,6 +10,8 @@ true true $(VersionSuffix)-$(BuildNumber) + + false diff --git a/samples/SampleApp/Program.cs b/samples/SampleApp/Program.cs index de9d7e39..03b135a2 100644 --- a/samples/SampleApp/Program.cs +++ b/samples/SampleApp/Program.cs @@ -7,6 +7,7 @@ using Microsoft.Extensions.Configuration; using Microsoft.Extensions.Logging; using ILogger = Microsoft.Extensions.Logging.ILogger; +using Microsoft.Extensions.DependencyInjection; namespace SampleApp { @@ -23,27 +24,30 @@ public Program() // A Web App based program would configure logging via the WebHostBuilder. // Create a logger factory with filters that can be applied across all logger providers. - var factory = new LoggerFactory() - .UseConfiguration(loggingConfiguration.GetSection("Logging")) - .AddFilter(new Dictionary - { - { "Microsoft", LogLevel.Warning }, - { "System", LogLevel.Warning }, - { "SampleApp.Program", LogLevel.Debug } - }); + var serviceCollection = new ServiceCollection(); + serviceCollection.AddLogging(loggingConfiguration.GetSection("Logging")); + + serviceCollection.Configure(options => + { + options + .AddFilter("Microsoft", LogLevel.Warning) + .AddFilter("System", LogLevel.Warning) + .AddFilter("SampleApp.Program", LogLevel.Debug); + }); // providers may be added to a LoggerFactory before any loggers are created #if NET46 - factory.AddEventLog(); + serviceCollection.AddEventLog(); #elif NETCOREAPP2_0 #else #error Target framework needs to be updated #endif - factory.AddConsole(); + serviceCollection.AddConsole(); + var serviceProvider = serviceCollection.BuildServiceProvider(); // getting the logger using the class's name is conventional - _logger = factory.CreateLogger(); + _logger = serviceProvider.GetRequiredService>(); } public static void Main(string[] args) diff --git a/samples/SampleApp/SampleApp.csproj b/samples/SampleApp/SampleApp.csproj index a0b3537f..1d5f9dcd 100644 --- a/samples/SampleApp/SampleApp.csproj +++ b/samples/SampleApp/SampleApp.csproj @@ -19,6 +19,7 @@ + diff --git a/src/Microsoft.Extensions.Logging.AzureAppServices/AzureAppServicesLoggerFactoryExtensions.cs b/src/Microsoft.Extensions.Logging.AzureAppServices/AzureAppServicesLoggerFactoryExtensions.cs index 644b77b9..43660deb 100644 --- a/src/Microsoft.Extensions.Logging.AzureAppServices/AzureAppServicesLoggerFactoryExtensions.cs +++ b/src/Microsoft.Extensions.Logging.AzureAppServices/AzureAppServicesLoggerFactoryExtensions.cs @@ -29,15 +29,12 @@ public static IServiceCollection AddAzureWebAppDiagnostics(this IServiceCollecti /// The setting object to configure loggers. public static IServiceCollection AddAzureWebAppDiagnostics(this IServiceCollection collection, AzureAppServicesDiagnosticsSettings settings) { + collection.AddLogging(); if (WebAppContext.Default.IsRunningInAzureWebApp) { // Only add the provider if we're in Azure WebApp. That cannot change once the apps started collection.AddSingleton(WebAppContext.Default); - collection.AddSingleton(); - if (settings != null) - { - collection.AddSingleton(settings); - } + collection.AddSingleton(new AzureAppServicesDiagnosticsLoggerProvider(WebAppContext.Default, settings ?? new AzureAppServicesDiagnosticsSettings())); } return collection; } diff --git a/src/Microsoft.Extensions.Logging.Console/ConsoleLoggerFactoryExtensions.cs b/src/Microsoft.Extensions.Logging.Console/ConsoleLoggerFactoryExtensions.cs index 5c2c6319..ea7183ec 100644 --- a/src/Microsoft.Extensions.Logging.Console/ConsoleLoggerFactoryExtensions.cs +++ b/src/Microsoft.Extensions.Logging.Console/ConsoleLoggerFactoryExtensions.cs @@ -16,7 +16,47 @@ public static class ConsoleLoggerExtensions /// The to use. public static IServiceCollection AddConsole(this IServiceCollection collection) { + collection.AddLogging(); collection.AddSingleton(); + + return collection; + } + + /// + /// Adds a console logger named 'Console' to the factory. + /// + /// The to use. + /// + public static IServiceCollection AddConsole(this IServiceCollection collection, Action configure) + { + if (configure == null) + { + throw new ArgumentNullException(nameof(configure)); + } + collection.AddConsole(); + + if (configure != null) + { + collection.Configure(configure); + } + return collection; + } + + /// + /// Adds a console logger named 'Console' to the factory. + /// + /// The to use. + /// + public static IServiceCollection AddConsole(this IServiceCollection collection, IConfiguration configuration) + { + if (configuration == null) + { + throw new ArgumentNullException(nameof(configuration)); + } + + collection.AddConsole(); + collection.Configure(configuration); + return collection; } diff --git a/src/Microsoft.Extensions.Logging.Console/ConsoleLoggerProvider.cs b/src/Microsoft.Extensions.Logging.Console/ConsoleLoggerProvider.cs index f350434b..ef9425c8 100644 --- a/src/Microsoft.Extensions.Logging.Console/ConsoleLoggerProvider.cs +++ b/src/Microsoft.Extensions.Logging.Console/ConsoleLoggerProvider.cs @@ -6,6 +6,7 @@ using System.Collections.Generic; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.Logging.Console.Internal; +using Microsoft.Extensions.Options; namespace Microsoft.Extensions.Logging.Console { @@ -16,10 +17,10 @@ public class ConsoleLoggerProvider : ILoggerProvider private readonly Func _filter; private IConsoleLoggerSettings _settings; private readonly ConsoleLoggerProcessor _messageQueue = new ConsoleLoggerProcessor(); - private readonly bool _isLegacy; private static readonly Func trueFilter = (cat, level) => true; private static readonly Func falseFilter = (cat, level) => false; + private IDisposable _optionsReloadToken; [Obsolete("This method is obsolete and will be removed in a future version. The recommended alternative is Microsoft.Extensions.Logging.Console.ConsoleLoggerProvider(IConfiguration).")] public ConsoleLoggerProvider(Func filter, bool includeScopes) @@ -34,27 +35,24 @@ public ConsoleLoggerProvider(Func filter, bool includeSc { IncludeScopes = includeScopes, }; - - _isLegacy = true; } - public ConsoleLoggerProvider(IConfiguration configuration) + public ConsoleLoggerProvider(IOptionsMonitor options) { - if (configuration != null) - { - _settings = new ConfigurationConsoleLoggerSettings(configuration); + // Filter would be applied on LoggerFactory level + _filter = trueFilter; + _optionsReloadToken = options.OnChange(ReloadLoggerOptions); + ReloadLoggerOptions(options.CurrentValue); + } - if (_settings.ChangeToken != null) - { - _settings.ChangeToken.RegisterChangeCallback(OnConfigurationReload, null); - } - } - else + private void ReloadLoggerOptions(ConsoleLoggerOptions options) + { + var includeScopes = options.IncludeScopes; + foreach (var logger in _loggers.Values) { - _settings = new ConsoleLoggerSettings(); + logger.Filter = GetFilter(logger.Name, _settings); + logger.IncludeScopes = includeScopes; } - - _isLegacy = false; } [Obsolete("This method is obsolete and will be removed in a future version. The recommended alternative is Microsoft.Extensions.Logging.Console.ConsoleLoggerProvider(IConfiguration).")] @@ -71,8 +69,6 @@ public ConsoleLoggerProvider(IConsoleLoggerSettings settings) { _settings.ChangeToken.RegisterChangeCallback(OnConfigurationReload, null); } - - _isLegacy = true; } private void OnConfigurationReload(object state) @@ -86,10 +82,7 @@ private void OnConfigurationReload(object state) var includeScopes = _settings?.IncludeScopes ?? false; foreach (var logger in _loggers.Values) { - if (_isLegacy) - { - logger.Filter = GetFilter(logger.Name, _settings); - } + logger.Filter = GetFilter(logger.Name, _settings); logger.IncludeScopes = includeScopes; } } @@ -119,12 +112,6 @@ private ConsoleLogger CreateLoggerImplementation(string name) private Func GetFilter(string name, IConsoleLoggerSettings settings) { - // Filters are now handled in Logger.cs with the Configuration and AddFilter methods on LoggerFactory - if (!_isLegacy) - { - return trueFilter; - } - if (_filter != null) { return _filter; @@ -162,7 +149,20 @@ private IEnumerable GetKeyPrefixes(string name) public void Dispose() { + _optionsReloadToken?.Dispose(); _messageQueue.Dispose(); } } + + public class ConsoleLoggerOptions + { + public bool IncludeScopes { get; set; } = false; + } + + public class ConfigurationConsoleLoggerConfigureOptions : ConfigureOptions + { + public ConfigurationConsoleLoggerConfigureOptions(IConfiguration configuration) : base(options => configuration.Bind(options)) + { + } + } } diff --git a/src/Microsoft.Extensions.Logging.Debug/DebugLoggerFactoryExtensions.cs b/src/Microsoft.Extensions.Logging.Debug/DebugLoggerFactoryExtensions.cs index a436247f..14385a53 100644 --- a/src/Microsoft.Extensions.Logging.Debug/DebugLoggerFactoryExtensions.cs +++ b/src/Microsoft.Extensions.Logging.Debug/DebugLoggerFactoryExtensions.cs @@ -15,11 +15,13 @@ public static class DebugLoggerFactoryExtensions /// /// Adds a debug logger named 'Debug' to the factory. /// - /// The extension method argument. - public static IServiceCollection AddDebug(this IServiceCollection serviceCollection) + /// The extension method argument. + public static IServiceCollection AddDebug(this IServiceCollection collection) { - serviceCollection.AddSingleton(); - return serviceCollection; + collection.AddLogging(); + collection.AddSingleton(); + + return collection; } /// diff --git a/src/Microsoft.Extensions.Logging.EventLog/EventLoggerFactoryExtensions.cs b/src/Microsoft.Extensions.Logging.EventLog/EventLoggerFactoryExtensions.cs index 8295fac2..c619f05d 100644 --- a/src/Microsoft.Extensions.Logging.EventLog/EventLoggerFactoryExtensions.cs +++ b/src/Microsoft.Extensions.Logging.EventLog/EventLoggerFactoryExtensions.cs @@ -15,31 +15,32 @@ public static class EventLoggerFactoryExtensions /// /// Adds an event logger named 'EventLog' to the factory. /// - /// The extension method argument. - public static IServiceCollection AddEventLog(this IServiceCollection serviceCollection) + /// The extension method argument. + public static IServiceCollection AddEventLog(this IServiceCollection collection) { - if (serviceCollection == null) + if (collection == null) { - throw new ArgumentNullException(nameof(serviceCollection)); + throw new ArgumentNullException(nameof(collection)); } - serviceCollection.AddSingleton(); + collection.AddLogging(); + collection.AddSingleton(); - return serviceCollection; + return collection; } /// /// Adds an event logger. Use to enable logging for specific s. /// - /// The extension method argument. + /// The extension method argument. /// The . - public static LoggerFactory AddEventLog( - this LoggerFactory factory, + public static IServiceCollection AddEventLog( + this IServiceCollection collection, EventLogSettings settings) { - if (factory == null) + if (collection == null) { - throw new ArgumentNullException(nameof(factory)); + throw new ArgumentNullException(nameof(collection)); } if (settings == null) @@ -47,8 +48,10 @@ public static LoggerFactory AddEventLog( throw new ArgumentNullException(nameof(settings)); } - factory.AddProvider(new EventLogLoggerProvider(settings)); - return factory; + collection.AddLogging(); + collection.AddSingleton(new EventLogLoggerProvider(settings)); + + return collection; } /// diff --git a/src/Microsoft.Extensions.Logging.EventSource/EventSourceLoggerFactoryExtensions.cs b/src/Microsoft.Extensions.Logging.EventSource/EventSourceLoggerFactoryExtensions.cs index 56556a65..647f3ae6 100644 --- a/src/Microsoft.Extensions.Logging.EventSource/EventSourceLoggerFactoryExtensions.cs +++ b/src/Microsoft.Extensions.Logging.EventSource/EventSourceLoggerFactoryExtensions.cs @@ -15,18 +15,20 @@ public static class EventSourceLoggerFactoryExtensions /// /// Adds an event logger named 'EventSource' to the factory. /// - /// The extension method argument. - public static IServiceCollection AddEventSourceLogger(this IServiceCollection serviceCollection) + /// The extension method argument. + public static IServiceCollection AddEventSourceLogger(this IServiceCollection collection) { - if (serviceCollection == null) + if (collection == null) { - throw new ArgumentNullException(nameof(serviceCollection)); + throw new ArgumentNullException(nameof(collection)); } + collection.AddLogging(); + var loggerProvider = LoggingEventSource.Instance.CreateLoggerProvider(); - serviceCollection.AddSingleton(loggerProvider); + collection.AddSingleton(loggerProvider); - return serviceCollection; + return collection; } /// diff --git a/src/Microsoft.Extensions.Logging.Testing/AssemblyTestLog.cs b/src/Microsoft.Extensions.Logging.Testing/AssemblyTestLog.cs index 000b133d..1e118104 100644 --- a/src/Microsoft.Extensions.Logging.Testing/AssemblyTestLog.cs +++ b/src/Microsoft.Extensions.Logging.Testing/AssemblyTestLog.cs @@ -9,6 +9,7 @@ using System.Runtime.CompilerServices; using Serilog; using Xunit.Abstractions; +using Microsoft.Extensions.DependencyInjection; namespace Microsoft.Extensions.Logging.Testing { @@ -53,12 +54,15 @@ public IDisposable StartTestLog(ITestOutputHelper output, string className, out public ILoggerFactory CreateLoggerFactory(ITestOutputHelper output, string className, [CallerMemberName] string testName = null) { - var loggerFactory = new LoggerFactory(); + var serviceCollection = new ServiceCollection(); + serviceCollection.AddLogging(); + if (output != null) { - loggerFactory.AddXunit(output, LogLevel.Debug); + serviceCollection.AddXunit(output, LogLevel.Debug); } + var loggerFactory = serviceCollection.BuildServiceProvider().GetRequiredService(); // Try to shorten the class name using the assembly name if (className.StartsWith(_assemblyName + ".")) { @@ -77,10 +81,12 @@ public ILoggerFactory CreateLoggerFactory(ITestOutputHelper output, string class public static AssemblyTestLog Create(string assemblyName, string baseDirectory) { - var loggerFactory = new LoggerFactory(); + var serviceCollection = new ServiceCollection(); // Let the global logger log to the console, it's just "Starting X..." "Finished X..." - loggerFactory.AddConsole(); + serviceCollection.AddConsole(); + + var loggerFactory = serviceCollection.BuildServiceProvider().GetRequiredService(); if (!string.IsNullOrEmpty(baseDirectory)) { @@ -106,7 +112,7 @@ public static AssemblyTestLog ForAssembly(Assembly assembly) } } - private static void AddFileLogging(LoggerFactory loggerFactory, string fileName) + private static void AddFileLogging(ILoggerFactory loggerFactory, string fileName) { var dir = Path.GetDirectoryName(fileName); if (!Directory.Exists(dir)) diff --git a/src/Microsoft.Extensions.Logging.Testing/Microsoft.Extensions.Logging.Testing.csproj b/src/Microsoft.Extensions.Logging.Testing/Microsoft.Extensions.Logging.Testing.csproj index e49ece5c..274e04bb 100644 --- a/src/Microsoft.Extensions.Logging.Testing/Microsoft.Extensions.Logging.Testing.csproj +++ b/src/Microsoft.Extensions.Logging.Testing/Microsoft.Extensions.Logging.Testing.csproj @@ -17,6 +17,7 @@ + diff --git a/src/Microsoft.Extensions.Logging.Testing/XunitLoggerFactoryExtensions.cs b/src/Microsoft.Extensions.Logging.Testing/XunitLoggerFactoryExtensions.cs index f154e9bd..d639ecbf 100644 --- a/src/Microsoft.Extensions.Logging.Testing/XunitLoggerFactoryExtensions.cs +++ b/src/Microsoft.Extensions.Logging.Testing/XunitLoggerFactoryExtensions.cs @@ -2,6 +2,7 @@ // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System; +using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Logging.Testing; using Xunit.Abstractions; @@ -9,16 +10,18 @@ namespace Microsoft.Extensions.Logging { public static class XunitLoggerFactoryExtensions { - public static LoggerFactory AddXunit(this LoggerFactory loggerFactory, ITestOutputHelper output) + public static IServiceCollection AddXunit(this IServiceCollection services, ITestOutputHelper output) { - loggerFactory.AddProvider(new XunitLoggerProvider(output)); - return loggerFactory; + services.AddLogging(); + services.AddSingleton(new XunitLoggerProvider(output)); + return services; } - public static LoggerFactory AddXunit(this LoggerFactory loggerFactory, ITestOutputHelper output, LogLevel minLevel) + public static IServiceCollection AddXunit(this IServiceCollection services, ITestOutputHelper output, LogLevel minLevel) { - loggerFactory.AddProvider(new XunitLoggerProvider(output, minLevel)); - return loggerFactory; + services.AddLogging(); + services.AddSingleton(new XunitLoggerProvider(output)); + return services; } [Obsolete("This method is obsolete and will be removed in a future version. The recommended alternative is to call the Microsoft.Extensions.Logging.AddXunit() extension method on the Microsoft.Extensions.Logging.LoggerFactory instance.")] diff --git a/src/Microsoft.Extensions.Logging.TraceSource/TraceSourceFactoryExtensions.cs b/src/Microsoft.Extensions.Logging.TraceSource/TraceSourceFactoryExtensions.cs index d7cbf62d..c1b23118 100644 --- a/src/Microsoft.Extensions.Logging.TraceSource/TraceSourceFactoryExtensions.cs +++ b/src/Microsoft.Extensions.Logging.TraceSource/TraceSourceFactoryExtensions.cs @@ -80,6 +80,7 @@ public static IServiceCollection AddTraceSource( throw new ArgumentNullException(nameof(sourceSwitch)); } + collection.AddLogging(); collection.AddSingleton(new TraceSourceLoggerProvider(sourceSwitch)); return collection; @@ -111,6 +112,7 @@ public static IServiceCollection AddTraceSource( throw new ArgumentNullException(nameof(listener)); } + collection.AddLogging(); collection.AddSingleton(new TraceSourceLoggerProvider(sourceSwitch, listener)); return collection; diff --git a/src/Microsoft.Extensions.Logging/LoggerFilterOptionsConfigurationSetup.cs b/src/Microsoft.Extensions.Logging/ConfigurationLoggerFilterConfigureOptions.cs similarity index 92% rename from src/Microsoft.Extensions.Logging/LoggerFilterOptionsConfigurationSetup.cs rename to src/Microsoft.Extensions.Logging/ConfigurationLoggerFilterConfigureOptions.cs index e7ab18da..ae1196b8 100644 --- a/src/Microsoft.Extensions.Logging/LoggerFilterOptionsConfigurationSetup.cs +++ b/src/Microsoft.Extensions.Logging/ConfigurationLoggerFilterConfigureOptions.cs @@ -5,12 +5,11 @@ namespace Microsoft.Extensions.Logging { - // TODO: Reload support - public class LoggerFilterOptionsConfigurationSetup : IConfigureOptions + public class ConfigurationLoggerFilterConfigureOptions : IConfigureOptions { private readonly IConfiguration _configuration; - public LoggerFilterOptionsConfigurationSetup(IConfiguration configuration) + public ConfigurationLoggerFilterConfigureOptions(IConfiguration configuration) { _configuration = configuration; } @@ -41,7 +40,7 @@ private void LoadDefaultConfigValues(ICollection rules) { // Load logger specific rules var logger = ExpandLoggerAlias(configurationSection.Key); - LoadRules(rules, configurationSection, logger); + LoadRules(rules, logLevelSection, logger); } } } diff --git a/src/Microsoft.Extensions.Logging/Logger.cs b/src/Microsoft.Extensions.Logging/Logger.cs index c9c5f4cd..e00b2eba 100644 --- a/src/Microsoft.Extensions.Logging/Logger.cs +++ b/src/Microsoft.Extensions.Logging/Logger.cs @@ -207,6 +207,8 @@ public struct LoggerInformation public string Category { get; set; } + public string ProviderType { get; set; } + public LogLevel? MinLevel { get; set; } public LogMessageFilter Filter { get; set; } @@ -220,7 +222,7 @@ public bool IsEnabled(LogLevel level) if (Filter != null) { - return Filter(Logger.GetType().FullName, Category, level); + return Filter(ProviderType, Category, level); } return true; diff --git a/src/Microsoft.Extensions.Logging/LoggerFactory1.cs b/src/Microsoft.Extensions.Logging/LoggerFactory1.cs index 3ac5e243..43965068 100644 --- a/src/Microsoft.Extensions.Logging/LoggerFactory1.cs +++ b/src/Microsoft.Extensions.Logging/LoggerFactory1.cs @@ -7,11 +7,17 @@ namespace Microsoft.Extensions.Logging { public class LoggerFactory : ILoggerFactory { + private struct ProviderRegistration + { + public ILoggerProvider Provider; + public bool ShouldDispose; + } + private static readonly LoggerRuleSelector RuleSelector = new LoggerRuleSelector(); private readonly Dictionary _loggers = new Dictionary(StringComparer.Ordinal); - private readonly List _providers; + private readonly List _providerRegistrations; private readonly object _sync = new object(); private volatile bool _disposed; private IDisposable _changeTokenRegistration; @@ -27,7 +33,7 @@ public LoggerFactory() : this(new LoggerFilterOptions()) public LoggerFactory(IEnumerable providers, IOptionsMonitor filterOption) { - _providers = providers.ToList(); + _providerRegistrations = providers.Select(provider => new ProviderRegistration { Provider = provider}).ToList(); _changeTokenRegistration = filterOption.OnChange(RefreshFilters); RefreshFilters(filterOption.CurrentValue); } @@ -49,6 +55,11 @@ private void RefreshFilters(LoggerFilterOptions filterOptions) public ILogger CreateLogger(string categoryName) { + if (CheckDisposed()) + { + throw new ObjectDisposedException(nameof(LoggerFactory)); + } + lock (_sync) { Logger logger; @@ -69,9 +80,14 @@ public ILogger CreateLogger(string categoryName) void ILoggerFactory.AddProvider(ILoggerProvider provider) { + if (CheckDisposed()) + { + throw new ObjectDisposedException(nameof(LoggerFactory)); + } + lock (_sync) { - _providers.Add(provider); + _providerRegistrations.Add(new ProviderRegistration { Provider = provider, ShouldDispose = true}); foreach (var logger in _loggers) { var loggerInformation = logger.Value.Loggers; @@ -80,18 +96,24 @@ void ILoggerFactory.AddProvider(ILoggerProvider provider) Array.Resize(ref loggerInformation, loggerInformation.Length + 1); var newLoggerIndex = loggerInformation.Length - 1; loggerInformation[newLoggerIndex].Logger = provider.CreateLogger(categoryName); + loggerInformation[newLoggerIndex].ProviderType = provider.GetType().FullName; ApplyRules(loggerInformation, categoryName, newLoggerIndex, 1); + + logger.Value.Loggers = loggerInformation; } } } private Logger.LoggerInformation[] CreateLoggers(string categoryName) { - Logger.LoggerInformation[] loggers = new Logger.LoggerInformation[_providers.Count]; - for (int i = 0; i < _providers.Count; i++) + Logger.LoggerInformation[] loggers = new Logger.LoggerInformation[_providerRegistrations.Count]; + for (int i = 0; i < _providerRegistrations.Count; i++) { - loggers[i].Logger = _providers[i].CreateLogger(categoryName); + var provider = _providerRegistrations[i].Provider; + + loggers[i].Logger = provider.CreateLogger(categoryName); + loggers[i].ProviderType = provider.GetType().FullName; } ApplyRules(loggers, categoryName, 0, loggers.Length); @@ -105,11 +127,12 @@ private void ApplyRules(Logger.LoggerInformation[] loggers, string categoryName, ref var loggerInformation = ref loggers[index]; RuleSelector.Select(_filterOptions, - loggerInformation.Logger.GetType().FullName, + loggerInformation.ProviderType, categoryName, out var minLevel, out var filter); + loggerInformation.Category = categoryName; loggerInformation.MinLevel = minLevel; loggerInformation.Filter = filter; } @@ -129,11 +152,14 @@ public void Dispose() _changeTokenRegistration?.Dispose(); - foreach (var provider in _providers) + foreach (var registration in _providerRegistrations) { try { - provider.Dispose(); + if (registration.ShouldDispose) + { + registration.Provider.Dispose(); + } } catch { diff --git a/src/Microsoft.Extensions.Logging/LoggerFilterOptionsExtensions.cs b/src/Microsoft.Extensions.Logging/LoggerFilterOptionsExtensions.cs new file mode 100644 index 00000000..c1bb8b32 --- /dev/null +++ b/src/Microsoft.Extensions.Logging/LoggerFilterOptionsExtensions.cs @@ -0,0 +1,60 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System; +using Microsoft.Extensions.Logging; + +namespace Microsoft.Extensions.DependencyInjection +{ + /// + /// Extension methods for setting up logging services in an . + /// + public static class LoggerFilterOptionsExtensions + { + public static LoggerFilterOptions AddFilter(this LoggerFilterOptions options, string category, LogLevel level) + { + options.Rules.Add(new LoggerFilterRule(null, category, level, null)); + return options; + } + + public static LoggerFilterOptions AddFilter(this LoggerFilterOptions options, string category, LogLevel level) + { + options.Rules.Add(new LoggerFilterRule(typeof(T).FullName, category, level, null)); + return options; + } + + public static LoggerFilterOptions AddFilter(this LoggerFilterOptions options, LogMessageFilter filter) + { + options.Rules.Add(new LoggerFilterRule(null, null, null, filter)); + return options; + } + public static LoggerFilterOptions AddFilter(this LoggerFilterOptions options, LogMessageFilter filter) + { + options.Rules.Add(new LoggerFilterRule(typeof(T).FullName, null, null, filter)); + return options; + } + + public static LoggerFilterOptions AddFilter(this LoggerFilterOptions options, Func categoryLevelFilter) + { + options.Rules.Add(new LoggerFilterRule(null, null, null, (type, name, level) => categoryLevelFilter(name, level))); + return options; + } + + public static LoggerFilterOptions AddFilter(this LoggerFilterOptions options, Func categoryLevelFilter) + { + options.Rules.Add(new LoggerFilterRule(typeof(T).FullName, null, null, (type, name, level) => categoryLevelFilter(name, level))); + return options; + } + public static LoggerFilterOptions AddFilter(this LoggerFilterOptions options, string category, Func levelFilter) + { + options.Rules.Add(new LoggerFilterRule(null, category, null, (type, name, level) => levelFilter(level))); + return options; + } + + public static LoggerFilterOptions AddFilter(this LoggerFilterOptions options, string category, Func levelFilter) + { + options.Rules.Add(new LoggerFilterRule(typeof(T).FullName, category, null, (type, name, level) => levelFilter(level))); + return options; + } + } +} \ No newline at end of file diff --git a/src/Microsoft.Extensions.Logging/LoggerRuleSelector.cs b/src/Microsoft.Extensions.Logging/LoggerRuleSelector.cs index 416379bf..c70f9e78 100644 --- a/src/Microsoft.Extensions.Logging/LoggerRuleSelector.cs +++ b/src/Microsoft.Extensions.Logging/LoggerRuleSelector.cs @@ -16,6 +16,7 @@ public void Select(LoggerFilterOptions options, string logger, string category, // Filter rule selection: // 1. Select rules for current logger type, if there is none, select ones without logger type specified // 2. Select rules with longest matching categories + // 3. If there no category // 3. If there is only one rule use it's level and filter // 4. If there are multiple rules combine them using AND operator // 5. If there are no applicable rules use global minimal level @@ -40,6 +41,11 @@ public void Select(LoggerFilterOptions options, string logger, string category, categorySpecificRules = loggerSpecificRules.Where(rule => string.IsNullOrEmpty(rule.CategoryName)).ToList(); } + if (!categorySpecificRules.Any()) + { + categorySpecificRules = loggerSpecificRules.Where(rule => rule.CategoryName.Equals("Default", StringComparison.OrdinalIgnoreCase)).ToList(); + } + if (categorySpecificRules.Any()) { if (categorySpecificRules.Count == 1) diff --git a/src/Microsoft.Extensions.Logging/LoggingServiceCollectionExtensions.cs b/src/Microsoft.Extensions.Logging/LoggingServiceCollectionExtensions.cs index dab76a66..db43cac4 100644 --- a/src/Microsoft.Extensions.Logging/LoggingServiceCollectionExtensions.cs +++ b/src/Microsoft.Extensions.Logging/LoggingServiceCollectionExtensions.cs @@ -18,7 +18,6 @@ public static class LoggingServiceCollectionExtensions /// Adds logging services to the specified . /// /// The to add services to. - /// The instance to use fol filter configuration /// The so that additional calls can be chained. public static IServiceCollection AddLogging(this IServiceCollection services) { @@ -38,13 +37,15 @@ public static IServiceCollection AddLogging(this IServiceCollection services, IC throw new ArgumentNullException(nameof(services)); } + services.AddOptions(); + services.TryAdd(ServiceDescriptor.Singleton()); services.TryAdd(ServiceDescriptor.Singleton(typeof(ILogger<>), typeof(Logger<>))); if (configuration != null) { - services.TryAdd(ServiceDescriptor.Singleton>(new LoggerFilterOptionsConfigurationSetup(configuration))); - services.TryAdd(ServiceDescriptor.Singleton>(new ConfigurationChangeTokenSource(configuration))); + services.TryAddEnumerable(ServiceDescriptor.Singleton>(new ConfigurationLoggerFilterConfigureOptions(configuration))); + services.TryAddEnumerable(ServiceDescriptor.Singleton>(new ConfigurationChangeTokenSource(configuration))); } return services; } diff --git a/test/Microsoft.Extensions.Logging.EventSource.Test/EventSourceLoggerTest.cs b/test/Microsoft.Extensions.Logging.EventSource.Test/EventSourceLoggerTest.cs index 9eb5d4b6..1b708b5b 100644 --- a/test/Microsoft.Extensions.Logging.EventSource.Test/EventSourceLoggerTest.cs +++ b/test/Microsoft.Extensions.Logging.EventSource.Test/EventSourceLoggerTest.cs @@ -9,6 +9,7 @@ using Microsoft.Extensions.Logging.EventSource; using Newtonsoft.Json; using Xunit; +using Microsoft.Extensions.DependencyInjection; namespace Microsoft.Extensions.Logging.Test { @@ -19,8 +20,9 @@ public void Logs_AsExpected_WithDefaults() { using (var testListener = new TestEventListener()) { - var factory = new LoggerFactory(); - factory.AddEventSourceLogger(); + var factory = LoggerFactoryBuilder.Create() + .WithServices(services => services.AddEventSourceLogger()) + .Build(); var listenerSettings = new TestEventListener.ListenerSettings(); listenerSettings.Keywords = (EventKeywords)(-1); @@ -53,7 +55,9 @@ public void Logs_Nothing_IfNotEnabled() { using (var testListener = new TestEventListener()) { - var factory = new LoggerFactory(); + var factory = LoggerFactoryBuilder.Create() + .Build(); + // No call to factory.AddEventSourceLogger(); var listenerSettings = new TestEventListener.ListenerSettings(); @@ -73,8 +77,9 @@ public void Logs_OnlyFormattedMessage_IfKeywordSet() { using (var testListener = new TestEventListener()) { - var factory = new LoggerFactory(); - factory.AddEventSourceLogger(); + var factory = LoggerFactoryBuilder.Create() + .WithServices(services => services.AddEventSourceLogger()) + .Build(); var listenerSettings = new TestEventListener.ListenerSettings(); listenerSettings.Keywords = LoggingEventSource.Keywords.FormattedMessage; @@ -105,8 +110,9 @@ public void Logs_OnlyJson_IfKeywordSet() { using (var testListener = new TestEventListener()) { - var factory = new LoggerFactory(); - factory.AddEventSourceLogger(); + var factory = LoggerFactoryBuilder.Create() + .WithServices(services => services.AddEventSourceLogger()) + .Build(); var listenerSettings = new TestEventListener.ListenerSettings(); listenerSettings.Keywords = LoggingEventSource.Keywords.JsonMessage; @@ -137,8 +143,9 @@ public void Logs_OnlyMessage_IfKeywordSet() { using (var testListener = new TestEventListener()) { - var factory = new LoggerFactory(); - factory.AddEventSourceLogger(); + var factory = LoggerFactoryBuilder.Create() + .WithServices(services => services.AddEventSourceLogger()) + .Build(); var listenerSettings = new TestEventListener.ListenerSettings(); listenerSettings.Keywords = LoggingEventSource.Keywords.Message; @@ -169,8 +176,9 @@ public void Logs_AllEvents_IfTraceSet() { using (var testListener = new TestEventListener()) { - var factory = new LoggerFactory(); - factory.AddEventSourceLogger(); + var factory = LoggerFactoryBuilder.Create() + .WithServices(services => services.AddEventSourceLogger()) + .Build(); var listenerSettings = new TestEventListener.ListenerSettings(); listenerSettings.Keywords = LoggingEventSource.Keywords.JsonMessage; @@ -201,8 +209,9 @@ public void Logs_AsExpected_AtErrorLevel() { using (var testListener = new TestEventListener()) { - var factory = new LoggerFactory(); - factory.AddEventSourceLogger(); + var factory = LoggerFactoryBuilder.Create() + .WithServices(services => services.AddEventSourceLogger()) + .Build(); var listenerSettings = new TestEventListener.ListenerSettings(); listenerSettings.Keywords = LoggingEventSource.Keywords.JsonMessage; @@ -227,8 +236,9 @@ public void Logs_AsExpected_AtWarningLevel() { using (var testListener = new TestEventListener()) { - var factory = new LoggerFactory(); - factory.AddEventSourceLogger(); + var factory = LoggerFactoryBuilder.Create() + .WithServices(services => services.AddEventSourceLogger()) + .Build(); var listenerSettings = new TestEventListener.ListenerSettings(); listenerSettings.Keywords = LoggingEventSource.Keywords.JsonMessage; @@ -255,8 +265,9 @@ public void Logs_AsExpected_WithSingleLoggerSpec() { using (var testListener = new TestEventListener()) { - var factory = new LoggerFactory(); - factory.AddEventSourceLogger(); + var factory = LoggerFactoryBuilder.Create() + .WithServices(services => services.AddEventSourceLogger()) + .Build(); var listenerSettings = new TestEventListener.ListenerSettings(); listenerSettings.Keywords = LoggingEventSource.Keywords.JsonMessage; @@ -278,8 +289,9 @@ public void Logs_AsExpected_WithSingleLoggerSpecWithVerbosity() { using (var testListener = new TestEventListener()) { - var factory = new LoggerFactory(); - factory.AddEventSourceLogger(); + var factory = LoggerFactoryBuilder.Create() + .WithServices(services => services.AddEventSourceLogger()) + .Build(); var listenerSettings = new TestEventListener.ListenerSettings(); listenerSettings.Keywords = LoggingEventSource.Keywords.JsonMessage; @@ -299,8 +311,9 @@ public void Logs_AsExpected_WithComplexLoggerSpec() { using (var testListener = new TestEventListener()) { - var factory = new LoggerFactory(); - factory.AddEventSourceLogger(); + var factory = LoggerFactoryBuilder.Create() + .WithServices(services => services.AddEventSourceLogger()) + .Build(); var listenerSettings = new TestEventListener.ListenerSettings(); listenerSettings.Keywords = LoggingEventSource.Keywords.JsonMessage; @@ -389,14 +402,6 @@ public TestEventListener() public List Events; - public void DumpEvents() - { - foreach (string eventData in Events) - { - Console.WriteLine(eventData); - } - } - public void EnableEvents(ListenerSettings settings) { var args = new Dictionary(); diff --git a/test/Microsoft.Extensions.Logging.EventSource.Test/Microsoft.Extensions.Logging.EventSource.Test.csproj b/test/Microsoft.Extensions.Logging.EventSource.Test/Microsoft.Extensions.Logging.EventSource.Test.csproj index 81198e1d..dd672d3c 100644 --- a/test/Microsoft.Extensions.Logging.EventSource.Test/Microsoft.Extensions.Logging.EventSource.Test.csproj +++ b/test/Microsoft.Extensions.Logging.EventSource.Test/Microsoft.Extensions.Logging.EventSource.Test.csproj @@ -10,6 +10,7 @@ + diff --git a/test/Microsoft.Extensions.Logging.Test/ConsoleLoggerTest.cs b/test/Microsoft.Extensions.Logging.Test/ConsoleLoggerTest.cs index 94e43493..406bfa33 100644 --- a/test/Microsoft.Extensions.Logging.Test/ConsoleLoggerTest.cs +++ b/test/Microsoft.Extensions.Logging.Test/ConsoleLoggerTest.cs @@ -40,23 +40,6 @@ public ConsoleLoggerTest() _paddingString = new string(' ', loglevelStringWithPadding.Length); } - private Tuple SetUpFactory(Func filter) - { - var t = SetUp(null); - var logger = t.Item1; - var sink = t.Item2; - - var provider = new Mock(); - provider.Setup(f => f.CreateLogger( - It.IsAny())) - .Returns(logger); - - var factory = new LoggerFactory(); - factory.AddProvider("Console", provider.Object); - - return new Tuple(factory, sink); - } - [Fact] public void LogsWhenMessageIsNotProvided() { @@ -736,7 +719,6 @@ public void ConsoleLogger_ReloadSettings_CanRecoverAfterFailedReload() #pragma warning disable CS0618 // Type or member is obsolete loggerFactory.AddConsole(settings); #pragma warning restore CS0618 // Type or member is obsolete - loggerFactory.AddDebug(); var logger = loggerFactory.CreateLogger("Test"); diff --git a/test/Microsoft.Extensions.Logging.Test/LoggerFactoryBuilder.cs b/test/Microsoft.Extensions.Logging.Test/LoggerFactoryBuilder.cs new file mode 100644 index 00000000..225940fd --- /dev/null +++ b/test/Microsoft.Extensions.Logging.Test/LoggerFactoryBuilder.cs @@ -0,0 +1,54 @@ +using System; +using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.DependencyInjection; + +namespace Microsoft.Extensions.Logging.Test +{ + public class LoggerFactoryBuilder + { + private ServiceCollection _serviceCollection; + + public LoggerFactoryBuilder() + { + _serviceCollection = new ServiceCollection(); + } + + public static LoggerFactoryBuilder Create(IConfiguration configuration = null) + { + return new LoggerFactoryBuilder() + .WithServices(collection => + { + if (configuration != null) + { + LoggingServiceCollectionExtensions.AddLogging(collection, configuration); + } + else + { + LoggingServiceCollectionExtensions.AddLogging(collection); + } + }); + } + + public LoggerFactoryBuilder WithProvider(ILoggerProvider provider) + { + return WithServices(collection => ServiceCollectionServiceExtensions.AddSingleton(collection, provider)); + } + + public LoggerFactoryBuilder WithFilters(Action filterConfiguration) + { + OptionsServiceCollectionExtensions.Configure(_serviceCollection, filterConfiguration); + return this; + } + + public LoggerFactoryBuilder WithServices(Action serviceConfiguration) + { + serviceConfiguration(_serviceCollection); + return this; + } + + public ILoggerFactory Build() + { + return ServiceCollectionContainerBuilderExtensions.BuildServiceProvider(_serviceCollection).GetRequiredService(); + } + } +} \ No newline at end of file diff --git a/test/Microsoft.Extensions.Logging.Test/LoggerFactoryTest.cs b/test/Microsoft.Extensions.Logging.Test/LoggerFactoryTest.cs index 8bd812f1..bdacd180 100644 --- a/test/Microsoft.Extensions.Logging.Test/LoggerFactoryTest.cs +++ b/test/Microsoft.Extensions.Logging.Test/LoggerFactoryTest.cs @@ -16,7 +16,9 @@ public void AddProvider_ThrowsAfterDisposed() { var factory = new LoggerFactory(); factory.Dispose(); - Assert.Throws(() => factory.AddProvider(CreateProvider())); +#pragma warning disable CS0618 // Type or member is obsolete + Assert.Throws(() => ((ILoggerFactory) factory).AddProvider(CreateProvider())); +#pragma warning restore CS0618 // Type or member is obsolete } [Fact] @@ -48,8 +50,11 @@ public void Dispose_ProvidersAreDisposed() var factory = new LoggerFactory(); var disposableProvider1 = CreateProvider(); var disposableProvider2 = CreateProvider(); - factory.AddProvider(disposableProvider1); - factory.AddProvider(disposableProvider2); + +#pragma warning disable CS0618 // Type or member is obsolete + ((ILoggerFactory) factory).AddProvider(disposableProvider1); + ((ILoggerFactory) factory).AddProvider(disposableProvider2); +#pragma warning restore CS0618 // Type or member is obsolete // Act factory.Dispose(); @@ -78,7 +83,9 @@ public void Dispose_ThrowException_SwallowsException() throwingProvider.As() .Setup(p => p.Dispose()) .Throws(); - factory.AddProvider(throwingProvider.Object); +#pragma warning disable CS0618 // Type or member is obsolete + ((ILoggerFactory) factory).AddProvider(throwingProvider.Object); +#pragma warning restore CS0618 // Type or member is obsolete // Act factory.Dispose(); diff --git a/test/Microsoft.Extensions.Logging.Test/LoggerFilterTest.cs b/test/Microsoft.Extensions.Logging.Test/LoggerFilterTest.cs index d919131b..81b9a467 100644 --- a/test/Microsoft.Extensions.Logging.Test/LoggerFilterTest.cs +++ b/test/Microsoft.Extensions.Logging.Test/LoggerFilterTest.cs @@ -6,6 +6,7 @@ using System.IO; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.Configuration.Json; +using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Logging.Testing; using Xunit; @@ -26,9 +27,12 @@ public void ChangingConfigReloadsDefaultFilter() } }"; var config = CreateConfiguration(() => json); - var factory = new LoggerFactory(config.GetSection("Logging")); var loggerProvider = new TestLoggerProvider(new TestSink(), isEnabled: true); - factory.AddProvider("Test", loggerProvider); + + var factory = LoggerFactoryBuilder + .Create(config.GetSection("Logging")) + .WithProvider(loggerProvider) + .Build(); var logger = factory.CreateLogger("Microsoft"); @@ -70,10 +74,11 @@ public void ChangingConfigFromUseConfigurationReloadsDefaultFilter() } }"; var config = CreateConfiguration(() => json); - var factory = new LoggerFactory(); - factory.UseConfiguration(config.GetSection("Logging")); var loggerProvider = new TestLoggerProvider(new TestSink(), isEnabled: true); - factory.AddProvider(loggerProvider); + var factory = LoggerFactoryBuilder + .Create(config.GetSection("Logging")) + .WithProvider(loggerProvider) + .Build(); var logger = factory.CreateLogger("Microsoft"); @@ -102,52 +107,6 @@ public void ChangingConfigFromUseConfigurationReloadsDefaultFilter() Assert.Equal(1, writes.Count); } - [Fact] - public void UseConfigurationReplacesOldConfiguration() - { - // Arrange - var json = -@"{ - ""Logging"": { - ""LogLevel"": { - ""Microsoft"": ""Information"" - } - } -}"; - var config = CreateConfiguration(() => json); - var factory = new LoggerFactory(); - factory.UseConfiguration(config.GetSection("Logging")); - var loggerProvider = new TestLoggerProvider(new TestSink(), isEnabled: true); - factory.AddProvider(loggerProvider); - - var logger = factory.CreateLogger("Microsoft"); - - // Act - logger.LogTrace("Message"); - - // Assert - var writes = loggerProvider.Sink.Writes; - Assert.Equal(0, writes.Count); - - var jsonTrace = -@"{ - ""Logging"": { - ""LogLevel"": { - ""Microsoft"": ""Trace"" - } - } -}"; - config = CreateConfiguration(() => jsonTrace); - factory.UseConfiguration(config); - - // Act - logger.LogTrace("Message"); - - // Assert - writes = loggerProvider.Sink.Writes; - Assert.Equal(1, writes.Count); - } - [Fact] public void CanFilterOnNamedProviders() { @@ -155,7 +114,7 @@ public void CanFilterOnNamedProviders() var json = @"{ ""Logging"": { - ""CustomName"": { + ""Microsoft.Extensions.Logging.Test.TestLoggerProvider"": { ""LogLevel"": { ""Microsoft"": ""Information"" } @@ -163,43 +122,12 @@ public void CanFilterOnNamedProviders() } }"; var config = CreateConfiguration(() => json); - var factory = new LoggerFactory(config.GetSection("Logging")); - var loggerProvider = new TestLoggerProvider(new TestSink(), isEnabled: true); - factory.AddProvider("CustomName", loggerProvider); - - var logger = factory.CreateLogger("Microsoft"); - - // Act - logger.LogTrace("Message"); - - // Assert - var writes = loggerProvider.Sink.Writes; - Assert.Equal(0, writes.Count); - } - [Fact] - public void PreferCustomProviderNameOverFullNameForFiltering() - { - // Arrange - var json = -@"{ - ""Logging"": { - ""CustomName"": { - ""LogLevel"": { - ""Microsoft"": ""Trace"" - } - }, - ""Microsoft.Extensions.Logging.Test.TestLoggerProvider"": { - ""LogLevel"": { - ""Microsoft"": ""Critical"" - } - } - } -}"; - var config = CreateConfiguration(() => json); - var factory = new LoggerFactory(config.GetSection("Logging")); var loggerProvider = new TestLoggerProvider(new TestSink(), isEnabled: true); - factory.AddProvider("CustomName", loggerProvider); + var factory = LoggerFactoryBuilder + .Create(config.GetSection("Logging")) + .WithProvider(loggerProvider) + .Build(); var logger = factory.CreateLogger("Microsoft"); @@ -208,7 +136,7 @@ public void PreferCustomProviderNameOverFullNameForFiltering() // Assert var writes = loggerProvider.Sink.Writes; - Assert.Equal(1, writes.Count); + Assert.Equal(0, writes.Count); } [Fact] @@ -229,44 +157,12 @@ public void PreferFullNameOverDefaultForFiltering() } }"; var config = CreateConfiguration(() => json); - var factory = new LoggerFactory(config.GetSection("Logging")); - var loggerProvider = new TestLoggerProvider(new TestSink(), isEnabled: true); - factory.AddProvider("Test", loggerProvider); - - var logger = factory.CreateLogger("Microsoft"); - - // Act - logger.LogTrace("Message"); - // Assert - var writes = loggerProvider.Sink.Writes; - Assert.Equal(1, writes.Count); - } - - [Fact] - public void CanHaveMultipleProvidersOfSameTypeWithDifferentNames() - { - // Arrange - var json = -@"{ - ""Logging"": { - ""Custom1"": { - ""LogLevel"": { - ""Microsoft"": ""Critical"" - } - }, - ""Custom2"": { - ""LogLevel"": { - ""Microsoft"": ""Trace"" - } - } - } -}"; - var config = CreateConfiguration(() => json); - var factory = new LoggerFactory(config.GetSection("Logging")); var loggerProvider = new TestLoggerProvider(new TestSink(), isEnabled: true); - factory.AddProvider("Custom1", loggerProvider); - factory.AddProvider("Custom2", loggerProvider); + var factory = LoggerFactoryBuilder + .Create(config.GetSection("Logging")) + .WithProvider(loggerProvider) + .Build(); var logger = factory.CreateLogger("Microsoft"); @@ -276,30 +172,6 @@ public void CanHaveMultipleProvidersOfSameTypeWithDifferentNames() // Assert var writes = loggerProvider.Sink.Writes; Assert.Equal(1, writes.Count); - - json = -@"{ - ""Logging"": { - ""Custom1"": { - ""LogLevel"": { - ""Microsoft"": ""Trace"" - } - }, - ""Custom2"": { - ""LogLevel"": { - ""Microsoft"": ""Trace"" - } - } - } -}"; - config.Reload(); - - // Act - logger.LogTrace("Message"); - - // Assert - writes = loggerProvider.Sink.Writes; - Assert.Equal(3, writes.Count); } [Fact] @@ -309,7 +181,7 @@ public void DefaultCategoryNameIsUsedIfNoneMatch() var json = @"{ ""Logging"": { - ""Name"": { + ""Microsoft.Extensions.Logging.Test.TestLoggerProvider"": { ""LogLevel"": { ""Default"": ""Information"", ""Microsoft"": ""Warning"" @@ -318,9 +190,12 @@ public void DefaultCategoryNameIsUsedIfNoneMatch() } }"; var config = CreateConfiguration(() => json); - var factory = new LoggerFactory(config.GetSection("Logging")); + var loggerProvider = new TestLoggerProvider(new TestSink(), isEnabled: true); - factory.AddProvider("Name", loggerProvider); + var factory = LoggerFactoryBuilder + .Create(config.GetSection("Logging")) + .WithProvider(loggerProvider) + .Build(); var logger = factory.CreateLogger("Microsoft"); @@ -350,21 +225,23 @@ public void DefaultCategoryNameIsUsedIfNoneMatch() [Fact] public void AddFilterForMatchingProviderFilters() { - var factory = new LoggerFactory(); var provider = new TestLoggerProvider(new TestSink(), isEnabled: true); - factory.AddProvider("Name", provider); - factory.AddFilter((name, cat, level) => - { - if (string.Equals("Name", name)) + var factory = LoggerFactoryBuilder + .Create() + .WithProvider(provider) + .WithFilters(options => options.AddFilter((name, cat, level) => { - if (string.Equals("Test", cat)) + if (string.Equals("Microsoft.Extensions.Logging.Test.TestLoggerProvider", name)) { - return level >= LogLevel.Information; + if (string.Equals("Test", cat)) + { + return level >= LogLevel.Information; + } } - } - return true; - }); + return true; + })) + .Build(); var logger = factory.CreateLogger("Test"); @@ -381,18 +258,20 @@ public void AddFilterForMatchingProviderFilters() [Fact] public void AddFilterForNonMatchingProviderDoesNotFilter() { - var factory = new LoggerFactory(); var provider = new TestLoggerProvider(new TestSink(), isEnabled: true); - factory.AddProvider("Test", provider); - factory.AddFilter((name, cat, level) => - { - if (string.Equals("None", name)) + var factory = LoggerFactoryBuilder + .Create() + .WithProvider(provider) + .WithFilters(options => options.AddFilter((name, cat, level) => { - return level >= LogLevel.Error; - } + if (string.Equals("None", name)) + { + return level >= LogLevel.Error; + } - return true; - }); + return true; + })) + .Build(); var logger = factory.CreateLogger("Test"); @@ -405,11 +284,14 @@ public void AddFilterForNonMatchingProviderDoesNotFilter() [Fact] public void AddFilterIsAdditive() { - var factory = new LoggerFactory(); var provider = new TestLoggerProvider(new TestSink(), isEnabled: true); - factory.AddProvider("Test", provider); - factory.AddFilter((name, cat, level) => level >= LogLevel.Warning); - factory.AddFilter((name, cat, level) => string.Equals(cat, "NotTest")); + var factory = LoggerFactoryBuilder + .Create() + .WithProvider(provider) + .WithFilters(options => + options.AddFilter((name, cat, level) => level >= LogLevel.Warning) + .AddFilter((name, cat, level) => string.Equals(cat, "NotTest"))) + .Build(); var logger = factory.CreateLogger("Test"); @@ -430,13 +312,13 @@ public void AddFilterIsAdditive() } [Fact] - public void AddFilterIsAdditiveWithConfigurationFilter() + public void ProviderLevelIsPreferredOverGlobalFilter() { // Arrange var json = @"{ ""Logging"": { - ""Name"": { + ""Microsoft.Extensions.Logging.Test.TestLoggerProvider"": { ""LogLevel"": { ""Test"": ""Debug"" } @@ -444,10 +326,14 @@ public void AddFilterIsAdditiveWithConfigurationFilter() } }"; var config = CreateConfiguration(() => json); - var factory = new LoggerFactory(config.GetSection("Logging")); var loggerProvider = new TestLoggerProvider(new TestSink(), isEnabled: true); - factory.AddProvider("Name", loggerProvider); - factory.AddFilter((name, cat, level) => level < LogLevel.Warning); + + var factory = LoggerFactoryBuilder + .Create(config.GetSection("Logging")) + .WithProvider(loggerProvider) + .WithFilters(options => + options.AddFilter((name, cat, level) => level < LogLevel.Critical)) + .Build(); var logger = factory.CreateLogger("Test"); @@ -463,16 +349,19 @@ public void AddFilterIsAdditiveWithConfigurationFilter() logger.LogCritical("Message"); - Assert.Equal(1, writes.Count); + Assert.Equal(2, writes.Count); } [Fact] public void AddFilterWithProviderNameCategoryNameAndFilterFuncFiltersCorrectly() { - var factory = new LoggerFactory(); var provider = new TestLoggerProvider(new TestSink(), isEnabled: true); - factory.AddProvider("Name", provider); - factory.AddFilter("Name", "Sample", l => l >= LogLevel.Warning); + var factory = LoggerFactoryBuilder + .Create() + .WithProvider(provider) + .WithFilters(options => + options.AddFilter((name, cat, level) => level >= LogLevel.Warning)) + .Build(); var logger = factory.CreateLogger("Sample.Test"); @@ -489,10 +378,13 @@ public void AddFilterWithProviderNameCategoryNameAndFilterFuncFiltersCorrectly() [Fact] public void AddFilterWithProviderNameCategoryNameAndMinLevelFiltersCorrectly() { - var factory = new LoggerFactory(); var provider = new TestLoggerProvider(new TestSink(), isEnabled: true); - factory.AddProvider("Name", provider); - factory.AddFilter("Name", "Sample", LogLevel.Warning); + var factory = LoggerFactoryBuilder + .Create() + .WithProvider(provider) + .WithFilters(options => + options.AddFilter("Sample", LogLevel.Warning)) + .Build(); var logger = factory.CreateLogger("Sample.Test"); @@ -509,30 +401,13 @@ public void AddFilterWithProviderNameCategoryNameAndMinLevelFiltersCorrectly() [Fact] public void AddFilterWithProviderNameAndCategoryFilterFuncFiltersCorrectly() { - var factory = new LoggerFactory(); - var provider = new TestLoggerProvider(new TestSink(), isEnabled: true); - factory.AddProvider("Name", provider); - factory.AddFilter("Name", (c, l) => l >= LogLevel.Warning); - - var logger = factory.CreateLogger("Sample.Test"); - - logger.LogInformation("Message"); - - var writes = provider.Sink.Writes; - Assert.Equal(0, writes.Count); - - logger.LogWarning("Message"); - - Assert.Equal(1, writes.Count); - } - - [Fact] - public void AddFilterWithProviderNameAndFilterFuncFiltersCorrectly() - { - var factory = new LoggerFactory(); var provider = new TestLoggerProvider(new TestSink(), isEnabled: true); - factory.AddProvider("Name", provider); - factory.AddFilter("Name", l => l >= LogLevel.Warning); + var factory = LoggerFactoryBuilder + .Create() + .WithProvider(provider) + .WithFilters(options => + options.AddFilter((c, l) => l >= LogLevel.Warning)) + .Build(); var logger = factory.CreateLogger("Sample.Test"); @@ -546,29 +421,6 @@ public void AddFilterWithProviderNameAndFilterFuncFiltersCorrectly() Assert.Equal(1, writes.Count); } - [Fact] - public void AddFilterWithDictionaryFiltersCorrectly() - { - var factory = new LoggerFactory(); - var provider = new TestLoggerProvider(new TestSink(), isEnabled: true); - factory.AddProvider("Name", provider); - factory.AddFilter(new Dictionary - { - { "Sample", LogLevel.Critical } - }); - - var logger = factory.CreateLogger("Sample.Test"); - - logger.LogInformation("Message"); - - var writes = provider.Sink.Writes; - Assert.Equal(0, writes.Count); - - logger.LogCritical("Message"); - - Assert.Equal(1, writes.Count); - } - internal ConfigurationRoot CreateConfiguration(Func getJson) { var provider = new TestConfiguration(new JsonConfigurationSource { Optional = true }, getJson); diff --git a/test/Microsoft.Extensions.Logging.Test/LoggerTest.cs b/test/Microsoft.Extensions.Logging.Test/LoggerTest.cs index 3ae76a2c..ecbbc442 100644 --- a/test/Microsoft.Extensions.Logging.Test/LoggerTest.cs +++ b/test/Microsoft.Extensions.Logging.Test/LoggerTest.cs @@ -3,6 +3,7 @@ using System; using System.Collections.Generic; +using Microsoft.Extensions.DependencyInjection; using Xunit; namespace Microsoft.Extensions.Logging.Test @@ -14,10 +15,12 @@ public void Log_IgnoresExceptionInIntermediateLoggersAndThrowsAggregateException { // Arrange var store = new List(); - var loggerFactory = new LoggerFactory(); - loggerFactory.AddProvider(new CustomLoggerProvider("provider1", ThrowExceptionAt.None, store)); - loggerFactory.AddProvider(new CustomLoggerProvider("provider2", ThrowExceptionAt.Log, store)); - loggerFactory.AddProvider(new CustomLoggerProvider("provider3", ThrowExceptionAt.None, store)); + var loggerFactory = LoggerFactoryBuilder.Create() + .WithProvider(new CustomLoggerProvider("provider1", ThrowExceptionAt.None, store)) + .WithProvider(new CustomLoggerProvider("provider2", ThrowExceptionAt.Log, store)) + .WithProvider(new CustomLoggerProvider("provider3", ThrowExceptionAt.None, store)) + .Build(); + var logger = loggerFactory.CreateLogger("Test"); // Act @@ -37,10 +40,12 @@ public void BeginScope_IgnoresExceptionInIntermediateLoggersAndThrowsAggregateEx { // Arrange var store = new List(); - var loggerFactory = new LoggerFactory(); - loggerFactory.AddProvider(new CustomLoggerProvider("provider1", ThrowExceptionAt.None, store)); - loggerFactory.AddProvider(new CustomLoggerProvider("provider2", ThrowExceptionAt.BeginScope, store)); - loggerFactory.AddProvider(new CustomLoggerProvider("provider3", ThrowExceptionAt.None, store)); + var loggerFactory = LoggerFactoryBuilder.Create() + .WithProvider(new CustomLoggerProvider("provider1", ThrowExceptionAt.None, store)) + .WithProvider(new CustomLoggerProvider("provider2", ThrowExceptionAt.BeginScope, store)) + .WithProvider(new CustomLoggerProvider("provider3", ThrowExceptionAt.None, store)) + .Build(); + var logger = loggerFactory.CreateLogger("Test"); // Act @@ -60,10 +65,12 @@ public void IsEnabled_IgnoresExceptionInIntermediateLoggers() { // Arrange var store = new List(); - var loggerFactory = new LoggerFactory(); - loggerFactory.AddProvider(new CustomLoggerProvider("provider1", ThrowExceptionAt.None, store)); - loggerFactory.AddProvider(new CustomLoggerProvider("provider2", ThrowExceptionAt.IsEnabled, store)); - loggerFactory.AddProvider(new CustomLoggerProvider("provider3", ThrowExceptionAt.None, store)); + var loggerFactory = LoggerFactoryBuilder.Create() + .WithProvider(new CustomLoggerProvider("provider1", ThrowExceptionAt.None, store)) + .WithProvider(new CustomLoggerProvider("provider2", ThrowExceptionAt.IsEnabled, store)) + .WithProvider(new CustomLoggerProvider("provider3", ThrowExceptionAt.None, store)) + .Build(); + var logger = loggerFactory.CreateLogger("Test"); // Act @@ -83,9 +90,11 @@ public void Log_AggregatesExceptionsFromMultipleLoggers() { // Arrange var store = new List(); - var loggerFactory = new LoggerFactory(); - loggerFactory.AddProvider(new CustomLoggerProvider("provider1", ThrowExceptionAt.Log, store)); - loggerFactory.AddProvider(new CustomLoggerProvider("provider2", ThrowExceptionAt.Log, store)); + var loggerFactory = LoggerFactoryBuilder.Create() + .WithProvider(new CustomLoggerProvider("provider1", ThrowExceptionAt.Log, store)) + .WithProvider(new CustomLoggerProvider("provider2", ThrowExceptionAt.Log, store)) + .Build(); + var logger = loggerFactory.CreateLogger("Test"); // Act @@ -108,7 +117,10 @@ public void LoggerCanGetProviderAfterItIsCreated() var store = new List(); var loggerFactory = new LoggerFactory(); var logger = loggerFactory.CreateLogger("Test"); - loggerFactory.AddProvider(new CustomLoggerProvider("provider1", ThrowExceptionAt.None, store)); + +#pragma warning disable CS0618 // Type or member is obsolete + ((ILoggerFactory)loggerFactory).AddProvider(new CustomLoggerProvider("provider1", ThrowExceptionAt.None, store)); +#pragma warning disable CS0618 // Type or member is obsolete // Act logger.LogInformation("Hello"); diff --git a/test/Microsoft.Extensions.Logging.Test/TraceSourceLoggerTest.cs b/test/Microsoft.Extensions.Logging.Test/TraceSourceLoggerTest.cs index 8dfd25dc..5936d44c 100644 --- a/test/Microsoft.Extensions.Logging.Test/TraceSourceLoggerTest.cs +++ b/test/Microsoft.Extensions.Logging.Test/TraceSourceLoggerTest.cs @@ -16,10 +16,11 @@ public static void IsEnabledReturnsCorrectValue() var testSwitch = new SourceSwitch("TestSwitch", "Level will be set to warning for this test"); testSwitch.Level = SourceLevels.Warning; - var factory = new LoggerFactory(); + var factory = LoggerFactoryBuilder.Create() + .WithServices(collection => collection.AddTraceSource(testSwitch)) + .Build(); // Act - factory.AddTraceSource(testSwitch); var logger = factory.CreateLogger("Test"); // Assert @@ -45,11 +46,17 @@ public static void MultipleLoggers_IsEnabledReturnsCorrectValue(SourceLevels fir var secondSwitch = new SourceSwitch("SecondSwitch", "Second Test Switch"); secondSwitch.Level = second; - var factory = new LoggerFactory(); - // Act - factory.AddTraceSource(firstSwitch); - factory.AddTraceSource(secondSwitch); + + var factory = LoggerFactoryBuilder.Create() + .WithServices(collection => + { + collection + .AddTraceSource(firstSwitch) + .AddTraceSource(secondSwitch); + }) + .Build(); + var logger = factory.CreateLogger("Test"); // Assert diff --git a/test/Microsoft.Extensions.Logging.Test/TraceSourceScopeTest.cs b/test/Microsoft.Extensions.Logging.Test/TraceSourceScopeTest.cs index 7a55ba07..937441f8 100644 --- a/test/Microsoft.Extensions.Logging.Test/TraceSourceScopeTest.cs +++ b/test/Microsoft.Extensions.Logging.Test/TraceSourceScopeTest.cs @@ -17,8 +17,10 @@ public static void DiagnosticsScope_PushesAndPops_LogicalOperationStack() Trace.CorrelationManager.StartLogicalOperation(baseState); var state = "1337state7331"; - var factory = new LoggerFactory(); - factory.AddTraceSource(new SourceSwitch("TestSwitch"), new ConsoleTraceListener()); + var factory = LoggerFactoryBuilder.Create() + .WithServices(collection => collection.AddTraceSource(new SourceSwitch("TestSwitch"), new ConsoleTraceListener())) + .Build(); + var logger = factory.CreateLogger("Test"); // Act diff --git a/test/Microsoft.Extensions.Logging.Testing.Tests/Microsoft.Extensions.Logging.Testing.Tests.csproj b/test/Microsoft.Extensions.Logging.Testing.Tests/Microsoft.Extensions.Logging.Testing.Tests.csproj index 0f3caba1..a75385a3 100644 --- a/test/Microsoft.Extensions.Logging.Testing.Tests/Microsoft.Extensions.Logging.Testing.Tests.csproj +++ b/test/Microsoft.Extensions.Logging.Testing.Tests/Microsoft.Extensions.Logging.Testing.Tests.csproj @@ -10,6 +10,7 @@ + diff --git a/test/Microsoft.Extensions.Logging.Testing.Tests/XunitLoggerProviderTest.cs b/test/Microsoft.Extensions.Logging.Testing.Tests/XunitLoggerProviderTest.cs index b27c4f13..2a26764c 100644 --- a/test/Microsoft.Extensions.Logging.Testing.Tests/XunitLoggerProviderTest.cs +++ b/test/Microsoft.Extensions.Logging.Testing.Tests/XunitLoggerProviderTest.cs @@ -2,6 +2,7 @@ // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System; +using Microsoft.Extensions.Logging.Test; using Xunit; namespace Microsoft.Extensions.Logging.Testing.Tests @@ -12,8 +13,10 @@ public class XunitLoggerProviderTest public void LoggerProviderWritesToTestOutputHelper() { var testTestOutputHelper = new TestTestOutputHelper(); - var loggerFactory = new LoggerFactory(); - loggerFactory.AddXunit(testTestOutputHelper); + + var loggerFactory = LoggerFactoryBuilder.Create() + .WithServices(services => services.AddXunit(testTestOutputHelper)) + .Build(); var logger = loggerFactory.CreateLogger("TestCategory"); logger.LogInformation("This is some great information"); @@ -30,8 +33,9 @@ public void LoggerProviderWritesToTestOutputHelper() public void LoggerProviderDoesNotWriteLogMessagesBelowMinimumLevel() { var testTestOutputHelper = new TestTestOutputHelper(); - var loggerFactory = new LoggerFactory(); - loggerFactory.AddXunit(testTestOutputHelper, LogLevel.Error); + var loggerFactory = LoggerFactoryBuilder.Create() + .WithServices(services => services.AddXunit(testTestOutputHelper)) + .Build(); var logger = loggerFactory.CreateLogger("TestCategory"); logger.LogInformation("This is some great information"); @@ -44,8 +48,9 @@ public void LoggerProviderDoesNotWriteLogMessagesBelowMinimumLevel() public void LoggerProviderPrependsPrefixToEachLine() { var testTestOutputHelper = new TestTestOutputHelper(); - var loggerFactory = new LoggerFactory(); - loggerFactory.AddXunit(testTestOutputHelper); + var loggerFactory = LoggerFactoryBuilder.Create() + .WithServices(services => services.AddXunit(testTestOutputHelper)) + .Build(); var logger = loggerFactory.CreateLogger("TestCategory"); logger.LogInformation("This is a" + Environment.NewLine + "multi-line" + Environment.NewLine + "message"); @@ -62,8 +67,10 @@ public void LoggerProviderPrependsPrefixToEachLine() public void LoggerProviderDoesNotThrowIfOutputHelperThrows() { var testTestOutputHelper = new TestTestOutputHelper(); - var loggerFactory = new LoggerFactory(); - loggerFactory.AddXunit(testTestOutputHelper); + var loggerFactory = LoggerFactoryBuilder.Create() + .WithServices(services => services.AddXunit(testTestOutputHelper)) + .Build(); + testTestOutputHelper.Throw = true; var logger = loggerFactory.CreateLogger("TestCategory"); From 7654420f0cac2df81658970c0bddd8c6ad60f002 Mon Sep 17 00:00:00 2001 From: Pavel Krymets Date: Tue, 23 May 2017 16:53:38 -0700 Subject: [PATCH 03/20] ILoggerBuilder all the things --- samples/SampleApp/Program.cs | 18 +- ...AzureAppServicesLoggerFactoryExtensions.cs | 17 +- .../ConsoleLoggerFactoryExtensions.cs | 33 +- .../DebugLoggerFactoryExtensions.cs | 9 +- .../EventLoggerFactoryExtensions.cs | 28 +- .../EventSourceLoggerFactoryExtensions.cs | 14 +- .../AssemblyTestLog.cs | 6 +- .../XunitLoggerFactoryExtensions.cs | 14 +- .../TraceSourceFactoryExtensions.cs | 22 +- .../FilterLoggerBuilderExtensions.cs | 59 ++ .../ILoggerBuilder.cs | 22 + src/Microsoft.Extensions.Logging/Logger.cs | 5 - .../LoggerFactory.cs | 713 +++++------------- .../LoggerFactory1.cs | 172 ----- .../LoggerFilterOptionsExtensions.cs | 60 -- .../LoggingServiceCollectionExtensions.cs | 34 +- .../EventSourceLoggerTest.cs | 42 +- .../LoggerBuilderTestExtensions.cs | 35 + .../LoggerFactoryBuilder.cs | 54 -- .../LoggerFilterTest.cs | 49 +- .../LoggerTest.cs | 9 +- .../TraceSourceLoggerTest.cs | 14 +- .../TraceSourceScopeTest.cs | 4 +- .../XunitLoggerProviderTest.cs | 16 +- 24 files changed, 438 insertions(+), 1011 deletions(-) create mode 100644 src/Microsoft.Extensions.Logging/FilterLoggerBuilderExtensions.cs create mode 100644 src/Microsoft.Extensions.Logging/ILoggerBuilder.cs delete mode 100644 src/Microsoft.Extensions.Logging/LoggerFactory1.cs delete mode 100644 src/Microsoft.Extensions.Logging/LoggerFilterOptionsExtensions.cs create mode 100644 test/Microsoft.Extensions.Logging.Test/LoggerBuilderTestExtensions.cs delete mode 100644 test/Microsoft.Extensions.Logging.Test/LoggerFactoryBuilder.cs diff --git a/samples/SampleApp/Program.cs b/samples/SampleApp/Program.cs index 03b135a2..7123d367 100644 --- a/samples/SampleApp/Program.cs +++ b/samples/SampleApp/Program.cs @@ -25,25 +25,21 @@ public Program() // A Web App based program would configure logging via the WebHostBuilder. // Create a logger factory with filters that can be applied across all logger providers. var serviceCollection = new ServiceCollection(); - serviceCollection.AddLogging(loggingConfiguration.GetSection("Logging")); - - serviceCollection.Configure(options => - { - options - .AddFilter("Microsoft", LogLevel.Warning) - .AddFilter("System", LogLevel.Warning) - .AddFilter("SampleApp.Program", LogLevel.Debug); - }); + var loggingBuilder = serviceCollection + .AddLogging(loggingConfiguration.GetSection("Logging")) + .AddFilter("Microsoft", LogLevel.Warning) + .AddFilter("System", LogLevel.Warning) + .AddFilter("SampleApp.Program", LogLevel.Debug); // providers may be added to a LoggerFactory before any loggers are created #if NET46 - serviceCollection.AddEventLog(); + loggingBuilder.AddEventLog(); #elif NETCOREAPP2_0 #else #error Target framework needs to be updated #endif - serviceCollection.AddConsole(); + loggingBuilder.AddConsole(); var serviceProvider = serviceCollection.BuildServiceProvider(); // getting the logger using the class's name is conventional diff --git a/src/Microsoft.Extensions.Logging.AzureAppServices/AzureAppServicesLoggerFactoryExtensions.cs b/src/Microsoft.Extensions.Logging.AzureAppServices/AzureAppServicesLoggerFactoryExtensions.cs index 43660deb..1368ff65 100644 --- a/src/Microsoft.Extensions.Logging.AzureAppServices/AzureAppServicesLoggerFactoryExtensions.cs +++ b/src/Microsoft.Extensions.Logging.AzureAppServices/AzureAppServicesLoggerFactoryExtensions.cs @@ -16,27 +16,26 @@ public static class AzureAppServicesLoggerFactoryExtensions /// /// Adds an Azure Web Apps diagnostics logger. /// - /// The extension method argument - public static IServiceCollection AddAzureWebAppDiagnostics(this IServiceCollection collection) + /// The extension method argument + public static ILoggerBuilder AddAzureWebAppDiagnostics(this ILoggerBuilder builder) { - return AddAzureWebAppDiagnostics(collection, null); + return AddAzureWebAppDiagnostics(builder, null); } /// /// Adds an Azure Web Apps diagnostics logger. /// - /// The extension method argument + /// The extension method argument /// The setting object to configure loggers. - public static IServiceCollection AddAzureWebAppDiagnostics(this IServiceCollection collection, AzureAppServicesDiagnosticsSettings settings) + public static ILoggerBuilder AddAzureWebAppDiagnostics(this ILoggerBuilder builder, AzureAppServicesDiagnosticsSettings settings) { - collection.AddLogging(); if (WebAppContext.Default.IsRunningInAzureWebApp) { // Only add the provider if we're in Azure WebApp. That cannot change once the apps started - collection.AddSingleton(WebAppContext.Default); - collection.AddSingleton(new AzureAppServicesDiagnosticsLoggerProvider(WebAppContext.Default, settings ?? new AzureAppServicesDiagnosticsSettings())); + builder.Services.AddSingleton(WebAppContext.Default); + builder.Services.AddSingleton(new AzureAppServicesDiagnosticsLoggerProvider(WebAppContext.Default, settings ?? new AzureAppServicesDiagnosticsSettings())); } - return collection; + return builder; } /// diff --git a/src/Microsoft.Extensions.Logging.Console/ConsoleLoggerFactoryExtensions.cs b/src/Microsoft.Extensions.Logging.Console/ConsoleLoggerFactoryExtensions.cs index ea7183ec..e71927da 100644 --- a/src/Microsoft.Extensions.Logging.Console/ConsoleLoggerFactoryExtensions.cs +++ b/src/Microsoft.Extensions.Logging.Console/ConsoleLoggerFactoryExtensions.cs @@ -13,51 +13,48 @@ public static class ConsoleLoggerExtensions /// /// Adds a console logger named 'Console' to the factory. /// - /// The to use. - public static IServiceCollection AddConsole(this IServiceCollection collection) + /// The to use. + public static ILoggerBuilder AddConsole(this ILoggerBuilder builder) { - collection.AddLogging(); - collection.AddSingleton(); + builder.Services.AddSingleton(); - return collection; + return builder; } /// /// Adds a console logger named 'Console' to the factory. /// - /// The to use. + /// The to use. /// - public static IServiceCollection AddConsole(this IServiceCollection collection, Action configure) + public static ILoggerBuilder AddConsole(this ILoggerBuilder builder, Action configure) { if (configure == null) { throw new ArgumentNullException(nameof(configure)); } - collection.AddConsole(); - if (configure != null) - { - collection.Configure(configure); - } - return collection; + builder.AddConsole(); + builder.Services.Configure(configure); + + return builder; } /// /// Adds a console logger named 'Console' to the factory. /// - /// The to use. + /// The to use. /// - public static IServiceCollection AddConsole(this IServiceCollection collection, IConfiguration configuration) + public static ILoggerBuilder AddConsole(this ILoggerBuilder builder, IConfiguration configuration) { if (configuration == null) { throw new ArgumentNullException(nameof(configuration)); } - collection.AddConsole(); - collection.Configure(configuration); + builder.AddConsole(); + builder.Services.Configure(configuration); - return collection; + return builder; } /// diff --git a/src/Microsoft.Extensions.Logging.Debug/DebugLoggerFactoryExtensions.cs b/src/Microsoft.Extensions.Logging.Debug/DebugLoggerFactoryExtensions.cs index 14385a53..1d70bd3b 100644 --- a/src/Microsoft.Extensions.Logging.Debug/DebugLoggerFactoryExtensions.cs +++ b/src/Microsoft.Extensions.Logging.Debug/DebugLoggerFactoryExtensions.cs @@ -15,13 +15,12 @@ public static class DebugLoggerFactoryExtensions /// /// Adds a debug logger named 'Debug' to the factory. /// - /// The extension method argument. - public static IServiceCollection AddDebug(this IServiceCollection collection) + /// The extension method argument. + public static ILoggerBuilder AddDebug(this ILoggerBuilder builder) { - collection.AddLogging(); - collection.AddSingleton(); + builder.Services.AddSingleton(); - return collection; + return builder; } /// diff --git a/src/Microsoft.Extensions.Logging.EventLog/EventLoggerFactoryExtensions.cs b/src/Microsoft.Extensions.Logging.EventLog/EventLoggerFactoryExtensions.cs index c619f05d..4064f495 100644 --- a/src/Microsoft.Extensions.Logging.EventLog/EventLoggerFactoryExtensions.cs +++ b/src/Microsoft.Extensions.Logging.EventLog/EventLoggerFactoryExtensions.cs @@ -15,32 +15,29 @@ public static class EventLoggerFactoryExtensions /// /// Adds an event logger named 'EventLog' to the factory. /// - /// The extension method argument. - public static IServiceCollection AddEventLog(this IServiceCollection collection) + /// The extension method argument. + public static ILoggerBuilder AddEventLog(this ILoggerBuilder builder) { - if (collection == null) + if (builder == null) { - throw new ArgumentNullException(nameof(collection)); + throw new ArgumentNullException(nameof(builder)); } - collection.AddLogging(); - collection.AddSingleton(); + builder.Services.AddSingleton(); - return collection; + return builder; } /// /// Adds an event logger. Use to enable logging for specific s. /// - /// The extension method argument. + /// The extension method argument. /// The . - public static IServiceCollection AddEventLog( - this IServiceCollection collection, - EventLogSettings settings) + public static ILoggerBuilder AddEventLog(this ILoggerBuilder builder, EventLogSettings settings) { - if (collection == null) + if (builder == null) { - throw new ArgumentNullException(nameof(collection)); + throw new ArgumentNullException(nameof(builder)); } if (settings == null) @@ -48,10 +45,9 @@ public static IServiceCollection AddEventLog( throw new ArgumentNullException(nameof(settings)); } - collection.AddLogging(); - collection.AddSingleton(new EventLogLoggerProvider(settings)); + builder.Services.AddSingleton(new EventLogLoggerProvider(settings)); - return collection; + return builder; } /// diff --git a/src/Microsoft.Extensions.Logging.EventSource/EventSourceLoggerFactoryExtensions.cs b/src/Microsoft.Extensions.Logging.EventSource/EventSourceLoggerFactoryExtensions.cs index 647f3ae6..5e9fff88 100644 --- a/src/Microsoft.Extensions.Logging.EventSource/EventSourceLoggerFactoryExtensions.cs +++ b/src/Microsoft.Extensions.Logging.EventSource/EventSourceLoggerFactoryExtensions.cs @@ -15,20 +15,18 @@ public static class EventSourceLoggerFactoryExtensions /// /// Adds an event logger named 'EventSource' to the factory. /// - /// The extension method argument. - public static IServiceCollection AddEventSourceLogger(this IServiceCollection collection) + /// The extension method argument. + public static ILoggerBuilder AddEventSourceLogger(this ILoggerBuilder builder) { - if (collection == null) + if (builder == null) { - throw new ArgumentNullException(nameof(collection)); + throw new ArgumentNullException(nameof(builder)); } - collection.AddLogging(); - var loggerProvider = LoggingEventSource.Instance.CreateLoggerProvider(); - collection.AddSingleton(loggerProvider); + builder.Services.AddSingleton(loggerProvider); - return collection; + return builder; } /// diff --git a/src/Microsoft.Extensions.Logging.Testing/AssemblyTestLog.cs b/src/Microsoft.Extensions.Logging.Testing/AssemblyTestLog.cs index 1e118104..79fb44ad 100644 --- a/src/Microsoft.Extensions.Logging.Testing/AssemblyTestLog.cs +++ b/src/Microsoft.Extensions.Logging.Testing/AssemblyTestLog.cs @@ -55,11 +55,11 @@ public IDisposable StartTestLog(ITestOutputHelper output, string className, out public ILoggerFactory CreateLoggerFactory(ITestOutputHelper output, string className, [CallerMemberName] string testName = null) { var serviceCollection = new ServiceCollection(); - serviceCollection.AddLogging(); + var loggerBuilder = serviceCollection.AddLogging(); if (output != null) { - serviceCollection.AddXunit(output, LogLevel.Debug); + loggerBuilder.AddXunit(output, LogLevel.Debug); } var loggerFactory = serviceCollection.BuildServiceProvider().GetRequiredService(); @@ -84,7 +84,7 @@ public static AssemblyTestLog Create(string assemblyName, string baseDirectory) var serviceCollection = new ServiceCollection(); // Let the global logger log to the console, it's just "Starting X..." "Finished X..." - serviceCollection.AddConsole(); + serviceCollection.AddLogging().AddConsole(); var loggerFactory = serviceCollection.BuildServiceProvider().GetRequiredService(); diff --git a/src/Microsoft.Extensions.Logging.Testing/XunitLoggerFactoryExtensions.cs b/src/Microsoft.Extensions.Logging.Testing/XunitLoggerFactoryExtensions.cs index d639ecbf..0f857571 100644 --- a/src/Microsoft.Extensions.Logging.Testing/XunitLoggerFactoryExtensions.cs +++ b/src/Microsoft.Extensions.Logging.Testing/XunitLoggerFactoryExtensions.cs @@ -10,18 +10,16 @@ namespace Microsoft.Extensions.Logging { public static class XunitLoggerFactoryExtensions { - public static IServiceCollection AddXunit(this IServiceCollection services, ITestOutputHelper output) + public static ILoggerBuilder AddXunit(this ILoggerBuilder builder, ITestOutputHelper output) { - services.AddLogging(); - services.AddSingleton(new XunitLoggerProvider(output)); - return services; + builder.Services.AddSingleton(new XunitLoggerProvider(output)); + return builder; } - public static IServiceCollection AddXunit(this IServiceCollection services, ITestOutputHelper output, LogLevel minLevel) + public static ILoggerBuilder AddXunit(this ILoggerBuilder builder, ITestOutputHelper output, LogLevel minLevel) { - services.AddLogging(); - services.AddSingleton(new XunitLoggerProvider(output)); - return services; + builder.Services.AddSingleton(new XunitLoggerProvider(output)); + return builder; } [Obsolete("This method is obsolete and will be removed in a future version. The recommended alternative is to call the Microsoft.Extensions.Logging.AddXunit() extension method on the Microsoft.Extensions.Logging.LoggerFactory instance.")] diff --git a/src/Microsoft.Extensions.Logging.TraceSource/TraceSourceFactoryExtensions.cs b/src/Microsoft.Extensions.Logging.TraceSource/TraceSourceFactoryExtensions.cs index c1b23118..1702f44c 100644 --- a/src/Microsoft.Extensions.Logging.TraceSource/TraceSourceFactoryExtensions.cs +++ b/src/Microsoft.Extensions.Logging.TraceSource/TraceSourceFactoryExtensions.cs @@ -15,8 +15,8 @@ public static class TraceSourceFactoryExtensions /// /// The to use. /// The name of the to use. - public static IServiceCollection AddTraceSource( - this IServiceCollection collection, + public static ILoggerBuilder AddTraceSource( + this ILoggerBuilder collection, string switchName) { if (collection == null) @@ -38,8 +38,8 @@ public static IServiceCollection AddTraceSource( /// The to use. /// The name of the to use. /// The to use. - public static IServiceCollection AddTraceSource( - this IServiceCollection collection, + public static ILoggerBuilder AddTraceSource( + this ILoggerBuilder collection, string switchName, TraceListener listener) { @@ -66,8 +66,8 @@ public static IServiceCollection AddTraceSource( /// /// The to use. /// The to use. - public static IServiceCollection AddTraceSource( - this IServiceCollection collection, + public static ILoggerBuilder AddTraceSource( + this ILoggerBuilder collection, SourceSwitch sourceSwitch) { if (collection == null) @@ -80,8 +80,7 @@ public static IServiceCollection AddTraceSource( throw new ArgumentNullException(nameof(sourceSwitch)); } - collection.AddLogging(); - collection.AddSingleton(new TraceSourceLoggerProvider(sourceSwitch)); + collection.Services.AddSingleton(new TraceSourceLoggerProvider(sourceSwitch)); return collection; } @@ -92,8 +91,8 @@ public static IServiceCollection AddTraceSource( /// The to use. /// The to use. /// The to use. - public static IServiceCollection AddTraceSource( - this IServiceCollection collection, + public static ILoggerBuilder AddTraceSource( + this ILoggerBuilder collection, SourceSwitch sourceSwitch, TraceListener listener) { @@ -112,8 +111,7 @@ public static IServiceCollection AddTraceSource( throw new ArgumentNullException(nameof(listener)); } - collection.AddLogging(); - collection.AddSingleton(new TraceSourceLoggerProvider(sourceSwitch, listener)); + collection.Services.AddSingleton(new TraceSourceLoggerProvider(sourceSwitch, listener)); return collection; } diff --git a/src/Microsoft.Extensions.Logging/FilterLoggerBuilderExtensions.cs b/src/Microsoft.Extensions.Logging/FilterLoggerBuilderExtensions.cs new file mode 100644 index 00000000..2f189854 --- /dev/null +++ b/src/Microsoft.Extensions.Logging/FilterLoggerBuilderExtensions.cs @@ -0,0 +1,59 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System; +using Microsoft.Extensions.Logging; + +namespace Microsoft.Extensions.DependencyInjection +{ + /// + /// Extension methods for setting up logging services in an . + /// + public static class FilterLoggerBuilderExtensions + { + public static ILoggerBuilder AddFilter(this ILoggerBuilder builder, string category, LogLevel level) + { + return AddRule(builder, new LoggerFilterRule(null, category, level, null)); + } + + public static ILoggerBuilder AddFilter(this ILoggerBuilder builder, string category, LogLevel level) + { + return AddRule(builder, new LoggerFilterRule(typeof(T).FullName, category, level, null)); + } + + public static ILoggerBuilder AddFilter(this ILoggerBuilder builder, LogMessageFilter filter) + { + return AddRule(builder, new LoggerFilterRule(null, null, null, filter)); + } + public static ILoggerBuilder AddFilter(this ILoggerBuilder builder, LogMessageFilter filter) + { + return AddRule(builder, new LoggerFilterRule(typeof(T).FullName, null, null, filter)); + } + + public static ILoggerBuilder AddFilter(this ILoggerBuilder builder, Func categoryLevelFilter) + { + return AddRule(builder, new LoggerFilterRule(null, null, null, (type, name, level) => categoryLevelFilter(name, level))); + } + + public static ILoggerBuilder AddFilter(this ILoggerBuilder builder, Func categoryLevelFilter) + { + return AddRule(builder, new LoggerFilterRule(typeof(T).FullName, null, null, (type, name, level) => categoryLevelFilter(name, level))); + } + + public static ILoggerBuilder AddFilter(this ILoggerBuilder builder, string category, Func levelFilter) + { + return AddRule(builder, new LoggerFilterRule(null, category, null, (type, name, level) => levelFilter(level))); + } + + public static ILoggerBuilder AddFilter(this ILoggerBuilder builder, string category, Func levelFilter) + { + return AddRule(builder, new LoggerFilterRule(typeof(T).FullName, category, null, (type, name, level) => levelFilter(level))); + } + + private static ILoggerBuilder AddRule(ILoggerBuilder builder, LoggerFilterRule loggerFilterRule) + { + builder.Services.Configure(options => options.Rules.Add(loggerFilterRule)); + return builder; + } + } +} \ No newline at end of file diff --git a/src/Microsoft.Extensions.Logging/ILoggerBuilder.cs b/src/Microsoft.Extensions.Logging/ILoggerBuilder.cs new file mode 100644 index 00000000..a386c627 --- /dev/null +++ b/src/Microsoft.Extensions.Logging/ILoggerBuilder.cs @@ -0,0 +1,22 @@ +using Microsoft.Extensions.DependencyInjection; + +namespace Microsoft.Extensions.Logging +{ + public interface ILoggerBuilder + { + /// + /// Gets the where Logging services are configured. + /// + IServiceCollection Services { get; } + } + + public class LoggerBuilder : ILoggerBuilder + { + public LoggerBuilder(IServiceCollection services) + { + Services = services; + } + + public IServiceCollection Services { get; } + } +} \ No newline at end of file diff --git a/src/Microsoft.Extensions.Logging/Logger.cs b/src/Microsoft.Extensions.Logging/Logger.cs index e00b2eba..44aaaf0b 100644 --- a/src/Microsoft.Extensions.Logging/Logger.cs +++ b/src/Microsoft.Extensions.Logging/Logger.cs @@ -194,11 +194,6 @@ public void Dispose() _isDisposed = true; } } - - internal void Add(IDisposable disposable) - { - throw new NotImplementedException(); - } } public struct LoggerInformation diff --git a/src/Microsoft.Extensions.Logging/LoggerFactory.cs b/src/Microsoft.Extensions.Logging/LoggerFactory.cs index ce3a8f9c..43965068 100644 --- a/src/Microsoft.Extensions.Logging/LoggerFactory.cs +++ b/src/Microsoft.Extensions.Logging/LoggerFactory.cs @@ -1,541 +1,172 @@ -//// Copyright (c) .NET Foundation. All rights reserved. -//// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. - -//using System; -//using System.Collections.Generic; -//using System.Linq; -//using Microsoft.Extensions.Configuration; -//using Microsoft.Extensions.Primitives; - -//namespace Microsoft.Extensions.Logging -//{ -// /// -// /// Summary description for LoggerFactory -// /// -// public class LoggerFactory : ILoggerFactory -// { -// private readonly Dictionary _loggers = new Dictionary(StringComparer.Ordinal); -// private KeyValuePair[] _providers = new KeyValuePair[0]; -// private readonly object _sync = new object(); -// private volatile bool _disposed; -// private IConfiguration _configuration; -// private IChangeToken _changeToken; -// private IDisposable _changeTokenRegistration; -// private Dictionary _defaultFilter; -// private Func _genericFilters; -// private Dictionary> _providerFilters = new Dictionary>(); -// private Dictionary> _categoryFilters = new Dictionary>(); - -// private static readonly Func _trueFilter = (providerName, category, level) => true; -// private static readonly Func _categoryTrueFilter = (n, l) => true; - -// public LoggerFactory() -// { -// _genericFilters = _trueFilter; -// } - -// public LoggerFactory(IConfiguration configuration) -// : this() -// { -// if (configuration == null) -// { -// throw new ArgumentNullException(nameof(configuration)); -// } - -// UseConfiguration(configuration); -// } - -// /// -// /// Replaces the used for filtering. -// /// -// /// The new configuration to use. -// /// The so that additional calls can be chained. -// public LoggerFactory UseConfiguration(IConfiguration configuration) -// { -// if (configuration == _configuration) -// { -// return this; -// } - -// // unregister the previous configuration callback if there was one -// _changeTokenRegistration?.Dispose(); - -// _configuration = configuration; - -// if (configuration == null) -// { -// _changeToken = null; -// _changeTokenRegistration = null; -// } -// else -// { -// _changeToken = _configuration.GetReloadToken(); -// _changeTokenRegistration = _changeToken?.RegisterChangeCallback(OnConfigurationReload, null); -// } - -// LoadDefaultConfigValues(); - -// return this; -// } - -// public ILogger CreateLogger(string categoryName) -// { -// if (CheckDisposed()) -// { -// throw new ObjectDisposedException(nameof(LoggerFactory)); -// } - -// Logger logger; -// lock (_sync) -// { -// if (!_loggers.TryGetValue(categoryName, out logger)) -// { -// Func filter = _categoryTrueFilter; -// foreach (var prefix in GetKeyPrefixes(categoryName)) -// { -// if (_categoryFilters.TryGetValue(prefix, out var categoryFilter)) -// { -// var previousFilter = filter; -// filter = (providerName, level) => -// { -// if (previousFilter(providerName, level)) -// { -// return categoryFilter(providerName, level); -// } - -// return false; -// }; -// } -// } -// logger = new Logger(this, categoryName, filter); -// _loggers[categoryName] = logger; -// } -// } - -// return logger; -// } - -// public void AddProvider(ILoggerProvider provider) -// { -// // REVIEW: Should we do the name resolution for our providers like this? -// var name = string.Empty; -// switch (provider.GetType().FullName) -// { -// case "Microsoft.Extensions.Logging.ConsoleLoggerProvider": -// name = "Console"; -// break; -// case "Microsoft.Extensions.Logging.DebugLoggerProvider": -// name = "Debug"; -// break; -// case "Microsoft.Extensions.Logging.AzureAppServices.Internal.AzureAppServicesDiagnosticsLoggerProvider": -// name = "AzureAppServices"; -// break; -// case "Microsoft.Extensions.Logging.EventLog.EventLogLoggerProvider": -// name = "EventLog"; -// break; -// case "Microsoft.Extensions.Logging.TraceSource.TraceSourceLoggerProvider": -// name = "TraceSource"; -// break; -// case "Microsoft.Extensions.Logging.EventSource.EventSourceLoggerProvider": -// name = "EventSource"; -// break; -// } - -// AddProvider(name, provider); -// } - -// public void AddProvider(string providerName, ILoggerProvider provider) -// { -// if (CheckDisposed()) -// { -// throw new ObjectDisposedException(nameof(LoggerFactory)); -// } - -// lock (_sync) -// { -// _providers = _providers.Concat(new[] { new KeyValuePair(provider, providerName) }).ToArray(); - -// foreach (var logger in _loggers) -// { -// logger.Value.AddProvider(providerName, provider); -// } -// } -// } - -// /// -// /// Adds a filter that applies to and with the given -// /// . -// /// -// /// The name of the provider. -// /// The name of the logger category. -// /// The filter that applies to logs for and . -// /// Returning true means allow log through, false means reject log. -// /// The so that additional calls can be chained. -// public LoggerFactory AddFilter(string providerName, string categoryName, Func filter) -// { -// if (filter == null) -// { -// throw new ArgumentNullException(nameof(filter)); -// } - -// lock (_sync) -// { -// if (_categoryFilters.TryGetValue(categoryName, out var previousFilter)) -// { -// _categoryFilters[categoryName] = (currentProviderName, level) => -// { -// if (previousFilter(currentProviderName, level)) -// { -// if (string.Equals(providerName, currentProviderName)) -// { -// return filter(level); -// } - -// return true; -// } - -// return false; -// }; -// } -// else -// { -// _categoryFilters[categoryName] = (currentProviderName, level) => -// { -// if (string.Equals(providerName, currentProviderName)) -// { -// return filter(level); -// } - -// return true; -// }; -// } -// } - -// return this; -// } - -// /// -// /// Adds a filter that applies to with the given . -// /// -// /// The name of the provider. -// /// The filter that applies to logs for . -// /// The string argument is the category being logged to. -// /// Returning true means allow log through, false means reject log. -// /// The so that additional calls can be chained. -// public LoggerFactory AddFilter(string providerName, Func filter) -// { -// if (filter == null) -// { -// throw new ArgumentNullException(nameof(filter)); -// } - -// lock (_sync) -// { -// if (_providerFilters.TryGetValue(providerName, out var value)) -// { -// _providerFilters[providerName] = (categoryName, level) => -// { -// if (value(categoryName, level)) -// { -// return filter(categoryName, level); -// } - -// return false; -// }; -// } -// else -// { -// _providerFilters[providerName] = (category, level) => filter(category, level); -// } -// } - -// return this; -// } - -// /// -// /// Adds a filter that applies to all logs. -// /// -// /// The filter that applies to logs. -// /// The first string is the provider name and the second string is the category name being logged to. -// /// Returning true means allow log through, false means reject log. -// /// The so that additional calls can be chained. -// public LoggerFactory AddFilter(Func filter) -// { -// if (filter == null) -// { -// throw new ArgumentNullException(nameof(filter)); -// } - -// lock (_sync) -// { -// var previousFilters = _genericFilters; -// _genericFilters = (providerName, category, level) => -// { -// if (previousFilters(providerName, category, level)) -// { -// return filter(providerName, category, level); -// } - -// return false; -// }; -// } - -// return this; -// } - -// /// -// /// Adds a filter to all logs. -// /// -// /// The filter that applies to logs. -// /// The key is the category and the is the minimum level allowed. -// /// The so that additional calls can be chained. -// public LoggerFactory AddFilter(IDictionary filter) -// { -// if (filter == null) -// { -// throw new ArgumentNullException(nameof(filter)); -// } - -// lock (_sync) -// { -// foreach (var kvp in filter) -// { -// if (_categoryFilters.TryGetValue(kvp.Key, out var currentFilter)) -// { -// _categoryFilters[kvp.Key] = (providerName, level) => -// { -// if (currentFilter(providerName, level)) -// { -// return level >= kvp.Value; -// } - -// return false; -// }; -// } -// else -// { -// _categoryFilters[kvp.Key] = (providerName, level) => level >= kvp.Value; -// } -// } -// } - -// return this; -// } - -// /// -// /// Adds a filter that applies to and , allowing logs with the given -// /// minimum or higher. -// /// -// /// The name of the provider. -// /// The name of the logger category. -// /// The minimum that logs from -// /// and are allowed. -// public LoggerFactory AddFilter(string providerName, string categoryName, LogLevel minLevel) -// { -// return AddFilter(providerName, categoryName, level => level >= minLevel); -// } - -// /// -// /// Adds a filter that applies to with the given -// /// . -// /// -// /// The name of the provider. -// /// The filter that applies to logs for . -// /// Returning true means allow log through, false means reject log. -// public LoggerFactory AddFilter(string providerName, Func filter) -// { -// if (filter == null) -// { -// throw new ArgumentNullException(nameof(filter)); -// } - -// // Using 'Default' for the category name means this filter will apply for all category names -// return AddFilter(providerName, "Default", filter); -// } - -// // TODO: Figure out how to do this better, perhaps a new IConfigurableLogger interface? -// public IConfiguration Configuration => _configuration; - -// internal KeyValuePair[] GetProviders() -// { -// return _providers; -// } - -// internal bool IsEnabled(List providerNames, string categoryName, LogLevel currentLevel) -// { -// if (_genericFilters != _trueFilter || _providerFilters.Count > 0) -// { -// foreach (var providerName in providerNames) -// { -// if (string.IsNullOrEmpty(providerName)) -// { -// continue; -// } - -// if (_providerFilters.TryGetValue(providerName, out var filter)) -// { -// if (!filter(categoryName, currentLevel)) -// { -// return false; -// } -// } - -// if (_genericFilters != _trueFilter) -// { -// // filters from factory.AddFilter(Func) -// if (!_genericFilters(providerName, categoryName, currentLevel)) -// { -// return false; -// } -// } -// } -// } - -// if (_configuration != null) -// { -// // need to loop over this separately because _filters can apply to multiple providerNames -// // but the configuration prefers early providerNames and will early out if a match is found -// foreach (var providerName in providerNames) -// { -// // TODO: Caching? -// var logLevelSection = _configuration.GetSection($"{providerName}:LogLevel"); -// if (logLevelSection != null) -// { -// foreach (var prefix in GetKeyPrefixes(categoryName)) -// { -// if (TryGetSwitch(logLevelSection[prefix], out var configLevel)) -// { -// return currentLevel >= configLevel; -// } -// } -// } -// } -// } - -// if (_defaultFilter == null) -// { -// return true; -// } - -// // get a local reference to the filter so that if the config is reloaded then `_defaultFilter` -// // doesn't change while we are accessing it -// var localDefaultFilter = _defaultFilter; - -// // No specific filter for this logger, check defaults -// foreach (var prefix in GetKeyPrefixes(categoryName)) -// { -// if (localDefaultFilter.TryGetValue(prefix, out var defaultLevel)) -// { -// return currentLevel >= defaultLevel; -// } -// } - -// return true; -// } - -// private void OnConfigurationReload(object state) -// { -// _changeToken = _configuration.GetReloadToken(); -// try -// { -// LoadDefaultConfigValues(); -// } -// catch (Exception /*ex*/) -// { -// // TODO: Can we do anything? -// //Console.WriteLine($"Error while loading configuration changes.{Environment.NewLine}{ex}"); -// } -// finally -// { -// // The token will change each time it reloads, so we need to register again. -// _changeTokenRegistration = _changeToken.RegisterChangeCallback(OnConfigurationReload, null); -// } -// } - -// private static bool TryGetSwitch(string value, out LogLevel level) -// { -// if (string.IsNullOrEmpty(value)) -// { -// level = LogLevel.None; -// return false; -// } -// else if (Enum.TryParse(value, true, out level)) -// { -// return true; -// } -// else -// { -// var message = $"Configuration value '{value}' is not supported."; -// throw new InvalidOperationException(message); -// } -// } - -// private static IEnumerable GetKeyPrefixes(string name) -// { -// while (!string.IsNullOrEmpty(name)) -// { -// yield return name; -// var lastIndexOfDot = name.LastIndexOf('.'); -// if (lastIndexOfDot == -1) -// { -// yield return "Default"; -// break; -// } -// name = name.Substring(0, lastIndexOfDot); -// } -// } - -// private void LoadDefaultConfigValues() -// { -// var replacementDefaultFilters = new Dictionary(); -// if (_configuration == null) -// { -// _defaultFilter = replacementDefaultFilters; -// return; -// } - -// var logLevelSection = _configuration.GetSection("LogLevel"); - -// if (logLevelSection != null) -// { -// foreach (var section in logLevelSection.AsEnumerable(true)) -// { -// if (TryGetSwitch(section.Value, out var level)) -// { -// replacementDefaultFilters[section.Key] = level; -// } -// } -// } - -// _defaultFilter = replacementDefaultFilters; -// } - -// /// -// /// Check if the factory has been disposed. -// /// -// /// True when as been called -// protected virtual bool CheckDisposed() => _disposed; - -// public void Dispose() -// { -// if (!_disposed) -// { -// _disposed = true; - -// _changeTokenRegistration?.Dispose(); - -// foreach (var provider in _providers) -// { -// try -// { -// provider.Key.Dispose(); -// } -// catch -// { -// // Swallow exceptions on dispose -// } -// } -// } -// } -// } -//} \ No newline at end of file +using System; +using System.Collections.Generic; +using System.Linq; +using Microsoft.Extensions.Options; + +namespace Microsoft.Extensions.Logging +{ + public class LoggerFactory : ILoggerFactory + { + private struct ProviderRegistration + { + public ILoggerProvider Provider; + public bool ShouldDispose; + } + + private static readonly LoggerRuleSelector RuleSelector = new LoggerRuleSelector(); + + private readonly Dictionary _loggers = new Dictionary(StringComparer.Ordinal); + + private readonly List _providerRegistrations; + private readonly object _sync = new object(); + private volatile bool _disposed; + private IDisposable _changeTokenRegistration; + private LoggerFilterOptions _filterOptions; + + public LoggerFactory() : this(new LoggerFilterOptions()) + { + } + + public LoggerFactory(LoggerFilterOptions filterOptions) : this(Enumerable.Empty(), new StaticFilterOptionsMonitor(filterOptions)) + { + } + + public LoggerFactory(IEnumerable providers, IOptionsMonitor filterOption) + { + _providerRegistrations = providers.Select(provider => new ProviderRegistration { Provider = provider}).ToList(); + _changeTokenRegistration = filterOption.OnChange(RefreshFilters); + RefreshFilters(filterOption.CurrentValue); + } + + private void RefreshFilters(LoggerFilterOptions filterOptions) + { + lock (_sync) + { + _filterOptions = filterOptions; + foreach (var logger in _loggers) + { + var loggerInformation = logger.Value.Loggers; + var categoryName = logger.Key; + + ApplyRules(loggerInformation, categoryName, 0, loggerInformation.Length); + } + } + } + + public ILogger CreateLogger(string categoryName) + { + if (CheckDisposed()) + { + throw new ObjectDisposedException(nameof(LoggerFactory)); + } + + lock (_sync) + { + Logger logger; + + if (!_loggers.TryGetValue(categoryName, out logger)) + { + + logger = new Logger() + { + Loggers = CreateLoggers(categoryName) + }; + _loggers[categoryName] = logger; + } + + return logger; + } + } + + void ILoggerFactory.AddProvider(ILoggerProvider provider) + { + if (CheckDisposed()) + { + throw new ObjectDisposedException(nameof(LoggerFactory)); + } + + lock (_sync) + { + _providerRegistrations.Add(new ProviderRegistration { Provider = provider, ShouldDispose = true}); + foreach (var logger in _loggers) + { + var loggerInformation = logger.Value.Loggers; + var categoryName = logger.Key; + + Array.Resize(ref loggerInformation, loggerInformation.Length + 1); + var newLoggerIndex = loggerInformation.Length - 1; + loggerInformation[newLoggerIndex].Logger = provider.CreateLogger(categoryName); + loggerInformation[newLoggerIndex].ProviderType = provider.GetType().FullName; + + ApplyRules(loggerInformation, categoryName, newLoggerIndex, 1); + + logger.Value.Loggers = loggerInformation; + } + } + } + + private Logger.LoggerInformation[] CreateLoggers(string categoryName) + { + Logger.LoggerInformation[] loggers = new Logger.LoggerInformation[_providerRegistrations.Count]; + for (int i = 0; i < _providerRegistrations.Count; i++) + { + var provider = _providerRegistrations[i].Provider; + + loggers[i].Logger = provider.CreateLogger(categoryName); + loggers[i].ProviderType = provider.GetType().FullName; + } + + ApplyRules(loggers, categoryName, 0, loggers.Length); + return loggers; + } + + private void ApplyRules(Logger.LoggerInformation[] loggers, string categoryName, int start, int count) + { + for (var index = start; index < start + count; index++) + { + ref var loggerInformation = ref loggers[index]; + + RuleSelector.Select(_filterOptions, + loggerInformation.ProviderType, + categoryName, + out var minLevel, + out var filter); + + loggerInformation.Category = categoryName; + loggerInformation.MinLevel = minLevel; + loggerInformation.Filter = filter; + } + } + + /// + /// Check if the factory has been disposed. + /// + /// True when as been called + protected virtual bool CheckDisposed() => _disposed; + + public void Dispose() + { + if (!_disposed) + { + _disposed = true; + + _changeTokenRegistration?.Dispose(); + + foreach (var registration in _providerRegistrations) + { + try + { + if (registration.ShouldDispose) + { + registration.Provider.Dispose(); + } + } + catch + { + // Swallow exceptions on dispose + } + } + } + } + } +} \ No newline at end of file diff --git a/src/Microsoft.Extensions.Logging/LoggerFactory1.cs b/src/Microsoft.Extensions.Logging/LoggerFactory1.cs deleted file mode 100644 index 43965068..00000000 --- a/src/Microsoft.Extensions.Logging/LoggerFactory1.cs +++ /dev/null @@ -1,172 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using Microsoft.Extensions.Options; - -namespace Microsoft.Extensions.Logging -{ - public class LoggerFactory : ILoggerFactory - { - private struct ProviderRegistration - { - public ILoggerProvider Provider; - public bool ShouldDispose; - } - - private static readonly LoggerRuleSelector RuleSelector = new LoggerRuleSelector(); - - private readonly Dictionary _loggers = new Dictionary(StringComparer.Ordinal); - - private readonly List _providerRegistrations; - private readonly object _sync = new object(); - private volatile bool _disposed; - private IDisposable _changeTokenRegistration; - private LoggerFilterOptions _filterOptions; - - public LoggerFactory() : this(new LoggerFilterOptions()) - { - } - - public LoggerFactory(LoggerFilterOptions filterOptions) : this(Enumerable.Empty(), new StaticFilterOptionsMonitor(filterOptions)) - { - } - - public LoggerFactory(IEnumerable providers, IOptionsMonitor filterOption) - { - _providerRegistrations = providers.Select(provider => new ProviderRegistration { Provider = provider}).ToList(); - _changeTokenRegistration = filterOption.OnChange(RefreshFilters); - RefreshFilters(filterOption.CurrentValue); - } - - private void RefreshFilters(LoggerFilterOptions filterOptions) - { - lock (_sync) - { - _filterOptions = filterOptions; - foreach (var logger in _loggers) - { - var loggerInformation = logger.Value.Loggers; - var categoryName = logger.Key; - - ApplyRules(loggerInformation, categoryName, 0, loggerInformation.Length); - } - } - } - - public ILogger CreateLogger(string categoryName) - { - if (CheckDisposed()) - { - throw new ObjectDisposedException(nameof(LoggerFactory)); - } - - lock (_sync) - { - Logger logger; - - if (!_loggers.TryGetValue(categoryName, out logger)) - { - - logger = new Logger() - { - Loggers = CreateLoggers(categoryName) - }; - _loggers[categoryName] = logger; - } - - return logger; - } - } - - void ILoggerFactory.AddProvider(ILoggerProvider provider) - { - if (CheckDisposed()) - { - throw new ObjectDisposedException(nameof(LoggerFactory)); - } - - lock (_sync) - { - _providerRegistrations.Add(new ProviderRegistration { Provider = provider, ShouldDispose = true}); - foreach (var logger in _loggers) - { - var loggerInformation = logger.Value.Loggers; - var categoryName = logger.Key; - - Array.Resize(ref loggerInformation, loggerInformation.Length + 1); - var newLoggerIndex = loggerInformation.Length - 1; - loggerInformation[newLoggerIndex].Logger = provider.CreateLogger(categoryName); - loggerInformation[newLoggerIndex].ProviderType = provider.GetType().FullName; - - ApplyRules(loggerInformation, categoryName, newLoggerIndex, 1); - - logger.Value.Loggers = loggerInformation; - } - } - } - - private Logger.LoggerInformation[] CreateLoggers(string categoryName) - { - Logger.LoggerInformation[] loggers = new Logger.LoggerInformation[_providerRegistrations.Count]; - for (int i = 0; i < _providerRegistrations.Count; i++) - { - var provider = _providerRegistrations[i].Provider; - - loggers[i].Logger = provider.CreateLogger(categoryName); - loggers[i].ProviderType = provider.GetType().FullName; - } - - ApplyRules(loggers, categoryName, 0, loggers.Length); - return loggers; - } - - private void ApplyRules(Logger.LoggerInformation[] loggers, string categoryName, int start, int count) - { - for (var index = start; index < start + count; index++) - { - ref var loggerInformation = ref loggers[index]; - - RuleSelector.Select(_filterOptions, - loggerInformation.ProviderType, - categoryName, - out var minLevel, - out var filter); - - loggerInformation.Category = categoryName; - loggerInformation.MinLevel = minLevel; - loggerInformation.Filter = filter; - } - } - - /// - /// Check if the factory has been disposed. - /// - /// True when as been called - protected virtual bool CheckDisposed() => _disposed; - - public void Dispose() - { - if (!_disposed) - { - _disposed = true; - - _changeTokenRegistration?.Dispose(); - - foreach (var registration in _providerRegistrations) - { - try - { - if (registration.ShouldDispose) - { - registration.Provider.Dispose(); - } - } - catch - { - // Swallow exceptions on dispose - } - } - } - } - } -} \ No newline at end of file diff --git a/src/Microsoft.Extensions.Logging/LoggerFilterOptionsExtensions.cs b/src/Microsoft.Extensions.Logging/LoggerFilterOptionsExtensions.cs deleted file mode 100644 index c1bb8b32..00000000 --- a/src/Microsoft.Extensions.Logging/LoggerFilterOptionsExtensions.cs +++ /dev/null @@ -1,60 +0,0 @@ -// Copyright (c) .NET Foundation. All rights reserved. -// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. - -using System; -using Microsoft.Extensions.Logging; - -namespace Microsoft.Extensions.DependencyInjection -{ - /// - /// Extension methods for setting up logging services in an . - /// - public static class LoggerFilterOptionsExtensions - { - public static LoggerFilterOptions AddFilter(this LoggerFilterOptions options, string category, LogLevel level) - { - options.Rules.Add(new LoggerFilterRule(null, category, level, null)); - return options; - } - - public static LoggerFilterOptions AddFilter(this LoggerFilterOptions options, string category, LogLevel level) - { - options.Rules.Add(new LoggerFilterRule(typeof(T).FullName, category, level, null)); - return options; - } - - public static LoggerFilterOptions AddFilter(this LoggerFilterOptions options, LogMessageFilter filter) - { - options.Rules.Add(new LoggerFilterRule(null, null, null, filter)); - return options; - } - public static LoggerFilterOptions AddFilter(this LoggerFilterOptions options, LogMessageFilter filter) - { - options.Rules.Add(new LoggerFilterRule(typeof(T).FullName, null, null, filter)); - return options; - } - - public static LoggerFilterOptions AddFilter(this LoggerFilterOptions options, Func categoryLevelFilter) - { - options.Rules.Add(new LoggerFilterRule(null, null, null, (type, name, level) => categoryLevelFilter(name, level))); - return options; - } - - public static LoggerFilterOptions AddFilter(this LoggerFilterOptions options, Func categoryLevelFilter) - { - options.Rules.Add(new LoggerFilterRule(typeof(T).FullName, null, null, (type, name, level) => categoryLevelFilter(name, level))); - return options; - } - public static LoggerFilterOptions AddFilter(this LoggerFilterOptions options, string category, Func levelFilter) - { - options.Rules.Add(new LoggerFilterRule(null, category, null, (type, name, level) => levelFilter(level))); - return options; - } - - public static LoggerFilterOptions AddFilter(this LoggerFilterOptions options, string category, Func levelFilter) - { - options.Rules.Add(new LoggerFilterRule(typeof(T).FullName, category, null, (type, name, level) => levelFilter(level))); - return options; - } - } -} \ No newline at end of file diff --git a/src/Microsoft.Extensions.Logging/LoggingServiceCollectionExtensions.cs b/src/Microsoft.Extensions.Logging/LoggingServiceCollectionExtensions.cs index db43cac4..e68b55c4 100644 --- a/src/Microsoft.Extensions.Logging/LoggingServiceCollectionExtensions.cs +++ b/src/Microsoft.Extensions.Logging/LoggingServiceCollectionExtensions.cs @@ -19,9 +19,19 @@ public static class LoggingServiceCollectionExtensions /// /// The to add services to. /// The so that additional calls can be chained. - public static IServiceCollection AddLogging(this IServiceCollection services) + public static ILoggerBuilder AddLogging(this IServiceCollection services) { - return AddLogging(services, null); + if (services == null) + { + throw new ArgumentNullException(nameof(services)); + } + + services.AddOptions(); + + services.TryAdd(ServiceDescriptor.Singleton()); + services.TryAdd(ServiceDescriptor.Singleton(typeof(ILogger<>), typeof(Logger<>))); + + return new LoggerBuilder(services); } /// @@ -30,24 +40,14 @@ public static IServiceCollection AddLogging(this IServiceCollection services) /// The to add services to. /// The instance to use fol filter configuration /// The so that additional calls can be chained. - public static IServiceCollection AddLogging(this IServiceCollection services, IConfiguration configuration) + public static ILoggerBuilder AddLogging(this IServiceCollection services, IConfiguration configuration) { - if (services == null) - { - throw new ArgumentNullException(nameof(services)); - } - - services.AddOptions(); + var builder = services.AddLogging(); - services.TryAdd(ServiceDescriptor.Singleton()); - services.TryAdd(ServiceDescriptor.Singleton(typeof(ILogger<>), typeof(Logger<>))); + builder.Services.TryAddEnumerable(ServiceDescriptor.Singleton>(new ConfigurationLoggerFilterConfigureOptions(configuration))); + builder.Services.TryAddEnumerable(ServiceDescriptor.Singleton>(new ConfigurationChangeTokenSource(configuration))); - if (configuration != null) - { - services.TryAddEnumerable(ServiceDescriptor.Singleton>(new ConfigurationLoggerFilterConfigureOptions(configuration))); - services.TryAddEnumerable(ServiceDescriptor.Singleton>(new ConfigurationChangeTokenSource(configuration))); - } - return services; + return builder; } } } \ No newline at end of file diff --git a/test/Microsoft.Extensions.Logging.EventSource.Test/EventSourceLoggerTest.cs b/test/Microsoft.Extensions.Logging.EventSource.Test/EventSourceLoggerTest.cs index 1b708b5b..9382d75f 100644 --- a/test/Microsoft.Extensions.Logging.EventSource.Test/EventSourceLoggerTest.cs +++ b/test/Microsoft.Extensions.Logging.EventSource.Test/EventSourceLoggerTest.cs @@ -20,8 +20,8 @@ public void Logs_AsExpected_WithDefaults() { using (var testListener = new TestEventListener()) { - var factory = LoggerFactoryBuilder.Create() - .WithServices(services => services.AddEventSourceLogger()) + var factory = TestLoggerBuilder.Create() + .AddEventSourceLogger() .Build(); var listenerSettings = new TestEventListener.ListenerSettings(); @@ -55,7 +55,7 @@ public void Logs_Nothing_IfNotEnabled() { using (var testListener = new TestEventListener()) { - var factory = LoggerFactoryBuilder.Create() + var factory = TestLoggerBuilder.Create() .Build(); // No call to factory.AddEventSourceLogger(); @@ -77,8 +77,8 @@ public void Logs_OnlyFormattedMessage_IfKeywordSet() { using (var testListener = new TestEventListener()) { - var factory = LoggerFactoryBuilder.Create() - .WithServices(services => services.AddEventSourceLogger()) + var factory = TestLoggerBuilder.Create() + .AddEventSourceLogger() .Build(); var listenerSettings = new TestEventListener.ListenerSettings(); @@ -110,8 +110,8 @@ public void Logs_OnlyJson_IfKeywordSet() { using (var testListener = new TestEventListener()) { - var factory = LoggerFactoryBuilder.Create() - .WithServices(services => services.AddEventSourceLogger()) + var factory = TestLoggerBuilder.Create() + .AddEventSourceLogger() .Build(); var listenerSettings = new TestEventListener.ListenerSettings(); @@ -143,8 +143,8 @@ public void Logs_OnlyMessage_IfKeywordSet() { using (var testListener = new TestEventListener()) { - var factory = LoggerFactoryBuilder.Create() - .WithServices(services => services.AddEventSourceLogger()) + var factory = TestLoggerBuilder.Create() + .AddEventSourceLogger() .Build(); var listenerSettings = new TestEventListener.ListenerSettings(); @@ -176,8 +176,8 @@ public void Logs_AllEvents_IfTraceSet() { using (var testListener = new TestEventListener()) { - var factory = LoggerFactoryBuilder.Create() - .WithServices(services => services.AddEventSourceLogger()) + var factory = TestLoggerBuilder.Create() + .AddEventSourceLogger() .Build(); var listenerSettings = new TestEventListener.ListenerSettings(); @@ -209,8 +209,8 @@ public void Logs_AsExpected_AtErrorLevel() { using (var testListener = new TestEventListener()) { - var factory = LoggerFactoryBuilder.Create() - .WithServices(services => services.AddEventSourceLogger()) + var factory = TestLoggerBuilder.Create() + .AddEventSourceLogger() .Build(); var listenerSettings = new TestEventListener.ListenerSettings(); @@ -236,8 +236,8 @@ public void Logs_AsExpected_AtWarningLevel() { using (var testListener = new TestEventListener()) { - var factory = LoggerFactoryBuilder.Create() - .WithServices(services => services.AddEventSourceLogger()) + var factory = TestLoggerBuilder.Create() + .AddEventSourceLogger() .Build(); var listenerSettings = new TestEventListener.ListenerSettings(); @@ -265,8 +265,8 @@ public void Logs_AsExpected_WithSingleLoggerSpec() { using (var testListener = new TestEventListener()) { - var factory = LoggerFactoryBuilder.Create() - .WithServices(services => services.AddEventSourceLogger()) + var factory = TestLoggerBuilder.Create() + .AddEventSourceLogger() .Build(); var listenerSettings = new TestEventListener.ListenerSettings(); @@ -289,8 +289,8 @@ public void Logs_AsExpected_WithSingleLoggerSpecWithVerbosity() { using (var testListener = new TestEventListener()) { - var factory = LoggerFactoryBuilder.Create() - .WithServices(services => services.AddEventSourceLogger()) + var factory = TestLoggerBuilder.Create() + .AddEventSourceLogger() .Build(); var listenerSettings = new TestEventListener.ListenerSettings(); @@ -311,8 +311,8 @@ public void Logs_AsExpected_WithComplexLoggerSpec() { using (var testListener = new TestEventListener()) { - var factory = LoggerFactoryBuilder.Create() - .WithServices(services => services.AddEventSourceLogger()) + var factory = TestLoggerBuilder.Create() + .AddEventSourceLogger() .Build(); var listenerSettings = new TestEventListener.ListenerSettings(); diff --git a/test/Microsoft.Extensions.Logging.Test/LoggerBuilderTestExtensions.cs b/test/Microsoft.Extensions.Logging.Test/LoggerBuilderTestExtensions.cs new file mode 100644 index 00000000..abd2bae1 --- /dev/null +++ b/test/Microsoft.Extensions.Logging.Test/LoggerBuilderTestExtensions.cs @@ -0,0 +1,35 @@ +using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.DependencyInjection; + +namespace Microsoft.Extensions.Logging.Test +{ + public static class TestLoggerBuilder + { + public static ILoggerBuilder Create(IConfiguration configuration = null) + { + var serviceCollection = new ServiceCollection(); + if (configuration != null) + { + return serviceCollection.AddLogging(configuration); + } + else + { + return serviceCollection.AddLogging(); + } + } + } + + public static class LoggerBuilderTestExtensions + { + public static ILoggerFactory Build(this ILoggerBuilder builder) + { + return builder.Services.BuildServiceProvider().GetRequiredService(); + } + + public static ILoggerBuilder WithProvider(this ILoggerBuilder builder, ILoggerProvider provider) + { + builder.Services.AddSingleton(provider); + return builder; + } + } +} diff --git a/test/Microsoft.Extensions.Logging.Test/LoggerFactoryBuilder.cs b/test/Microsoft.Extensions.Logging.Test/LoggerFactoryBuilder.cs deleted file mode 100644 index 225940fd..00000000 --- a/test/Microsoft.Extensions.Logging.Test/LoggerFactoryBuilder.cs +++ /dev/null @@ -1,54 +0,0 @@ -using System; -using Microsoft.Extensions.Configuration; -using Microsoft.Extensions.DependencyInjection; - -namespace Microsoft.Extensions.Logging.Test -{ - public class LoggerFactoryBuilder - { - private ServiceCollection _serviceCollection; - - public LoggerFactoryBuilder() - { - _serviceCollection = new ServiceCollection(); - } - - public static LoggerFactoryBuilder Create(IConfiguration configuration = null) - { - return new LoggerFactoryBuilder() - .WithServices(collection => - { - if (configuration != null) - { - LoggingServiceCollectionExtensions.AddLogging(collection, configuration); - } - else - { - LoggingServiceCollectionExtensions.AddLogging(collection); - } - }); - } - - public LoggerFactoryBuilder WithProvider(ILoggerProvider provider) - { - return WithServices(collection => ServiceCollectionServiceExtensions.AddSingleton(collection, provider)); - } - - public LoggerFactoryBuilder WithFilters(Action filterConfiguration) - { - OptionsServiceCollectionExtensions.Configure(_serviceCollection, filterConfiguration); - return this; - } - - public LoggerFactoryBuilder WithServices(Action serviceConfiguration) - { - serviceConfiguration(_serviceCollection); - return this; - } - - public ILoggerFactory Build() - { - return ServiceCollectionContainerBuilderExtensions.BuildServiceProvider(_serviceCollection).GetRequiredService(); - } - } -} \ No newline at end of file diff --git a/test/Microsoft.Extensions.Logging.Test/LoggerFilterTest.cs b/test/Microsoft.Extensions.Logging.Test/LoggerFilterTest.cs index 81b9a467..b4516913 100644 --- a/test/Microsoft.Extensions.Logging.Test/LoggerFilterTest.cs +++ b/test/Microsoft.Extensions.Logging.Test/LoggerFilterTest.cs @@ -29,7 +29,7 @@ public void ChangingConfigReloadsDefaultFilter() var config = CreateConfiguration(() => json); var loggerProvider = new TestLoggerProvider(new TestSink(), isEnabled: true); - var factory = LoggerFactoryBuilder + var factory = TestLoggerBuilder .Create(config.GetSection("Logging")) .WithProvider(loggerProvider) .Build(); @@ -75,7 +75,7 @@ public void ChangingConfigFromUseConfigurationReloadsDefaultFilter() }"; var config = CreateConfiguration(() => json); var loggerProvider = new TestLoggerProvider(new TestSink(), isEnabled: true); - var factory = LoggerFactoryBuilder + var factory = TestLoggerBuilder .Create(config.GetSection("Logging")) .WithProvider(loggerProvider) .Build(); @@ -124,7 +124,7 @@ public void CanFilterOnNamedProviders() var config = CreateConfiguration(() => json); var loggerProvider = new TestLoggerProvider(new TestSink(), isEnabled: true); - var factory = LoggerFactoryBuilder + var factory = TestLoggerBuilder .Create(config.GetSection("Logging")) .WithProvider(loggerProvider) .Build(); @@ -159,7 +159,7 @@ public void PreferFullNameOverDefaultForFiltering() var config = CreateConfiguration(() => json); var loggerProvider = new TestLoggerProvider(new TestSink(), isEnabled: true); - var factory = LoggerFactoryBuilder + var factory = TestLoggerBuilder .Create(config.GetSection("Logging")) .WithProvider(loggerProvider) .Build(); @@ -192,7 +192,7 @@ public void DefaultCategoryNameIsUsedIfNoneMatch() var config = CreateConfiguration(() => json); var loggerProvider = new TestLoggerProvider(new TestSink(), isEnabled: true); - var factory = LoggerFactoryBuilder + var factory = TestLoggerBuilder .Create(config.GetSection("Logging")) .WithProvider(loggerProvider) .Build(); @@ -226,10 +226,10 @@ public void DefaultCategoryNameIsUsedIfNoneMatch() public void AddFilterForMatchingProviderFilters() { var provider = new TestLoggerProvider(new TestSink(), isEnabled: true); - var factory = LoggerFactoryBuilder + var factory = TestLoggerBuilder .Create() .WithProvider(provider) - .WithFilters(options => options.AddFilter((name, cat, level) => + .AddFilter((name, cat, level) => { if (string.Equals("Microsoft.Extensions.Logging.Test.TestLoggerProvider", name)) { @@ -240,7 +240,7 @@ public void AddFilterForMatchingProviderFilters() } return true; - })) + }) .Build(); var logger = factory.CreateLogger("Test"); @@ -259,10 +259,10 @@ public void AddFilterForMatchingProviderFilters() public void AddFilterForNonMatchingProviderDoesNotFilter() { var provider = new TestLoggerProvider(new TestSink(), isEnabled: true); - var factory = LoggerFactoryBuilder + var factory = TestLoggerBuilder .Create() .WithProvider(provider) - .WithFilters(options => options.AddFilter((name, cat, level) => + .AddFilter((name, cat, level) => { if (string.Equals("None", name)) { @@ -270,7 +270,7 @@ public void AddFilterForNonMatchingProviderDoesNotFilter() } return true; - })) + }) .Build(); var logger = factory.CreateLogger("Test"); @@ -285,12 +285,11 @@ public void AddFilterForNonMatchingProviderDoesNotFilter() public void AddFilterIsAdditive() { var provider = new TestLoggerProvider(new TestSink(), isEnabled: true); - var factory = LoggerFactoryBuilder + var factory = TestLoggerBuilder .Create() .WithProvider(provider) - .WithFilters(options => - options.AddFilter((name, cat, level) => level >= LogLevel.Warning) - .AddFilter((name, cat, level) => string.Equals(cat, "NotTest"))) + .AddFilter((name, cat, level) => level >= LogLevel.Warning) + .AddFilter((name, cat, level) => string.Equals(cat, "NotTest")) .Build(); var logger = factory.CreateLogger("Test"); @@ -328,11 +327,10 @@ public void ProviderLevelIsPreferredOverGlobalFilter() var config = CreateConfiguration(() => json); var loggerProvider = new TestLoggerProvider(new TestSink(), isEnabled: true); - var factory = LoggerFactoryBuilder + var factory = TestLoggerBuilder .Create(config.GetSection("Logging")) .WithProvider(loggerProvider) - .WithFilters(options => - options.AddFilter((name, cat, level) => level < LogLevel.Critical)) + .AddFilter((name, cat, level) => level < LogLevel.Critical) .Build(); var logger = factory.CreateLogger("Test"); @@ -356,11 +354,10 @@ public void ProviderLevelIsPreferredOverGlobalFilter() public void AddFilterWithProviderNameCategoryNameAndFilterFuncFiltersCorrectly() { var provider = new TestLoggerProvider(new TestSink(), isEnabled: true); - var factory = LoggerFactoryBuilder + var factory = TestLoggerBuilder .Create() .WithProvider(provider) - .WithFilters(options => - options.AddFilter((name, cat, level) => level >= LogLevel.Warning)) + .AddFilter((name, cat, level) => level >= LogLevel.Warning) .Build(); var logger = factory.CreateLogger("Sample.Test"); @@ -379,11 +376,10 @@ public void AddFilterWithProviderNameCategoryNameAndFilterFuncFiltersCorrectly() public void AddFilterWithProviderNameCategoryNameAndMinLevelFiltersCorrectly() { var provider = new TestLoggerProvider(new TestSink(), isEnabled: true); - var factory = LoggerFactoryBuilder + var factory = TestLoggerBuilder .Create() .WithProvider(provider) - .WithFilters(options => - options.AddFilter("Sample", LogLevel.Warning)) + .AddFilter("Sample", LogLevel.Warning) .Build(); var logger = factory.CreateLogger("Sample.Test"); @@ -402,11 +398,10 @@ public void AddFilterWithProviderNameCategoryNameAndMinLevelFiltersCorrectly() public void AddFilterWithProviderNameAndCategoryFilterFuncFiltersCorrectly() { var provider = new TestLoggerProvider(new TestSink(), isEnabled: true); - var factory = LoggerFactoryBuilder + var factory = TestLoggerBuilder .Create() .WithProvider(provider) - .WithFilters(options => - options.AddFilter((c, l) => l >= LogLevel.Warning)) + .AddFilter((c, l) => l >= LogLevel.Warning) .Build(); var logger = factory.CreateLogger("Sample.Test"); diff --git a/test/Microsoft.Extensions.Logging.Test/LoggerTest.cs b/test/Microsoft.Extensions.Logging.Test/LoggerTest.cs index ecbbc442..07fbc852 100644 --- a/test/Microsoft.Extensions.Logging.Test/LoggerTest.cs +++ b/test/Microsoft.Extensions.Logging.Test/LoggerTest.cs @@ -3,7 +3,6 @@ using System; using System.Collections.Generic; -using Microsoft.Extensions.DependencyInjection; using Xunit; namespace Microsoft.Extensions.Logging.Test @@ -15,7 +14,7 @@ public void Log_IgnoresExceptionInIntermediateLoggersAndThrowsAggregateException { // Arrange var store = new List(); - var loggerFactory = LoggerFactoryBuilder.Create() + var loggerFactory = TestLoggerBuilder.Create() .WithProvider(new CustomLoggerProvider("provider1", ThrowExceptionAt.None, store)) .WithProvider(new CustomLoggerProvider("provider2", ThrowExceptionAt.Log, store)) .WithProvider(new CustomLoggerProvider("provider3", ThrowExceptionAt.None, store)) @@ -40,7 +39,7 @@ public void BeginScope_IgnoresExceptionInIntermediateLoggersAndThrowsAggregateEx { // Arrange var store = new List(); - var loggerFactory = LoggerFactoryBuilder.Create() + var loggerFactory = TestLoggerBuilder.Create() .WithProvider(new CustomLoggerProvider("provider1", ThrowExceptionAt.None, store)) .WithProvider(new CustomLoggerProvider("provider2", ThrowExceptionAt.BeginScope, store)) .WithProvider(new CustomLoggerProvider("provider3", ThrowExceptionAt.None, store)) @@ -65,7 +64,7 @@ public void IsEnabled_IgnoresExceptionInIntermediateLoggers() { // Arrange var store = new List(); - var loggerFactory = LoggerFactoryBuilder.Create() + var loggerFactory = TestLoggerBuilder.Create() .WithProvider(new CustomLoggerProvider("provider1", ThrowExceptionAt.None, store)) .WithProvider(new CustomLoggerProvider("provider2", ThrowExceptionAt.IsEnabled, store)) .WithProvider(new CustomLoggerProvider("provider3", ThrowExceptionAt.None, store)) @@ -90,7 +89,7 @@ public void Log_AggregatesExceptionsFromMultipleLoggers() { // Arrange var store = new List(); - var loggerFactory = LoggerFactoryBuilder.Create() + var loggerFactory = TestLoggerBuilder.Create() .WithProvider(new CustomLoggerProvider("provider1", ThrowExceptionAt.Log, store)) .WithProvider(new CustomLoggerProvider("provider2", ThrowExceptionAt.Log, store)) .Build(); diff --git a/test/Microsoft.Extensions.Logging.Test/TraceSourceLoggerTest.cs b/test/Microsoft.Extensions.Logging.Test/TraceSourceLoggerTest.cs index 5936d44c..6edeee45 100644 --- a/test/Microsoft.Extensions.Logging.Test/TraceSourceLoggerTest.cs +++ b/test/Microsoft.Extensions.Logging.Test/TraceSourceLoggerTest.cs @@ -16,8 +16,8 @@ public static void IsEnabledReturnsCorrectValue() var testSwitch = new SourceSwitch("TestSwitch", "Level will be set to warning for this test"); testSwitch.Level = SourceLevels.Warning; - var factory = LoggerFactoryBuilder.Create() - .WithServices(collection => collection.AddTraceSource(testSwitch)) + var factory = TestLoggerBuilder.Create() + .AddTraceSource(testSwitch) .Build(); // Act @@ -48,13 +48,9 @@ public static void MultipleLoggers_IsEnabledReturnsCorrectValue(SourceLevels fir // Act - var factory = LoggerFactoryBuilder.Create() - .WithServices(collection => - { - collection - .AddTraceSource(firstSwitch) - .AddTraceSource(secondSwitch); - }) + var factory = TestLoggerBuilder.Create() + .AddTraceSource(firstSwitch) + .AddTraceSource(secondSwitch) .Build(); var logger = factory.CreateLogger("Test"); diff --git a/test/Microsoft.Extensions.Logging.Test/TraceSourceScopeTest.cs b/test/Microsoft.Extensions.Logging.Test/TraceSourceScopeTest.cs index 937441f8..d08a4d03 100644 --- a/test/Microsoft.Extensions.Logging.Test/TraceSourceScopeTest.cs +++ b/test/Microsoft.Extensions.Logging.Test/TraceSourceScopeTest.cs @@ -17,8 +17,8 @@ public static void DiagnosticsScope_PushesAndPops_LogicalOperationStack() Trace.CorrelationManager.StartLogicalOperation(baseState); var state = "1337state7331"; - var factory = LoggerFactoryBuilder.Create() - .WithServices(collection => collection.AddTraceSource(new SourceSwitch("TestSwitch"), new ConsoleTraceListener())) + var factory = TestLoggerBuilder.Create() + .AddTraceSource(new SourceSwitch("TestSwitch"), new ConsoleTraceListener()) .Build(); var logger = factory.CreateLogger("Test"); diff --git a/test/Microsoft.Extensions.Logging.Testing.Tests/XunitLoggerProviderTest.cs b/test/Microsoft.Extensions.Logging.Testing.Tests/XunitLoggerProviderTest.cs index 2a26764c..5f83e36b 100644 --- a/test/Microsoft.Extensions.Logging.Testing.Tests/XunitLoggerProviderTest.cs +++ b/test/Microsoft.Extensions.Logging.Testing.Tests/XunitLoggerProviderTest.cs @@ -14,8 +14,8 @@ public void LoggerProviderWritesToTestOutputHelper() { var testTestOutputHelper = new TestTestOutputHelper(); - var loggerFactory = LoggerFactoryBuilder.Create() - .WithServices(services => services.AddXunit(testTestOutputHelper)) + var loggerFactory = TestLoggerBuilder.Create() + .AddXunit(testTestOutputHelper) .Build(); var logger = loggerFactory.CreateLogger("TestCategory"); @@ -33,8 +33,8 @@ public void LoggerProviderWritesToTestOutputHelper() public void LoggerProviderDoesNotWriteLogMessagesBelowMinimumLevel() { var testTestOutputHelper = new TestTestOutputHelper(); - var loggerFactory = LoggerFactoryBuilder.Create() - .WithServices(services => services.AddXunit(testTestOutputHelper)) + var loggerFactory = TestLoggerBuilder.Create() + .AddXunit(testTestOutputHelper) .Build(); var logger = loggerFactory.CreateLogger("TestCategory"); @@ -48,8 +48,8 @@ public void LoggerProviderDoesNotWriteLogMessagesBelowMinimumLevel() public void LoggerProviderPrependsPrefixToEachLine() { var testTestOutputHelper = new TestTestOutputHelper(); - var loggerFactory = LoggerFactoryBuilder.Create() - .WithServices(services => services.AddXunit(testTestOutputHelper)) + var loggerFactory = TestLoggerBuilder.Create() + .AddXunit(testTestOutputHelper) .Build(); var logger = loggerFactory.CreateLogger("TestCategory"); @@ -67,8 +67,8 @@ public void LoggerProviderPrependsPrefixToEachLine() public void LoggerProviderDoesNotThrowIfOutputHelperThrows() { var testTestOutputHelper = new TestTestOutputHelper(); - var loggerFactory = LoggerFactoryBuilder.Create() - .WithServices(services => services.AddXunit(testTestOutputHelper)) + var loggerFactory = TestLoggerBuilder.Create() + .AddXunit(testTestOutputHelper) .Build(); testTestOutputHelper.Throw = true; From 1571100351ac5cbc2b20aa8be35ec272441ddad9 Mon Sep 17 00:00:00 2001 From: Pavel Krymets Date: Wed, 24 May 2017 08:56:58 -0700 Subject: [PATCH 04/20] All tests --- .../XunitLoggerProviderTest.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/Microsoft.Extensions.Logging.Testing.Tests/XunitLoggerProviderTest.cs b/test/Microsoft.Extensions.Logging.Testing.Tests/XunitLoggerProviderTest.cs index 5f83e36b..44fc4d72 100644 --- a/test/Microsoft.Extensions.Logging.Testing.Tests/XunitLoggerProviderTest.cs +++ b/test/Microsoft.Extensions.Logging.Testing.Tests/XunitLoggerProviderTest.cs @@ -34,7 +34,7 @@ public void LoggerProviderDoesNotWriteLogMessagesBelowMinimumLevel() { var testTestOutputHelper = new TestTestOutputHelper(); var loggerFactory = TestLoggerBuilder.Create() - .AddXunit(testTestOutputHelper) + .AddXunit(testTestOutputHelper, LogLevel.Warning) .Build(); var logger = loggerFactory.CreateLogger("TestCategory"); From 00a9b5f36bc52e0095a298cab04b64f34d6c31ae Mon Sep 17 00:00:00 2001 From: Pavel Krymets Date: Wed, 24 May 2017 09:46:19 -0700 Subject: [PATCH 05/20] Fix tests for real this time --- .../XunitLoggerFactoryExtensions.cs | 2 +- .../FilterLoggerBuilderExtensions.cs | 8 ++++---- .../LoggingServiceCollectionExtensionsTest.cs | 5 +++-- 3 files changed, 8 insertions(+), 7 deletions(-) diff --git a/src/Microsoft.Extensions.Logging.Testing/XunitLoggerFactoryExtensions.cs b/src/Microsoft.Extensions.Logging.Testing/XunitLoggerFactoryExtensions.cs index 0f857571..b85ca963 100644 --- a/src/Microsoft.Extensions.Logging.Testing/XunitLoggerFactoryExtensions.cs +++ b/src/Microsoft.Extensions.Logging.Testing/XunitLoggerFactoryExtensions.cs @@ -18,7 +18,7 @@ public static ILoggerBuilder AddXunit(this ILoggerBuilder builder, ITestOutputHe public static ILoggerBuilder AddXunit(this ILoggerBuilder builder, ITestOutputHelper output, LogLevel minLevel) { - builder.Services.AddSingleton(new XunitLoggerProvider(output)); + builder.Services.AddSingleton(new XunitLoggerProvider(output, minLevel)); return builder; } diff --git a/src/Microsoft.Extensions.Logging/FilterLoggerBuilderExtensions.cs b/src/Microsoft.Extensions.Logging/FilterLoggerBuilderExtensions.cs index 2f189854..944332b0 100644 --- a/src/Microsoft.Extensions.Logging/FilterLoggerBuilderExtensions.cs +++ b/src/Microsoft.Extensions.Logging/FilterLoggerBuilderExtensions.cs @@ -16,7 +16,7 @@ public static ILoggerBuilder AddFilter(this ILoggerBuilder builder, string categ return AddRule(builder, new LoggerFilterRule(null, category, level, null)); } - public static ILoggerBuilder AddFilter(this ILoggerBuilder builder, string category, LogLevel level) + public static ILoggerBuilder AddFilter(this ILoggerBuilder builder, string category, LogLevel level) where T: ILoggerProvider { return AddRule(builder, new LoggerFilterRule(typeof(T).FullName, category, level, null)); } @@ -25,7 +25,7 @@ public static ILoggerBuilder AddFilter(this ILoggerBuilder builder, LogMessageFi { return AddRule(builder, new LoggerFilterRule(null, null, null, filter)); } - public static ILoggerBuilder AddFilter(this ILoggerBuilder builder, LogMessageFilter filter) + public static ILoggerBuilder AddFilter(this ILoggerBuilder builder, LogMessageFilter filter) where T : ILoggerProvider { return AddRule(builder, new LoggerFilterRule(typeof(T).FullName, null, null, filter)); } @@ -35,7 +35,7 @@ public static ILoggerBuilder AddFilter(this ILoggerBuilder builder, Func categoryLevelFilter(name, level))); } - public static ILoggerBuilder AddFilter(this ILoggerBuilder builder, Func categoryLevelFilter) + public static ILoggerBuilder AddFilter(this ILoggerBuilder builder, Func categoryLevelFilter) where T : ILoggerProvider { return AddRule(builder, new LoggerFilterRule(typeof(T).FullName, null, null, (type, name, level) => categoryLevelFilter(name, level))); } @@ -45,7 +45,7 @@ public static ILoggerBuilder AddFilter(this ILoggerBuilder builder, string categ return AddRule(builder, new LoggerFilterRule(null, category, null, (type, name, level) => levelFilter(level))); } - public static ILoggerBuilder AddFilter(this ILoggerBuilder builder, string category, Func levelFilter) + public static ILoggerBuilder AddFilter(this ILoggerBuilder builder, string category, Func levelFilter) where T : ILoggerProvider { return AddRule(builder, new LoggerFilterRule(typeof(T).FullName, category, null, (type, name, level) => levelFilter(level))); } diff --git a/test/Microsoft.Extensions.Logging.Test/LoggingServiceCollectionExtensionsTest.cs b/test/Microsoft.Extensions.Logging.Test/LoggingServiceCollectionExtensionsTest.cs index 7d2b9706..4602d9ba 100644 --- a/test/Microsoft.Extensions.Logging.Test/LoggingServiceCollectionExtensionsTest.cs +++ b/test/Microsoft.Extensions.Logging.Test/LoggingServiceCollectionExtensionsTest.cs @@ -9,11 +9,12 @@ namespace Microsoft.Extensions.Logging.Test public class LoggingServiceCollectionExtensionsTest { [Fact] - public void AddLogging_allows_chaining() + public void AddLogging_WrapsServiceCollection() { var services = new ServiceCollection(); - Assert.Same(services, services.AddLogging()); + var loggerBuilder = services.AddLogging(); + Assert.Same(services, loggerBuilder.Services); } } } From baf6c726b3cb11ded09ec54b0fec37da3f129ad5 Mon Sep 17 00:00:00 2001 From: Pavel Krymets Date: Wed, 24 May 2017 09:55:35 -0700 Subject: [PATCH 06/20] Move AddProvider to main lib --- .../LoggerBuilderExtensions.cs | 19 +++++++++++++++ .../LoggerBuilderTestExtensions.cs | 6 ----- .../LoggerFilterTest.cs | 24 +++++++++---------- .../LoggerTest.cs | 23 +++++++++--------- 4 files changed, 43 insertions(+), 29 deletions(-) create mode 100644 src/Microsoft.Extensions.Logging/LoggerBuilderExtensions.cs diff --git a/src/Microsoft.Extensions.Logging/LoggerBuilderExtensions.cs b/src/Microsoft.Extensions.Logging/LoggerBuilderExtensions.cs new file mode 100644 index 00000000..54022d3d --- /dev/null +++ b/src/Microsoft.Extensions.Logging/LoggerBuilderExtensions.cs @@ -0,0 +1,19 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using Microsoft.Extensions.Logging; + +namespace Microsoft.Extensions.DependencyInjection +{ + /// + /// Extension methods for setting up logging services in an . + /// + public static class LoggerBuilderExtensions + { + public static ILoggerBuilder AddProvider(this ILoggerBuilder builder, ILoggerProvider provider) + { + builder.Services.AddSingleton(provider); + return builder; + } + } +} \ No newline at end of file diff --git a/test/Microsoft.Extensions.Logging.Test/LoggerBuilderTestExtensions.cs b/test/Microsoft.Extensions.Logging.Test/LoggerBuilderTestExtensions.cs index abd2bae1..ea67d58e 100644 --- a/test/Microsoft.Extensions.Logging.Test/LoggerBuilderTestExtensions.cs +++ b/test/Microsoft.Extensions.Logging.Test/LoggerBuilderTestExtensions.cs @@ -25,11 +25,5 @@ public static ILoggerFactory Build(this ILoggerBuilder builder) { return builder.Services.BuildServiceProvider().GetRequiredService(); } - - public static ILoggerBuilder WithProvider(this ILoggerBuilder builder, ILoggerProvider provider) - { - builder.Services.AddSingleton(provider); - return builder; - } } } diff --git a/test/Microsoft.Extensions.Logging.Test/LoggerFilterTest.cs b/test/Microsoft.Extensions.Logging.Test/LoggerFilterTest.cs index b4516913..5e4c091f 100644 --- a/test/Microsoft.Extensions.Logging.Test/LoggerFilterTest.cs +++ b/test/Microsoft.Extensions.Logging.Test/LoggerFilterTest.cs @@ -31,7 +31,7 @@ public void ChangingConfigReloadsDefaultFilter() var factory = TestLoggerBuilder .Create(config.GetSection("Logging")) - .WithProvider(loggerProvider) + .AddProvider(loggerProvider) .Build(); var logger = factory.CreateLogger("Microsoft"); @@ -77,7 +77,7 @@ public void ChangingConfigFromUseConfigurationReloadsDefaultFilter() var loggerProvider = new TestLoggerProvider(new TestSink(), isEnabled: true); var factory = TestLoggerBuilder .Create(config.GetSection("Logging")) - .WithProvider(loggerProvider) + .AddProvider(loggerProvider) .Build(); var logger = factory.CreateLogger("Microsoft"); @@ -126,7 +126,7 @@ public void CanFilterOnNamedProviders() var loggerProvider = new TestLoggerProvider(new TestSink(), isEnabled: true); var factory = TestLoggerBuilder .Create(config.GetSection("Logging")) - .WithProvider(loggerProvider) + .AddProvider(loggerProvider) .Build(); var logger = factory.CreateLogger("Microsoft"); @@ -161,7 +161,7 @@ public void PreferFullNameOverDefaultForFiltering() var loggerProvider = new TestLoggerProvider(new TestSink(), isEnabled: true); var factory = TestLoggerBuilder .Create(config.GetSection("Logging")) - .WithProvider(loggerProvider) + .AddProvider(loggerProvider) .Build(); var logger = factory.CreateLogger("Microsoft"); @@ -194,7 +194,7 @@ public void DefaultCategoryNameIsUsedIfNoneMatch() var loggerProvider = new TestLoggerProvider(new TestSink(), isEnabled: true); var factory = TestLoggerBuilder .Create(config.GetSection("Logging")) - .WithProvider(loggerProvider) + .AddProvider(loggerProvider) .Build(); var logger = factory.CreateLogger("Microsoft"); @@ -228,7 +228,7 @@ public void AddFilterForMatchingProviderFilters() var provider = new TestLoggerProvider(new TestSink(), isEnabled: true); var factory = TestLoggerBuilder .Create() - .WithProvider(provider) + .AddProvider(provider) .AddFilter((name, cat, level) => { if (string.Equals("Microsoft.Extensions.Logging.Test.TestLoggerProvider", name)) @@ -261,7 +261,7 @@ public void AddFilterForNonMatchingProviderDoesNotFilter() var provider = new TestLoggerProvider(new TestSink(), isEnabled: true); var factory = TestLoggerBuilder .Create() - .WithProvider(provider) + .AddProvider(provider) .AddFilter((name, cat, level) => { if (string.Equals("None", name)) @@ -287,7 +287,7 @@ public void AddFilterIsAdditive() var provider = new TestLoggerProvider(new TestSink(), isEnabled: true); var factory = TestLoggerBuilder .Create() - .WithProvider(provider) + .AddProvider(provider) .AddFilter((name, cat, level) => level >= LogLevel.Warning) .AddFilter((name, cat, level) => string.Equals(cat, "NotTest")) .Build(); @@ -329,7 +329,7 @@ public void ProviderLevelIsPreferredOverGlobalFilter() var factory = TestLoggerBuilder .Create(config.GetSection("Logging")) - .WithProvider(loggerProvider) + .AddProvider(loggerProvider) .AddFilter((name, cat, level) => level < LogLevel.Critical) .Build(); @@ -356,7 +356,7 @@ public void AddFilterWithProviderNameCategoryNameAndFilterFuncFiltersCorrectly() var provider = new TestLoggerProvider(new TestSink(), isEnabled: true); var factory = TestLoggerBuilder .Create() - .WithProvider(provider) + .AddProvider(provider) .AddFilter((name, cat, level) => level >= LogLevel.Warning) .Build(); @@ -378,7 +378,7 @@ public void AddFilterWithProviderNameCategoryNameAndMinLevelFiltersCorrectly() var provider = new TestLoggerProvider(new TestSink(), isEnabled: true); var factory = TestLoggerBuilder .Create() - .WithProvider(provider) + .AddProvider(provider) .AddFilter("Sample", LogLevel.Warning) .Build(); @@ -400,7 +400,7 @@ public void AddFilterWithProviderNameAndCategoryFilterFuncFiltersCorrectly() var provider = new TestLoggerProvider(new TestSink(), isEnabled: true); var factory = TestLoggerBuilder .Create() - .WithProvider(provider) + .AddProvider(provider) .AddFilter((c, l) => l >= LogLevel.Warning) .Build(); diff --git a/test/Microsoft.Extensions.Logging.Test/LoggerTest.cs b/test/Microsoft.Extensions.Logging.Test/LoggerTest.cs index 07fbc852..0a89f143 100644 --- a/test/Microsoft.Extensions.Logging.Test/LoggerTest.cs +++ b/test/Microsoft.Extensions.Logging.Test/LoggerTest.cs @@ -3,6 +3,7 @@ using System; using System.Collections.Generic; +using Microsoft.Extensions.DependencyInjection; using Xunit; namespace Microsoft.Extensions.Logging.Test @@ -15,9 +16,9 @@ public void Log_IgnoresExceptionInIntermediateLoggersAndThrowsAggregateException // Arrange var store = new List(); var loggerFactory = TestLoggerBuilder.Create() - .WithProvider(new CustomLoggerProvider("provider1", ThrowExceptionAt.None, store)) - .WithProvider(new CustomLoggerProvider("provider2", ThrowExceptionAt.Log, store)) - .WithProvider(new CustomLoggerProvider("provider3", ThrowExceptionAt.None, store)) + .AddProvider(new CustomLoggerProvider("provider1", ThrowExceptionAt.None, store)) + .AddProvider(new CustomLoggerProvider("provider2", ThrowExceptionAt.Log, store)) + .AddProvider(new CustomLoggerProvider("provider3", ThrowExceptionAt.None, store)) .Build(); var logger = loggerFactory.CreateLogger("Test"); @@ -40,9 +41,9 @@ public void BeginScope_IgnoresExceptionInIntermediateLoggersAndThrowsAggregateEx // Arrange var store = new List(); var loggerFactory = TestLoggerBuilder.Create() - .WithProvider(new CustomLoggerProvider("provider1", ThrowExceptionAt.None, store)) - .WithProvider(new CustomLoggerProvider("provider2", ThrowExceptionAt.BeginScope, store)) - .WithProvider(new CustomLoggerProvider("provider3", ThrowExceptionAt.None, store)) + .AddProvider(new CustomLoggerProvider("provider1", ThrowExceptionAt.None, store)) + .AddProvider(new CustomLoggerProvider("provider2", ThrowExceptionAt.BeginScope, store)) + .AddProvider(new CustomLoggerProvider("provider3", ThrowExceptionAt.None, store)) .Build(); var logger = loggerFactory.CreateLogger("Test"); @@ -65,9 +66,9 @@ public void IsEnabled_IgnoresExceptionInIntermediateLoggers() // Arrange var store = new List(); var loggerFactory = TestLoggerBuilder.Create() - .WithProvider(new CustomLoggerProvider("provider1", ThrowExceptionAt.None, store)) - .WithProvider(new CustomLoggerProvider("provider2", ThrowExceptionAt.IsEnabled, store)) - .WithProvider(new CustomLoggerProvider("provider3", ThrowExceptionAt.None, store)) + .AddProvider(new CustomLoggerProvider("provider1", ThrowExceptionAt.None, store)) + .AddProvider(new CustomLoggerProvider("provider2", ThrowExceptionAt.IsEnabled, store)) + .AddProvider(new CustomLoggerProvider("provider3", ThrowExceptionAt.None, store)) .Build(); var logger = loggerFactory.CreateLogger("Test"); @@ -90,8 +91,8 @@ public void Log_AggregatesExceptionsFromMultipleLoggers() // Arrange var store = new List(); var loggerFactory = TestLoggerBuilder.Create() - .WithProvider(new CustomLoggerProvider("provider1", ThrowExceptionAt.Log, store)) - .WithProvider(new CustomLoggerProvider("provider2", ThrowExceptionAt.Log, store)) + .AddProvider(new CustomLoggerProvider("provider1", ThrowExceptionAt.Log, store)) + .AddProvider(new CustomLoggerProvider("provider2", ThrowExceptionAt.Log, store)) .Build(); var logger = loggerFactory.CreateLogger("Test"); From 4b7360906df55ee39a717c8c06fad814c861798c Mon Sep 17 00:00:00 2001 From: Pavel Krymets Date: Thu, 25 May 2017 11:58:13 -0700 Subject: [PATCH 07/20] More fixes --- samples/SampleApp/Program.cs | 6 +- ...figurationConsoleLoggerConfigureOptions.cs | 15 ++++ .../ConsoleLoggerOptions.cs | 10 +++ .../ConsoleLoggerProvider.cs | 13 --- ...nfigurationLoggerFilterConfigureOptions.cs | 29 +++++-- .../DefaultLoggerLevelConfigureOptions.cs | 11 +++ .../FilterLoggerBuilderExtensions.cs | 5 ++ src/Microsoft.Extensions.Logging/Logger.cs | 20 +++-- .../LoggerBuilderExtensions.cs | 24 ++++++ .../LoggerFilterOptions.cs | 2 +- .../LoggerRuleSelector.cs | 86 ++++++++++--------- .../LoggingServiceCollectionExtensions.cs | 19 +--- .../LoggerBuilderTestExtensions.cs | 11 ++- .../LoggerFactoryTest.cs | 19 ---- 14 files changed, 157 insertions(+), 113 deletions(-) create mode 100644 src/Microsoft.Extensions.Logging.Console/ConfigurationConsoleLoggerConfigureOptions.cs create mode 100644 src/Microsoft.Extensions.Logging.Console/ConsoleLoggerOptions.cs create mode 100644 src/Microsoft.Extensions.Logging/DefaultLoggerLevelConfigureOptions.cs diff --git a/samples/SampleApp/Program.cs b/samples/SampleApp/Program.cs index 7123d367..67dd5324 100644 --- a/samples/SampleApp/Program.cs +++ b/samples/SampleApp/Program.cs @@ -2,12 +2,11 @@ // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System; -using System.Collections.Generic; using System.IO; using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Logging; using ILogger = Microsoft.Extensions.Logging.ILogger; -using Microsoft.Extensions.DependencyInjection; namespace SampleApp { @@ -26,7 +25,8 @@ public Program() // Create a logger factory with filters that can be applied across all logger providers. var serviceCollection = new ServiceCollection(); var loggingBuilder = serviceCollection - .AddLogging(loggingConfiguration.GetSection("Logging")) + .AddLogging() + .AddConfiguration(loggingConfiguration.GetSection("Logging")) .AddFilter("Microsoft", LogLevel.Warning) .AddFilter("System", LogLevel.Warning) .AddFilter("SampleApp.Program", LogLevel.Debug); diff --git a/src/Microsoft.Extensions.Logging.Console/ConfigurationConsoleLoggerConfigureOptions.cs b/src/Microsoft.Extensions.Logging.Console/ConfigurationConsoleLoggerConfigureOptions.cs new file mode 100644 index 00000000..ad48312a --- /dev/null +++ b/src/Microsoft.Extensions.Logging.Console/ConfigurationConsoleLoggerConfigureOptions.cs @@ -0,0 +1,15 @@ +// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.Options; + +namespace Microsoft.Extensions.Logging.Console +{ + public class ConfigurationConsoleLoggerConfigureOptions : ConfigureOptions + { + public ConfigurationConsoleLoggerConfigureOptions(IConfiguration configuration) : base(configuration.Bind) + { + } + } +} \ No newline at end of file diff --git a/src/Microsoft.Extensions.Logging.Console/ConsoleLoggerOptions.cs b/src/Microsoft.Extensions.Logging.Console/ConsoleLoggerOptions.cs new file mode 100644 index 00000000..4c687a82 --- /dev/null +++ b/src/Microsoft.Extensions.Logging.Console/ConsoleLoggerOptions.cs @@ -0,0 +1,10 @@ +// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +namespace Microsoft.Extensions.Logging.Console +{ + public class ConsoleLoggerOptions + { + public bool IncludeScopes { get; set; } = false; + } +} \ No newline at end of file diff --git a/src/Microsoft.Extensions.Logging.Console/ConsoleLoggerProvider.cs b/src/Microsoft.Extensions.Logging.Console/ConsoleLoggerProvider.cs index ef9425c8..7124a89e 100644 --- a/src/Microsoft.Extensions.Logging.Console/ConsoleLoggerProvider.cs +++ b/src/Microsoft.Extensions.Logging.Console/ConsoleLoggerProvider.cs @@ -4,7 +4,6 @@ using System; using System.Collections.Concurrent; using System.Collections.Generic; -using Microsoft.Extensions.Configuration; using Microsoft.Extensions.Logging.Console.Internal; using Microsoft.Extensions.Options; @@ -153,16 +152,4 @@ public void Dispose() _messageQueue.Dispose(); } } - - public class ConsoleLoggerOptions - { - public bool IncludeScopes { get; set; } = false; - } - - public class ConfigurationConsoleLoggerConfigureOptions : ConfigureOptions - { - public ConfigurationConsoleLoggerConfigureOptions(IConfiguration configuration) : base(options => configuration.Bind(options)) - { - } - } } diff --git a/src/Microsoft.Extensions.Logging/ConfigurationLoggerFilterConfigureOptions.cs b/src/Microsoft.Extensions.Logging/ConfigurationLoggerFilterConfigureOptions.cs index ae1196b8..5d2de3ec 100644 --- a/src/Microsoft.Extensions.Logging/ConfigurationLoggerFilterConfigureOptions.cs +++ b/src/Microsoft.Extensions.Logging/ConfigurationLoggerFilterConfigureOptions.cs @@ -8,18 +8,20 @@ namespace Microsoft.Extensions.Logging public class ConfigurationLoggerFilterConfigureOptions : IConfigureOptions { private readonly IConfiguration _configuration; + private readonly bool _replace; - public ConfigurationLoggerFilterConfigureOptions(IConfiguration configuration) + public ConfigurationLoggerFilterConfigureOptions(IConfiguration configuration, bool replace) { _configuration = configuration; + _replace = replace; } public void Configure(LoggerFilterOptions options) { - LoadDefaultConfigValues(options.Rules); + LoadDefaultConfigValues(options); } - private void LoadDefaultConfigValues(ICollection rules) + private void LoadDefaultConfigValues(LoggerFilterOptions options) { if (_configuration == null) { @@ -31,7 +33,7 @@ private void LoadDefaultConfigValues(ICollection rules) if (configurationSection.Key == "LogLevel") { // Load global category defaults - LoadRules(rules, configurationSection, null); + LoadRules(options, configurationSection, null); } else { @@ -40,19 +42,32 @@ private void LoadDefaultConfigValues(ICollection rules) { // Load logger specific rules var logger = ExpandLoggerAlias(configurationSection.Key); - LoadRules(rules, logLevelSection, logger); + LoadRules(options, logLevelSection, logger); } } } } - private static void LoadRules(ICollection rules, IConfigurationSection configurationSection, string logger) + private void LoadRules(LoggerFilterOptions options, IConfigurationSection configurationSection, string logger) { foreach (var section in configurationSection.AsEnumerable(true)) { if (TryGetSwitch(section.Value, out var level)) { - rules.Add(new LoggerFilterRule(logger, section.Key, level, null)); + var newRule = new LoggerFilterRule(logger, section.Key, level, null); + options.Rules.Add(newRule); + + // If we are in replace mode we need to find other matching rules and remove them + if (_replace) + { + foreach (var rule in LoggerRuleSelector.GetMatchingRules(options, logger, section.Key)) + { + if (rule != newRule) + { + options.Rules.Remove(rule); + } + } + } } } } diff --git a/src/Microsoft.Extensions.Logging/DefaultLoggerLevelConfigureOptions.cs b/src/Microsoft.Extensions.Logging/DefaultLoggerLevelConfigureOptions.cs new file mode 100644 index 00000000..dfba026a --- /dev/null +++ b/src/Microsoft.Extensions.Logging/DefaultLoggerLevelConfigureOptions.cs @@ -0,0 +1,11 @@ +using Microsoft.Extensions.Options; + +namespace Microsoft.Extensions.Logging +{ + public class DefaultLoggerLevelConfigureOptions : ConfigureOptions + { + public DefaultLoggerLevelConfigureOptions(LogLevel level) : base(options => options.MinLevel = level) + { + } + } +} \ No newline at end of file diff --git a/src/Microsoft.Extensions.Logging/FilterLoggerBuilderExtensions.cs b/src/Microsoft.Extensions.Logging/FilterLoggerBuilderExtensions.cs index 944332b0..3e571601 100644 --- a/src/Microsoft.Extensions.Logging/FilterLoggerBuilderExtensions.cs +++ b/src/Microsoft.Extensions.Logging/FilterLoggerBuilderExtensions.cs @@ -11,6 +11,11 @@ namespace Microsoft.Extensions.DependencyInjection /// public static class FilterLoggerBuilderExtensions { + public static ILoggerBuilder AddFilter(this ILoggerBuilder builder, Func levelFilter) where T : ILoggerProvider + { + return AddRule(builder, new LoggerFilterRule(typeof(T).FullName, null, null, (type, name, level) => levelFilter(level))); + } + public static ILoggerBuilder AddFilter(this ILoggerBuilder builder, string category, LogLevel level) { return AddRule(builder, new LoggerFilterRule(null, category, level, null)); diff --git a/src/Microsoft.Extensions.Logging/Logger.cs b/src/Microsoft.Extensions.Logging/Logger.cs index 44aaaf0b..044f5076 100644 --- a/src/Microsoft.Extensions.Logging/Logger.cs +++ b/src/Microsoft.Extensions.Logging/Logger.cs @@ -13,13 +13,14 @@ internal class Logger : ILogger public void Log(LogLevel logLevel, EventId eventId, TState state, Exception exception, Func formatter) { - if (Loggers == null) + var loggers = Loggers; + if (loggers == null) { return; } List exceptions = null; - foreach (var loggerInfo in Loggers) + foreach (var loggerInfo in loggers) { if (!loggerInfo.IsEnabled(logLevel)) { @@ -50,13 +51,14 @@ public void Log(LogLevel logLevel, EventId eventId, TState state, Except public bool IsEnabled(LogLevel logLevel) { - if (Loggers == null) + var loggers = Loggers; + if (loggers == null) { return false; } List exceptions = null; - foreach (var loggerInfo in Loggers) + foreach (var loggerInfo in loggers) { if (!loggerInfo.IsEnabled(logLevel)) { @@ -93,18 +95,18 @@ public bool IsEnabled(LogLevel logLevel) public IDisposable BeginScope(TState state) { - if (Loggers == null) + var loggers = Loggers; + + if (loggers == null) { return NullScope.Instance; } - if (Loggers.Length == 1) + if (loggers.Length == 1) { - return Loggers[0].Logger.BeginScope(state); + return loggers[0].Logger.BeginScope(state); } - var loggers = Loggers; - var scope = new Scope(loggers.Length); List exceptions = null; for (var index = 0; index < loggers.Length; index++) diff --git a/src/Microsoft.Extensions.Logging/LoggerBuilderExtensions.cs b/src/Microsoft.Extensions.Logging/LoggerBuilderExtensions.cs index 54022d3d..7ae4dced 100644 --- a/src/Microsoft.Extensions.Logging/LoggerBuilderExtensions.cs +++ b/src/Microsoft.Extensions.Logging/LoggerBuilderExtensions.cs @@ -1,7 +1,11 @@ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. +using System; +using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.DependencyInjection.Extensions; using Microsoft.Extensions.Logging; +using Microsoft.Extensions.Options; namespace Microsoft.Extensions.DependencyInjection { @@ -10,6 +14,26 @@ namespace Microsoft.Extensions.DependencyInjection /// public static class LoggerBuilderExtensions { + public static ILoggerBuilder AddConfiguration(this ILoggerBuilder builder, IConfiguration configuration) + { + return builder.AddConfiguration(configuration, false); + } + + public static ILoggerBuilder AddConfiguration(this ILoggerBuilder builder, IConfiguration configuration, bool replace) + { + builder.Services.TryAddEnumerable(ServiceDescriptor.Singleton>(new ConfigurationLoggerFilterConfigureOptions(configuration, replace))); + builder.Services.TryAddEnumerable(ServiceDescriptor.Singleton>(new ConfigurationChangeTokenSource(configuration))); + + return builder; + } + + public static ILoggerBuilder SetMinimalLevel(this ILoggerBuilder builder, LogLevel level) + { + builder.Services.TryAddEnumerable(ServiceDescriptor.Singleton>( + new DefaultLoggerLevelConfigureOptions(LogLevel.Information))); + return builder; + } + public static ILoggerBuilder AddProvider(this ILoggerBuilder builder, ILoggerProvider provider) { builder.Services.AddSingleton(provider); diff --git a/src/Microsoft.Extensions.Logging/LoggerFilterOptions.cs b/src/Microsoft.Extensions.Logging/LoggerFilterOptions.cs index ca417901..8cb217ab 100644 --- a/src/Microsoft.Extensions.Logging/LoggerFilterOptions.cs +++ b/src/Microsoft.Extensions.Logging/LoggerFilterOptions.cs @@ -4,7 +4,7 @@ namespace Microsoft.Extensions.Logging { public class LoggerFilterOptions { - public LogLevel MinLevel { get; set; } = LogLevel.Information; + public LogLevel MinLevel { get; set; } public ICollection Rules { get; } = new List(); } diff --git a/src/Microsoft.Extensions.Logging/LoggerRuleSelector.cs b/src/Microsoft.Extensions.Logging/LoggerRuleSelector.cs index c70f9e78..5ec95aa7 100644 --- a/src/Microsoft.Extensions.Logging/LoggerRuleSelector.cs +++ b/src/Microsoft.Extensions.Logging/LoggerRuleSelector.cs @@ -10,10 +10,47 @@ public class LoggerRuleSelector { public void Select(LoggerFilterOptions options, string logger, string category, out LogLevel? minLevel, out LogMessageFilter filter) { - minLevel = null; filter = null; - // Filter rule selection: + var categorySpecificRules = GetMatchingRules(options, logger, category); + + if (categorySpecificRules?.Any() == true) + { + if (categorySpecificRules.Count == 1) + { + var loggerFilterRule = categorySpecificRules.Single(); + filter = loggerFilterRule.Filter; + minLevel = loggerFilterRule.LogLevel; + } + else + { + // Combine rules, for min level we take maximum of all rules + // for filter delegated we take firs if there is only one or apply AND operator to all + minLevel = categorySpecificRules.Max(rule => rule.LogLevel); + filter = (type, c, level) => + { + foreach (var loggerFilterRule in categorySpecificRules) + { + if (!loggerFilterRule.Filter?.Invoke(type, c, level) == true) + { + return false; + } + } + + return true; + }; + } + } + else + { + // If there are no rules fallback to global min level + minLevel = options.MinLevel; + } + } + + public static List GetMatchingRules(LoggerFilterOptions options, string logger, string category) + { +// Filter rule selection: // 1. Select rules for current logger type, if there is none, select ones without logger type specified // 2. Select rules with longest matching categories // 3. If there no category @@ -27,12 +64,14 @@ public void Select(LoggerFilterOptions options, string logger, string category, loggerSpecificRules = options.Rules.Where(rule => string.IsNullOrEmpty(rule.LoggerType)).ToList(); } + List categorySpecificRules = null; if (loggerSpecificRules.Any()) { - var categorySpecificRules = loggerSpecificRules - .Where(rule => !string.IsNullOrEmpty(rule.CategoryName) && category.StartsWith(rule.CategoryName, StringComparison.OrdinalIgnoreCase)) + categorySpecificRules = loggerSpecificRules + .Where(rule => !string.IsNullOrEmpty(rule.CategoryName) && + category.StartsWith(rule.CategoryName, StringComparison.OrdinalIgnoreCase)) .GroupBy(rule => rule.CategoryName.Length) - .OrderByDescending(group => group.Key) + .OrderByDescending(group => @group.Key) .FirstOrDefault() ?.ToList(); @@ -43,42 +82,11 @@ public void Select(LoggerFilterOptions options, string logger, string category, if (!categorySpecificRules.Any()) { - categorySpecificRules = loggerSpecificRules.Where(rule => rule.CategoryName.Equals("Default", StringComparison.OrdinalIgnoreCase)).ToList(); - } - - if (categorySpecificRules.Any()) - { - if (categorySpecificRules.Count == 1) - { - var loggerFilterRule = categorySpecificRules.Single(); - filter = loggerFilterRule.Filter; - minLevel = loggerFilterRule.LogLevel; - } - else - { - // Combine rules, for min level we take maximum of all rules - // for filter delegated we take firs if there is only one or apply AND operator to all - minLevel = categorySpecificRules.Max(rule => rule.LogLevel); - filter = (type, c, level) => - { - foreach (var loggerFilterRule in categorySpecificRules) - { - if (!loggerFilterRule.Filter(type, c, level)) - { - return false; - } - } - - return true; - }; - } - } - else - { - // If there are no rules fallback to global min level - minLevel = options.MinLevel; + categorySpecificRules = loggerSpecificRules + .Where(rule => rule.CategoryName.Equals("Default", StringComparison.OrdinalIgnoreCase)).ToList(); } } + return categorySpecificRules; } } } \ No newline at end of file diff --git a/src/Microsoft.Extensions.Logging/LoggingServiceCollectionExtensions.cs b/src/Microsoft.Extensions.Logging/LoggingServiceCollectionExtensions.cs index e68b55c4..c70fbef4 100644 --- a/src/Microsoft.Extensions.Logging/LoggingServiceCollectionExtensions.cs +++ b/src/Microsoft.Extensions.Logging/LoggingServiceCollectionExtensions.cs @@ -31,23 +31,10 @@ public static ILoggerBuilder AddLogging(this IServiceCollection services) services.TryAdd(ServiceDescriptor.Singleton()); services.TryAdd(ServiceDescriptor.Singleton(typeof(ILogger<>), typeof(Logger<>))); - return new LoggerBuilder(services); - } + services.TryAddEnumerable(ServiceDescriptor.Singleton>( + new DefaultLoggerLevelConfigureOptions(LogLevel.Information))); - /// - /// Adds logging services to the specified . - /// - /// The to add services to. - /// The instance to use fol filter configuration - /// The so that additional calls can be chained. - public static ILoggerBuilder AddLogging(this IServiceCollection services, IConfiguration configuration) - { - var builder = services.AddLogging(); - - builder.Services.TryAddEnumerable(ServiceDescriptor.Singleton>(new ConfigurationLoggerFilterConfigureOptions(configuration))); - builder.Services.TryAddEnumerable(ServiceDescriptor.Singleton>(new ConfigurationChangeTokenSource(configuration))); - - return builder; + return new LoggerBuilder(services); } } } \ No newline at end of file diff --git a/test/Microsoft.Extensions.Logging.Test/LoggerBuilderTestExtensions.cs b/test/Microsoft.Extensions.Logging.Test/LoggerBuilderTestExtensions.cs index ea67d58e..01cd8584 100644 --- a/test/Microsoft.Extensions.Logging.Test/LoggerBuilderTestExtensions.cs +++ b/test/Microsoft.Extensions.Logging.Test/LoggerBuilderTestExtensions.cs @@ -7,15 +7,14 @@ public static class TestLoggerBuilder { public static ILoggerBuilder Create(IConfiguration configuration = null) { - var serviceCollection = new ServiceCollection(); + var builder = new ServiceCollection().AddLogging(); + // Most test setup their own filtering or for all events to pass through + builder.Services.Configure(options => options.MinLevel = LogLevel.Trace); if (configuration != null) { - return serviceCollection.AddLogging(configuration); - } - else - { - return serviceCollection.AddLogging(); + builder.AddConfiguration(configuration); } + return builder; } } diff --git a/test/Microsoft.Extensions.Logging.Test/LoggerFactoryTest.cs b/test/Microsoft.Extensions.Logging.Test/LoggerFactoryTest.cs index bdacd180..0352cd2f 100644 --- a/test/Microsoft.Extensions.Logging.Test/LoggerFactoryTest.cs +++ b/test/Microsoft.Extensions.Logging.Test/LoggerFactoryTest.cs @@ -2,8 +2,6 @@ // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System; -using Microsoft.Extensions.Configuration; -using Microsoft.Extensions.Primitives; using Moq; using Xunit; @@ -94,22 +92,5 @@ public void Dispose_ThrowException_SwallowsException() throwingProvider.As() .Verify(p => p.Dispose(), Times.Once()); } - - // TODO: Replace with reload test - //[Fact] - //public void UseConfiguration_RegistersChangeCallback() - //{ - // // Arrange - // var factory = new LoggerFactory(); - // var changeToken = new Mock(); - // var configuration = new Mock(); - // configuration.Setup(c => c.GetReloadToken()).Returns(changeToken.Object); - - // // Act - // factory.UseConfiguration(configuration.Object); - - // // Assert - // changeToken.Verify(c => c.RegisterChangeCallback(It.IsAny>(), It.IsAny()), Times.Once); - //} } } From dd49a17fd4ba92b7ddabf570af54896027a8c14f Mon Sep 17 00:00:00 2001 From: Pavel Krymets Date: Thu, 25 May 2017 15:04:56 -0700 Subject: [PATCH 08/20] Add abstraction --- src/Microsoft.Extensions.Logging/LoggerFactory.cs | 4 ++++ .../LoggerBuilderTestExtensions.cs | 3 +++ 2 files changed, 7 insertions(+) diff --git a/src/Microsoft.Extensions.Logging/LoggerFactory.cs b/src/Microsoft.Extensions.Logging/LoggerFactory.cs index 43965068..3dc2b307 100644 --- a/src/Microsoft.Extensions.Logging/LoggerFactory.cs +++ b/src/Microsoft.Extensions.Logging/LoggerFactory.cs @@ -31,6 +31,10 @@ public LoggerFactory() : this(new LoggerFilterOptions()) { } + public LoggerFactory(IEnumerable providers) : this(providers, new StaticFilterOptionsMonitor(new LoggerFilterOptions())) + { + } + public LoggerFactory(IEnumerable providers, IOptionsMonitor filterOption) { _providerRegistrations = providers.Select(provider => new ProviderRegistration { Provider = provider}).ToList(); diff --git a/test/Microsoft.Extensions.Logging.Test/LoggerBuilderTestExtensions.cs b/test/Microsoft.Extensions.Logging.Test/LoggerBuilderTestExtensions.cs index 01cd8584..b5999fe5 100644 --- a/test/Microsoft.Extensions.Logging.Test/LoggerBuilderTestExtensions.cs +++ b/test/Microsoft.Extensions.Logging.Test/LoggerBuilderTestExtensions.cs @@ -1,3 +1,6 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; From 34414f993220333e3c878982aa3038ce5a54e9ed Mon Sep 17 00:00:00 2001 From: Pavel Krymets Date: Thu, 25 May 2017 15:29:20 -0700 Subject: [PATCH 09/20] Nits and (c) --- .../TraceSourceFactoryExtensions.cs | 44 +++++++++---------- ...nfigurationLoggerFilterConfigureOptions.cs | 4 +- .../DefaultLoggerLevelConfigureOptions.cs | 3 ++ .../FilterLoggerBuilderExtensions.cs | 4 +- .../ILoggerBuilder.cs | 13 ++---- .../LogMessageFilter.cs | 4 -- src/Microsoft.Extensions.Logging/Logger.cs | 28 ------------ .../LoggerBuilder.cs | 17 +++++++ .../LoggerBuilderExtensions.cs | 1 - .../LoggerFactory.cs | 23 +++++----- .../LoggerFilterOptions.cs | 3 ++ .../LoggerFilterRule.cs | 7 ++- .../LoggerInformation.cs | 35 +++++++++++++++ .../LoggerRuleSelector.cs | 7 +-- .../LoggingServiceCollectionExtensions.cs | 1 - .../StaticFilterOptionsMonitor.cs | 5 ++- 16 files changed, 114 insertions(+), 85 deletions(-) delete mode 100644 src/Microsoft.Extensions.Logging/LogMessageFilter.cs create mode 100644 src/Microsoft.Extensions.Logging/LoggerBuilder.cs create mode 100644 src/Microsoft.Extensions.Logging/LoggerInformation.cs diff --git a/src/Microsoft.Extensions.Logging.TraceSource/TraceSourceFactoryExtensions.cs b/src/Microsoft.Extensions.Logging.TraceSource/TraceSourceFactoryExtensions.cs index 1702f44c..1b4e772a 100644 --- a/src/Microsoft.Extensions.Logging.TraceSource/TraceSourceFactoryExtensions.cs +++ b/src/Microsoft.Extensions.Logging.TraceSource/TraceSourceFactoryExtensions.cs @@ -13,15 +13,15 @@ public static class TraceSourceFactoryExtensions /// /// Adds a TraceSource logger named 'TraceSource' to the factory. /// - /// The to use. + /// The to use. /// The name of the to use. public static ILoggerBuilder AddTraceSource( - this ILoggerBuilder collection, + this ILoggerBuilder builder, string switchName) { - if (collection == null) + if (builder == null) { - throw new ArgumentNullException(nameof(collection)); + throw new ArgumentNullException(nameof(builder)); } if (switchName == null) @@ -29,23 +29,23 @@ public static ILoggerBuilder AddTraceSource( throw new ArgumentNullException(nameof(switchName)); } - return collection.AddTraceSource(new SourceSwitch(switchName)); + return builder.AddTraceSource(new SourceSwitch(switchName)); } /// /// Adds a TraceSource logger named 'TraceSource' to the factory. /// - /// The to use. + /// The to use. /// The name of the to use. /// The to use. public static ILoggerBuilder AddTraceSource( - this ILoggerBuilder collection, + this ILoggerBuilder builder, string switchName, TraceListener listener) { - if (collection == null) + if (builder == null) { - throw new ArgumentNullException(nameof(collection)); + throw new ArgumentNullException(nameof(builder)); } if (switchName == null) @@ -58,21 +58,21 @@ public static ILoggerBuilder AddTraceSource( throw new ArgumentNullException(nameof(listener)); } - return collection.AddTraceSource(new SourceSwitch(switchName), listener); + return builder.AddTraceSource(new SourceSwitch(switchName), listener); } /// /// Adds a TraceSource logger named 'TraceSource' to the factory. /// - /// The to use. + /// The to use. /// The to use. public static ILoggerBuilder AddTraceSource( - this ILoggerBuilder collection, + this ILoggerBuilder builder, SourceSwitch sourceSwitch) { - if (collection == null) + if (builder == null) { - throw new ArgumentNullException(nameof(collection)); + throw new ArgumentNullException(nameof(builder)); } if (sourceSwitch == null) @@ -80,25 +80,25 @@ public static ILoggerBuilder AddTraceSource( throw new ArgumentNullException(nameof(sourceSwitch)); } - collection.Services.AddSingleton(new TraceSourceLoggerProvider(sourceSwitch)); + builder.Services.AddSingleton(new TraceSourceLoggerProvider(sourceSwitch)); - return collection; + return builder; } /// /// Adds a TraceSource logger named 'TraceSource' to the factory. /// - /// The to use. + /// The to use. /// The to use. /// The to use. public static ILoggerBuilder AddTraceSource( - this ILoggerBuilder collection, + this ILoggerBuilder builder, SourceSwitch sourceSwitch, TraceListener listener) { - if (collection == null) + if (builder == null) { - throw new ArgumentNullException(nameof(collection)); + throw new ArgumentNullException(nameof(builder)); } if (sourceSwitch == null) @@ -111,9 +111,9 @@ public static ILoggerBuilder AddTraceSource( throw new ArgumentNullException(nameof(listener)); } - collection.Services.AddSingleton(new TraceSourceLoggerProvider(sourceSwitch, listener)); + builder.Services.AddSingleton(new TraceSourceLoggerProvider(sourceSwitch, listener)); - return collection; + return builder; } /// diff --git a/src/Microsoft.Extensions.Logging/ConfigurationLoggerFilterConfigureOptions.cs b/src/Microsoft.Extensions.Logging/ConfigurationLoggerFilterConfigureOptions.cs index 5d2de3ec..82815c3f 100644 --- a/src/Microsoft.Extensions.Logging/ConfigurationLoggerFilterConfigureOptions.cs +++ b/src/Microsoft.Extensions.Logging/ConfigurationLoggerFilterConfigureOptions.cs @@ -1,5 +1,7 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + using System; -using System.Collections.Generic; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.Options; diff --git a/src/Microsoft.Extensions.Logging/DefaultLoggerLevelConfigureOptions.cs b/src/Microsoft.Extensions.Logging/DefaultLoggerLevelConfigureOptions.cs index dfba026a..20f40701 100644 --- a/src/Microsoft.Extensions.Logging/DefaultLoggerLevelConfigureOptions.cs +++ b/src/Microsoft.Extensions.Logging/DefaultLoggerLevelConfigureOptions.cs @@ -1,3 +1,6 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + using Microsoft.Extensions.Options; namespace Microsoft.Extensions.Logging diff --git a/src/Microsoft.Extensions.Logging/FilterLoggerBuilderExtensions.cs b/src/Microsoft.Extensions.Logging/FilterLoggerBuilderExtensions.cs index 3e571601..84fc5aac 100644 --- a/src/Microsoft.Extensions.Logging/FilterLoggerBuilderExtensions.cs +++ b/src/Microsoft.Extensions.Logging/FilterLoggerBuilderExtensions.cs @@ -26,11 +26,11 @@ public static ILoggerBuilder AddFilter(this ILoggerBuilder builder, string ca return AddRule(builder, new LoggerFilterRule(typeof(T).FullName, category, level, null)); } - public static ILoggerBuilder AddFilter(this ILoggerBuilder builder, LogMessageFilter filter) + public static ILoggerBuilder AddFilter(this ILoggerBuilder builder, Func filter) { return AddRule(builder, new LoggerFilterRule(null, null, null, filter)); } - public static ILoggerBuilder AddFilter(this ILoggerBuilder builder, LogMessageFilter filter) where T : ILoggerProvider + public static ILoggerBuilder AddFilter(this ILoggerBuilder builder, Func filter) where T : ILoggerProvider { return AddRule(builder, new LoggerFilterRule(typeof(T).FullName, null, null, filter)); } diff --git a/src/Microsoft.Extensions.Logging/ILoggerBuilder.cs b/src/Microsoft.Extensions.Logging/ILoggerBuilder.cs index a386c627..f3e2f697 100644 --- a/src/Microsoft.Extensions.Logging/ILoggerBuilder.cs +++ b/src/Microsoft.Extensions.Logging/ILoggerBuilder.cs @@ -1,3 +1,6 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + using Microsoft.Extensions.DependencyInjection; namespace Microsoft.Extensions.Logging @@ -9,14 +12,4 @@ public interface ILoggerBuilder /// IServiceCollection Services { get; } } - - public class LoggerBuilder : ILoggerBuilder - { - public LoggerBuilder(IServiceCollection services) - { - Services = services; - } - - public IServiceCollection Services { get; } - } } \ No newline at end of file diff --git a/src/Microsoft.Extensions.Logging/LogMessageFilter.cs b/src/Microsoft.Extensions.Logging/LogMessageFilter.cs deleted file mode 100644 index c6a7bfa2..00000000 --- a/src/Microsoft.Extensions.Logging/LogMessageFilter.cs +++ /dev/null @@ -1,4 +0,0 @@ -namespace Microsoft.Extensions.Logging -{ - public delegate bool LogMessageFilter(string loggerType, string categoryName, LogLevel level); -} \ No newline at end of file diff --git a/src/Microsoft.Extensions.Logging/Logger.cs b/src/Microsoft.Extensions.Logging/Logger.cs index 044f5076..293d3659 100644 --- a/src/Microsoft.Extensions.Logging/Logger.cs +++ b/src/Microsoft.Extensions.Logging/Logger.cs @@ -197,33 +197,5 @@ public void Dispose() } } } - - public struct LoggerInformation - { - public ILogger Logger { get; set; } - - public string Category { get; set; } - - public string ProviderType { get; set; } - - public LogLevel? MinLevel { get; set; } - - public LogMessageFilter Filter { get; set; } - - public bool IsEnabled(LogLevel level) - { - if (MinLevel != null && level < MinLevel) - { - return false; - } - - if (Filter != null) - { - return Filter(ProviderType, Category, level); - } - - return true; - } - } } } \ No newline at end of file diff --git a/src/Microsoft.Extensions.Logging/LoggerBuilder.cs b/src/Microsoft.Extensions.Logging/LoggerBuilder.cs new file mode 100644 index 00000000..5f26c1d0 --- /dev/null +++ b/src/Microsoft.Extensions.Logging/LoggerBuilder.cs @@ -0,0 +1,17 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using Microsoft.Extensions.DependencyInjection; + +namespace Microsoft.Extensions.Logging +{ + internal class LoggerBuilder : ILoggerBuilder + { + public LoggerBuilder(IServiceCollection services) + { + Services = services; + } + + public IServiceCollection Services { get; } + } +} \ No newline at end of file diff --git a/src/Microsoft.Extensions.Logging/LoggerBuilderExtensions.cs b/src/Microsoft.Extensions.Logging/LoggerBuilderExtensions.cs index 7ae4dced..41cc2c1b 100644 --- a/src/Microsoft.Extensions.Logging/LoggerBuilderExtensions.cs +++ b/src/Microsoft.Extensions.Logging/LoggerBuilderExtensions.cs @@ -1,7 +1,6 @@ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. -using System; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection.Extensions; using Microsoft.Extensions.Logging; diff --git a/src/Microsoft.Extensions.Logging/LoggerFactory.cs b/src/Microsoft.Extensions.Logging/LoggerFactory.cs index 3dc2b307..1ea5c0de 100644 --- a/src/Microsoft.Extensions.Logging/LoggerFactory.cs +++ b/src/Microsoft.Extensions.Logging/LoggerFactory.cs @@ -1,4 +1,7 @@ -using System; +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System; using System.Collections.Generic; using System.Linq; using Microsoft.Extensions.Options; @@ -7,12 +10,6 @@ namespace Microsoft.Extensions.Logging { public class LoggerFactory : ILoggerFactory { - private struct ProviderRegistration - { - public ILoggerProvider Provider; - public bool ShouldDispose; - } - private static readonly LoggerRuleSelector RuleSelector = new LoggerRuleSelector(); private readonly Dictionary _loggers = new Dictionary(StringComparer.Ordinal); @@ -109,9 +106,9 @@ void ILoggerFactory.AddProvider(ILoggerProvider provider) } } - private Logger.LoggerInformation[] CreateLoggers(string categoryName) + private LoggerInformation[] CreateLoggers(string categoryName) { - Logger.LoggerInformation[] loggers = new Logger.LoggerInformation[_providerRegistrations.Count]; + var loggers = new LoggerInformation[_providerRegistrations.Count]; for (int i = 0; i < _providerRegistrations.Count; i++) { var provider = _providerRegistrations[i].Provider; @@ -124,7 +121,7 @@ private Logger.LoggerInformation[] CreateLoggers(string categoryName) return loggers; } - private void ApplyRules(Logger.LoggerInformation[] loggers, string categoryName, int start, int count) + private void ApplyRules(LoggerInformation[] loggers, string categoryName, int start, int count) { for (var index = start; index < start + count; index++) { @@ -172,5 +169,11 @@ public void Dispose() } } } + + private struct ProviderRegistration + { + public ILoggerProvider Provider; + public bool ShouldDispose; + } } } \ No newline at end of file diff --git a/src/Microsoft.Extensions.Logging/LoggerFilterOptions.cs b/src/Microsoft.Extensions.Logging/LoggerFilterOptions.cs index 8cb217ab..9030ccfa 100644 --- a/src/Microsoft.Extensions.Logging/LoggerFilterOptions.cs +++ b/src/Microsoft.Extensions.Logging/LoggerFilterOptions.cs @@ -1,3 +1,6 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + using System.Collections.Generic; namespace Microsoft.Extensions.Logging diff --git a/src/Microsoft.Extensions.Logging/LoggerFilterRule.cs b/src/Microsoft.Extensions.Logging/LoggerFilterRule.cs index a8419632..e872ac88 100644 --- a/src/Microsoft.Extensions.Logging/LoggerFilterRule.cs +++ b/src/Microsoft.Extensions.Logging/LoggerFilterRule.cs @@ -1,10 +1,13 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + using System; namespace Microsoft.Extensions.Logging { public class LoggerFilterRule { - public LoggerFilterRule(string loggerType, string categoryName, LogLevel? logLevel, LogMessageFilter filter) + public LoggerFilterRule(string loggerType, string categoryName, LogLevel? logLevel, Func filter) { LoggerType = loggerType; CategoryName = categoryName; @@ -18,6 +21,6 @@ public LoggerFilterRule(string loggerType, string categoryName, LogLevel? logLev public LogLevel? LogLevel { get; } - public LogMessageFilter Filter { get; } + public Func Filter { get; } } } \ No newline at end of file diff --git a/src/Microsoft.Extensions.Logging/LoggerInformation.cs b/src/Microsoft.Extensions.Logging/LoggerInformation.cs new file mode 100644 index 00000000..f634f84e --- /dev/null +++ b/src/Microsoft.Extensions.Logging/LoggerInformation.cs @@ -0,0 +1,35 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System; + +namespace Microsoft.Extensions.Logging +{ + internal struct LoggerInformation + { + public ILogger Logger { get; set; } + + public string Category { get; set; } + + public string ProviderType { get; set; } + + public LogLevel? MinLevel { get; set; } + + public Func Filter { get; set; } + + public bool IsEnabled(LogLevel level) + { + if (MinLevel != null && level < MinLevel) + { + return false; + } + + if (Filter != null) + { + return Filter(ProviderType, Category, level); + } + + return true; + } + } +} \ No newline at end of file diff --git a/src/Microsoft.Extensions.Logging/LoggerRuleSelector.cs b/src/Microsoft.Extensions.Logging/LoggerRuleSelector.cs index 5ec95aa7..6b8324c0 100644 --- a/src/Microsoft.Extensions.Logging/LoggerRuleSelector.cs +++ b/src/Microsoft.Extensions.Logging/LoggerRuleSelector.cs @@ -1,14 +1,15 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + using System; using System.Collections.Generic; using System.Linq; -using System.Runtime.InteropServices; -using Microsoft.Extensions.Options; namespace Microsoft.Extensions.Logging { public class LoggerRuleSelector { - public void Select(LoggerFilterOptions options, string logger, string category, out LogLevel? minLevel, out LogMessageFilter filter) + public void Select(LoggerFilterOptions options, string logger, string category, out LogLevel? minLevel, out Func filter) { filter = null; diff --git a/src/Microsoft.Extensions.Logging/LoggingServiceCollectionExtensions.cs b/src/Microsoft.Extensions.Logging/LoggingServiceCollectionExtensions.cs index c70fbef4..a793a618 100644 --- a/src/Microsoft.Extensions.Logging/LoggingServiceCollectionExtensions.cs +++ b/src/Microsoft.Extensions.Logging/LoggingServiceCollectionExtensions.cs @@ -2,7 +2,6 @@ // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System; -using Microsoft.Extensions.Configuration; using Microsoft.Extensions.Logging; using Microsoft.Extensions.DependencyInjection.Extensions; using Microsoft.Extensions.Options; diff --git a/src/Microsoft.Extensions.Logging/StaticFilterOptionsMonitor.cs b/src/Microsoft.Extensions.Logging/StaticFilterOptionsMonitor.cs index 448f21d3..f910426f 100644 --- a/src/Microsoft.Extensions.Logging/StaticFilterOptionsMonitor.cs +++ b/src/Microsoft.Extensions.Logging/StaticFilterOptionsMonitor.cs @@ -1,4 +1,7 @@ -using System; +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System; using Microsoft.Extensions.Options; namespace Microsoft.Extensions.Logging From 09207353c8fba98c1c1537b3b8305f7862324833 Mon Sep 17 00:00:00 2001 From: Pavel Krymets Date: Fri, 26 May 2017 10:09:16 -0700 Subject: [PATCH 10/20] Logger builder delegate --- samples/SampleApp/Program.cs | 24 +++--- .../AssemblyTestLog.cs | 13 ++-- .../LoggerBuilderExtensions.cs | 4 +- .../LoggingServiceCollectionExtensions.cs | 16 +++- .../EventSourceLoggerTest.cs | 51 +++++------- .../LoggerBuilderTestExtensions.cs | 25 ++---- .../LoggerFilterTest.cs | 78 +++++++------------ .../LoggerTest.cs | 20 ++--- .../LoggingServiceCollectionExtensionsTest.cs | 8 +- .../TraceSourceLoggerTest.cs | 10 +-- .../TraceSourceScopeTest.cs | 6 +- .../XunitLoggerProviderTest.cs | 22 +++--- 12 files changed, 122 insertions(+), 155 deletions(-) diff --git a/samples/SampleApp/Program.cs b/samples/SampleApp/Program.cs index 67dd5324..e73d9fb6 100644 --- a/samples/SampleApp/Program.cs +++ b/samples/SampleApp/Program.cs @@ -23,23 +23,25 @@ public Program() // A Web App based program would configure logging via the WebHostBuilder. // Create a logger factory with filters that can be applied across all logger providers. - var serviceCollection = new ServiceCollection(); - var loggingBuilder = serviceCollection - .AddLogging() - .AddConfiguration(loggingConfiguration.GetSection("Logging")) - .AddFilter("Microsoft", LogLevel.Warning) - .AddFilter("System", LogLevel.Warning) - .AddFilter("SampleApp.Program", LogLevel.Debug); - - // providers may be added to a LoggerFactory before any loggers are created + var serviceCollection = new ServiceCollection() + .AddLogging(builder => + { + builder + .AddConfiguration(loggingConfiguration.GetSection("Logging")) + .AddFilter("Microsoft", LogLevel.Warning) + .AddFilter("System", LogLevel.Warning) + .AddFilter("SampleApp.Program", LogLevel.Debug) + .AddConsole(); #if NET46 - loggingBuilder.AddEventLog(); + builder.AddEventLog(); #elif NETCOREAPP2_0 #else #error Target framework needs to be updated #endif + }); + + // providers may be added to a LoggerFactory before any loggers are created - loggingBuilder.AddConsole(); var serviceProvider = serviceCollection.BuildServiceProvider(); // getting the logger using the class's name is conventional diff --git a/src/Microsoft.Extensions.Logging.Testing/AssemblyTestLog.cs b/src/Microsoft.Extensions.Logging.Testing/AssemblyTestLog.cs index 79fb44ad..617318f3 100644 --- a/src/Microsoft.Extensions.Logging.Testing/AssemblyTestLog.cs +++ b/src/Microsoft.Extensions.Logging.Testing/AssemblyTestLog.cs @@ -55,12 +55,13 @@ public IDisposable StartTestLog(ITestOutputHelper output, string className, out public ILoggerFactory CreateLoggerFactory(ITestOutputHelper output, string className, [CallerMemberName] string testName = null) { var serviceCollection = new ServiceCollection(); - var loggerBuilder = serviceCollection.AddLogging(); - - if (output != null) + serviceCollection.AddLogging(builder => { - loggerBuilder.AddXunit(output, LogLevel.Debug); - } + if (output != null) + { + builder.AddXunit(output, LogLevel.Debug); + } + }); var loggerFactory = serviceCollection.BuildServiceProvider().GetRequiredService(); // Try to shorten the class name using the assembly name @@ -84,7 +85,7 @@ public static AssemblyTestLog Create(string assemblyName, string baseDirectory) var serviceCollection = new ServiceCollection(); // Let the global logger log to the console, it's just "Starting X..." "Finished X..." - serviceCollection.AddLogging().AddConsole(); + serviceCollection.AddLogging(builder => builder.AddConsole()); var loggerFactory = serviceCollection.BuildServiceProvider().GetRequiredService(); diff --git a/src/Microsoft.Extensions.Logging/LoggerBuilderExtensions.cs b/src/Microsoft.Extensions.Logging/LoggerBuilderExtensions.cs index 41cc2c1b..d756a079 100644 --- a/src/Microsoft.Extensions.Logging/LoggerBuilderExtensions.cs +++ b/src/Microsoft.Extensions.Logging/LoggerBuilderExtensions.cs @@ -28,8 +28,8 @@ public static ILoggerBuilder AddConfiguration(this ILoggerBuilder builder, IConf public static ILoggerBuilder SetMinimalLevel(this ILoggerBuilder builder, LogLevel level) { - builder.Services.TryAddEnumerable(ServiceDescriptor.Singleton>( - new DefaultLoggerLevelConfigureOptions(LogLevel.Information))); + builder.Services.Add(ServiceDescriptor.Singleton>( + new DefaultLoggerLevelConfigureOptions(level))); return builder; } diff --git a/src/Microsoft.Extensions.Logging/LoggingServiceCollectionExtensions.cs b/src/Microsoft.Extensions.Logging/LoggingServiceCollectionExtensions.cs index a793a618..29372b26 100644 --- a/src/Microsoft.Extensions.Logging/LoggingServiceCollectionExtensions.cs +++ b/src/Microsoft.Extensions.Logging/LoggingServiceCollectionExtensions.cs @@ -18,7 +18,18 @@ public static class LoggingServiceCollectionExtensions /// /// The to add services to. /// The so that additional calls can be chained. - public static ILoggerBuilder AddLogging(this IServiceCollection services) + public static IServiceCollection AddLogging(this IServiceCollection services) + { + return AddLogging(services, builder => { }); + } + + /// + /// Adds logging services to the specified . + /// + /// The to add services to. + /// The configuration delegate. + /// The so that additional calls can be chained. + public static IServiceCollection AddLogging(this IServiceCollection services, Action configure) { if (services == null) { @@ -33,7 +44,8 @@ public static ILoggerBuilder AddLogging(this IServiceCollection services) services.TryAddEnumerable(ServiceDescriptor.Singleton>( new DefaultLoggerLevelConfigureOptions(LogLevel.Information))); - return new LoggerBuilder(services); + configure(new LoggerBuilder(services)); + return services; } } } \ No newline at end of file diff --git a/test/Microsoft.Extensions.Logging.EventSource.Test/EventSourceLoggerTest.cs b/test/Microsoft.Extensions.Logging.EventSource.Test/EventSourceLoggerTest.cs index 9382d75f..e0069873 100644 --- a/test/Microsoft.Extensions.Logging.EventSource.Test/EventSourceLoggerTest.cs +++ b/test/Microsoft.Extensions.Logging.EventSource.Test/EventSourceLoggerTest.cs @@ -20,9 +20,7 @@ public void Logs_AsExpected_WithDefaults() { using (var testListener = new TestEventListener()) { - var factory = TestLoggerBuilder.Create() - .AddEventSourceLogger() - .Build(); + var factory = CreateLoggerFactory(); var listenerSettings = new TestEventListener.ListenerSettings(); listenerSettings.Keywords = (EventKeywords)(-1); @@ -55,10 +53,9 @@ public void Logs_Nothing_IfNotEnabled() { using (var testListener = new TestEventListener()) { - var factory = TestLoggerBuilder.Create() - .Build(); - // No call to factory.AddEventSourceLogger(); + var factory = TestLoggerBuilder.Create(builder => builder + .SetMinimalLevel(LogLevel.Trace)); var listenerSettings = new TestEventListener.ListenerSettings(); listenerSettings.Keywords = EventKeywords.None; @@ -77,9 +74,7 @@ public void Logs_OnlyFormattedMessage_IfKeywordSet() { using (var testListener = new TestEventListener()) { - var factory = TestLoggerBuilder.Create() - .AddEventSourceLogger() - .Build(); + var factory = CreateLoggerFactory(); var listenerSettings = new TestEventListener.ListenerSettings(); listenerSettings.Keywords = LoggingEventSource.Keywords.FormattedMessage; @@ -110,9 +105,7 @@ public void Logs_OnlyJson_IfKeywordSet() { using (var testListener = new TestEventListener()) { - var factory = TestLoggerBuilder.Create() - .AddEventSourceLogger() - .Build(); + var factory = CreateLoggerFactory(); var listenerSettings = new TestEventListener.ListenerSettings(); listenerSettings.Keywords = LoggingEventSource.Keywords.JsonMessage; @@ -143,9 +136,7 @@ public void Logs_OnlyMessage_IfKeywordSet() { using (var testListener = new TestEventListener()) { - var factory = TestLoggerBuilder.Create() - .AddEventSourceLogger() - .Build(); + var factory = CreateLoggerFactory(); var listenerSettings = new TestEventListener.ListenerSettings(); listenerSettings.Keywords = LoggingEventSource.Keywords.Message; @@ -176,9 +167,7 @@ public void Logs_AllEvents_IfTraceSet() { using (var testListener = new TestEventListener()) { - var factory = TestLoggerBuilder.Create() - .AddEventSourceLogger() - .Build(); + var factory = CreateLoggerFactory(); var listenerSettings = new TestEventListener.ListenerSettings(); listenerSettings.Keywords = LoggingEventSource.Keywords.JsonMessage; @@ -209,9 +198,7 @@ public void Logs_AsExpected_AtErrorLevel() { using (var testListener = new TestEventListener()) { - var factory = TestLoggerBuilder.Create() - .AddEventSourceLogger() - .Build(); + var factory = CreateLoggerFactory(); var listenerSettings = new TestEventListener.ListenerSettings(); listenerSettings.Keywords = LoggingEventSource.Keywords.JsonMessage; @@ -236,9 +223,7 @@ public void Logs_AsExpected_AtWarningLevel() { using (var testListener = new TestEventListener()) { - var factory = TestLoggerBuilder.Create() - .AddEventSourceLogger() - .Build(); + var factory = CreateLoggerFactory(); var listenerSettings = new TestEventListener.ListenerSettings(); listenerSettings.Keywords = LoggingEventSource.Keywords.JsonMessage; @@ -265,9 +250,7 @@ public void Logs_AsExpected_WithSingleLoggerSpec() { using (var testListener = new TestEventListener()) { - var factory = TestLoggerBuilder.Create() - .AddEventSourceLogger() - .Build(); + var factory = CreateLoggerFactory(); var listenerSettings = new TestEventListener.ListenerSettings(); listenerSettings.Keywords = LoggingEventSource.Keywords.JsonMessage; @@ -289,9 +272,7 @@ public void Logs_AsExpected_WithSingleLoggerSpecWithVerbosity() { using (var testListener = new TestEventListener()) { - var factory = TestLoggerBuilder.Create() - .AddEventSourceLogger() - .Build(); + var factory = CreateLoggerFactory(); var listenerSettings = new TestEventListener.ListenerSettings(); listenerSettings.Keywords = LoggingEventSource.Keywords.JsonMessage; @@ -311,9 +292,7 @@ public void Logs_AsExpected_WithComplexLoggerSpec() { using (var testListener = new TestEventListener()) { - var factory = TestLoggerBuilder.Create() - .AddEventSourceLogger() - .Build(); + var factory = CreateLoggerFactory(); var listenerSettings = new TestEventListener.ListenerSettings(); listenerSettings.Keywords = LoggingEventSource.Keywords.JsonMessage; @@ -331,6 +310,12 @@ public void Logs_AsExpected_WithComplexLoggerSpec() } } + private static ILoggerFactory CreateLoggerFactory() + { + return TestLoggerBuilder.Create(builder => builder + .AddEventSourceLogger() + .SetMinimalLevel(LogLevel.Trace)); + } private void LogStuff(ILoggerFactory factory) { diff --git a/test/Microsoft.Extensions.Logging.Test/LoggerBuilderTestExtensions.cs b/test/Microsoft.Extensions.Logging.Test/LoggerBuilderTestExtensions.cs index b5999fe5..109ac51f 100644 --- a/test/Microsoft.Extensions.Logging.Test/LoggerBuilderTestExtensions.cs +++ b/test/Microsoft.Extensions.Logging.Test/LoggerBuilderTestExtensions.cs @@ -1,31 +1,20 @@ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. -using Microsoft.Extensions.Configuration; +using System; +using Castle.Core.Logging; using Microsoft.Extensions.DependencyInjection; namespace Microsoft.Extensions.Logging.Test { public static class TestLoggerBuilder { - public static ILoggerBuilder Create(IConfiguration configuration = null) + public static ILoggerFactory Create(Action configure) { - var builder = new ServiceCollection().AddLogging(); - // Most test setup their own filtering or for all events to pass through - builder.Services.Configure(options => options.MinLevel = LogLevel.Trace); - if (configuration != null) - { - builder.AddConfiguration(configuration); - } - return builder; - } - } - - public static class LoggerBuilderTestExtensions - { - public static ILoggerFactory Build(this ILoggerBuilder builder) - { - return builder.Services.BuildServiceProvider().GetRequiredService(); + return new ServiceCollection() + .AddLogging(configure) + .BuildServiceProvider() + .GetRequiredService(); } } } diff --git a/test/Microsoft.Extensions.Logging.Test/LoggerFilterTest.cs b/test/Microsoft.Extensions.Logging.Test/LoggerFilterTest.cs index 5e4c091f..79ef5819 100644 --- a/test/Microsoft.Extensions.Logging.Test/LoggerFilterTest.cs +++ b/test/Microsoft.Extensions.Logging.Test/LoggerFilterTest.cs @@ -29,10 +29,9 @@ public void ChangingConfigReloadsDefaultFilter() var config = CreateConfiguration(() => json); var loggerProvider = new TestLoggerProvider(new TestSink(), isEnabled: true); - var factory = TestLoggerBuilder - .Create(config.GetSection("Logging")) - .AddProvider(loggerProvider) - .Build(); + var factory = TestLoggerBuilder.Create(builder => builder + .AddConfiguration(config.GetSection("Logging")) + .AddProvider(loggerProvider)); var logger = factory.CreateLogger("Microsoft"); @@ -75,10 +74,9 @@ public void ChangingConfigFromUseConfigurationReloadsDefaultFilter() }"; var config = CreateConfiguration(() => json); var loggerProvider = new TestLoggerProvider(new TestSink(), isEnabled: true); - var factory = TestLoggerBuilder - .Create(config.GetSection("Logging")) - .AddProvider(loggerProvider) - .Build(); + var factory = TestLoggerBuilder.Create(builder => builder + .AddConfiguration(config.GetSection("Logging")) + .AddProvider(loggerProvider)); var logger = factory.CreateLogger("Microsoft"); @@ -124,10 +122,9 @@ public void CanFilterOnNamedProviders() var config = CreateConfiguration(() => json); var loggerProvider = new TestLoggerProvider(new TestSink(), isEnabled: true); - var factory = TestLoggerBuilder - .Create(config.GetSection("Logging")) - .AddProvider(loggerProvider) - .Build(); + var factory = TestLoggerBuilder.Create(builder => builder + .AddConfiguration(config.GetSection("Logging")) + .AddProvider(loggerProvider)); var logger = factory.CreateLogger("Microsoft"); @@ -159,10 +156,9 @@ public void PreferFullNameOverDefaultForFiltering() var config = CreateConfiguration(() => json); var loggerProvider = new TestLoggerProvider(new TestSink(), isEnabled: true); - var factory = TestLoggerBuilder - .Create(config.GetSection("Logging")) - .AddProvider(loggerProvider) - .Build(); + var factory = TestLoggerBuilder.Create(builder => builder + .AddConfiguration(config.GetSection("Logging")) + .AddProvider(loggerProvider)); var logger = factory.CreateLogger("Microsoft"); @@ -192,10 +188,9 @@ public void DefaultCategoryNameIsUsedIfNoneMatch() var config = CreateConfiguration(() => json); var loggerProvider = new TestLoggerProvider(new TestSink(), isEnabled: true); - var factory = TestLoggerBuilder - .Create(config.GetSection("Logging")) - .AddProvider(loggerProvider) - .Build(); + var factory = TestLoggerBuilder.Create(builder => builder + .AddConfiguration(config.GetSection("Logging")) + .AddProvider(loggerProvider)); var logger = factory.CreateLogger("Microsoft"); @@ -226,8 +221,7 @@ public void DefaultCategoryNameIsUsedIfNoneMatch() public void AddFilterForMatchingProviderFilters() { var provider = new TestLoggerProvider(new TestSink(), isEnabled: true); - var factory = TestLoggerBuilder - .Create() + var factory = TestLoggerBuilder.Create(builder => builder .AddProvider(provider) .AddFilter((name, cat, level) => { @@ -240,8 +234,7 @@ public void AddFilterForMatchingProviderFilters() } return true; - }) - .Build(); + })); var logger = factory.CreateLogger("Test"); @@ -259,8 +252,7 @@ public void AddFilterForMatchingProviderFilters() public void AddFilterForNonMatchingProviderDoesNotFilter() { var provider = new TestLoggerProvider(new TestSink(), isEnabled: true); - var factory = TestLoggerBuilder - .Create() + var factory = TestLoggerBuilder.Create(builder => builder .AddProvider(provider) .AddFilter((name, cat, level) => { @@ -270,8 +262,7 @@ public void AddFilterForNonMatchingProviderDoesNotFilter() } return true; - }) - .Build(); + })); var logger = factory.CreateLogger("Test"); @@ -285,12 +276,10 @@ public void AddFilterForNonMatchingProviderDoesNotFilter() public void AddFilterIsAdditive() { var provider = new TestLoggerProvider(new TestSink(), isEnabled: true); - var factory = TestLoggerBuilder - .Create() + var factory = TestLoggerBuilder.Create(builder => builder .AddProvider(provider) .AddFilter((name, cat, level) => level >= LogLevel.Warning) - .AddFilter((name, cat, level) => string.Equals(cat, "NotTest")) - .Build(); + .AddFilter((name, cat, level) => string.Equals(cat, "NotTest"))); var logger = factory.CreateLogger("Test"); @@ -327,11 +316,10 @@ public void ProviderLevelIsPreferredOverGlobalFilter() var config = CreateConfiguration(() => json); var loggerProvider = new TestLoggerProvider(new TestSink(), isEnabled: true); - var factory = TestLoggerBuilder - .Create(config.GetSection("Logging")) + var factory = TestLoggerBuilder.Create(builder => builder + .AddConfiguration(config.GetSection("Logging")) .AddProvider(loggerProvider) - .AddFilter((name, cat, level) => level < LogLevel.Critical) - .Build(); + .AddFilter((name, cat, level) => level < LogLevel.Critical)); var logger = factory.CreateLogger("Test"); @@ -354,11 +342,9 @@ public void ProviderLevelIsPreferredOverGlobalFilter() public void AddFilterWithProviderNameCategoryNameAndFilterFuncFiltersCorrectly() { var provider = new TestLoggerProvider(new TestSink(), isEnabled: true); - var factory = TestLoggerBuilder - .Create() + var factory = TestLoggerBuilder.Create(builder => builder .AddProvider(provider) - .AddFilter((name, cat, level) => level >= LogLevel.Warning) - .Build(); + .AddFilter((name, cat, level) => level >= LogLevel.Warning)); var logger = factory.CreateLogger("Sample.Test"); @@ -376,11 +362,9 @@ public void AddFilterWithProviderNameCategoryNameAndFilterFuncFiltersCorrectly() public void AddFilterWithProviderNameCategoryNameAndMinLevelFiltersCorrectly() { var provider = new TestLoggerProvider(new TestSink(), isEnabled: true); - var factory = TestLoggerBuilder - .Create() + var factory = TestLoggerBuilder.Create(builder => builder .AddProvider(provider) - .AddFilter("Sample", LogLevel.Warning) - .Build(); + .AddFilter("Sample", LogLevel.Warning)); var logger = factory.CreateLogger("Sample.Test"); @@ -398,11 +382,9 @@ public void AddFilterWithProviderNameCategoryNameAndMinLevelFiltersCorrectly() public void AddFilterWithProviderNameAndCategoryFilterFuncFiltersCorrectly() { var provider = new TestLoggerProvider(new TestSink(), isEnabled: true); - var factory = TestLoggerBuilder - .Create() + var factory = TestLoggerBuilder.Create(builder => builder .AddProvider(provider) - .AddFilter((c, l) => l >= LogLevel.Warning) - .Build(); + .AddFilter((c, l) => l >= LogLevel.Warning)); var logger = factory.CreateLogger("Sample.Test"); diff --git a/test/Microsoft.Extensions.Logging.Test/LoggerTest.cs b/test/Microsoft.Extensions.Logging.Test/LoggerTest.cs index 0a89f143..6907a114 100644 --- a/test/Microsoft.Extensions.Logging.Test/LoggerTest.cs +++ b/test/Microsoft.Extensions.Logging.Test/LoggerTest.cs @@ -15,11 +15,10 @@ public void Log_IgnoresExceptionInIntermediateLoggersAndThrowsAggregateException { // Arrange var store = new List(); - var loggerFactory = TestLoggerBuilder.Create() + var loggerFactory = TestLoggerBuilder.Create(builder => builder .AddProvider(new CustomLoggerProvider("provider1", ThrowExceptionAt.None, store)) .AddProvider(new CustomLoggerProvider("provider2", ThrowExceptionAt.Log, store)) - .AddProvider(new CustomLoggerProvider("provider3", ThrowExceptionAt.None, store)) - .Build(); + .AddProvider(new CustomLoggerProvider("provider3", ThrowExceptionAt.None, store))); var logger = loggerFactory.CreateLogger("Test"); @@ -40,11 +39,10 @@ public void BeginScope_IgnoresExceptionInIntermediateLoggersAndThrowsAggregateEx { // Arrange var store = new List(); - var loggerFactory = TestLoggerBuilder.Create() + var loggerFactory = TestLoggerBuilder.Create(builder => builder .AddProvider(new CustomLoggerProvider("provider1", ThrowExceptionAt.None, store)) .AddProvider(new CustomLoggerProvider("provider2", ThrowExceptionAt.BeginScope, store)) - .AddProvider(new CustomLoggerProvider("provider3", ThrowExceptionAt.None, store)) - .Build(); + .AddProvider(new CustomLoggerProvider("provider3", ThrowExceptionAt.None, store))); var logger = loggerFactory.CreateLogger("Test"); @@ -65,11 +63,10 @@ public void IsEnabled_IgnoresExceptionInIntermediateLoggers() { // Arrange var store = new List(); - var loggerFactory = TestLoggerBuilder.Create() + var loggerFactory = TestLoggerBuilder.Create(builder => builder .AddProvider(new CustomLoggerProvider("provider1", ThrowExceptionAt.None, store)) .AddProvider(new CustomLoggerProvider("provider2", ThrowExceptionAt.IsEnabled, store)) - .AddProvider(new CustomLoggerProvider("provider3", ThrowExceptionAt.None, store)) - .Build(); + .AddProvider(new CustomLoggerProvider("provider3", ThrowExceptionAt.None, store))); var logger = loggerFactory.CreateLogger("Test"); @@ -90,10 +87,9 @@ public void Log_AggregatesExceptionsFromMultipleLoggers() { // Arrange var store = new List(); - var loggerFactory = TestLoggerBuilder.Create() + var loggerFactory = TestLoggerBuilder.Create(builder => builder .AddProvider(new CustomLoggerProvider("provider1", ThrowExceptionAt.Log, store)) - .AddProvider(new CustomLoggerProvider("provider2", ThrowExceptionAt.Log, store)) - .Build(); + .AddProvider(new CustomLoggerProvider("provider2", ThrowExceptionAt.Log, store))); var logger = loggerFactory.CreateLogger("Test"); diff --git a/test/Microsoft.Extensions.Logging.Test/LoggingServiceCollectionExtensionsTest.cs b/test/Microsoft.Extensions.Logging.Test/LoggingServiceCollectionExtensionsTest.cs index 4602d9ba..afe1eeb0 100644 --- a/test/Microsoft.Extensions.Logging.Test/LoggingServiceCollectionExtensionsTest.cs +++ b/test/Microsoft.Extensions.Logging.Test/LoggingServiceCollectionExtensionsTest.cs @@ -13,8 +13,12 @@ public void AddLogging_WrapsServiceCollection() { var services = new ServiceCollection(); - var loggerBuilder = services.AddLogging(); - Assert.Same(services, loggerBuilder.Services); + var callbackCalled = true; + var loggerBuilder = services.AddLogging(builder => + { + Assert.Same(services, builder.Services); + }); + Assert.True(callbackCalled); } } } diff --git a/test/Microsoft.Extensions.Logging.Test/TraceSourceLoggerTest.cs b/test/Microsoft.Extensions.Logging.Test/TraceSourceLoggerTest.cs index 6edeee45..6b45e252 100644 --- a/test/Microsoft.Extensions.Logging.Test/TraceSourceLoggerTest.cs +++ b/test/Microsoft.Extensions.Logging.Test/TraceSourceLoggerTest.cs @@ -3,6 +3,7 @@ #if NET46 using System.Diagnostics; +using Microsoft.Extensions.DependencyInjection; using Xunit; namespace Microsoft.Extensions.Logging.Test @@ -16,9 +17,7 @@ public static void IsEnabledReturnsCorrectValue() var testSwitch = new SourceSwitch("TestSwitch", "Level will be set to warning for this test"); testSwitch.Level = SourceLevels.Warning; - var factory = TestLoggerBuilder.Create() - .AddTraceSource(testSwitch) - .Build(); + var factory = TestLoggerBuilder.Create(builder => builder.AddTraceSource(testSwitch)); // Act var logger = factory.CreateLogger("Test"); @@ -48,10 +47,9 @@ public static void MultipleLoggers_IsEnabledReturnsCorrectValue(SourceLevels fir // Act - var factory = TestLoggerBuilder.Create() + var factory = TestLoggerBuilder.Create(builder => builder .AddTraceSource(firstSwitch) - .AddTraceSource(secondSwitch) - .Build(); + .AddTraceSource(secondSwitch)); var logger = factory.CreateLogger("Test"); diff --git a/test/Microsoft.Extensions.Logging.Test/TraceSourceScopeTest.cs b/test/Microsoft.Extensions.Logging.Test/TraceSourceScopeTest.cs index d08a4d03..ad76f518 100644 --- a/test/Microsoft.Extensions.Logging.Test/TraceSourceScopeTest.cs +++ b/test/Microsoft.Extensions.Logging.Test/TraceSourceScopeTest.cs @@ -2,6 +2,7 @@ // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System.Diagnostics; +using Microsoft.Extensions.DependencyInjection; using Xunit; namespace Microsoft.Extensions.Logging.Test @@ -17,9 +18,8 @@ public static void DiagnosticsScope_PushesAndPops_LogicalOperationStack() Trace.CorrelationManager.StartLogicalOperation(baseState); var state = "1337state7331"; - var factory = TestLoggerBuilder.Create() - .AddTraceSource(new SourceSwitch("TestSwitch"), new ConsoleTraceListener()) - .Build(); + var factory = TestLoggerBuilder.Create(builder => + builder.AddTraceSource(new SourceSwitch("TestSwitch"), new ConsoleTraceListener())); var logger = factory.CreateLogger("Test"); diff --git a/test/Microsoft.Extensions.Logging.Testing.Tests/XunitLoggerProviderTest.cs b/test/Microsoft.Extensions.Logging.Testing.Tests/XunitLoggerProviderTest.cs index 44fc4d72..12669e3b 100644 --- a/test/Microsoft.Extensions.Logging.Testing.Tests/XunitLoggerProviderTest.cs +++ b/test/Microsoft.Extensions.Logging.Testing.Tests/XunitLoggerProviderTest.cs @@ -2,6 +2,7 @@ // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System; +using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Logging.Test; using Xunit; @@ -14,9 +15,9 @@ public void LoggerProviderWritesToTestOutputHelper() { var testTestOutputHelper = new TestTestOutputHelper(); - var loggerFactory = TestLoggerBuilder.Create() - .AddXunit(testTestOutputHelper) - .Build(); + var loggerFactory = TestLoggerBuilder.Create(builder => builder + .SetMinimalLevel(LogLevel.Trace) + .AddXunit(testTestOutputHelper)); var logger = loggerFactory.CreateLogger("TestCategory"); logger.LogInformation("This is some great information"); @@ -33,9 +34,8 @@ public void LoggerProviderWritesToTestOutputHelper() public void LoggerProviderDoesNotWriteLogMessagesBelowMinimumLevel() { var testTestOutputHelper = new TestTestOutputHelper(); - var loggerFactory = TestLoggerBuilder.Create() - .AddXunit(testTestOutputHelper, LogLevel.Warning) - .Build(); + var loggerFactory = TestLoggerBuilder.Create(builder => builder + .AddXunit(testTestOutputHelper, LogLevel.Warning)); var logger = loggerFactory.CreateLogger("TestCategory"); logger.LogInformation("This is some great information"); @@ -48,9 +48,8 @@ public void LoggerProviderDoesNotWriteLogMessagesBelowMinimumLevel() public void LoggerProviderPrependsPrefixToEachLine() { var testTestOutputHelper = new TestTestOutputHelper(); - var loggerFactory = TestLoggerBuilder.Create() - .AddXunit(testTestOutputHelper) - .Build(); + var loggerFactory = TestLoggerBuilder.Create(builder => builder + .AddXunit(testTestOutputHelper)); var logger = loggerFactory.CreateLogger("TestCategory"); logger.LogInformation("This is a" + Environment.NewLine + "multi-line" + Environment.NewLine + "message"); @@ -67,9 +66,8 @@ public void LoggerProviderPrependsPrefixToEachLine() public void LoggerProviderDoesNotThrowIfOutputHelperThrows() { var testTestOutputHelper = new TestTestOutputHelper(); - var loggerFactory = TestLoggerBuilder.Create() - .AddXunit(testTestOutputHelper) - .Build(); + var loggerFactory = TestLoggerBuilder.Create(builder => builder + .AddXunit(testTestOutputHelper)); testTestOutputHelper.Throw = true; From ba88967a7c1fc788aa844e03179b7298ee78baae Mon Sep 17 00:00:00 2001 From: Pavel Krymets Date: Fri, 26 May 2017 14:57:32 -0700 Subject: [PATCH 11/20] Less overloads --- src/Microsoft.Extensions.Logging/LoggerFactory.cs | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/src/Microsoft.Extensions.Logging/LoggerFactory.cs b/src/Microsoft.Extensions.Logging/LoggerFactory.cs index 1ea5c0de..28c4c211 100644 --- a/src/Microsoft.Extensions.Logging/LoggerFactory.cs +++ b/src/Microsoft.Extensions.Logging/LoggerFactory.cs @@ -20,15 +20,11 @@ public class LoggerFactory : ILoggerFactory private IDisposable _changeTokenRegistration; private LoggerFilterOptions _filterOptions; - public LoggerFactory() : this(new LoggerFilterOptions()) + public LoggerFactory() : this(Enumerable.Empty(), new LoggerFilterOptions()) { } - public LoggerFactory(LoggerFilterOptions filterOptions) : this(Enumerable.Empty(), new StaticFilterOptionsMonitor(filterOptions)) - { - } - - public LoggerFactory(IEnumerable providers) : this(providers, new StaticFilterOptionsMonitor(new LoggerFilterOptions())) + public LoggerFactory(IEnumerable providers, LoggerFilterOptions filterOptions) : this(providers, new StaticFilterOptionsMonitor(filterOptions)) { } From 5bfc38523d10e0f0bd452fd0e99be4e3cfb6483e Mon Sep 17 00:00:00 2001 From: Pavel Krymets Date: Fri, 26 May 2017 15:21:52 -0700 Subject: [PATCH 12/20] More overloads less obsoletes --- .../ILoggerFactory.cs | 4 --- ...AzureAppServicesLoggerFactoryExtensions.cs | 8 ----- .../ConsoleLoggerFactoryExtensions.cs | 32 ------------------- .../ConsoleLoggerProvider.cs | 2 -- .../DebugLogger.cs | 9 +----- .../DebugLoggerFactoryExtensions.cs | 12 ------- .../DebugLoggerProvider.cs | 6 ---- .../EventLoggerFactoryExtensions.cs | 12 ------- .../EventSourceLoggerFactoryExtensions.cs | 4 --- .../XunitLoggerFactoryExtensions.cs | 2 -- .../TraceSourceFactoryExtensions.cs | 22 ------------- .../LoggerFactory.cs | 8 +++-- .../ConsoleLoggerTest.cs | 14 +++----- .../LoggerFactoryTest.cs | 14 +++----- .../LoggerTest.cs | 4 +-- 15 files changed, 17 insertions(+), 136 deletions(-) diff --git a/src/Microsoft.Extensions.Logging.Abstractions/ILoggerFactory.cs b/src/Microsoft.Extensions.Logging.Abstractions/ILoggerFactory.cs index 1647bb34..69a012ce 100644 --- a/src/Microsoft.Extensions.Logging.Abstractions/ILoggerFactory.cs +++ b/src/Microsoft.Extensions.Logging.Abstractions/ILoggerFactory.cs @@ -19,13 +19,9 @@ public interface ILoggerFactory : IDisposable ILogger CreateLogger(string categoryName); /// - /// - /// This method is obsolete and will be removed in a future version. The recommended alternative is to call the Microsoft.Extensions.Logging.AddProvider() method on the Microsoft.Extensions.Logging.LoggerFactory instance. - /// /// Adds an to the logging system. /// /// The . - [Obsolete("This method is obsolete and will be removed in a future version. The recommended alternative is to call the Microsoft.Extensions.Logging.AddProvider() method on the Microsoft.Extensions.Logging.LoggerFactory instance.")] void AddProvider(ILoggerProvider provider); } } \ No newline at end of file diff --git a/src/Microsoft.Extensions.Logging.AzureAppServices/AzureAppServicesLoggerFactoryExtensions.cs b/src/Microsoft.Extensions.Logging.AzureAppServices/AzureAppServicesLoggerFactoryExtensions.cs index 1368ff65..fd28c59b 100644 --- a/src/Microsoft.Extensions.Logging.AzureAppServices/AzureAppServicesLoggerFactoryExtensions.cs +++ b/src/Microsoft.Extensions.Logging.AzureAppServices/AzureAppServicesLoggerFactoryExtensions.cs @@ -39,27 +39,19 @@ public static ILoggerBuilder AddAzureWebAppDiagnostics(this ILoggerBuilder build } /// - /// - /// This method is obsolete and will be removed in a future version. The recommended alternative is to call the Microsoft.Extensions.Logging.AddAzureWebAppDiagnostics() extension method on the Microsoft.Extensions.Logging.LoggerFactory instance. - /// /// Adds an Azure Web Apps diagnostics logger. /// /// The extension method argument - [Obsolete("This method is obsolete and will be removed in a future version. The recommended alternative is to call the Microsoft.Extensions.Logging.AddAzureWebAppDiagnostics() extension method on the Microsoft.Extensions.Logging.LoggerFactory instance.")] public static ILoggerFactory AddAzureWebAppDiagnostics(this ILoggerFactory factory) { return AddAzureWebAppDiagnostics(factory, new AzureAppServicesDiagnosticsSettings()); } /// - /// - /// This method is obsolete and will be removed in a future version. The recommended alternative is to call the Microsoft.Extensions.Logging.AddAzureWebAppDiagnostics() extension method on the Microsoft.Extensions.Logging.LoggerFactory instance. - /// /// Adds an Azure Web Apps diagnostics logger. /// /// The extension method argument /// The setting object to configure loggers. - [Obsolete("This method is obsolete and will be removed in a future version. The recommended alternative is to call the Microsoft.Extensions.Logging.AddAzureWebAppDiagnostics() extension method on the Microsoft.Extensions.Logging.LoggerFactory instance.")] public static ILoggerFactory AddAzureWebAppDiagnostics(this ILoggerFactory factory, AzureAppServicesDiagnosticsSettings settings) { if (WebAppContext.Default.IsRunningInAzureWebApp) diff --git a/src/Microsoft.Extensions.Logging.Console/ConsoleLoggerFactoryExtensions.cs b/src/Microsoft.Extensions.Logging.Console/ConsoleLoggerFactoryExtensions.cs index e71927da..886aba12 100644 --- a/src/Microsoft.Extensions.Logging.Console/ConsoleLoggerFactoryExtensions.cs +++ b/src/Microsoft.Extensions.Logging.Console/ConsoleLoggerFactoryExtensions.cs @@ -58,28 +58,20 @@ public static ILoggerBuilder AddConsole(this ILoggerBuilder builder, IConfigurat } /// - /// - /// This method is obsolete and will be removed in a future version. The recommended alternative is to call the Microsoft.Extensions.Logging.AddConsole() extension method on the Microsoft.Extensions.Logging.LoggerFactory instance. - /// /// Adds a console logger that is enabled for .Information or higher. /// /// The to use. - [Obsolete("This method is obsolete and will be removed in a future version. The recommended alternative is to call the Microsoft.Extensions.Logging.AddConsole() extension method on the Microsoft.Extensions.Logging.LoggerFactory instance.")] public static ILoggerFactory AddConsole(this ILoggerFactory factory) { return factory.AddConsole(includeScopes: false); } /// - /// - /// This method is obsolete and will be removed in a future version. The recommended alternative is to call the Microsoft.Extensions.Logging.AddConsole() extension method on the Microsoft.Extensions.Logging.LoggerFactory instance. - /// /// Adds a console logger that is enabled for .Information or higher. /// /// The to use. /// A value which indicates whether log scope information should be displayed /// in the output. - [Obsolete("This method is obsolete and will be removed in a future version. The recommended alternative is to call the Microsoft.Extensions.Logging.AddConsole() extension method on the Microsoft.Extensions.Logging.LoggerFactory instance.")] public static ILoggerFactory AddConsole(this ILoggerFactory factory, bool includeScopes) { factory.AddConsole((n, l) => l >= LogLevel.Information, includeScopes); @@ -87,14 +79,10 @@ public static ILoggerFactory AddConsole(this ILoggerFactory factory, bool includ } /// - /// - /// This method is obsolete and will be removed in a future version. The recommended alternative is to call the Microsoft.Extensions.Logging.AddConsole() extension method on the Microsoft.Extensions.Logging.LoggerFactory instance. - /// /// Adds a console logger that is enabled for s of minLevel or higher. /// /// The to use. /// The minimum to be logged - [Obsolete("This method is obsolete and will be removed in a future version. The recommended alternative is to call the Microsoft.Extensions.Logging.AddConsole() extension method on the Microsoft.Extensions.Logging.LoggerFactory instance.")] public static ILoggerFactory AddConsole(this ILoggerFactory factory, LogLevel minLevel) { factory.AddConsole(minLevel, includeScopes: false); @@ -102,16 +90,12 @@ public static ILoggerFactory AddConsole(this ILoggerFactory factory, LogLevel mi } /// - /// - /// This method is obsolete and will be removed in a future version. The recommended alternative is to call the Microsoft.Extensions.Logging.AddConsole() extension method on the Microsoft.Extensions.Logging.LoggerFactory instance. - /// /// Adds a console logger that is enabled for s of minLevel or higher. /// /// The to use. /// The minimum to be logged /// A value which indicates whether log scope information should be displayed /// in the output. - [Obsolete("This method is obsolete and will be removed in a future version. The recommended alternative is to call the Microsoft.Extensions.Logging.AddConsole() extension method on the Microsoft.Extensions.Logging.LoggerFactory instance.")] public static ILoggerFactory AddConsole( this ILoggerFactory factory, LogLevel minLevel, @@ -122,14 +106,10 @@ public static ILoggerFactory AddConsole( } /// - /// - /// This method is obsolete and will be removed in a future version. The recommended alternative is to call the Microsoft.Extensions.Logging.AddConsole() extension method on the Microsoft.Extensions.Logging.LoggerFactory instance. - /// /// Adds a console logger that is enabled as defined by the filter function. /// /// The to use. /// The category filter to apply to logs. - [Obsolete("This method is obsolete and will be removed in a future version. The recommended alternative is to call the Microsoft.Extensions.Logging.AddConsole() extension method on the Microsoft.Extensions.Logging.LoggerFactory instance.")] public static ILoggerFactory AddConsole( this ILoggerFactory factory, Func filter) @@ -139,16 +119,12 @@ public static ILoggerFactory AddConsole( } /// - /// - /// This method is obsolete and will be removed in a future version. The recommended alternative is to call the Microsoft.Extensions.Logging.AddConsole() extension method on the Microsoft.Extensions.Logging.LoggerFactory instance. - /// /// Adds a console logger that is enabled as defined by the filter function. /// /// The to use. /// The category filter to apply to logs. /// A value which indicates whether log scope information should be displayed /// in the output. - [Obsolete("This method is obsolete and will be removed in a future version. The recommended alternative is to call the Microsoft.Extensions.Logging.AddConsole() extension method on the Microsoft.Extensions.Logging.LoggerFactory instance.")] public static ILoggerFactory AddConsole( this ILoggerFactory factory, Func filter, @@ -160,14 +136,10 @@ public static ILoggerFactory AddConsole( /// - /// - /// This method is obsolete and will be removed in a future version. The recommended alternative is to call the Microsoft.Extensions.Logging.AddConsole() extension method on the Microsoft.Extensions.Logging.LoggerFactory instance. - /// /// /// The to use. /// The settings to apply to created 's. /// - [Obsolete("This method is obsolete and will be removed in a future version. The recommended alternative is to call the Microsoft.Extensions.Logging.AddConsole() extension method on the Microsoft.Extensions.Logging.LoggerFactory instance.")] public static ILoggerFactory AddConsole( this ILoggerFactory factory, IConsoleLoggerSettings settings) @@ -177,14 +149,10 @@ public static ILoggerFactory AddConsole( } /// - /// - /// This method is obsolete and will be removed in a future version. The recommended alternative is to call the Microsoft.Extensions.Logging.AddConsole() extension method on the Microsoft.Extensions.Logging.LoggerFactory instance. - /// /// /// The to use. /// The to use for . /// - [Obsolete("This method is obsolete and will be removed in a future version. The recommended alternative is to call the Microsoft.Extensions.Logging.AddConsole() extension method on the Microsoft.Extensions.Logging.LoggerFactory instance.")] public static ILoggerFactory AddConsole(this ILoggerFactory factory, IConfiguration configuration) { var settings = new ConfigurationConsoleLoggerSettings(configuration); diff --git a/src/Microsoft.Extensions.Logging.Console/ConsoleLoggerProvider.cs b/src/Microsoft.Extensions.Logging.Console/ConsoleLoggerProvider.cs index 7124a89e..948a3e7f 100644 --- a/src/Microsoft.Extensions.Logging.Console/ConsoleLoggerProvider.cs +++ b/src/Microsoft.Extensions.Logging.Console/ConsoleLoggerProvider.cs @@ -21,7 +21,6 @@ public class ConsoleLoggerProvider : ILoggerProvider private static readonly Func falseFilter = (cat, level) => false; private IDisposable _optionsReloadToken; - [Obsolete("This method is obsolete and will be removed in a future version. The recommended alternative is Microsoft.Extensions.Logging.Console.ConsoleLoggerProvider(IConfiguration).")] public ConsoleLoggerProvider(Func filter, bool includeScopes) { if (filter == null) @@ -54,7 +53,6 @@ private void ReloadLoggerOptions(ConsoleLoggerOptions options) } } - [Obsolete("This method is obsolete and will be removed in a future version. The recommended alternative is Microsoft.Extensions.Logging.Console.ConsoleLoggerProvider(IConfiguration).")] public ConsoleLoggerProvider(IConsoleLoggerSettings settings) { if (settings == null) diff --git a/src/Microsoft.Extensions.Logging.Debug/DebugLogger.cs b/src/Microsoft.Extensions.Logging.Debug/DebugLogger.cs index 1c356f21..eb2127a2 100644 --- a/src/Microsoft.Extensions.Logging.Debug/DebugLogger.cs +++ b/src/Microsoft.Extensions.Logging.Debug/DebugLogger.cs @@ -18,22 +18,15 @@ public partial class DebugLogger : ILogger /// Initializes a new instance of the class. /// /// The name of the logger. - public DebugLogger(string name) -#pragma warning disable CS0618 // Type or member is obsolete - : this(name, filter: null) -#pragma warning restore CS0618 // Type or member is obsolete + public DebugLogger(string name) : this(name, filter: null) { } /// - /// - /// This method is obsolete and will be removed in a future version. The recommended alternative is Microsoft.Extensions.Logging.Debug.DebugLogger(string). - /// /// Initializes a new instance of the class. /// /// The name of the logger. /// The function used to filter events based on the log level. - [Obsolete("This method is obsolete and will be removed in a future version. The recommended alternative is Microsoft.Extensions.Logging.Debug.DebugLogger(string).")] public DebugLogger(string name, Func filter) { _name = string.IsNullOrEmpty(name) ? nameof(DebugLogger) : name; diff --git a/src/Microsoft.Extensions.Logging.Debug/DebugLoggerFactoryExtensions.cs b/src/Microsoft.Extensions.Logging.Debug/DebugLoggerFactoryExtensions.cs index 1d70bd3b..0df9c266 100644 --- a/src/Microsoft.Extensions.Logging.Debug/DebugLoggerFactoryExtensions.cs +++ b/src/Microsoft.Extensions.Logging.Debug/DebugLoggerFactoryExtensions.cs @@ -24,27 +24,19 @@ public static ILoggerBuilder AddDebug(this ILoggerBuilder builder) } /// - /// - /// This method is obsolete and will be removed in a future version. The recommended alternative is to call the Microsoft.Extensions.Logging.AddDebug() extension method on the Microsoft.Extensions.Logging.LoggerFactory instance. - /// /// Adds a debug logger that is enabled for .Information or higher. /// /// The extension method argument. - [Obsolete("This method is obsolete and will be removed in a future version. The recommended alternative is to call the Microsoft.Extensions.Logging.AddDebug() extension method on the Microsoft.Extensions.Logging.LoggerFactory instance.")] public static ILoggerFactory AddDebug(this ILoggerFactory factory) { return AddDebug(factory, LogLevel.Information); } /// - /// - /// This method is obsolete and will be removed in a future version. The recommended alternative is to call the Microsoft.Extensions.Logging.AddDebug() extension method on the Microsoft.Extensions.Logging.LoggerFactory instance. - /// /// Adds a debug logger that is enabled as defined by the filter function. /// /// The extension method argument. /// The function used to filter events based on the log level. - [Obsolete("This method is obsolete and will be removed in a future version. The recommended alternative is to call the Microsoft.Extensions.Logging.AddDebug() extension method on the Microsoft.Extensions.Logging.LoggerFactory instance.")] public static ILoggerFactory AddDebug(this ILoggerFactory factory, Func filter) { factory.AddProvider(new DebugLoggerProvider(filter)); @@ -52,14 +44,10 @@ public static ILoggerFactory AddDebug(this ILoggerFactory factory, Func - /// - /// This method is obsolete and will be removed in a future version. The recommended alternative is to call the Microsoft.Extensions.Logging.AddDebug() extension method on the Microsoft.Extensions.Logging.LoggerFactory instance. - /// /// Adds a debug logger that is enabled for s of minLevel or higher. /// /// The extension method argument. /// The minimum to be logged - [Obsolete("This method is obsolete and will be removed in a future version. The recommended alternative is to call the Microsoft.Extensions.Logging.AddDebug() extension method on the Microsoft.Extensions.Logging.LoggerFactory instance.")] public static ILoggerFactory AddDebug(this ILoggerFactory factory, LogLevel minLevel) { return AddDebug( diff --git a/src/Microsoft.Extensions.Logging.Debug/DebugLoggerProvider.cs b/src/Microsoft.Extensions.Logging.Debug/DebugLoggerProvider.cs index 266662c3..7c2570fc 100644 --- a/src/Microsoft.Extensions.Logging.Debug/DebugLoggerProvider.cs +++ b/src/Microsoft.Extensions.Logging.Debug/DebugLoggerProvider.cs @@ -18,13 +18,9 @@ public DebugLoggerProvider() } /// - /// - /// This method is obsolete and will be removed in a future version. The recommended alternative is Microsoft.Extensions.Logging.Debug.DebugLoggerProvider(). - /// /// Initializes a new instance of the class. /// /// The function used to filter events based on the log level. - [Obsolete("This method is obsolete and will be removed in a future version. The recommended alternative is Microsoft.Extensions.Logging.Debug.DebugLoggerProvider().")] public DebugLoggerProvider(Func filter) { _filter = filter; @@ -33,9 +29,7 @@ public DebugLoggerProvider(Func filter) /// public ILogger CreateLogger(string name) { -#pragma warning disable CS0618 // Type or member is obsolete return new DebugLogger(name, _filter); -#pragma warning restore CS0618 // Type or member is obsolete } public void Dispose() diff --git a/src/Microsoft.Extensions.Logging.EventLog/EventLoggerFactoryExtensions.cs b/src/Microsoft.Extensions.Logging.EventLog/EventLoggerFactoryExtensions.cs index 4064f495..6f1118fd 100644 --- a/src/Microsoft.Extensions.Logging.EventLog/EventLoggerFactoryExtensions.cs +++ b/src/Microsoft.Extensions.Logging.EventLog/EventLoggerFactoryExtensions.cs @@ -51,13 +51,9 @@ public static ILoggerBuilder AddEventLog(this ILoggerBuilder builder, EventLogSe } /// - /// - /// This method is obsolete and will be removed in a future version. The recommended alternative is to call the Microsoft.Extensions.Logging.AddEventLog() extension method on the Microsoft.Extensions.Logging.LoggerFactory instance. - /// /// Adds an event logger that is enabled for .Information or higher. /// /// The extension method argument. - [Obsolete("This method is obsolete and will be removed in a future version. The recommended alternative is to call the Microsoft.Extensions.Logging.AddEventLog() extension method on the Microsoft.Extensions.Logging.LoggerFactory instance.")] public static ILoggerFactory AddEventLog(this ILoggerFactory factory) { if (factory == null) @@ -69,14 +65,10 @@ public static ILoggerFactory AddEventLog(this ILoggerFactory factory) } /// - /// - /// This method is obsolete and will be removed in a future version. The recommended alternative is to call the Microsoft.Extensions.Logging.AddEventLog() extension method on the Microsoft.Extensions.Logging.LoggerFactory instance. - /// /// Adds an event logger that is enabled for s of minLevel or higher. /// /// The extension method argument. /// The minimum to be logged - [Obsolete("This method is obsolete and will be removed in a future version. The recommended alternative is to call the Microsoft.Extensions.Logging.AddEventLog() extension method on the Microsoft.Extensions.Logging.LoggerFactory instance.")] public static ILoggerFactory AddEventLog(this ILoggerFactory factory, LogLevel minLevel) { if (factory == null) @@ -91,14 +83,10 @@ public static ILoggerFactory AddEventLog(this ILoggerFactory factory, LogLevel m } /// - /// - /// This method is obsolete and will be removed in a future version. The recommended alternative is to call the Microsoft.Extensions.Logging.AddEventLog() extension method on the Microsoft.Extensions.Logging.LoggerFactory instance. - /// /// Adds an event logger. Use to enable logging for specific s. /// /// The extension method argument. /// The . - [Obsolete("This method is obsolete and will be removed in a future version. The recommended alternative is to call the Microsoft.Extensions.Logging.AddEventLog() extension method on the Microsoft.Extensions.Logging.LoggerFactory instance.")] public static ILoggerFactory AddEventLog( this ILoggerFactory factory, EventLogSettings settings) diff --git a/src/Microsoft.Extensions.Logging.EventSource/EventSourceLoggerFactoryExtensions.cs b/src/Microsoft.Extensions.Logging.EventSource/EventSourceLoggerFactoryExtensions.cs index 5e9fff88..a27a88e7 100644 --- a/src/Microsoft.Extensions.Logging.EventSource/EventSourceLoggerFactoryExtensions.cs +++ b/src/Microsoft.Extensions.Logging.EventSource/EventSourceLoggerFactoryExtensions.cs @@ -30,13 +30,9 @@ public static ILoggerBuilder AddEventSourceLogger(this ILoggerBuilder builder) } /// - /// - /// This method is obsolete and will be removed in a future version. The recommended alternative is to call the Microsoft.Extensions.Logging.AddEventSourceLogger() extension method on the Microsoft.Extensions.Logging.LoggerFactory instance. - /// /// Adds an event logger that is enabled for .Information or higher. /// /// The extension method argument. - [Obsolete("This method is obsolete and will be removed in a future version. The recommended alternative is to call the Microsoft.Extensions.Logging.AddEventSourceLogger() extension method on the Microsoft.Extensions.Logging.LoggerFactory instance.")] public static ILoggerFactory AddEventSourceLogger(this ILoggerFactory factory) { if (factory == null) diff --git a/src/Microsoft.Extensions.Logging.Testing/XunitLoggerFactoryExtensions.cs b/src/Microsoft.Extensions.Logging.Testing/XunitLoggerFactoryExtensions.cs index b85ca963..d2d7b613 100644 --- a/src/Microsoft.Extensions.Logging.Testing/XunitLoggerFactoryExtensions.cs +++ b/src/Microsoft.Extensions.Logging.Testing/XunitLoggerFactoryExtensions.cs @@ -22,14 +22,12 @@ public static ILoggerBuilder AddXunit(this ILoggerBuilder builder, ITestOutputHe return builder; } - [Obsolete("This method is obsolete and will be removed in a future version. The recommended alternative is to call the Microsoft.Extensions.Logging.AddXunit() extension method on the Microsoft.Extensions.Logging.LoggerFactory instance.")] public static ILoggerFactory AddXunit(this ILoggerFactory loggerFactory, ITestOutputHelper output) { loggerFactory.AddProvider(new XunitLoggerProvider(output)); return loggerFactory; } - [Obsolete("This method is obsolete and will be removed in a future version. The recommended alternative is to call the Microsoft.Extensions.Logging.AddEventSourceLogger() extension method on the Microsoft.Extensions.Logging.LoggerFactory instance.")] public static ILoggerFactory AddXunit(this ILoggerFactory loggerFactory, ITestOutputHelper output, LogLevel minLevel) { loggerFactory.AddProvider(new XunitLoggerProvider(output, minLevel)); diff --git a/src/Microsoft.Extensions.Logging.TraceSource/TraceSourceFactoryExtensions.cs b/src/Microsoft.Extensions.Logging.TraceSource/TraceSourceFactoryExtensions.cs index 1b4e772a..db8532bc 100644 --- a/src/Microsoft.Extensions.Logging.TraceSource/TraceSourceFactoryExtensions.cs +++ b/src/Microsoft.Extensions.Logging.TraceSource/TraceSourceFactoryExtensions.cs @@ -117,13 +117,9 @@ public static ILoggerBuilder AddTraceSource( } /// - /// - /// This method is obsolete and will be removed in a future version. The recommended alternative is to call the Microsoft.Extensions.Logging.AddTraceSource() extension method on the Microsoft.Extensions.Logging.LoggerFactory instance. - /// /// /// The to use. /// The name of the to use. - [Obsolete("This method is obsolete and will be removed in a future version. The recommended alternative is to call the Microsoft.Extensions.Logging.AddTraceSource() extension method on the Microsoft.Extensions.Logging.LoggerFactory instance.")] public static ILoggerFactory AddTraceSource( this ILoggerFactory factory, string switchName) @@ -141,15 +137,9 @@ public static ILoggerFactory AddTraceSource( return factory.AddTraceSource(new SourceSwitch(switchName)); } - /// - /// - /// This method is obsolete and will be removed in a future version. The recommended alternative is to call the Microsoft.Extensions.Logging.AddTraceSource() extension method on the Microsoft.Extensions.Logging.LoggerFactory instance. - /// - /// /// The to use. /// The name of the to use. /// The to use. - [Obsolete("This method is obsolete and will be removed in a future version. The recommended alternative is to call the Microsoft.Extensions.Logging.AddTraceSource() extension method on the Microsoft.Extensions.Logging.LoggerFactory instance.")] public static ILoggerFactory AddTraceSource( this ILoggerFactory factory, string switchName, @@ -173,14 +163,8 @@ public static ILoggerFactory AddTraceSource( return factory.AddTraceSource(new SourceSwitch(switchName), listener); } - /// - /// - /// This method is obsolete and will be removed in a future version. The recommended alternative is to call the Microsoft.Extensions.Logging.AddTraceSource() extension method on the Microsoft.Extensions.Logging.LoggerFactory instance. - /// - /// /// The to use. /// The to use. - [Obsolete("This method is obsolete and will be removed in a future version. The recommended alternative is to call the Microsoft.Extensions.Logging.AddTraceSource() extension method on the Microsoft.Extensions.Logging.LoggerFactory instance.")] public static ILoggerFactory AddTraceSource( this ILoggerFactory factory, SourceSwitch sourceSwitch) @@ -200,15 +184,9 @@ public static ILoggerFactory AddTraceSource( return factory; } - /// - /// - /// This method is obsolete and will be removed in a future version. The recommended alternative is to call the Microsoft.Extensions.Logging.AddTraceSource() extension method on the Microsoft.Extensions.Logging.LoggerFactory instance. - /// - /// /// The to use. /// The to use. /// The to use. - [Obsolete("This method is obsolete and will be removed in a future version. The recommended alternative is to call the Microsoft.Extensions.Logging.AddTraceSource() extension method on the Microsoft.Extensions.Logging.LoggerFactory instance.")] public static ILoggerFactory AddTraceSource( this ILoggerFactory factory, SourceSwitch sourceSwitch, diff --git a/src/Microsoft.Extensions.Logging/LoggerFactory.cs b/src/Microsoft.Extensions.Logging/LoggerFactory.cs index 28c4c211..e40a93d4 100644 --- a/src/Microsoft.Extensions.Logging/LoggerFactory.cs +++ b/src/Microsoft.Extensions.Logging/LoggerFactory.cs @@ -20,7 +20,11 @@ public class LoggerFactory : ILoggerFactory private IDisposable _changeTokenRegistration; private LoggerFilterOptions _filterOptions; - public LoggerFactory() : this(Enumerable.Empty(), new LoggerFilterOptions()) + public LoggerFactory() : this(Enumerable.Empty()) + { + } + + public LoggerFactory(IEnumerable providers) : this(providers, new StaticFilterOptionsMonitor(new LoggerFilterOptions())) { } @@ -75,7 +79,7 @@ public ILogger CreateLogger(string categoryName) } } - void ILoggerFactory.AddProvider(ILoggerProvider provider) + public void AddProvider(ILoggerProvider provider) { if (CheckDisposed()) { diff --git a/test/Microsoft.Extensions.Logging.Test/ConsoleLoggerTest.cs b/test/Microsoft.Extensions.Logging.Test/ConsoleLoggerTest.cs index 406bfa33..ae2875c3 100644 --- a/test/Microsoft.Extensions.Logging.Test/ConsoleLoggerTest.cs +++ b/test/Microsoft.Extensions.Logging.Test/ConsoleLoggerTest.cs @@ -648,9 +648,7 @@ public void ConsoleLogger_ReloadSettings_CanChangeLogLevel() }; var loggerFactory = new LoggerFactory(); -#pragma warning disable CS0618 // Type or member is obsolete loggerFactory.AddConsole(settings); -#pragma warning restore CS0618 // Type or member is obsolete var logger = loggerFactory.CreateLogger("Test"); Assert.False(logger.IsEnabled(LogLevel.Trace)); @@ -680,10 +678,8 @@ public void ConsoleLogger_ReloadSettings_CanReloadMultipleTimes() } }; - var loggerFactory = new LoggerFactory(); -#pragma warning disable CS0618 // Type or member is obsolete - loggerFactory.AddConsole(settings); -#pragma warning restore CS0618 // Type or member is obsolete + var loggerFactory = new LoggerFactory() + .AddConsole(settings); var logger = loggerFactory.CreateLogger("Test"); Assert.False(logger.IsEnabled(LogLevel.Trace)); @@ -715,10 +711,8 @@ public void ConsoleLogger_ReloadSettings_CanRecoverAfterFailedReload() } }; - var loggerFactory = new LoggerFactory(); -#pragma warning disable CS0618 // Type or member is obsolete - loggerFactory.AddConsole(settings); -#pragma warning restore CS0618 // Type or member is obsolete + var loggerFactory = new LoggerFactory() + .AddConsole(settings); var logger = loggerFactory.CreateLogger("Test"); diff --git a/test/Microsoft.Extensions.Logging.Test/LoggerFactoryTest.cs b/test/Microsoft.Extensions.Logging.Test/LoggerFactoryTest.cs index 0352cd2f..1e5520d9 100644 --- a/test/Microsoft.Extensions.Logging.Test/LoggerFactoryTest.cs +++ b/test/Microsoft.Extensions.Logging.Test/LoggerFactoryTest.cs @@ -14,9 +14,8 @@ public void AddProvider_ThrowsAfterDisposed() { var factory = new LoggerFactory(); factory.Dispose(); -#pragma warning disable CS0618 // Type or member is obsolete + Assert.Throws(() => ((ILoggerFactory) factory).AddProvider(CreateProvider())); -#pragma warning restore CS0618 // Type or member is obsolete } [Fact] @@ -49,10 +48,8 @@ public void Dispose_ProvidersAreDisposed() var disposableProvider1 = CreateProvider(); var disposableProvider2 = CreateProvider(); -#pragma warning disable CS0618 // Type or member is obsolete - ((ILoggerFactory) factory).AddProvider(disposableProvider1); - ((ILoggerFactory) factory).AddProvider(disposableProvider2); -#pragma warning restore CS0618 // Type or member is obsolete + factory.AddProvider(disposableProvider1); + factory.AddProvider(disposableProvider2); // Act factory.Dispose(); @@ -81,9 +78,8 @@ public void Dispose_ThrowException_SwallowsException() throwingProvider.As() .Setup(p => p.Dispose()) .Throws(); -#pragma warning disable CS0618 // Type or member is obsolete - ((ILoggerFactory) factory).AddProvider(throwingProvider.Object); -#pragma warning restore CS0618 // Type or member is obsolete + + factory.AddProvider(throwingProvider.Object); // Act factory.Dispose(); diff --git a/test/Microsoft.Extensions.Logging.Test/LoggerTest.cs b/test/Microsoft.Extensions.Logging.Test/LoggerTest.cs index 6907a114..c9b3d878 100644 --- a/test/Microsoft.Extensions.Logging.Test/LoggerTest.cs +++ b/test/Microsoft.Extensions.Logging.Test/LoggerTest.cs @@ -114,9 +114,7 @@ public void LoggerCanGetProviderAfterItIsCreated() var loggerFactory = new LoggerFactory(); var logger = loggerFactory.CreateLogger("Test"); -#pragma warning disable CS0618 // Type or member is obsolete - ((ILoggerFactory)loggerFactory).AddProvider(new CustomLoggerProvider("provider1", ThrowExceptionAt.None, store)); -#pragma warning disable CS0618 // Type or member is obsolete + loggerFactory.AddProvider(new CustomLoggerProvider("provider1", ThrowExceptionAt.None, store)); // Act logger.LogInformation("Hello"); From d699b31078be5ed8987cb898274c20810f524f33 Mon Sep 17 00:00:00 2001 From: Pavel Krymets Date: Tue, 30 May 2017 08:50:38 -0700 Subject: [PATCH 13/20] Minimum and use last filter --- ...nfigurationLoggerFilterConfigureOptions.cs | 16 +------ .../LoggerBuilderExtensions.cs | 9 +--- .../LoggerRuleSelector.cs | 44 +++++-------------- .../EventSourceLoggerTest.cs | 4 +- .../LoggerFilterTest.cs | 8 ++-- .../XunitLoggerProviderTest.cs | 2 +- 6 files changed, 20 insertions(+), 63 deletions(-) diff --git a/src/Microsoft.Extensions.Logging/ConfigurationLoggerFilterConfigureOptions.cs b/src/Microsoft.Extensions.Logging/ConfigurationLoggerFilterConfigureOptions.cs index 82815c3f..75fe823f 100644 --- a/src/Microsoft.Extensions.Logging/ConfigurationLoggerFilterConfigureOptions.cs +++ b/src/Microsoft.Extensions.Logging/ConfigurationLoggerFilterConfigureOptions.cs @@ -10,12 +10,10 @@ namespace Microsoft.Extensions.Logging public class ConfigurationLoggerFilterConfigureOptions : IConfigureOptions { private readonly IConfiguration _configuration; - private readonly bool _replace; - public ConfigurationLoggerFilterConfigureOptions(IConfiguration configuration, bool replace) + public ConfigurationLoggerFilterConfigureOptions(IConfiguration configuration) { _configuration = configuration; - _replace = replace; } public void Configure(LoggerFilterOptions options) @@ -58,18 +56,6 @@ private void LoadRules(LoggerFilterOptions options, IConfigurationSection config { var newRule = new LoggerFilterRule(logger, section.Key, level, null); options.Rules.Add(newRule); - - // If we are in replace mode we need to find other matching rules and remove them - if (_replace) - { - foreach (var rule in LoggerRuleSelector.GetMatchingRules(options, logger, section.Key)) - { - if (rule != newRule) - { - options.Rules.Remove(rule); - } - } - } } } } diff --git a/src/Microsoft.Extensions.Logging/LoggerBuilderExtensions.cs b/src/Microsoft.Extensions.Logging/LoggerBuilderExtensions.cs index d756a079..4d44a23a 100644 --- a/src/Microsoft.Extensions.Logging/LoggerBuilderExtensions.cs +++ b/src/Microsoft.Extensions.Logging/LoggerBuilderExtensions.cs @@ -15,18 +15,13 @@ public static class LoggerBuilderExtensions { public static ILoggerBuilder AddConfiguration(this ILoggerBuilder builder, IConfiguration configuration) { - return builder.AddConfiguration(configuration, false); - } - - public static ILoggerBuilder AddConfiguration(this ILoggerBuilder builder, IConfiguration configuration, bool replace) - { - builder.Services.TryAddEnumerable(ServiceDescriptor.Singleton>(new ConfigurationLoggerFilterConfigureOptions(configuration, replace))); + builder.Services.TryAddEnumerable(ServiceDescriptor.Singleton>(new ConfigurationLoggerFilterConfigureOptions(configuration))); builder.Services.TryAddEnumerable(ServiceDescriptor.Singleton>(new ConfigurationChangeTokenSource(configuration))); return builder; } - public static ILoggerBuilder SetMinimalLevel(this ILoggerBuilder builder, LogLevel level) + public static ILoggerBuilder SetMinimumLevel(this ILoggerBuilder builder, LogLevel level) { builder.Services.Add(ServiceDescriptor.Singleton>( new DefaultLoggerLevelConfigureOptions(level))); diff --git a/src/Microsoft.Extensions.Logging/LoggerRuleSelector.cs b/src/Microsoft.Extensions.Logging/LoggerRuleSelector.cs index 6b8324c0..8520ce17 100644 --- a/src/Microsoft.Extensions.Logging/LoggerRuleSelector.cs +++ b/src/Microsoft.Extensions.Logging/LoggerRuleSelector.cs @@ -12,51 +12,27 @@ public class LoggerRuleSelector public void Select(LoggerFilterOptions options, string logger, string category, out LogLevel? minLevel, out Func filter) { filter = null; + minLevel = options.MinLevel; var categorySpecificRules = GetMatchingRules(options, logger, category); - if (categorySpecificRules?.Any() == true) + var loggerFilterRule = categorySpecificRules?.LastOrDefault(); + if (loggerFilterRule != null) { - if (categorySpecificRules.Count == 1) - { - var loggerFilterRule = categorySpecificRules.Single(); - filter = loggerFilterRule.Filter; - minLevel = loggerFilterRule.LogLevel; - } - else - { - // Combine rules, for min level we take maximum of all rules - // for filter delegated we take firs if there is only one or apply AND operator to all - minLevel = categorySpecificRules.Max(rule => rule.LogLevel); - filter = (type, c, level) => - { - foreach (var loggerFilterRule in categorySpecificRules) - { - if (!loggerFilterRule.Filter?.Invoke(type, c, level) == true) - { - return false; - } - } - - return true; - }; - } - } - else - { - // If there are no rules fallback to global min level - minLevel = options.MinLevel; + filter = loggerFilterRule.Filter; + minLevel = loggerFilterRule.LogLevel; } } - public static List GetMatchingRules(LoggerFilterOptions options, string logger, string category) + private static List GetMatchingRules(LoggerFilterOptions options, string logger, string category) { -// Filter rule selection: + // TODO: This can be rewritten to a single loop. + // Filter rule selection: // 1. Select rules for current logger type, if there is none, select ones without logger type specified // 2. Select rules with longest matching categories // 3. If there no category // 3. If there is only one rule use it's level and filter - // 4. If there are multiple rules combine them using AND operator + // 4. If there are multiple rules use last // 5. If there are no applicable rules use global minimal level var loggerSpecificRules = options.Rules.Where(rule => rule.LoggerType == logger).ToList(); @@ -72,7 +48,7 @@ public static List GetMatchingRules(LoggerFilterOptions option .Where(rule => !string.IsNullOrEmpty(rule.CategoryName) && category.StartsWith(rule.CategoryName, StringComparison.OrdinalIgnoreCase)) .GroupBy(rule => rule.CategoryName.Length) - .OrderByDescending(group => @group.Key) + .OrderByDescending(group => group.Key) .FirstOrDefault() ?.ToList(); diff --git a/test/Microsoft.Extensions.Logging.EventSource.Test/EventSourceLoggerTest.cs b/test/Microsoft.Extensions.Logging.EventSource.Test/EventSourceLoggerTest.cs index e0069873..0eb14eb7 100644 --- a/test/Microsoft.Extensions.Logging.EventSource.Test/EventSourceLoggerTest.cs +++ b/test/Microsoft.Extensions.Logging.EventSource.Test/EventSourceLoggerTest.cs @@ -55,7 +55,7 @@ public void Logs_Nothing_IfNotEnabled() { // No call to factory.AddEventSourceLogger(); var factory = TestLoggerBuilder.Create(builder => builder - .SetMinimalLevel(LogLevel.Trace)); + .SetMinimumLevel(LogLevel.Trace)); var listenerSettings = new TestEventListener.ListenerSettings(); listenerSettings.Keywords = EventKeywords.None; @@ -314,7 +314,7 @@ private static ILoggerFactory CreateLoggerFactory() { return TestLoggerBuilder.Create(builder => builder .AddEventSourceLogger() - .SetMinimalLevel(LogLevel.Trace)); + .SetMinimumLevel(LogLevel.Trace)); } private void LogStuff(ILoggerFactory factory) diff --git a/test/Microsoft.Extensions.Logging.Test/LoggerFilterTest.cs b/test/Microsoft.Extensions.Logging.Test/LoggerFilterTest.cs index 79ef5819..11623f0c 100644 --- a/test/Microsoft.Extensions.Logging.Test/LoggerFilterTest.cs +++ b/test/Microsoft.Extensions.Logging.Test/LoggerFilterTest.cs @@ -273,13 +273,13 @@ public void AddFilterForNonMatchingProviderDoesNotFilter() } [Fact] - public void AddFilterIsAdditive() + public void AddFilterLastWins() { var provider = new TestLoggerProvider(new TestSink(), isEnabled: true); var factory = TestLoggerBuilder.Create(builder => builder .AddProvider(provider) .AddFilter((name, cat, level) => level >= LogLevel.Warning) - .AddFilter((name, cat, level) => string.Equals(cat, "NotTest"))); + .AddFilter((name, cat, level) => string.Equals(cat, "NotTest"))); var logger = factory.CreateLogger("Test"); @@ -292,11 +292,11 @@ public void AddFilterIsAdditive() logger.LogInformation("Message"); - Assert.Equal(0, writes.Count); + Assert.Equal(1, writes.Count); logger.LogError("Message"); - Assert.Equal(1, writes.Count); + Assert.Equal(2, writes.Count); } [Fact] diff --git a/test/Microsoft.Extensions.Logging.Testing.Tests/XunitLoggerProviderTest.cs b/test/Microsoft.Extensions.Logging.Testing.Tests/XunitLoggerProviderTest.cs index 12669e3b..4eb0de21 100644 --- a/test/Microsoft.Extensions.Logging.Testing.Tests/XunitLoggerProviderTest.cs +++ b/test/Microsoft.Extensions.Logging.Testing.Tests/XunitLoggerProviderTest.cs @@ -16,7 +16,7 @@ public void LoggerProviderWritesToTestOutputHelper() var testTestOutputHelper = new TestTestOutputHelper(); var loggerFactory = TestLoggerBuilder.Create(builder => builder - .SetMinimalLevel(LogLevel.Trace) + .SetMinimumLevel(LogLevel.Trace) .AddXunit(testTestOutputHelper)); var logger = loggerFactory.CreateLogger("TestCategory"); From 9f3d9f52215037e47751c7a7691083ee8e94f5d8 Mon Sep 17 00:00:00 2001 From: Pavel Krymets Date: Tue, 30 May 2017 13:25:25 -0700 Subject: [PATCH 14/20] Some PR feedback --- ...AzureAppServicesLoggerFactoryExtensions.cs | 1 - .../ConsoleLoggerProvider.cs | 1 - .../FilterLoggerBuilderExtensions.cs | 31 +++++++++++-------- src/Microsoft.Extensions.Logging/Logger.cs | 2 +- .../LoggerBuilderExtensions.cs | 4 +-- .../LoggerRuleSelector.cs | 4 +-- 6 files changed, 23 insertions(+), 20 deletions(-) diff --git a/src/Microsoft.Extensions.Logging.AzureAppServices/AzureAppServicesLoggerFactoryExtensions.cs b/src/Microsoft.Extensions.Logging.AzureAppServices/AzureAppServicesLoggerFactoryExtensions.cs index fd28c59b..8b23c123 100644 --- a/src/Microsoft.Extensions.Logging.AzureAppServices/AzureAppServicesLoggerFactoryExtensions.cs +++ b/src/Microsoft.Extensions.Logging.AzureAppServices/AzureAppServicesLoggerFactoryExtensions.cs @@ -32,7 +32,6 @@ public static ILoggerBuilder AddAzureWebAppDiagnostics(this ILoggerBuilder build if (WebAppContext.Default.IsRunningInAzureWebApp) { // Only add the provider if we're in Azure WebApp. That cannot change once the apps started - builder.Services.AddSingleton(WebAppContext.Default); builder.Services.AddSingleton(new AzureAppServicesDiagnosticsLoggerProvider(WebAppContext.Default, settings ?? new AzureAppServicesDiagnosticsSettings())); } return builder; diff --git a/src/Microsoft.Extensions.Logging.Console/ConsoleLoggerProvider.cs b/src/Microsoft.Extensions.Logging.Console/ConsoleLoggerProvider.cs index 948a3e7f..78da38c9 100644 --- a/src/Microsoft.Extensions.Logging.Console/ConsoleLoggerProvider.cs +++ b/src/Microsoft.Extensions.Logging.Console/ConsoleLoggerProvider.cs @@ -48,7 +48,6 @@ private void ReloadLoggerOptions(ConsoleLoggerOptions options) var includeScopes = options.IncludeScopes; foreach (var logger in _loggers.Values) { - logger.Filter = GetFilter(logger.Name, _settings); logger.IncludeScopes = includeScopes; } } diff --git a/src/Microsoft.Extensions.Logging/FilterLoggerBuilderExtensions.cs b/src/Microsoft.Extensions.Logging/FilterLoggerBuilderExtensions.cs index 84fc5aac..41233182 100644 --- a/src/Microsoft.Extensions.Logging/FilterLoggerBuilderExtensions.cs +++ b/src/Microsoft.Extensions.Logging/FilterLoggerBuilderExtensions.cs @@ -2,9 +2,9 @@ // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System; -using Microsoft.Extensions.Logging; +using Microsoft.Extensions.DependencyInjection; -namespace Microsoft.Extensions.DependencyInjection +namespace Microsoft.Extensions.Logging { /// /// Extension methods for setting up logging services in an . @@ -13,51 +13,56 @@ public static class FilterLoggerBuilderExtensions { public static ILoggerBuilder AddFilter(this ILoggerBuilder builder, Func levelFilter) where T : ILoggerProvider { - return AddRule(builder, new LoggerFilterRule(typeof(T).FullName, null, null, (type, name, level) => levelFilter(level))); + return AddRule(builder, type: typeof(T).FullName, filter: (type, name, level) => levelFilter(level)); } public static ILoggerBuilder AddFilter(this ILoggerBuilder builder, string category, LogLevel level) { - return AddRule(builder, new LoggerFilterRule(null, category, level, null)); + return AddRule(builder, category: category, level: level); } public static ILoggerBuilder AddFilter(this ILoggerBuilder builder, string category, LogLevel level) where T: ILoggerProvider { - return AddRule(builder, new LoggerFilterRule(typeof(T).FullName, category, level, null)); + return AddRule(builder, type: typeof(T).FullName, category: category, level: level); } public static ILoggerBuilder AddFilter(this ILoggerBuilder builder, Func filter) { - return AddRule(builder, new LoggerFilterRule(null, null, null, filter)); + return AddRule(builder, filter: filter); } + public static ILoggerBuilder AddFilter(this ILoggerBuilder builder, Func filter) where T : ILoggerProvider { - return AddRule(builder, new LoggerFilterRule(typeof(T).FullName, null, null, filter)); + return AddRule(builder, type: typeof(T).FullName, filter: filter); } public static ILoggerBuilder AddFilter(this ILoggerBuilder builder, Func categoryLevelFilter) { - return AddRule(builder, new LoggerFilterRule(null, null, null, (type, name, level) => categoryLevelFilter(name, level))); + return AddRule(builder, filter: (type, name, level) => categoryLevelFilter(name, level)); } public static ILoggerBuilder AddFilter(this ILoggerBuilder builder, Func categoryLevelFilter) where T : ILoggerProvider { - return AddRule(builder, new LoggerFilterRule(typeof(T).FullName, null, null, (type, name, level) => categoryLevelFilter(name, level))); + return AddRule(builder, type: typeof(T).FullName, filter: (type, name, level) => categoryLevelFilter(name, level)); } public static ILoggerBuilder AddFilter(this ILoggerBuilder builder, string category, Func levelFilter) { - return AddRule(builder, new LoggerFilterRule(null, category, null, (type, name, level) => levelFilter(level))); + return AddRule(builder, category: category, filter: (type, name, level) => levelFilter(level)); } public static ILoggerBuilder AddFilter(this ILoggerBuilder builder, string category, Func levelFilter) where T : ILoggerProvider { - return AddRule(builder, new LoggerFilterRule(typeof(T).FullName, category, null, (type, name, level) => levelFilter(level))); + return AddRule(builder, type: typeof(T).FullName, category: category, filter: (type, name, level) => levelFilter(level)); } - private static ILoggerBuilder AddRule(ILoggerBuilder builder, LoggerFilterRule loggerFilterRule) + private static ILoggerBuilder AddRule(ILoggerBuilder builder, + string type = null, + string category = null, + LogLevel? level = null, + Func filter = null) { - builder.Services.Configure(options => options.Rules.Add(loggerFilterRule)); + builder.Services.Configure(options => options.Rules.Add(new LoggerFilterRule(type, category, level, filter))); return builder; } } diff --git a/src/Microsoft.Extensions.Logging/Logger.cs b/src/Microsoft.Extensions.Logging/Logger.cs index 293d3659..4c593eea 100644 --- a/src/Microsoft.Extensions.Logging/Logger.cs +++ b/src/Microsoft.Extensions.Logging/Logger.cs @@ -62,7 +62,7 @@ public bool IsEnabled(LogLevel logLevel) { if (!loggerInfo.IsEnabled(logLevel)) { - return false; + continue; } try diff --git a/src/Microsoft.Extensions.Logging/LoggerBuilderExtensions.cs b/src/Microsoft.Extensions.Logging/LoggerBuilderExtensions.cs index 4d44a23a..5a4c157d 100644 --- a/src/Microsoft.Extensions.Logging/LoggerBuilderExtensions.cs +++ b/src/Microsoft.Extensions.Logging/LoggerBuilderExtensions.cs @@ -2,11 +2,11 @@ // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection.Extensions; -using Microsoft.Extensions.Logging; using Microsoft.Extensions.Options; -namespace Microsoft.Extensions.DependencyInjection +namespace Microsoft.Extensions.Logging { /// /// Extension methods for setting up logging services in an . diff --git a/src/Microsoft.Extensions.Logging/LoggerRuleSelector.cs b/src/Microsoft.Extensions.Logging/LoggerRuleSelector.cs index 8520ce17..cae24d47 100644 --- a/src/Microsoft.Extensions.Logging/LoggerRuleSelector.cs +++ b/src/Microsoft.Extensions.Logging/LoggerRuleSelector.cs @@ -7,7 +7,7 @@ namespace Microsoft.Extensions.Logging { - public class LoggerRuleSelector + internal class LoggerRuleSelector { public void Select(LoggerFilterOptions options, string logger, string category, out LogLevel? minLevel, out Func filter) { @@ -30,7 +30,7 @@ private static List GetMatchingRules(LoggerFilterOptions optio // Filter rule selection: // 1. Select rules for current logger type, if there is none, select ones without logger type specified // 2. Select rules with longest matching categories - // 3. If there no category + // 3. If there nothing matched by category take all rules without category // 3. If there is only one rule use it's level and filter // 4. If there are multiple rules use last // 5. If there are no applicable rules use global minimal level From f8bf9ce17fbf6df27ecc934fedfd575835c28c26 Mon Sep 17 00:00:00 2001 From: Pavel Krymets Date: Tue, 30 May 2017 16:19:22 -0700 Subject: [PATCH 15/20] Internal, comments, tests. --- ...nfigurationLoggerFilterConfigureOptions.cs | 2 +- .../DefaultLoggerLevelConfigureOptions.cs | 2 +- .../ILoggerBuilder.cs | 3 + .../LoggerFactory.cs | 2 +- .../LoggerFilterOptions.cs | 8 +- .../LoggerFilterRule.cs | 15 +++ .../LoggerFilterTest.cs | 119 ++++++++++++++++++ .../Microsoft.Extensions.Logging.Test.csproj | 2 + .../TestLoggerProvider.cs | 7 ++ 9 files changed, 156 insertions(+), 4 deletions(-) diff --git a/src/Microsoft.Extensions.Logging/ConfigurationLoggerFilterConfigureOptions.cs b/src/Microsoft.Extensions.Logging/ConfigurationLoggerFilterConfigureOptions.cs index 75fe823f..2e78cb6e 100644 --- a/src/Microsoft.Extensions.Logging/ConfigurationLoggerFilterConfigureOptions.cs +++ b/src/Microsoft.Extensions.Logging/ConfigurationLoggerFilterConfigureOptions.cs @@ -7,7 +7,7 @@ namespace Microsoft.Extensions.Logging { - public class ConfigurationLoggerFilterConfigureOptions : IConfigureOptions + internal class ConfigurationLoggerFilterConfigureOptions : IConfigureOptions { private readonly IConfiguration _configuration; diff --git a/src/Microsoft.Extensions.Logging/DefaultLoggerLevelConfigureOptions.cs b/src/Microsoft.Extensions.Logging/DefaultLoggerLevelConfigureOptions.cs index 20f40701..dc6f4214 100644 --- a/src/Microsoft.Extensions.Logging/DefaultLoggerLevelConfigureOptions.cs +++ b/src/Microsoft.Extensions.Logging/DefaultLoggerLevelConfigureOptions.cs @@ -5,7 +5,7 @@ namespace Microsoft.Extensions.Logging { - public class DefaultLoggerLevelConfigureOptions : ConfigureOptions + internal class DefaultLoggerLevelConfigureOptions : ConfigureOptions { public DefaultLoggerLevelConfigureOptions(LogLevel level) : base(options => options.MinLevel = level) { diff --git a/src/Microsoft.Extensions.Logging/ILoggerBuilder.cs b/src/Microsoft.Extensions.Logging/ILoggerBuilder.cs index f3e2f697..9c5d8bfb 100644 --- a/src/Microsoft.Extensions.Logging/ILoggerBuilder.cs +++ b/src/Microsoft.Extensions.Logging/ILoggerBuilder.cs @@ -5,6 +5,9 @@ namespace Microsoft.Extensions.Logging { + /// + /// An interface for configuring logging providers. + /// public interface ILoggerBuilder { /// diff --git a/src/Microsoft.Extensions.Logging/LoggerFactory.cs b/src/Microsoft.Extensions.Logging/LoggerFactory.cs index e40a93d4..2eb71495 100644 --- a/src/Microsoft.Extensions.Logging/LoggerFactory.cs +++ b/src/Microsoft.Extensions.Logging/LoggerFactory.cs @@ -34,7 +34,7 @@ public LoggerFactory() : this(Enumerable.Empty()) public LoggerFactory(IEnumerable providers, IOptionsMonitor filterOption) { - _providerRegistrations = providers.Select(provider => new ProviderRegistration { Provider = provider}).ToList(); + _providerRegistrations = providers.Select(provider => new ProviderRegistration { Provider = provider }).ToList(); _changeTokenRegistration = filterOption.OnChange(RefreshFilters); RefreshFilters(filterOption.CurrentValue); } diff --git a/src/Microsoft.Extensions.Logging/LoggerFilterOptions.cs b/src/Microsoft.Extensions.Logging/LoggerFilterOptions.cs index 9030ccfa..7c32e1fd 100644 --- a/src/Microsoft.Extensions.Logging/LoggerFilterOptions.cs +++ b/src/Microsoft.Extensions.Logging/LoggerFilterOptions.cs @@ -7,8 +7,14 @@ namespace Microsoft.Extensions.Logging { public class LoggerFilterOptions { + /// + /// Gets or sets the minimum level of log messages if none of the rules match. + /// public LogLevel MinLevel { get; set; } - public ICollection Rules { get; } = new List(); + /// + /// Gets the collection of used for filtering log messages. + /// + public IList Rules { get; } = new List(); } } \ No newline at end of file diff --git a/src/Microsoft.Extensions.Logging/LoggerFilterRule.cs b/src/Microsoft.Extensions.Logging/LoggerFilterRule.cs index e872ac88..83267b54 100644 --- a/src/Microsoft.Extensions.Logging/LoggerFilterRule.cs +++ b/src/Microsoft.Extensions.Logging/LoggerFilterRule.cs @@ -5,6 +5,9 @@ namespace Microsoft.Extensions.Logging { + /// + /// Defines a rule used to filter log messages + /// public class LoggerFilterRule { public LoggerFilterRule(string loggerType, string categoryName, LogLevel? logLevel, Func filter) @@ -15,12 +18,24 @@ public LoggerFilterRule(string loggerType, string categoryName, LogLevel? logLev Filter = filter; } + /// + /// Gets the logger provider type this rule applies to. + /// public string LoggerType { get; } + /// + /// Gets the logger category this rule applies to. + /// public string CategoryName { get; } + /// + /// Gets the minimum of messages. + /// public LogLevel? LogLevel { get; } + /// + /// Gets the filter delegate that would be applied to messages that passed the . + /// public Func Filter { get; } } } \ No newline at end of file diff --git a/test/Microsoft.Extensions.Logging.Test/LoggerFilterTest.cs b/test/Microsoft.Extensions.Logging.Test/LoggerFilterTest.cs index 11623f0c..8b6b0c60 100644 --- a/test/Microsoft.Extensions.Logging.Test/LoggerFilterTest.cs +++ b/test/Microsoft.Extensions.Logging.Test/LoggerFilterTest.cs @@ -398,6 +398,125 @@ public void AddFilterWithProviderNameAndCategoryFilterFuncFiltersCorrectly() Assert.Equal(1, writes.Count); } + [Theory] + [MemberData(nameof(FilterTestData))] + public void FilterTest(LoggerFilterOptions options, (string category, LogLevel level, bool expectInProvider1, bool expectInProvider2) message) + { + var testSink1 = new TestSink(); + var testSink2 = new TestSink(); + + var loggerFactory = new LoggerFactory(new[] + { + new TestLoggerProvider(testSink1, true), + new TestLoggerProvider2(testSink2) + }, options); + + var logger = loggerFactory.CreateLogger(message.category); + Assert.Equal(message.expectInProvider1 || message.expectInProvider2, logger.IsEnabled(message.Item2)); + logger.Log(message.level, 0, "hello", null, (s, exception) => s); + + Assert.Equal(message.expectInProvider1 ? 1 : 0, testSink1.Writes.Count); + Assert.Equal(message.expectInProvider2 ? 1 : 0, testSink2.Writes.Count); + } + + + public static TheoryData FilterTestData = + new TheoryData() + { + { // Provider specific rule if preferred + new LoggerFilterOptions() + { + Rules = + { + new LoggerFilterRule(typeof(TestLoggerProvider).FullName, null, LogLevel.Information, null), + new LoggerFilterRule(null, null, LogLevel.Critical, null) + } + }, + ("Category", LogLevel.Information, true, false) + }, + { // Category specific rule if preferred + new LoggerFilterOptions() + { + Rules = + { + new LoggerFilterRule(null, "Category", LogLevel.Information, null), + new LoggerFilterRule(null, null, LogLevel.Critical, null) + } + }, + ("Category", LogLevel.Information, true, true) + }, + { // Longest category specific rule if preferred + new LoggerFilterOptions() + { + Rules = + { + new LoggerFilterRule(null, "Category.Sub", LogLevel.Trace, null), + new LoggerFilterRule(null, "Category", LogLevel.Information, null), + new LoggerFilterRule(null, null, LogLevel.Critical, null) + } + }, + ("Category.Sub", LogLevel.Trace, true, true) + }, + { // Provider is selected first, then category + new LoggerFilterOptions() + { + Rules = + { + new LoggerFilterRule(null, "Category.Sub", LogLevel.Trace, null), + new LoggerFilterRule(typeof(TestLoggerProvider).FullName, "Category", LogLevel.Information, null), + new LoggerFilterRule(null, null, LogLevel.Critical, null) + } + }, + ("Category.Sub", LogLevel.Trace, false, true) + }, + { // Last most specific is selected + new LoggerFilterOptions() + { + Rules = + { + new LoggerFilterRule(null, "Category.Sub", LogLevel.Trace, null), + new LoggerFilterRule(typeof(TestLoggerProvider).FullName, "Category", LogLevel.Information, null), + new LoggerFilterRule(typeof(TestLoggerProvider).FullName, "Category", LogLevel.Trace, null), + new LoggerFilterRule(null, null, LogLevel.Critical, null) + } + }, + ("Category.Sub", LogLevel.Trace, true, true) + }, + { // Filter is used if matches level + new LoggerFilterOptions() + { + Rules = + { + new LoggerFilterRule(null, null, LogLevel.Critical, (logger, category, level) => true) + } + }, + ("Category.Sub", LogLevel.Error, false, false) + }, + { // Last filter is used is used + new LoggerFilterOptions() + { + Rules = + { + new LoggerFilterRule(null, null, LogLevel.Critical, (logger, category, level) => false), + new LoggerFilterRule(null, null, LogLevel.Critical, (logger, category, level) => true) + } + }, + ("Category.Sub", LogLevel.Critical, true, true) + }, + { // MinLevel is used when no match + new LoggerFilterOptions() + { + Rules = + { + new LoggerFilterRule(typeof(TestLoggerProvider).FullName, null, LogLevel.Trace, null), + }, + MinLevel = LogLevel.Debug + }, + ("Category.Sub", LogLevel.Trace, true, false) + } + }; + + internal ConfigurationRoot CreateConfiguration(Func getJson) { var provider = new TestConfiguration(new JsonConfigurationSource { Optional = true }, getJson); diff --git a/test/Microsoft.Extensions.Logging.Test/Microsoft.Extensions.Logging.Test.csproj b/test/Microsoft.Extensions.Logging.Test/Microsoft.Extensions.Logging.Test.csproj index 1c43998c..7f7b7f56 100644 --- a/test/Microsoft.Extensions.Logging.Test/Microsoft.Extensions.Logging.Test.csproj +++ b/test/Microsoft.Extensions.Logging.Test/Microsoft.Extensions.Logging.Test.csproj @@ -26,6 +26,8 @@ + + diff --git a/test/Microsoft.Extensions.Logging.Test/TestLoggerProvider.cs b/test/Microsoft.Extensions.Logging.Test/TestLoggerProvider.cs index 543c9da7..a031670b 100644 --- a/test/Microsoft.Extensions.Logging.Test/TestLoggerProvider.cs +++ b/test/Microsoft.Extensions.Logging.Test/TestLoggerProvider.cs @@ -35,4 +35,11 @@ public void Dispose() DisposeCalled = true; } } + + public class TestLoggerProvider2 : TestLoggerProvider + { + public TestLoggerProvider2(TestSink testSink) : base(testSink, true) + { + } + } } From b5f8d22c97e9078e7b10ce6e52422ed6239eba03 Mon Sep 17 00:00:00 2001 From: Pavel Krymets Date: Tue, 30 May 2017 16:26:26 -0700 Subject: [PATCH 16/20] Shorten class name --- src/Microsoft.Extensions.Logging/LoggerBuilderExtensions.cs | 2 +- ...terConfigureOptions.cs => LoggerFilterConfigureOptions.cs} | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) rename src/Microsoft.Extensions.Logging/{ConfigurationLoggerFilterConfigureOptions.cs => LoggerFilterConfigureOptions.cs} (95%) diff --git a/src/Microsoft.Extensions.Logging/LoggerBuilderExtensions.cs b/src/Microsoft.Extensions.Logging/LoggerBuilderExtensions.cs index 5a4c157d..6792cc5c 100644 --- a/src/Microsoft.Extensions.Logging/LoggerBuilderExtensions.cs +++ b/src/Microsoft.Extensions.Logging/LoggerBuilderExtensions.cs @@ -15,7 +15,7 @@ public static class LoggerBuilderExtensions { public static ILoggerBuilder AddConfiguration(this ILoggerBuilder builder, IConfiguration configuration) { - builder.Services.TryAddEnumerable(ServiceDescriptor.Singleton>(new ConfigurationLoggerFilterConfigureOptions(configuration))); + builder.Services.TryAddEnumerable(ServiceDescriptor.Singleton>(new LoggerFilterConfigureOptions(configuration))); builder.Services.TryAddEnumerable(ServiceDescriptor.Singleton>(new ConfigurationChangeTokenSource(configuration))); return builder; diff --git a/src/Microsoft.Extensions.Logging/ConfigurationLoggerFilterConfigureOptions.cs b/src/Microsoft.Extensions.Logging/LoggerFilterConfigureOptions.cs similarity index 95% rename from src/Microsoft.Extensions.Logging/ConfigurationLoggerFilterConfigureOptions.cs rename to src/Microsoft.Extensions.Logging/LoggerFilterConfigureOptions.cs index 2e78cb6e..2e7a24cb 100644 --- a/src/Microsoft.Extensions.Logging/ConfigurationLoggerFilterConfigureOptions.cs +++ b/src/Microsoft.Extensions.Logging/LoggerFilterConfigureOptions.cs @@ -7,11 +7,11 @@ namespace Microsoft.Extensions.Logging { - internal class ConfigurationLoggerFilterConfigureOptions : IConfigureOptions + internal class LoggerFilterConfigureOptions : IConfigureOptions { private readonly IConfiguration _configuration; - public ConfigurationLoggerFilterConfigureOptions(IConfiguration configuration) + public LoggerFilterConfigureOptions(IConfiguration configuration) { _configuration = configuration; } From 027db2bec4690130f661b8e09f23b240e99017ad Mon Sep 17 00:00:00 2001 From: Pavel Krymets Date: Tue, 30 May 2017 16:55:31 -0700 Subject: [PATCH 17/20] ApiCheck --- build/common.props | 2 -- 1 file changed, 2 deletions(-) diff --git a/build/common.props b/build/common.props index ed9d38a9..2e376efe 100644 --- a/build/common.props +++ b/build/common.props @@ -10,8 +10,6 @@ true true $(VersionSuffix)-$(BuildNumber) - - false From f84a45a97a51fc5e129562d573ba6a529542698f Mon Sep 17 00:00:00 2001 From: Pavel Krymets Date: Wed, 31 May 2017 09:24:59 -0700 Subject: [PATCH 18/20] ILoggingBuilder --- ...AzureAppServicesLoggerFactoryExtensions.cs | 4 ++-- .../ConsoleLoggerFactoryExtensions.cs | 12 +++++----- .../DebugLoggerFactoryExtensions.cs | 2 +- .../EventLoggerFactoryExtensions.cs | 4 ++-- .../EventSourceLoggerFactoryExtensions.cs | 2 +- .../XunitLoggerFactoryExtensions.cs | 4 ++-- .../TraceSourceFactoryExtensions.cs | 22 +++++++++---------- ...s.cs => FilterLoggingBuilderExtensions.cs} | 22 +++++++++---------- .../{ILoggerBuilder.cs => ILoggingBuilder.cs} | 2 +- .../{LoggerBuilder.cs => LoggingBuilder.cs} | 4 ++-- ...ensions.cs => LoggingBuilderExtensions.cs} | 14 ++++++------ .../LoggingServiceCollectionExtensions.cs | 6 ++--- .../LoggerBuilderTestExtensions.cs | 2 +- 13 files changed, 50 insertions(+), 50 deletions(-) rename src/Microsoft.Extensions.Logging/{FilterLoggerBuilderExtensions.cs => FilterLoggingBuilderExtensions.cs} (57%) rename src/Microsoft.Extensions.Logging/{ILoggerBuilder.cs => ILoggingBuilder.cs} (93%) rename src/Microsoft.Extensions.Logging/{LoggerBuilder.cs => LoggingBuilder.cs} (77%) rename src/Microsoft.Extensions.Logging/{LoggerBuilderExtensions.cs => LoggingBuilderExtensions.cs} (53%) diff --git a/src/Microsoft.Extensions.Logging.AzureAppServices/AzureAppServicesLoggerFactoryExtensions.cs b/src/Microsoft.Extensions.Logging.AzureAppServices/AzureAppServicesLoggerFactoryExtensions.cs index 8b23c123..c52c4ddb 100644 --- a/src/Microsoft.Extensions.Logging.AzureAppServices/AzureAppServicesLoggerFactoryExtensions.cs +++ b/src/Microsoft.Extensions.Logging.AzureAppServices/AzureAppServicesLoggerFactoryExtensions.cs @@ -17,7 +17,7 @@ public static class AzureAppServicesLoggerFactoryExtensions /// Adds an Azure Web Apps diagnostics logger. /// /// The extension method argument - public static ILoggerBuilder AddAzureWebAppDiagnostics(this ILoggerBuilder builder) + public static ILoggingBuilder AddAzureWebAppDiagnostics(this ILoggingBuilder builder) { return AddAzureWebAppDiagnostics(builder, null); } @@ -27,7 +27,7 @@ public static ILoggerBuilder AddAzureWebAppDiagnostics(this ILoggerBuilder build /// /// The extension method argument /// The setting object to configure loggers. - public static ILoggerBuilder AddAzureWebAppDiagnostics(this ILoggerBuilder builder, AzureAppServicesDiagnosticsSettings settings) + public static ILoggingBuilder AddAzureWebAppDiagnostics(this ILoggingBuilder builder, AzureAppServicesDiagnosticsSettings settings) { if (WebAppContext.Default.IsRunningInAzureWebApp) { diff --git a/src/Microsoft.Extensions.Logging.Console/ConsoleLoggerFactoryExtensions.cs b/src/Microsoft.Extensions.Logging.Console/ConsoleLoggerFactoryExtensions.cs index 886aba12..6beabca4 100644 --- a/src/Microsoft.Extensions.Logging.Console/ConsoleLoggerFactoryExtensions.cs +++ b/src/Microsoft.Extensions.Logging.Console/ConsoleLoggerFactoryExtensions.cs @@ -13,8 +13,8 @@ public static class ConsoleLoggerExtensions /// /// Adds a console logger named 'Console' to the factory. /// - /// The to use. - public static ILoggerBuilder AddConsole(this ILoggerBuilder builder) + /// The to use. + public static ILoggingBuilder AddConsole(this ILoggingBuilder builder) { builder.Services.AddSingleton(); @@ -24,9 +24,9 @@ public static ILoggerBuilder AddConsole(this ILoggerBuilder builder) /// /// Adds a console logger named 'Console' to the factory. /// - /// The to use. + /// The to use. /// - public static ILoggerBuilder AddConsole(this ILoggerBuilder builder, Action configure) + public static ILoggingBuilder AddConsole(this ILoggingBuilder builder, Action configure) { if (configure == null) { @@ -42,9 +42,9 @@ public static ILoggerBuilder AddConsole(this ILoggerBuilder builder, Action /// Adds a console logger named 'Console' to the factory. /// - /// The to use. + /// The to use. /// - public static ILoggerBuilder AddConsole(this ILoggerBuilder builder, IConfiguration configuration) + public static ILoggingBuilder AddConsole(this ILoggingBuilder builder, IConfiguration configuration) { if (configuration == null) { diff --git a/src/Microsoft.Extensions.Logging.Debug/DebugLoggerFactoryExtensions.cs b/src/Microsoft.Extensions.Logging.Debug/DebugLoggerFactoryExtensions.cs index 0df9c266..3b88c4cb 100644 --- a/src/Microsoft.Extensions.Logging.Debug/DebugLoggerFactoryExtensions.cs +++ b/src/Microsoft.Extensions.Logging.Debug/DebugLoggerFactoryExtensions.cs @@ -16,7 +16,7 @@ public static class DebugLoggerFactoryExtensions /// Adds a debug logger named 'Debug' to the factory. /// /// The extension method argument. - public static ILoggerBuilder AddDebug(this ILoggerBuilder builder) + public static ILoggingBuilder AddDebug(this ILoggingBuilder builder) { builder.Services.AddSingleton(); diff --git a/src/Microsoft.Extensions.Logging.EventLog/EventLoggerFactoryExtensions.cs b/src/Microsoft.Extensions.Logging.EventLog/EventLoggerFactoryExtensions.cs index 6f1118fd..d95b07a4 100644 --- a/src/Microsoft.Extensions.Logging.EventLog/EventLoggerFactoryExtensions.cs +++ b/src/Microsoft.Extensions.Logging.EventLog/EventLoggerFactoryExtensions.cs @@ -16,7 +16,7 @@ public static class EventLoggerFactoryExtensions /// Adds an event logger named 'EventLog' to the factory. /// /// The extension method argument. - public static ILoggerBuilder AddEventLog(this ILoggerBuilder builder) + public static ILoggingBuilder AddEventLog(this ILoggingBuilder builder) { if (builder == null) { @@ -33,7 +33,7 @@ public static ILoggerBuilder AddEventLog(this ILoggerBuilder builder) /// /// The extension method argument. /// The . - public static ILoggerBuilder AddEventLog(this ILoggerBuilder builder, EventLogSettings settings) + public static ILoggingBuilder AddEventLog(this ILoggingBuilder builder, EventLogSettings settings) { if (builder == null) { diff --git a/src/Microsoft.Extensions.Logging.EventSource/EventSourceLoggerFactoryExtensions.cs b/src/Microsoft.Extensions.Logging.EventSource/EventSourceLoggerFactoryExtensions.cs index a27a88e7..05054bcc 100644 --- a/src/Microsoft.Extensions.Logging.EventSource/EventSourceLoggerFactoryExtensions.cs +++ b/src/Microsoft.Extensions.Logging.EventSource/EventSourceLoggerFactoryExtensions.cs @@ -16,7 +16,7 @@ public static class EventSourceLoggerFactoryExtensions /// Adds an event logger named 'EventSource' to the factory. /// /// The extension method argument. - public static ILoggerBuilder AddEventSourceLogger(this ILoggerBuilder builder) + public static ILoggingBuilder AddEventSourceLogger(this ILoggingBuilder builder) { if (builder == null) { diff --git a/src/Microsoft.Extensions.Logging.Testing/XunitLoggerFactoryExtensions.cs b/src/Microsoft.Extensions.Logging.Testing/XunitLoggerFactoryExtensions.cs index d2d7b613..498ef39c 100644 --- a/src/Microsoft.Extensions.Logging.Testing/XunitLoggerFactoryExtensions.cs +++ b/src/Microsoft.Extensions.Logging.Testing/XunitLoggerFactoryExtensions.cs @@ -10,13 +10,13 @@ namespace Microsoft.Extensions.Logging { public static class XunitLoggerFactoryExtensions { - public static ILoggerBuilder AddXunit(this ILoggerBuilder builder, ITestOutputHelper output) + public static ILoggingBuilder AddXunit(this ILoggingBuilder builder, ITestOutputHelper output) { builder.Services.AddSingleton(new XunitLoggerProvider(output)); return builder; } - public static ILoggerBuilder AddXunit(this ILoggerBuilder builder, ITestOutputHelper output, LogLevel minLevel) + public static ILoggingBuilder AddXunit(this ILoggingBuilder builder, ITestOutputHelper output, LogLevel minLevel) { builder.Services.AddSingleton(new XunitLoggerProvider(output, minLevel)); return builder; diff --git a/src/Microsoft.Extensions.Logging.TraceSource/TraceSourceFactoryExtensions.cs b/src/Microsoft.Extensions.Logging.TraceSource/TraceSourceFactoryExtensions.cs index db8532bc..989b95e4 100644 --- a/src/Microsoft.Extensions.Logging.TraceSource/TraceSourceFactoryExtensions.cs +++ b/src/Microsoft.Extensions.Logging.TraceSource/TraceSourceFactoryExtensions.cs @@ -13,10 +13,10 @@ public static class TraceSourceFactoryExtensions /// /// Adds a TraceSource logger named 'TraceSource' to the factory. /// - /// The to use. + /// The to use. /// The name of the to use. - public static ILoggerBuilder AddTraceSource( - this ILoggerBuilder builder, + public static ILoggingBuilder AddTraceSource( + this ILoggingBuilder builder, string switchName) { if (builder == null) @@ -35,11 +35,11 @@ public static ILoggerBuilder AddTraceSource( /// /// Adds a TraceSource logger named 'TraceSource' to the factory. /// - /// The to use. + /// The to use. /// The name of the to use. /// The to use. - public static ILoggerBuilder AddTraceSource( - this ILoggerBuilder builder, + public static ILoggingBuilder AddTraceSource( + this ILoggingBuilder builder, string switchName, TraceListener listener) { @@ -64,10 +64,10 @@ public static ILoggerBuilder AddTraceSource( /// /// Adds a TraceSource logger named 'TraceSource' to the factory. /// - /// The to use. + /// The to use. /// The to use. - public static ILoggerBuilder AddTraceSource( - this ILoggerBuilder builder, + public static ILoggingBuilder AddTraceSource( + this ILoggingBuilder builder, SourceSwitch sourceSwitch) { if (builder == null) @@ -91,8 +91,8 @@ public static ILoggerBuilder AddTraceSource( /// The to use. /// The to use. /// The to use. - public static ILoggerBuilder AddTraceSource( - this ILoggerBuilder builder, + public static ILoggingBuilder AddTraceSource( + this ILoggingBuilder builder, SourceSwitch sourceSwitch, TraceListener listener) { diff --git a/src/Microsoft.Extensions.Logging/FilterLoggerBuilderExtensions.cs b/src/Microsoft.Extensions.Logging/FilterLoggingBuilderExtensions.cs similarity index 57% rename from src/Microsoft.Extensions.Logging/FilterLoggerBuilderExtensions.cs rename to src/Microsoft.Extensions.Logging/FilterLoggingBuilderExtensions.cs index 41233182..2cfb5230 100644 --- a/src/Microsoft.Extensions.Logging/FilterLoggerBuilderExtensions.cs +++ b/src/Microsoft.Extensions.Logging/FilterLoggingBuilderExtensions.cs @@ -9,54 +9,54 @@ namespace Microsoft.Extensions.Logging /// /// Extension methods for setting up logging services in an . /// - public static class FilterLoggerBuilderExtensions + public static class FilterLoggingBuilderExtensions { - public static ILoggerBuilder AddFilter(this ILoggerBuilder builder, Func levelFilter) where T : ILoggerProvider + public static ILoggingBuilder AddFilter(this ILoggingBuilder builder, Func levelFilter) where T : ILoggerProvider { return AddRule(builder, type: typeof(T).FullName, filter: (type, name, level) => levelFilter(level)); } - public static ILoggerBuilder AddFilter(this ILoggerBuilder builder, string category, LogLevel level) + public static ILoggingBuilder AddFilter(this ILoggingBuilder builder, string category, LogLevel level) { return AddRule(builder, category: category, level: level); } - public static ILoggerBuilder AddFilter(this ILoggerBuilder builder, string category, LogLevel level) where T: ILoggerProvider + public static ILoggingBuilder AddFilter(this ILoggingBuilder builder, string category, LogLevel level) where T: ILoggerProvider { return AddRule(builder, type: typeof(T).FullName, category: category, level: level); } - public static ILoggerBuilder AddFilter(this ILoggerBuilder builder, Func filter) + public static ILoggingBuilder AddFilter(this ILoggingBuilder builder, Func filter) { return AddRule(builder, filter: filter); } - public static ILoggerBuilder AddFilter(this ILoggerBuilder builder, Func filter) where T : ILoggerProvider + public static ILoggingBuilder AddFilter(this ILoggingBuilder builder, Func filter) where T : ILoggerProvider { return AddRule(builder, type: typeof(T).FullName, filter: filter); } - public static ILoggerBuilder AddFilter(this ILoggerBuilder builder, Func categoryLevelFilter) + public static ILoggingBuilder AddFilter(this ILoggingBuilder builder, Func categoryLevelFilter) { return AddRule(builder, filter: (type, name, level) => categoryLevelFilter(name, level)); } - public static ILoggerBuilder AddFilter(this ILoggerBuilder builder, Func categoryLevelFilter) where T : ILoggerProvider + public static ILoggingBuilder AddFilter(this ILoggingBuilder builder, Func categoryLevelFilter) where T : ILoggerProvider { return AddRule(builder, type: typeof(T).FullName, filter: (type, name, level) => categoryLevelFilter(name, level)); } - public static ILoggerBuilder AddFilter(this ILoggerBuilder builder, string category, Func levelFilter) + public static ILoggingBuilder AddFilter(this ILoggingBuilder builder, string category, Func levelFilter) { return AddRule(builder, category: category, filter: (type, name, level) => levelFilter(level)); } - public static ILoggerBuilder AddFilter(this ILoggerBuilder builder, string category, Func levelFilter) where T : ILoggerProvider + public static ILoggingBuilder AddFilter(this ILoggingBuilder builder, string category, Func levelFilter) where T : ILoggerProvider { return AddRule(builder, type: typeof(T).FullName, category: category, filter: (type, name, level) => levelFilter(level)); } - private static ILoggerBuilder AddRule(ILoggerBuilder builder, + private static ILoggingBuilder AddRule(ILoggingBuilder builder, string type = null, string category = null, LogLevel? level = null, diff --git a/src/Microsoft.Extensions.Logging/ILoggerBuilder.cs b/src/Microsoft.Extensions.Logging/ILoggingBuilder.cs similarity index 93% rename from src/Microsoft.Extensions.Logging/ILoggerBuilder.cs rename to src/Microsoft.Extensions.Logging/ILoggingBuilder.cs index 9c5d8bfb..6330e4d5 100644 --- a/src/Microsoft.Extensions.Logging/ILoggerBuilder.cs +++ b/src/Microsoft.Extensions.Logging/ILoggingBuilder.cs @@ -8,7 +8,7 @@ namespace Microsoft.Extensions.Logging /// /// An interface for configuring logging providers. /// - public interface ILoggerBuilder + public interface ILoggingBuilder { /// /// Gets the where Logging services are configured. diff --git a/src/Microsoft.Extensions.Logging/LoggerBuilder.cs b/src/Microsoft.Extensions.Logging/LoggingBuilder.cs similarity index 77% rename from src/Microsoft.Extensions.Logging/LoggerBuilder.cs rename to src/Microsoft.Extensions.Logging/LoggingBuilder.cs index 5f26c1d0..7a4b5ced 100644 --- a/src/Microsoft.Extensions.Logging/LoggerBuilder.cs +++ b/src/Microsoft.Extensions.Logging/LoggingBuilder.cs @@ -5,9 +5,9 @@ namespace Microsoft.Extensions.Logging { - internal class LoggerBuilder : ILoggerBuilder + internal class LoggingBuilder : ILoggingBuilder { - public LoggerBuilder(IServiceCollection services) + public LoggingBuilder(IServiceCollection services) { Services = services; } diff --git a/src/Microsoft.Extensions.Logging/LoggerBuilderExtensions.cs b/src/Microsoft.Extensions.Logging/LoggingBuilderExtensions.cs similarity index 53% rename from src/Microsoft.Extensions.Logging/LoggerBuilderExtensions.cs rename to src/Microsoft.Extensions.Logging/LoggingBuilderExtensions.cs index 6792cc5c..14a08df1 100644 --- a/src/Microsoft.Extensions.Logging/LoggerBuilderExtensions.cs +++ b/src/Microsoft.Extensions.Logging/LoggingBuilderExtensions.cs @@ -9,26 +9,26 @@ namespace Microsoft.Extensions.Logging { /// - /// Extension methods for setting up logging services in an . + /// Extension methods for setting up logging services in an . /// - public static class LoggerBuilderExtensions + public static class LoggingBuilderExtensions { - public static ILoggerBuilder AddConfiguration(this ILoggerBuilder builder, IConfiguration configuration) + public static ILoggingBuilder AddConfiguration(this ILoggingBuilder builder, IConfiguration configuration) { - builder.Services.TryAddEnumerable(ServiceDescriptor.Singleton>(new LoggerFilterConfigureOptions(configuration))); - builder.Services.TryAddEnumerable(ServiceDescriptor.Singleton>(new ConfigurationChangeTokenSource(configuration))); + builder.Services.AddSingleton>(new LoggerFilterConfigureOptions(configuration)); + builder.Services.AddSingleton>(new ConfigurationChangeTokenSource(configuration)); return builder; } - public static ILoggerBuilder SetMinimumLevel(this ILoggerBuilder builder, LogLevel level) + public static ILoggingBuilder SetMinimumLevel(this ILoggingBuilder builder, LogLevel level) { builder.Services.Add(ServiceDescriptor.Singleton>( new DefaultLoggerLevelConfigureOptions(level))); return builder; } - public static ILoggerBuilder AddProvider(this ILoggerBuilder builder, ILoggerProvider provider) + public static ILoggingBuilder AddProvider(this ILoggingBuilder builder, ILoggerProvider provider) { builder.Services.AddSingleton(provider); return builder; diff --git a/src/Microsoft.Extensions.Logging/LoggingServiceCollectionExtensions.cs b/src/Microsoft.Extensions.Logging/LoggingServiceCollectionExtensions.cs index 29372b26..86fbf4ce 100644 --- a/src/Microsoft.Extensions.Logging/LoggingServiceCollectionExtensions.cs +++ b/src/Microsoft.Extensions.Logging/LoggingServiceCollectionExtensions.cs @@ -27,9 +27,9 @@ public static IServiceCollection AddLogging(this IServiceCollection services) /// Adds logging services to the specified . /// /// The to add services to. - /// The configuration delegate. + /// The configuration delegate. /// The so that additional calls can be chained. - public static IServiceCollection AddLogging(this IServiceCollection services, Action configure) + public static IServiceCollection AddLogging(this IServiceCollection services, Action configure) { if (services == null) { @@ -44,7 +44,7 @@ public static IServiceCollection AddLogging(this IServiceCollection services, Ac services.TryAddEnumerable(ServiceDescriptor.Singleton>( new DefaultLoggerLevelConfigureOptions(LogLevel.Information))); - configure(new LoggerBuilder(services)); + configure(new LoggingBuilder(services)); return services; } } diff --git a/test/Microsoft.Extensions.Logging.Test/LoggerBuilderTestExtensions.cs b/test/Microsoft.Extensions.Logging.Test/LoggerBuilderTestExtensions.cs index 109ac51f..015c281c 100644 --- a/test/Microsoft.Extensions.Logging.Test/LoggerBuilderTestExtensions.cs +++ b/test/Microsoft.Extensions.Logging.Test/LoggerBuilderTestExtensions.cs @@ -9,7 +9,7 @@ namespace Microsoft.Extensions.Logging.Test { public static class TestLoggerBuilder { - public static ILoggerFactory Create(Action configure) + public static ILoggerFactory Create(Action configure) { return new ServiceCollection() .AddLogging(configure) From 12cf60057cc5ce57f0f66f43ce7a0fdc975389b9 Mon Sep 17 00:00:00 2001 From: Pavel Krymets Date: Wed, 31 May 2017 11:57:20 -0700 Subject: [PATCH 19/20] No alloc rule matching Provider aliases support --- .../LoggerFactory.cs | 4 +- .../LoggerFilterConfigureOptions.cs | 7 +- .../LoggerFilterRule.cs | 13 +- .../LoggerInformation.cs | 4 +- .../LoggerRuleSelector.cs | 104 +++++++---- .../LoggerFilterTest.cs | 170 ++++++++++-------- .../TestLoggerProvider.cs | 2 + 7 files changed, 188 insertions(+), 116 deletions(-) diff --git a/src/Microsoft.Extensions.Logging/LoggerFactory.cs b/src/Microsoft.Extensions.Logging/LoggerFactory.cs index 2eb71495..a4622b17 100644 --- a/src/Microsoft.Extensions.Logging/LoggerFactory.cs +++ b/src/Microsoft.Extensions.Logging/LoggerFactory.cs @@ -97,7 +97,7 @@ public void AddProvider(ILoggerProvider provider) Array.Resize(ref loggerInformation, loggerInformation.Length + 1); var newLoggerIndex = loggerInformation.Length - 1; loggerInformation[newLoggerIndex].Logger = provider.CreateLogger(categoryName); - loggerInformation[newLoggerIndex].ProviderType = provider.GetType().FullName; + loggerInformation[newLoggerIndex].ProviderType = provider.GetType(); ApplyRules(loggerInformation, categoryName, newLoggerIndex, 1); @@ -114,7 +114,7 @@ private LoggerInformation[] CreateLoggers(string categoryName) var provider = _providerRegistrations[i].Provider; loggers[i].Logger = provider.CreateLogger(categoryName); - loggers[i].ProviderType = provider.GetType().FullName; + loggers[i].ProviderType = provider.GetType(); } ApplyRules(loggers, categoryName, 0, loggers.Length); diff --git a/src/Microsoft.Extensions.Logging/LoggerFilterConfigureOptions.cs b/src/Microsoft.Extensions.Logging/LoggerFilterConfigureOptions.cs index 2e7a24cb..0d40d9dd 100644 --- a/src/Microsoft.Extensions.Logging/LoggerFilterConfigureOptions.cs +++ b/src/Microsoft.Extensions.Logging/LoggerFilterConfigureOptions.cs @@ -54,7 +54,12 @@ private void LoadRules(LoggerFilterOptions options, IConfigurationSection config { if (TryGetSwitch(section.Value, out var level)) { - var newRule = new LoggerFilterRule(logger, section.Key, level, null); + var category = section.Key; + if (category == "Default") + { + category = null; + } + var newRule = new LoggerFilterRule(logger, category, level, null); options.Rules.Add(newRule); } } diff --git a/src/Microsoft.Extensions.Logging/LoggerFilterRule.cs b/src/Microsoft.Extensions.Logging/LoggerFilterRule.cs index 83267b54..197ea02c 100644 --- a/src/Microsoft.Extensions.Logging/LoggerFilterRule.cs +++ b/src/Microsoft.Extensions.Logging/LoggerFilterRule.cs @@ -10,18 +10,18 @@ namespace Microsoft.Extensions.Logging /// public class LoggerFilterRule { - public LoggerFilterRule(string loggerType, string categoryName, LogLevel? logLevel, Func filter) + public LoggerFilterRule(string providerName, string categoryName, LogLevel? logLevel, Func filter) { - LoggerType = loggerType; + ProviderName = providerName; CategoryName = categoryName; LogLevel = logLevel; Filter = filter; } /// - /// Gets the logger provider type this rule applies to. + /// Gets the logger provider type or alias this rule applies to. /// - public string LoggerType { get; } + public string ProviderName { get; } /// /// Gets the logger category this rule applies to. @@ -37,5 +37,10 @@ public LoggerFilterRule(string loggerType, string categoryName, LogLevel? logLev /// Gets the filter delegate that would be applied to messages that passed the . /// public Func Filter { get; } + + public override string ToString() + { + return $"{nameof(ProviderName)}: '{ProviderName}', {nameof(CategoryName)}: '{CategoryName}', {nameof(LogLevel)}: '{LogLevel}', {nameof(Filter)}: '{Filter}'"; + } } } \ No newline at end of file diff --git a/src/Microsoft.Extensions.Logging/LoggerInformation.cs b/src/Microsoft.Extensions.Logging/LoggerInformation.cs index f634f84e..8c6e2f86 100644 --- a/src/Microsoft.Extensions.Logging/LoggerInformation.cs +++ b/src/Microsoft.Extensions.Logging/LoggerInformation.cs @@ -11,7 +11,7 @@ internal struct LoggerInformation public string Category { get; set; } - public string ProviderType { get; set; } + public Type ProviderType { get; set; } public LogLevel? MinLevel { get; set; } @@ -26,7 +26,7 @@ public bool IsEnabled(LogLevel level) if (Filter != null) { - return Filter(ProviderType, Category, level); + return Filter(ProviderType.FullName, Category, level); } return true; diff --git a/src/Microsoft.Extensions.Logging/LoggerRuleSelector.cs b/src/Microsoft.Extensions.Logging/LoggerRuleSelector.cs index cae24d47..95f03c28 100644 --- a/src/Microsoft.Extensions.Logging/LoggerRuleSelector.cs +++ b/src/Microsoft.Extensions.Logging/LoggerRuleSelector.cs @@ -2,31 +2,17 @@ // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System; -using System.Collections.Generic; -using System.Linq; +using System.Reflection; namespace Microsoft.Extensions.Logging { internal class LoggerRuleSelector { - public void Select(LoggerFilterOptions options, string logger, string category, out LogLevel? minLevel, out Func filter) + public void Select(LoggerFilterOptions options, Type providerType, string category, out LogLevel? minLevel, out Func filter) { filter = null; minLevel = options.MinLevel; - var categorySpecificRules = GetMatchingRules(options, logger, category); - - var loggerFilterRule = categorySpecificRules?.LastOrDefault(); - if (loggerFilterRule != null) - { - filter = loggerFilterRule.Filter; - minLevel = loggerFilterRule.LogLevel; - } - } - - private static List GetMatchingRules(LoggerFilterOptions options, string logger, string category) - { - // TODO: This can be rewritten to a single loop. // Filter rule selection: // 1. Select rules for current logger type, if there is none, select ones without logger type specified // 2. Select rules with longest matching categories @@ -35,35 +21,85 @@ private static List GetMatchingRules(LoggerFilterOptions optio // 4. If there are multiple rules use last // 5. If there are no applicable rules use global minimal level - var loggerSpecificRules = options.Rules.Where(rule => rule.LoggerType == logger).ToList(); - if (!loggerSpecificRules.Any()) + var providerAlias = GetAlias(providerType); + LoggerFilterRule current = null; + foreach (var rule in options.Rules) + { + if (IsBetter(rule, current, providerType.FullName, category) + || (!string.IsNullOrEmpty(providerAlias) && IsBetter(rule, current, providerAlias, category))) + { + current = rule; + } + } + + if (current != null) + { + filter = current.Filter; + minLevel = current.LogLevel; + } + } + + private string GetAlias(Type providerType) + { + return providerType.GetTypeInfo() + .GetCustomAttribute() + ?.Name; + } + + private static bool IsBetter(LoggerFilterRule rule, LoggerFilterRule current, string logger, string category) + { + // Skip rules with inapplicable type or category + if (rule.ProviderName != null && rule.ProviderName != logger) + { + return false; + } + + if (rule.CategoryName != null && !category.StartsWith(rule.CategoryName, StringComparison.OrdinalIgnoreCase)) { - loggerSpecificRules = options.Rules.Where(rule => string.IsNullOrEmpty(rule.LoggerType)).ToList(); + return false; } - List categorySpecificRules = null; - if (loggerSpecificRules.Any()) + if (current?.ProviderName != null) + { + if (rule.ProviderName == null) + { + return false; + } + } + else { - categorySpecificRules = loggerSpecificRules - .Where(rule => !string.IsNullOrEmpty(rule.CategoryName) && - category.StartsWith(rule.CategoryName, StringComparison.OrdinalIgnoreCase)) - .GroupBy(rule => rule.CategoryName.Length) - .OrderByDescending(group => group.Key) - .FirstOrDefault() - ?.ToList(); + // We want to skip category check when going from no provider to having provider + if (rule.ProviderName != null) + { + return true; + } + } - if (categorySpecificRules?.Any() != true) + if (current?.CategoryName != null) + { + if (rule.CategoryName == null) { - categorySpecificRules = loggerSpecificRules.Where(rule => string.IsNullOrEmpty(rule.CategoryName)).ToList(); + return false; } - if (!categorySpecificRules.Any()) + if (current.CategoryName.Length > rule.CategoryName.Length) { - categorySpecificRules = loggerSpecificRules - .Where(rule => rule.CategoryName.Equals("Default", StringComparison.OrdinalIgnoreCase)).ToList(); + return false; } } - return categorySpecificRules; + + return true; } } + + public class ProviderAliasAttribute: Attribute + { + public ProviderAliasAttribute(string name) + { + Name = name; + } + + public string Name { get; } + + } } \ No newline at end of file diff --git a/test/Microsoft.Extensions.Logging.Test/LoggerFilterTest.cs b/test/Microsoft.Extensions.Logging.Test/LoggerFilterTest.cs index 8b6b0c60..f6fd4c01 100644 --- a/test/Microsoft.Extensions.Logging.Test/LoggerFilterTest.cs +++ b/test/Microsoft.Extensions.Logging.Test/LoggerFilterTest.cs @@ -423,97 +423,121 @@ public void FilterTest(LoggerFilterOptions options, (string category, LogLevel l public static TheoryData FilterTestData = new TheoryData() { - { // Provider specific rule if preferred + //{ // Provider specific rule if preferred + // new LoggerFilterOptions() + // { + // Rules = + // { + // new LoggerFilterRule(typeof(TestLoggerProvider).FullName, null, LogLevel.Information, null), + // new LoggerFilterRule(null, null, LogLevel.Critical, null) + // } + // }, + // ("Category", LogLevel.Information, true, false) + //}, + //{ // Category specific rule if preferred + // new LoggerFilterOptions() + // { + // Rules = + // { + // new LoggerFilterRule(null, "Category", LogLevel.Information, null), + // new LoggerFilterRule(null, null, LogLevel.Critical, null) + // } + // }, + // ("Category", LogLevel.Information, true, true) + //}, + //{ // Longest category specific rule if preferred + // new LoggerFilterOptions() + // { + // Rules = + // { + // new LoggerFilterRule(null, "Category.Sub", LogLevel.Trace, null), + // new LoggerFilterRule(null, "Category", LogLevel.Information, null), + // new LoggerFilterRule(null, null, LogLevel.Critical, null) + // } + // }, + // ("Category.Sub", LogLevel.Trace, true, true) + //}, + //{ // Provider is selected first, then category + // new LoggerFilterOptions() + // { + // Rules = + // { + // new LoggerFilterRule(null, "Category.Sub", LogLevel.Trace, null), + // new LoggerFilterRule(typeof(TestLoggerProvider).FullName, "Category", LogLevel.Information, null), + // new LoggerFilterRule(null, null, LogLevel.Critical, null) + // } + // }, + // ("Category.Sub", LogLevel.Trace, false, true) + //}, + //{ // Last most specific is selected + // new LoggerFilterOptions() + // { + // Rules = + // { + // new LoggerFilterRule(null, "Category.Sub", LogLevel.Trace, null), + // new LoggerFilterRule(typeof(TestLoggerProvider).FullName, "Category", LogLevel.Information, null), + // new LoggerFilterRule(typeof(TestLoggerProvider).FullName, "Category", LogLevel.Trace, null), + // new LoggerFilterRule(null, null, LogLevel.Critical, null) + // } + // }, + // ("Category.Sub", LogLevel.Trace, true, true) + //}, + //{ // Filter is used if matches level + // new LoggerFilterOptions() + // { + // Rules = + // { + // new LoggerFilterRule(null, null, LogLevel.Critical, (logger, category, level) => true) + // } + // }, + // ("Category.Sub", LogLevel.Error, false, false) + //}, + //{ // Last filter is used is used + // new LoggerFilterOptions() + // { + // Rules = + // { + // new LoggerFilterRule(null, null, LogLevel.Critical, (logger, category, level) => false), + // new LoggerFilterRule(null, null, LogLevel.Critical, (logger, category, level) => true) + // } + // }, + // ("Category.Sub", LogLevel.Critical, true, true) + //}, + //{ // MinLevel is used when no match + // new LoggerFilterOptions() + // { + // Rules = + // { + // new LoggerFilterRule(typeof(TestLoggerProvider).FullName, null, LogLevel.Trace, null), + // }, + // MinLevel = LogLevel.Debug + // }, + // ("Category.Sub", LogLevel.Trace, true, false) + //}, + { // Provider aliases work new LoggerFilterOptions() { Rules = { - new LoggerFilterRule(typeof(TestLoggerProvider).FullName, null, LogLevel.Information, null), - new LoggerFilterRule(null, null, LogLevel.Critical, null) - } - }, - ("Category", LogLevel.Information, true, false) - }, - { // Category specific rule if preferred - new LoggerFilterOptions() - { - Rules = - { - new LoggerFilterRule(null, "Category", LogLevel.Information, null), - new LoggerFilterRule(null, null, LogLevel.Critical, null) - } - }, - ("Category", LogLevel.Information, true, true) - }, - { // Longest category specific rule if preferred - new LoggerFilterOptions() - { - Rules = - { - new LoggerFilterRule(null, "Category.Sub", LogLevel.Trace, null), - new LoggerFilterRule(null, "Category", LogLevel.Information, null), - new LoggerFilterRule(null, null, LogLevel.Critical, null) - } - }, - ("Category.Sub", LogLevel.Trace, true, true) - }, - { // Provider is selected first, then category - new LoggerFilterOptions() - { - Rules = - { - new LoggerFilterRule(null, "Category.Sub", LogLevel.Trace, null), new LoggerFilterRule(typeof(TestLoggerProvider).FullName, "Category", LogLevel.Information, null), + new LoggerFilterRule("TestLogger", "Category", LogLevel.Trace, null), new LoggerFilterRule(null, null, LogLevel.Critical, null) } }, - ("Category.Sub", LogLevel.Trace, false, true) + ("Category.Sub", LogLevel.Trace, true, false) }, - { // Last most specific is selected + { // Aliases equivalent to full names new LoggerFilterOptions() { Rules = { - new LoggerFilterRule(null, "Category.Sub", LogLevel.Trace, null), - new LoggerFilterRule(typeof(TestLoggerProvider).FullName, "Category", LogLevel.Information, null), + new LoggerFilterRule("TestLogger", "Category", LogLevel.Information, null), new LoggerFilterRule(typeof(TestLoggerProvider).FullName, "Category", LogLevel.Trace, null), new LoggerFilterRule(null, null, LogLevel.Critical, null) } }, - ("Category.Sub", LogLevel.Trace, true, true) - }, - { // Filter is used if matches level - new LoggerFilterOptions() - { - Rules = - { - new LoggerFilterRule(null, null, LogLevel.Critical, (logger, category, level) => true) - } - }, - ("Category.Sub", LogLevel.Error, false, false) - }, - { // Last filter is used is used - new LoggerFilterOptions() - { - Rules = - { - new LoggerFilterRule(null, null, LogLevel.Critical, (logger, category, level) => false), - new LoggerFilterRule(null, null, LogLevel.Critical, (logger, category, level) => true) - } - }, - ("Category.Sub", LogLevel.Critical, true, true) - }, - { // MinLevel is used when no match - new LoggerFilterOptions() - { - Rules = - { - new LoggerFilterRule(typeof(TestLoggerProvider).FullName, null, LogLevel.Trace, null), - }, - MinLevel = LogLevel.Debug - }, ("Category.Sub", LogLevel.Trace, true, false) - } + }, }; diff --git a/test/Microsoft.Extensions.Logging.Test/TestLoggerProvider.cs b/test/Microsoft.Extensions.Logging.Test/TestLoggerProvider.cs index a031670b..ca25b6ca 100644 --- a/test/Microsoft.Extensions.Logging.Test/TestLoggerProvider.cs +++ b/test/Microsoft.Extensions.Logging.Test/TestLoggerProvider.cs @@ -6,6 +6,7 @@ namespace Microsoft.Extensions.Logging.Test { + [ProviderAlias("TestLogger")] public class TestLoggerProvider : ILoggerProvider { private readonly Func _filter; @@ -36,6 +37,7 @@ public void Dispose() } } + [ProviderAlias("TestLogger2")] public class TestLoggerProvider2 : TestLoggerProvider { public TestLoggerProvider2(TestSink testSink) : base(testSink, true) From 4bc4987a46ac21ab5d076033599b40bffef37903 Mon Sep 17 00:00:00 2001 From: Pavel Krymets Date: Wed, 31 May 2017 12:04:09 -0700 Subject: [PATCH 20/20] Uncomment tests Use ProviderAliasAttribute Fix ProviderAliasAttribute usage rules --- ...ureAppServicesDiagnosticsLoggerProvider.cs | 1 + .../ConsoleLoggerProvider.cs | 1 + .../DebugLoggerProvider.cs | 1 + .../EventLogLoggerProvider.cs | 1 + .../EventSourceLoggerProvider.cs | 1 + .../TraceSourceLoggerProvider.cs | 1 + .../LoggerFilterConfigureOptions.cs | 29 +-- .../LoggerRuleSelector.cs | 13 +- .../ProviderAliasAttribute.cs | 22 +++ .../LoggerFilterTest.cs | 186 +++++++++--------- .../TestLoggerProvider.cs | 1 - 11 files changed, 123 insertions(+), 134 deletions(-) create mode 100644 src/Microsoft.Extensions.Logging/ProviderAliasAttribute.cs diff --git a/src/Microsoft.Extensions.Logging.AzureAppServices/Internal/AzureAppServicesDiagnosticsLoggerProvider.cs b/src/Microsoft.Extensions.Logging.AzureAppServices/Internal/AzureAppServicesDiagnosticsLoggerProvider.cs index 6fde1cfd..a120e825 100644 --- a/src/Microsoft.Extensions.Logging.AzureAppServices/Internal/AzureAppServicesDiagnosticsLoggerProvider.cs +++ b/src/Microsoft.Extensions.Logging.AzureAppServices/Internal/AzureAppServicesDiagnosticsLoggerProvider.cs @@ -10,6 +10,7 @@ namespace Microsoft.Extensions.Logging.AzureAppServices.Internal /// /// Logger provider for Azure WebApp. /// + [ProviderAlias("AzureAppServices")] public class AzureAppServicesDiagnosticsLoggerProvider : ILoggerProvider { private readonly IWebAppLogConfigurationReader _configurationReader; diff --git a/src/Microsoft.Extensions.Logging.Console/ConsoleLoggerProvider.cs b/src/Microsoft.Extensions.Logging.Console/ConsoleLoggerProvider.cs index 78da38c9..989ea50d 100644 --- a/src/Microsoft.Extensions.Logging.Console/ConsoleLoggerProvider.cs +++ b/src/Microsoft.Extensions.Logging.Console/ConsoleLoggerProvider.cs @@ -9,6 +9,7 @@ namespace Microsoft.Extensions.Logging.Console { + [ProviderAlias("Console")] public class ConsoleLoggerProvider : ILoggerProvider { private readonly ConcurrentDictionary _loggers = new ConcurrentDictionary(); diff --git a/src/Microsoft.Extensions.Logging.Debug/DebugLoggerProvider.cs b/src/Microsoft.Extensions.Logging.Debug/DebugLoggerProvider.cs index 7c2570fc..9c374643 100644 --- a/src/Microsoft.Extensions.Logging.Debug/DebugLoggerProvider.cs +++ b/src/Microsoft.Extensions.Logging.Debug/DebugLoggerProvider.cs @@ -8,6 +8,7 @@ namespace Microsoft.Extensions.Logging.Debug /// /// The provider for the . /// + [ProviderAlias("Debug")] public class DebugLoggerProvider : ILoggerProvider { private readonly Func _filter; diff --git a/src/Microsoft.Extensions.Logging.EventLog/EventLogLoggerProvider.cs b/src/Microsoft.Extensions.Logging.EventLog/EventLogLoggerProvider.cs index 8da0a10c..cd365157 100644 --- a/src/Microsoft.Extensions.Logging.EventLog/EventLogLoggerProvider.cs +++ b/src/Microsoft.Extensions.Logging.EventLog/EventLogLoggerProvider.cs @@ -8,6 +8,7 @@ namespace Microsoft.Extensions.Logging.EventLog /// /// The provider for the . /// + [ProviderAlias("EventLog")] public class EventLogLoggerProvider : ILoggerProvider { private readonly EventLogSettings _settings; diff --git a/src/Microsoft.Extensions.Logging.EventSource/EventSourceLoggerProvider.cs b/src/Microsoft.Extensions.Logging.EventSource/EventSourceLoggerProvider.cs index 48b629aa..3346c5df 100644 --- a/src/Microsoft.Extensions.Logging.EventSource/EventSourceLoggerProvider.cs +++ b/src/Microsoft.Extensions.Logging.EventSource/EventSourceLoggerProvider.cs @@ -9,6 +9,7 @@ namespace Microsoft.Extensions.Logging.EventSource /// /// The provider for the . /// + [ProviderAlias("EventSource")] internal class EventSourceLoggerProvider : ILoggerProvider { // A small integer that uniquely identifies the LoggerFactory assoicated with this LoggingProvider. diff --git a/src/Microsoft.Extensions.Logging.TraceSource/TraceSourceLoggerProvider.cs b/src/Microsoft.Extensions.Logging.TraceSource/TraceSourceLoggerProvider.cs index d059de17..13fd3a9f 100644 --- a/src/Microsoft.Extensions.Logging.TraceSource/TraceSourceLoggerProvider.cs +++ b/src/Microsoft.Extensions.Logging.TraceSource/TraceSourceLoggerProvider.cs @@ -11,6 +11,7 @@ namespace Microsoft.Extensions.Logging.TraceSource /// /// Provides an ILoggerFactory based on System.Diagnostics.TraceSource. /// + [ProviderAlias("TraceSource")] public class TraceSourceLoggerProvider : ILoggerProvider { private readonly SourceSwitch _rootSourceSwitch; diff --git a/src/Microsoft.Extensions.Logging/LoggerFilterConfigureOptions.cs b/src/Microsoft.Extensions.Logging/LoggerFilterConfigureOptions.cs index 0d40d9dd..7972f308 100644 --- a/src/Microsoft.Extensions.Logging/LoggerFilterConfigureOptions.cs +++ b/src/Microsoft.Extensions.Logging/LoggerFilterConfigureOptions.cs @@ -41,7 +41,7 @@ private void LoadDefaultConfigValues(LoggerFilterOptions options) if (logLevelSection != null) { // Load logger specific rules - var logger = ExpandLoggerAlias(configurationSection.Key); + var logger = configurationSection.Key; LoadRules(options, logLevelSection, logger); } } @@ -65,33 +65,6 @@ private void LoadRules(LoggerFilterOptions options, IConfigurationSection config } } - public string ExpandLoggerAlias(string name) - { - switch (name) - { - case "Console": - name = "Microsoft.Extensions.Logging.ConsoleLoggerProvider"; - break; - case "Debug": - name = "Microsoft.Extensions.Logging.DebugLoggerProvider"; - break; - case "AzureAppServices": - name = "Microsoft.Extensions.Logging.AzureAppServices.Internal.AzureAppServicesDiagnosticsLoggerProvider"; - break; - case "EventLog": - name = "Microsoft.Extensions.Logging.EventLog.EventLogLoggerProvider"; - break; - case "TraceSource": - name = "Microsoft.Extensions.Logging.TraceSource.TraceSourceLoggerProvider"; - break; - case "EventSource": - name = "Microsoft.Extensions.Logging.EventSource.EventSourceLoggerProvider"; - break; - } - - return name; - } - private static bool TryGetSwitch(string value, out LogLevel level) { if (string.IsNullOrEmpty(value)) diff --git a/src/Microsoft.Extensions.Logging/LoggerRuleSelector.cs b/src/Microsoft.Extensions.Logging/LoggerRuleSelector.cs index 95f03c28..df9f0245 100644 --- a/src/Microsoft.Extensions.Logging/LoggerRuleSelector.cs +++ b/src/Microsoft.Extensions.Logging/LoggerRuleSelector.cs @@ -43,7 +43,7 @@ private string GetAlias(Type providerType) { return providerType.GetTypeInfo() .GetCustomAttribute() - ?.Name; + ?.Alias; } private static bool IsBetter(LoggerFilterRule rule, LoggerFilterRule current, string logger, string category) @@ -91,15 +91,4 @@ private static bool IsBetter(LoggerFilterRule rule, LoggerFilterRule current, st return true; } } - - public class ProviderAliasAttribute: Attribute - { - public ProviderAliasAttribute(string name) - { - Name = name; - } - - public string Name { get; } - - } } \ No newline at end of file diff --git a/src/Microsoft.Extensions.Logging/ProviderAliasAttribute.cs b/src/Microsoft.Extensions.Logging/ProviderAliasAttribute.cs new file mode 100644 index 00000000..e6c48179 --- /dev/null +++ b/src/Microsoft.Extensions.Logging/ProviderAliasAttribute.cs @@ -0,0 +1,22 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System; + +namespace Microsoft.Extensions.Logging +{ + /// + /// Defines alias for implementation to be used in filtering rules. + /// + [AttributeUsage(AttributeTargets.Class, AllowMultiple = false, Inherited = false)] + public class ProviderAliasAttribute: Attribute + { + public ProviderAliasAttribute(string alias) + { + Alias = alias; + } + + public string Alias { get; } + + } +} \ No newline at end of file diff --git a/test/Microsoft.Extensions.Logging.Test/LoggerFilterTest.cs b/test/Microsoft.Extensions.Logging.Test/LoggerFilterTest.cs index f6fd4c01..db725d16 100644 --- a/test/Microsoft.Extensions.Logging.Test/LoggerFilterTest.cs +++ b/test/Microsoft.Extensions.Logging.Test/LoggerFilterTest.cs @@ -146,7 +146,7 @@ public void PreferFullNameOverDefaultForFiltering() ""LogLevel"": { ""Microsoft"": ""Critical"" }, - ""Microsoft.Extensions.Logging.Test.TestLoggerProvider"": { + ""TestLogger"": { ""LogLevel"": { ""Microsoft"": ""Trace"" } @@ -306,7 +306,7 @@ public void ProviderLevelIsPreferredOverGlobalFilter() var json = @"{ ""Logging"": { - ""Microsoft.Extensions.Logging.Test.TestLoggerProvider"": { + ""TestLogger"": { ""LogLevel"": { ""Test"": ""Debug"" } @@ -423,97 +423,97 @@ public void FilterTest(LoggerFilterOptions options, (string category, LogLevel l public static TheoryData FilterTestData = new TheoryData() { - //{ // Provider specific rule if preferred - // new LoggerFilterOptions() - // { - // Rules = - // { - // new LoggerFilterRule(typeof(TestLoggerProvider).FullName, null, LogLevel.Information, null), - // new LoggerFilterRule(null, null, LogLevel.Critical, null) - // } - // }, - // ("Category", LogLevel.Information, true, false) - //}, - //{ // Category specific rule if preferred - // new LoggerFilterOptions() - // { - // Rules = - // { - // new LoggerFilterRule(null, "Category", LogLevel.Information, null), - // new LoggerFilterRule(null, null, LogLevel.Critical, null) - // } - // }, - // ("Category", LogLevel.Information, true, true) - //}, - //{ // Longest category specific rule if preferred - // new LoggerFilterOptions() - // { - // Rules = - // { - // new LoggerFilterRule(null, "Category.Sub", LogLevel.Trace, null), - // new LoggerFilterRule(null, "Category", LogLevel.Information, null), - // new LoggerFilterRule(null, null, LogLevel.Critical, null) - // } - // }, - // ("Category.Sub", LogLevel.Trace, true, true) - //}, - //{ // Provider is selected first, then category - // new LoggerFilterOptions() - // { - // Rules = - // { - // new LoggerFilterRule(null, "Category.Sub", LogLevel.Trace, null), - // new LoggerFilterRule(typeof(TestLoggerProvider).FullName, "Category", LogLevel.Information, null), - // new LoggerFilterRule(null, null, LogLevel.Critical, null) - // } - // }, - // ("Category.Sub", LogLevel.Trace, false, true) - //}, - //{ // Last most specific is selected - // new LoggerFilterOptions() - // { - // Rules = - // { - // new LoggerFilterRule(null, "Category.Sub", LogLevel.Trace, null), - // new LoggerFilterRule(typeof(TestLoggerProvider).FullName, "Category", LogLevel.Information, null), - // new LoggerFilterRule(typeof(TestLoggerProvider).FullName, "Category", LogLevel.Trace, null), - // new LoggerFilterRule(null, null, LogLevel.Critical, null) - // } - // }, - // ("Category.Sub", LogLevel.Trace, true, true) - //}, - //{ // Filter is used if matches level - // new LoggerFilterOptions() - // { - // Rules = - // { - // new LoggerFilterRule(null, null, LogLevel.Critical, (logger, category, level) => true) - // } - // }, - // ("Category.Sub", LogLevel.Error, false, false) - //}, - //{ // Last filter is used is used - // new LoggerFilterOptions() - // { - // Rules = - // { - // new LoggerFilterRule(null, null, LogLevel.Critical, (logger, category, level) => false), - // new LoggerFilterRule(null, null, LogLevel.Critical, (logger, category, level) => true) - // } - // }, - // ("Category.Sub", LogLevel.Critical, true, true) - //}, - //{ // MinLevel is used when no match - // new LoggerFilterOptions() - // { - // Rules = - // { - // new LoggerFilterRule(typeof(TestLoggerProvider).FullName, null, LogLevel.Trace, null), - // }, - // MinLevel = LogLevel.Debug - // }, - // ("Category.Sub", LogLevel.Trace, true, false) - //}, + { // Provider specific rule if preferred + new LoggerFilterOptions() + { + Rules = + { + new LoggerFilterRule(typeof(TestLoggerProvider).FullName, null, LogLevel.Information, null), + new LoggerFilterRule(null, null, LogLevel.Critical, null) + } + }, + ("Category", LogLevel.Information, true, false) + }, + { // Category specific rule if preferred + new LoggerFilterOptions() + { + Rules = + { + new LoggerFilterRule(null, "Category", LogLevel.Information, null), + new LoggerFilterRule(null, null, LogLevel.Critical, null) + } + }, + ("Category", LogLevel.Information, true, true) + }, + { // Longest category specific rule if preferred + new LoggerFilterOptions() + { + Rules = + { + new LoggerFilterRule(null, "Category.Sub", LogLevel.Trace, null), + new LoggerFilterRule(null, "Category", LogLevel.Information, null), + new LoggerFilterRule(null, null, LogLevel.Critical, null) + } + }, + ("Category.Sub", LogLevel.Trace, true, true) + }, + { // Provider is selected first, then category + new LoggerFilterOptions() + { + Rules = + { + new LoggerFilterRule(null, "Category.Sub", LogLevel.Trace, null), + new LoggerFilterRule(typeof(TestLoggerProvider).FullName, "Category", LogLevel.Information, null), + new LoggerFilterRule(null, null, LogLevel.Critical, null) + } + }, + ("Category.Sub", LogLevel.Trace, false, true) + }, + { // Last most specific is selected + new LoggerFilterOptions() + { + Rules = + { + new LoggerFilterRule(null, "Category.Sub", LogLevel.Trace, null), + new LoggerFilterRule(typeof(TestLoggerProvider).FullName, "Category", LogLevel.Information, null), + new LoggerFilterRule(typeof(TestLoggerProvider).FullName, "Category", LogLevel.Trace, null), + new LoggerFilterRule(null, null, LogLevel.Critical, null) + } + }, + ("Category.Sub", LogLevel.Trace, true, true) + }, + { // Filter is used if matches level + new LoggerFilterOptions() + { + Rules = + { + new LoggerFilterRule(null, null, LogLevel.Critical, (logger, category, level) => true) + } + }, + ("Category.Sub", LogLevel.Error, false, false) + }, + { // Last filter is used is used + new LoggerFilterOptions() + { + Rules = + { + new LoggerFilterRule(null, null, LogLevel.Critical, (logger, category, level) => false), + new LoggerFilterRule(null, null, LogLevel.Critical, (logger, category, level) => true) + } + }, + ("Category.Sub", LogLevel.Critical, true, true) + }, + { // MinLevel is used when no match + new LoggerFilterOptions() + { + Rules = + { + new LoggerFilterRule(typeof(TestLoggerProvider).FullName, null, LogLevel.Trace, null), + }, + MinLevel = LogLevel.Debug + }, + ("Category.Sub", LogLevel.Trace, true, false) + }, { // Provider aliases work new LoggerFilterOptions() { diff --git a/test/Microsoft.Extensions.Logging.Test/TestLoggerProvider.cs b/test/Microsoft.Extensions.Logging.Test/TestLoggerProvider.cs index ca25b6ca..c7144224 100644 --- a/test/Microsoft.Extensions.Logging.Test/TestLoggerProvider.cs +++ b/test/Microsoft.Extensions.Logging.Test/TestLoggerProvider.cs @@ -37,7 +37,6 @@ public void Dispose() } } - [ProviderAlias("TestLogger2")] public class TestLoggerProvider2 : TestLoggerProvider { public TestLoggerProvider2(TestSink testSink) : base(testSink, true)