使用Java选择性地解析日志文件

时间:2011-01-10 09:47:20

标签: java regex bash shell text

我必须解析一大堆日志文件,它们采用以下格式。

SOME SQL STATEMENT/QUERY

DB20000I  The SQL command completed successfully.

SOME OTHER SQL STATEMENT/QUERY

DB21034E  The command was processed as an SQL statement because it was not a 
valid Command Line Processor command.

编辑1:前3行(包括空行)表示SQL语句成功执行,而后三行表示语句及其引起的异常。 darioo 下面的回复,建议使用grep而不是Java,可以很好地用于单行SQL语句。

编辑2:但是,SQL语句/查询可能不一定是单行。有时这是一个很大的CREATE PROCEDURE...END PROCEDURE块。只使用Unix命令可以解决这个问题吗?

现在我需要解析整个日志文件并选择所有出现的对象(SQL语句+错误)并将它们写在一个单独的文件中。

请告诉我该怎么做!

5 个答案:

答案 0 :(得分:4)

我的回答将是非基于Java的,因为这是一个可以用更简单的方式解决的问题的典型例子。

您只需要工具grep。如果您使用的是Windows,则可以找到它here

假设您的日志位于文件log.txt中,您问题的解决方案就是一个问题:

grep -hE --before-context 1 "^DB2[0-9]+E" log.txt > filtered.txt

说明:

  • -h - 不打印文件名
  • -E - 正则表达式搜索
  • --before-context 1 - 这将在找到错误消息之前打印一行(如果所有SQL查询都在一行中,这将有效)
  • ^DB2[0-9]+E - 搜索以“DB2”开头的行,有一些数字并以“E”结尾

上面的表达式将在名为filtered.txt的新文件中打印您需要的每一行。


更新:经过一番摸索后,我设法使用标准的* nix实用程序获得了所需的功能。小心,它不漂亮。最后的表达:

grep -nE "^DB2[0-9]+" log.txt | cut -f 1 -d " " | gawk "/E$/{y=$0;print x, y};{x=$0}" | sed -e "s/:DB2[[:digit:]]\+[IE]//g" | gawk "{print \"sed -n \\\"\" $1+1 \",\" $2 \"p\\\" log.txt \"}" | sed -e "s/$/ >> filtered.txt/g" > run.bat

说明:

  • grep -nE "^DB2[0-9]+" log.txt - 打印以DB2...开头的行及其行号。例如:
6:DB20000I  The SQL command completed successfully.
12:DB21034E  The command was processed as an SQL statement because it was not a valid Command Line Processor command.
19:DB21034E  The command was processed as an SQL statement because it was not a valid Command Line Processor command.
26:DB21034E  The command was processed as an SQL statement because it was not a valid Command Line Processor command.
34:DB20000I  The SQL command completed successfully.
41:DB20000I  The SQL command completed successfully.
47:DB21034E  The command was processed as an SQL statement because it was not a valid Command Line Processor command.
54:DB20000I  The SQL command completed successfully.
  • cut -f 1 -d " " - 仅打印“第一列”,即删除错误消息后的所有内容。例如:
6:DB20000I
12:DB21034E
19:DB21034E
26:DB21034E
34:DB20000I
41:DB20000I
47:DB21034E
54:DB20000I
  • gawk "/E$/{y=$0;print x, y};{x=$0}" - 对于以“E”结尾的每一行(错误行),在它之前打印行,然后打印错误行。例如:
6:DB20000I 12:DB21034E
12:DB21034E 19:DB21034E
19:DB21034E 26:DB21034E
41:DB20000I 47:DB21034E
  • sed -e "s/:DB2[[:digit:]]\+[IE]//g" - 删除冒号和错误消息,只留下行号。例如:
6 12
12 19
19 26
41 47
  • gawk "{print \"sed -n \\\"\" $1+1 \",\" $2 \"p\\\" log.txt \"}" - 在sed处理的行上方格式化,并将第一行号递增1。例如:
sed -n "7,12p" log.txt 
sed -n "13,19p" log.txt 
sed -n "20,26p" log.txt 
sed -n "42,47p" log.txt 
  • sed -e "s/$/ >> filtered.txt/g" - 将>> filtered.txt附加到行,以附加到最终输出文件。例如:
sed -n "7,12p" log.txt  >> filtered.txt
sed -n "13,19p" log.txt  >> filtered.txt
sed -n "20,26p" log.txt  >> filtered.txt
sed -n "42,47p" log.txt  >> filtered.txt
  • > run.bat - 最后,将最后一行打印到名为run.bat
  • 的批处理文件中

执行此文件后,您想要的内容将显示在filtered.txt

更新2

这是另一个适用于Ubuntu的版本(以前的版本是在Windows上编写的):

grep -nE "^DB2[0-9]+" log.txt | cut -f 1 -d " " | gawk '/E/{y=$0;print x, y};{x=$0}' | sed -e "s/:DB2[[:digit:]]\+[IE]//g" | gawk '{print "sed -n \""$1+1" ,"$2 "p\" log.txt" }' | sed -e "s/$/ >> filtered.txt/g" > run.sh

以前的版本有两件事情无效:

  1. 出于某种原因,gawk '/E$/'无法正常工作(它无法识别E在行尾),所以我只是放/E/,因为E不会在其他地方找到了。
  2. 引用,"因为不喜欢双引号而被转换为' gawk;之后,在最后一个gawk表达式中引用被修改

答案 1 :(得分:1)

假设您正在寻找一个非空行的块,后跟一个空行,然后是一个非空行的块,其中第一行以DB开头,然后尝试:

Pattern regex = Pattern.compile(
    "(?:.+\\n)+    # Match one or more non-blank lines\n" +
    "\\n           # Match one blank line\n" +
    "DB(?:.+\\n)+  # Match one or more non-blank lines, the first one starting with DB", 
    Pattern.COMMENTS);
Matcher regexMatcher = regex.matcher(subjectString);
while (regexMatcher.find()) {
    // matched text: regexMatcher.group()
    // match start: regexMatcher.start()
    // match end: regexMatcher.end()
}

这假定每个匹配之间有一个空行,并假定Unix行结尾。如果是DOS / Windows文件,则将\\n替换为\\r\\n

答案 2 :(得分:1)

就个人而言,我会略微区别。我没有找到所有的错误,而是取消了所有的成功。

这样的事情:

  • 读取日志文件(使用read方法,而不是readLine,因为后者会删除换行符)到字符串
  • 在字符串上使用以下正则表达式和replaceAll(regex,“”)删除所有成功的条目:(?:.+\r\n)+\r\n+DB2.+I(?:.+\r\n)+
  • 将生成的字符串写入新文件。

在代码中(只需使用日志的File对象调用processLog):

private void openAndProcessLog(){
    JFileChooser chooser = new JFileChooser();
    chooser.showOpenDialog(this);
    if (chooser.getSelectedFile() != null) {
        processLog(chooser.getSelectedFile());
    }
}

private void processLog(File logfile){
    String originalLog = readFile(logfile);
    String onlyFailures = removeAllSuccessFull(originalLog);
    System.out.println(onlyFailures);
}

private String readFile(File file) {
    String ret = "";
    try {
        BufferedReader in = new BufferedReader(
                new FileReader(file));
        StringWriter out = new StringWriter();
        char[] buf = new char[10000];
        int n;
        while( (n = in.read(buf)) >= 0 ) {
            out.write(buf, 0, n);
        }
        ret = out.toString();
    } catch (IOException e) {
    }
    return ret;
}

private String removeAllSuccessFull(String text) {
    String sep = System.getProperty("line.separator");
    Pattern regex = Pattern.compile(
            "(?:.+"+sep+")+"+sep+"+DB2.+I(?:.+"+sep+")+");
    return regex.matcher(text).replaceAll("");
}

答案 3 :(得分:1)

尝试一下:

#!/usr/bin/awk -f
$1 ~ /^DB.*I$/ {lines=""; nl=""; next} # discard successes
$1 ~ /^DB.*E$/ {print lines; print $0; print "-----"; lines=""; next} # print error blocks
$0 !~ /^$/ { lines = lines nl $0; nl="\n" } # accumulate lines in block

如果您不想删除空白行,请删除$0 !~ /^$/

像这样运行:

./script.awk inputfile

答案 4 :(得分:-1)

如果你在windows上使用linux shell或cygwin,我建议你使用grep with flags -a(after)和-b(before):

grep -a 2 "The SQL command completed successfully" mylog.log

将在与给定模式匹配的行之后打印2行。

如果您想自己编写,我建议您执行以下操作:

迭代线条,直到遇到符合您图案的线条。然后继续读N行(例如2行)并在某处打印。然后继续阅读。