关键在于控制缓存粒度与刷新时机,集合仅作极短时缓冲或配合异步队列解耦;纯内存集合长期缓存会导致实时性丧失和崩溃丢日志;轻量场景用filewriter+flush,可靠场景用blockingqueue+消费者线程,首选printwriter自动刷新。

用集合缓存日志再配合基础流写入,本质是“先暂存、后批量落盘”,但它和真正意义的“实时写入”存在天然矛盾——集合缓存会延迟输出,而实时日志要求每条记录尽快可见。所以关键不在于“用集合缓存”,而在于**控制缓存粒度与刷新时机**,让集合只作为轻量中转,不阻塞日志时效性。
为什么不用纯内存集合长期缓存日志
集合(如ArrayList、LinkedList)本身不带持久化能力,全靠后续流写入;若等集合攒满1000条再刷盘,就失去实时性。更严重的是:程序崩溃时未刷盘的日志会永久丢失。因此,集合仅适合做极短时缓冲(例如单次批量写入前的临时聚合),或配合异步线程+队列实现解耦,而非替代流的实时能力。
轻量场景:集合 + FileWriter 追加写入(单线程)
适用于调试、脚本工具或低频日志,结构简单、无依赖:
- 用ArrayList暂存几条日志(非必须,可直接边生成边写)
- 创建FileWriter时传
true开启追加模式:new FileWriter("app.log", true) - 套一层BufferedWriter提升IO效率,但每次写完必须调用
flush() - 务必用try-with-resources确保流关闭,避免句柄泄漏
示例片段:
try (BufferedWriter writer = new BufferedWriter(new FileWriter("app.log", true))) {
List<string> batch = Arrays.asList(
"[INFO] 启动完成",
"[DEBUG] 加载配置文件 config.yml"
);
for (String line : batch) {
writer.write(line);
writer.newLine();
writer.flush(); // 每行都刷盘,保实时
}
}</string>
可靠场景:集合作消息队列中转(多线程安全)
当多个线程产生日志,又不想用Log4j2等框架时,可用BlockingQueue模拟“日志集合缓存”,由单独消费者线程落盘:
- 定义
BlockingQueue<string> logQueue = new LinkedBlockingQueue()</string> - 各业务线程调用
logQueue.offer(logLine)快速入队(非阻塞) - 启动一个守护线程,循环
logQueue.poll()取日志,用FileWriter+flush写入文件 - 这样既避免了多线程争抢文件流,又保证日志按序写入,还不会因某次IO卡住主线程
绕过集合:更推荐的“伪缓存”实时写法
其实多数情况下,不需要显式用集合缓存。直接用带自动刷新的PrintWriter更简洁可靠:
-
PrintWriter pw = new PrintWriter(new FileWriter("app.log", true), true)—— 第二个true即开启自动flush - 每次调用
pw.println("..."),内容立刻写入文件,无需手动flush,也不依赖集合暂存 - 同时可并行调用
System.out.println()输出到控制台,二者完全独立
这种写法逻辑清晰、无丢日志风险、代码量少,是轻量级实时日志的首选方案。











