求助:如何用正则表达式解析多行日志条目(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
相关产品推荐
相关产品推荐

