Thursday, April 21, 2011

Log4j: Log concrete and abstract classes

In the past I've often pondered how to get better logging when I'm dealing with abstract classes.

Let's take the following class struture as an example:
  • SomeInterface
    • AbstractImpl
      private static final Logger logger = Logger.getLogger(AbstractImpl.class)
      • ConcreteImpl_A
        private static final Logger logger = Logger.getLogger(ConcreteImpl_A.class)
      • ConcreteImpl_B
        private static final Logger logger = Logger.getLogger(ConcreteImpl_B.class)
This usually results in logs like:
21 Apr 2011 DEBUG [main] (AbstractImpl.java:10) - the actual log message
21 Apr 2011 DEBUG [main] (AbstractImpl.java:10) - the actual log message
... which makes it difficult to distinguish between the concrete implementation classes that are actually being used when the message was logged.

This entry helped me realize that the (AbstractImpl.java:10) portion of the logged line was being placed there by the regex (%F:%L) from the PatternLayout that was specified in the log4j.properties file like so:
log4j.appender.CONSOLE.layout.ConversionPattern=%d{dd MMM yyyy} %5p [%t] (%F:%L) - %m%n
Therefore, the solution would be to add the regex [%c{1}]:
log4j.appender.CONSOLE.layout.ConversionPattern=%d{dd MMM yyyy} %5p [%t] [%c{1}] (%F:%L) - %m%n
Hoping to get logs that look like:
21 Apr 2011 DEBUG [main] [ConcreteImpl_A] (AbstractImpl.java:10) - the actual log message
21 Apr 2011 DEBUG [main] [ConcreteImpl_B] (AbstractImpl.java:10) - the actual log message
... but that will not happen yet because there is still one major flaw in the way the logger code in the java classes above has been written!

The code needs to be like this:
  • SomeInterface
    • AbstractImpl
      protected Logger logger = null;
      • ConcreteImpl_A
        private static final Logger myLogger = Logger.getLogger(ConcreteImpl_A.class)
        public ConcreteImpl_A() {
        super();
        super.logger = myLogger;
        }
      • ConcreteImpl_B
        private static final Logger myLogger = Logger.getLogger(ConcreteImpl_B.class)
        public ConcreteImpl_B() {
        super();
        super.logger = myLogger;
        }

4 comments:

  1. Thanks for this post!

    You could also define the logger in the abstract class like so:

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

    ReplyDelete
  2. This comment has been removed by the author.

    ReplyDelete
  3. Nowhere in the constructor, you can garanty that this has been initialised ...
    Only once the constructor has been initialized you can be sure it has been fully initialised

    ReplyDelete