使用正则表达式解析日志文件

时间:2020-03-05 18:46:57  来源:igfitidea点击:

我目前正在为内部日志文件(由log4php,log4net和log4j生成)解析器。到目前为止,我有一个很好的正则表达式来解析日志,除了一个令人讨厌的地方:一些日志消息跨越多行,我无法正确匹配。我现在使用的正则表达式是这样的:

(?<date>\d{2}/\d{2}/\d{2})\s(?<time>\d{2}):\d{2}:\d{2}),\d{3})\s(?<message>.+)

日志格式(用于测试解析器)是这样的:

07/23/08 14:17:31,321 log 
message
spanning
multiple
lines
07/23/08 14:17:31,321 log message on one line

当我现在运行解析器时,我只会看到日志开始的那一行。如果我将其更改为跨越多行,则只会得到一个结果(整个日志文件)。

@samjudson:

我们需要将RegexOptions.Singleline标志传递到正则表达式中,以便使用"。"。匹配所有字符,而不仅仅是除换行符(这是默认值)以外的所有字符。

我试过了,但随后它匹配了整个文件。我还尝试将消息组设置为。+? (非贪婪),但是它匹配一个字符(这不是我要查找的字符)。

问题在于消息的模式在日期组上也匹配,因此当它在换行符上不中断时,它会不断出现。

我现在将此正则表达式用于消息组。除非日志消息中有与日志消息开头相同的模式,否则它会起作用。

(?<message>(.(?!\d{2}/\d{2}/\d{2}\s\d{2}:\d{2}:\d{2},\d{3}\s\[\d{4}\]))+)

解决方案

回答

我们需要通过RegexOptions。在单行标志中加入正则表达式,使之为"。"。匹配所有字符,而不仅仅是除换行符(这是默认值)以外的所有字符。

回答

我们遇到的问题是我们需要终止RegEx模式,以便它知道一条消息何时结束然后下一条开始。

在默认模式下运行时,换行符用作隐式终止符。

问题是,如果我们进入多行模式,则没有终结符,因此模式会吞噬整个文件。非贪心匹配的字符越少越好,只会是一个。

现在,如果使用下一条消息的日期作为终止符,我认为解析器将仅获得每隔一行。

文件中还有其他内容可以终止模式吗?

回答

我们显然需要将"消息行"与"日志行"区分开来;如果我们允许消息部分以换行后的日期/时间开头,则根本无法确定消息的什么部分,而不是什么。因此,不需要使用点,而是需要一个表达式,该表达式允许所有不包含换行符后跟日期和时间的内容。

但是,就我个人而言,我不会使用正则表达式来解析整个日志条目。我更喜欢使用自己的循环遍历每一行,并使用一个简单的正则表达式来确定行是否是新条目的开始。从可读性的角度来看,这也是我的偏爱。

回答

仅当日志消息在行首不包含日期时,此方法才有效,但是我们可以尝试在"消息"组中为日期添加否定的前瞻性断言:

(?<date>\d{2}/\d{2}/\d{2})\s(?<time>\d{2}:\d{2}:\d{2},\d{3})\s(?<message>(.(?!^\d{2}/\d{2}/
\d{2}))+)

请注意,这需要使用RegexOptions.MultiLine标志。

回答

我们可能会发现,使用适当的解析器生成器来解析文件要容易得多,ANTLR可以在C#中生成一个解析器。表情...