Skip to main content
 首页 » 编程设计

java之从日志文件中提取 Java 错误堆栈

2025年05月04日21del

我有一个 Java 应用程序,当出现错误时,它会为每个错误写入类似于下面的错误堆栈。

<Errors> 
    <Error ErrorCode="Code" ErrorDescription="Description" ErrorInfo="" ErrorId="ID"> 
        <Attribute Name="ErrorCode" Value="Code"/> 
        <Attribute Name="ErrorDescription" Value="Description"/> 
        <Attribute Name="Key" Value="Key"/> 
        <Attribute Name="Number" Value="Number"/> 
        <Attribute Name="ErrorId" Value="ID"/> 
        <Attribute Name="UserId" Value="User"/> 
        <Attribute Name="ProgId" Value="Prog"/> 
        <Stack>typical Java stack</Stack> 
    </Error> 
    <Error> 
      Similar info to the above 
    </Error> 
</Errors> 

我编写了一个 Java 日志解析器来遍历日志文件并收集有关此类错误的信息,虽然它确实有效,但速度慢且效率低下,尤其是对于数百兆字节的日志文件。我基本上只是使用字符串操作来检测开始/结束标记的位置并计算它们。

有没有一种方法(通过 Unix grep、Python 或 Java)有效地提取错误并计算每个错误发生的次数?整个日志文件不是 XML,所以我不能使用 XML 解析器或 Xpath。我面临的另一个问题是,有时错误的结尾可能会滚动到另一个文件中,因此当前文件可能不会像上面那样包含整个堆栈。

编辑 1:

这是我目前拥有的(相关部分只是为了节省空间)。

//Parse files 
for (File f : allFiles) { 
   System.out.println("Parsing: " + f.getAbsolutePath()); 
   BufferedReader br = new BufferedReader(new FileReader(f)); 
   String line = ""; 
   String fullErrorStack = ""; 
   while ((line = br.readLine()) != null) {      
      if (line.contains("<Errors>")) { 
         fullErrorStack = line; 
         while (!line.contains("</Errors>")) { 
            line = br.readLine(); 
            try { 
               fullErrorStack = fullErrorStack + line.trim() + " "; 
            } catch (NullPointerException e) { 
               //End of file but end of error stack is in another file. 
               fullErrorStack = fullErrorStack + "</Stack></Error></Errors> "; 
               break; 
            } 
         } 
         String errorCode = fullErrorStack.substring(fullErrorStack.indexOf("ErrorCode=\"") + "ErrorCode=\"".length(), fullErrorStack.indexOf("\" ", fullErrorStack.indexOf("ErrorCode=\""))); 
         String errorDescription = fullErrorStack.substring(fullErrorStack.indexOf("ErrorDescription=\"") + "ErrorDescription=\"".length(), fullErrorStack.indexOf("\" ", fullErrorStack.indexOf("ErrorDescription=\""))); 
         String errorStack = fullErrorStack.substring(fullErrorStack.indexOf("<Stack>") + "<Stack>".length(), fullErrorStack.indexOf("</Stack>", fullErrorStack.indexOf("<Stack>"))); 
         apiErrors.add(f.getAbsolutePath() + splitter + errorCode + ": " + errorDescription + splitter + errorStack.trim()); 
         fullErrorStack = ""; 
      } 
   } 
} 
 
 
Set<String> uniqueApiErrors = new HashSet<String>(apiErrors); 
for (String uniqueApiError : uniqueApiErrors) { 
    apiErrorsUnique.add(uniqueApiError + splitter + Collections.frequency(apiErrors, uniqueApiError)); 
} 
Collections.sort(apiErrorsUnique); 

编辑 2:

抱歉忘记提及所需的输出。像下面这样的东西是理想的。

Count、ErrorCode、ErrorDescription、它出现的文件列表(如果可能)

请您参考如下方法:

好吧,从技术上讲,这不是 grep,但如果您愿意使用其他标准的 UNIX 式命令,这里有一个可以完成这项工作的单行代码,而且应该很快(会实际上,有兴趣在您的数据集上查看结果):

sed -r -e '/Errors/,/<\/Errors>/!d' *.log -ne 's/.*<Error\s+ErrorCode="([^"]*)"\s+ErrorDescription="([^"]*)".*$/\1: \2/p' | sort | uniq -c | sort -nr 

假设它们按日期顺序排列,*.log glob还将解决日志滚动的问题(当然要调整以匹配您的日志命名)。

示例输出

根据我基于你的(可疑的)测试数据:

 10 SomeOtherCode: This extended description 
  4 Code: Description 
  3 ReallyBadCode: Disaster Description 

简要说明

  1. 使用 sed 仅在选定的地址(此处为行)之间打印
  2. 再次使用 sed 以正则表达式过滤这些,用组合的足够唯一的错误字符串(包括描述)替换标题行,类似于您的 Java(或者至少我们可以看到的它)
  3. 对这些独特的字符串进行排序和计数
  4. 按出现频率降序排列