Showing posts with label log4j. Show all posts
Showing posts with label log4j. Show all posts

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;
        }

Saturday, November 6, 2010

Send different logs to separate files in Tomcat with log4j

Everyone who works with web applications must have at least once wished for separating out his/her log files. It is painful to scan for infomation when multiple threads are dumping information from all over. I'm sure that all the great admins who watch over production or staging systems must quickly learn to use tools that allow them to set up rules and get a clean view of what they need but developers tend to be a more lethargic breed.

I finally decided to dig though the web and put this extremely basic skill into my bag of known tricks.
  1. If you use log4j, do not waste your time reading the instructions for JULI when you land on the page in Tomcat's documentation which talks about logging.properties file etc.
  2. Everything that you need to do in order to send the logs to a separate file can be done in log4j.properties.
    • Unless you are looking to do something really advanced, in which case you should use log4j.xml file.
    • You can read more about that in this Log4j tutorial with Tomcat examples.
  3. Here's the basic blurb (+/-) that you can throw in with minimal edits to get the job done.
    ##
    # Name your appender whatever you want, and choose whatever
    # implementation makes most sense for you.
    ##
    log4j.appender.yourAppenderNameGoesHere=org.apache.log4j.RollingFileAppender
    ##
    # You will most likely want to place the log file in a known location.
    # Leverage ${catalina.base}/logs/yourFilename.log to accomplish this.
    ##
    log4j.appender.yourAppenderNameGoesHere.File=${catalina.base}/logs/yourFileName.log
    ##
    # The rest is generic stuff which you can tweak as you please.
    ##
    log4j.appender.yourAppenderNameGoesHere.MaxFileSize=2MB
    log4j.appender.yourAppenderNameGoesHere.MaxBackupIndex=10
    log4j.appender.yourAppenderNameGoesHere.layout=org.apache.log4j.PatternLayout
    log4j.appender.yourAppenderNameGoesHere.layout.ConversionPattern=%d{dd MMM yyyy HH:mm:ss,SSS} %5p [%t] (%F:%L) - %m%n
  4. Use this blurb to direct the output from a particular package or a class to the appender you have created for the new separate file.
    # Redirect the logs from an entire package
    log4j.logger.com.my.packageName=ERROR, yourAppenderNameGoesHere
    # Redirect logs from only a specific class
    log4j.logger.com.my.packageName.MyClass=DEBUG, yourAppenderNameGoesHere
    # Set the additivity flag to false if you do NOT want the logs to show up redundantly in log files that the parent loggers point to
    log4j.additivity.com.my.packageName=false
    log4j.additivity.com.my.packageName.MyClass=false
  5. By the way, do NOT make the mistake of putting the name of your appender as one of the values in the following line of your log4j.properties file.
    # don't change this
    log4j.rootLogger=INFO, blah, blah, yourAppenderNameGoesHere