在上一章中,我们对 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
则在 MetaPackages 中,提供了一些简化方法。
- WebHost
- 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)));
- }
而
则是 Logging 系统的入口点,是由
- AddLogging
所提供的扩展方法:
- 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
该方法是对日志系统的一个全局配置:
- 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
而在 Logging 系统中,也是通过注入
来使用 LoggerFilterOptions 的:
- IOptionsMonitor<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);
- }
上面我们提到,在配置文件中可以指定针对某个 Provider 的配置,而
则是用来添加一个 Console 类型的 Provider,用来将日志记录到控制台中:
- AddConsole
- 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 中,首先提供了
的注入方法,用来启用控制台的日志记录功能,而且还提供了一个方法重载,用来指定针对 ConsoleProvider 的配置。
- ILoggerProvider
而 AddDebug 与 AddConsole 类似,只不过是把日志输出在 Debug 窗口中。
更多关于 Provider 的配置,会在以后再详细探索。
上面介绍了 ASP.NET Core 中对日志系统的默认配置,那么如果我们想再添加一些其它配置应该怎么做呢?
在 1.0 时代,我们通过是在 Startup 类中的 Configure 方法中,注入
来进行配置,当然,在 2.0 中我们仍然可以这样做,但是更加推荐的做法是在 Program 入口方法中进行配置,而 Configure 方法通过是对一些中间件的配置。
- ILoggerFactory
我们可以直接使用上面介绍过的
扩展方法来添加我们自己的配置:
- 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 的作用类似于 前面介绍的 AddConfiguration,只是把配置方式从配置文件变成了代码。
- AddFilter
- 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 系统进行配置,分为日志级别过滤和日志提供者两种配置,而下一章则会分析一下日志的过滤原理。
来源: http://www.cnblogs.com/RainingNight/p/asp-net-core-logging-configure.html