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

使用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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.22 09:48:12