Java原生Logger默认日志级别为INFO,故fine/fine/finest等调试日志不输出;需同时设置logger.setLevel(Level.FINE)和handler.setLevel(Level.FINE),并禁用父处理器以防根Logger的INFO级别二次过滤。

Logger 默认不输出 DEBUG 级别日志,得手动调低级别
Java 原生 Logger 默认日志级别是 INFO,所以直接调用 logger.fine("debug info") 或 logger.finest("trace") 不会输出任何内容——不是代码错了,是级别被拦住了。
必须显式设置 Logger 实例及其父 Handler 的级别:
-
logger.setLevel(Level.FINE):让 Logger 接收 FINE 及以上级别(FINE、FINER、FINEST)的日志 -
handler.setLevel(Level.FINE):JUL 默认使用ConsoleHandler,它也有自己的级别开关,不设就只输出 INFO+(ConsoleHandler默认是 INFO) - 别漏掉
logger.setUseParentHandlers(false),否则日志可能被根 Logger 的INFO级别再次过滤
用 fine/finer/finest 而不是 info/warning,才叫“调试信息”
调试信息的核心是「开发期需要、运行期可关闭」,java.util.logging 为此专门设计了 FINE(对应 debug)、FINER(对应 trace)、FINEST(极细粒度)三个级别。它们和 info()、warning() 是平行关系,不是子集。
常见误用:
立即学习“Java免费学习笔记(深入)”;
- 用
logger.info("debug: value=" + obj)—— 这属于“伪调试”,字符串拼接无法避免,且上线后仍执行 - 正确写法是
logger.fine("value={0}", obj),参数延迟格式化,只有在日志真正启用时才计算 - 若需条件性调试(比如只在某个 flag 为 true 时打日志),先用
logger.isLoggable(Level.FINE)判断,避免无谓的对象构造
配置文件比代码硬编码更可靠,但路径和命名有坑
通过 logging.properties 配置全局行为最稳妥,但容易因加载时机或路径失败而静默回退到默认配置。
关键点:
- JVM 启动时加
-Djava.util.logging.config.file=/path/to/logging.properties,路径必须绝对且文件可读 - 配置中要明确指定 Handler 级别,例如:
java.util.logging.ConsoleHandler.level = FINE - 包名或 logger 名的配置项必须完全匹配,比如
com.example.MyService.level = FINE,少个点或大小写错都不生效 - 如果没指定
handlers,根 Logger 可能不绑定任何 Handler,导致日志“发出去却没人收”
生产环境慎用 ConsoleHandler,FileHandler 性能要注意
开发时用 ConsoleHandler 直观,但生产环境应换 FileHandler,不过它默认同步写入,高并发下可能拖慢应用。
优化建议:
- 创建
FileHandler时传true开启 append 模式:new FileHandler("app.log", true) - 避免频繁新建 Handler(比如每次请求都 new 一个),应复用单例实例
- 注意
FileHandler的默认缓冲区很小,大量日志可能触发频繁 flush;如需更高吞吐,可包装成异步(但 JUL 本身不提供内置 AsyncHandler,得自己套ExecutorService或换 Log4j/SLF4J)
真正难的不是怎么打日志,而是怎么确保调试信息在需要时出现、不需要时彻底消失,且不拖慢系统——JUL 的级别链路(Logger → Handler → Formatter)、延迟求值机制、配置加载时机,每个环节断掉都会让调试日志失效。



















