6

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.

Max
  • 123
  • 1
  • 4

10 Answers10

6

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
  • 73,784
  • 33
  • 194
  • 347
  • 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 Jun 22 '10 at 10:22
  • 1
    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 Jun 22 '10 at 14:27
2
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
  • 107,573
  • 31
  • 204
  • 267
2

It is possible to post-process the java class files using Proguard and use the -assumenosideeffects option to remove the logging calls. This is done most commonly for Android using:

-assumenosideeffects class android.util.Log {
    public static *** d(...);
    public static *** v(...);
}

to remove all calls to Log.d(TAG, "message"), etc ...

  • Welcome to SO.Your asking style is good using code in your question is good prectice – khan Nov 08 '12 at 03:42
1

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

Sjoerd
  • 74,049
  • 16
  • 131
  • 175
1

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.

Riduidel
  • 22,052
  • 14
  • 85
  • 185
unbeli
  • 29,501
  • 5
  • 55
  • 57
  • Inlining the log call is not my preferred option, as it adds a lot of clutter. – Max Jun 22 '10 at 09:47
  • then the options are preprocessing the source or using things like AOP to add logging when you need to. – unbeli Jun 22 '10 at 10:04
1

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

what's log4j actually doing when we turn on or off some log places?

Community
  • 1
  • 1
tim_yates
  • 167,322
  • 27
  • 342
  • 338
0

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.

daangemist
  • 113
  • 10
0

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
  • 34,983
  • 11
  • 83
  • 114
  • I wasn't sure whether the compiler would be "smart" enough to optmise that. I might try it out... – Max Jun 22 '10 at 09:56
  • 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 Jun 22 '10 at 10:10
  • 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 Jun 22 '10 at 12:08
0

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
  • 2,240
  • 13
  • 11
  • 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 Jun 22 '10 at 10:17
  • 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 Jun 22 '10 at 10:25
0

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
  • 698,415
  • 94
  • 811
  • 1,216