Clean code, clean logs: use appropriate tools (1/10)
log.info("!@#$%");
happily somewhere in the code, he probably don't realize the importance of application logs during maintenance, tuning and failure identification. Underestimating the value of good logs is a terrible mistake. I have collected few random advices that I find especially useful when it comes to writing logging routines and I will present them in a series of short articles. First tip (out of ten) is about logging libraries and tools.
In my opinion, SLF4J is the best logging API available, mostly because of a great pattern substitution support:
log.debug("Found {} records matching filter: '{}'", records, filter);
In Log4j you would have to use:
log.debug("Found " + records + " records matching filter: '" + filter + "'");
This is not only longer and less readable, but also inefficient because of extensive use of string concatenation. SLF4J adds nice {} substitution feature. Also, because string concatenation is avoided and toString() is not called if the logging statement is filtered, there is no need for isDebugEnabled() anymore. BTW, have you noticed single quotes around filter string parameter?
SLF4J is just a façade, as an implementation I would recommend Logback framework, already advertised on my blog, instead of well established Log4J. It has many interesting features (some of them will be discussed in future tips) and, in contrary to Log4J, is actively developed.
The last tool to recommend is Perf4J. To quote their motto:
Perf4J is to System.currentTimeMillis() as log4j is to System.out.println()
I've added Perf4J to one existing application under heavy load and seen it in action in few other. Both administrators and business users were impressed by the nice graphs produced by this simple utility. Also we were able to discover performance flaws in no time. Perf4J itself deserves its own article, but for now just check their Developer Guide.
To summarize, this is the ideal pom.xml excerpt to start with:
<repositories>
<repository>
<id>Version99</id>
<name>Version 99 Does Not Exist Maven repository</name>
<layout>default</layout>
<url>http://no-commons-logging.zapto.org/mvn2</url>
</repository>
</repositories>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>1.5.11</version>
</dependency>
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
<version>0.9.20</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>jul-to-slf4j</artifactId>
<version>1.5.11</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>log4j-over-slf4j</artifactId>
<version>1.5.11</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>jcl-over-slf4j</artifactId>
<version>1.5.11</version>
</dependency>
<dependency>
<groupId>commons-logging</groupId>
<artifactId>commons-logging</artifactId>
<version>99.0-does-not-exist</version>
</dependency>
To test this, try the following code:
SLF4JBridgeHandler.install();
org.apache.log4j.Logger.getLogger("A").info("Log4J");
java.util.logging.Logger.getLogger("B").info("java.util.logging");
org.apache.commons.logging.LogFactory.getLog("C").info("commons-logging");
org.slf4j.LoggerFactory.getLogger("D").info("Logback via SLF4J");
As you can see, no matter which logging framework is used (we don't even have Log4J and Commons-Logging on our CLASSPATH, see 99.0-does-not-exist version!), every logging statement is printed using Logback (see how it works). So even if your favorite libraries stick to Commons-Logging (very bad thing! [1], [2]) or even worse to Log4J, you don't need to include them in your project.
UPDATE: Ceki Gülcü (founder of the Log4J, SLF4J and Logback projects) suggested simplier approach to get rid of commons-logging dependency (see his comment).
- Clean code, clean logs: use appropriate tools (1/10)
- Clean code, clean logs: logging levels are there for you (2/10)
- Clean code, clean logs: do you know what you are logging? (3/10)
- Clean code, clean logs: avoid side effects (4/10)
- Clean code, clean logs: concise and descriptive (5/10)
- Clean code, clean logs: tune your pattern (6/10)
- Clean code, clean logs: log method arguments and return values (7/10)
- Clean code, clean logs: watch out for external systems (8/10)
- Clean code, clean logs: log exceptions properly (9/10)
- Clean code, clean logs: easy to read, easy to parse (10/10)