You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

求助:如何用正则表达式解析多行日志条目(Java环境可选)

解析多行日志条目:解决思路与实现

问题说明

我用Java 8正则解析多行日志条目时遇到了大麻烦,希望能得到解决思路,而且我不局限于Java 8环境。

日志格式规则:每个日志条目以类似12:56:13.861 [http-nio-7777-exec-1] INFO dg.mpro.filter.CustomRequestLoggingFilter - REST REQUEST的行开头,以空行结尾,条目内容跨越多行。

期望结果:能够单独处理每个完整的日志条目。

当前尝试的无效正则:

[0-9]{1,2}+:[0-9]{1,2}+:[0-9]{1,2}+\\.[0-9]{1,3}+.*Response\\stime:.*\n

日志示例(entries.log):

12:56:13.861 [http-nio-7777-exec-1] INFO  dg.mpro.filter.CustomRequestLoggingFilter - REST REQUEST
NUMBERS OF REQUESTS:= [1]
METHOD:= [GET]
PARAMETERS & VALUES :=
REQUEST:= [/rest/indexv2/getMetadata]
12:56:13.861 [http-nio-7777-exec-1] INFO  dg.mpro.filter.CustomRequestLoggingFilter - Response time: 1 seconds

13:14:46.049 [http-nio-7777-exec-2] INFO  dg.mpro.filter.CustomRequestLoggingFilter - REST REQUEST
NUMBERS OF REQUESTS:= [2]
METHOD:= [GET]
PARAMETERS & VALUES := [pagesize:= 20][name:= Mosolv][page:= 1][tolerance:= 4]
REQUEST:= [/rest/indexv2/getProds/{{licensekey}}/{{username}}/%7B%22tolerance%22:%224%22,%22pagesize%22:%2220%22,%22name%22:%22Mosolv%22,%22page%22:%221%22%7D]
13:14:46.050 [http-nio-7777-exec-2] INFO  dg.mpro.filter.CustomRequestLoggingFilter - Response time: 2 seconds

12:57:14.812 [http-nio-7777-exec-1] INFO  dg.mpro.filter.CustomRequestLoggingFilter - REST REQUEST
NUMBERS OF REQUESTS:= [3]
METHOD:= [GET]
PARAMETERS & VALUES :=
REQUEST:= [/rest/indexv2/getMetadata]
12:57:14.813 [http-nio-7777-exec-1] INFO  dg.mpro.filter.CustomRequestLoggingFilter - Response time: 1 seconds

解决方法

方法一:正则表达式匹配(支持Java 8+)

核心逻辑是精准匹配从条目起始行到结束行(含空行分隔符)的所有内容,利用非贪婪模式避免跨条目匹配。

正则表达式:

^\d{1,2}:\d{1,2}:\d{1,2}\.\d{3} \[.*?\] INFO  dg\.mpro\.filter\.CustomRequestLoggingFilter - REST REQUEST\n(?:.*\n)*?^\d{1,2}:\d{1,2}:\d{1,2}\.\d{3} \[.*?\] INFO  dg\.mpro\.filter\.CustomRequestLoggingFilter - Response time: .*?\n\n

关键细节:

  • ^\d{1,2}:\d{1,2}:\d{1,2}\.\d{3} \[.*?\] INFO dg\.mpro\.filter\.CustomRequestLoggingFilter - REST REQUEST:匹配条目起始行,^锁定行开头,.*?非贪婪匹配线程名,防止过度截取。
  • (?:.*\n)*?:非捕获组,匹配起始行与结束行之间的所有内容,*?确保不会跨条目匹配。
  • ^\d{1,2}:\d{1,2}:\d{1,2}\.\d{3} \[.*?\] INFO dg\.mpro\.filter\.CustomRequestLoggingFilter - Response time: .*?\n\n:匹配条目结束行,最后的\n\n匹配分隔用的空行。

Java 8代码示例:

import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

public class LogRegexParser {
    public static void main(String[] args) throws Exception {
        String logContent = new String(Files.readAllBytes(Paths.get("entries.log")));
        Pattern pattern = Pattern.compile(
            "^\\d{1,2}:\\d{1,2}:\\d{1,2}\\.\\d{3} \\[.*?\\] INFO  dg\\.mpro\\.filter\\.CustomRequestLoggingFilter - REST REQUEST\\n(?:.*\\n)*?^\\d{1,2}:\\d{1,2}:\\d{1,2}\\.\\d{3} \\[.*?\\] INFO  dg\\.mpro\\.filter\\.CustomRequestLoggingFilter - Response time: .*?\\n\\n",
            Pattern.MULTILINE
        );
        Matcher matcher = pattern.matcher(logContent);
        
        while (matcher.find()) {
            String entry = matcher.group().trim();
            // 这里替换为你的条目处理逻辑
            System.out.println("=== 日志条目 ===");
            System.out.println(entry);
            System.out.println("===============");
        }
    }
}

方法二:逐行读取(直观易维护)

如果正则调试成本高,可通过逐行遍历+状态标记的方式收集条目,逻辑更清晰,适合复杂日志扩展。

Java代码示例:

import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.List;

public class LogLineParser {
    public static void main(String[] args) throws Exception {
        List<String> lines = Files.readAllLines(Paths.get("entries.log"));
        List<String> currentEntry = new ArrayList<>();
        boolean inEntry = false;

        for (String line : lines) {
            String trimmedLine = line.trim();
            // 检测条目起始行
            if (trimmedLine.matches("^\\d{1,2}:\\d{1,2}:\\d{1,2}\\.\\d{3} \\[.*?\\] INFO  dg\\.mpro\\.filter\\.CustomRequestLoggingFilter - REST REQUEST$")) {
                inEntry = true;
                currentEntry.add(line);
            } else if (inEntry) {
                if (trimmedLine.isEmpty()) {
                    // 遇到空行,结束当前条目并处理
                    processEntry(currentEntry);
                    currentEntry.clear();
                    inEntry = false;
                } else {
                    currentEntry.add(line);
                }
            }
        }
        // 处理文件末尾无空行的最后一条条目
        if (!currentEntry.isEmpty()) {
            processEntry(currentEntry);
        }
    }

    private static void processEntry(List<String> entry) {
        // 这里替换为你的条目处理逻辑
        System.out.println("=== 日志条目 ===");
        entry.forEach(System.out::println);
        System.out.println("===============");
    }
}

优点:

  • 逻辑直观,容易调试和扩展(比如提取METHOD、REQUEST等字段时,直接在processEntry中处理)
  • 无需复杂正则,降低维护成本

补充说明

  • 若日志最后一条条目无结尾空行,正则需调整为匹配$(文件结尾),或在非正则方法中保留最后未处理的条目(如上例)。
  • Java 9+可使用Files.lines()流式读取,提升大文件处理效率。

内容的提问来源于stack exchange,提问作者William

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.13 00:59:57