Friday, 1 April 2011

Java Logging API tutorial (step by step)

The JDK contains the "Java Logging API". Via a Logger one can save text to a central place to report on errors, provide additional information about your program, even the finest detail to errors depending on the priorities. See here for what is java logging API.

Creating a Logger


The package java.util.logging provides the logging capabilities via the class Logger.
Java code:
import java.util.logging.Logger;

private final static Logger LOGGER = Logger.getLogger(MyClass.class .getName());


Logging Levels



Once the logger created, (above we have created logger per class, so its static.) we can use it to log errors, or simple info or even the lowest detail of the user program.

So the levels are divided as follows:
Log Levels in descending order are:




  • SEVERE (highest)
  • WARNING
  • INFO
  • CONFIG
  • FINE
  • FINER
  • FINEST

In addition to that you have also the levels OFF and ALL to turn the logging of or to log everything.
Java code to set logging level:


LOGGER.setLevel(Level.DEBUG);

Example:

package logging.example;
import java.util.logging.Logger;

public class HelloLogging {
private static Logger theLogger =
Logger.getLogger(HelloLogging.class.getName());

private String aMessage;

public HelloWorld(String message) {
aMessage = message;
}

public void sayHello() {
// use the 'least important' type of message, one at
// the 'finest' level.
theLogger.finest("Hello logging!");
System.err.println(aMessage);
}
}


Now in main simply call this class:

public static void main(String[] args) {
HelloLogger helloLog = new HelloLogger("Hello world logger!");
helloLog.sayHello();
}


As we have done logger level to finest we cannot get "Hello logging" on console. For this in sayHello we can write this:


theLogger.INFO("Hello logging!");


This will log "INFO" or higher level logs to the console.

 

Handler



Now we can handle these logs. By handling I mean is that we get the log message and export it to certain target accordingly. See Log handlers in Java APIs for this.

 

Formatter


Each handlers output can be configured with a formatter, which format the output in custom way before sent to display. See here for more.

 

Logger Inheritance


Loggers inherit certain characteristics from their parents when they are created, Typically a Logger will inherit the following:


  • If a Logger is created with no Level set it will inherit the first Level it finds set in a Logger as it walks up the tree.
  • The default setting is to use parent handlers so if you don't specify a handler explicitly LogRecords may still be published by parent Loggers.
  • If a Logger has a null ResourceBundle name it will use the name given to its parent and so on.

Logging Convenience Methods


The default Logger implimentation has numerous convenience logging methods that can save a great deal of time when adding log messages to code. The convenience methods take one of two forms. Either "void info( String message )" or "void info( String sourceClass, String sourceMethod, String message )". The first version is sold to developers that want quick logging the second is to developers that want accurate logging. The second is more "accurate" because it explicitly names the source class and method, information that may be lost when the class is JITed. In reality I have never seen the first version turn out the wrong result (or no result which is more likely).

 

Logging Configuration


This section will have a bit of a Tomcat twist to it towards the end because most of my work is done with Tomcat. This is another area where Sun seem to have dropped the ball a little. There is no way using the basic Java logging framework to distinguish between different web applications in the same container if they use the same Logger. This means that if you had the same code deployed twice you wouldn't be able to tell where the log messages were coming from. Log4j solves that problem but for now we will stick to the default Java logging API and later I will show you a way round the problem.

Configuring Java Logging

Logging can be configured in three different ways: you can supply a properties file that describes the logging setup (by default this is called logging.properties and lives in JRE/lib/), you can provide a class that can be instantiated at VM initialisation that will configure the logging or you can roll your own logging configuration. I include rolling you own because I don't like either of the other two solutions much. The first means you have to play with a file that lives in one of hte VM distribution directories which, to my mind, is a bad thing. The second means adding another argument to the invocation of the VM which, IMHO, leads to screw ups. Either way, those are your options. I will focus mostly on the properties file as that is more commonly used. The class configuration technique is useful of you need to configure loggers from a database or other source but if you need something that complex you probably have enough time to "do it right".

The logging.properties file simply defines a number of Loggers, Handlers, Formatters and Filters that are constructed and ready to go shortly after the VM has loaded. Normally the top portion of the file is devoted to setting up Handlers for the root logger. The set up for the root Logger is a little different to the other Loggers because it doesn't have a name. To add handlers to the root logger include a line that begins handlers and then lists the handlers to add to the root logger. For instance this

handlers = java.util.logging.ConsoleHandler,
java.util.logging.FileHandler


will add a ConsoleHandler and FileHandler to the root logger. Next set the logging level of the root logger which is done with the name .level. To set it to all use

.level = ALL


To set the logging levels for the two handlers created add lines like this

java.util.logging.ConsoleHandler.level = INFO
java.util.logging.FileHandler.level = ALL


and finally to set a formatter simply specify its name like this

java.util.logging.ConsoleHandler.formatter = java.util.logging.SimpleFormatter


It is possible to set any of the instance variables on the Loggers, Handlers, Formatters and Filters in a similar manner by specifying its name followed by the property name you want to set. To set handlers on other loggers use the syntax loggerName.handlers=list_of_handler_class_names.

You can probably see the limitation in the default Java logging setup now. There is no way to separatly configure the handlers for different loggers because you have no way to seperate the names of the handlers - you have to refer to them by their class name.

1 comment:

  1. Excellent article , you have indeed covered topic in details with code examples and explanation. I have also blogged some of my experience as 10 tips on logging in Java

    Thanks
    Javin
    10 tips on logging in Java

    ReplyDelete