65.9K
CodeProject 正在变化。 阅读更多。
Home

用于事件日志的按时间顺序排列的表达式 (ChronEx)

starIconstarIconstarIconstarIconstarIcon

5.00/5 (3投票s)

2018年6月12日

CPOL

7分钟阅读

viewsIcon

8341

downloadIcon

84

Chronological Expressions 是一个受 RegEx 启发的模式匹配库和用于查询事件日志的规范

引言

查询事件日志非常困难。是的,查找特定事件的实例很容易,但查找特定事件模式可能极其困难,尤其是在模式中存在可变元素的情况下。

时间表达式 (Chronological Expressions) 是一种模式匹配规范和参考实现,用 C# 编写,很快也将提供 Python 版本。它允许开发人员或商业智能分析师以类似于正则表达式匹配文本的方式,指定模式来匹配日志中的事件。

让我们在正则表达式的背景下看看这个。假设您有一个日志文件,其中可能包含名为 "A"、"B"、"C"、"D" 或 "E" 的事件。您需要搜索日志文件,查找所有事件 A 立即发生两次,然后是事件 B,然后是事件 CD,最后是事件 E 的实例。

幸运的是,您的日志将所有事件作为字符序列保存在一行上(一个 string)。

查询日志最简单的方法是使用正则表达式 "A{2}B(C|D)E"。这个正则表达式将查找日志中此特定模式的所有实例。

将此概念扩展到 ChronEx,假设这些事件具有更详细的名称并存储在日志中。您可以使用如下 ChronEx 模式:

EventA{2}

EventB
[
   EventC
   EventD
]

EventE

此模式在日志文件上运行时,将类似于正则表达式在 string 上运行时。

Using the Code

ChronEx 是模式匹配语言的规范。还有一个部分的 C# 参考实现,我也正在开发 Python 实现。

这些都是非常早期和极其粗糙的实现。我欢迎所有人提交建议、改进等。

C# 实现

IChronologicalEvent 列表

ChronEx 上的所有事件输入都是 IChronologicalEvents 的列表。

public interface IChronologicalEvent
    {
        string EventName { get;  }
        DateTime? EventDateTime { get; }
    }

该库提供了辅助方法,可以解析 CSV 并生成一个适合在 ChronEx 中使用的对象列表。

ChronEx 类

ChronEx 类包含运行模式匹配的方法。

public static bool IsMatch(string ChronExStatment,IEnumerable<IChronologicalEvent> Events)

如果存在任何匹配的事件模式,此方法返回布尔值 true/false

public static int MatchCount(string ChronExStatment, IEnumerable<IChronologicalEvent> Events)

此方法返回事件日志中的模式数量(不是实际事件的数量,而是模式匹配的数量)。

public static ChronExMatches Matches
(string ChronExStatment, IEnumerable<IChronologicalEvent> Events)

此方法返回一个匹配列表,包括每个模式的匹配事件。

这些都是 static 方法,它们接受模式,进行词法分析、解析并构建运行表达式所需的 AST(抽象语法树)。最终,我们将添加方法,使用预解析的 AST 实例化这些模式,以加快构建速度。

ChronEx 与 RegEx 的比较

我之前说过 ChronEx 是基于 RegEx 的,确实如此,但非常宽松。

现在大多数正则表达式引擎都是 RDE(Regex Driven Engines,正则表达式驱动引擎),而不是所谓的 TDE(Text Driven Engine,文本驱动引擎)。这指的是驱动引擎的东西,我们是在文本上枚举还是在模式上枚举。

TDE 简单且比 RDE 快得多,但 RDE 带来了更大的能力,非贪婪选择器和前瞻是 TDE 不具备的功能。

在这种情况下,ChronEx 可以被视为 EDE(Event Driven Engine,事件驱动引擎?)。它枚举事件流并对它们应用模式。这有两个原因:

  1. 作为 EDE 编写更容易。
  2. 事件本身适合作为流处理。在正则表达式中,期望您已经在内存中拥有要处理的完整文本,然后可以根据需要让正则表达式在文本中移动。对于事件日志,我们需要能够处理可能包含数百万个事件的日志,因此我们需要借助流,只在引擎需要时加载数据,并允许根据需要从内存中清除已处理的旧数据。事实上,虽然尚未实现,但我可以看到拥有长时间运行的实时模式,这些模式将持续运行并在每次新事件添加到日志时收到通知,并能够实时输出匹配项。

当前已实现的功能

截至 0.8 版本,这是当前已实现的功能。

功能 描述
   
选择符 与事件名称的逐字匹配
正则表达式选择器 以斜杠开头并以斜杠结尾的文本——正则表达式将用于匹配事件名称
. (点) 匹配任何事件
* 当附加到选择器或组时——匹配该选择器的 0 个或多个事件
? 当附加到选择器或组时——匹配该选择器的 0 个或 1 个事件
+ 当附加到选择器或组时——匹配该选择器的 1 个或多个事件
! (否定) 当预置到选择器时——如果选择器不匹配则匹配
   
- 表示匹配但不返回/捕获特定事件
( ) (AND 组) 如果组中的所有项都匹配,则匹配。允许嵌套组。
(**注意**:目前不允许否定和组 ( !( ))
[ ] (OR 组) 如果组中的任何选择器匹配,则匹配(注意,对于 0.8 版本——只支持或组中的基本选择器,嵌套组可以编译,但结果可能会出错)
{X,X} (数字量词) 将匹配特定数量的事件(正则表达式风格)

试用 - ChronExQuery

存储库中包含并附在本文中的是一个简单的模式查询应用程序。

它附带一个改编自建筑能源监控系统的数据集,日志包含建筑物锅炉在 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 中运行查询

ChronEx 有三个选项卡,第一个选项卡是日志。第二个包含查询和结果。如果您想分析其他日志,只需替换第一个选项卡中的日志即可。

第三个选项卡将显示您执行的最后一个表达式的序列化 AST。

快速查看日志告诉我们,许多 BoilOn 事件都与 BoilOff 事件匹配。让我们找出此事件涵盖了多少个周期。

模式

BoilOn

结果

MatchCount: 389

好的,现在让我们看看典型的 BoilOn/BoilOff 周期是什么样的

模式

BoilOn
BoilOff

结果:(最后 4 个)

Result #347
   BoilOn  :  2/19/2018 12:50:00 PM
   BoilOff  :  2/19/2018 12:53:00 PM
Result #348
   BoilOn  :  2/19/2018 12:53:00 PM
   BoilOff  :  2/19/2018 12:56:00 PM
Result #349
   BoilOn  :  2/19/2018 12:56:00 PM
   BoilOff  :  2/19/2018 12:58:00 PM
Result #350
   BoilOn  :  2/19/2018 1:00:00 PM
   BoilOff  :  2/19/2018 1:00:00 PM

但等等,之前说有 389 个结果,现在只列出了 350 个,原因是锅炉运行时可能会发生其他事件,因此我们需要一个考虑这种情况的表达式。

以下模式将以 On 开始,然后查找 Off,通过允许多个非 Off

BoilOn
!BoilOff*
BoilOff

结果显示了这些类型的模式,例如:

Result #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

但查看最后的结果,我们看到了一些奇怪的事情

Result #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

这里有两个问题

  1. 我们只得到了 372 个结果。我们预期是 389 个。
  2. 我们有一个 boilon 没有与 BoilOff 匹配。

所以看起来我们也有数据质量问题,我们需要量化并看看这个问题有多严重。

因此,我们将一个损坏的循环定义为在另一个 BoilOff 之前,BoilOn 后面紧跟着另一个 BoilOn

此模式使用正则表达式定义了一个否定的 OR

BoilOn
!/BoilOff|BoilOn/*
BoilOn
!BoilOff*
BoilOff

结果确实显示我们大约有 15 个损坏的循环。

例如,这是最后 2 个损坏的循环

Result #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
Result #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?

果然,我们得到了所有预期的结果

Result #387
   BoilOn  :  2/19/2018 12:59:00 PM
   HWOn  :  2/19/2018 1:00:00 PM
Result #388
   BoilOn  :  2/19/2018 1:00:00 PM
   BoilOff  :  2/19/2018 1:00:00 PM

历史

  • 2018年6月12日:初始版本
© . All rights reserved.