使用正则表达式提取堆栈跟踪的最后 'Caused by'

Extracting last 'Caused by' of stack trace with regex

不幸的是,我还不是正则表达式专家,因此遇到了以下问题: 假设我有一个包含多个链式异常的 Java 堆栈跟踪,我想要达到的是提取以 "Caused by" 开头的最后一行。

javax.servlet.ServletException: Something bad happened
     at com.example.myproject.OpenSessionInViewFilter.doFilter(OpenSessionInViewFilter.java:60)
.
.
Caused by: com.example.myproject.MyProjectServletException
.
.
Caused by: This is the line I want to capture

到目前为止,我发现 Caused by.(?!.*Caused by) 基于否定的前瞻性,在我删除所有制表符和空格后给我最后一个 "Caused by" (但不是该行的其余部分)。有什么方法可以给我想要的结果吗?如果必须删除所有空格,那对我来说没问题。谢谢!

编辑:抱歉,我想我忘记了一些非常重要的事情。在 Java 中使用 'substring' 将是一个完美的解决方案,但我需要的是一个正则表达式,我可以将其用于 Logstash 中的 grok 模式。

我建议您将 substringlastIndexOf 结合使用以获得最后一次出现的 Caused by

像那样:

String lastCaused = yourStacktraceAsString.substring(yourStacktraceAsString.lastIndexOf("Caused by"));

这可以通过正则表达式来完成,但在这种情况下,仅子字符串可能更简单。假设您的异常堆栈跟踪在字符串调用 ex:

中被捕获
ex.substring(ex.lastIndexOf('Caused by:'))

正则表达式很棘手、脆弱,而且难以阅读和维护。消极的前瞻使情况变得更糟。仅在需要时使用它们。我建议简单地迭代 text.split("\n"),查找以 Caused by: 开头的行,并将该行保留在循环的末尾。

谢谢@Pshemo,你的想法让我找到了解决方案。经过数小时的反复试验,以下模式按预期工作:

(?m)(Caused by:)(?![\s\S.]*Caused by).*$

这与您在评论中的建议非常接近!

这是一个基于贪婪量词的非环视解决方案:

\A[\s\S]*\nCaused by:\s*(?<LastCausedBy>.*)\Z

the regex demo

模式匹配

  • \A - 字符串的开头
  • [\s\S]* - 任意 0+ 个字符尽可能多(实际上,抓取所有文本到末尾然后向后移动 - 回溯 - 找到最后一个...)
  • \nCaused by: - 换行符后跟 Caused by:
  • \s* - 0+ 个空格符号
  • (?<LastCausedBy>.*) - 除换行符外的任何 0+ 个字符(捕获到 LastCausedBy 命名组
  • \Z - 字符串结尾

Grok Debugger 测试: