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 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 源码学习之 Logging[2]:Configure的更多相关文章
- ASP.NET Core 源码学习之 Logging[1]:Introduction
在ASP.NET 4.X中,我们通常使用 log4net, NLog 等来记录日志,但是当我们引用的一些第三方类库使用不同的日志框架时,就比较混乱了.而在 ASP.Net Core 中内置了日志系统, ...
- ASP.NET Core 源码学习之 Logging[3]:Logger
上一章,我们介绍了日志的配置,在熟悉了配置之后,自然是要了解一下在应用程序中如何使用,而本章则从最基本的使用开始,逐步去了解去源码. LoggerFactory 我们可以在构造函数中注入 ILogge ...
- 【ASP.NET Core 】ASP.NET Core 源码学习之 Logging[1]:Introduction
在ASP.NET 4.X中,我们通常使用 log4net, NLog 等来记录日志,但是当我们引用的一些第三方类库使用不同的日志框架时,就比较混乱了.而在 ASP.Net Core 中内置了日志系统, ...
- ASP.NET Core 源码学习之 Logging[4]:FileProvider
前面几章介绍了 ASP.NET Core Logging 系统的配置和使用,而对于 Provider ,微软也提供了 Console, Debug, EventSource, TraceSource ...
- ASP.NET Core 源码学习之 Options[1]:Configure
配置的本质就是字符串的键值对,但是对于面向对象语言来说,能使用强类型的配置是何等的爽哉! 目录 ASP.NET Core 配置系统 强类型的 Options Configure 方法 源码解析 ASP ...
- ASP.NET Core源码学习(一)Hosting
ASP.NET Core源码的学习,我们从Hosting开始, Hosting的GitHub地址为:https://github.com/aspnet/Hosting.git 朋友们可以从以上链接克隆 ...
- ASP.NET Core 源码学习之 Options[4]:IOptionsMonitor
前面我们讲到 IOptions 和 IOptionsSnapshot,他们两个最大的区别便是前者注册的是单例模式,后者注册的是 Scope 模式.而 IOptionsMonitor 则要求配置源必须是 ...
- asp.net core源码飘香:Logging组件
简介: 作为基础组件,日志组件被其他组件和中间件所使用,它提供了一个统一的编程模型,即不需要知道日志最终记录到哪里去,只需要调用它即可. 使用方法很简单,通过依赖注入ILogFactory(Creat ...
- ASP.NET Core 源码学习之 Options[2]:IOptions
在上一篇中,介绍了一下Options的注册,而使用时只需要注入IOption即可: public ValuesController(IOptions<MyOptions> options) ...
随机推荐
- vijos1325 桐桐的糖果计划
Description 桐桐是一个快乐的小朋友,他生活中有许多许多好玩的事,让我们一起来看看吧-- 桐桐很喜欢吃棒棒糖.他家处在一大堆糖果店的附近. 但是,他们家的区域经常出现塞车.塞人等情况,这导致 ...
- 无法启动此程序因为计算机中丢失msvcr71
http://jingyan.baidu.com/article/25648fc1abc4d69190fd0077.html 下面是msvcr文件下载地址: 链接:http://pan.b ...
- 有关SQL模糊查询
执行 数据库查询时,有完整查询和模糊查询之分. 一般模糊语句如下: SELECT 字段 FROM 表 WHERE 某字段 Like 条件 其中关于条件,SQL提供了四种匹配模式: 1,%:表示任意0个 ...
- mysql+keepalived 双主热备高可用
理论介绍:我们通常说的双机热备是指两台机器都在运行,但并不是两台机器都同时在提供服务.当提供服务的一台出现故障的时候,另外一台会马上自动接管并且提供服务,而且切换的时间非常短.MySQL双主复制,即互 ...
- object覆盖的div解决办法
最近做个web项目,因为里面有个<object>的插件,弹出<div>对话框会被其遮盖,我做了个<iframe>标签,在弹框时,把<object>覆盖掉 ...
- linux使用crontab实现PHP执行定时任务
首先说说cron,它是一个linux下的定时执行工具.根用户以外的用户可以使用 crontab 工具来配置 cron 任务. 所有用户定义的 crontab 都被保存在/var/spool/cron ...
- 【jframe】Java Web应用程序框架 - 第01篇:Get Started
jframe是什么? jframe是一个基于MIT协议开源的java web应用程序框架,汇聚了我们团队之于java web应用程序的核心架构思想以及大量最佳实践,并且持续在实际项目中不断完善优化. ...
- 用php+mysql+ajax实现淘宝客服或阿里旺旺聊天功能 之 前台页面
首先来看一下我已经实现的效果图: 消费者页面:(本篇随笔) (1)会显示店主的头像 (2)当前用户发送信息显示在右侧,接受的信息,显示在左侧 店主或客服页面:(下一篇随笔) (1)在左侧有一个列表 , ...
- call, apply,bind 方法解析
call(), apply(),bind() 三者皆为Function的方法 call(),apply()的作用是调用方法,并改变函数运行时的context(作用上下文) bind() 的作用是引用方 ...
- Java负载均衡-輪詢法
import java.util.ArrayList; import java.util.HashMap; import java.util.Map; import java.util.Set; /* ...