从日志文件中拉出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"

我有一个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堆栈
与上述类似的信息
我编写了一个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上做的。此解决方案假定:

  • perl的位置是
    /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)