上一章,我们介绍了日志的配置,在熟悉了配置之后,自然是要了解一下在应用程序中如何使用,而本章则从最基本的使用开始,逐步去了解去源码。

LoggerFactory

我们可以在构造函数中注入 ILoggerFactory,来创建一个日志记录器:

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 的默认实现是 LoggerFactory。而 LoggerFactory 中的代码较多,我们慢慢来看:

首先是构造函数:

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 也有一个 CreateLogger 方法,那他们之间有什么区别呢,后面会解释。

而现在我们可以猜到,在具体的 Provider 中,如 AddConsole 扩展方法,只是简单的将注册了一个 ILoggerProvider 的实例,这样,在DI系统创建 LoggerFactory 实例时,便能够解析到所有注册的 Provider。再往下看一下我们所调用的 LoggerFactoryCreateLogger 方法:

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 了一个 Logger 实例,然后为其 Loggers 属性赋值,而 Loggers 属性则是由注册的所有 ILoggerProvider 所创建出来的 Logger 集合。

回到刚才的问题,有两种 CreateLogger 方法,也就有两种 Logger,一种是直接 New 出来的 Logger,是 ILogger 的默认实现者,也是我们记录日志时所直接调用的 Logger,另外一种则是由 ILoggerProvider 所创建出来的 Logger,姑且称之为 PLogger 吧。而在我们的应用程序中记录日志时,只需要关注 Logger 就可以了,而不需要去关注 PLogger,因为 Logger 是一个日志记录器的聚合,包含所有注册的 PLogger,PLogger 则是具体的执行者,我们可以通过代码来更清楚的了解:

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 方法只是依次调用 Loggers 属性中的 Log 方法,而其本身并不具有记录日志的功能。

再继续把视线转回到 LoggerFactory 类,在其 CreateLoggers 方法中,最后还调用了 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 中的代码我的就不贴了,简单说一下其过滤规则的选择顺序:

  1. 首先查找指定了 ProviderName 并与当且 Provider的名称(Alias)相同的过滤规则,如果没有,则选用未指定 ProviderName 的过滤规则。
  2. 选择指定的 CategoryName 相符合的过滤规则,如果没有,则选择未指定 CategoryName 的那一条。
  3. 如果符合的规则有多条,则选用最后一条。
  4. 如果未找到相符合的规则,则使用全局的最小过滤级别。

而且我们可以看到,LoggerInformation 带有 MinLeve 属性和 Filter 委托两种过滤配置,而这两种配置的来源,在上一章中可以看到,分别是从配置文件(AddConfiguration)和直接使用委托(AddFilter)来进行配置的。而他们的优先级又是怎么样的?

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;}
}

无需多说,通过上面的 IsEnabled 方法能够清楚的看到,先使用 MinLevel 过滤,再使用 Filter 进行过滤。

再总结一下其整个流程:首先是注册 Provider,然后 LogFactory 通过DI系统解析所有注册的 Privoder,在我们调用其 CreateLogger 方法时,创建一个 Logger 对象,并通过这些 Provider 创建一个 LoggerInformation 集合赋予给 Logger 对象,并包含 PLogger 和 经过筛选后的过滤规则,最后在我们调用 Log 方法记录日志时,会遍历 LoggerInformation 集合,然后执行过滤方法,再调用 PLogger 中的 Log 方法。

ILogger

上面介绍了使用 LogFactory 创建 ILogger 的方式,其实还有一种更简单的使用方式,我们可以直接在构造函数中注入 ILogger<T> 来记录日志:

public class TestController : Controller
{private readonly ILogger _logger;public TestController(ILogger<AccountController> logger){_logger = logger;}
}

是不是很神奇,那么是如何实现的呢?

public interface ILogger<out TCategoryName> : ILogger
{}

而 ILogger 的默认实现,在上一章中介绍的 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 其实是一种简写形式,使用泛型的方式来获取 categoryName,最后还是调用了 factory.CreateLogger

总结

本章主要讲解了 ILogger 的创建,过滤一系列过程,主要关注的是 Logger 的实现方式,而到此对 ASP.NET Core Logging 系统也有了一个基本的了解,在我们的应用程序中对 Logging 的配置和使用也算是游刃有余。而下一章则会介绍了一下 LoggerProvider 的实现以及如何自己来写一个 LoggerProvider。

转载于:https://www.cnblogs.com/RainingNight/p/asp-net-core-logging-logger.html

ASP.NET Core 源码学习之 Logging[3]:Logger相关推荐

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

    在上一章中,我们对 ASP.NET Logging 系统做了一个整体的介绍,而在本章中则开始从最基本的配置开始,逐步深入到源码当中去. 默认配置 在 ASP.NET Core 2.0 中,对默认配置做 ...

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

    在ASP.NET 4.X中,我们通常使用 log4net, NLog 等来记录日志,但是当我们引用的一些第三方类库使用不同的日志框架时,就比较混乱了.而在 ASP.Net Core 中内置了日志系统, ...

  3. ASP.NET Core 源码学习之 Logging[4]:FileProvider

    前面几章介绍了 ASP.NET Core Logging 系统的配置和使用,而对于 Provider ,微软也提供了 Console, Debug, EventSource, TraceSource ...

  4. ASP.NET Core 源码学习之 Options[4]:IOptionsMonitor

    前面我们讲到 IOptions 和 IOptionsSnapshot,他们两个最大的区别便是前者注册的是单例模式,后者注册的是 Scope 模式.而 IOptionsMonitor 则要求配置源必须是 ...

  5. ASP.NET Core 源码学习之 Options[3]:IOptionsSnapshot

    2017-06-28 更新: OptionsSnapshot 已改为 OptionsManager 变更详情 IOptionsCache 已改为 IOptionsMonitorCache 变更详情 在 ...

  6. ASP.NET Core 源码学习之 Options[2]:IOptions

    在 上一篇 中,介绍了一下Options的注册,而使用时只需要注入 IOption 即可: public ValuesController(IOptions<MyOptions> opti ...

  7. asp.net core源码飘香:Logging组件

    简介: 作为基础组件,日志组件被其他组件和中间件所使用,它提供了一个统一的编程模型,即不需要知道日志最终记录到哪里去,只需要调用它即可. 使用方法很简单,通过依赖注入ILogFactory(Creat ...

  8. ASP.NET Core源码学习(一)Hosting

    ASP.NET Core源码的学习,我们从Hosting开始, Hosting的GitHub地址为:https://github.com/aspnet/Hosting.git 朋友们可以从以上链接克隆 ...

  9. ASP.NET Core 源码学习之 Options[1]:Configure

    ASP.NET Core 配置系统 在ASP.NET 4.X中,通常将配置存储在 web.config 中,使用静态帮助类来获取这些配置,而对 web.cofng 中进行任何修改时,则会导致应用程序池 ...

最新文章

  1. docker与git实现push-to-deploy
  2. AI:一个20年程序猿的学习资料大全—结构分析软件/办公软件/电气制造控制/高级语言编程/平面三维设计/视频编辑/FQ格式转换软件——只有你不想要的,没有你找不到的
  3. [深入JUnit] 测试运行的入口
  4. Python+Selenium学习笔记10 - send_keys上传文件
  5. python-json模块
  6. 阅读《C陷阱与缺陷》的知识增量
  7. 计算机科学与技术 物联网工,北京科技大学计算机与通信工程学院-王睿
  8. Vue实现点击按钮上下滑动隐藏或展示查询条件
  9. 寻找创业方向的3个方法
  10. laplace 偏微分方程 MATLAB help
  11. arch linux密码,Arch Linux root密码忘记了怎么办
  12. python---turtle库(详解)
  13. Disparity Map
  14. MLC转成SLC模式没有想象的那么好
  15. redis源码学习-03_动态字符串SDS
  16. 自动化缺陷检测系统01-项目总体方案设计
  17. iThoughtsX for mac(优秀的思维导图软件)
  18. elasticsearch7.0.1集群搭建(最后有ES6.7的配置)
  19. java的setquality值多少_java使用JPEGEncodeParam类的setQuality方法进行高质量图片缩放类(转)...
  20. 【虚幻引擎UE】UE5 可编辑动态样条线轨迹及AI跟随路径移动(含工程源码)

热门文章

  1. 加载指定路径下所有文件
  2. NSURL中fileURLWithPath和URLWithString
  3. modbus注意几点
  4. 关于apache和tomcat集群,线程是否占用实验
  5. IDEA去除mapper.xml文件中的sql语句的背景色
  6. 飞行模式的开启和关闭
  7. iOS绘圆形图-CGContextAddArc各参数说明
  8. repcached配置与简单測试
  9. DM8168 TILER(3)
  10. ASP.NET MVC5+EF6+EasyUI 后台管理系统(40)-精准在线人数统计实现-【过滤器+Cache】...