最新Serilog源码解析——使用方法(1),Java全套视频

感受:

其实我投简历的时候,都不太敢投递阿里。因为在阿里一面前已经过了字节的三次面试,投阿里的简历一直没被捞,所以以为简历就挂了。

特别感谢一面的面试官捞了我,给了我机会,同时也认可我的努力和态度。对比我的面经和其他大佬的面经,自己真的是运气好。别人8成实力,我可能8成运气。所以对我而言,我要继续加倍努力,弥补自己技术上的不足,以及与科班大佬们基础上的差距。希望自己能继续保持学习的热情,继续努力走下去。

也祝愿各位同学,都能找到自己心动的offer。

分享我在这次面试前所做的准备(刷题复习资料以及一些大佬们的学习笔记和学习路线),都已经整理成了电子文档

拿到字节跳动offer后,简历被阿里捞了起来,二面迎来了P9"盘问"

本文已被CODING开源项目:【一线大厂Java面试题解析+核心总结学习笔记+最新讲解视频+实战项目源码】收录

需要这份系统化的资料的朋友,可以点击这里获取

log.Information(“Hello world.”);

log.Error(“Hello world, again.”);

如果大家对之前的 Demo 了解的话,那么会觉得这段代码非常的熟悉。LoggerConfiguration类似于先前的LogBuilder,通过CreateLogger函数来创建对应的日志记录器,调用Console和File函数类似于先前的AddConsole以及AddFile方法,其输入参数的个数和形式都完全一样。但不同的是这些函数在WriteTo对象上调用而不是LoggerConfiguration上做调用,这一点和先前的 Demo 不一样,不过这一点并不影响我们的理解。再往后,日志的记录是通过Information和Error函数来调用,而LogDemo 中采用LogInformation和LogError函数记录日志。

Serilog源码解析——使用方法

上图显示的是 Serilog 向控制台中记录的日志信息,可以看到,其最终记录的日志信息和 LogDemo 差不多,均是日志时间+等级+消息。

2. Serilog 向 Sink 中具体写入了什么?#

=================================

在之前的 LogDemo 中,我们一直认为日志消息本质上就是一字符串。将日志记录下来就是将相关信息组合成字符串并写入到对应 Sink 中,这是非结构化日志记录库常用的做法。然而,这种使用方式有两个问题:

2.1 日志消息不能附带数据。日志消息附带数据有非常多的好处,比如说,如果类库具有自动解析数据的能力,那么我们只需要给出数据以及带有插入位置的消息字符串模板,就可以由类库自行构造对应的日志。在 Serilog 中,这种带有数据的日志消息被称为日志事件,它包含了待解析的字符串模板以及需要渲染的数据。

下面就取官网的例子做说明吧。可以看出,position是一个匿名类对象,它包含经度和纬度两个值。在使用日志记录的时候,向函数中传入三个参数,第一个是字符串模板,后两个是数据。其格式遵循Message Template定义,字符串模板的写法非常像C#中的 开头的字符串,字符串采用 来标记数据的位置,采用 : 分割变量名和数据输出格式,二者的区别几乎只有开头有没有 开头的字符串,字符串采用{}来标记数据的位置,采用:分割变量名和数据输出格式,二者的区别几乎只有开头有没有 开头的字符串,字符串采用来标记数据的位置,采用:分割变量名和数据输出格式,二者的区别几乎只有开头有没有。另外,在字符串模板中的变量名部分,还可以使用@和 来决定数据的渲染方法(即如何将数据内容写入到字符串中)。 @ 采用的是解构方法,将内部内容取出直到基本类型后写入, 来决定数据的渲染方法(即如何将数据内容写入到字符串中)。@采用的是解构方法,将内部内容取出直到基本类型后写入, 来决定数据的渲染方法(即如何将数据内容写入到字符串中)。@采用的是解构方法,将内部内容取出直到基本类型后写入,则是直接调用数据的ToString方法渲染。在下例中,position采用结构的方式渲染,而整数elapseMs利用000格式化字符串控制其渲染方法。

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

var elapsedMs = 34;

var log = new LoggerConfiguration()

.WriteTo.Console()

.CreateLogger();

log.Information(“Processed {@Position} in {Elapsed:000} ms.”, position, elapsedMs);

// 输出: [20:54:34 INF] Processed {“Latitude”: 25, “Longitude”: 134} in 034 ms.

2.2 不同的人有不同的日志记录方式。举个例子,日志所包含的时间、等级和消息不同的人希望采用不同的格式输入。可以发现,在之前 Demo 中,通过LogData类给定的Tostring()方法转化不利于不同人的定制化需求。

对于这个需求,如果大家对前一个问题充分了解的话,可以发现,日志事件、日志等级以及日志消息都可以算是日志事件中的数据,我们可以通过设置输出模板(output template)达到。实际上,在 Serilog 中,大部分 Sink 都提供了一个默认的输出模板,通过提供自定义的输出模板可以达到日志信息定制化的目的。

var log = new LoggerConfiguration()

.WriteTo.Console(outputTemplate: “({Timestamp:HH:mm:ss}/{Level}) {Message:lj}{NewLine}{Exception}”)

.CreateLogger();

log.Information(“Hello world.”);

// 输出: (21:22:14/Information) Hello world.

这里通过设置outputTemplate输入参数来控制日志的输出格式。输出模板的改变会导致日志的输出内容发生变化,但可以看到其数据内容是一样的。

2.3 在完成前两个需求后,通过结合这二者,我们可以提供一个新的功能。即向日志中添加其他的自定义数据并将其渲染到日志中。

这个功能非常的方便,比如说,我们在日志记录的时候还需要记录当前上下文的用户名称。一种简单的办法是将用户名称放在消息字符串中,但这样处理的方法会在每次记录一条日志都需要手动填写相关数据和模板。更好的一种操作是,将用户名称这个数据项放在日志事件中,就像日志时间和等级一样,在合适的位置自动记录而不是每次调用。

var log = new LoggerConfiguration()

.Enrich.WithProperty(“User”, “Dave”)

.WriteTo.Console(outputTemplate: “[{Timestamp:HH:mm:ss} {Level:u3}] [{User}] {Message:lj}{NewLine}{Exception}”)

.CreateLogger();

log.Information(“Hello world.”);

// 输出: [21:44:04 INF] [Dave] Hello world.

在 Serilog 中,这种向日志事件中添加数据的行为叫 Enrichment,对应数据对象是 Enricher。Enrichment 是 Serilog 在数据方面一个具有强大扩展性的功能,通过向 Serilog 日志记录时塞入新数据,并在日志模板中进行使用,可以大大降低了调用时代码的重复性,且减少了遗漏的可能。比如说,有的人希望每次日志记录都记录当前运行的线程信息、进程信息以及环境变量数据等,通过添加相应的 Enricher 可以达到无需过多关心这些值而直接记录。甚至,像这些较为常用的 Enrichers,官方组织早已给出了相应的扩展包:

  • Serilog.Enrichers.Thread:附带当前处理的进程信息

  • Serilog.Enrichers.Process:附带当前处理的线程信息

  • Serilog.Enrichers.Environment:附带当前的环境信息

3. Serilog 该不该记录这条日志#

==========================

对于一条日志记录,很多时候,我们并不是要求每条都记录下来,往往是需要丢弃一些日志。这看起来似乎挺反直觉的,数据是重要的,不应该隐式地丢弃某些数据,但是,在实际应用中这样的需求确实是合理的,有时候我们仅希望记录最为重要的日志而不是全部的日志信息。

日志的过滤有两种形式,一种是在将日志记录到各个 Sink 前需要过滤一遍,这种通常是全局过滤。另一种则是每个 Sink 对象有其自己的过滤方式,通常是局部过滤。这里通过两个例子说明。

全局过滤#

=========

全局过滤应用场景是日志记录器会记录海量的日志,通常大部分是等级非常低的日志,这类日志往往在开发时候有用,运行期间不应该再输出出来。这种情况下,我们需要设置最小日志输出等级为Information即可。其使用方式如下:

var log = new LoggerConfiguration()

.WriteTo.Console()

.MinimumLevel.Information()

.CreateLogger();

log.Debug(“Test here.”); // 没有任何输出

另外,全局的过滤条件也可以很复杂,甚至我们可以将之前的 Enricher 结合在一起,比如说,在原先带有用户名的 Enricher 中,我们希望只记录用户名为 Lily 的日志,其他用户名都不记录。这里ForContext也是一种添加 Enricher 的方法,和之前不同的是,它将 Enricher 添加到的Logger中,即所添加的数据只有log1和log2有,log中并没有。

var log = new LoggerConfiguration()

.Filter.ByIncludingOnly(Matching.WithProperty(“User”, “Lily”)) // 添加过滤条件

.WriteTo.Console(outputTemplate: “[{Timestamp:HH:mm:ss} {Level:u3}] [{User}] {Message:lj}{NewLine}{Exception}”)

.CreateLogger();

var log1 = log.ForContext(“User”, “Lily”);

log1.Information(“Log successed.”); // 输出

var log2 = log.ForContext(“User”, “Dave”);

log2.Information(“Log failed.”); // 不输出

局部过滤#

=========

同样地,Serilog 允许我们将日志的过滤条件从全局设置缩小到对某个 Sink 的过滤,即只有指定的 Sink 具有过滤日志的功能。

var log = new LoggerConfiguration()

.WriteTo.Console(restrictedToMinimumLevel: LogEventLevel.Debug)

.WriteTo.File(“log.txt”)

.CreateLogger();

log.Information(“Hello world.”); // 在Console中没有记录,在File中被记录

4. Serilog中的日志记录器的配置#

==========================

从上述几个问题中可以看到,所有的日志记录器都是通过LogConfiguration对象的相关属性执行配置后通过CreateLogger方法而生成的。因为在之前都已经或多或少提到,这里就不细谈了。值得一提的是 Serilog 不光提供了代码文本的设置方法(即通过调用某个函数执行设置),还提供了一套通过配置字符串的设置方法,这种方法较为动态,且不需要写固定的代码流程而只需要提供相关配置文件,具体处理流程在后续再提。

Serilog 源码准备#

=================

好了,终于开始接触 Serilog 的源码了。这部分主要准备好源码,以便为了后续的学习。

读者福利

由于篇幅过长,就不展示所有面试题了,感兴趣的小伙伴

35K成功入职:蚂蚁金服面试Java后端经历!「含面试题+答案」

35K成功入职:蚂蚁金服面试Java后端经历!「含面试题+答案」

35K成功入职:蚂蚁金服面试Java后端经历!「含面试题+答案」

更多笔记分享

35K成功入职:蚂蚁金服面试Java后端经历!「含面试题+答案」

本文已被CODING开源项目:【一线大厂Java面试题解析+核心总结学习笔记+最新讲解视频+实战项目源码】收录

需要这份系统化的资料的朋友,可以点击这里获取

…(img-F7F9aASU-1715647385834)]

更多笔记分享

[外链图片转存中…(img-OwxFfRNW-1715647385835)]

本文已被CODING开源项目:【一线大厂Java面试题解析+核心总结学习笔记+最新讲解视频+实战项目源码】收录

需要这份系统化的资料的朋友,可以点击这里获取

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值