gpt4 book ai didi

那些年我们用过的组件-结构化日志组件Serilog

转载 作者:我是一只小鸟 更新时间:2023-01-05 14:31:32 26 4
gpt4 key购买 nike

什么是结构化日志

我们记录日志惯常使用 log4j2 、 NLog 等日志组件,这些组件提供了输出到多种终端的能力,但是大部分时候我们选择将日志输出到操作系统的文件系统中,为什么呢?至少有一部分原因是记录的每条日志为字符串格式,且按时间由远往进顺序记录,打开文件可以直接人肉检索;如果这些日志记录到其它终端比如数据库中,由于是字符串格式,无法依靠数据库的机制提高检索效率,反而日志的频繁写入和数据量的持续增大,对数据库造成很大压力,还需要花时间调优数据库结构.

但 22 世纪都快到了,还在用古老的人肉检索实在说不过去,于是出现了流行一时的 EFK 、 ELK 框架,它们是几个组件的集合。大致流程如下:

  1. 首先是日志采集组件比如 filebeats ,定时从配置好的路径中采集增量日志;
  2. 上传到消息队列比如 kafka ,缓解日志过多时的传输压力;
  3. 然后送达日志处理组件比如 logstash , logstash 使用 filter 对日志进行拆分、映射、过滤等,抽取关键内容并形成符合目标数据库特性的格式。注意此处出来的就是 结构化日志 ;
  4. 将结构化日志存储到特定的数据库比如 elasticsearch 中;
  5. 通过用户界面如 Kibana 进行日志检索。

上述流程在不同场景下有一些变种,不再赘述。 它们的主要目的就是使得传统的文件日志可以被计算机高效检索.

那么有没有一种可能,跳过文件存储,直接将日志按特定格式写入到目标存储容器,可能是 elasticsearch,也可能是 mysql ,甚至是文件系统。同样代码,输出不同的格式到不同的终端,同时满足 human-friendly and machine-readable .

在 .NET 世界中, 本文的主角 Serilog 就可以帮我们省去那些弯弯绕绕,依靠它,记录与查询日志显得简单而纯粹.

Serilog

以官方例子说明:

                        
                          var position = new { Latitude = 25, Longitude = 134 };
var elapsedMs = 34;

log.Information("Processed {@Position} in {Elapsed} ms", position, elapsedMs);

                        
                      

按字面意思,最终会输出

                        
                          09:14:22 [INF] Processed {"Latitude": 25, "Longitude": 134} in 34 ms.

                        
                      

当 Serilog 将日志直接输出到文件系统或命令行时,结果是这样没错,其它日志组件也能做到(废话).

当输出到 MongoDB 时,结果就不一样了:

                        
                          { "Position": { "Latitude": 25, "Longitude": 134 }, "Elapsed": 34 }

                        
                      

Sink

Serilog 将输出目标称之为 sink ,不同的 sink 可以有各自的格式要求。其实原理很简单,输出到特定 sink 时,日志对象会先格式化处理(注意不是先生成字符串再格式化)。 Serilog.Formatting.Compact 就是格式化为 json 的类库,输出到 elasticsearch 还需要 Serilog.Formatting.Elasticsearch 。不过除非自定义 sink,这些我们都不用关心,使用时只要引入需要的 sink 类库即可.

使用

下面介绍在 .NET6 中使用 Serilog.

先引入 Serilog 类库和需要的 Sink 库比如这里的 Serilog.Sinks.File :

                        
                          <PackageReference Include="Serilog" Version="2.12.0" />
<PackageReference Include="Serilog.Sinks.File" Version="5.0.0" />

                        
                      

以通用宿主程序为例:

                        
                          IHost host = Host.CreateDefaultBuilder(args).Build();

// 配置并创建 logger 实例
var log = new LoggerConfiguration()
    .MinimumLevel.Warning()
    .WriteTo.File("log.txt", rollingInterval: RollingInterval.Day, fileSizeLimitBytes: 10485760, rollOnFileSizeLimit: true, retainedFileCountLimit: 100, buffered: true)
    .CreateLogger();

log.Information("Hello, Serilog!"); // 直接使用(可以创建多个实例使用)

Log.Logger = log;   // Serilog 并没有实例状态需要线程间维护,所以为了方便我们可以使用单例模式,将实例赋给全局静态属性
Log.Information("The global logger has been configured");   // 项目内任意其它地方均可使用

await host.RunAsync().ContinueWith(_=> Log.CloseAndFlush());    // app 退出时释放 logger 占用资源

                        
                      

如果想以 .NET 内置的方式调用 Serilog,对于通用宿主程序,须引入 Serilog.Extensions.Hosting ,其扮演适配器的角色,将 Serilog 自己的接口 Serilog.ILogger 转换为 Microsoft.Extensions.Logging.ILogger 使用。如果是 web 项目的话,引入的是 Serilog.AspNetCore ; .NET Core 1.0, 1.1 等版本需要引入的是 Serilog.Extensions.Logging .

更改后的版本如下:

                        
                          IHost host = Host
    .CreateDefaultBuilder(args)
    .UseSerilog()   // 新增该行
    .Build();

// ... 其余代码同上

                        
                      

另外,上述代码是直接硬编码配置 logger,更好的方式是通过 appsettings.json 配置 logger。首先引入 Serilog.Settings.Configuration ,然后在 appsettings.json 中移除默认的 Logging 配置节,替换为 Serilog 配置节如下:

                        
                          {
  "Serilog": {
    "Using": [ "Serilog.Sinks.File" ],
    "MinimumLevel": "Warning",
    "WriteTo": [
      {
        "Name": "File",
        "Args": {
          "path": "Logs/log.txt",
          "rollingInterval": "Day",
          "fileSizeLimitBytes": 10485760,
          "rollOnFileSizeLimit": true,
          "retainedFileCountLimit": 100,
          "buffered": true
        }
      }
    ]
  }
}

                        
                      

代码更改如下:

                        
                          IHost host = Host
    .CreateDefaultBuilder(args)
    .UseSerilog((ctx, config) => config
        .ReadFrom.Configuration(ctx.Configuration))
    .Build();

//以下注释
//var log = new LoggerConfiguration()
//    .MinimumLevel.Warning()
//    .WriteTo.File("log.txt", rollingInterval: RollingInterval.Day, fileSizeLimitBytes: 10485760, rollOnFileSizeLimit: true, retainedFileCountLimit: 100, shared: true, buffered: true)
//    .CreateLogger();
//Log.Logger = log;

await host.RunAsync(); //注释.ContinueWith(_ => Log.CloseAndFlush());

                        
                      

采用这种方式,Log.Logger 会隐式赋值,并在系统退出时自动释放资源.

参考资料

Docker+EFK 快速搭建日志收集系统 Message Templates .NET Worker Service 添加 Serilog 日志记录 。

最后此篇关于那些年我们用过的组件-结构化日志组件Serilog的文章就讲到这里了,如果你想了解更多关于那些年我们用过的组件-结构化日志组件Serilog的内容请搜索CFSDN的文章或继续浏览相关文章,希望大家以后支持我的博客! 。

26 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com