Technology: Log4J

Overview

In most applications, it makes sense to create a log of the activities. The "traditional" way is to print a message to stdout, which is usually the local console, using the command

System.out.println("My log message");

This, however, has certain drawbacks:

  • If you have inserted many logging statements in your code, there is no easy way of "turning them off" when you package your product for your customers. You have to modify the code and comment the log messages out.
  • This log will go to your stdout, which is usually a console. This is rather useless when a server-application is developed, then the logging statements should be written to a log file or even a database.
  • In a distributed system environment, a component may run on a entirely different machine. That means your log messages would also get distributed on many machines.
  • In a complex server environment like in a Servlet container, multiple server applications and modules are running at the same time. The logging of all these modules should be controlable by a log-configuration of the central server.
  • Using the "print" method, it is not possible to tune the logging: e.g., when debugging an applications debug-statements should be printed out, in the production mode only warnings and errors should be logged.
  • Tuning of the log may also include various modules/packages: it might be e.g., desired to keep Hibernate log only errors, but let the own part of the application log debug statements, or vice versa, because you have a problem with the Hibernate configuration (as one example).

With the Log4J framework, you can write something like

	    
	    Log.info("My info message");
	    ...
	    Log.error("My error message");
	    

The "importance" of the log statement can be set by using the appropriate methods:

debug<info<warn<error<fatal

"debug" should be used for debug statements, "info" for general information logged by the application, "warn" for warning of problematic conditions, "error" when a recoverable error and "fatal" when a non-recoverable error occured.

In the log-configuration this level can be configured for output of individual components: e.g., you could define that the "main" application should log everything up to "info" level, Springframework only up to "warn" and Hibernate only "error" statements.

Log4J can be configured (without changing the code) to filter these messages, store them in a database, output them on a console or even send you a mail if certain errors arise - and all of that quite fast.

Configuration

Before using the logger in your application, you need to configure it using the "log4j.properties" file (the naming of the file is important!). If the config filed is then put into the main/resources or test/resources directory, log4j finds it automatically at startup and no further configuration is needed:

As can be seen above: different logging configurations could be provided for testing and for production system by putting them into the "main" or "test" directory of the project.

After Log4J is configured, you need to create a reference to a Logger object. This can be done in multiple ways, one of them being to create a static instance in every class:

private static Logger log = Logger.getLogger(my.package.subpackage.classname.class);

Then, you can use this variable to send log messages in 5 log levels:

  • fatal
  • error
  • warn
  • debug
  • trace

Usage

Let's have a look at an example (taken from the BP Examples (basic\src\main\java\log4j.properties))


We set the root logger level to INFO and its appenders to stdout (output on console). Additionally we set the logging level by default (rootLogger) to INFO, but for all Apache libraries to WARN and the same for the Springframework

log4j.rootLogger = INFO, stdout
log4j.logger.org.apache = WARN, stdout
log4j.logger.org.springframework = WARN, stdout

Now we define the appender for stdout console output which uses the PatternLayout. An appender is the logging component that receives the log-statements and sends them to the desired output channel, the console in this case

log4j.appender.stdout = org.apache.log4j.ConsoleAppender
log4j.appender.stdout.layout = org.apache.log4j.PatternLayout
log4j.appender.stdout.layout.ConversionPattern = %d [%t] %-5p %c - %m%n

%d: outputs the date of the logging event
[%t]: outputs the thread the logging event produced
%c: the category of the logging event
%-5: outputs the priority level (INFO, WARN,...); "-" = left-aligned, "5" = length of level (f.ex. for having INFO and DEBUG shortly one below the other)
%m%n: output of the logging message and new line

Already this simple example shows the potential: the logging levels to be seen can be easily reconfigured on one place for the whole application. Additionally, if logging into a file, or database would be desired only the appender definition needs to be changed. Even logging into multiple channels with different loglevels and much more is possible.

Reference

The official Log4J page can be found at http://logging.apache.org/log4j/docs/.
Other resources of logging documentation can be found at http://www.torsten-horn.de/techdocs/java-log4j.htm (german documentation)
.