【问题标题】:Selectively parsing log files using Java使用 Java 选择性地解析日志文件
【发布时间】:2011-06-06 10:28:31
【问题描述】:

我要解析一大堆日志文件,格式如下。

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 语句 + 错误) 对并将它们写入单独的文件中。

请教我怎么做!

【问题讨论】:

  • 您正在写“前 2 行”,但我数了三行(其中一个是空的)。由于空格在正则表达式中很重要,这很重要,所以你能指定哪种解释是正确的吗?另外,SQL 语句和较长的消息是否总是各占一行,或者可能有变化?成对的日志条目之间是否有空行?
  • @Joel - 到目前为止我还没有尝试过任何东西。我刚刚完成了一小轮讨论,刚刚发布了我的问题!
  • @Tim - 你是对的!空格确实很重要。 3 行!
  • 好的,那么你怎么知道一个 SQL 过程从哪里开始呢?是不是以DB 开头的行之前的非空行集合? (那行是否总是以DB 开头?)
  • 它将以CREATE开头。

标签: java regex bash shell text


【解决方案1】:

我的答案将不基于 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 SQL 命令成功完成。 12:DB21034E 该命令被作为 SQL 语句处理,因为它不是有效的命令行处理器命令。 19:DB21034E 该命令被作为 SQL 语句处理,因为它不是有效的命令行处理器命令。 26:DB21034E 该命令被作为 SQL 语句处理,因为它不是有效的命令行处理器命令。 34:DB20000I SQL 命令成功完成。 41:DB20000I SQL 命令成功完成。 47:DB21034E 该命令作为 SQL 语句处理,因为它不是有效的命令行处理器命令。 54:DB20000I SQL 命令成功完成。
  • 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 处理并将第一行号加一。示例:
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 >> 过滤的.txt sed -n "13,19p" log.txt >> 过滤的.txt sed -n "20,26p" log.txt >> 过滤的.txt sed -n "42,47p" log.txt >> 过滤的.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 表达式中的引用

【讨论】:

  • @darioo - 据我理解的问题 - 他不想过滤带有状态/错误的行(这些消息可能是多行的),他需要成对的 SQL 消息和相应的数据库状态/错误消息。
  • 酷正则表达式。我不知道选项-h。但是我觉得他是想自己提取sql语句,所以推荐他用switch -a (after)
  • 实际上他应该添加一个 -b 1 标志。失败的查询在错误消息之前。
  • @Toader: --before-context 1 正是这样做的
  • 我在原始帖子中又添加了一个场景 - 一大块 CREATE TABLECREATE PROCEDURE 语句位于错误行之前。如何检测和打印导致错误的整个块?
【解决方案2】:

如果您在 Windows 上使用 linux shell 或 cygwin,我建议您使用带有标志 -a(之后)和 -b(之前)的 grep:

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

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

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

遍历线条,直到遇到符合您的模式的线条。然后继续阅读 N 行(例如 2 行)并在某处打印它们。然后继续阅读。

【讨论】:

  • 在出现错误查询之前可能有 n 个成功查询。
【解决方案3】:

假设您正在查找一个非空行块,然后是一个空行,然后是一个非空行块,其中第一个以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

【讨论】:

  • 什么不起作用?无匹配?匹配错误?您能否在您的问题中复制/粘贴实际数据摘录?
【解决方案4】:

就我个人而言,我会稍有不同。我不会发现所有错误,而是删除所有成功。

类似这样的:

  • 将日志文件(使用 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("");
}

【讨论】:

    【解决方案5】:

    试试这个:

    #!/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
    

    【讨论】:

      猜你喜欢
      • 2013-12-19
      • 2020-05-10
      • 2011-01-17
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多