ASP.NET Core 源码学习之 Logging[2]:Configure
在上一章中,我們對 ASP.NET Logging 系統做了一個整體的介紹,而在本章中則開始從最基本的配置開始,逐步深入到源碼當中去。
默認配置
在 ASP.NET Core 2.0 中,對默認配置做了很大的簡化,并把一些基本配置移動到了程序的入口點?Program?類中,更加簡潔。
public class Program{ ??public static void Main(string[] args) ? ?{BuildWebHost(args).Run();} ?
?
??public static IWebHost BuildWebHost(string[] args) =>WebHost.CreateDefaultBuilder(args).UseStartup<Startup>().Build(); }
如上,可以看到基本的配置都放到了?CreateDefaultBuilder?方法中,而?WebHost則在?MetaPackages?中,提供了一些簡化方法。
public static IWebHostBuilder CreateDefaultBuilder(string[] args){ ? ?var builder = new WebHostBuilder().UseKestrel().UseContentRoot(Directory.GetCurrentDirectory()).ConfigureAppConfiguration((hostingContext, config) =>{ ? ? ? ?
? ? ? ? ?? ?var env = hostingContext.HostingEnvironment;config.AddJsonFile("appsettings.json", optional: true, reloadOnChange: true).AddJsonFile($"appsettings.{env.EnvironmentName}.json", optional: true, reloadOnChange: true); ?
? ? ? ? ?? ?? ?if (env.IsDevelopment()){ ? ? ? ? ? ? ?
? ? ? ? ?? ?? ? ?var appAssembly = Assembly.Load(new AssemblyName(env.ApplicationName)); ? ? ? ?
? ? ? ? ?? ?? ? if (appAssembly != null){config.AddUserSecrets(appAssembly, optional: true);}}config.AddEnvironmentVariables(); ? ?
? ? ? ? ?? if (args != null){config.AddCommandLine(args);}}).ConfigureLogging((hostingContext, logging) =>{logging.AddConfiguration(hostingContext.Configuration.GetSection("Logging"));logging.AddConsole();logging.AddDebug();}).UseIISIntegration().UseDefaultServiceProvider((context, options) =>{options.ValidateScopes = context.HostingEnvironment.IsDevelopment();}); ?
? ? ? ??return builder; }
如上可以看到一些我們在 1.0 中非常熟悉的代碼,而?ConfigureLogging?則是?IWebHostBuilder?類的一個擴展方法:
public static IWebHostBuilder ConfigureLogging(this IWebHostBuilder hostBuilder, Action<WebHostBuilderContext, ILoggingBuilder> configureLogging){ ??return hostBuilder.ConfigureServices((context, collection) => collection.AddLogging(builder => configureLogging(context, builder))); }
而?AddLogging?則是 Logging 系統的入口點,是由?Microsoft.Extensions.Logging?所提供的擴展方法:
public static IServiceCollection AddLogging(this IServiceCollection services, Action<ILoggingBuilder> configure){ ??if (services == null){ ? ? ?
? ?throw new ArgumentNullException(nameof(services));}services.AddOptions();services.TryAdd(ServiceDescriptor.Singleton<ILoggerFactory, LoggerFactory>());services.TryAdd(ServiceDescriptor.Singleton(typeof(ILogger<>), typeof(Logger<>))); ? ?services.TryAddEnumerable(ServiceDescriptor.Singleton<IConfigureOptions<LoggerFilterOptions>>( ? ? ?
? ? ?new DefaultLoggerLevelConfigureOptions(LogLevel.Information)));configure(new LoggingBuilder(services)); ?
? ? ? ?return services; }
首先注冊了 Logging 系統基本服務的默認實現,用來激活 Logging 系統,然后創建?LoggingBuilder?對象,而后一系列對日志系統的配置,都是調用的該對象的擴展方法。
internal class LoggingBuilder : ILoggingBuilder{ ??public LoggingBuilder(IServiceCollection services) ? ?{Services = services;} ? ?public IServiceCollection Services { get; } }
現在回頭看看?CreateDefaultBuilder方法中通過?ConfigureLogging?來對日志系統所做的默認配置。
AddConfiguration
該方法是對日志系統的一個全局配置:
logging.AddConfiguration(hostingContext.Configuration.GetSection("Logging"));public static ILoggingBuilder AddConfiguration(this ILoggingBuilder builder, IConfiguration configuration){builder.Services.AddSingleton<IConfigureOptions<LoggerFilterOptions>>(new LoggerFilterConfigureOptions(configuration));builder.Services.AddSingleton<IOptionsChangeTokenSource<LoggerFilterOptions>>(new ConfigurationChangeTokenSource<LoggerFilterOptions>(configuration)); ?
?return builder; }
首先使用?Options?模式注冊了一個?LoggerFilterOptions:
public class LoggerFilterOptions{? ?public LogLevel MinLevel { get; set; } ?
? ? ?public IList<LoggerFilterRule> Rules { get; } = new List<LoggerFilterRule>(); }
? ? ?public class LoggerFilterRule{... ?
? ? ??public string ProviderName { get; } ?
? ? ??public string CategoryName { get; } ?
? ? ?? ?public LogLevel? LogLevel { get; } ?
? ? ?? ??public Func<string, string, LogLevel, bool> Filter { get; }.... }
而默認實現?LoggerFilterConfigureOptions?的邏輯很簡單,就是從配置文件中讀取?LogLevel?的配置:
internal class LoggerFilterConfigureOptions : IConfigureOptions<LoggerFilterOptions> {... ??private void LoadDefaultConfigValues(LoggerFilterOptions options) ? ?{ ? ? ? ?if (_configuration == null){ ? ? ? ?
? ? ?return;} ? ? ? ?
? ? ?foreach (var configurationSection in _configuration.GetChildren()){ ? ? ? ?
? ? ?? ?if (configurationSection.Key == "LogLevel"){ ? ? ? ? ? ?
? ? ?? ? ? ?// Load global category defaultsLoadRules(options, configurationSection, null);} ? ? ?
? ? ?? ? ? else{ ? ? ??var logLevelSection = configurationSection.GetSection("LogLevel"); ? ? ?
? ? ?? ? ? ? ? ? ? ?if (logLevelSection != null){ ? ? ? ? ? ? ? ? ? ?// Load logger specific rulesvar 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);}}}... }
通過代碼,我們可以清楚的知道,我們的配置文件應該按如下格式來定義
{"Logging": {"LogLevel": { // 表示全局"Default": "Warning" // 不指定CategoryName,應用于所有Category},"Console":{ // 指定 ProviderName,僅針對于 ConsoleProvider"Default": "Warning","Microsoft": "Error" // 指定CategoryName為Microsoft的日志級別為Error}}}而?IOptionsChangeTokenSource 是對上面?IConfigureOptions 的一個補充,為我們獲取?OptionsMonitor?注入了必要的服務,更多關于 Options 的介紹可以看我之前文章?IOptionsMonitor。
而在 Logging 系統中,也是通過注入?IOptionsMonitor<LoggerFilterOptions>?來使用?LoggerFilterOptions?的:
public LoggerFactory(IEnumerable<ILoggerProvider> providers, IOptionsMonitor<LoggerFilterOptions> filterOption){_providerRegistrations = providers.Select(provider => new ProviderRegistration { Provider = provider }).ToList();
_changeTokenRegistration = filterOption.OnChange(RefreshFilters);RefreshFilters(filterOption.CurrentValue); }
AddConsole
上面我們提到,在配置文件中可以指定針對某個 Provider 的配置,而?AddConsole?則是用來添加一個 Console 類型的 Provider,用來將日志記錄到控制臺中:
public static ILoggingBuilder AddConsole(this ILoggingBuilder builder){builder.Services.AddSingleton<ILoggerProvider, ConsoleLoggerProvider>(); ??return builder; }
?public static ILoggingBuilder AddConsole(this ILoggingBuilder builder, Action<ConsoleLoggerOptions> configure){ ?
??if (configure == null){ ? ? ?
?? ?throw new ArgumentNullException(nameof(configure));}builder.AddConsole();builder.Services.Configure(configure); ?
?? ? ?return builder; }
以上代碼在?Microsoft.Extensions.Logging.Console?Package 中,首先提供了?ILoggerProvider?的注入方法,用來啟用控制臺的日志記錄功能,而且還提供了一個方法重載,用來指定針對 ConsoleProvider 的配置。
AddDebug
而 AddDebug 與 AddConsole 類似,只不過是把日志輸出在 Debug 窗口中。
更多關于 Provider 的配置,會在以后再詳細探索。
自定義配置
上面介紹了 ASP.NET Core 中對日志系統的默認配置,那么如果我們想再添加一些其它配置應該怎么做呢?
在 1.0 時代,我們通過是在 Startup 類中的 Configure 方法中,注入?ILoggerFactory?來進行配置,當然,在 2.0 中我們仍然可以這樣做,但是更加推薦的做法是在 Program 入口方法中進行配置,而 Configure 方法通過是對一些中間件的配置。
我們可以直接使用上面介紹過的?ConfigureLogging?擴展方法來添加我們自己的配置:
public static IWebHost BuildWebHost(string[] args) =>WebHost.CreateDefaultBuilder(args).ConfigureLogging(build =>{build.AddFilter(f => f == LogLevel.Debug);build.AddEventSourceLogger();}).UseStartup<Startup>().Build();我們添加了一個 EventSource Provider,并且使用了?AddFilter擴展方法對日志的過濾進行配置。而?AddFilter?的作用類似于 前面介紹的?AddConfiguration,只是把配置方式從配置文件變成了代碼。
public static class FilterLoggingBuilderExtensions{ ??// 具有多個重載,此處省略public static ILoggingBuilder AddFilter(this ILoggingBuilder builder, Func<string, string, LogLevel, bool> filter) =>builder.ConfigureFilter(options => options.AddFilter(filter)); ?
?
??private static ILoggingBuilder ConfigureFilter(this ILoggingBuilder builder, Action<LoggerFilterOptions> configureOptions) ? ?{builder.Services.Configure(configureOptions); ? ? ?
???return builder;} }
可以看到,最終也是對?ConfigureOptions?的配置,而后執行的配置會覆蓋之前配置的。
總結
本章從 Logging 系統的起始點入手,詳細分析了如何對 Logging 系統進行配置,分為日志級別過濾和日志提供者兩種配置,而下一章則會分析一下日志的過濾原理。
相關文章:?
ASP.NET Core 源碼學習之 Options[1]:Configure
ASP.NET Core 源碼學習之 Options[2]:IOptions
ASP.NET Core 源碼學習之 Options[3]:IOptionsSnapshot
ASP.NET Core 源碼學習之 Options[4]:IOptionsMonitor
ASP.NET Core 源碼學習之Logging[1]:Introduction
ASP.NET Core MVC 源碼學習:詳解 Action 的匹配
asp.net core源碼飄香:從Hosting開始
asp.net core源碼飄香:Configuration組件
asp.net core源碼飄香:Options組件
asp.net core源碼飄香:Logging組件
原文地址:http://www.cnblogs.com/RainingNight/p/asp-net-core-logging-configure.html
.NET社區新聞,深度好文,微信中搜索dotNET跨平臺或掃描二維碼關注
總結
以上是生活随笔為你收集整理的ASP.NET Core 源码学习之 Logging[2]:Configure的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: ASP.NET Core 2.0 特性介
- 下一篇: Entity Framework Cor