使用Java 8 Stream按线程前缀合并滚动日志文件
Java 8 Stream实现按线程前缀合并滚动日志文件
嘿,这个需求用Java 8 Stream来实现完全没问题,刚好能衔接你现有的代码逻辑!核心思路就是先按线程前缀分组日志文件,再按日期排序每组内的文件,最后批量合并到对应前缀的输出文件,具体实现如下:
完整实现代码
import java.io.IOException; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; import java.nio.file.StandardOpenOption; import java.util.Comparator; import java.util.Map; import java.util.stream.Collectors; public class LogMerger { public static void main(String[] args) throws IOException { Path logFolder = Paths.get("path/to/log/folder"); // 1. 按线程前缀分组,同时按日志日期排序每组内的文件 Map<String, java.util.List<Path>> groupedLogs = Files.list(logFolder) // 过滤.log文件,和你之前的逻辑一致 .filter(path -> path.getFileName().toString().endsWith(".log")) .collect(Collectors.groupingBy( // 提取线程前缀:比如thread1.2018-04-09.log -> thread1 path -> path.getFileName().toString().split("\\.")[0], // 对每组内的文件按日期排序,确保合并顺序符合日志时间线 Collectors.collectingAndThen( Collectors.toList(), logPaths -> logPaths.stream() .sorted(Comparator.comparing(path -> { String fileName = path.getFileName().toString(); // 提取日期部分用于排序:比如thread1.2018-04-09.log -> 2018-04-09 return fileName.substring(fileName.indexOf(".") + 1, fileName.lastIndexOf(".")); })) .collect(Collectors.toList()) ) )); // 2. 遍历分组,合并每组日志到对应输出文件 groupedLogs.forEach((threadPrefix, sortedLogPaths) -> { Path outputFile = logFolder.resolve(threadPrefix + ".merged.log"); try { // 先创建/清空输出文件,避免残留旧内容 Files.write(outputFile, new byte[0], StandardOpenOption.CREATE, StandardOpenOption.TRUNCATE_EXISTING); // 逐个追加日志内容,保持顺序 for (Path logPath : sortedLogPaths) { Files.write(outputFile, Files.readAllBytes(logPath), StandardOpenOption.APPEND); // 可选:在不同日志文件之间添加换行符,避免内容粘连 Files.write(outputFile, System.lineSeparator().getBytes(), StandardOpenOption.APPEND); } System.out.println("合并完成:" + outputFile.getFileName()); } catch (IOException e) { System.err.println("合并线程[" + threadPrefix + "]日志失败:" + e.getMessage()); e.printStackTrace(); } }); } }
关键细节说明
- 分组逻辑:用
split("\\.")[0]提取线程前缀,如果你日志文件名格式有变化(比如前缀包含点),可以改用正则匹配更严谨,比如:path -> { String fileName = path.getFileName().toString(); return fileName.replaceFirst("\\.\\d{4}-\\d{2}-\\d{2}\\.log$", ""); } - 排序必要性:滚动日志是按日期生成的,必须按日期排序后再合并,否则日志时间线会混乱,这里直接用日期字符串排序(因为
yyyy-MM-dd格式的字符串本身是可排序的)。 - 大文件优化:如果日志文件很大,建议用
Files.copy逐流复制而不是readAllBytes,避免内存溢出:Files.copy(logPath, Files.newOutputStream(outputFile, StandardOpenOption.APPEND)); - 衔接原有代码:如果你想复用之前的
parseLog方法,可以在分组后先解析每组文件再合并,而不是直接合并原始内容。
内容的提问来源于stack exchange,提问作者Ben
相关产品推荐
相关产品推荐

