Abstract: 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 6.
javaspecialists.teachable.com: Please visit our new self-study course catalog to see how you can upskill your Java knowledge.
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 static final 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 static final 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 :-)
We are always happy to receive comments from our readers. Feel free to send me a comment via email or discuss the newsletter in our JavaSpecialists Slack Channel (Get an invite here)
We deliver relevant courses, by top Java developers to produce more resourceful and efficient programmers within their organisations.