|
The Java Specialists' Newsletter
Issue 137 2006-12-28
Category:
Tips and Tricks
Java version: JDK 1.4+ Creating Loggers DRY-lyby Dr. Heinz M. KabutzAbstract: A common idiom for logging is to create a logger in each class that is based on the class name. The name of the class is then duplicated in the class, both in the class definition and in the logger field definition, since the class is for some reason not available from a static context. Read how to solve that problem.
Welcome to the 137th edition of The Java(tm) Specialists' Newsletter. The JavaPolis
conference was a huge success, with lots of interesting talks,
exhibitors and people. Well done to Stephan Janssen and his
team! At the conference, Bill Venners interviewed
me for his website. You will especially enjoy the "blooper
segment", where Bill could not compose himself :-) Let me
know what you think ...
So, here is wishing you a fantastic 2007. May you discover
hidden paths through the JDK 1.6.
Would you like to really understand Java concurrency? Join us for an
in-depth study of how threading works in Java. During the course,
you will learn how to write correct and fast multi-threaded Java code.
Please
click here if you would like to learn more. Creating Loggers DRY-ly
A few months ago, Thomas Vogler from Software AG pointed out an
ugly idiom that has been creeping into Java code. In both Log4j
and the java.util.logging frameworks, it is typical that when we
construct the logger, we use the name of the class as the name.
You could, for example, see code like this:
package com.cretesoft.tjsn;
import java.util.logging.Logger;
public class Application {
private final static Logger logger =
Logger.getLogger(Application.class.getName());
public static void main(String[] args) {
Application m = new Application();
m.run();
}
public void run() {
logger.info("Hello");
}
}
Every time we make a new class, we habitually put a static
logger variable at the top. This is typically copy & pasted
from another class, so we have to remember to change the class
to be correct, otherwise the logging is incorrect and thereby
confusing.
There are probably AOP and IoC ways of doing this easily and
elegantly. Ideally I would never want to define a "logger"
variable - that should automatically be inside my class, and I
should be able to just use it.
We wanted to be able to define an idiom that we could copy &
paste without having to change it. However, the static context
for some reason does not know in which class it is being called.
When you are in a non-static context, you can call the
getClass() method.
One approach would be to do the following. Leave the static
logger undefined, and initialize it in an initializer block:
package com.cretesoft.tjsn;
import java.util.logging.Logger;
public class CleverApplication {
private static Logger logger = null;
{ logger = Logger.getLogger(this.getClass().getName()); }
public static void main(String[] args) {
try {
logger.info("static Hello");
} catch (NullPointerException e) {
System.out.println(
"failed to call Logger from static context");
}
CleverApplication m = new CleverApplication();
m.run();
}
public void run() {
logger.info("Hello");
}
}
There are several reasons why this is a not a good idea. First
off, the logger is not available from a static context.
Secondly, every time a new instance is created, we make a new
logger. This could be avoided if we used the lazy
initialization idiom, but that would then introduce contention.
Thirdly, the class name of the instance might be a subclass of
CleverApplication.
Another approach is to create a LoggerFactory that uses the
method call stack to determine from where it is being called.
It then returns a logger instance depending on who called it.
Here is how you could use it:
package com.cretesoft.tjsn;
import java.util.logging.Logger;
public class BetterApplication {
private final static Logger logger = LoggerFactory.make();
public static void main(String[] args) {
BetterApplication m = new BetterApplication();
m.run();
}
public void run() {
logger.info("Hello");
}
}
The LoggerFactory can use several approaches for determining who
called it. It could see the call stack by generating an
exception (the approach I use below). It could use the security
manager to determine the context. There might be more
approaches, but this one works well enough.
package com.cretesoft.tjsn;
import java.util.logging.Logger;
public class LoggerFactory {
public static Logger make() {
Throwable t = new Throwable();
StackTraceElement directCaller = t.getStackTrace()[1];
return Logger.getLogger(directCaller.getClassName());
}
}
There is one little problem with this method. A method should
ideally not be dependent on who calls it. There is probably a
law for it, whose name I could not remember.
Using this idiom will make it easier to get the logger correct
per class, without too much effort.
Kind regards from the Island of Crete
Heinz
P.S. I got my 5-year residence permit for Greece today. We have
moved into our rental property, so if you come to Crete, make
sure you visit us in Chania. The roads are not that fast-moving
- our poor new car is averaging 30 km/h according to the
navigational system. The best map available here is Google
Earth. My daughter Connie and I discovered a little backroad
down to the beach by looking at satellite pictures :-)
Tips and Tricks Articles
Related Java Course
Discuss at The Java Specialist Club
|