Hello World! – Log4j Tutorial

**adsense_4x1Block**
Objective of this tutorial is to enable you to understand and add basic logging capabilities in your java projects. Starting with a business case of why logging is required, we will cover high level architecture of Log4j and its different components, and finally implement a Hello World example to demonstrate interaction of these components.

In Exercise examples, we will extend our finished code and log4j configurations to use pattern layout and a file appender. Don’t worry about these right now; you’ll learn them along the way.

Introduction:

You must have some programming experience if you are reading this tutorial. So recalling that experience, you will remember one common pattern in your development process, which is inserting debug statements in your code in order to remain informed of where current execution is.

For example in C++ you might have used printf or cout, similarly alert in JavaScript, echo in PHP, trace in ActionScript and similar output statements in other languages.

So what do you use for printing out debug information in Java? … System.out.println(“my message”);

What, then what is Log4j? … You can say Log4j is improved version of System.out.println and one of the major reasons we needed it, is:

When we are developing software, we insert output statements in the code everywhere. Almost Every function has one or more output statements so that we remain notified of which section of code is executed and what is current state of the execution. However when we need to ship this piece of software to client, we have to manually remove or comment out these statements so that they don’t cause unnecessary load on server resulting in slow application response.

Silencing these statements in code means, once you have shipped the binary, you cannot track back a failure or investigate what went wrong, since no one is there to inform you of that.

So we needed some mechanism of controlling debug statement in more configurable way, which we could enable or disable externally without changing the binary. Also being able to silence certain types of debug statements, and letting others print would be a cherry on the cake.

Log4j is exactly that, we insert log4j logging statements wherever in code we would have inserted System.out. And then during development we configure it to print out everything. When shipped to production, we let the logging statements to remain there in code, but configure log4j so that only highly critical information is printed out e.g. exceptions. This way by limiting output, we saved extra processing load on server, while remaining capable of tracking back an issue in software.

Enough on why we needed Log4j, let’s quickly go through different components of Log4j and then implement an example using them.

Overview of Log4j Framework

Log4j is very lightweight and simple logging framework, comprising of three main components.

Loggers: Loggers are basic, developer customized, org.apache.log4j.Logger objects, which provide control mechanism over disabling or enabling certain type of log statements when logged against them.

So we always need a logger in our code to start logging against it. We can either create a new custom Logger (one line of code) or simply use the root level Logger available to us by default.

We can also set the Log Level of these Loggers. The set of possible levels is:

DEBUG < INFO < WARN < ERROR < FATAL

In increasing level of their criticality. So when logging a statement, we tell Logger what is the criticality of each statement, and it will decide later based on external configuration file what to do with this statement.

Appenders: Log4j allows logging requests to print to multiple destinations e.g. console, to a file and much more, these output destinations are called appenders.

So we attach one or more Appenders with each Logger, so that it will send logging statements to all its Appenders.

Default root level Logger has default Console Appender associated with it, thus it prints on Console by default.

Also for example, we can create a custom Logger, and attach it to default logger, to print each logging statement to both Console and an external text file.

Layouts: In order to customize not only the output destination (Appender) but also the output format, a Layout is associated with an Appender.

Layouts may add some additional information to the original text e.g. thread making the log request, level of the log statement or number of milliseconds elapsed since the start of the program.

Workflow of a Logging Statement:

Let’s dry run overall flow of events in a common logging scenario. We initiate a Logger, and send a simple text based logging statement to it, while setting its Log Level. Logger will consult the configuration file, and firstly see if it has to print this Log Level or ignore it, this is done by comparing log level associated with Logger in configurations and log level of the logging statement in code.

If Log Level of logging statement is of equal or greater criticality, then Logger will move on and see which Appenders are associated with it. It will then direct this logging statement to each Appender.

When those Appenders will receive logging statement, they will consult the configuration file, to see what Layout is associated with them. Based on associated Layout formatting is done to original logging text, and finally formatted logging statement is printed out in respective Appender.

While keeping this information in mind, let’s implement a basic Hello World example so that we can see these components in action in code.

Tutorial’s Technology Stack:

Log4j – 1.2.16 (Tutorial should work fine with newer versions as well)
Eclipse – Helios

Initial Code Setup:

How to Use: create a blank Java Project using Eclipse (File > New > Java Project> and name it “Tut-Log4JHelloWorld”.

Step 1: Downloading and Adding Log4j jar in project

Log4j too is distributed as a bundled jar. So in order to add logging capabilities to our project using log4j, our project must know where to find those classes. Let’s download and add log4j’s latest jar to our project. Download latest log4j distribution and unpack it in a folder. Create a folder named ‘lib’ in our java project, and place log4j-1.2.16.jar from unzipped distribution in lib folder.

Now we need to tell Eclipse, where to find that distribution. Right click on project name in Eclipse and select Properties. In Properties wizard, select Java Build Path > Libraries Tab > Add Jars, navigate to Tut-Log4jHelloWorld project’s lib folder, and select newly added log4j.jar file (you might need to refresh the project before doing that).

Fig 1-Creating a Java Class and adding Logging capabilities using log4j

Fig 1-Creating a java class and adding logging capabilities


Step 2: Creating a Java Class and adding Logging capabilities

Let us now create a new Java Class and then add logging capabilities using Log4j into it. Add an empty java class named Log4jHelloWorld (Right click on Project > New > Class, name class Log4jHelloWorld), also select main method to be included.

Newly added class should look like this:

1
2
3
4
5
6
7
8
9
10
package sawan;
 
public class Log4jHelloWorld {
	/**
	 * @param args
	 */
	public static void main(String[] args) {
		// TODO Auto-generated method stub
	}
}

Now this simple Plain Old Java Object class (POJO) has nothing special in it. We’ll first use root level Logger available to us by default. Then we’ll see how to create a custom logger.
Add following import statement in class definition.

1
import org.apache.log4j.Logger;

then let’s create a class level variable, and initiate it with reference to default root level logger. Add this line of code inside class definition but outside main method.

1
static final Logger logger = Logger.getRootLogger();

So far we have imported log4j Logger class, and created an object of it as our custom class variable. Next we’ll initiate logging statements against this Logger. Since it is a root level logger, it is associated with Console Appender by default. We’ll see that in action shortly.

Let’s add logging statements of all Log Levels i.e. DEBUG < INFO < WARN < ERROR < FATAL. Add following lines of code inside main method so that they can be executed when our class is run.

1
2
3
4
5
logger.debug("Sample debug message");
logger.info("Sample info message");
logger.warn("Sample warn message");
logger.error("Sample error message");
logger.fatal("Sample fatal message");

As you can notice, Logger class’s object has provided us with methods, named same as logging level, thus simply passing a normal string to one of these methods, will set the log level of the statement.

Let’s execute our class at this point. Click on the Class Name, right click and select Run As > Java Application. Following error will be displayed on console.

1
2
3
log4j:WARN No appenders could be found for logger (root).
log4j:WARN Please initialize the log4j system properly.
log4j:WARN See http://logging.apache.org/log4j/1.2/faq.html#noconfig for more info.

The console output is detailed enough, and you can follow the link to read further. It suggests we need a configuration file, so that is what we will do next.

Step 3: Adding Properties file based Log4j Configuration

Log4j is very flexible on configurations. There are two ways we can configure a Log4j implementation, by either using a standard properties file named ‘log4j.properties’ or by using an xml based configuration file and naming it ‘log4j.xml’.

To start with, we’ll choose the simpler path and add a log4j.properties file to our project with minimum configurations required. Right click on the project name and create a new blank file and name it log4j.properties. It should be placed in root folder of our project.

Let’s configure our root logger inside log4j properties file. Add following key=value pair inside properties file.

1
log4j.rootLogger=DEBUG,SAWAN

What this line of configuration tells to log4j is two things, one it sets the Log Level of the Logger itself to DEBUG. Meaning, now logger will only display statements having DEBUG or high level (since DEBUG is at minimum level, thus it will display all. We’ll play with this later).

After the comma, SAWAN is the custom name of an appender, that we are telling to root logger, we will be configuring later in the properties configuration file. You can name it to anything.

So now let’s add that Appender we promised to root level Logger. Add following property

1
log4j.appender.SAWAN=org.apache.log4j.ConsoleAppender

This property, simply creates and configures the appender with our custom name. Value of this property is set to org.apache.log4j.ConsoleAppender thus this appender will print on Console. We have already associated it with root level logger in first statement so now root level Logger is able to display its logging statements on Console.

However there is still one piece of information that this Appender wants to know and that is, ‘How should I display the passed logging statement on Console”. Well that’s a valid question, so let’s tell our appender to relax and print in simplest possible format. For this add following line of configuration.

1
log4j.appender.SAWAN.layout=org.apache.log4j.SimpleLayout

in this line SAWAN.layout is set to SimpleLayout, provided by log4j package by default.

The configuration file should now look like:

1
2
3
log4j.rootLogger=DEBUG,SAWAN 
log4j.appender.SAWAN=org.apache.log4j.ConsoleAppender
log4j.appender.SAWAN.layout=org.apache.log4j.SimpleLayout

Let’s now execute our class. Right click on class name and select Run As > Java Application. Now you’ll see all our logging statements displayed on Console.

Output of basic logging statements

Fig 2-Output of basic logging statements


Wala! Our project is now Log4j enabled, and we can print as many log statements as we want, and configure them externally using properties file based configurations. From here, you will use same configuration file to enable any java class with logging capabilities.

Issue Resolution

If you have not faced any issue trying out this tutorial then you can skip this section. Some common issue(s) and their solutions reported by programmers following this tutorial are listed below

Problem: log4j:WARN No appenders could be found for logger (root). log4j:WARN Please initialize the log4j system properly.

Solution: This problem is most likely due to the location of the log4j.properties file. Try to place it in src folder and see if issue is resolved.

Download Tutorial Code:

Tutorial Working Code: Download

Note: Use File > Import option in Eclipse to import downloaded project source files.

Exercise:

  • Change the debug level of Root Logger to WARN. Execute the class and see how many log statements are now printed, are they greater than or less than WARN level.

  • Change the name of appender in configurations for the root logger. How many places do we need to change the Appender name?

  • Change the Appender layout to use org.apache.log4j.PatternLayout and add following pattern property at the end of properties file.

    1
    
    log4j.appender.SAWAN.layout.ConversionPattern=%-4r [%t] %-5p %c %x - %m%n

    Did you notice any difference in layout of logging statements?

  • Add another appender to root logger, after SAWAN by inserting it at end as comma separated list. Name this Appender as ‘FILE’. Also add following lines of configurations to configure this FILE appender.

    1
    2
    3
    4
    5
    
    log4j.appender.FILE=org.apache.log4j.RollingFileAppender
    log4j.appender.FILE.File=helloworld.log
    log4j.appender.FILE.MaxFileSize=20480KB
    log4j.appender.FILE.MaxBackupIndex=5
    log4j.appender.FILE.layout=org.apache.log4j.SimpleLayout

    After executing class, were you able to locate a file named helloworld.log in root folder of project. (Refresh your workspace in eclipse). What are the output log statements in that file?