登录
首页 >  文章 >  java教程

Java日志记录与文件写入教程

时间:2026-01-14 14:12:34 466浏览 收藏

小伙伴们对文章编程感兴趣吗?是否正在学习相关知识点?如果是,那么本文《Java日志记录实现与文件写入教程》,就很适合你,本篇文章讲解的知识点主要包括。在之后的文章中也会多多分享相关知识点,希望对大家的知识积累有所帮助!

Java日志需避开java.util.logging的FileHandler缺陷,优先选Log4j2 RollingFileAppender(配append="false"与immediateFlush="true")或调优SLF4J+Logback异步队列,禁用裸Files.write。

在Java里实现日志记录功能_Java文件写入实战说明

java.util.logging 写日志到文件太麻烦?别硬扛

标准库的 FileHandler 默认不自动轮转、不压缩、路径出错静默失败,且 SimpleFormatter 输出格式固定难调试。实际项目里直接用它写生产日志,大概率会在凌晨三点被报警叫醒。

  • FileHandler 构造时若目录不存在,会抛 IOException 而不是自动创建——得手动 new File("logs").mkdirs()
  • 日志级别需显式调用 setLevel(),父 logger(如 Logger.getGlobal())的级别默认是 INFO,子 handler 不继承
  • 多个 JVM 实例同时写同一日志文件会冲突,FileHandler 没有跨进程锁机制

Log4j2 的 RollingFileAppender 怎么配才不丢日志

关键在 append="false"immediateFlush="true" 的组合。设为 false 会导致重启后覆盖旧日志;设为 true 但没开 immediateFlush,断电或 kill -9 时最后几条日志就丢了。

<RollingFile name="RollingFile" fileName="logs/app.log"
             filePattern="logs/app-%d{yyyy-MM-dd}-%i.log.gz">
  <PatternLayout>
    <Pattern>%d %p %c{1.} [%t] %m%n</Pattern>
  </PatternLayout>
  <Policies>
    <TimeBasedTriggeringPolicy />
    <SizeBasedTriggeringPolicy size="10 MB"/>
  </Policies>
  <DefaultRolloverStrategy max="30"/>
</RollingFile>
  • fileName 是当前活跃日志路径,filePattern 是归档命名规则,两者必须不同,否则启动报错
  • TimeBasedTriggeringPolicySizeBasedTriggeringPolicy 是“或”关系,满足任一即滚动
  • max="30" 表示最多保留 30 个归档文件,超量时删最老的——注意磁盘空间监控

SLF4J + Logback 的 AsyncAppender 为什么反而变慢了

异步日志不是加个 wrapper 就万事大吉。默认队列大小是 256,如果日志量突增(比如全链路 trace 日志打开),队列满后会阻塞业务线程,比同步还卡。

  • 必须显式配置 queueSize,建议按峰值 QPS × 平均每请求日志行数 × 2 来估算
  • includeCallerData="true" 会触发堆栈遍历,CPU 开销翻倍,仅调试时开启
  • 异步 appender 下游若接的是 RollingFileAppender,滚动动作仍发生在异步线程里,IO 延迟会影响整个队列消费速度

自定义日志写入:绕过框架直接 Files.write() 安不安全

安全,但只适合极简场景(如启动脚本记录 PID 或单次错误快照)。直接用 NIO 写文件没有缓冲区管理、无并发控制、无编码处理,StandardOpenOption.APPEND 在 Windows 上对大文件性能陡降。

try {
  Files.write(Paths.get("logs/startup.log"),
              ("[" + Instant.now() + "] started\n").getBytes(StandardCharsets.UTF_8),
              StandardOpenOption.CREATE, StandardOpenOption.APPEND);
} catch (IOException e) {
  // 这里不能只打印 e.printStackTrace() —— 日志系统可能还没初始化
  System.err.println("Failed to write startup log: " + e.getMessage());
}
  • 每次写都 new 字节数组 + 系统调用,高频写入下 GC 和 syscall 开销明显
  • 没做字符集校验,如果字符串含 BOM 或代理对,String.getBytes() 可能截断
  • 多线程写同一文件必须自己加 synchronized 或用 FileChannel.lock(),后者在 NFS 上不可靠
真正要稳,就别碰裸 IO。Log4j2 的 RandomAccessFileAppender 在高吞吐下表现更可控,但前提是把 bufferSizeimmediateFlush 的取舍想清楚——这俩参数背后是持久性与性能的硬博弈。

以上就是本文的全部内容了,是否有顺利帮助你解决问题?若是能给你带来学习上的帮助,请大家多多支持golang学习网!更多关于文章的相关知识,也可关注golang学习网公众号。

前往漫画官网入口并下载 ➜
相关阅读
更多>
最新阅读
更多>
课程推荐
更多>