在Serializable Java类中使用Logger的正确方法是什么?

我在一个正在研究的系统中有以下( doctored )类, Findbugs正在生成一个SE_BAD_FIELD警告,我试图理解为什么它会在我按照我认为的方式修复之前说出来。 我感到困惑的原因是因为描述似乎表明我在类中没有使用其他非可序列化的实例字段,但bar.model.Foo也不是可序列化的并且以完全相同的方式使用(就我而言)可以告诉)但Findbugs没有发出任何警告。

import bar.model.Foo; import java.io.File; import java.io.Serializable; import java.util.List; import org.slf4j.Logger; import org.slf4j.LoggerFactory; public class Demo implements Serializable { private final Logger logger = LoggerFactory.getLogger(this.getClass()); private final File file; private final List originalFoos; private Integer count; private int primitive = 0; public Demo() { for (Foo foo : originalFoos) { this.logger.debug(...); } } ... } 

我最初对解决方案的羞耻是在我使用时从工厂获得记录器参考:

 public DispositionFile() { Logger logger = LoggerFactory.getLogger(this.getClass()); for (Foo foo : originalFoos) { this.logger.debug(...); } } 

但这似乎并不特别有效。

思考?

首先,不要过早优化。 可能是LoggerFactory.getLogger()足够快,并且不会给执行时间带来很大的开销。 如有疑问,请对其进行分析。

其次,findbugs没有抱怨使用Foo的原因是因为该类没有Foo类型的字段,它有一个List类型的字段。 generics在编译时被擦除,就字段定义而言,在类中没有实际的Foo引用。 在运行时,如果您尝试序列化Demo类的实例,则Foo不可序列化的事实会导致exception,但findbugs无法知道这一点。

我的第一反应是使Logger成为静态字段,而不是实例字段。 在这种情况下应该正常工作。

 public class Demo implements Serializable { private static final Logger logger = LoggerFactory.getLogger(Demo.class); // .. other stuff } 

我不希望事情在切线上起飞,但您是否考虑过传统的记录器初始化?

 private static final Logger logger = LoggerFactory.getLogger(Demo.class); 

如果你真的不需要为每个实例使用不同的记录器(这是不寻常的),问题就会消失。

顺便说一句,SL4J的作者说(在批评 Log4J包装器像commons-logging中),

通常情况下,这些包装器质量可疑,因此与直接log4j使用相比,非活动(或禁用)日志记录语句的成本乘以1’000(一千)。 包装类中最常见的错误是在每个日志请求上调用Logger.getLogger方法。 这可以保证对您的应用程序的性能造成严重破坏。 真!!!

这表明不建议您在每次需要时获取记录器的另一种想法。

在这种特殊情况下,FindBugs会误导您,因为org.slf4j.Logger接口标记为java.io.Serializable。 但是,SLF4J附带的SLF4J记录器实现都支持开箱即用的序列化。 试试吧。 你会发现它有效。

以下是SLF4j FAQ的摘录:

与静态变量相反,默认情况下会对实例变量进行序列化。 从SLF4J版本1.5.3开始,记录器实例在序列化后继续存在。 因此,即使将记录器声明为实例变量,主机类的序列化也不再需要任何特殊操作。 在以前的版本中,需要在主机类中将记录器实例声明为瞬态。

另见http://slf4j.org/faq.html#declared_static

我最初的反应是想知道在对象中序列化Logger实例是否有意义。 当您稍后对其进行反序列化时,期望Logger的环境是否正确是否真的公平? 我想我宁愿选择这个并称之为一天:

 private transient Logger logger = LoggerFactory.getLogger(this.getClass());