【.NET】Logging 库探索(一):基础接口
Logging 属于 .NET 官方扩展库的一部分,因此,它不算第三方库。应某大伙伴要求,老周出一系列介绍 Logging 的水文。老周向来乐于分享技术,啥主题不重要,只要老周会的东西就行;不会的东西,老周就不误人子弟。
Logging 库嘛,当然是用来记录日志的。从功能上而言,它不会太复杂。为了照顾入门的大伙伴,老周先说说库的结构。
第一句话:它有两个基础包:Microsoft.Extensions.Logging.Abstractions;Microsoft.Extensions.Logging。这些包中只是定义一些公共的,基本的类型。即抽象层。
第二句话:基础包不包含真正的日志功能,不同类型的日志分散到各种 Provider 包中。如 Microsoft.Extensions.Logging.Console 用于控制台输出日志;再如 Microsoft.Extensions.Logging.Debug 包将日志输出到“调试”窗口。属于实现层。
第三句话:你可以实现自己的日志 Provider,如果把日志输出到数据库,输出到文本文件,输出到串口,输出为音频(很新鲜吧,日志还能输出为音频,其实就是 TTS),等等。
你只需把上面三句话在心中默念三遍即可,一定要像念“阿弥陀佛”那样虔诚。然后,你可以忘了,不影响你使用它。
一、Logging 的前世今生
相信很多人也和老周一样,最早是在 ASP.NET Core 项目中知道 Logging 库的。是的,一开始它就是用在 ASP.NET Core 中的,你只要在配置服务容器时来一句 AddLogging 就配置好了,至于里面有啥,不知道。
不仅仅 Logging 库,其实用于配置应用程序的 Configuration 和 Options 库一开始也是为 ASP.NET Core 服务的。后来,官方团队把 Microsoft.Extensions.**** 库都分离出来,成为了各种 .NET 扩展,以补充主体功能。这使得服务 Hosting、服务容器、配置、选项、日志、缓存、对象池等等一大堆东西可以用在各种 .NET 项目中。
二、如何在服务容器外使用 Logging
有服务容器的情况下,你只要调用一下 AddLogging 方法,再在委托中用 LoggingBuilder 添加一下日志 Provider 就完事了。在没有使用容器的时候咋办呢?下面老周举个例子。
1、新建一个控制台项目,不要复杂的,控制台就行了。而且咱们用的就是输出到控制台的日志。
2、引用以下两个包。方法你随便选,用 VS 的 nuget 包管理器也行,用 dotnet add package 命令也行,直接编辑项目文件也行。
<ItemGroup> <PackageReference Include="Microsoft.Extensions.Logging.Console" Version="10.0.12" /> <PackageReference Include="Microsoft.Extensions.Logging" Version="10.0.12" /> </ItemGroup>
Microsoft.Extensions.Logging 可以不引用,只引用 *.Console 就行了,因为存在依赖关系,nuget 会自己查找的。
3、找到 Main 方法(用顶层语句可以不写 Main),直接开写。
static void Main(string[] args) { // 先建工厂 ILoggerFactory logfac = LoggerFactory.Create(loggingBuilder => { // 购买生产设备——Console loggingBuilder.AddConsole(); }); // 生产一个 Logger 用一下 ILogger logger = logfac.CreateLogger("大冬菇OA系统"); // 现在可以写日志了 logger.LogInformation("上半年卖出设备5台"); // 一般信息 logger.LogWarning("售出后接到大量用户投诉"); // 警告,可能出事了 logger.LogError("5台设备中有4台存在质量问题,要返厂"); // 错误,出大事了 logger.LogCritical("下半年公司资金链断了"); // 关键消息,重大险情 // See you la la }
首先,需要一个工厂类 LoggerFactory,它的功能是创建 Logger。
Logger 的功能单一,就是记录日志。完了。就这么简单。
LogXXX 是扩展方法,方便写日志时调用。如 LogInformation 表示输出的日志是一般的消息,LogWarning 方法表示输出的消息是警告。这些 Information、Warning、Error、Critical 叫日志级别(或日志等级)。通俗地说就是消息的严重程度。
记录的日志会长这个样子。

现在,你只要会写上面的代码就行(别交给 AI 去写,那样你学不会的),一些细节不懂不用管,老周后面会介绍的。
三、重要的接口
有几个接口类型很重要,它们共同构筑了 Logging 库的主体骨架和规范。
1、ILoggerProvider:如果要自定义的日志输出,那这个接口就很重要。该接口只实现一个方法:
ILogger CreateLogger(string categoryName);
ILogger 接口的实现类用于写日志,咱们稍后再介绍。categoryName 参数表示日志的类别。说人话就是,方便人们知道这条日志是哪来的,谁写入的,跟哪个功能模块有关。这样才方便跟踪。
总的来说,ILoggerProvider 的作用是创建 Logger。
2、ILogger:这个接口的实现就是写日志的核心逻辑。
BeginScope 方法:这个东西主要在连续输出多条日志时用得上,它返回的对象实现 IDisposable 接口,可在 using 语句块中使用,用完释放。也就是在一个作用域内输出N条日志时可以共享这些日志之间的一些数据(上下文)。
想看效果?来,给大伙举个例子。
var fac = sp.GetRequiredService<ILoggerFactory>(); var logger = fac.CreateLogger("Hhahaha"); using (var w = logger.BeginScope<string>("HR模块")) { logger.LogInformation("招人啦"); logger.LogWarning("招不到人啦"); } using (var k = logger.BeginScope<string>("采购模块")) { logger.LogInformation("9-12:购入200台PDA"); logger.LogCritical("9-13:其中有150台货不对版"); }
效果如下:

上述代码中,“HR模块”、“采购模块”就是上下文数据。
IsEnabled 方法:决定这条日志要不要记录。返回 false 表示不记录这条日志(禁用日志)。
Log 方法:真正写日志的逻辑在这里。如把日志写入文本文件。
3、ILoggerFactory:这个一般咱们不需要实现,框架默认实现类为 LoggerFactory。此接口也存在 CreateLogger 方法,但与 ILoggerProvider 接口不同。LoggerFactory 内部维护一个 ILoggerProvider 列表,在创建 Logger 会调用每个 Provider 的 CreateLogger 方法。比如,你添加了两种日志:输出到控制台(Console)和输出到“调试”窗口(Debug)。LoggerFactory 会维护两个 Provider,当创建 Logger 时,这两个 Provider 会创建各个的 Logger,然后把多个 Logger 套娃,封装进一个 Logger中。这个套了娃的 Logger 就是返回给你用的,当你调用这个 Logger 写日志时,它就 for 循环调用里面套的 Logger 崽。比如,你用这个 Looger 写了一条错误信息,它里面有 Console 和 Debug 两个 Logger,于是循环调用这两个 Logger 的相关方法。这使得你这条错误信息会同时写到控制台和“调试”窗口中。
于是,你会疑惑:如果我启用多种日志,是不是会影响性能?当然会有点的,所以日志内容要简短,生成的逻辑也别太复杂。在实现自定义的日志 Provider 时,最好用一个队列先缓存日志,再在多线程上提取队列来输出日志。这样输出日志时能减少对其他代码的影响。
4、ILogger<T>:这个接口其实与 ILogger 一样,而且是从 ILogger 派生。它只是把日志类别类型化罢了,即将某个类型的名称作为日志类别(Category)。它有个默认实现类 Logger<T>,而且它们自动注册到服务容器中。Logger<T> 通过构造函数注入 ILoggerFactory 服务,在类的内部套了个娃——内部维护一个 ILogger 类型的字段。由 LoggerFactory 负责创建 Logger,赋值给内部的 ILogger 字段。所以,Logger<T> 的公共方法实际上是调用了内部 ILogger 字段的方法。结论:咱们一般不需要自己实现 ILogger<T> 接口。
5、ILoggingBuilder:内部实现,不公开。此接口主要是调用它的扩展方法,作用是配置与日志有关的东西。如调用 AddConsole 方法来添加控制台的日志功能。还可以调用 AddFilter 方法配置日志过滤——就是控制哪些日志会记录,哪些日志忽略。如果使用配置文件(如 JSON 文件),可以调用 AddConfiguration 方法指定从哪个节点开始读取配置。例如,ASP.NET Core Host 在初始化时会添加对 Logging 节点。
services.AddLogging(logging => { logging.AddConfiguration(configuration.GetSection("Logging")); logging.AddSimpleConsole(); logging.Configure(options => { …… }); });
现在,你就明白为啥 appsettings.json 文件中默认会有个 Logging 节点了吧。
最后,老周画了一张没啥艺术性的图,来描述各个接口之间的关系。

好了,本篇水文只是个开胃菜,后面老周会介绍日志过滤器、配置,以及自定义日志的内容。

浙公网安备 33010602011771号