Logger.getLogger行为不一致:变量内联与否引发差异
我之前也踩过类似的日志配置坑,结合你的场景来拆解问题和解决方法:
先明确你的核心问题:在Eclipse运行的标准J2SE应用里,用PDFBox+PDF2DOM做PDF转HTML时,想屏蔽多余的INFO日志,但发现Logger.getLogger()的效果居然和变量是否内绑定有关——内联调用和先赋值给本地变量再设置级别,结果完全不一致;而且你的逻辑是在实例方法里每次处理单个文件执行一次,本地变量不会被复用,每次迭代后GC会清理该字段。
可能的原因
这种不一致大概率和java.util.logging的Logger实例缓存机制有关:当你内联调用时,JVM可能因为即时编译优化、类加载的细微差异,导致两次获取的Logger实例不是同一个;而非内联时,本地变量持有了实例,设置级别是生效的,但下次方法调用重新获取时,可能又拿到了未配置的全新实例。
解决办法
1. 用全局日志配置文件(最推荐)
别在代码里零散设置日志级别,直接用JDK自带的logging.properties统一配置,一劳永逸。
在项目根目录新建logging.properties文件,内容如下:
# 全局默认日志级别 .level=WARNING # 专门针对PDFBox的包设置级别 org.apache.pdfbox.level=WARNING # 针对PDF2DOM的包设置级别 org.fit.pdf2dom.level=WARNING
然后在Eclipse的Run Configuration里,给你的main类添加VM参数:-Djava.util.logging.config.file=./logging.properties
这样不管你代码里怎么调用Logger,全局配置都会生效,彻底避免实例不一致的问题。
2. 把Logger实例设为类静态变量
如果必须在代码里动态调整日志级别,别用本地变量,把Logger实例做成类的静态常量,这样每次方法调用都会复用同一个实例,设置级别只会生效一次:
public class PdfToHtmlConverter { // 提前拿到PDFBox和PDF2DOM的根Logger private static final Logger PDFBOX_ROOT_LOGGER = Logger.getLogger("org.apache.pdfbox"); private static final Logger PDF2DOM_ROOT_LOGGER = Logger.getLogger("org.fit.pdf2dom"); public void convertSingleFile(File pdfFile) { // 确保日志级别设置一次就生效 PDFBOX_ROOT_LOGGER.setLevel(Level.WARNING); PDF2DOM_ROOT_LOGGER.setLevel(Level.WARNING); // 后续的PDF加载和转换逻辑 try (PDDocument doc = PDDocument.load(pdfFile)) { // PDF2DOM转换代码... } catch (IOException e) { e.printStackTrace(); } } }
这样不管你处理多少个文件,都是同一个Logger实例,不会出现内联与否的差异。
3. 排查类加载器差异(极端情况)
如果上面两种方法都不行,可以打印Logger实例的哈希值,看看内联和非内联时是不是同一个对象:
// 非内联方式 Logger loggerLocal = Logger.getLogger(org.apache.pdfbox.PDDocument.class); System.out.println("非内联Logger哈希:" + loggerLocal.hashCode()); loggerLocal.setLevel(Level.WARNING); // 内联方式 Logger loggerInline = Logger.getLogger(org.apache.pdfbox.PDDocument.class); System.out.println("内联Logger哈希:" + loggerInline.hashCode()); loggerInline.setLevel(Level.WARNING);
如果哈希值不同,说明是类加载器导致的实例重复,这时候可以尝试用上下文类加载器来获取Logger:
Class<?> pdfDocClass = Thread.currentThread().getContextClassLoader().loadClass("org.apache.pdfbox.PDDocument"); Logger logger = Logger.getLogger(pdfDocClass.getName()); logger.setLevel(Level.WARNING);
总结
优先用全局配置文件管理日志,这是最规范也最不容易出问题的方式;如果必须代码设置,静态持有Logger实例是可靠的选择。
内容的提问来源于stack exchange,提问作者Serban

