从日志文件中拉出Java错误堆栈
我有一个Java应用程序,当出错时,它会为每个错误编写一个类似于下面的错误堆栈从日志文件中拉出Java错误堆栈,java,python,xml,unix,grep,Java,Python,Xml,Unix,Grep,我有一个Java应用程序,当出错时,它会为每个错误编写一个类似于下面的错误堆栈 <Errors> <Error ErrorCode="Code" ErrorDescription="Description" ErrorInfo="" ErrorId="ID"> <Attribute Name="ErrorCode" Value="Code"/> <Attribute Name="ErrorDescription"
<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堆栈
与上述类似的信息
我编写了一个Java日志解析器来遍历日志文件并收集有关此类错误的信息,虽然它确实有效,但速度慢且效率低,特别是对于数百兆字节的日志文件。我基本上只是使用字符串操作来检测开始/结束标记的位置,并对它们进行汇总
有没有一种方法(通过Unix grep、Python或Java)可以有效地提取错误并计算每次错误发生的次数?整个日志文件不是XML,因此我不能使用XML解析器或Xpath。我面临的另一个问题是,有时错误的结尾可能会滚到另一个文件中,因此当前文件可能没有上面提到的整个堆栈
编辑1:
以下是我目前拥有的(相关部分只是为了节省空间)
//解析文件
对于(文件f:所有文件){
System.out.println(“解析:+f.getAbsolutePath());
BufferedReader br=新的BufferedReader(新文件读取器(f));
字符串行=”;
字符串:堆栈=”;
而((line=br.readLine())!=null){
如果(第行包含(“”)){
堆栈=行;
而(!line.contains(“”){
line=br.readLine();
试一试{
fullErrorStack=fullErrorStack+line.trim()+“”;
}捕获(NullPointerException e){
//文件结束,但错误堆栈结束在另一个文件中。
fullErrorStack=fullErrorStack+“”;
打破
}
}
字符串errorCode=fullErrorStack.substring(fullErrorStack.indexOf(“errorCode=\”)+“errorCode=\”.length(),fullErrorStack.indexOf(\”,fullErrorStack.indexOf(“errorCode=\”));
字符串errorDescription=fullErrorStack.substring(fullErrorStack.indexOf(“errorDescription=\”)+“errorDescription=\”.length(),fullErrorStack.indexOf(\”,fullErrorStack.indexOf(“errorDescription=\”);
字符串errorStack=fullErrorStack.substring(fullErrorStack.indexOf(“”+“”.length(),fullErrorStack.indexOf(“”),fullErrorStack.indexOf(“”));
添加(f.getAbsolutePath()+拆分器+错误代码+:“+errorDescription+拆分器+errorStack.trim());
fullErrorStack=“”;
}
}
}
Set uniqueApiErrors=新哈希集(APIRRORS);
for(字符串uniqueapier:uniqueapiers){
添加(uniqueapierr+splitter+Collections.frequency(apierrs,uniqueapierr));
}
Collections.sort(apirorsunique);
编辑2:
对不起,忘了提及所需的输出。像下面这样的东西将是理想的
Count、ErrorCode、ErrorDescription、它出现在其中的文件列表(如果可能)好吧,从技术上讲,这不是grep,但是如果您愿意使用其他标准的UNIX类命令,这里有一个行程序可以完成这项工作,而且应该很快(实际上,您希望看到数据集上的结果): 简要说明
sed
仅在所选地址之间打印(此处为行)sed
使用正则表达式过滤这些错误,将标题行替换为合成的足够唯一的错误字符串(包括描述),类似于您的Java(或至少我们可以看到的)从技术上讲,它不是grep,但是如果您愿意使用其他标准的UNIX风格的命令,这里有一个单行程序可以完成这项工作,而且应该很快(实际上,您希望看到数据集上的结果): 简要说明
sed
仅在所选地址之间打印(此处为行)sed
使用正则表达式过滤这些错误,将标题行替换为合成的足够唯一的错误字符串(包括描述),类似于您的Java(或至少我们可以看到的)我假设既然您提到了unixgrep,您可能也有perl。 下面是一个简单的perl解决方案:
#!/usr/bin/perl
my %countForErrorCode;
while (<>) { /<Error ErrorCode="([^"]*)"/ && $countForErrorCode{$1}++ }
foreach my $e (keys %countForErrorCode) { print "$countForErrorCode{$e} $e\n" }
您应该得到如下输出
8 Code1
203 Code2
...
其中“Code1”等是在正则表达式中双引号之间捕获的错误代码
我和Cygwin在Windows上做的。此解决方案假定:
/usr/bin/perl
。您可以使用$验证哪个perl
/我假设既然您提到了Unix grep,那么您可能也有perl。
下面是一个简单的perl解决方案:
#!/usr/bin/perl
my %countForErrorCode;
while (<>) { /<Error ErrorCode="([^"]*)"/ && $countForErrorCode{$1}++ }
foreach my $e (keys %countForErrorCode) { print "$countForErrorCode{$e} $e\n" }
您应该得到如下输出
8 Code1
203 Code2
...
其中“Code1”等是在正则表达式中双引号之间捕获的错误代码
我和Cygwin在Windows上做的。此解决方案假定:
perl的位置是/usr/bin/perl
。您可以使用$验证哪个perl
上面的正则表达式,/给出了您的更新问题:
$ cat tst.awk
BEGIN{ OFS="," }
match($0,/\s+*<Error ErrorCode="([^"]+)" ErrorDescription="([^"]+)".*/,a) {
code = a[1]
desc[code] = a[2]
count[code]++
files[code][FILENAME]
}
END {
print "Count", "ErrorCode", "ErrorDescription", "List of files it occurs in"
for (code in desc) {
fnames = ""
for (fname in files[code]) {
fnames = (fnames ? fnames " " : "") fname
}
print count[code], code, desc[code], fnames
}
}
$
$ awk -f tst.awk file
Count,ErrorCode,ErrorDescription,List of files it occurs in
1,Code,Description,file
如果您的数据分布在多个文件中,那么上面的内容就不重要了,只需在命令行中列出它们:
gawk -f tst.awk file1 file2 file3 ...
它使用GNU awk 4.*来实现真正的多维数组,但如果需要的话,任何其他awk都有一些简单的解决方法
对在目录下递归找到的文件运行awk命令的一种方法:
awk -f tst.awk $(find dir -type f -print)
鉴于你的最新问题:
$ cat tst.awk
BEGIN{ OFS="," }
match($0,/\s+*<Error ErrorCode="([^"]+)" ErrorDescription="([^"]+)".*/,a) {
code = a[1]
desc[code] = a[2]
count[code]++
files[code][FILENAME]
}
END {
print "Count", "ErrorCode", "ErrorDescription", "List of files it occurs in"
for (code in desc) {
fnames = ""
for (fname in files[code]) {
fnames = (fnames ? fnames " " : "") fname
}
print count[code], code, desc[code], fnames
}
}
$
$ awk -f tst.awk file
Count,ErrorCode,ErrorDescription,List of files it occurs in
1,Code,Description,file
如果您的数据分布在多个文件中,那么上面的内容就不重要了,只需在命令行中列出它们:
gawk -f tst.awk file1 file2 file3 ...
它使用GNU awk 4.*来实现真正的多维数组,但如果需要的话,任何其他awk都有一些简单的解决方法
对在目录下递归找到的文件运行awk命令的一种方法:
awk -f tst.awk $(find dir -type f -print)
你能发布你目前使用的Java代码吗
$ cat tst.awk
match($0,/\s+*<Attribute Name="([^"]+)" Value="([^"]+)".*/,a) { count[a[1]][a[2]]++ }
END {
print "\nIf you just want to see the count of all error codes:"
name = "ErrorCode"
for (value in count[name]) {
print name, value, count[name][value]
}
print "\nOr if theres a few specific attributes you care about:"
split("ErrorId ErrorCode",names,/ /)
for (i=1; i in names; i++) {
name = names[i]
for (value in count[name]) {
print name, value, count[name][value]
}
}
print "\nOr if you want to see the count of all values for all attributes:"
for (name in count) {
for (value in count[name]) {
print name, value, count[name][value]
}
}
}
$ gawk -f tst.awk file
If you just want to see the count of all error codes:
ErrorCode Code 1
Or if theres a few specific attributes you care about:
ErrorId ID 1
ErrorCode Code 1
Or if you want to see the count of all values for all attributes:
ErrorId ID 1
ErrorDescription Description 1
ErrorCode Code 1
Number Number 1
ProgId Prog 1
UserId User 1
Key Key 1
gawk -f tst.awk file1 file2 file3 ...
awk -f tst.awk $(find dir -type f -print)