We use the logging package bundled into J2SE 1.4 and above (java.util.logging). An overview is available on line in the Sun's SDK documentation. An other valuable source of inspiration is Logging in NetBeans.
GeoTools typically (but not always) uses one logger per package and is named after the package name. Private classes or implementations sometime use the name of their public counterpart.
Getting a Logger
The Java way to get a logger is to invoke Logger.getLogger(String). However in the GeoTools library, this call shall be replaced by a call to Logging.setLogger(String) where Logging is a class in the org.geotools.util.logging package. Example:
import org.geotools.util.logging.Logging: class myClass { void myMethod() { Logger logger = Logging.getLogger("org.geotools.mypackage"); logger.config("There is some configuration information."); } }
Logger Declaration
The logger may be declared in the class's static fields or returned by a class's static method. This is not mandatory but suggested if it is going to be used in many places.
import java.util.logging.Logger; import org.geotools.util.logging.Logging: public class GMLDataStore { /** The logger for the GML Data Store */ private static final Logger LOGGER = Logging.getLogger("org.geotools.data.gml.GMLDataStore"); }
Logging Messages
Message can be conveniently logged using one of 7 predefined levels. The levels in descending order are:
| Level | Displayed on standard output | Comments |
|---|---|---|
| severe | yes by default | highest value |
| warning | yes by default | non-fatal warning to bring to user attention |
| info | yes by default | information for end users (not debugging information) |
| config | no unless configured | configuration information (services available, etc.) |
| fine | no unless configured | information for developpers (high level) |
| finer | no unless configured | commonly used when entering, returning, or throwing an exception |
| finest | no unless configured | most verbose output |
A convenience method exists in Logger for each of those levels
LOGGER.info("There is a message of interest for ordinary user");
| Logging are not println Do not use the logging info level as a replacement of System.out.println for displaying debug information to the console. The info level is for end users. Use the fine, finer or finest levels for debug information, and setup yours $JAVA_HOME/jre/lib/logging.properties file accordingly (see Logging Configuration below). |
Entering/Existing Logger
There is three more convenience methods: entering, exiting and throwing when entering and exiting a method, or when we are about to terminate a method with an exception.
public Object myMethod(String myArgument) { LOGGER.entering("MyClass", "MyMethod", myArgument); // ... do some process here LOGGER.exiting("MyClass", "MyMethod", myReturnValue); return myReturnValue; }
Minimising Logger output
When logging a message, the logger will include many informations like date and time, source class and method name, current thread, etc. In order to avoid too many informations to be logged, it may be useful to merge consecutive logging into a single log statement. This is especially appropriate if the many logs are actually different parts of a multi-lines message. Using distinct logger calls can result in an output interleaved with the logging from an other thread. Merging the logging is not appropriate if the log messages are conceptually unrelated.
// Wasteful use of logging LOGGER.finer("Value for A is "+A); LOGGER.finer("Value for B is "+B); LOGGER.finer("Value for C is "+C); // Good use of logging LOGGER.finer("Computed values: A="+A+"; B="+B+"; C="+C);
Selective Logging
If the log message is expensive to construct, then consider enclosing it in a if statement.
if (LOGGER.isLoggable(Level.FINER)) { LOGGER.finer("Current state = "+someVeryExpensiveMethodCall()); }
Logging Configuration
To change the default logging setting, edit the following file:
$JAVA_HOME/jre/lib/logging.properties
Default Level Configuration
Define the .level property to the minimal level of interest for you:
.level= FINER
Console Level Configuration
Define the java.util.logging.ConsoleHandler.level property to the minimal level you want to see on the console. It may be different than the level logged to the XML file.
# Limit the message that are printed on the console to FINE and above. java.util.logging.ConsoleHandler.level = FINE java.util.logging.ConsoleHandler.formatter = java.util.logging.SimpleFormatter java.util.logging.ConsoleHandler.encoding = Cp850
Note the encoding property. For Windows user, it should be set to the value displayed by chcp on the command line. Linux and Unix users may ignore this line since Unix systems do a more intelligent work with page codes.
Module Level Configuration
Finally, a different logging level may be specified for each module.
org.geotools.gml.level = FINE org.geotools.referencing.level = INFO
Provides fairly detailed logging message from the GML module, but not from the referencing module.
Log4J interoperability
Geotools can produces a console output similar to the Log4J one (single-line instead of multi-line log message) if the following code is invoked once at application starting time:
Logging.ALL.forceMonolineConsoleOutput();
Alternatively, this formatter can also be configured in the logging.properties without the need for the above-cited method call. See the MonolineFormatter javadoc for details.
The logging output can also be redirected to the real Log4J framework (or any other framework supported by Apache's Common Logging) if the following code is invoked once at application starting time:
Logging.ALL.setLoggerFactory("org.geotools.util.logging.Log4JLoggerFactory");
Why not common-logging?
The common-logging API is basically a set of println functions with name (info, trace, debug, etc.). Java logging API provides the same convenience methods, but is also richer. We use some of its extra capabilities in GeoTools code base:
- ResourceBundle support for localization.
- Logging of stack traces.
- Information on source class and method names.
- Information about which thread produced the logging.
- Can be used through Java Monitoring and Management system.
Log4J offers similar functionalities with a wider range of handler implementations. On the other hand, Java logging is more closely tied to the JVM, which avoid some ClassLoader problems that prevent usage of Log4J in some environments.
We are not claiming that Java logging in superior to Log4J, neither we are forcing peoples to use Java logging. We push for usage of Java logging API, which may very well redirect to Log4J under the hood through java.util.logging.Log4JLoggerFactory implementations.
Commons-logging is widely used in server containers, but other communities like scientists face a different picture. For example the NetCDF library developped by University Corporation for Atmospheric Research (UCAR) uses SLF4J, yet other logging framework that aims to be a replacement for commons-logging.