Log4JLog4j is an OpenSource logging API developed under the Jakarta Apache project. It provides a robust, reliable, fully configurable, easily extendible, and easy to implement framework for logging Java applications for debugging and monitoring purposes. Log4j allows developers to insert log statements in their code and configure them externally. This article covers the need for logging; a brief introduction to log4j; an explanation of its components and terminology, implementation, and configuration; its advantages and shortcomings; and how to use it to log Java applications. The Need for LoggingLogging, or writing the state of a program at various stages of its execution to some repository such as a log file, is an age-old method used for debugging and monitoring applications. By inserting simple yet explanatory output statements (such as system.out.println() in the case of Java) in the application code that write to a simple text file, console, or any other repository, a reliable monitoring and debugging solution can be achieved. Although low-level, this is the mechanism to fall back upon when sophisticated debugging tools are either unavailable for any reason or useless; such as in a distributed application scenario. Inserting log statements manually is tedious and time-consuming, not to mention managing them (such as modifying and updating) down the road due to various reasons such as ongoing upgrading and bug-fixing process for application code, and so forth. To ease this process, there is a useful, efficient, and easy-to-use utility available, called log4j API. What Is log4j?Log4j is an OpenSource logging API for Java. This logging API, currently in version 1.2.8, became so popular that it has been ported to other languages such as C, C++, Python, and even C# to provide logging framework for these languages.What Can log4j Do?
How Does log4j Work?Logically, log4j can be viewed as being comprised of three main components: Logger, Appender, and Layout namely. The functionalities of each of these components are accessible through Java classes of the same name. Users can extend these basic classes to create their own loggers, appenders, and layouts.LoggerThe component logger accepts or enables log requests generated by log statements (or printing methods) during application execution and sends their output to appropriate destination, i.e. appender(s), specified by user. The logger component is accessible through the Logger class of the log4j API. This class provides a static method Logger.getLogger(name) that either retrieves an existing logger object by the given name, or creates a new logger of given name if none exists. This logger object is then used to set properties of logger component and invoke printing methods debug(), info(), warn(), error(), fatal(), and log(). These methods generate log requests during application execution. These methods and their respective usage are discussed in following section. Each class in the Java application being logged can have an individual logger assigned to it or share a common logger with other classes. One can create any number of loggers for the application to suit specific logging needs. It is a common practice to create one logger for each class, with a name same as the fully-qualified class name. This practice helps organize log outputs in groups by the classes they originate from, and identify origin of log output, which is useful for debugging. Log4j provides a default root logger that all user-defined loggers inherit from. Root logger is at the top of the logger hierarchy; in other words, root logger is either parent or ancestor of all logger objects created. If an application class doesn't have a logger assigned to it, it can still be logged using the root logger. For example: A class MyClass in com.foo.sampleapp application package can have a logger named com.foo.sampleapp.MyClass instantiated in it by using the Logger.getLogger("com.foo.sampleapp.MyClass") method. This logger will implicitly inherit from its nearest existing ancestor (maybe com.foo.sampleapp or com.foo or ...; or root logger if none exists), follow the same parent-child relationship as the classsubclass they log and have same package hierarchy as these classes. Priority levels of log statementsLoggers can be assigned different levels of priorities. These priority levels decide which log statement is going to be logged. There are five different priority levels: DEBUG, INFO, WARN, ERROR, and FATAL; in ascending order of priority. As we can see, log4j has corresponding printing methods for each of these priority levels. These printing methods are used to generate log requests of corresponding priority level for log statements. For example: mylogger.info("logstatement-1"); generates log request of priority level INFO for logstatement-1. The root logger is assigned the default priority level DEBUG. All loggers inherit priority level from their parent or nearest existing ancestor logger, which is in effect until they are assigned another priority level. A logger object can be assigned a priority level either programmatically by invoking its method setLevel(Level.x) where x can be any of the five priority levels, or through external configuration files. The latter is the most preferred way to do so. After assigning a priority level to a logger, it will enable only those log requests with a priority level equal to or greater than its own. This technique helps prevent log statements of lesser importance from being logged. This concept is the core of log4j functionality. Listing 1: Example of priority level of logger and log requests. /* Instantiate a logger named MyLogger */ Logger mylogger = Logger.getLogger("MyLogger"); ... /* Set logger priority level to INFO programmatically. Though this is better done externally */ mylogger.setLevel(Level.INFO); ... /* This log request is enabled and log statement logged, since INFO = INFO */ mylogger.info("The values of parameters passed to do_something( ) are: " + a, b); ... /* This log request is not enabled, since DEBUG < INFO */ mylogger.debug("Operation performed successfully"); ... /* this log request is enabled and log statement logged, since ERROR > INFO*/ mylogger.error("Value of X is null"); ... AppenderAppender component is interface to the destination of log statements, a repository where the log statements are written/recorded. A logger object receives log request from log statements being executed, enables appropriate ones, and sends their output to the appender(s) assigned to it. The appender writes this output to repository associated with it. There are various appenders available; such as ConsoleAppender(for console), FileAppender (for file), JDBCAppender (for database), SMTPAppender (for SMTP server), SocketAppender (for remote server) and even Instant Messenger (for IMAppender). An appender is assigned to a logger using the addAppender( ) method of the Logger class, or through external configuration files. A logger can be assigned one or more appenders that can be different from appenders of another logger. This is useful for sending log outputs of different priority levels to different destinations for better monitoring. For example: All log outputs with levels less than FATAL and ERROR being sent to files, while all those with levels equal to ERROR and FATAL sent to console for faster detection. A logger also implicitly inherits appenders from its parents (and from ancestors, in that effect). Therefore, the log requests accepted by logger are sent to its own appenders along with that of all its ancestors. This phenomenon is known as appender additivity. LayoutThe Layout component defines the format in which the log statements are written to the log destination by appender. Layout is used to specify the style and content of the log output to be recorded; such as inclusion/exclusion of date and time of log output, priority level, info about the logger, line numbers of application code from where log output originated, and so forth. This is accomplished by assigning a layout to the appender concerned. Layout is an abstract class in log4j API; it can be extended to create user-defined layouts. Some readymade layouts are also available in a log4j package; they are PatternLayout, SimpleLayout, DateLayout, HTMLLayout, and XMLLayout.
Implementing and Configuring log4j
Requirement
Installation and running log4j To log an application class, follow these steps:
Listing 2: A class com.foo.sampleapp.MyClass being logged with log4j./* Application package */ package com.foo.sampleapp; /*Import necessary log4j API classes import org.apache.log4j.*; public class MyClass { /* get a static logger instance with name com.foo.sampleapp.MyClass */ static Logger myLogger = Logger.getLogger(MyClass.class.getName( )); Appender myAppender; SimpleLayout myLayout; /* Constructor */ public MyClass(){ ... /* Set logger priority level programmatically. Though this is better done externally */ myLogger.setLevel(Level.INFO); ... /* Instantiate a layout and an appender, assign layout to appender programmatically */ myLayout = new SimpleLayout(); myAppender = new ConsoleAppender(myLayout); // Appender is // Interface ... /* Assign appender to the logger programmatically */ myLogger.addAppender(myAppender); ... ... } //end constructor public void do_something( int a, float b){ /* This log request enabled and log statement logged, since INFO = INFO */ myLogger.info("The values of parameters passed to method do_something are: " + a, b); */ ... /* this log request is not enabled, since DEBUG < INFO*/ myLogger.debug("Operation performed successfully"); ... if (x == null){ /* this log request is enabled and log statement logged, since ERROR > INFO*/ myLogger.error("Value of X is null"); ... } } //end do_something() } // end class MyClassUpon application execution the resulting log output will look like Listing 3: Listing 3: Log output generated by logging the class MyClass.INFO - The values of parameters passed to method do_something are:21, 34.8f ERROR - Value of X is null Configuring log4jThe log4j can be configured both programmatically and externally using special configuration files. External configuration is most preferred, because to take effect it doesn't require change in application code, recompilation, or redeployment. Configuration files can be XML files or Java property files that can be created and edited using any text editor or XML editor, respectively. The simplest configuration file will contain following specifications that can be modified, both programmatically and externally, to suit specific logging requirements.
Listing 4: A simple configuration file config-simple.properties in Java property format.# The root logger is assigned priority level DEBUG and an appender # named myAppender. log4j.rootLogger=debug, myAppender # The appender's type specified as FileAppender, i.e. log output written to a file. log4j.appender.myAppender=org.apache.log4j.FileAppender # The appender is assigned a layout SimpleLayout. # SimpleLayout will include only priority level of the log # statement and the log statement itself in log output. log4j.appender.myAppender.layout=org.apache.log4j.SimpleLayout Listing 5 shows an XML configuration file with similar specifications. Listing 5: Configuration file log4j.xml in XML format.<?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE log4j:configuration SYSTEM "log4j.dtd"> <log4j:configuration xmlns:log4j="http://jakarta.apache.org/log4j/"> <appender name="myAppender" class="org.apache.log4j.FileAppender"> <layout class="org.apache.log4j.SimpleLayout"/> </appender> <root> <priority value="debug" /> <appender-ref ref="myAppender"/> </root> </log4j:configuration>To use a log4j configuration file, it should be loaded using following code, preferably inserted in startup class of the application. import org.apache.log4j.PropertyConfigurator; ... PropertyConfigurator.configure("path/to/configuration_file"); Advantages and ShortcomingsThe advantages of using log4j are listed below:
The shortcomings of log4j are listed below:
Although log4j has received competition from new a logging API integrated into JSDK 1.4, log4j's strengths of being a mature, feature-ric, and efficient logging API framework, and wide usage for a long time are bound to hold against any competition. Also, compatibility with JSDK 1.4's logging API for easy to-and-fro migration, possibility for further improvements in this API, and new features to suit growing needs will surely make log4j's use continue for a long time to come. |