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