一、前言
对内置日志系统的整体实现进行了介绍之后,可以通过使用内置记录器来实现日志的输出路径。而在实际项目开发中,使用第三方日志框架(如: Log4Net、NLog、Loggr、Serilog、Sentry 等)来记录也是非常多的。首先一般基础的内置日志记录器在第三方日志框架中都有实现,然后第三方日志框架在功能上更加强大和丰富,能满足我们更多的项目分析和诊断的需求。
所以在这一篇中,我们将介绍第三方日志记录提供程序——Serilog
二、回顾
系统内置日志系列:
1. 基于.NetCore3.1系列 —— 日志记录之日志配置揭秘
2. 基于.NetCore3.1系列 —— 日志记录之日志核心要素揭秘
3. 基于.NetCore3.1系列 —— 日志记录之自定义日志组件
从之前学习的内置日志系统中,我们根据日志配置的方式了解到了通过配置的方式,可以有效的输出日志记录,方便我们查找发现问题。
而在进一步对内部运行的主要核心机制进行深入探究后发现了内置日志记录的几个核心要素,在日志工厂记录器(ILoggerFactory
)中实现将日志记录提供器(ILoggerProvider
)对象都可以集成到Logger
对象组合中,这样的话,我们就可以通过基于ILoggerProvider
自定义日志记录程序集成到Logger
中,再创建写日志定义Ilogger
,自定义日志记录器实现日志的输出方式,这样实现自定义日志记录工具。
在最后我们通过自定义的方式简单的实现了自定义日志组件,在这个基础上,我们可以根据具体的需求进行完善修改。当然了,我们也可以借用第三方日志框架组件程序进行使用。
三、说明
我们都知道日志记录在项目开发中或者生产环境中,都起到举足轻重的作用。因此,我们都会采用在项目加入第三方框架日志或自行封装日志记录来记录日志。
所以在这一篇中,我们会采用在项目中使用Serilog,目的不仅仅在于希望在用户使用之前发现代码中的BUG和错误,更多的是方便我们可以快速的查询生产环境的日志问题,深入的了解系统运行的表现。
从Serilog的官方介绍中,我们可以发现 其框架是.net中的诊断日志库,可以在所有的.net平台上运行。支持结构化日志记录,对复杂、分布式、异步应用程序的支持非常出色。
Serilog是基于日志事件(log events),而不是日志消息(log message)。可以将日志事件格式化为控制台的可读文本或者将事件化为JSON格式。应用程序中的日志语句会创建LogEvent
对象,而连接到管道的接收器(sinks)会知道如何记录它们。(接收器 包括各种终端、控制台、文本、SqlServer、ElasticSearch等等可用的列表)
结构化与非结构化之间的问题:
对于日志的处理,在大部分情况下,会权衡是否对开发者的友好型以及对程序解析的方便性。在很多情况下,开发者可能只是想记录一段日志而已,所以可以会考虑简单的加上一行代码来以达到记录日志的目的,如(
log.debug("Disk quota {0} exceeded by user {1}", quota, user);
)当然了,日志的执行结构可能被存于文本文件或者数据库中。这样的日志从开发者的角度来说,清晰易懂,十分友好。但是如果后续要使用程序取查找海量的的上述例子在某段时间内的特定用户,则很难高效率地完成这一要求,因为需要对每个日志进行字符串解析。因此,我们就需要寻求更快更方便的方式来查找记录。
非结构的日志
对自由格式文本的解析往往依赖于正则表达式,并且依赖于不变的文本。这会使解析自由格式的文本变得非常脆弱(即解析与代码中的确切文本紧密耦合)。
还考虑搜索/查找的情况,例如:
SELECT text FROM logs WHERE text LIKE "Disk quota";
LIKE
条件需要与每个text
行值进行比较;再次,这在计算上是相对浪费的,尤其是在使用通配符时:
SELECT text FROM logs WHERE text LIKE "Disk %";
结构化的日志
使用结构化日志记录,与磁盘错误相关的日志消息在JSON中可能如下所示:
{ "level": "DEBUG", "user": "username", "error_type": "disk", "text": "Disk quota ... exceeded by user ..." }
这种结构的字段可以很容易地映射到例如 SQL表列名,这意味着查找可以更具体/更细粒度:
SELECT user, text FROM logs WHERE error_type = "disk";
您可以在希望经常搜索/查找其值的列上放置索引,只要您不对
LIKE
这些列值使用子句即可。您可以将日志消息细分为特定类别的内容越多,查找的对象就越有针对性。例如,除了error_type
上面示例中的字段/列之外,您甚至可以设置为be"error_category": "disk", "error_type": "quota"
或诸如此类。结构越多,你的日志消息,通过解析/检索系统(如
fluentd
,elasticsearch
,kibana
),可以利用该结构,并以更快的速度和更低的CPU /内存执行任务。总之这不仅与速度和效率有关,更重要的是使用结构化日志记录和“结构化查询”时,能以特定格式捕获以及呈现结构化日志,同时提供对开发者与程序友好的解析支持。可以更方便地以其为条件进行筛选,搜索结果的相关性将更高。如果没有这种搜索,那么在不同上下文中出现的任何单词都会给您带来大量无关的点击。
四、开始
为了更好的理解认识Serilog,我们这简单的创建一个新的项目来认识一下Serilog的使用。这里我们就简单的使用Console
和Debug
的方式来实现,后续有机会我们可以实现更多方式的接收器写入日志。
4.1 Serilog使用
4.1.1 安装依赖包
Serilog.AspNetCore : 基于AspNetCore框架整合的Serilog日志记录程序包,包含了Serilog基本库和控制台日志的实现。
当然了,你也可以直接安装Serilog 基本库,然后根据需要安装对应的拓展包。
说明:
- Serilog.Extensions.Logging 包含了注入了Serilog的拓展方法。
- Serilog.Sinks.Async 实现了日志异步收集。
- Serilog.Sinks.Console 实现了控制台输出日志。
- Serilog.Sinks.Debug 实现了调试台输出日志。
- Serilog.Sinks.File 实现了文件输出日志。
4.1.2 配置Serilog
在应用程序中Program.cs
文件中,配置Serilog记录,确保正确记录任何配置日志问题。
public static void Main(string[] args) { Log.Logger = new LoggerConfiguration() .MinimumLevel.Debug() .MinimumLevel.Override("Microsoft", LogEventLevel.Information) .Enrich.FromLogContext() .WriteTo.Console() .CreateLogger(); try { Log.Information("Starting web host"); CreateHostBuilder(args).Build().Run(); } catch (Exception ex) { Log.Fatal(ex, "Host terminated unexpectedly"); } finally { Log.CloseAndFlush(); } }
然后,添加UseSerilog()
到CreateHostBuilder()
中的通用主机中。
public static IHostBuilder CreateHostBuilder(string[] args) => Host.CreateDefaultBuilder(args) //从appsettings.json中读取配置。 .UseSerilog() // <-- Add this line .ConfigureLogging((hostingContext, logging) => { logging.ClearProviders(); //去掉默认添加的日志提供程序 }) .ConfigureWebHostDefaults(webBuilder => { webBuilder.UseStartup<Startup>(); });}
最后,通过删除默认记录器的其余配置进行清理,从appsettings.json
文件中删除Logging
对应的配置部分。可以再使用根据Serilog
的配置规则进行相应配置替换它。
"Serilog": {"MinimumLevel": {"Default": "Information","Override": {"Microsoft": "Warning","System": "Warning"}}}
4.1.3 提示
当在IIS下运行时候,要在Visual Studio输出窗口中查看Serilog输出日志的时候,需要将输出方式选择为 Web 服务器方式,输出窗口查看日志,或者使用WriteTo.Debug()
替换记录器配置中的WriteTo.Console()
。
4.2 输出格式
4.2.1 文本格式
作为文本,它的格式如下:
[21:45:15 INF] HTTP GET / responded 200 in 227.3253 ms
测试在控制台中输出如下: