diff --git a/samples/SampleApp/Program.cs b/samples/SampleApp/Program.cs index de9d7e39..e73d9fb6 100644 --- a/samples/SampleApp/Program.cs +++ b/samples/SampleApp/Program.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 System.Collections.Generic; using System.IO; using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Logging; using ILogger = Microsoft.Extensions.Logging.ILogger; @@ -23,27 +23,29 @@ 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 + var serviceCollection = new ServiceCollection() + .AddLogging(builder => { - { "Microsoft", LogLevel.Warning }, - { "System", LogLevel.Warning }, - { "SampleApp.Program", LogLevel.Debug } - }); - - // providers may be added to a LoggerFactory before any loggers are created + builder + .AddConfiguration(loggingConfiguration.GetSection("Logging")) + .AddFilter("Microsoft", LogLevel.Warning) + .AddFilter("System", LogLevel.Warning) + .AddFilter("SampleApp.Program", LogLevel.Debug) + .AddConsole(); #if NET46 - factory.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 - factory.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.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 9f7c7c62..c52c4ddb 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,49 +16,41 @@ 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 ILoggingBuilder AddAzureWebAppDiagnostics(this ILoggingBuilder builder) { - return AddAzureWebAppDiagnostics(factory, new AzureAppServicesDiagnosticsSettings()); + 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 LoggerFactory AddAzureWebAppDiagnostics(this LoggerFactory factory, AzureAppServicesDiagnosticsSettings settings) + public static ILoggingBuilder AddAzureWebAppDiagnostics(this ILoggingBuilder builder, 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)); + builder.Services.AddSingleton(new AzureAppServicesDiagnosticsLoggerProvider(WebAppContext.Default, settings ?? new AzureAppServicesDiagnosticsSettings())); } - return factory; + return builder; } /// - /// - /// 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.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/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/ConsoleLoggerFactoryExtensions.cs b/src/Microsoft.Extensions.Logging.Console/ConsoleLoggerFactoryExtensions.cs index baade3b8..6beabca4 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,36 +13,65 @@ 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 ILoggingBuilder AddConsole(this ILoggingBuilder builder) { - factory.AddProvider("Console", new ConsoleLoggerProvider(factory.Configuration)); - return factory; + builder.Services.AddSingleton(); + + return builder; + } + + /// + /// Adds a console logger named 'Console' to the factory. + /// + /// The to use. + /// + public static ILoggingBuilder AddConsole(this ILoggingBuilder builder, Action configure) + { + if (configure == null) + { + throw new ArgumentNullException(nameof(configure)); + } + + builder.AddConsole(); + builder.Services.Configure(configure); + + return builder; + } + + /// + /// Adds a console logger named 'Console' to the factory. + /// + /// The to use. + /// + public static ILoggingBuilder AddConsole(this ILoggingBuilder builder, IConfiguration configuration) + { + if (configuration == null) + { + throw new ArgumentNullException(nameof(configuration)); + } + + builder.AddConsole(); + builder.Services.Configure(configuration); + + return builder; } /// - /// - /// 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); @@ -49,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); @@ -64,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, @@ -84,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) @@ -101,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, @@ -122,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) @@ -139,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/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 f350434b..989ea50d 100644 --- a/src/Microsoft.Extensions.Logging.Console/ConsoleLoggerProvider.cs +++ b/src/Microsoft.Extensions.Logging.Console/ConsoleLoggerProvider.cs @@ -4,11 +4,12 @@ using System; using System.Collections.Concurrent; using System.Collections.Generic; -using Microsoft.Extensions.Configuration; using Microsoft.Extensions.Logging.Console.Internal; +using Microsoft.Extensions.Options; namespace Microsoft.Extensions.Logging.Console { + [ProviderAlias("Console")] public class ConsoleLoggerProvider : ILoggerProvider { private readonly ConcurrentDictionary _loggers = new ConcurrentDictionary(); @@ -16,12 +17,11 @@ 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) { if (filter == null) @@ -34,30 +34,25 @@ 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.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).")] public ConsoleLoggerProvider(IConsoleLoggerSettings settings) { if (settings == null) @@ -71,8 +66,6 @@ public ConsoleLoggerProvider(IConsoleLoggerSettings settings) { _settings.ChangeToken.RegisterChangeCallback(OnConfigurationReload, null); } - - _isLegacy = true; } private void OnConfigurationReload(object state) @@ -86,10 +79,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 +109,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,6 +146,7 @@ private IEnumerable GetKeyPrefixes(string name) public void Dispose() { + _optionsReloadToken?.Dispose(); _messageQueue.Dispose(); } } 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 74224362..3b88c4cb 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,35 +15,28 @@ 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 ILoggingBuilder AddDebug(this ILoggingBuilder builder) { - factory.AddProvider("Debug", new DebugLoggerProvider()); - return factory; + builder.Services.AddSingleton(); + + return 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)); @@ -50,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..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; @@ -18,13 +19,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 +30,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/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.EventLog/EventLoggerFactoryExtensions.cs b/src/Microsoft.Extensions.Logging.EventLog/EventLoggerFactoryExtensions.cs index 3af41254..d95b07a4 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,31 +15,29 @@ 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 ILoggingBuilder AddEventLog(this ILoggingBuilder builder) { - if (factory == null) + if (builder == null) { - throw new ArgumentNullException(nameof(factory)); + throw new ArgumentNullException(nameof(builder)); } - factory.AddProvider("EventLog", new EventLogLoggerProvider()); + builder.Services.AddSingleton(); - return factory; + return builder; } /// /// 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, - EventLogSettings settings) + public static ILoggingBuilder AddEventLog(this ILoggingBuilder builder, EventLogSettings settings) { - if (factory == null) + if (builder == null) { - throw new ArgumentNullException(nameof(factory)); + throw new ArgumentNullException(nameof(builder)); } if (settings == null) @@ -46,18 +45,15 @@ public static LoggerFactory AddEventLog( throw new ArgumentNullException(nameof(settings)); } - factory.AddProvider(new EventLogLoggerProvider(settings)); - return factory; + builder.Services.AddSingleton(new EventLogLoggerProvider(settings)); + + return builder; } /// - /// - /// 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 417f94ff..05054bcc 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,28 +15,24 @@ 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 ILoggingBuilder AddEventSourceLogger(this ILoggingBuilder builder) { - if (factory == null) + if (builder == null) { - throw new ArgumentNullException(nameof(factory)); + throw new ArgumentNullException(nameof(builder)); } var loggerProvider = LoggingEventSource.Instance.CreateLoggerProvider(); - factory.AddProvider("EventSource", loggerProvider); + builder.Services.AddSingleton(loggerProvider); - return factory; + return 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.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.Testing/AssemblyTestLog.cs b/src/Microsoft.Extensions.Logging.Testing/AssemblyTestLog.cs index 000b133d..617318f3 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,16 @@ public IDisposable StartTestLog(ITestOutputHelper output, string className, out public ILoggerFactory CreateLoggerFactory(ITestOutputHelper output, string className, [CallerMemberName] string testName = null) { - var loggerFactory = new LoggerFactory(); - if (output != null) + var serviceCollection = new ServiceCollection(); + serviceCollection.AddLogging(builder => { - loggerFactory.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 if (className.StartsWith(_assemblyName + ".")) { @@ -77,10 +82,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.AddLogging(builder => builder.AddConsole()); + + var loggerFactory = serviceCollection.BuildServiceProvider().GetRequiredService(); if (!string.IsNullOrEmpty(baseDirectory)) { @@ -106,7 +113,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 dc778add..498ef39c 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,26 +10,24 @@ namespace Microsoft.Extensions.Logging { public static class XunitLoggerFactoryExtensions { - public static LoggerFactory AddXunit(this LoggerFactory loggerFactory, ITestOutputHelper output) + public static ILoggingBuilder AddXunit(this ILoggingBuilder builder, ITestOutputHelper output) { - loggerFactory.AddProvider("Xunit", new XunitLoggerProvider(output)); - return loggerFactory; + builder.Services.AddSingleton(new XunitLoggerProvider(output)); + return builder; } - public static LoggerFactory AddXunit(this LoggerFactory loggerFactory, ITestOutputHelper output, LogLevel minLevel) + public static ILoggingBuilder AddXunit(this ILoggingBuilder builder, ITestOutputHelper output, LogLevel minLevel) { - loggerFactory.AddProvider("Xunit", new XunitLoggerProvider(output, minLevel)); - return loggerFactory; + builder.Services.AddSingleton(new XunitLoggerProvider(output, minLevel)); + 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 e71409ed..989b95e4 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 ILoggingBuilder AddTraceSource( + this ILoggingBuilder builder, string switchName) { - if (factory == null) + if (builder == null) { - throw new ArgumentNullException(nameof(factory)); + throw new ArgumentNullException(nameof(builder)); } if (switchName == null) @@ -28,23 +29,23 @@ public static LoggerFactory AddTraceSource( throw new ArgumentNullException(nameof(switchName)); } - return factory.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 LoggerFactory AddTraceSource( - this LoggerFactory factory, + public static ILoggingBuilder AddTraceSource( + this ILoggingBuilder builder, string switchName, TraceListener listener) { - if (factory == null) + if (builder == null) { - throw new ArgumentNullException(nameof(factory)); + throw new ArgumentNullException(nameof(builder)); } if (switchName == null) @@ -57,21 +58,21 @@ public static LoggerFactory AddTraceSource( throw new ArgumentNullException(nameof(listener)); } - return factory.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 LoggerFactory AddTraceSource( - this LoggerFactory factory, + public static ILoggingBuilder AddTraceSource( + this ILoggingBuilder builder, SourceSwitch sourceSwitch) { - if (factory == null) + if (builder == null) { - throw new ArgumentNullException(nameof(factory)); + throw new ArgumentNullException(nameof(builder)); } if (sourceSwitch == null) @@ -79,25 +80,25 @@ public static LoggerFactory AddTraceSource( throw new ArgumentNullException(nameof(sourceSwitch)); } - factory.AddProvider("TraceSource", new TraceSourceLoggerProvider(sourceSwitch)); + builder.Services.AddSingleton(new TraceSourceLoggerProvider(sourceSwitch)); - return factory; + 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 LoggerFactory AddTraceSource( - this LoggerFactory factory, + public static ILoggingBuilder AddTraceSource( + this ILoggingBuilder builder, SourceSwitch sourceSwitch, TraceListener listener) { - if (factory == null) + if (builder == null) { - throw new ArgumentNullException(nameof(factory)); + throw new ArgumentNullException(nameof(builder)); } if (sourceSwitch == null) @@ -110,19 +111,15 @@ public static LoggerFactory AddTraceSource( throw new ArgumentNullException(nameof(listener)); } - factory.AddProvider("TraceSource", new TraceSourceLoggerProvider(sourceSwitch, listener)); + builder.Services.AddSingleton(new TraceSourceLoggerProvider(sourceSwitch, listener)); - return factory; + return builder; } /// - /// - /// 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) @@ -140,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, @@ -172,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) @@ -199,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.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/DefaultLoggerLevelConfigureOptions.cs b/src/Microsoft.Extensions.Logging/DefaultLoggerLevelConfigureOptions.cs new file mode 100644 index 00000000..dc6f4214 --- /dev/null +++ b/src/Microsoft.Extensions.Logging/DefaultLoggerLevelConfigureOptions.cs @@ -0,0 +1,14 @@ +// 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 +{ + internal 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/FilterLoggingBuilderExtensions.cs b/src/Microsoft.Extensions.Logging/FilterLoggingBuilderExtensions.cs new file mode 100644 index 00000000..2cfb5230 --- /dev/null +++ b/src/Microsoft.Extensions.Logging/FilterLoggingBuilderExtensions.cs @@ -0,0 +1,69 @@ +// 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.DependencyInjection; + +namespace Microsoft.Extensions.Logging +{ + /// + /// Extension methods for setting up logging services in an . + /// + public static class FilterLoggingBuilderExtensions + { + 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 ILoggingBuilder AddFilter(this ILoggingBuilder builder, string category, LogLevel level) + { + return AddRule(builder, category: category, level: level); + } + + 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 ILoggingBuilder AddFilter(this ILoggingBuilder builder, Func filter) + { + return AddRule(builder, filter: filter); + } + + public static ILoggingBuilder AddFilter(this ILoggingBuilder builder, Func filter) where T : ILoggerProvider + { + return AddRule(builder, type: typeof(T).FullName, filter: filter); + } + + public static ILoggingBuilder AddFilter(this ILoggingBuilder builder, Func categoryLevelFilter) + { + return AddRule(builder, filter: (type, name, level) => categoryLevelFilter(name, level)); + } + + 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 ILoggingBuilder AddFilter(this ILoggingBuilder builder, string category, Func levelFilter) + { + return AddRule(builder, category: category, filter: (type, name, level) => levelFilter(level)); + } + + 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 ILoggingBuilder AddRule(ILoggingBuilder builder, + string type = null, + string category = null, + LogLevel? level = null, + Func filter = null) + { + builder.Services.Configure(options => options.Rules.Add(new LoggerFilterRule(type, category, level, filter))); + return builder; + } + } +} \ No newline at end of file diff --git a/src/Microsoft.Extensions.Logging/ILoggingBuilder.cs b/src/Microsoft.Extensions.Logging/ILoggingBuilder.cs new file mode 100644 index 00000000..6330e4d5 --- /dev/null +++ b/src/Microsoft.Extensions.Logging/ILoggingBuilder.cs @@ -0,0 +1,18 @@ +// 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 +{ + /// + /// An interface for configuring logging providers. + /// + public interface ILoggingBuilder + { + /// + /// Gets the where Logging services are configured. + /// + 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 b320883a..4c593eea 100644 --- a/src/Microsoft.Extensions.Logging/Logger.cs +++ b/src/Microsoft.Extensions.Logging/Logger.cs @@ -9,59 +9,20 @@ 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) + var loggers = Loggers; + 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,22 +51,16 @@ 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 (!_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; } @@ -140,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++) @@ -181,33 +136,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 { @@ -268,17 +196,6 @@ public void Dispose() _isDisposed = true; } } - - internal void Add(IDisposable disposable) - { - throw new NotImplementedException(); - } - } - - private struct LoggerInformation - { - public ILogger Logger { get; set; } - public List ProviderNames { get; set; } } } } \ No newline at end of file diff --git a/src/Microsoft.Extensions.Logging/LoggerFactory.cs b/src/Microsoft.Extensions.Logging/LoggerFactory.cs index 06de9be5..a4622b17 100644 --- a/src/Microsoft.Extensions.Logging/LoggerFactory.cs +++ b/src/Microsoft.Extensions.Logging/LoggerFactory.cs @@ -1,519 +1,148 @@ -// Copyright (c) .NET Foundation. All rights reserved. +// 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; +using Microsoft.Extensions.Options; namespace Microsoft.Extensions.Logging { - /// - /// Summary description for LoggerFactory - /// public class LoggerFactory : ILoggerFactory { + private static readonly LoggerRuleSelector RuleSelector = new LoggerRuleSelector(); + private readonly Dictionary _loggers = new Dictionary(StringComparer.Ordinal); - private KeyValuePair[] _providers = new KeyValuePair[0]; + + private readonly List _providerRegistrations; 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 LoggerFilterOptions _filterOptions; - private static readonly Func _trueFilter = (providerName, category, level) => true; - private static readonly Func _categoryTrueFilter = (n, l) => true; - - public LoggerFactory() + public LoggerFactory() : this(Enumerable.Empty()) { - _genericFilters = _trueFilter; } - public LoggerFactory(IConfiguration configuration) - : this() + public LoggerFactory(IEnumerable providers) : this(providers, new StaticFilterOptionsMonitor(new LoggerFilterOptions())) { - 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) + public LoggerFactory(IEnumerable providers, LoggerFilterOptions filterOptions) : this(providers, new StaticFilterOptionsMonitor(filterOptions)) { - 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) + public LoggerFactory(IEnumerable providers, IOptionsMonitor filterOption) { - // 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); + _providerRegistrations = providers.Select(provider => new ProviderRegistration { Provider = provider }).ToList(); + _changeTokenRegistration = filterOption.OnChange(RefreshFilters); + RefreshFilters(filterOption.CurrentValue); } - public void AddProvider(string providerName, ILoggerProvider provider) + private void RefreshFilters(LoggerFilterOptions filterOptions) { - if (CheckDisposed()) - { - throw new ObjectDisposedException(nameof(LoggerFactory)); - } - lock (_sync) { - _providers = _providers.Concat(new[] { new KeyValuePair(provider, providerName) }).ToArray(); - + _filterOptions = filterOptions; foreach (var logger in _loggers) { - logger.Value.AddProvider(providerName, provider); + var loggerInformation = logger.Value.Loggers; + var categoryName = logger.Key; + + ApplyRules(loggerInformation, categoryName, 0, loggerInformation.Length); } } } - /// - /// 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) + public ILogger CreateLogger(string categoryName) { - if (filter == null) + if (CheckDisposed()) { - throw new ArgumentNullException(nameof(filter)); + throw new ObjectDisposedException(nameof(LoggerFactory)); } 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); - } + Logger logger; - return true; - } - - return false; - }; - } - else + if (!_loggers.TryGetValue(categoryName, out logger)) { - _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) => + logger = new Logger() { - if (value(categoryName, level)) - { - return filter(categoryName, level); - } - - return false; + Loggers = CreateLoggers(categoryName) }; + _loggers[categoryName] = logger; } - 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 logger; } - - 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) + public void AddProvider(ILoggerProvider provider) { - if (filter == null) + if (CheckDisposed()) { - throw new ArgumentNullException(nameof(filter)); + throw new ObjectDisposedException(nameof(LoggerFactory)); } 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) + _providerRegistrations.Add(new ProviderRegistration { Provider = provider, ShouldDispose = true}); + foreach (var logger in _loggers) { - // 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; - } - } - } - } - } + var loggerInformation = logger.Value.Loggers; + var categoryName = logger.Key; - if (_defaultFilter == null) - { - return true; - } + Array.Resize(ref loggerInformation, loggerInformation.Length + 1); + var newLoggerIndex = loggerInformation.Length - 1; + loggerInformation[newLoggerIndex].Logger = provider.CreateLogger(categoryName); + loggerInformation[newLoggerIndex].ProviderType = provider.GetType(); - // 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; + ApplyRules(loggerInformation, categoryName, newLoggerIndex, 1); - // No specific filter for this logger, check defaults - foreach (var prefix in GetKeyPrefixes(categoryName)) - { - if (localDefaultFilter.TryGetValue(prefix, out var defaultLevel)) - { - return currentLevel >= defaultLevel; + logger.Value.Loggers = loggerInformation; } } - - return true; } - private void OnConfigurationReload(object state) + private LoggerInformation[] CreateLoggers(string categoryName) { - _changeToken = _configuration.GetReloadToken(); - try - { - LoadDefaultConfigValues(); - } - catch (Exception /*ex*/) + var loggers = new LoggerInformation[_providerRegistrations.Count]; + for (int i = 0; i < _providerRegistrations.Count; i++) { - // 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); - } - } + var provider = _providerRegistrations[i].Provider; - 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); + loggers[i].Logger = provider.CreateLogger(categoryName); + loggers[i].ProviderType = provider.GetType(); } - } - 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); - } + ApplyRules(loggers, categoryName, 0, loggers.Length); + return loggers; } - private void LoadDefaultConfigValues() + private void ApplyRules(LoggerInformation[] loggers, string categoryName, int start, int count) { - var replacementDefaultFilters = new Dictionary(); - if (_configuration == null) + for (var index = start; index < start + count; index++) { - _defaultFilter = replacementDefaultFilters; - return; - } + ref var loggerInformation = ref loggers[index]; - var logLevelSection = _configuration.GetSection("LogLevel"); + RuleSelector.Select(_filterOptions, + loggerInformation.ProviderType, + categoryName, + out var minLevel, + out var filter); - if (logLevelSection != null) - { - foreach (var section in logLevelSection.AsEnumerable(true)) - { - if (TryGetSwitch(section.Value, out var level)) - { - replacementDefaultFilters[section.Key] = level; - } - } + loggerInformation.Category = categoryName; + loggerInformation.MinLevel = minLevel; + loggerInformation.Filter = filter; } - - _defaultFilter = replacementDefaultFilters; } /// /// Check if the factory has been disposed. /// - /// True when as been called + /// True when as been called protected virtual bool CheckDisposed() => _disposed; public void Dispose() @@ -524,11 +153,14 @@ public void Dispose() _changeTokenRegistration?.Dispose(); - foreach (var provider in _providers) + foreach (var registration in _providerRegistrations) { try { - provider.Key.Dispose(); + if (registration.ShouldDispose) + { + registration.Provider.Dispose(); + } } catch { @@ -537,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/LoggerFilterConfigureOptions.cs b/src/Microsoft.Extensions.Logging/LoggerFilterConfigureOptions.cs new file mode 100644 index 00000000..7972f308 --- /dev/null +++ b/src/Microsoft.Extensions.Logging/LoggerFilterConfigureOptions.cs @@ -0,0 +1,85 @@ +// 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.Options; + +namespace Microsoft.Extensions.Logging +{ + internal class LoggerFilterConfigureOptions : IConfigureOptions + { + private readonly IConfiguration _configuration; + + public LoggerFilterConfigureOptions(IConfiguration configuration) + { + _configuration = configuration; + } + + public void Configure(LoggerFilterOptions options) + { + LoadDefaultConfigValues(options); + } + + private void LoadDefaultConfigValues(LoggerFilterOptions options) + { + if (_configuration == null) + { + return; + } + + foreach (var configurationSection in _configuration.GetChildren()) + { + if (configurationSection.Key == "LogLevel") + { + // Load global category defaults + LoadRules(options, configurationSection, null); + } + else + { + var logLevelSection = configurationSection.GetSection("LogLevel"); + if (logLevelSection != null) + { + // Load logger specific rules + var logger = configurationSection.Key; + LoadRules(options, logLevelSection, logger); + } + } + } + } + + private void LoadRules(LoggerFilterOptions options, IConfigurationSection configurationSection, string logger) + { + foreach (var section in configurationSection.AsEnumerable(true)) + { + if (TryGetSwitch(section.Value, out var level)) + { + var category = section.Key; + if (category == "Default") + { + category = null; + } + var newRule = new LoggerFilterRule(logger, category, level, null); + options.Rules.Add(newRule); + } + } + } + + 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/LoggerFilterOptions.cs b/src/Microsoft.Extensions.Logging/LoggerFilterOptions.cs new file mode 100644 index 00000000..7c32e1fd --- /dev/null +++ b/src/Microsoft.Extensions.Logging/LoggerFilterOptions.cs @@ -0,0 +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 System.Collections.Generic; + +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; } + + /// + /// 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 new file mode 100644 index 00000000..197ea02c --- /dev/null +++ b/src/Microsoft.Extensions.Logging/LoggerFilterRule.cs @@ -0,0 +1,46 @@ +// 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 a rule used to filter log messages + /// + public class LoggerFilterRule + { + public LoggerFilterRule(string providerName, string categoryName, LogLevel? logLevel, Func filter) + { + ProviderName = providerName; + CategoryName = categoryName; + LogLevel = logLevel; + Filter = filter; + } + + /// + /// Gets the logger provider type or alias this rule applies to. + /// + public string ProviderName { 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; } + + 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 new file mode 100644 index 00000000..8c6e2f86 --- /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 Type 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.FullName, 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 new file mode 100644 index 00000000..df9f0245 --- /dev/null +++ b/src/Microsoft.Extensions.Logging/LoggerRuleSelector.cs @@ -0,0 +1,94 @@ +// 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.Reflection; + +namespace Microsoft.Extensions.Logging +{ + internal class LoggerRuleSelector + { + public void Select(LoggerFilterOptions options, Type providerType, string category, out LogLevel? minLevel, out Func filter) + { + filter = null; + minLevel = options.MinLevel; + + // 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 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 + + 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() + ?.Alias; + } + + 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)) + { + return false; + } + + if (current?.ProviderName != null) + { + if (rule.ProviderName == null) + { + return false; + } + } + else + { + // We want to skip category check when going from no provider to having provider + if (rule.ProviderName != null) + { + return true; + } + } + + if (current?.CategoryName != null) + { + if (rule.CategoryName == null) + { + return false; + } + + if (current.CategoryName.Length > rule.CategoryName.Length) + { + return false; + } + } + + return true; + } + } +} \ No newline at end of file diff --git a/src/Microsoft.Extensions.Logging/LoggingBuilder.cs b/src/Microsoft.Extensions.Logging/LoggingBuilder.cs new file mode 100644 index 00000000..7a4b5ced --- /dev/null +++ b/src/Microsoft.Extensions.Logging/LoggingBuilder.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 LoggingBuilder : ILoggingBuilder + { + public LoggingBuilder(IServiceCollection services) + { + Services = services; + } + + public IServiceCollection Services { get; } + } +} \ No newline at end of file diff --git a/src/Microsoft.Extensions.Logging/LoggingBuilderExtensions.cs b/src/Microsoft.Extensions.Logging/LoggingBuilderExtensions.cs new file mode 100644 index 00000000..14a08df1 --- /dev/null +++ b/src/Microsoft.Extensions.Logging/LoggingBuilderExtensions.cs @@ -0,0 +1,37 @@ +// 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; +using Microsoft.Extensions.DependencyInjection.Extensions; +using Microsoft.Extensions.Options; + +namespace Microsoft.Extensions.Logging +{ + /// + /// Extension methods for setting up logging services in an . + /// + public static class LoggingBuilderExtensions + { + public static ILoggingBuilder AddConfiguration(this ILoggingBuilder builder, IConfiguration configuration) + { + builder.Services.AddSingleton>(new LoggerFilterConfigureOptions(configuration)); + builder.Services.AddSingleton>(new ConfigurationChangeTokenSource(configuration)); + + return builder; + } + + public static ILoggingBuilder SetMinimumLevel(this ILoggingBuilder builder, LogLevel level) + { + builder.Services.Add(ServiceDescriptor.Singleton>( + new DefaultLoggerLevelConfigureOptions(level))); + return builder; + } + + public static ILoggingBuilder AddProvider(this ILoggingBuilder builder, ILoggerProvider provider) + { + builder.Services.AddSingleton(provider); + return builder; + } + } +} \ No newline at end of file diff --git a/src/Microsoft.Extensions.Logging/LoggingServiceCollectionExtensions.cs b/src/Microsoft.Extensions.Logging/LoggingServiceCollectionExtensions.cs index d3335492..86fbf4ce 100644 --- a/src/Microsoft.Extensions.Logging/LoggingServiceCollectionExtensions.cs +++ b/src/Microsoft.Extensions.Logging/LoggingServiceCollectionExtensions.cs @@ -4,6 +4,7 @@ using System; using Microsoft.Extensions.Logging; using Microsoft.Extensions.DependencyInjection.Extensions; +using Microsoft.Extensions.Options; namespace Microsoft.Extensions.DependencyInjection { @@ -18,15 +19,32 @@ public static class LoggingServiceCollectionExtensions /// The to add services to. /// The so that additional calls can be chained. 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) { throw new ArgumentNullException(nameof(services)); } + services.AddOptions(); + services.TryAdd(ServiceDescriptor.Singleton()); services.TryAdd(ServiceDescriptor.Singleton(typeof(ILogger<>), typeof(Logger<>))); + services.TryAddEnumerable(ServiceDescriptor.Singleton>( + new DefaultLoggerLevelConfigureOptions(LogLevel.Information))); + + configure(new LoggingBuilder(services)); 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/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/src/Microsoft.Extensions.Logging/StaticFilterOptionsMonitor.cs b/src/Microsoft.Extensions.Logging/StaticFilterOptionsMonitor.cs new file mode 100644 index 00000000..f910426f --- /dev/null +++ b/src/Microsoft.Extensions.Logging/StaticFilterOptionsMonitor.cs @@ -0,0 +1,23 @@ +// 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 +{ + 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.EventSource.Test/EventSourceLoggerTest.cs b/test/Microsoft.Extensions.Logging.EventSource.Test/EventSourceLoggerTest.cs index 9eb5d4b6..0eb14eb7 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,7 @@ public void Logs_AsExpected_WithDefaults() { using (var testListener = new TestEventListener()) { - var factory = new LoggerFactory(); - factory.AddEventSourceLogger(); + var factory = CreateLoggerFactory(); var listenerSettings = new TestEventListener.ListenerSettings(); listenerSettings.Keywords = (EventKeywords)(-1); @@ -53,8 +53,9 @@ public void Logs_Nothing_IfNotEnabled() { using (var testListener = new TestEventListener()) { - var factory = new LoggerFactory(); // No call to factory.AddEventSourceLogger(); + var factory = TestLoggerBuilder.Create(builder => builder + .SetMinimumLevel(LogLevel.Trace)); var listenerSettings = new TestEventListener.ListenerSettings(); listenerSettings.Keywords = EventKeywords.None; @@ -73,8 +74,7 @@ public void Logs_OnlyFormattedMessage_IfKeywordSet() { using (var testListener = new TestEventListener()) { - var factory = new LoggerFactory(); - factory.AddEventSourceLogger(); + var factory = CreateLoggerFactory(); var listenerSettings = new TestEventListener.ListenerSettings(); listenerSettings.Keywords = LoggingEventSource.Keywords.FormattedMessage; @@ -105,8 +105,7 @@ public void Logs_OnlyJson_IfKeywordSet() { using (var testListener = new TestEventListener()) { - var factory = new LoggerFactory(); - factory.AddEventSourceLogger(); + var factory = CreateLoggerFactory(); var listenerSettings = new TestEventListener.ListenerSettings(); listenerSettings.Keywords = LoggingEventSource.Keywords.JsonMessage; @@ -137,8 +136,7 @@ public void Logs_OnlyMessage_IfKeywordSet() { using (var testListener = new TestEventListener()) { - var factory = new LoggerFactory(); - factory.AddEventSourceLogger(); + var factory = CreateLoggerFactory(); var listenerSettings = new TestEventListener.ListenerSettings(); listenerSettings.Keywords = LoggingEventSource.Keywords.Message; @@ -169,8 +167,7 @@ public void Logs_AllEvents_IfTraceSet() { using (var testListener = new TestEventListener()) { - var factory = new LoggerFactory(); - factory.AddEventSourceLogger(); + var factory = CreateLoggerFactory(); var listenerSettings = new TestEventListener.ListenerSettings(); listenerSettings.Keywords = LoggingEventSource.Keywords.JsonMessage; @@ -201,8 +198,7 @@ public void Logs_AsExpected_AtErrorLevel() { using (var testListener = new TestEventListener()) { - var factory = new LoggerFactory(); - factory.AddEventSourceLogger(); + var factory = CreateLoggerFactory(); var listenerSettings = new TestEventListener.ListenerSettings(); listenerSettings.Keywords = LoggingEventSource.Keywords.JsonMessage; @@ -227,8 +223,7 @@ public void Logs_AsExpected_AtWarningLevel() { using (var testListener = new TestEventListener()) { - var factory = new LoggerFactory(); - factory.AddEventSourceLogger(); + var factory = CreateLoggerFactory(); var listenerSettings = new TestEventListener.ListenerSettings(); listenerSettings.Keywords = LoggingEventSource.Keywords.JsonMessage; @@ -255,8 +250,7 @@ public void Logs_AsExpected_WithSingleLoggerSpec() { using (var testListener = new TestEventListener()) { - var factory = new LoggerFactory(); - factory.AddEventSourceLogger(); + var factory = CreateLoggerFactory(); var listenerSettings = new TestEventListener.ListenerSettings(); listenerSettings.Keywords = LoggingEventSource.Keywords.JsonMessage; @@ -278,8 +272,7 @@ public void Logs_AsExpected_WithSingleLoggerSpecWithVerbosity() { using (var testListener = new TestEventListener()) { - var factory = new LoggerFactory(); - factory.AddEventSourceLogger(); + var factory = CreateLoggerFactory(); var listenerSettings = new TestEventListener.ListenerSettings(); listenerSettings.Keywords = LoggingEventSource.Keywords.JsonMessage; @@ -299,8 +292,7 @@ public void Logs_AsExpected_WithComplexLoggerSpec() { using (var testListener = new TestEventListener()) { - var factory = new LoggerFactory(); - factory.AddEventSourceLogger(); + var factory = CreateLoggerFactory(); var listenerSettings = new TestEventListener.ListenerSettings(); listenerSettings.Keywords = LoggingEventSource.Keywords.JsonMessage; @@ -318,6 +310,12 @@ public void Logs_AsExpected_WithComplexLoggerSpec() } } + private static ILoggerFactory CreateLoggerFactory() + { + return TestLoggerBuilder.Create(builder => builder + .AddEventSourceLogger() + .SetMinimumLevel(LogLevel.Trace)); + } private void LogStuff(ILoggerFactory factory) { @@ -389,14 +387,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..ae2875c3 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() { @@ -665,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)); @@ -697,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)); @@ -732,11 +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 - loggerFactory.AddDebug(); + var loggerFactory = new LoggerFactory() + .AddConsole(settings); var logger = loggerFactory.CreateLogger("Test"); diff --git a/test/Microsoft.Extensions.Logging.Test/LoggerBuilderTestExtensions.cs b/test/Microsoft.Extensions.Logging.Test/LoggerBuilderTestExtensions.cs new file mode 100644 index 00000000..015c281c --- /dev/null +++ b/test/Microsoft.Extensions.Logging.Test/LoggerBuilderTestExtensions.cs @@ -0,0 +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 System; +using Castle.Core.Logging; +using Microsoft.Extensions.DependencyInjection; + +namespace Microsoft.Extensions.Logging.Test +{ + public static class TestLoggerBuilder + { + public static ILoggerFactory Create(Action configure) + { + return new ServiceCollection() + .AddLogging(configure) + .BuildServiceProvider() + .GetRequiredService(); + } + } +} diff --git a/test/Microsoft.Extensions.Logging.Test/LoggerFactoryTest.cs b/test/Microsoft.Extensions.Logging.Test/LoggerFactoryTest.cs index 969a8a9c..1e5520d9 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; @@ -16,7 +14,8 @@ public void AddProvider_ThrowsAfterDisposed() { var factory = new LoggerFactory(); factory.Dispose(); - Assert.Throws(() => factory.AddProvider(CreateProvider())); + + Assert.Throws(() => ((ILoggerFactory) factory).AddProvider(CreateProvider())); } [Fact] @@ -48,6 +47,7 @@ public void Dispose_ProvidersAreDisposed() var factory = new LoggerFactory(); var disposableProvider1 = CreateProvider(); var disposableProvider2 = CreateProvider(); + factory.AddProvider(disposableProvider1); factory.AddProvider(disposableProvider2); @@ -78,6 +78,7 @@ public void Dispose_ThrowException_SwallowsException() throwingProvider.As() .Setup(p => p.Dispose()) .Throws(); + factory.AddProvider(throwingProvider.Object); // Act @@ -87,21 +88,5 @@ public void Dispose_ThrowException_SwallowsException() throwingProvider.As() .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); - - // Act - factory.UseConfiguration(configuration.Object); - - // Assert - changeToken.Verify(c => c.RegisterChangeCallback(It.IsAny>(), It.IsAny()), Times.Once); - } } } diff --git a/test/Microsoft.Extensions.Logging.Test/LoggerFilterTest.cs b/test/Microsoft.Extensions.Logging.Test/LoggerFilterTest.cs index d919131b..db725d16 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,11 @@ 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 = TestLoggerBuilder.Create(builder => builder + .AddConfiguration(config.GetSection("Logging")) + .AddProvider(loggerProvider)); var logger = factory.CreateLogger("Microsoft"); @@ -70,10 +73,10 @@ 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 = TestLoggerBuilder.Create(builder => builder + .AddConfiguration(config.GetSection("Logging")) + .AddProvider(loggerProvider)); var logger = factory.CreateLogger("Microsoft"); @@ -102,52 +105,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 +112,7 @@ public void CanFilterOnNamedProviders() var json = @"{ ""Logging"": { - ""CustomName"": { + ""Microsoft.Extensions.Logging.Test.TestLoggerProvider"": { ""LogLevel"": { ""Microsoft"": ""Information"" } @@ -163,43 +120,11 @@ 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 = TestLoggerBuilder.Create(builder => builder + .AddConfiguration(config.GetSection("Logging")) + .AddProvider(loggerProvider)); var logger = factory.CreateLogger("Microsoft"); @@ -208,7 +133,7 @@ public void PreferCustomProviderNameOverFullNameForFiltering() // Assert var writes = loggerProvider.Sink.Writes; - Assert.Equal(1, writes.Count); + Assert.Equal(0, writes.Count); } [Fact] @@ -221,7 +146,7 @@ public void PreferFullNameOverDefaultForFiltering() ""LogLevel"": { ""Microsoft"": ""Critical"" }, - ""Microsoft.Extensions.Logging.Test.TestLoggerProvider"": { + ""TestLogger"": { ""LogLevel"": { ""Microsoft"": ""Trace"" } @@ -229,44 +154,11 @@ 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 = TestLoggerBuilder.Create(builder => builder + .AddConfiguration(config.GetSection("Logging")) + .AddProvider(loggerProvider)); var logger = factory.CreateLogger("Microsoft"); @@ -276,30 +168,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 +177,7 @@ public void DefaultCategoryNameIsUsedIfNoneMatch() var json = @"{ ""Logging"": { - ""Name"": { + ""Microsoft.Extensions.Logging.Test.TestLoggerProvider"": { ""LogLevel"": { ""Default"": ""Information"", ""Microsoft"": ""Warning"" @@ -318,9 +186,11 @@ 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 = TestLoggerBuilder.Create(builder => builder + .AddConfiguration(config.GetSection("Logging")) + .AddProvider(loggerProvider)); var logger = factory.CreateLogger("Microsoft"); @@ -350,21 +220,21 @@ 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 = TestLoggerBuilder.Create(builder => builder + .AddProvider(provider) + .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; + })); var logger = factory.CreateLogger("Test"); @@ -381,18 +251,18 @@ 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 = TestLoggerBuilder.Create(builder => builder + .AddProvider(provider) + .AddFilter((name, cat, level) => { - return level >= LogLevel.Error; - } + if (string.Equals("None", name)) + { + return level >= LogLevel.Error; + } - return true; - }); + return true; + })); var logger = factory.CreateLogger("Test"); @@ -403,13 +273,13 @@ public void AddFilterForNonMatchingProviderDoesNotFilter() } [Fact] - public void AddFilterIsAdditive() + public void AddFilterLastWins() { - 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 = TestLoggerBuilder.Create(builder => builder + .AddProvider(provider) + .AddFilter((name, cat, level) => level >= LogLevel.Warning) + .AddFilter((name, cat, level) => string.Equals(cat, "NotTest"))); var logger = factory.CreateLogger("Test"); @@ -422,21 +292,21 @@ 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] - public void AddFilterIsAdditiveWithConfigurationFilter() + public void ProviderLevelIsPreferredOverGlobalFilter() { // Arrange var json = @"{ ""Logging"": { - ""Name"": { + ""TestLogger"": { ""LogLevel"": { ""Test"": ""Debug"" } @@ -444,10 +314,12 @@ 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 = TestLoggerBuilder.Create(builder => builder + .AddConfiguration(config.GetSection("Logging")) + .AddProvider(loggerProvider) + .AddFilter((name, cat, level) => level < LogLevel.Critical)); var logger = factory.CreateLogger("Test"); @@ -463,16 +335,16 @@ 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 = TestLoggerBuilder.Create(builder => builder + .AddProvider(provider) + .AddFilter((name, cat, level) => level >= LogLevel.Warning)); var logger = factory.CreateLogger("Sample.Test"); @@ -489,10 +361,10 @@ 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 = TestLoggerBuilder.Create(builder => builder + .AddProvider(provider) + .AddFilter("Sample", LogLevel.Warning)); var logger = factory.CreateLogger("Sample.Test"); @@ -509,10 +381,10 @@ 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 factory = TestLoggerBuilder.Create(builder => builder + .AddProvider(provider) + .AddFilter((c, l) => l >= LogLevel.Warning)); var logger = factory.CreateLogger("Sample.Test"); @@ -526,48 +398,148 @@ public void AddFilterWithProviderNameAndCategoryFilterFuncFiltersCorrectly() Assert.Equal(1, writes.Count); } - [Fact] - public void AddFilterWithProviderNameAndFilterFuncFiltersCorrectly() + [Theory] + [MemberData(nameof(FilterTestData))] + public void FilterTest(LoggerFilterOptions options, (string category, LogLevel level, bool expectInProvider1, bool expectInProvider2) message) { - var factory = new LoggerFactory(); - var provider = new TestLoggerProvider(new TestSink(), isEnabled: true); - factory.AddProvider("Name", provider); - factory.AddFilter("Name", l => l >= LogLevel.Warning); - - var logger = factory.CreateLogger("Sample.Test"); - - logger.LogInformation("Message"); + var testSink1 = new TestSink(); + var testSink2 = new TestSink(); - var writes = provider.Sink.Writes; - Assert.Equal(0, writes.Count); - - logger.LogWarning("Message"); - - 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 + var loggerFactory = new LoggerFactory(new[] { - { "Sample", LogLevel.Critical } - }); + new TestLoggerProvider(testSink1, true), + new TestLoggerProvider2(testSink2) + }, options); - var logger = factory.CreateLogger("Sample.Test"); + 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); - logger.LogInformation("Message"); + Assert.Equal(message.expectInProvider1 ? 1 : 0, testSink1.Writes.Count); + Assert.Equal(message.expectInProvider2 ? 1 : 0, testSink2.Writes.Count); + } - var writes = provider.Sink.Writes; - Assert.Equal(0, writes.Count); - logger.LogCritical("Message"); + 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 aliases work + new LoggerFilterOptions() + { + Rules = + { + 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, true, false) + }, + { // Aliases equivalent to full names + new LoggerFilterOptions() + { + Rules = + { + 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, false) + }, + }; - Assert.Equal(1, writes.Count); - } internal ConfigurationRoot CreateConfiguration(Func getJson) { diff --git a/test/Microsoft.Extensions.Logging.Test/LoggerTest.cs b/test/Microsoft.Extensions.Logging.Test/LoggerTest.cs index 3ae76a2c..c9b3d878 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,11 @@ 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 = 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))); + var logger = loggerFactory.CreateLogger("Test"); // Act @@ -37,10 +39,11 @@ 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 = 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))); + var logger = loggerFactory.CreateLogger("Test"); // Act @@ -60,10 +63,11 @@ 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 = 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))); + var logger = loggerFactory.CreateLogger("Test"); // Act @@ -83,9 +87,10 @@ 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 = TestLoggerBuilder.Create(builder => builder + .AddProvider(new CustomLoggerProvider("provider1", ThrowExceptionAt.Log, store)) + .AddProvider(new CustomLoggerProvider("provider2", ThrowExceptionAt.Log, store))); + var logger = loggerFactory.CreateLogger("Test"); // Act @@ -108,6 +113,7 @@ public void LoggerCanGetProviderAfterItIsCreated() var store = new List(); var loggerFactory = new LoggerFactory(); var logger = loggerFactory.CreateLogger("Test"); + loggerFactory.AddProvider(new CustomLoggerProvider("provider1", ThrowExceptionAt.None, store)); // Act diff --git a/test/Microsoft.Extensions.Logging.Test/LoggingServiceCollectionExtensionsTest.cs b/test/Microsoft.Extensions.Logging.Test/LoggingServiceCollectionExtensionsTest.cs index 7d2b9706..afe1eeb0 100644 --- a/test/Microsoft.Extensions.Logging.Test/LoggingServiceCollectionExtensionsTest.cs +++ b/test/Microsoft.Extensions.Logging.Test/LoggingServiceCollectionExtensionsTest.cs @@ -9,11 +9,16 @@ 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 callbackCalled = true; + var loggerBuilder = services.AddLogging(builder => + { + Assert.Same(services, builder.Services); + }); + Assert.True(callbackCalled); } } } 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..c7144224 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; @@ -35,4 +36,11 @@ public void Dispose() DisposeCalled = true; } } + + public class TestLoggerProvider2 : TestLoggerProvider + { + public TestLoggerProvider2(TestSink testSink) : base(testSink, true) + { + } + } } diff --git a/test/Microsoft.Extensions.Logging.Test/TraceSourceLoggerTest.cs b/test/Microsoft.Extensions.Logging.Test/TraceSourceLoggerTest.cs index 8dfd25dc..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,10 +17,9 @@ 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 = TestLoggerBuilder.Create(builder => builder.AddTraceSource(testSwitch)); // Act - factory.AddTraceSource(testSwitch); var logger = factory.CreateLogger("Test"); // Assert @@ -45,11 +45,12 @@ 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 = TestLoggerBuilder.Create(builder => builder + .AddTraceSource(firstSwitch) + .AddTraceSource(secondSwitch)); + 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..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,8 +18,9 @@ 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 = TestLoggerBuilder.Create(builder => + builder.AddTraceSource(new SourceSwitch("TestSwitch"), new ConsoleTraceListener())); + 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..4eb0de21 100644 --- a/test/Microsoft.Extensions.Logging.Testing.Tests/XunitLoggerProviderTest.cs +++ b/test/Microsoft.Extensions.Logging.Testing.Tests/XunitLoggerProviderTest.cs @@ -2,6 +2,8 @@ // 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; namespace Microsoft.Extensions.Logging.Testing.Tests @@ -12,8 +14,10 @@ public class XunitLoggerProviderTest public void LoggerProviderWritesToTestOutputHelper() { var testTestOutputHelper = new TestTestOutputHelper(); - var loggerFactory = new LoggerFactory(); - loggerFactory.AddXunit(testTestOutputHelper); + + var loggerFactory = TestLoggerBuilder.Create(builder => builder + .SetMinimumLevel(LogLevel.Trace) + .AddXunit(testTestOutputHelper)); var logger = loggerFactory.CreateLogger("TestCategory"); logger.LogInformation("This is some great information"); @@ -30,8 +34,8 @@ public void LoggerProviderWritesToTestOutputHelper() public void LoggerProviderDoesNotWriteLogMessagesBelowMinimumLevel() { var testTestOutputHelper = new TestTestOutputHelper(); - var loggerFactory = new LoggerFactory(); - loggerFactory.AddXunit(testTestOutputHelper, LogLevel.Error); + var loggerFactory = TestLoggerBuilder.Create(builder => builder + .AddXunit(testTestOutputHelper, LogLevel.Warning)); var logger = loggerFactory.CreateLogger("TestCategory"); logger.LogInformation("This is some great information"); @@ -44,8 +48,8 @@ public void LoggerProviderDoesNotWriteLogMessagesBelowMinimumLevel() public void LoggerProviderPrependsPrefixToEachLine() { var testTestOutputHelper = new TestTestOutputHelper(); - var loggerFactory = new LoggerFactory(); - loggerFactory.AddXunit(testTestOutputHelper); + 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"); @@ -62,8 +66,9 @@ public void LoggerProviderPrependsPrefixToEachLine() public void LoggerProviderDoesNotThrowIfOutputHelperThrows() { var testTestOutputHelper = new TestTestOutputHelper(); - var loggerFactory = new LoggerFactory(); - loggerFactory.AddXunit(testTestOutputHelper); + var loggerFactory = TestLoggerBuilder.Create(builder => builder + .AddXunit(testTestOutputHelper)); + testTestOutputHelper.Throw = true; var logger = loggerFactory.CreateLogger("TestCategory");