tags:

views:

278

answers:

3

We know we can config log4j to turn off log on specific places (class or package in Java) via its properties/configuration file. My questions are followed:

  1. what's log4j actually doing for those flags?
  2. is the log statement in log4j still called but just not being written to file or console because of that flag? so still have performance impact?
  3. is it like #ifdef in C++ which take effect at compile time then can limit the performance impact?

thanks,

+11  A: 

Yes, the log statements will still be executed. This is why it's a good pattern to check the log level first: something like

if (log.isInfoEnabled()) {
    log.info("My big long info string: " + someMessage);
}

This is to keep from reallocating space for the info String when the log level does not support INFO statements.

It's not anything like #ifdef - #ifdef is a compiler directive, whereas Log4J configurations are processed at runtime.

Edit: I hate getting downmodded due to ignorance, so here is one article backing up my answer.

From http://surguy.net/articles/removing-log-messages.xml:

In Log4J, if you log a message at DEBUG level, and the current Appender is set to only log messages of INFO level and above, then the message will not be displayed. The performance penalty for calling the log method itself is minimal - a few nanoseconds. However, it may take longer to evaluate the arguments to the log method. For example:

logger.debug("The large object is "+largeObject.toString());

Evaluating largeObject.toString() may be slow, and it is evaluated before the call to the logger, so the logger cannot prevent it being evaluated, even though it will not be used.

Edit 2: from the log4j manual itself (http://logging.apache.org/log4j/1.2/manual.html):

The user should be aware of the following performance issues.

  1. Logging performance when logging is turned off. When logging is turned off entirely or just for a set of levels, the cost of a log request consists of a method invocation plus an integer comparison. On a 233 MHz Pentium II machine this cost is typically in the 5 to 50 nanosecond range.

    However, The method invocation involves the "hidden" cost of parameter construction.

    For example, for some logger cat, writing,

     logger.debug("Entry number: " + i + " is " + String.valueOf(entry[i]));
    

    incurs the cost of constructing the message parameter, i.e. converting both integer i and entry[i] to a String, and concatenating intermediate strings, regardless of whether the message will be logged or not. This cost of parameter construction can be quite high and it depends on the size of the parameters involved.

    To avoid the parameter construction cost write:

    if(logger.isDebugEnabled() {
      logger.debug("Entry number: " + i + " is " + String.valueOf(entry[i]));
    }
    

    This will not incur the cost of parameter construction if debugging is disabled. On the other hand, if the logger is debug-enabled, it will incur twice the cost of evaluating whether the logger is enabled or not: once in debugEnabled and once in debug. This is an insignificant overhead because evaluating a logger takes about 1% of the time it takes to actually log.

danben
Actually it is not a good idea to wrap all your log statements into if statements. Logging should be as little work as possible, that is why you have a logging framework. Also if the log statement (accidentally) has a side effect you will make debug different from release. Only when you do this a million times you should add the if. Also it is possible that the VM already optimized this code that is never used. (Btw, the remark that it is evaluated is of course correct)
Thirler
Sorry, you are grossly uninformed. The VM cannot possibly optimize this away, as the statement IS EXECUTED every time. You don't need "a million" log statements for a performance penalty, you just need one log statement that is in a loop. And "logging should be as little work as possible" is a meaningless statement in this context - especially given the OP is specifically asking about performance.
danben
I agree with danben, in certain cases. If you want to log 10 lines in a row with large strings (or complex toString()) calls in them, wrapping it in an if is a good idea. I don't think the downvote was deserved.
karoberts
The downmod is a bit too harsh, so I will remove it. However, to me your post seems to advice to always check the log level before logging. I still strongly oppose to that, perhaps you should add that this should only be done when the log statement is a performance problem.Do note that with Java 1.6 many optimizations have been added regarding method calls (such as automatic inlining). I was suggesting that possibly this was one of them (it being a call to a function that does nothing, where the evaluation of the arguments has no side effects)
Thirler
My favorite "optimization golf" technique is removing debug log statements from tight loops. It might seem marginal overhead for 1 task, but the net effect of "harmless" code accumulates very quickly at higher orders of magnitude.
TK
A: 
  1. log4j will process the log statement, and check to see if the particular logger is enabled at the particular logging level. If it is not, then the statement will not be logged.

  2. These checks are significantly less expensive than the actual writing to disk (or console), but they still have an impact.

  3. no, java has no such concept as #ifdef (out of the box anyway, there are java precompilers out there)

karoberts
+4  A: 

I ran a simple benchmark to test.

    for (int j = 0; j < 5; j++) {
        long t1 = System.nanoTime() / 1000000;
        int iterations = 1000000;
        for (int i = 0; i < iterations; i++) {
            int test = i % 10;
            log.debug("Test " + i + " has value " + test);
        }
        long t2 = System.nanoTime() / 1000000;
        log.info("elapsed time: " + (t2 - t1));

        long t3 = System.nanoTime() / 1000000;
        for (int i = 0; i < iterations; i++) {
            int test = i % 10;
            if (log.isDebugEnabled()) {
                log.debug("Test " + i + " has value " + test);
            }
        }
        long t4 = System.nanoTime() / 1000000;
        log.info("elapsed time 2: " + (t4 - t3));
    }

elapsed time: 539
elapsed time 2: 17
elapsed time: 450
elapsed time 2: 18
elapsed time: 454
elapsed time 2: 19
elapsed time: 454
elapsed time 2: 17
elapsed time: 450
elapsed time 2: 19

With 1.6.0_18, which surprised me as I would have thought the inlining would have prevented this. Maybe Java 7 with escape analysis will.

However I still wouldn't wrap debug statements in an if clause unless time improvements of the order of half a microsecond get to be important!

Pool