Logging enables information about the current state and execution of a program to be outputted for debugging purposes. We already did use some limited form of logging utilizing System.out
to write information to standard output. While this already is useful, this method has some drawbacks especifically in the context of larger, more complex programs:
The purpose of logging frameworks is to allow this type of flexible logging. Java has a build-in logging framework in package java.logging
. The framework consists of the following major compontents:
Loggers create log messages. In java.util.logging
loggers have names and are organized into a hierarchy based on their names. This hierarchy has a root called the root logger (its name is ""
). The strucure of the hierarchy is determined based on logger names. A Logger's name is split into parts separated by .
. For example, a logger named "A.B.Hello.C"
would placed in this hierarchy as follows:
root -> A -> B -> Hello -> C
The hierarchy comes into play when configuring Loggers were decendents will per default inherit certain propoerties like formatting settings from their ancestors.
Formatters take log messages created by Loggers and transform them into text. The default formatter logs the log message and additional information such as the level (see below) and the class and method from where the log message was created and a timestamp.
Handlers are responsible for processing the log messages created by Loggers. For example, the build-in default handler for the root logger is a ConsoleHandler
which writes to standard error and has its log level set to INFO
(see below). To control how log messages are transformed into strings, you can set the formatter to be used with the handler with setFormatter
. Handlers are associated with Logger by calling addHandler
. A logger can have multiple handlers (which all would process messages from the logger). Per default log messages of a logger are also processed by handlers associated with ancestors of the logger.
Log levels allow log messages to be classified by their severity. For each logger you can set a log level and only messages with this log level or a more severe log level will be shown. These are the log levels supported by java.util.logging
in decreasing order of severity:
SEVERE
(highest value)WARNING
INFO
CONFIG
FINE
FINER
FINEST
(lowest value)In addition to programtic configuration, the logging framework can also be configured from a java Properties object. A Properties object stores a set of key-value pairs which are Strings. The class provides methods to read properties from a text file where each key-value pair is given as a line key = value
or an XML file.
The LogManager classes documentation lists keys that are supported: https://docs.oracle.com/javase/6/docs/api/java/util/logging/LogManager.html.
package lecture;
import java.util.logging.*;
public class ClassWithLogging {
private static final Logger log = Logger.getLogger(ClassWithLogging.class.getName());
public static void someMethod () {
log.finer("Some low priority message");
log.info("a info level message");
log.warning("a warning");
}
public static void main (String[] args) {
Logger root = Logger.getLogger(""); // the root logger's name is ""
Handler[] rootHandlers = root.getHandlers();
for(Handler h: rootHandlers) { // remove the default handler from the root logger
root.removeHandler(h);
}
// set log levels for loggers
log.setLevel(Level.ALL); // show log messages of all level for our logger
root.setLevel(Level.SEVERE); // show only SEVERE messages for the root logger
// create our own handler
ConsoleHandler handler = new ConsoleHandler(); // create a new console handler for this logger since the default one has log level INFO
handler.setLevel(Level.ALL); // this handler does show all log messages passed from loggers
root.addHandler(handler); // set this handler for the root logger, per default all other loggers inherit the handlers of their ancestors
// cause some logging
someMethod();
root.severe("this will be shown");
root.info("but this will not!");
}
}
import lecture.ClassWithLogging;
ClassWithLogging.main(null);