Log4J 记录器是否应该声明为瞬态记录器?
-
09-06-2019 - |
题
我正在使用 Java 1.4 和 Log4J。
我的一些代码涉及序列化和反序列化值对象 (POJO)。
我的每个 POJO 都声明一个记录器
private final Logger log = Logger.getLogger(getClass());
序列化程序抱怨 org.apache.log4j.Logger 不可序列化。
我应该使用
private final transient Logger log = Logger.getLogger(getClass());
反而?
解决方案
使用静态记录器怎么样?或者您是否需要为类的每个实例使用不同的记录器引用?静态字段默认不序列化;您可以显式声明要使用私有、静态、最终数组进行序列化的字段 ObjectStreamField
命名的 serialPersistentFields
. 请参阅 Oracle 文档
新增内容:当你使用 getLogger(getClass()), ,您将在每个实例中使用相同的记录器。如果您想为每个实例使用单独的记录器,则必须在 getLogger() 方法中区分记录器的名称。例如getLogger(getClass().getName() + hashCode())。然后,您应该使用瞬态属性来确保记录器未序列化。
其他提示
记录器必须是静态的;这将使其不可序列化。
没有理由使记录器成为非静态的,除非您有充分的理由这样做。
如果你 真的 想要采用瞬态方法,您需要在反序列化对象时重置日志。做到这一点的方法是实现该方法:
private void readObject(java.io.ObjectInputStream in)
throws IOException, ClassNotFoundException;
javadoc 为 可串行化 有关于此方法的信息。
您的实现将类似于:
private void readObject(java.io.ObjectInputStream in)
throws IOException, ClassNotFoundException {
log = Logger.getLogger(...);
in.defaultReadObject();
}
如果您不这样做,则反序列化对象后日志将为空。
将记录器字段声明为静态或瞬态。
这两种方法都确保 writeObject() 方法在序列化期间不会尝试将字段写入输出流。
通常记录器字段被声明为静态的,但如果您需要它作为实例字段,只需将其声明为瞬态字段,就像通常对任何不可序列化字段所做的那样。不过,反序列化后,记录器字段将为空,因此您必须实现 readObject() 方法才能正确初始化它。
尝试将 Logger 设置为静态。您不必关心序列化,因为它是由类加载器处理的。
这些类型的情况,特别是在 EJB 中,通常最好通过线程本地状态来处理。通常,用例类似于您有一个遇到问题的特定事务,并且您需要提升日志记录以调试该操作,以便可以生成有关问题操作的详细日志记录。在事务中携带一些线程本地状态,并使用它来选择正确的记录器。坦率地说,我不知道在这种环境中设置实例的级别会有什么好处,因为实例到事务的映射应该是容器级别的函数,您实际上无法控制在实例中使用哪个实例。无论如何,给定交易。
即使在处理 DTO 的情况下,以需要给定特定实例的方式设计系统通常也不是一个好主意,因为设计很容易演变为一个糟糕的选择。一个月后,您可能会发现效率考虑因素(缓存或其他一些改变生命周期的优化)将打破您关于将实例映射到工作单元的假设。
如果您希望记录器是针对每个实例的,那么是的,如果您要序列化对象,您会希望将其设为瞬态。Log4J 记录器不可序列化,无论如何,在我使用的 Log4J 版本中都不可序列化,因此,如果您不使记录器字段瞬态,您将在序列化时遇到异常。
记录器不可序列化,因此在将它们存储在实例字段中时必须使用瞬态。如果您想在反序列化后恢复记录器,您可以将级别(字符串)存储在确实被序列化的对象中。
使用实例记录器有充分的理由。一个非常好的用例是,您可以在超类中声明记录器并在所有子类中使用它(唯一的缺点是来自超类的日志归因于子类,但通常很容易看到那个)。
(就像其他人提到的使用静态或瞬态)。