从应用程序检索日志时,您通常会寻找异常值(例如唯一错误)或出现最多的模式。但是,手动定义模式非常耗时,并且在整个项目中都要求严格。在这一系列博客文章中,我们将探索自动化的Log PatternRecognition。
我最近开始使用Datadog日志模式,然后沉迷于它。不幸的是,我无法在Kibana上找到这样的功能或能够进行此分析的查询代理。如果不存在,还可以进一步了解并制作一个!
令我惊讶的是,我没有找到我想的那么多论文。公开实施的论文数量甚至更少。我们的系列文章始于一篇名为LogMine:Log Analytics的快速模式识别的论文,以及Tony Dinh的实现,这对我研究LogMine的行为很有帮助。
令我惊讶的是,我在Hacker News上搜索了与LogMine相关的任何内容,但只发现了Tony Dinh的提交内容。
本文中使用的某些术语可能具有多种含义。在Logmine的上下文中,我使用的术语表示:
字段:模式/日志中的单词的一部分(固定值,通配符或变量)
LogMine论文描述了一种在没有任何先验知识或监督的情况下解析日志,将其分组和提取模式的方法。 LogMine可以使用MapReduce来实现,并且可以一次通过数据集。
固定值,该字段在集群中的所有日志中保持不变。这是在模式提取步骤中检测到的。
变量,与用户提供的模式匹配的字段。这在预处理步骤中检测到。
通配符,该字段在集群中的所有日志中都不恒定。这是在模式提取步骤中检测到的。
10:00:00-[DEBUG]-用户123断开连接10:30:00-[DEBUG]-用户123断开连接11:11:11-[ERROR]-断开用户45612:12:12时发生错误-[DEBUG]-用户789已断开连接
好的做法是定义更多变量,以避免由相似度低的日志形成两个聚类。例如,我们应将< number>定义为\ d +。
根据日志的模式,将< log-level>之类的通用字段定义为[(DEBUG | INFO | WARNING | ERROR)]可能最终会隐藏信息。这是因为带有错误消息的警告日志可能并不那么重要,但是具有错误级别的同一条日志消息可能具有您要突出显示的确切信息。
第一步是将日志解析为模式并识别密集集群。 Densecluster是原始日志几乎相同的集群。
为此,我们首先将日志分成模式(字段数组)。默认情况下,这些分隔符是任何空格字符。
一旦完成此转换,我们将把这些模式减少为密集的集群。识别密集群集可以看作是群集算法的一个特殊用例:我们识别群集,但是由于这些模式几乎相同,因此我们跳过了模式提取步骤。
因此,我将首先介绍聚类算法和模式之间距离的概念。
聚类算法获取模式列表,并识别彼此接近的模式。托尼·丁(Tony Dinh)的实现方式和本文均使用以下距离定义:
Dist(P,Q)= 1-∑ i = 1 Min(len(P),len(Q))得分(P i,Q i)Max(len(P),len(Q))\ text {Dist} (P,Q)= 1-\ sum_ {i = 1} ^ {\ text {Min}(\ text {len}(P),\ text {len}(Q))} {\ frac {\ text {Score }(P_i,Q_i)} {\ text {Max}(\ text {len}(P),\ text {len}(Q))}} Dist(P,Q)= 1 − i = 1 ∑ Min(len (P),len(Q))Max(len(P),len(Q)分数(P i,Q i))如果两个模式之间的距离小于内部定义的阈值MaxDist,则两个模式被视为同一集群的成员。
本文提出的评分功能允许根据字段类型调整权重。
分数(x,y)= {k 1如果x = y且均为固定值k 2如果x = y且均为变量0否则\ text {Score}(x,y)= \ begin {cases)k1& \ text {if} x = y& \ text {都是固定值} \\ k2& \ text {if} x = y& \ text {都是可变的} \\ 0& \ text {否则} \ end {cases}分数(x,y)= k k 1 k 2 0如果x = y则x = y否则x和y均为固定值,且两者均为变量为了保持性能,计分功能不能返回负值。因此,k1和k2不能为负。
LogPai的LogParser放弃了变量的概念,并使用k2来调整通配符的权重
让我们定义k1 = 1和k2 = 1,这是Tony Dinh的实现所使用的权重,并由原始论文推荐。
在此示例中,我将使用原始纸张建议(MaxDist = 0.01).MaxDist参数用于调整算法灵敏度:MaxDist越高,检测到的模式越多。如果MaxDist太高,则模式会被分组为很大聚类和模式提取变得毫无意义。
两个日志与1-7 7< MaxDist 1-\ frac {7} {7}< \ text {MaxDist} 1 − 7 7 〈 MaxDist。
两个日志不属于与1 − 3 11>相同的群集。 MaxDist 1-\ frac {3} {11}> \ text {MaxDist} 1 − 1 1 3> MaxDist。
由于我们使用的是MaxDist = 0.01,并且由于我的示例中的日志非常短,因此我们只会标识相同的日志。因此,我们可以使用聚类算法通过跳过模式识别步骤来识别密集聚类。
在顺序(与MapReduce相对)模式下,每次在集群中插入模式时,LogMine都会尝试从中提取模式。 LogMine只知道如何识别两种类型:
尽管看起来很严格,但请记住,令牌化期间的早期模式检测已经负责识别用户定义的模式。
由于同一群集中的模式可以具有不同数量的字段,因此本文使用Smith-Waterman算法来对齐它们。 Smith-Waterman主要用于生物信息学中以比对序列。有趣的是,将类似的算法应用于对数模式识别。
为了与之前的介绍保持一致,我们将它们称为L o g 1 Log_1 L o g 1和L o g 4 Log_4 L o g 4。
因为本文中的示例会变得非常复杂,所以我决定在示例中不使用非对齐日志。但是,您可能会对Smith-Waterman的工作方式感到好奇。
在实现上,LogMine将通过在字段中添加None值来对齐以下两个模式。例如:
<时间> -[DEBUG]-用户123< None>断开<时间> -[DEBUG]-用户123已断开连接
史密斯沃特曼不了解模式。 Smith-Waterman使用与聚类算法相同的评分功能。因此,如果两个字段不相等,则不能保证占位符将正确插入。
<时间> -[DEBUG]-用户< None> 123断开<时间> -[DEBUG]-用户456已断开连接
当模式提取算法返回模式列表时,我们可以放宽MaxDist并将该列表反馈给聚类算法。
在3.4模式层次结构中,本文描述了一种生成可能的模式层次结构(从非常严格到非常松懈)的过程。该过程通过将MaxDist乘以α\ alphaα因子重复几次聚类和提取步骤来进行。
托尼·丁(Tony Dinh)的实现并未在多次迭代中放宽MaxDist,而是更倾向于一次迭代。尽管效率很高,但是这会降低精度,并且需要更好的初始调整。
我们使用与先前定义相同的四个日志。在此步骤中,我们将具有三种模式,即L o g 2 Log_2 L o g 2与L og 1 Log_1 L o g 1在一个密集的群集中合并。
跳到MaxDist = 0.6(严重)会在模式层次结构中模拟较高的水平,这使我可以举一个示例来实际识别模式。
在α\αα因子为1.3的情况下,这将介于16thiteration(MaxDist = 0.51)和17th迭代(MaxDist = 0.66)之间。
让我们通过在第一个对数和其他对数之间应用距离公式进行说明。
两个日志不属于与1 − 3 11>相同的群集。 MaxDist 1-\ frac {3} {11}> \ text {MaxDist} 1 − 1 1 3> MaxDist。
这两个日志与1-3 7< 3 7属于同一个集群。 MaxDist 1-\ frac {3} {7}< \ text {MaxDist} 1 − 7 3 〈 MaxDist。总和只能增加,因此我们可以在此处停止计算。
我们可以将L o g 1 Log_1 L o g 1和L o g 4 Log_4 L o g 4分组到同一集群中,并将L o g 3 Log_3 L o g 3分组到另一个集群中。扫描新日志时,我们会将其与所有集群进行比较。
在实现模式层次结构时,我们获得一棵树(或列表数组),其中每个级别代表不同级别的敏感性。为了找到合适的返回级别,我们需要定义一种对每个级别进行排名的方法。
Cost = ∑ i = 1个集群大小i ∗(1 WC i + 2 Var i + 3 FV i)\ text {Cost} = \ sum_ {i = 1} ^ {\ text {\# }} {\ text {Size} _i *(a_1 WC_i + a_2 \ text {Var} _i + a_3 FV_i)} Cost = i = 1 ∑群簇大小i ∗(a 1 WC i + a 2 Var i + 3 FV i)由于树允许我们计算每个级别的模式数量(簇),因此我们可以允许最终用户提供最大数量的模式。
使用MapReduce框架时,我们将“日志聚类”和“模式识别”分为两个不同的步骤。这意味着我们需要存储集群的Everylog代表。
映射功能将日志映射到键值对。键是一个固定数字,值是一个模式。
reduce函数使用前面提到的算法将这些对合并在一起。请记住,此步骤仅保留一个代表。
此步骤仅执行一次,识别出的群集数量足够少,因此我们可以使用单个键。
请注意,地图功能仅在解析日志时起作用。一旦使用了模式,就可以在聚类步骤中简单地使用模式识别的输出。
请注意,reduce顺序可以更改群集,但这并不是重复的问题,因为这些群集将在下一次迭代中合并。
map函数将集群映射到成对列表,其中键是集群的标识符,而值是模式。
我发现LogMine真正有趣的是,它几乎可以改变所有算法(例如评分和模式检测算法),而不会影响整个算法。
虽然可以对LogMine中的所有算法进行调整,但从整体上看,LogMine并不容易调整:
a 1 a_1 a 1,a 2 a_2 a 2和3 a_3 a 3可以与最终用户提供的变量以及日志结构是什么高度相关
此外,LogMine不支持多字段令牌。跨越多个字段的令牌可以轻松创建足够的噪声,以将单个模式拆分成几个集群。
您可以在Github上的Apache Logs演示中看到未正确解析多字段令牌的示例。日期的格式设置为[Sun Dec 04 04:47:44 2005],LogMine仅能提取时间。尽管您可以轻松地将整个日期解析为一个日期,但解析其每个字段都会产生很多误报(与04匹配< day的正则表达式也将匹配任何具有两位数字的整数)。
LogMine也不跟踪由模式识别的日志数量,但是可以轻松实现,而不会影响性能。
最后,LogMine对日志结构没有任何了解。这意味着,如果您的日志格式包含可以更改顺序的字段,则需要首先对该顺序进行规范化。 LogMine似乎是分析消息本身的理想选择,例如,一旦每个logfmt字段都被解析并删除后,消息仍然保留。
对于我的用例,只要我能找到能够从样本生成正则表达式的算法,似乎LogMine就足够了。此算法必须一次通过并处理流数据,以避免需要多个代表日志来工作。
本系列的第二部分将集中于一篇名为“ Drain:具有固定深度树的联机日志解析方法”的论文。 非常感谢Tony Dinh实施LogMine,并感谢我的回答。 我还要感谢Marc Schmitt和BriceDutheil对本文的审阅。