一、前言

对内置日志系统的整体实现进行了介绍之后,可以通过使用内置记录器来实现日志的输出路径。而在实际项目开发中,使用第三方日志框架(如: Log4NetNLogLoggrSerilogSentry 等)来记录也是非常多的。首先一般基础的内置日志记录器在第三方日志框架中都有实现,然后第三方日志框架在功能上更加强大和丰富,能满足我们更多的项目分析和诊断的需求。

所以在这一篇中,我们将介绍第三方日志记录提供程序——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"或诸如此类。

结构越多,你的日志消息,通过解析/检索系统(如fluentdelasticsearchkibana),可以利用该结构,并以更快的速度和更低的CPU /内存执行任务。

总之这不仅与速度和效率有关,更重要的是使用结构化日志记录和“结构化查询”时,能以特定格式捕获以及呈现结构化日志,同时提供对开发者与程序友好的解析支持。可以更方便地以其为条件进行筛选,搜索结果的相关性将更高。如果没有这种搜索,那么在不同上下文中出现的任何单词都会给您带来大量无关的点击。

四、开始

为了更好的理解认识Serilog,我们这简单的创建一个新的项目来认识一下Serilog的使用。这里我们就简单的使用ConsoleDebug的方式来实现,后续有机会我们可以实现更多方式的接收器写入日志。

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

测试在控制台中输出如下:

上述事件格式中,可以看出由以下几个格式组成:

  • 事件发生时的时间戳[timestamp]
  • 描述何时应该捕获事件的级别[level]
  • 记录事件的消息[message]内容]
  • 描述事件的命名属性[properties]
  • 还可能有一个Exception对象

4.2.2 JSON格式

作为JSON格式,它的格式如下:

{
"@t": "2020-08-27T13:59:44.6410761Z",
"@mt": "HTTP {RequestMethod} {RequestPath} responded {StatusCode} in {Elapsed:0.0000} ms",
"@r": ["224.5185"],
"RequestMethod": "GET",
"RequestPath": "/",
"StatusCode": 200,
"Elapsed": 224.5185,
"RequestId": "0HLNPVG1HI42T:00000001",
"CorrelationId": null,
"ConnectionId": "0HLNPVG1HI42T"
}

在写入日志文件中,根据Serilog的多种接收器的中(Console()、Debug()、File())等支持使用JSON写入日志记录,通过引用紧凑的JSON格式化类库[Serilog.Formatting.Compact]接收所有JSON格式的输出。

要编写以换行符分隔的JSON,请将CompactJsonFormatterRenderedCompactJsonFormatter传递到接收器配置方法,如下:

 .WriteTo.Console(new RenderedCompactJsonFormatter())

.WriteTo.Console(new CompactJsonFormatter())

运行这个程序将产生使用Serilog的紧凑格式JSON,并在对应的输出路径中生成换行符分隔的JSON流。

4.3 示例

4.3.1 安装依赖包

安装 Serilog.AspNetCore NuGet 包 ;

4.3.2 配置文件

appsettings.json配置文件添加 Serilog 配置,WriteTo 指定输出目标位置,它是一个数组类型,所以可以指定多个目标位置,这里暂时只指定输出到控制台:

{
"Serilog": {
"MinimumLevel": {
"Default": "Debug"
}
}
}

4.3.3 设置配置信息

读取配置文件信息,设置配置信息

public static IConfiguration Configuration { get; } = new ConfigurationBuilder()
.SetBasePath(Directory.GetCurrentDirectory())
.AddJsonFile("appsettings.json", optional: false, reloadOnChange: true)
.AddJsonFile($"appsettings.{Environment.GetEnvironmentVariable("ASPNETCORE_ENVIRONMENT") ?? "Production"}.json", optional: true)
.AddEnvironmentVariables()
.Build();

在main方法中,

 public static void Main(string[] args)
{
Log.Logger = new LoggerConfiguration()
.ReadFrom.Configuration(Configuration)
.Enrich.FromLogContext()
.WriteTo.Debug() //输出路径
.WriteTo.Console(
outputTemplate: "[{Timestamp:HH:mm:ss} {Level:u3}] {Message:lj} {Properties:j}{NewLine}{Exception}") //模板
.CreateLogger();
try
{
Log.Information("Starting web host");
CreateHostBuilder(args).Build().Run();
}
catch (Exception ex)
{
Log.Fatal(ex, "Host terminated unexpectedly");
}
finally
{
Log.CloseAndFlush();
}
}

Program.cs 添加 UseSerilog()

public static IHostBuilder CreateHostBuilder(string[] args) =>
Host.CreateDefaultBuilder(args)
.ConfigureWebHostDefaults(webBuilder =>
{
webBuilder.UseStartup<Startup>();
})
.UseSerilog(); //添加

4.3.4 设置请求管道

在 Startup.cs 的 中的Configure 请求管道中添加 UseSerilogRequestLogging

public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
app.UseStaticFiles();
app.UseSerilogRequestLogging(); app.UseRouting(); app.UseAuthorization(); app.UseEndpoints(endpoints =>
{
endpoints.MapControllers();
});
}

重要的是UseSerilogRequestLogging()调用应出现在诸如MVC之类的处理程序之前。 中间件不会对管道中出现在它之前的组件进行时间或日志记录。通过将UseSerilogRequestLogging() 放在它们之后,可以将其用于从日志中排除杂乱的处理程序,例如UseStaticFiles()。)

为了减少每个HTTP请求需要构造,传输和存储的日志事件的数量。 在同一事件上具有许多属性还可以使请求详细信息和其他数据的关联更加容易。

默认情况下,以下请求信息将作为属性添加:

  • 请求方法

  • 请求路径

  • 状态码

  • 响应时间

您可以使用UseSerilogRequestLogging()上的选项回调来修改用于请求完成事件的消息模板,添加其他属性或更改事件级别:

app.UseSerilogRequestLogging(options =>
{
// 自定义消息模板
options.MessageTemplate = "Handled {RequestPath}";
// 发出调试级别的事件,而不是默认事件
options.GetLevel = (httpContext, elapsed, ex) => LogEventLevel.Debug;
//将其他属性附加到请求完成事件
options.EnrichDiagnosticContext = (diagnosticContext, httpContext) =>
{
diagnosticContext.Set("RequestHost", httpContext.Request.Host.Value);
diagnosticContext.Set("RequestScheme", httpContext.Request.Scheme);
};
});

4.3.5 输出效果

由于日志总是输出一堆,我们不能快速的查找定位问题,其实 Serilog 输出的日志是非常简洁的,只有 HTTP GET ... 这一条,其他都是 AspNetCore 系统本身输出的,所以我们可以对输出的日志进行简化操作。

4.3.6 输出简化

为了使日志输出更简洁,我们可以设置不输出 AspNetCore Info 日志,只需在 Serilog配置节点中设置 AspNetCore 日志输出级别为 Warning

{
"Serilog": {
"MinimumLevel": {
"Default": "Debug",
"Override": {
"Microsoft": "Warning",
"System": "Warning"
}
}
}
}

五、总结

  1. 本篇主要是对Serilog的说明,认识到是一个基于日志事件的而非日志消息的结构化日志类库。
  2. 简单的涉及对基础知识的认识以及使用,通过构建一个新的项目来实现Serilog的日志记录以及怎么使用这个框架。
  3. 在后续中如何结合这个日志类库引入项目中使用,以及对日志怎么存储和查询进行说明(会考虑 ELK存储采集分析 )。
  4. 如果有不对的或不理解的地方,希望大家可以多多指正,提出问题,一起讨论,不断学习,共同进步。
  5. 参考资料: 官方简介Serilog文档serilog-aspnetcore
  6. 本文源码下载地址

基于.NetCore3.1系列 —— 日志记录之初识Serilog的更多相关文章

  1. 基于.NetCore3.1系列 —— 日志记录之日志配置揭秘

    一.前言 在项目的开发维护阶段,有时候我们关注的问题不仅仅在于功能的实现,甚至需要关注系统发布上线后遇到的问题能否及时的查找并解决.所以我们需要有一个好的解决方案来及时的定位错误的根源并做出正确及时的 ...

  2. 基于.NetCore3.1系列 —— 日志记录之自定义日志组件

    一.前言 回顾:日志记录之日志核心要素揭秘 在上一篇中,我们通过学习了解在.net core 中内置的日志记录中的几大核心要素,在日志工厂记录器(ILoggerFactory)中实现将日志记录提供器( ...

  3. 基于.NetCore3.1系列 —— 日志记录之日志核心要素揭秘

    一.前言 在上一篇中,我们已经了解了内置系统的默认配置和自定义配置的方式,在学习了配置的基础上,我们进一步的对日志在程序中是如何使用的深入了解学习.所以在这一篇中,主要是对日志记录的核心机制进行学习说 ...

  4. 基于.NetCore3.1系列 ——认证授权方案之Swagger加锁

    一.前言 在之前的使用Swagger做Api文档中,我们已经使用Swagger进行开发接口文档,以及更加方便的使用.这一转换,让更多的接口可以以通俗易懂的方式展现给开发人员.而在后续的内容中,为了对a ...

  5. 基于.NetCore3.1系列 —— 认证授权方案之授权揭秘 (下篇)

    一.前言 回顾:基于.NetCore3.1系列 -- 认证授权方案之授权揭秘 (上篇) 在上一篇中,主要讲解了授权在配置方面的源码,从添加授权配置开始,我们引入了需要的授权配置选项,而不同的授权要求构 ...

  6. 记一次基于springboot+aop实现日志记录实战

    1. 为什么要记录日志 好处: a. 可以对一些重要功能进行记录,方便以后跟踪是谁操作此功能的. b. 在操作某些功能时可能会发生异常,但每次出现异常我们想定位日志都要去服务器查看我们的日志.有了日志 ...

  7. .NET Core微服务之基于Exceptionless实现分布式日志记录

    Tip: 此篇已加入.NET Core微服务基础系列文章索引 一.Exceptionless极简介绍 Exceptionless 是一个开源的实时的日志收集框架,它可以应用在基于 ASP.NET,AS ...

  8. 七、.net core(.NET 6)使用Serilog进行配置和实现日志记录

    使用Serilog来实现日志记录 先安装Serilog六件套神装包: 也可以对个别相应的包进行删除等,都是可以的.例如,标注的1是读取配置文件的,如果不需要通过配置文件进行操作,就可以使用这个包.2是 ...

  9. 基于SqlSugar的开发框架循序渐进介绍(8)-- 在基类函数封装实现用户操作日志记录

    在我们对数据进行重要修改调整的时候,往往需要跟踪记录好用户操作日志.一般来说,如对重要表记录的插入.修改.删除都需要记录下来,由于用户操作日志会带来一定的额外消耗,因此我们通过配置的方式来决定记录那些 ...

随机推荐

  1. 5.19 省选模拟赛 小B的夏令营 概率 dp 前缀和优化dp

    LINK:小B的夏令营 这道题是以前从没见过的优化dp的方法 不过也在情理之中. 注意读题 千万不要像我这个sb一样 考完连题意都不知道是啥. 一个长方形 要求从上到下联通的概率. 容易发现 K天只是 ...

  2. 5.12 省选模拟赛 T2 贪心 dp 搜索 差分

    LINK:T2 这题感觉很套路 但是不会写. 区间操作 显然直接使用dp不太行 直接爆搜也不太行复杂度太高. 容易想到差分 由于使得整个序列都为0 那么第一个数也要i差分前一个数 强行加一个0 然后 ...

  3. luogu P4321 随机漫游 期望dp 二进制 高斯消元

    LINK:随机漫游 非常妙的一道题. 容易想到倒推期望. 设状态 f[i][j]表示到达第i个点 此时已经到达的集合为j能走到全集的期望边数. 只要求出来这个就能O(1)回答询问. \(f[i][j] ...

  4. ABC 162 F Select Half dp 贪心

    LINK:Select Half 考试的时候调了一个小时给调自闭了 原来是dp的姿势不太对. 首先 容易发现 奇数最多空2个位置 偶数最多空1一个位置 然后 设f[i][j][k]表示第i个数选了没有 ...

  5. Qt 之 Graphics View Framework 简介

    Graphics View Framework 交互式 2D 图形的 Graphics View 框架概述.自 Qt4.2 中引入了 Graphics View,以取代其前身 QCanvas.Grap ...

  6. 2017面向对象程序设计(Java)第五周工作总结

    时光如逝,岁月如梭,不知不觉已经开学五个星期了.在代老师的带领下,我们一步一步走近Java,也渐渐的适应了翻转课堂的个性化教学,此时此刻相信同学们对Java也有了更加深入的了解.下面我对第五周的助教工 ...

  7. 打开IDEA后tomcat不能用,Cannot load project of unknown project type,无法加载类或者项目

    这一问题在网络中有比较统一的解决方法,我这个也是按这个方法解决的. 问题出现的前提和原因: 一个运行正常项目,我关闭后第二天打开发现tomcat不能用了. 解决方法: 我查了一下,这是一个IDEA软件 ...

  8. Android Studio--家庭记账本(一)

    今天通过观看视频,根据老师所讲内容,编译代码.实现了Android Studio记账本里面的增加功能 源代码如下: CostBean.java: package com.example.family; ...

  9. Django Web 测试

    Django 单元测试 模拟浏览器发起请求,测试 web 功能.只是简单记录一下怎么使用. 环境 Win10 Python2.7 Django 1.8.11 MySQL5.6 项目结构 大致如下 my ...

  10. CSDN新版Markdown编辑器(Alpha 2.0版)

    Markdown编辑器 欢迎使用Markdown编辑器 新的改变 功能快捷键 合理的创建标题,有助于目录的生成 如何改变文本的样式 插入链接与图片 如何插入一段漂亮的代码片 生成一个适合你的列表 创建 ...