Serilog 源码解析——解析字符串模板

大家好啊,上一篇中我们谈到 Serilog 是如何决定日志记录的目的地的,那么从这篇开始,我们着重于 Serilog 是向 Sinks 中记录什么的,这个大功能比较复杂,我尝试再将其再拆分成几个小块方便大家理解。(系列目录)

本篇要解决什么

之前提到,在Logger类中构造对应的LogEvent对象之前,日志记录器通过MessageTemplateProcessor类对象的Process方法处理字符串模板和传入进来的数据信息。这个方法内部只是做了两件事:

解析消息模板,分析哪些是字符串字面值哪些是需要转换的属性值

构造相关的数据对象

public void Process(string messageTemplate, object[] messageTemplateParameters, out MessageTemplate parsedTemplate, out EventProperty[] properties) { parsedTemplate = _parser.Parse(messageTemplate); // 第一件事 properties = _propertyBinder.ConstructProperties(parsedTemplate, messageTemplateParameters); // 第二件事 }

这篇文章主要分析第一件事的处理方法。之后将对应的数据与模板信息绑定内容则放在下一篇中。

MessageTemplate类

在分析如何处理之前,需要弄明白这个功能函数的输入是什么,输出是什么,在对生成什么东西有一定了解后,才能更加方便了解其运行机理。这里,在第一行代码可以发现,输入是一个字符串,而输出则是一个MessageTemplate类对象。因此,有必要对MessageTemplate类深入研究。MessageTemplate类保存在 Core 文件夹下,和LogEvent类一样,都是保存数据而用。这也就说明,MessageTemplate也是LogEvent中的一个属性,表明它是日志事件数据中的一部分。

MessageTemplate类中有很多的属性和方法,这里仅考虑一些较为重要的属性。

public class MessageTemplate { public string Text { get; } readonly MessageTemplateToken[] _tokens; internal ProertyToken[] NamedProperties { get; } internal ProertyToken[] PositionalProerties { get; } ... }

Text属性不用多说,该值为传入的字符串模板数据。接下来是MessageTemplateToken对象,该对象描述的是模板解析的结果,主要包含两类 Token,一个是文本 Token,即TextToken类,它描述的是模板中的文本信息,另一个是属性 Token,即PropertyToken类,描述的是模板内需要替换的属性数据名。这些类均是描述解析后的结果信息,且类文件均位于在 Parsing 文件夹中,且都继承于MessageTemplateToken类。在MessageTemplate类中,通过引用MessageTemplateToken数组来达到保有模板解析的结果信息。从变量名上可以发现,MessageTemplate类对象内所拥有的NameProperties和PositionProperties均描述一组属性 Token,二者的区别在于:前者描述的是具名的属性Token,该Token在字符串中具有具体的名字;后者描述的是位置的属性Token,即它在字符串模板中以位置数据出现。

举个例子,如果字符串模板为版本{version},那么其中版本就是文本 Token,version是具名属性 Token;如果字符串模板为版本{0},那么0则是位置的属性Token,它表示使用后续第一个值作为它的数据。

MessageTemplateToken类及其继承类

前面提到了 Token 这一描述结果的类型,接下来就是看描述这些 Token 是如何实现自己的功能的。

作为描述字符串解析结果的基类MessageTemplateToken,它主要包含两大属性,StartIndex描述该Token在字符串模板中的起始位置,Length描述该Token的长度。另外,这个类是一个抽象类,不允许直接实例化该类。

public abstract class MessageTemplateToken { public int StartIndex { get; } public abstract int Length { get; } }

接下来是文本 Token,即TextToken类。这个类非常简单,既然文本 Token 只描述模板中的文本部分,它只需要包含描述文本的Text属性,其长度也就被设置为文本的长度。

public sealed class TextToken : MessageTemplateToken { public string Text { get; } public override int Length => Text.Length; }

之后是属性 Token,即PropertyToken类。

public sealed class PropertyToken : MessageTemplateToken { readonly string _rawText; readonly int? _position; public override int Length => _rawText.Length; public string PropertyName { get; } public Destructuring Destructuring { get; } public string Format { get; } public Alignment? Alignment { get; } public bool IsPositional => _position.HasValue; }

从上面的代码可以看出来,该类要比TextToken复杂。这里一个个来分析:_rawText变量顾名思义,表示字符串模板中属性字符串,通常为花括号所括起来的部分。position作为一个可空int型数据,描述该属性Token的位置,这里只有位置的属性Token才有该值,具名的属性Token该值为空,二者的从IsPositional属性来区分。Length表示原始字符串的长度。PropertyName属性记录的是属性 Token 的名字。而Destructuring属性指明该属性值应该如何渲染(模板中的变量采用$还是@渲染,即采用数据本身类的ToString方法还是将数据对象解构再渲染),Format指明输出的格式化字符串,Alignment属性指明对其的方式,默认左对齐,通过设置可以让日志右对齐。举个例子,比如字符串模板为{version: 000},那么其_rawText值为{version: 000}, _position为null, Length为14,PropertyName为version,Destructuring值为Default,Format值为000,Alignment为默认值null,IsPositional为false。

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:https://www.heiqu.com/wsppzp.html