Serilog 源码解析——解析字符串模板 (2)

总的来说,MessageTemplate类描述字符串模板解析后的数据,自然也是LogEvent类中的一个重要属性。在MessageTemplate中,维护一组经解析后的MessageTemplateToken数组,不同的 Token 用不同的类来描述,即描述文本信息的TextToken以及描述属性信息的PropertyToken。

MessageTemplateCache类

在了解完数据的存储部分后,接下来需要弄清楚的就是处理生成这些数据类的行为类。在MessageTemplateProcessor类的Process函数中,负责处理字符串模板解析的是_parser字段,它属于MessageTemplateCache类。那么首先看下其内部的结构。

interface IMessageTemplateParser { MessageTemplate Parse(string messageTemplate); } class MessageTemplateCache : IMessageTemplateParser { readonly IMessageTemplateParser _innerParser; readonly object _templatesLock = new object(); readonly HashTable _templates = new HashTable(); public MessageTemplateCache(IMessageTemplateParser innerParser) { _innerParser = innerParser; } public MessageTemplate Parse(string messageTemplate) { ... // 第一步 var result = (MessageTemplate)_templates[messageTemplate]; if (result != null) return result; // 第二步 result = _innerParser.Parse(messageTemplate); // 第三步 lock (_templatesLock) { ... _templates[messageTemplate] = result; } } }

首先,MessageTemplateCache类继承IMessageTemplateParser接口,该接口位于Core文件夹下,表示是一个解析字符串模板的核心接口,内部包含解析函数Parse,该函数的输入是字符串模板的字符串数据,输出是MessageTemplate类。其次,看下继承类MessageTemplateCache的实现,从名称上来看,可以看出它带有缓存的解析。当然,内部的实现也是这样的,在该类内部,有一个_innerParser的同类接口对象,感觉有点熟悉。继续往下,_templates是一个哈希表,它是字典类的非泛型实现,通过它可以寻找字符串模板对应的MessageTemplate对象,可以将其看成是一个缓存。构造函数附带一个对应消息解析对象,并给_innerParser赋值。在其核心的Parser方法中,它给出了具体的解析逻辑:

如果当前字符串的解析数据被哈希表所记录下来,那么直接从对应的位置提取解析好的MessageTemplate对象并返回。

如果没有,则利用内部维护的_innerParser对其解析

将解析后的MessageTemplate对象添加到哈希表中,为后续同一个消息模板中提供缓存数据。

可以发现,这种代码结构和之前的 Sink 逻辑非常像,它也是装饰模式的一个实现。即无论采用何种具体解析消息模板的逻辑,通过MessageTemplateCache类可以为其动态添加缓存记录的功能,对于常用的消息模板场合下可以提高解析的效率,缩短运行时间。换句话来说,解析这一操作行为是一个纯函数,即给定的输入就能给定输出,不存在副作用,该函数的处理结果可以缓存下来。

MessageTemplateParser类

那么在 Serilog 有提供具体的解析类么?有的,它是位于 Parsing 文件夹下的MessageTemplateParser类。

public class MessageTemplateParser : IMessageTemplateParser { public MessageTemplate Parse(string messageTemplate) { ... return new MessageTemplate(messageTemplate, Tokenize(messageTemplate)); } }

可以看到,这个类做的就是直接构造对应的MessageTemplate类对象,这里的Tokenize函数则是将字符串模板转换成一个或多个MessageTemplateToken对象,其核心思想就是从左到右依次扫描字符串中的每个字符,判断其是否是属性Token起始的{,然后将其分割。如果感兴趣的话请阅读具体源码,考虑到这段代码是一个过程性代码,通过调试一步步读下去即可,这里就不进行详述了。

总结

本篇主要讲述字符串解析过程的代码结构,该结构较为简单,模板解析的数据均保存在MessageTemplate类中,主要以MessageTemplateToken类对象的形式存在。解析后的 Token 主要分为两类,只用于描述文本信息的TextToken类以及描述属性数据的PropertyToken类。整个字符串模板通过MessageTemplateProcessor的Process函数进行解析,而其内部,利用装饰模式给处理行为添加缓存机制,即MessageTemplateCache类,真正的解析处理逻辑则放在MessageTemplateParser类中,同时这两个类实现IMessageTemplateParser接口,方便第三方进行替换。

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

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