Stupid log4j tricks
Submitted by charlie.collins on Tue, 11/11/2008 - 15:24
Tagged:
I know the cool kids don't even use log4j anymore (at least not directly), but I still do (just habit, and it still seems to work just fine). Recently on a project we ran into an issue where log4j was configured twice. A developer had done the BasicConfigurator.configure() thing in a standalone file (not part of the main API), and there was also a log4j.xml on the classpath.
The result was more than one root logger and double log output.
The easy way to fix this is what I have always done over the years, kick anyone in the shin that provides a log4j config file WITH A LIBRARY. You can't do that man, you take away the control of the user of the library, and you can create all sorts of weird stuff. If your library JAR has a log4.xml (or log4j.properties) you will cause headaches. Turns out this simple "just don't do that" approach is a bit naive. There is more to this tale on a few fronts.
When you don't include any configuration for log4j, but do use it in a library, you still put a little burden on the users of your library. That is to say, you expect them to configure logging somehow, or they will see this (and we have all seen this):
There are a few downsides to this too. First your Logger isn't a constant anymore, and really it should be (normally you want private static final Logger LOG = Logger.getLogger(YourLibraryClassHere.class);). Second it's a little verbose. Third, it's a hack. It assumes that anyone who does bother to configure log4j will make a root logger, and there may be rare cases where people don't do that (I have never seen one, but it could happen).
I might not use this for normal API classes, but it works well for command line main method type classes. And, I suppose it could be done in one "bootstrap" type class for your API too, if you really wanted to.
The idea here is to make it nice for the people, whether they want to configure the logging, or not.
(Credit where due, I originally found the jist of this approach here: http://marc.info/?l=log4j-user&m=102775658930710&w=2.)
The result was more than one root logger and double log output.
The easy way to fix this is what I have always done over the years, kick anyone in the shin that provides a log4j config file WITH A LIBRARY. You can't do that man, you take away the control of the user of the library, and you can create all sorts of weird stuff. If your library JAR has a log4.xml (or log4j.properties) you will cause headaches. Turns out this simple "just don't do that" approach is a bit naive. There is more to this tale on a few fronts.
When you don't include any configuration for log4j, but do use it in a library, you still put a little burden on the users of your library. That is to say, you expect them to configure logging somehow, or they will see this (and we have all seen this):
log4j:WARN No appenders could be found for logger (YourLibraryClassHere). log4j:WARN Please initialize the log4j system properly.So what if you want to BOTH allow the user to configure their own logging settings if they want, and also not bother them at all if they don't. Well, it turns out there is a bit of a hack that seems to work pretty well for this.
. . .
private static Logger log;
. . .
static {
boolean rootIsConfigured = Logger.getRootLogger().getAllAppenders().hasMoreElements();
if (!rootIsConfigured) {
BasicConfigurator.configure();
Logger.getRootLogger().setLevel(Level.INFO);
}
log = Logger.getLogger(YourLibraryClassHere.class);
}
What this essentially does is check if there are any root loggers, no matter how they might have been configured, and then only if and when there are none it does the BasicConfigurator thing. This allows users to setup the config if they desire, and also avoids the error message and still gives them some logging (to the console generally, but up to your library), if they don't.
There are a few downsides to this too. First your Logger isn't a constant anymore, and really it should be (normally you want private static final Logger LOG = Logger.getLogger(YourLibraryClassHere.class);). Second it's a little verbose. Third, it's a hack. It assumes that anyone who does bother to configure log4j will make a root logger, and there may be rare cases where people don't do that (I have never seen one, but it could happen).
I might not use this for normal API classes, but it works well for command line main method type classes. And, I suppose it could be done in one "bootstrap" type class for your API too, if you really wanted to.
The idea here is to make it nice for the people, whether they want to configure the logging, or not.
(Credit where due, I originally found the jist of this approach here: http://marc.info/?l=log4j-user&m=102775658930710&w=2.)







Comments
Use a static factory method
import java.io.IOException; import java.io.InputStream; import java.util.Properties; import org.apache.log4j.Logger; import org.apache.log4j.PropertyConfigurator; public class LoggerFactory { private static boolean isConfigured = false; private static Object lock = new Object(); public static Logger getLogger(Class<?> klass) { if (!isConfigured) { synchronized (lock) { if (!isConfigured) { boolean rootIsConfigured = Logger.getRootLogger().getAllAppenders().hasMoreElements(); if (!rootIsConfigured) { //log4j setup Properties p = new Properties(); try { InputStream is = LoggerFactory.class.getResourceAsStream("com.acme.myapplication.default-log4j.properties"); p.load(is); PropertyConfigurator.configure(p); is.close(); } catch (IOException e) { //can't do anything about this } } } isConfigured = true; } } return Logger.getLogger(klass); } }And in your classpublic class Foobar { private final static Logger log = LogFactory.getLogger(FooBar.class); }The point here wasn't how to
What do people use instead of log4j?
java.util.logging logback etc
Cool kids switched from using