Log4J记录器是否应声明为瞬态?

时间:2020-03-05 18:58:35  来源:igfitidea点击:

我将Java 1.4与Log4J一起使用。

我的某些代码涉及序列化和反序列化值对象(PO​​JO)。

我的每个POJO都声明一个记录器,其中包含

private final Logger log = Logger.getLogger(getClass());

序列化程序抱怨org.apache.log4j.Logger无法序列化。

我应该使用

private final transient Logger log = Logger.getLogger(getClass());

反而?

解决方案

回答

记录器必须是静态的;这将使其不可序列化。

没有必要使记录器为非静态,除非我们有充分的理由这样做。

回答

如何使用静态记录器?还是我们需要为该类的每个实例使用不同的记录器参考?静态字段默认情况下不序列化;我们可以显式声明字段以使用名为serialPersistentFields的ObjectStreamField的私有,静态最终数组进行序列化。请参阅Oracle文档

新增内容:
使用getLogger(getClass())时,将在每个实例中使用相同的记录器。如果要为每个实例使用单独的记录器,则必须在getLogger()方法中区分记录器的名称。例如getLogger(getClass()。getName()+ hashCode())。然后,我们应该使用transient属性来确保记录器未序列化。

回答

尝试改为使Logger静态。比我们不必在意的是串行化,因为它是由类加载器处理的。

回答

如果希望Logger按实例进行,则可以,如果要序列化对象,则希望使其成为瞬态。 Log4J Logger不能序列化,也不是我正在使用的Log4J版本,因此,如果不将Logger字段设为瞬态,则会在序列化中遇到异常。

回答

记录器不可序列化,因此在将它们存储在实例字段中时必须使用瞬态。
如果要在反序列化之后恢复记录器,则可以存储级别(字符串)表示对象(该对象确实已序列化)。

回答

通常,最好通过线程本地状态来处理这类情况,尤其是在EJB中。通常,用例就像我们遇到问题的特定事务一样,我们需要提升日志记录以对该操作进行调试,以便可以生成有关问题操作的详细日志记录。在事务中携带一些线程本地状态,并使用它来选择正确的记录器。坦率地说,我不知道在这种环境中在INSTANCE上设置级别会在什么地方有利,因为实例到事务的映射应该是容器级别的函数,我们实际上将无法控制在实例中使用哪个实例。无论如何给定交易。

即使在处理DTO的情况下,通常也不用以给定特定实例为必需的方式来设计系统,因为设计很容易演变为错误的选择。我们可能需要一个月的时间,然后再决定效率方面的考虑(缓存或者其他生命周期更改优化)将打破我们对将实例映射到工作单元的假设。

回答

可以将记录器字段声明为静态或者瞬态。

两种方式都确保writeObject()方法在序列化期间不会尝试将字段写入输出流。

通常,记录器字段被声明为静态的,但是如果我们需要将其作为实例字段,则只需将其声明为瞬态即可,因为通常对任何不可序列化的字段都将其声明为静态。反序列化时,logger字段将为null,因此,我们必须实现readObject()方法来正确初始化它。

回答

如果我们真的想使用瞬态方法,则需要在对对象进行反序列化时重置日志。做到这一点的方法是实现该方法:

private void readObject(java.io.ObjectInputStream in) 
   throws IOException, ClassNotFoundException;

Serializable的javadocs包含有关此方法的信息。

实现将如下所示:

private void readObject(java.io.ObjectInputStream in) 
     throws IOException, ClassNotFoundException {
   log = Logger.getLogger(...);
   in.defaultReadObject();
 }

如果不这样做,则在反序列化对象后,log将为null。

回答

有充分的理由使用实例记录器。一个非常好的用例是,我们可以在超类中声明记录器,并将其在所有子类中使用(唯一的缺点是,超类的日志属于该子类,但通常很容易看到)。

(就像其他人提到的使用静态或者瞬态)。