日志丢失主因是异步队列未消费、jvm异常退出、级别/过滤器拦截、appender配置失效或系统资源不足。需从框架行为、配置、线程、环境四方面排查:检查asyncappender/asynclogger队列是否清空,确认shutdown钩子调用,验证日志级别继承与filter策略,核实appender路径可写及无冲突,排查磁盘、句柄、多线程mdc传递等问题。

日志丢失通常不是代码没写日志,而是日志根本没落盘或被拦截了。排查要从日志框架行为、配置、线程和运行环境四方面入手。
检查日志框架的异步机制是否丢日志
Logback AsyncAppender、Log4j2 AsyncLogger 等异步组件默认启用队列缓冲,JVM 异常退出(如 kill -9)或程序未正常 shutdown 时,队列中未消费的日志会直接丢失。
- 确认是否使用了异步 Appender:查看配置中是否有 AsyncAppender 或 asyncRoot
- 设置合理的队列大小和丢弃策略:例如 Logback 的 discardingThreshold="0" 可禁用丢弃,但需配合 includeCallerData="false" 减少开销
- 确保 JVM 退出前调用 LoggerContext.stop()(Logback)或 LogManager.shutdown()(Log4j2)
验证日志级别和过滤器是否误拦
日志没输出,很可能是被级别限制或自定义 Filter 拦截了,尤其在多环境配置切换时容易出错。
在 Java 中初始化和管理阿里云 SDK客户端。包括单例模式、线程安全、endpoint 与 region 配置、VPC 终端节点、同步与异步等。
- 检查 logger 实例的实际有效级别:用 logger.getLevel() 打印,注意它可能继承自父 logger,不一定等于配置文件写的 level
- 排查 TurboFilter(Logback)或 Custom Filter(Log4j2)是否返回 DENY,可在测试时临时注释过滤器验证
- 确认 root logger 和具体 logger 的级别关系,比如 com.example.service 设为 DEBUG,但 root 是 WARN,则仍不输出
确认日志输出目标是否真正生效
看似配置了 FileAppender,但日志可能写到了控制台、内存、甚至被重定向到 /dev/null。
- 检查 appender 的 file 路径是否可写:用 new File("path").canWrite() 验证;相对路径注意是相对于 JVM 启动目录,不是项目根目录
- 观察是否有多个 appender 冲突:比如同时配置了 ConsoleAppender 和 RollingFileAppender,但只看到控制台输出,说明日志确实发出,只是没进文件
- 留意 Spring Boot 的 logging.file.name 和 logging.logback.rollingpolicy.* 是否覆盖了自定义 logback-spring.xml 中的同名配置
排查运行时资源与线程问题
磁盘满、句柄耗尽、线程阻塞等系统级问题也会导致日志静默失败,且往往无异常抛出。
- 检查磁盘空间:df -h,Logback 默认遇到 IO 异常会静默吞掉日志(可通过 neverBlock="true" 改为阻塞或加 error handler)
- 验证文件句柄数:lsof -p $PID | wc -l,若接近系统上限,FileAppender 可能无法打开新文件
- 确认日志打印是否在非主线程中执行:比如定时任务、线程池、CompletableFuture,需确保 logger 实例在线程内可访问,避免因 MDC 未传递或 SLF4J 绑定错误导致空日志
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南










