首页 > 其他分享 >那些年我们用过的组件-结构化日志组件 Serilog

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

时间:2023-01-05 10:57:26浏览次数:60  
标签:log Serilog 组件 日志 true Log

什么是结构化日志

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

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

  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 日志记录

标签:log,Serilog,组件,日志,true,Log
From: https://www.cnblogs.com/newton/p/17026903.html

相关文章

  • 自定义注解+面向切面 日志记录
    自定义注解packagecom.example.spring.controller;​importjava.lang.annotation.*;​@Target(value=ElementType.METHOD)@Retention(RetentionPolicy.RUNTIME)@Docum......
  • ExtJS-UI组件-TreePanel
    ExtJS教程汇总:https://www.cnblogs.com/cqpanda/p/16328016.html转载请注明出处:https://www.cnblogs.com/cqpanda/p/16587500.html更新记录2023年1月2日从笔记迁移到......
  • 6.日志
    1.日志工厂通过使用内置的日志工厂提供日志功能。  可以通过在MyBatis配置文件mybatis-config.xml里面添加一项setting来选择日志实现。2.STDOUT_LOGGING......
  • 日志信息和日志等级
     (1)日志级别:CRITICAL:严重错误ERROR:一般错误WARNING:警告INFO:一般信息DEBUG:调试信息默认的日志等级是DEBUG只要出现了DEBUG或者DEBUG以上等级的日志那么这些日......
  • jango框架:forms组件渲染标签、forms组件展示信息、forms组件校验补充、forms组件参数
    目录forms组件渲染标签forms组件展示信息forms组件校验补充钩子函数forms组件参数补充forms组件源码剖析modelform组件基本使用classMeta下常用参数save()方法forms组件......
  • 2022.1.4 营业日志
    感觉阳了之后没完全好啊,非常想睡觉。以后这个东西把三天放在一起吧,感觉每天都更有点多的(P3488[POI2009]LYZ-IceSkatesDescription给一张二分图,其中左边第\(i\)个点......
  • 告警日志中报错ORA-07445 kkqstcrf
    问题描述:数据库例行巡检时发现告警日志中报错ORA-07445kkqstcrf,如下所示:数据库:oracle11.2.0.1告警日志:SunDec2522:08:222022BeginautomaticSQLTuningAdvisorrun......
  • 组件,父子组件的传值
    组件化开发组件化开发,指的是,根据封装的思想,把页面上,可以重用的部分分装成为组件,从而更方便项目开发和维护一个页面,可以拆分成一个个组件,一个个组件是一个整体,每个组件可......
  • MongoDB数据的导出导入及日志分析
    一、远程连接导出报错超时mongodump-h10.110.63.150:27017-u'admin'-p'passwd!'--authenticationDatabaseflowtest--dbflowtest-o/home/mongod/bak>mongodump......
  • Vue学习-基础-尚硅谷vue-todolist学习-组件开发入门
    1,编写静态组件 :抽取组件,使用组件实现静态页面效果   这里组件中的component   style  部分使用的静态页面粘贴过来的  , 这里的MyItem是MyList......