Saturday, August 13, 2011

Log4j Tutorial


   Log4j is an open source project based on the work of many authors. It allows the developer to control which log statements are output with arbitrary granularity. It is fully configurable at runtime using external configuration files.

   Log4j is an open source project based on the work of many authors. It allows the developer to control which log statements are output with arbitrary granularity. It is fully configurable at runtime using external configuration files.

   The package is distributed under the Apache Software License, a fully-fledged open source license certified by the initiative. The latest log4j version, including full-source code, class files and documentation can be found at http://logging.apache.org/log4j/. By the way, log4j has been ported to the C, C++, C#, Perl, Python, Ruby, and Eiffel languages.


   Inserting log statements into code is a low-tech method for debugging it. It may also be the only way because debuggers are not always available or applicable. This is usually the case for multithreaded applications and distributed applications at large.

Lets get the ball rolling by getting an essence of writing logs in the code. I would explain logging in java codes.

Example 1:

//Try1.java
import org.apache.log4j.Logger;
import org.apache.log4j.BasicConfigurator;

public class Try1 {

        private static final Logger logger = Logger.getLogger(Try1.class);

        public static void main(String args[])
       {
                 BasicConfigurator.configure();
                 logger.info("What a beatiful day.");
                 logger.debug("Hello world.");
        }
}
The output for the above code would be

0 [main] INFO firstTry.Try1 - What a beatiful day.
0 [main] DEBUG firstTry.Try1 - Hello world.


Example 2:

Now logging using the "log4j.properties" file.

The "log4j.properties" file has the following content:

When you configure using the BasicConfigurator.configure() method by default it uses then ConsoleAppender and PatternLayout for all the loggers.

The following configuration creates the same result as the BasicConfigurator.configure()method.


log4j.rootLogger=DEBUG, CA
log4j.appender.CA=org.apache.log4j.ConsoleAppender log4j.appender.CA.layout=org.apache.log4j.PatternLayout
log4j.appender.CA.layout.ConversionPattern=%-4r [%t] %-5p %c %x - %m%n


//Try2.java
import org.apache.log4j.Logger;
import org.apache.log4j.PropertyConfigurator;

public class Try2 {

      static final Logger logger = Logger.getLogger(Try2.class);


      public static void main(String[] args) {

               PropertyConfigurator.configure("log4j.properties");
               logger.debug("Sample debug message");
               logger.info("Sample info message");
               logger.warn("Sample warn message");
               logger.fatal("Sample fatal message");
               logger.error("Sample error message");
      }
}


The output for the above code would be:

0 [main] INFO firstTry.Try2 - Sample info message
0 [main] WARN firstTry.Try2 - Sample warn message
16 [main] FATAL firstTry.Try2 - Sample fatal message
16 [main] ERROR firstTry.Try2 - Sample error message

NOTE: While executing the Try2.java make sure the log4j jars & log4j.properties file are in the classpath.

Instead of using "log4j.properties" file, its better to use "log4j.xml" file which also defines the log properties similar to log4j.properties.

Example 3:

The previous code tells the usage of log4j.properties. Now we will see how to use "log4j.xml"

log4j.xml is as follows,

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE log4j:configuration SYSTEM "log4j.dtd">
<log4j:configuration xmlns:log4j='http://jakarta.apache.org/log4j/'>
   <appender name="CA" class="org.apache.log4j.ConsoleAppender">
      <layout class="org.apache.log4j.PatternLayout">
         <param name="ConversionPattern" value="%-4r [%t] %-5p %c %x - %m%n" />
      </layout>
   </appender>
   <root>
        <level value="DEBUG"/>
        <appender-ref ref="CA"/>
   </root>
</log4j:configuration>


The modified version of Try2.java is,

//Try2.java
import org.apache.log4j.Logger;
import org.apache.log4j.xml.DOMConfigurator;


public class Try2 {

      static final Logger logger = Logger.getLogger(Try2.class);


      public static void main(String[] args) {
               DOMConfigurator.configure("log4j.xml");
               logger.debug("Sample debug message");
               logger.info("Sample info message");
               logger.warn("Sample warn message");
               logger.fatal("Sample fatal message");
               logger.error("Sample error message");
     }
}


The output is the same as Example2

Example 4:

Now let us learn about writing logs into a file. It is called as file appender. A typical requirement in the project is to log different modules in different log files.

Suppose the log4j.xml has following contents:

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE log4j:configuration SYSTEM "log4j.dtd">
<log4j:configuration xmlns:log4j='http://jakarta.apache.org/log4j/'>
     <appender name="CA" class="org.apache.log4j.ConsoleAppender">
         <layout class="org.apache.log4j.PatternLayout">
             <param name="ConversionPattern" value="%-4r [%t] %-5p %c %x - %m%n" />
         </layout>
     </appender>
     <appender name="FA" class="org.apache.log4j.FileAppender">
          <param name="File" value="logs/sample.log"/>
          <param name="Threshold" value="WARN"/>
          <layout class="org.apache.log4j.PatternLayout">
               <param name="ConversionPattern" value="%d{dd MMM yyyy HH:mm:ss,SSS} [%t] %-5p %c       %x - %m%n" />
          </layout>
     </appender>
     <root>
          <level value="DEBUG" />
          <appender-ref ref="CA" />
          <appender-ref ref="FA" />
      </root>
</log4j:configuration>


Consider the same Try2.java now. The output on console would be:

0 [main] INFO firstTry.Try2 - Sample info message
0 [main] WARN firstTry.Try2 - Sample warn message
16 [main] FATAL firstTry.Try2 - Sample fatal message
16 [main] ERROR firstTry.Try2 - Sample error message


When the java code executes, it actually creates a directory as specified in "log4j.xml". In this example, directory named "logs" gets created. In that directory "sample.log" gets created, when the log file appender writes the logs according to programmer's requirement.

For the present example, we can see the sample.log file content as:

13 Aug 2011 18:32:27,640 [main] WARN firstTry.Try2 - Sample warn message
13 Aug 2011 18:32:27,656 [main] FATAL firstTry.Try2 - Sample fatal message
13 Aug 2011 18:32:27,656 [main] ERROR firstTry.Try2 - Sample error message


Example 5:

From now on, i would use explain examples using eclipse.

  1. Create a Project name "SampleLog" in eclipse.
  2. Create two packages. One is named as "com.zinnia.admin" & the other one is named as "com.zinnia.report".
  3. Create two java classes "SampleAdmin.java" & "SampleReport.java", each in one of the packages. The directory structure and the java class placement is as shown.
  4. The code for SampleAdmin.java  & SampleReport.java is given below.




5. The contents in log4j.xml is as shown:


Now run the application, we can find that a directory named "logs" gets created in the project.


The contents in the admin.log and report.log is as follows:


 Please try this out giving different combinations of info in log4j.xml. If there are any mistakes or errors in my explanations, just let me know.