在处理日志文件时,寻找特定事件的实例可能并不困难,但当模式中包含变量元素时,寻找特定事件模式就会变得极其困难。ChronEx 是一种模式匹配规范和参考实现,允许开发者或商业智能分析师以类似于文本中的正则表达式(RegEx)匹配的方式,指定要匹配的事件模式。
以正则表达式为例,假设有一个日志文件,其中可能包含名为 "A"、"B"、"C"、"D" 或 "E" 的事件。需要在日志文件中搜索所有事件 "A" 发生两次,紧接着是事件 "B",然后是事件 "C" 或 "D",最后是事件 "E" 的实例。幸运的是,日志将所有事件保存为单行字符串中的字符序列。查询日志的最简单方法是使用正则表达式 "A{2}B(C|D)E"。这个正则表达式将找到日志中所有这种特定模式的实例。
将这个概念扩展到 ChronEx,假设这些事件有更详细的名称,并存储在日志中。可以使用如下 ChronEx 模式:
EventA{2}
EventB
[
EventC
EventD
]
EventE
这个模式在运行时,将类似于在字符串上运行正则表达式时的行为。
ChronEx 是一种模式匹配语言的规范。目前有一个部分C#参考实现,并且正在开发Python实现。规范在 GitHub 上的地址是:
https://github.com/mbadler/ChronologicalExpressions
C#实现在:
https://github.com/mbadler/ChronEx-CSharp
这些都是非常早期且非常粗糙的实现。欢迎任何人提交建议、改进等。
所有 ChronEx 的事件输入都是 IChronologicalEvents 的列表:
public interface IChronologicalEvent
{
string EventName { get; }
DateTime? EventDateTime { get; }
}
该库有帮助方法可以解析 CSV 并生成适合在 ChronEx 中使用的一系列对象。
ChronEx 类包含运行模式匹配的方法:
public static bool IsMatch(string ChronExStatment, IEnumerable Events)
如果有任何事件模式匹配,则此方法返回布尔值 true/false:
public static int MatchCount(string ChronExStatment, IEnumerable Events)
此方法返回事件日志中的模式数量(不是实际事件的数量,而是模式匹配的数量):
public static ChronExMatches Matches(string ChronExStatment, IEnumerable Events)
此方法返回一个包含每个模式的匹配事件的匹配列表。这些全部是静态方法,接受模式,Lex,解析并构建运行表达式所需的 AST(抽象语法树)。最终,将添加方法来实例化这些模式,使用预先解析的 AST 以加快构建速度。
之前提到 ChronEx 基于 RegEx,确实是这样,但非常松散。现在的大多数正则表达式引擎是 RDE(Regex Driven Engines),而不是所谓的 TDE(Text Driven Engine)。这指的是什么驱动引擎,是枚举文本还是模式。TDE 简单且比 RDE 快得多,但 RDE 提供了更多的功能,非贪婪选择器和前瞻是 TDE 不可用的。
从这个意义上说,ChronEx 可以被认为是 EDE(Event Driven Engine?)。它枚举事件流并应用模式。这是出于两个原因:作为 EDE 更容易编写。事件适合作为流进行处理,在正则表达式中 - 预期已经拥有想要处理的完整文本在内存中,然后可以让正则表达式根据需要在文本中移动。对于事件日志,需要能够处理可能包含数百万事件的日志,因此需要使用流,仅在引擎需要时加载数据,并允许已处理的旧数据根据需要从内存中清除。实际上 - 尽管尚未实现 - 可以看到有长期运行的实时模式,它们将不断运行,并在日志中添加新事件时通知,并实时输出匹配项。
截至版本 0.8,这是当前实现的功能。
功能 描述
选择器 事件名称的逐字匹配
正则表达式选择器 以斜杠开头并以斜杠结尾的文本 - 正则表达式表达式将用于匹配事件名称
.(点) 匹配任何事件
* 当附加到选择器或组时 - 匹配 0 个或多个匹配该选择器的事件
? 当附加到选择器或组时 - 匹配 0 个或 1 个匹配该选择器的事件
+ 当附加到选择器或组时 - 匹配 1 个或多个匹配该选择器的事件
!(否定) 当附加到选择器时 - 如果选择器不匹配,则匹配
- 指示匹配但不返回/捕获特定事件
( )(AND 组) 如果组中的所有项匹配,则匹配 允许嵌套组
[ ](OR 组) 如果组中的任何选择器匹配,则匹配(注意,对于版本 0.8 - 仅支持在或组中使用基本选择器,嵌套组将编译,但结果可能是错误的)
{X,X}(数值量词) 将匹配特定数量的事件(正则表达式风格)
包含在存储库中并附加到本文的是一个简单的模式查询应用程序。它附带了一个从建筑能源监控系统适应的数据集,日志包含在建筑锅炉中发生的事件列表,时间跨度为 24 小时。
日志看起来像这样:
BoilOn,Feb 18 2018 1:21PM
BoilOff,Feb 18 2018 1:25PM
BoilOn,Feb 18 2018 1:25PM
BoilOff,Feb 18 2018 1:26PM
BoilOn,Feb 18 2018 1:27PM
BoilOff,Feb 18 2018 1:30PM
建筑所有者报告说锅炉在燃料成本上花费很多,从日志的前 6 行快速查看,确实看到在下午的 9 分钟期间,锅炉运行了大约 8 分钟,这看起来是过度的。
ChronExQuery 有三个标签,第一个标签是日志。第二个包含查询和结果。如果想分析其他日志 - 只需替换第一个标签中的日志。第三个标签将显示执行的最后一个表达式的序列化 AST。
快速查看日志告诉有很多与 BoilOn 事件匹配的 BoilOff 事件。让找出这个事件覆盖了多少个周期。
模式:
BoilOn
结果:
MatchCount: 389
好的,现在让看看一个典型的 BoilOn/BoilOff 周期是什么样子:
模式:
BoilOn BoilOff
结果:(最后 4 个)
结果 #347
BoilOn : 2/19/2018 12:50:00 PM
BoilOff : 2/19/2018 12:53:00 PM
结果 #348
BoilOn : 2/19/2018 12:53:00 PM
BoilOff : 2/19/2018 12:56:00 PM
结果 #349
BoilOn : 2/19/2018 12:56:00 PM
BoilOff : 2/19/2018 12:58:00 PM
结果 #350
BoilOn : 2/19/2018 1:00:00 PM
BoilOff : 2/19/2018 1:00:00 PM
但等等,之前说有 389 个结果,但现在只列出了 350 个,原因是在锅炉运行时可能会发生其他事件,所以需要一个表达式来考虑这一点。
以下模式将从 BoilOn 开始,但然后会寻找 BoilOff,允许多个非 BoilOffs。
BoilOn
!BoilOff*
BoilOff
结果显示这些类型的模式,例如:
结果 #280
BoilOn : 2/19/2018 7:19:00 AM
HTMOn : 2/19/2018 7:20:00 AM
HMComp : 2/19/2018 7:20:00 AM
BoilOff : 2/19/2018 7:22:00 AM
但查看最后一个结果,看到了一些问题:
结果 #372
BoilOn : 2/19/2018 12:59:00 PM
HWOn : 2/19/2018 1:00:00 PM
BoilOn : 2/19/2018 1:00:00 PM
BoilOff : 2/19/2018 1:00:00 PM
这里有两个问题:
只得到了 372 个结果。预期的是 389。
有一个 BoilOn 没有与 BoilOff 匹配。
所以看起来也有数据质量问题,需要量化并看看这个问题有多严重。
所以让定义一个破损的周期是一个 BoilOn 后面跟着另一个 BoilOn,然后是 BoilOff。这个模式使用正则表达式定义了一个否定的 OR:
BoilOn
!/BoilOff|BoilOn/*
BoilOn
!BoilOff*
BoilOff
结果确实显示大约有 15 个破损的周期。例如,这是最后 2 个破损的周期:
结果 #14
BoilOn : 2/19/2018 11:57:00 AM
BoilOn : 2/19/2018 12:00:00 PM
BoilOff : 2/19/2018 12:01:00 PM
结果 #15
BoilOn : 2/19/2018 12:59:00 PM
HWOn : 2/19/2018 1:00:00 PM
BoilOn : 2/19/2018 1:00:00 PM
BoilOff : 2/19/2018 1:00:00 PM
所以将定义一个锅炉运行事件从 BoilOn 开始,并在下一个 BoilOff 或如果在此之前遇到 BoilOn 时结束。这是将要使用的模式:
BoilOn
![
BoilOn
BoilOff
]*
BoilOff?
正如预期的那样,得到了预期的所有结果:
结果 #387
BoilOn : 2/19/2018 12:59:00 PM
HWOn : 2/19/2018 1:00:00 PM
BoilOn : 2/19/2018 1:00:00 PM
BoilOff : 2/19/2018 1:00:00 PM