views:

111

answers:

9

I have some Java code that I'd like to instrument with log messages for debugging purposes. The final (compiled) production code, however, should not contain any logging as it would slow down the execution time. Is there any way in Java to disable a logger at compile time?

I am not afraid of the footprint a check inside the log method of a run-time enabled/disabled logger would add.

if (logging==enabled) {// do logging}

But I'd like to avoid parameter construction like the following in my production code:

Logger.log("undefined state" + state + " @ " + new Date());

I am using Sun's Java Compiler.

A: 

I don't know about any Compiler functionality about this, but what you also could do is create a build-script. This script would copy the original (debug) Java source files, copy them to a temporary location, strip the logging statements from them and then compile the new source files which are then without debug code.

Dean
+1  A: 

There is no built-in way that makes this possible. You may want to use a Java Preprocessor.

Sjoerd
I thought so when I was googling...
Max
+2  A: 
if(logger.isDebugEnabled()) {
  logger.debug(expression);
}

Every logging framework I've used requires you to use the above pattern to avoid unnecessary evaluation of the logging expression.

McDowell
Investigate {}-placeholders in slf4j.
Thorbjørn Ravn Andersen
@Thorbjørn Ravn Andersen - neat - that's clearly better for many use cases. _I'd nitpick about the object array instantiation, potential for unnecessary autoboxing and so on, but much of that should be JIT-compiled away at runtime anyway. The slf4j API doesn't preclude using boolean guards where necessary._
McDowell
Thats a matter of taste. For me, the {}-notation results in less code (no if) and less clutter (no string concatenation).
Thorbjørn Ravn Andersen
+1  A: 

One way is to do a check outside of the log method. Almost the same check as you mentioned, but you do it yourself:

if (LOGGER.isLoggable(Level.DEBUG)) {
    LOGGER.log("undefined state" + state + " @ " + new Date());
}

This method can be used with any logging level. see Logger#isLoggable(Level) for more infos. Notice it is the recommanded way to avoid log method parameter construction.

unbeli
You beat me on this one !
Riduidel
Inlining the log call is not my preferred option, as it adds a lot of clutter.
Max
then the options are preprocessing the source or using things like AOP to add logging when you need to.
unbeli
+1  A: 

This would seem to be what you're looking for:

http://stackoverflow.com/questions/2230180/whats-log4j-actually-doing-when-we-turn-on-or-off-some-log-places/2230194#2230194

tim_yates
Yes, I read this log4j article before posting. Thanks anyway...
Max
A: 

A ugly hack that might work is to hide the implementation of the Logger library with an empty implementation. The optimizer will inline them and deadcode removal should remove the parameter construction. This will not work when the parameter construction contains side-effects.

Now this is theory, I have not tested this in practice. I am curious about this though. Maybe this warrants a separate question?

Peter Tillemans
I wasn't sure whether the compiler would be "smart" enough to optmise that. I might try it out...
Max
The compiler doesn't seem to be optimising the code. The execution time for calling an empty method while passing a static string vs. one that has been concatenated differs by orders of magnitude (at least with no special compiler options turned on).
Max
In any case I would not rely on it. I cOnfigured my IDE to expand 'logd' to if(log.isDebugEnabled()) {log.debug("yadayada");} and so forth.
Peter Tillemans
A: 

You really should be using a Level indicator for your log messages, Level.SEVERE -> Level.FINEST.

There are predefined methods in the Logger for this i.e:

Logger.info(msg);
Logger.finest(msg); // debug/trace message.

Logger.log(Level.CONFIG, "config message");

this will allow you to configure minumim logging level at the application level and switch on/off those messages below the configured level, using the command line, configuration files or manually using the LogManager

Adrian Regan
Sure, but if you'd like to pass in a string that is constructed (see my example), the the construction will be called prior to checking the log level.
Max
You could write a parameter insertion implementation of your own, using varargs, for use with logging that checks the current nd desired loglevel. Also check out log4j or slf4j
Adrian Regan
+2  A: 

Have you considered the slf4j approach with {}-placeholders. That allows for delayed construction of the toString() meaning that log.debug(...) is cheap if debug logging is disabled.

log.debug("in loop() - a={}, b={}", a, b);
Thorbjørn Ravn Andersen
Yes, I've considered this approach of passing in all the parameters and constructing them after the check, but I was still hoping to get a positive response on handling this at compile level. This is my preferred way at the moment.
Max
Then consider the assert statement which allows you to completely disable parts of code. Unfortunately the slf4j API returns void so they cannot be used with this - you will have to roll your own wrapper.
Thorbjørn Ravn Andersen
A: 

I understand that the Java compiler is able to eliminate blocks of code that are guarded by a compile time constant expression. So in theory, you should be able to do this with something like this:

 public class Logging {
     public static final boolean ENABLED = true;  // change to false
 }

 public class Something
     ....

     if (Logging.ENABLED) {
          logger.debug("hello mum");
     }
 }

Unfortunately, you need to recompile every class that depends on the Logging.ENABLED flag whenever you change its value. So I much prefer the following:

public class Something
     ....

     if (logger.isDebugEnabled()) {
          logger.debug("hello mum");
     }
 }

which has the advantage that you can make fine-grained adjustments to the logging levels at configuration time or even at runtime; e.g. using a debugger, JMX, etc. Also, the overhead of calling logger.isDebugEnabled() in log4j is low enough that it is unlikely to be noticeable unless you have an insane amount of logging in your codebase.

Stephen C