Skip to main content

Logging using Apache Log4j

Introduction

- Log4j is a brilliant logging API available both on Java and .net framework.
- Log4j allows you to have a very good logging infrastructure without putting in any efforts.
- Log4j gives you the ability to categorize logs at different levels (Trace, Debug, Info, Warn, Error and Fatal).
- Log4j gives you the ability to direct logs to different outputs. For e.g. to a file, Console or a Database.
- Log4j gives you the ability to define the format of output logs.
- Log4j gives you the ability to write Asynchronous logs which helps to increase the performance of the application.
- Loggers in Log4j follow a class hierarchy which may come handy to your applications

Log4j consists of five main components
- LogManager
- Loggers
- Appenders
- Layouts
- Configuration file

 

Log Manager

This is the static class that helps us get loggers with different names and hierarchy. You can consider LogManager as a factory producing logger objects.
package Log4jSample;

import org.apache.log4j.BasicConfigurator;
import org.apache.log4j.LogManager;
import org.apache.log4j.Logger;
public class SampleEntry {

    //mainLogger is a logger object that we got from LogManager. All loggers are
        //using this method only. We can consider LogManager as a factory to create
        //Logger objects
    static Logger mainLogger = LogManager.getLogger(SampleEntry.class.getName());

    public static void main(String[] args) {
        // TODO Auto-generated method stub

        BasicConfigurator.configure();
        mainLogger.info("This is just a logger");   

    }
}

Loggers

This is the most important class that you will need. This is the object which lets you log information to the required Log location, be it console or a file or even a database.
Logger objects follow hierarchy similar to class hierarchy in any OOP language. Naming convention of Logger hierarchy is in the name. Each objects name decide which hierarchy it follows. For example we have a logger named “Main.Utility”. So Utility is the child of Main and Main is the father of Utility. Also, all Loggers are derived from root Logger. The actual hierarchy will be root.Main.Utility with root being ancestor of Utility and Father of Main

Hierarchy example

package Log4jSample;
import org.apache.log4j.LogManager;
import org.apache.log4j.Logger;

public class SampleEntry {

    public static void main(String[] args) {
        // TODO Auto-generated method stub   

      Logger chance = LogManager.getLogger(SampleEntry.class.getName());   
      Logger logger1 = LogManager.getLogger("Child1");
      Logger logger1Child = logger1.getLogger("Child1.ChildOfLogger1");
      Logger loggerGrandChild = LogManager.getLogger("Child1.ChildOfLogger1.GrandChild");

      System.out.println("logger1's parent’s full name is " + logger1.getParent().getName());
      System.out.println("logger1Child's parent’s full name is " + logger1Child.getParent().getName());
      System.out.println("loggerGrandChild's parent’s full name is " + loggerGrandChild.getParent().getName());

    }
}
Output:
logger1's parent’s full name is root
logger1Child's parent’s full name is Child1
loggerGrandChild's parent’s full name is Child1.ChildOfLogger1

Logging levels

Logger class have following print methods that help you log information.
- Trace
- Debug
- Info
- Warn
- Error
- Fatal
So lets say you want to print a Debug log you would just do it by saying Logger.Debug(“This is a debug log”). You may choose to use any other overloaded Logger.Debug() method. All these print statements are called Levels.
Each log level expects a certain type of information for e.g Debug level expects logging of that information which may help a programmer debug the application in case of failures. Similarly Error Level expects all the Errors to be logged using this level.
You can set log level of a logger using the Logger.setLevel method. Once you set the Log level of your logger only loggers with that and higher level will be logged. Log levels have following order
TRACE < DEBUG < INFO < WARN < ERROR < FATAL. Lets understand this with an example, in the code below we have set the level to DEBUG first and than WARN. You will see that only the logs which are at that or higher level will be logged. Here is the code sample
import org.apache.log4j.BasicConfigurator;
import org.apache.log4j.Level;
import org.apache.log4j.LogManager;
import org.apache.log4j.Logger;

public class SampleEntry {

    public static void main(String[] args) {
        // TODO Auto-generated method stub

     BasicConfigurator.configure();
     Logger Mylogger = LogManager.getLogger("DebugLogger");

     //Setting up the log level of both loggers
      Mylogger.setLevel(Level.DEBUG);

      Mylogger.trace("This is the trace log - DEBUG");
      Mylogger.debug("This is debug log - DEBUG");
      Mylogger.info("This is info log - DEBUG");
      Mylogger.warn("This is Warn log - DEBUG");
      Mylogger.error("This is error log - DEBUG");
      Mylogger.fatal("This is Fatal log - DEBUG");

      Mylogger.setLevel(Level.WARN);
      Mylogger.trace("This is the trace log - WARN");
      Mylogger.debug("This is debug log - WARN");
      Mylogger.info("This is info log - WARN");
      Mylogger.warn("This is Warn log - WARN");
      Mylogger.error("This is error log - WARN");
      Mylogger.fatal("This is Fatal log - WARN");     
    }
}

The level here is set as Debug.
So you will see messages above Debug’s level for the first set ie Trace message is not printed.
For the second set where WARN is set as the Level, Only warn, error and fatal messages are logged.

Log level inheritance

If a Level Hierarchy of a logger is not defined then it is picked from the Level of parent. Lets say we have two loggers
LoggerParent and LoggerParent.Child and lets say Logger LoggerParent has log level set to LoggerParent.setLevel(Level.WARN) to warn. Now if we dont set the Level of Logger child than default logging Level of Child will be set to Level.WARN which is the Level of its parent.
Logging run time exceptions
This is a very important feature of a Logger class, it enables you to pass on the exception to the output. This comes handy specifically in the cases where we have intentionally caught the exception but we also want to log the information about the exception.
every print method (TRACE, DEBUG…. FATAL) have an overload which is Logger.Debug(Object message, Throwable t), off course we have just taken the example of .Debug only, this allows us to pass the exception. Lets see how its beneficial using a code sample
 package Log4jSample;

import org.apache.log4j.BasicConfigurator;
import org.apache.log4j.Level;
import org.apache.log4j.LogManager;
import org.apache.log4j.Logger;

public class SampleEntry {

    public static void main(String[] args) {
        // TODO Auto-generated method stub

     BasicConfigurator.configure();
     Logger LoggerParent = LogManager.getLogger("LoggerParent");
         try
         {
             // We will get a divide by zero exception her
             int x = 200 / 0;
         }
         catch(Exception exp)
         {
           LoggerParent.warn("Following exception was raised", exp);    
         }    
    }
}

Appenders



Appenders are the Log4j objects which deliver logs to the required destinations. For example a ConsoleAppender will deliver the logs to the console and a FileAppender to the log file. There are many types of Appenders that we have in Log4j, the ones that we will cover today are

- FileAppender 
- ConsoleAppender 
- JDBCAppender 


Configuration File


The configuration can be done programmatically or using files. If it’s done using files, you can either create a xml file or a properties file. A XML file is usually used.

<!DOCTYPE log4j:configuration SYSTEM "log4j.dtd">

<log4j:configuration xmlns:log4j="http://jakarta.apache.org/log4j/" debug="false">

<appender name="fileAppender" class="org.apache.log4j.FileAppender">

<param name="Threshold" value="INFO" />

<param name="File" value="logfile.log"/>

<layout class="org.apache.log4j.PatternLayout">

<param name="ConversionPattern" value="%d %-5p [%c{1}] %m %n" />

</layout>

</appender>

<root>

<level value="INFO"/>

<appender-ref ref="fileAppender"/>

</root>

</log4j:configuration>
The configuration file is used to set the appender, the pattern and the level of logging.

Comments

Popular posts from this blog

How to Install Selenium and execute your first program

You can install and test Selenium using the following steps. All files can be downloaded from  http://www.seleniumhq.org/download/   Download Selenium Server jar file   Download client library java jar file   Download InternetExplorer Driver file   Install eclipse workspace. (download eclipse. Unzip folder. Find eclipse.exe application and open it) Create a new java project with 1,2 as references Copy 3 to workspace For IE, go to internet options->security and change the security level so that it’s the same for all zones. Write sample java program Below program opens google in IE and searches for selenium test and closes it. // All the imports happen automatically when you use eclipse ide and add the downloaded jars in the project references package test; import org.openqa.selenium.Alert; import org.openqa.selenium.By; import org.openqa.selenium.WebDriver; import org.openqa.selenium.WebElement; import org.openqa.selenium.ie...

Form - User Input Automation

Text boxes WebElement t=driver.findElement(By.id(“username”)); t.sendkeys(“test name”); Buttons WebElement t=driver.findElement(By.id(“createbutton”)); t.click(); DropDown Boxes  WebElement select = driver.findElement(By.tagName("select")); List<WebElement> allOptions = select.findElements(By.tagName("option")); for (WebElement option : allOptions) { System.out.println(String.format("Value is: %s", option.getAttribute("value"))); option.click(); } This will find the first “SELECT” element on the page, and cycle through each of its OPTIONs in turn, printing out their values, and selecting each in turn. As you will notice, this isn’t the most efficient way of dealing with SELECT elements. WebDriver’s support classes include one called “Select”, which provides useful methods for interacting with these Select oSelection = new Select(driver.findElement(By.tagName("select"))); oSelection.deselectAll(); //All op...

Transition from Test Automation Engineer to Test Engineer

What's the difference? Test Automation Engineer works on automating the Regression Test Suite. Test Engineer ensures the product can be released to the customer. With my industry experience, I have realized, that, as part of automation engineer your job duties don't stop with creating Automation tests, Frameworks and running them. With more tests being automated, your responsibilities not only include running the complete Regression suite but occasionally validation and verification of the product. There are different documents/sites defining different types of testing and where their usage, however,but finding bugs is a different skill. Though we are testers by role, our analytical minds work like developers as we develop scripts too. We could be biased. When something doesn't work, we look for a workaround and think it's no big deal. This attitude is a deal breaker!Instead use your skills to analyse and break the code. Here I am going to list some of my ideas ...