《Drain: An Online Log Parsing Approach with Fixed Depth Tree》
Drain是一种基于固定深度树的在线日志解析(日志解析的目标是将原始日志信息转换为结构化的日志消息,如下图所示,结构化的日志分为常量部分例如src、dest和变量部分例如blk_3587)方法。当新的原始日志消息到达时,Drain将根据领域知识通过简单的正则表达式对其进行预处理。然后,Drain按照树内部节点中编码的特殊设计规则搜索日志组(即树的叶子节点)。如果找到合适的日志组,则日志消息将与存储在该日志组中的日志事件相匹配。否则,将根据日志信息创建新的日志组
本质上来讲,Drain就是将不同类型的日志区分开来,进行一个聚类
当原始日志消息到达时,在线日志解析器需要为它搜索最合适的日志组,或者创建一个新的日志组。
根节点和内部节点编码专门设计的规则来指导搜索过程,它们不存储日志组
叶子节点存储一个日志组列表,每个日志组由两部分组成:日志事件和日志id。日志事件是最好地描述该组中的日志消息的模板,该组由日志消息的常量部分组成;日志id记录该组日志消息的id。
解析树的一个特殊设计是,所有叶节点的深度都是相同的,并由一个预定义的参数 d e p t h depth depth固定,该参数限制了搜索过程中节点Drain访问的数量,提高搜索效率。
此外,为了避免树枝爆炸,使用参数 m a x C h i l d maxChild maxChild来限制一个节点的最大子节点数.
Drain将第 n n n层节点定义为深度为 n n n的节点,后续的算法流程以上图为参照。
Drain允许用户根据表示常用变量(如IP地址和块ID)的专业知识提供简单的正则表达式,然后Drain会从原始日志消息中删除这些正则表达式匹配到的token(例如最上面图中的块ID会被blk_[0-9]+移除)。一个数据集通常只需要几个这样的正则表达式。
Drain从带有预处理日志消息的解析树的根节点开始。解析树的第1层节点以日志消息的长度(token的数量)区分日志组,Drain根据预处理日志消息的日志消息长度选择到第1层节点的路径。(基于假设:具有相同日志事件的日志消息可能具有相同的日志消息长度)
Drain从步骤2中搜索的第1层节点遍历到叶节点。(此步骤基于这样一个假设:日志消息开始位置的token更有可能是常量)。
Drain通过日志消息开始位置中的token选择下一个内部节点。例如,对于上图中节点Length=4的日志消息Receive, Drain从第1层节点Length=4遍历到第2层节点Receive,因为日志消息的第一个位置的token是Receive。在这个实例下,内部节点的数量是 d e p t h − 2 depth-2 depth−2,因此有 d e p t h − 2 depth-2 depth−2个层将日志消息的前 d e p t h − 2 depth-2 depth−2个token当做搜索规则。
在某些情况下,日志消息可能以一个参数开始,例如,120 bytes received。这类日志消息可能导致解析树中的分支爆炸,因为每个参数将被编码在一个内部节点中。为了避免分支爆炸,在这一步只考虑不包含数字的token。
如果一个token包含数字,它将匹配一个特殊的内部节点*。例如,对于上面的日志消息"120 bytes received",Drain将遍历到内部节点*而不是120。此外参数 m a x C h i l d maxChild maxChild限制了节点的最大子节点数。如果一个节点已经有 m a x C h i l d maxChild maxChild子节点,那么任何不匹配的标记都将在其所有子节点中匹配特殊的内部节点*。
在此步骤之前,Drain已经遍历了一个叶节点,其中包含一个日志组列表。这些日志组中的日志消息遵循路径内部节点中编码的规则。例如,图2中的日志组中有一个日志消息包含4个令牌,并以令牌Receive开始。
在此步骤中,Drain从日志组列表中选择最合适的日志组。通过计算日志消息和每个日志组的日志事件之间的 s i m S e q simSeq simSeq相似性:
s i n S e q = ∑ i = 1 n e q u ( s e q 1 ( i ) , s e q 2 ( i ) ) n sinSeq = \frac{\sum_{i=1}^n equ(seq_1(i), seq_2(i))}{n} sinSeq=n∑i=1nequ(seq1(i),seq2(i))
其中 s e q 1 ( i ) , s e q 2 ( i ) seq_1(i),seq_2(i) seq1(i),seq2(i)分别代表日志消息和日志事件, S e q ( i ) Seq (i) Seq(i)为序列的第 i i i个token; N N N为序列的日志消息长度, e q u equ equ函数定义如下:
e q u ( t 1 , t 2 ) = { 1 i f t 1 = = t 2 0 o t h e r w i s e equ(t_1, t_2) = \begin{cases} 1 &if\ \ t_1 == t_2\\ 0&otherwise \end{cases} equ(t1,t2)={10if t1==t2otherwise
在找到 s i m S e q simSeq simSeq最大的日志组后,将其与预定义的相似度阈值 s t st st进行比较,如果 s i m S e q ≥ s t simSeq \ge st simSeq≥st,那么Drain就会返回该组作为最佳匹配,否则返回一个标志位表示没有合适的。
如果在步骤4中返回了合适的日志组,则Drain将当前日志消息的日志ID添加到返回的日志组中的日志ID中。此外,将更新返回日志组中的日志事件。
Drain扫描日志消息和日志事件相同位置的token,如果两个token相同,则不修改该token位置上的token。否则,在日志事件中通过通配符*更新该token位置上的token
如果无法找到合适的日志组,则根据当前日志消息创建一个新的日志组,其中日志ID只包含日志消息的ID,日志事件就是这个日志消息。然后,Drain将用新的日志组更新解析树。
直观地,Drain从根节点遍历到应该包含新日志组的叶节点,并相应地沿着这条路径添加确实的内部节点和叶子节点。下图展示了这个过程
可以看到接受消息 Receive 120 bytes,在解析树中被编码成了最右边这条路径,第三层的内部节点被编码为了通配符*,因为120是数字。该树 d e p t h = 4 depth=4 depth=4