views:

3154

answers:

10

I am using Java 1.4 with Log4J.

Some of my code involves serializing and deserializing value objects (POJOs).

Each of my POJOs declares a logger with

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

The serializer complains of org.apache.log4j.Logger not being Serializable.

Should I use

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

instead?

+8  A: 

The logger must be static; this would make it non-serializable.

There's no reason to make logger non-static, unless you have a strong reason to do it so.

mindas
It can sometimes be useful to have a non-static logger if you want to use different logger names for each instance (e.g. appending an instance-unique string onto the end of the logger name), or if you have a logger in a superclass object and you want it to use the name of the subclass instance.
MB
+12  A: 

How about using a static logger? Or do you need a different logger reference for each instance of the class? Static fields are not serialized as far as I know.

Added content: As you use getLogger(getClass()), you will use the same logger in each instance. If you want to use separate logger for each instance you have to differentiate on the name of the logger in the getLogger() -method. e.g. getLogger(getClass().getName() + hashCode()). You should then use the transient attribute to make sure that the logger is not serialized.

Aleksi
+2  A: 

Try making the Logger static instead. Than you don't have to care about serialization because it is handled by the class loader.

A: 

I should have mentioned, I like the idea of having a logger for each instance rather than one for each class.

I would have many instances of the same class instantiated and managed by a container (say a Spring application context) and would like to switch logging levels on a per-bean basis rather than on a per-class basis.

In the example of a Spring application context, the logger would be declared as something like

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

where the bean is no longer a POJO and implements BeanNameAware

Vihung
You should add this comment as an edit to your question
svrist
if your object gets deserialized then the logger (log) will be null
Steve Kuo
A: 

If you want the Logger to be per-instance then yes, you would want to make it transient if you're going to serialize your objects. Log4J Loggers aren't serializable, not in the version of Log4J that I'm using anyway, so if you don't make your Logger fields transient you'll get exceptions on serialization.

MB
A: 

Loggers are not serializable so you must use transient when storing them in instance fields. If you want to restore the logger after deserialization you can store the Level (String) indide your object which does get serialized.

Glever
+1  A: 

These kinds of cases, particularly in EJB, are generally best handled via thread local state. Usually the use case is something like you have a particular transaction which is encountering a problem and you need to elevate logging to debug for that operation so you can generate detailed logging on the problem operation. Carry some thread local state across the transaction and use that to select the correct logger. Frankly I don't know where it would be beneficial to set the level on an INSTANCE in this environment because the mapping of instances into the transaction should be a container level function, you won't actually have control of which instance is used in a given transaction anyway.

Even in cases where you're dealing with a DTO it is not generally a good idea to design your system in such a way that a given specific instance is required because the design can easily evolve in ways that make that a bad choice. You could come along a month from now and decide that efficiency considerations (caching or some other life cycle changing optimization) will break your assumption about the mapping of instances into units of work.

+3  A: 

Either declare your logger field as static or as transient.

Both ways ensure the writeObject() method will not attempt to write the field to the output stream during serialization.

Usually logger fields are declared static, but if you need it to be an instance field just declare it transient, as its usually done for any non-serializable field. Upon deserialization the logger field will be null, though, so you have to implement a readObject() method to initialize it properly.

+4  A: 

If you really want to go the transient approach you will need to reset the log when your object is deserialized. The way to do that is to implement the method:

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

The javadocs for Serializable has information on this method.

Your implementation of it will look something like:

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

If you do not do this then log will be null after deserializing your object.

John Meagher
A: 

There are good reasons to use an instance logger. One very good use case is so you can declare the logger in a super-class and use it in all sub-classes (the only downside is that logs from the super-class are attributed to the sub-class but it is usually easy to see that).

(Like others have mentioned use static or transient).

James A. N. Stauffer