ASP.NET Core 源码学习之 Logging[3]:Logger
上一章,我們介紹了日志的配置,在熟悉了配置之后,自然是要了解一下在應(yīng)用程序中如何使用,而本章則從最基本的使用開始,逐步去了解去源碼。
LoggerFactory
我們可以在構(gòu)造函數(shù)中注入 ILoggerFactory,來創(chuàng)建一個(gè)日志記錄器:
public class TestController : Controller {private readonly ILogger _logger;public TestController(ILoggerFactory factory){_logger = factory.CreateLogger(nameof(TestController));}public void TestLog(){_logger.LogInformation("info");_logger.LogDebug("debug");} }在上一章中我們有介紹到,ILoggerFactory 的默認(rèn)實(shí)現(xiàn)是 LoggerFactory。而 LoggerFactory 中的代碼較多,我們慢慢來看:
首先是構(gòu)造函數(shù):
public LoggerFactory() : this(Enumerable.Empty<ILoggerProvider>()) {}public LoggerFactory(IEnumerable<ILoggerProvider> providers) : this(providers, new StaticFilterOptionsMonitor(new LoggerFilterOptions())) {}public LoggerFactory(IEnumerable<ILoggerProvider> providers, LoggerFilterOptions filterOptions) : this(providers, new StaticFilterOptionsMonitor(filterOptions)) {}public LoggerFactory(IEnumerable<ILoggerProvider> providers, IOptionsMonitor<LoggerFilterOptions> filterOption) {_providerRegistrations = providers.Select(provider => new ProviderRegistration { Provider = provider }).ToList();_changeTokenRegistration = filterOption.OnChange(RefreshFilters);RefreshFilters(filterOption.CurrentValue); }看到這里不得不先說一下 ILoggerProvider:
public interface ILoggerProvider : IDisposable {ILogger CreateLogger(string categoryName); }而我們知道,LoggerFactory 也有一個(gè) CreateLogger 方法,那他們之間有什么區(qū)別呢,后面會(huì)解釋。
而現(xiàn)在我們可以猜到,在具體的 Provider 中,如 AddConsole 擴(kuò)展方法,只是簡(jiǎn)單的將注冊(cè)了一個(gè) ILoggerProvider 的實(shí)例,這樣,在DI系統(tǒng)創(chuàng)建 LoggerFactory 實(shí)例時(shí),便能夠解析到所有注冊(cè)的 Provider。再往下看一下我們所調(diào)用的 LoggerFactory 的 CreateLogger 方法:
public ILogger CreateLogger(string categoryName) {if (CheckDisposed()){throw new ObjectDisposedException(nameof(LoggerFactory));}lock (_sync){Logger logger;if (!_loggers.TryGetValue(categoryName, out logger)){logger = new Logger(){Loggers = CreateLoggers(categoryName)};_loggers[categoryName] = logger;}return logger;} }private LoggerInformation[] CreateLoggers(string categoryName) {var loggers = new LoggerInformation[_providerRegistrations.Count];for (int i = 0; i < _providerRegistrations.Count; i++){var provider = _providerRegistrations[i].Provider;loggers[i].Logger = provider.CreateLogger(categoryName);loggers[i].ProviderType = provider.GetType();}ApplyRules(loggers, categoryName, 0, loggers.Length);return loggers; }首先是直接 new 了一個(gè) Logger 實(shí)例,然后為其 Loggers 屬性賦值,而 Loggers 屬性則是由注冊(cè)的所有 ILoggerProvider 所創(chuàng)建出來的 Logger 集合。
回到剛才的問題,有兩種 CreateLogger 方法,也就有兩種 Logger,一種是直接 New 出來的 Logger,是 ILogger 的默認(rèn)實(shí)現(xiàn)者,也是我們記錄日志時(shí)所直接調(diào)用的 Logger,另外一種則是由 ILoggerProvider 所創(chuàng)建出來的 Logger,姑且稱之為 PLogger 吧。而在我們的應(yīng)用程序中記錄日志時(shí),只需要關(guān)注 Logger 就可以了,而不需要去關(guān)注 PLogger,因?yàn)?Logger 是一個(gè)日志記錄器的聚合,包含所有注冊(cè)的 PLogger,PLogger 則是具體的執(zhí)行者,我們可以通過代碼來更清楚的了解:
internal class Logger : ILogger {public LoggerInformation[] Loggers { get; set; }public void Log<TState>(LogLevel logLevel, EventId eventId, TState state, Exception exception, Func<TState, Exception, string> formatter){var loggers = Loggers;if (loggers == null){return;}List<Exception> exceptions = null;foreach (var loggerInfo in loggers){if (!loggerInfo.IsEnabled(logLevel)){continue;}try{loggerInfo.Logger.Log(logLevel, eventId, state, exception, formatter);}catch (Exception ex){if (exceptions == null){exceptions = new List<Exception>();}exceptions.Add(ex);}}if (exceptions != null && exceptions.Count > 0){throw new AggregateException(message: "An error occurred while writing to logger(s).", innerExceptions: exceptions);}}public bool IsEnabled(LogLevel logLevel){...}public IDisposable BeginScope<TState>(TState state){...} }可以看到,Logger 的 Log 方法只是依次調(diào)用 Loggers 屬性中的 Log 方法,而其本身并不具有記錄日志的功能。
再繼續(xù)把視線轉(zhuǎn)回到 LoggerFactory 類,在其 CreateLoggers 方法中,最后還調(diào)用了 ApplyRules,這便是我們上一章中所配置的過濾器的用武之地了。
private void ApplyRules(LoggerInformation[] loggers, string categoryName, int start, int count) {for (var index = start; index < start + count; index++){ref var loggerInformation = ref loggers[index];RuleSelector.Select(_filterOptions,loggerInformation.ProviderType,categoryName,out var minLevel,out var filter);loggerInformation.Category = categoryName;loggerInformation.MinLevel = minLevel;loggerInformation.Filter = filter;} }RuleSelector 中的代碼我的就不貼了,簡(jiǎn)單說一下其過濾規(guī)則的選擇順序:
而且我們可以看到,LoggerInformation 帶有 MinLeve 屬性和 Filter 委托兩種過濾配置,而這兩種配置的來源,在上一章中可以看到,分別是從配置文件(AddConfiguration)和直接使用委托(AddFilter)來進(jìn)行配置的。而他們的優(yōu)先級(jí)又是怎么樣的?
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<string, string, LogLevel, bool> 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;} }無(wú)需多說,通過上面的 IsEnabled 方法能夠清楚的看到,先使用 MinLevel 過濾,再使用 Filter 進(jìn)行過濾。
再總結(jié)一下其整個(gè)流程:首先是注冊(cè) Provider,然后 LogFactory 通過DI系統(tǒng)解析所有注冊(cè)的 Privoder,在我們調(diào)用其 CreateLogger 方法時(shí),創(chuàng)建一個(gè) Logger 對(duì)象,并通過這些 Provider 創(chuàng)建一個(gè) LoggerInformation 集合賦予給 Logger 對(duì)象,并包含 PLogger 和 經(jīng)過篩選后的過濾規(guī)則,最后在我們調(diào)用 Log 方法記錄日志時(shí),會(huì)遍歷 LoggerInformation 集合,然后執(zhí)行過濾方法,再調(diào)用 PLogger 中的 Log 方法。
ILogger
上面介紹了使用 LogFactory 創(chuàng)建 ILogger 的方式,其實(shí)還有一種更簡(jiǎn)單的使用方式,我們可以直接在構(gòu)造函數(shù)中注入 ILogger<T> 來記錄日志:
public class TestController : Controller {private readonly ILogger _logger;public TestController(ILogger<AccountController> logger){_logger = logger;} }是不是很神奇,那么是如何實(shí)現(xiàn)的呢?
public interface ILogger<out TCategoryName> : ILogger {}而 ILogger 的默認(rèn)實(shí)現(xiàn),在上一章中介紹的 AddLogging 方法中,我們知道是 Logger<T>。
public class Logger<T> : ILogger<T> {private readonly ILogger _logger;/// <summary>/// Creates a new <see cref="Logger{T}"/>./// </summary>/// <param name="factory">The factory.</param>public Logger(ILoggerFactory factory){if (factory == null){throw new ArgumentNullException(nameof(factory));}_logger = factory.CreateLogger(TypeNameHelper.GetTypeDisplayName(typeof(T)));}IDisposable ILogger.BeginScope<TState>(TState state){return _logger.BeginScope(state);}bool ILogger.IsEnabled(LogLevel logLevel){return _logger.IsEnabled(logLevel);}void ILogger.Log<TState>(LogLevel logLevel, EventId eventId, TState state, Exception exception, Func<TState, Exception, string> formatter){_logger.Log(logLevel, eventId, state, exception, formatter);} }可以看到,在 Logger 其實(shí)是一種簡(jiǎn)寫形式,使用泛型的方式來獲取 categoryName,最后還是調(diào)用了 factory.CreateLogger。
總結(jié)
本章主要講解了 ILogger 的創(chuàng)建,過濾一系列過程,主要關(guān)注的是 Logger 的實(shí)現(xiàn)方式,而到此對(duì) ASP.NET Core Logging 系統(tǒng)也有了一個(gè)基本的了解,在我們的應(yīng)用程序中對(duì) Logging 的配置和使用也算是游刃有余。而下一章則會(huì)介紹了一下 LoggerProvider 的實(shí)現(xiàn)以及如何自己來寫一個(gè) LoggerProvider。
轉(zhuǎn)載于:https://www.cnblogs.com/RainingNight/p/asp-net-core-logging-logger.html
總結(jié)
以上是生活随笔為你收集整理的ASP.NET Core 源码学习之 Logging[3]:Logger的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 梦到被蛇咬了是怎么回事
- 下一篇: 设计模式(一)---简单工厂模式