【问题标题】:Parse multiline log entries using a regex使用正则表达式解析多行日志条目
【发布时间】:2015-03-04 20:14:57
【问题描述】:

我正在尝试使用此正则表达式解析 C# 应用程序中的日志条目:(^[0-9]{4}(-[0-9]{2}){2}([^|]+\|){3})(?!\1) 用于格式为 [日期(某种格式)] 的日志 | [等级] | [应用名称] | [消息].

在哪里(我认为):

  • ^ 匹配行首(在 regex101 上启用 /gm
  • [0-9]{4}(-[0-9]{2}){2} 后跟日期的开头,例如 2015-03-03
  • ([^|]+\|){3}) 后跟日期的其余部分、日志级别和应用名称
  • (?!\1) 后跟不是新日志条目的开始(应该是消息)

例如,我有以下 4 个日志条目(由换行符分隔以进行说明):

2015-03-03 19:30:47.2725|INFO|MyApp|这是一条单行日志消息。 2015-03-03 19:31:29.1209|INFO|MyApp|此日志消息有多个 与 2015-03-03 里面有个日期。 2015-03-03 19:32:50.1106|INFO|MyApp|此日志消息有 多行 但只是文字。 2015-03-03 19:33:20.2683|ERROR|MyApp|此日志消息有多行但 还有一些令人困惑的文字,例如 2015-03-03 19:33:20.2683|错误|这应该 仍然是有效的日志消息。

但是当我在regex101 上测试它时,正则表达式没有捕获该消息,可能是因为我不明白如何捕获负前瞻。

如果我在正则表达式中包含.*(^[0-9]{4}(-[0-9]{2}){2}([^|]+\|){3}).*(?!\1) 它匹配消息,但只匹配一行(因为 . 不匹配换行符)。

那么我怎样才能捕获(多行)消息?

【问题讨论】:

  • 您在这里使用什么语言?有几种不同类型的正则表达式,具体取决于环境,所以请具体说明。
  • @phillip 我想在 C# 应用程序中使用它,但我首先尝试使用 regex101 的默认 PHP 风格使其工作。

标签: c# .net regex negative-lookahead


【解决方案1】:

你可以使用这个正则表达式:

(^\d{4}(-\d{2}){2}([^|]+\|){3})([\s\S]*?)\n*(?=^\d{4}.*?(?:[^|\n]+\|){3}|\z)

RegEx Demo

这个正则表达式也应该在 C# 中工作,只要确保使用 MULTILINE 标志。

【讨论】:

  • @anubhava 感谢您的回答,但我遇到的问题与我刚刚在Necreaux answer 上评论的问题相同
  • 我读过但不明白它在哪种情况下不起作用。你能更新演示链接并提供一个更新的 regex101 链接,显示什么不起作用。
  • 在最后一条消息中,您缺少MyApp|,这是您的应用程序名称。这真的会发生在应用程序日志中吗?
  • 如果可以,那么世界上没有适合的正则表达式。在这种情况下,应该将日志记录拆分为多个日志,这样才能正常工作,但记录器仍然应该能够强制输出包含字段
  • 但是早先的正则表达式已经有 4 并且你说 最后一条消息应该是有效的
【解决方案2】:

这样的事情应该可以工作。
请参阅正则表达式中的 cmets。
(mod: 为 EOS 或单行消息设置换行符)

 @"(?m)^[0-9]{4}(?:-[0-9]{2}){2}(?:[^|\r\n]+\|){3}((?:(?!^[0-9]{4}(?:-[0-9]{2}){2}(?:[^|\r\n]+\|){3}).*(?:\r?\n)?)+)"

格式化(with this):

 (?m)                          # Modifier - multiline
 ^                             # BOL
 [0-9]{4}                      # Message header
 (?: - [0-9]{2} ){2}
 (?: [^|\r\n]+ \| ){3}
 (                             # (1 start), The Message
      (?:
           (?!                           # Assert, not a Message header
                ^                             # BOL
                [0-9]{4} 
                (?: - [0-9]{2} ){2}
                (?: [^|\r\n]+ \| ){3}
           )
           .*                            # Line is ok, its part of the message
           (?: \r? \n )?                 # Optional line break
      )+
 )                             # (1 end)

输出:

 **  Grp 0 -  ( pos 0 , len 74 ) 
2015-03-03 19:30:47.2725|INFO|MyApp|This is a single line log message.


 **  Grp 1 -  ( pos 36 , len 38 ) 
This is a single line log message.

--------------

 **  Grp 0 -  ( pos 74 , len 108 ) 
2015-03-03 19:31:29.1209|INFO|MyApp|This log message has multiple
lines with
2015-03-03
a date in it.


 **  Grp 1 -  ( pos 110 , len 72 ) 
This log message has multiple
lines with
2015-03-03
a date in it.

--------------

 **  Grp 0 -  ( pos 182 , len 97 ) 
2015-03-03 19:32:50.1106|INFO|MyApp|This log message has
multiple lines
but just text only.


 **  Grp 1 -  ( pos 218 , len 61 ) 
This log message has
multiple lines
but just text only.

--------------

 **  Grp 0 -  ( pos 279 , len 186 ) 
2015-03-03 19:33:20.2683|ERROR|MyApp|This log message has multiple lines but
also some confusing text like
2015-03-03 19:33:20.2683|ERROR| which should
still be a valid log message.

 **  Grp 1 -  ( pos 316 , len 149 ) 
This log message has multiple lines but
also some confusing text like
2015-03-03 19:33:20.2683|ERROR| which should
still be a valid log message.

【讨论】:

  • 谢谢你的回答,我看到这个也可以。
【解决方案3】:

您使用的是什么正则表达式引擎?例如,在 Java 中,有一个标志来告诉“。”。匹配换行符。

以下正则表达式似乎可以解决问题:

/(([0-9]{4})(-[0-9]{2}){2}([^|]+\|){3})((.(?!\2))*)/sg

我对您的查询所做的修改主要是一些清理(您的日期捕获组错误)。然后我添加了一个 .和 * 在最后的捕获组中。 https://regex101.com/r/fU1vV1/2

最重要的部分是 sg 标志的使用。 g 使其获得所有匹配项。 s 使它像一行一样对待它(否则你的负前瞻将永远不会起作用)。如果您可以保证 cmets 在一行上(在您的示例中就是这样),那么所有这些都是不必要的,因为您可以捕获到行尾。

【讨论】:

  • @Necreaux 看起来您的答案确实有效!我看到您改用 s (单行)修饰符。您能否像我一样在这里逐步解释正则表达式,以便我和其他人可以从中学习? (而不是某天可能会损坏的链接)
  • 添加了进一步的清理/澄清。
  • @Necreaux 我现在看到,当我有一条包含日期本身(或仅一年)的日志消息时,该消息不再完全包含在正则表达式中。日志消息应该是除了可以通过我描述的日志格式检测到的新日志消息的开始之外的任何内容。有什么建议吗?
  • @Necreaux:你犯了和 OP 一样的错误,使用反向引用(\1\2)作为子程序。反向引用不会再次尝试匹配相同的子表达式,它会尝试匹配在该组中捕获的实际子字符串。您的前瞻似乎起作用的原因是因为您只比较前四个字符,它们恰好都是相同的。此外,您遗漏了行首锚点(^ 在多行模式下)。
  • @AlanMoore 我完全同意反向引用的东西。也许我应该以不同的方式处理这个问题,但我的方法是通过回答特定问题来尝试将 OP 指向正确的方向,而不是直接解决正则表达式中的其他问题。
猜你喜欢
  • 1970-01-01
  • 2015-09-06
  • 2011-09-12
  • 2022-01-13
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多