During the software development life cycle, you would be tempted to write the state of a program at various stages of its execution to some repository such as a log file, an
e-mail system, or a persistent database for debugging and monitoring your applications. This article is an introduction to Log4j, an open source logging tool developed and distributed under the Jakarta Apache Project. We will start by exploring the architecture of Log4j. Next, we will use an example to demonstrate how to
insert log statements in a J2EE Web application.
|
Log4j allows developers to configure log statements at runtime with flexibility. Log4j can prove to be an asset by providing the
developer with detailed context of a running application. It is certified by the open-source community and has evolved as a
de-facto logging and debugging tool.
Setting up Log4j
Download the latest version of Log4j distribution in ZIP format from the PCQEssential CD and unpack it. Inside the dist
directory of your log4j home, copy the log4j-1.2.11.jar file to the WEB-INF/lib directory of your Web app's root directory.
Why use log4j?
There are other logging tools available, including the log API bundled in JDK 1.4 (java.util.logging). Log4j scores over all other logging APIs because of its ability to provide runtime control over logging without having to touch the source code or binaries. Log4j is purely written in Java. Once the log statements have been
inserted into the code, they can be controlled with external configuration files without any human intervention. Moreover, log statements can remain in shipped code without incurring a heavy performance cost.
|
Also it supports inheritance in loggers. Using a logger hierarchy you can control which log statements are output (filtering). This in-turn helps to reduce the volume of logged output and minimize the cost of logging.
Basic components
There are three aspects of log4j: logger, appender and layout. Let's discuss these in some detail.
Logger: A logger is an entity that your application program will use to log messages. Each class in your
application can have an individual logger or a common logger. The root logger is the super logger that all
loggers inherit from. Each logger in log4j is assigned a level. If you don't assign a level to a logger yourself, log4j automatically assigns to your logger the level of the parent logger. The
default level of the root logger is DEBUG.
Following are the different logging levels available in log4j. We have listed these in ascending order
of priority):
DEBUG: logs messages while developing and debugging an application
INFO: helps in monitoring the progress of an application
WARN: represents potentially harmful situations
ERROR: indicates recoverable errors
FATAL: indicates errors that will cause an application to crash
|
A logging request is said to be enabled if its priority level is higher than or equal to the priority of its Logger.
Appenders and layouts: Log4j allows logging statements to print to multiple output destinations called appenders. The
target of the logged output can be the console, a file, an OutputStream, a GUI component, a remote socket server, a remote Unix Syslog daemon, or many other output targets. With log4j you can also control the layout of your log statements. You can specify
numerous different layouts for the messages using the log4j configuration file.
Sample application
Our sample application is pretty simple, it consists of two listener classes, a servlet and a property configuration file. We will log the IP address and domain name of the client whenever a session is created. Below is a brief explanation of the application files.
Configuration File
This is a text file with name/value pairs for configuring log4j elements such as loggers, appenders and layouts. You can either use an XML file or a Java properties file, as shown here.
log4j.rootLogger=DEBUG, cons
log4j.logger.com.pcquest=, myAppender
The configuration file is named MyLogger.properties, and placed inside the Web app's WEB-INF/classes directory. This file creates a logger named com.pcquest, which inherits the root
logger's DEBUG level and console appender. It will use an appender named myAppender. It is worth noticing that the logger names are based on Java package names, so the complete name of our
appender is com.pcquest.myAppender. The 'myAppender' is a rolling file appender, a log file that automatically creates a backup file when the original log reaches a certain size. Here is how the log file is configured in the log4j properties file.
og4j.appender.myAppender.File=D:/log/uservisits.log
ContextLogger.java: This listener class implements the ServletContextListener interface, so that it's notified whenever a context is created and destroyed for the Web app. Inside the
contextInitialized() lifecycle method, we begin by reading the log4j properties file which is set inside the DD (Deployment Descriptor), web.xml file.
Next we create a new logger named after the class as:
log=Logger.getLogger(ContextLogger.class);
The static getLogger(Class className) method creates a logger named after the class (com.pcquest. ContextLogger). This logger inherits the appender and layout that the properties file set up for com.pcquest, because the new logger's name has com.pcquest as a prefix.
SessionListener.java: This class implements HttpSessionListener, to get notified whenever a session is created or invalidated by the container. Inside its default constructor, we get a reference to the logger object, its logger is also named after the package name as com.pcquest.SessionListener. Then, in the sessionCreated lifecycle method we log the session ID created by the container as:
log.info("A session has been created for this web app it's ID is : "+se.getSession().getId());
LogTesterServlet.java: This servlet creates a child logger (com.pcquest.LogTesterServlet) named after the full qualified class name inheriting from the com.pcquest logger. Inside the
doGet() lifecycle method we create a session and log the domain name and IP address of the client. The code for the same looks like that shown below:
log.info("Client host name="+req.getRemoteHost()+" with IP
address="+req.getRemoteAddr());
Logged output
Log4j supports multiple output appenders per logger.
Appenders are inherited additively from the logger hierarchy. This means that if the root logger has been configured to append to the console and your child logger appends to a file, then all the logging requests will print to the file and on the console, as in our example.
Conclusion
Log4j, thus, dramatically cuts down on logging overheads in the applications that you work with. This also reduces the code clutter, and at the same time minimizes the performance loss in a production application with these debugging statements.
Kunal Jaggi