您的位置:首页 > 编程语言 > ASP

ASP.NET Core 源码学习之 Logging[2]:Configure

2017-08-21 07:07 961 查看
在上一章中,我们对 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 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);
           }
       }
   }

   ...
}

通过代码,我们可以清楚的知道,我们的配置文件应该按如下格式来定义
{
 "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跨平台或扫描二维码关注
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: