简体   繁体   中英

Java logging API overhead

I've read a bit about the various ways of logging a debugging message with Java, and coming from a C background my concern is as follow :

Those libraries claim minimal overhead in case where logging is disabled (such as production environment), but since argument to their log() function are still evaluated, my concern is that the overhead in real-world scenario will, in fact, not be negligible at all.

For example, a log(myobject.toString(), "info message") still has the overhead of evaluating myobject.toString() , which can be pretty big, even if the log function itself does nothing.

Does anyone has a solution to this issue ?

PS: for those wondering why I mentioned a C background : C lets you use preprocessor macro and compile-time instructions that will completely remove all the code related to debugging at compilation time, including macros parameters (which will simply not appear at all).

EDIT : After having read the first batch of answers, it seems that java clearly doesn't have anything that would do the trick (think logging the cosine of a number in a big loop in a mobile environment where every bit of CPU matters). So i'll add that i would even go for an IDE based solution. My last resort being building something like a "find all / replace" macro. I first thought that maybe something grabbed from an aspect oriented framework would help... Anyone ?

I think that the log4j FAQ does a good job of addressing this:

For some logger l, writing,

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

incurs the cost of constructing the message parameter, that is converting both integer i and entry[i] to a String, and concatenating intermediate strings. This, regardless of whether the message will be logged or not.

If you are worried about speed, then write

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

This way you will not incur the cost of parameter construction if debugging is disabled for logger l. On the other hand, if the logger is debug enabled, you will incur the cost of evaluating whether the logger is enabled or not, twice: once in debugEnabled and once in debug. This is an insignificant overhead since evaluating a logger takes less than 1% of the time it takes to actually log a statement.

Using a guard clause is the general approach to avoid string construction here.

Other popular frameworks, such as slf4j , take the approach of using formatted strings / parameterized messages so that the message is not evaulated unless needed.

Modern logging frameworks have variable replacement. Your logging then looks something like this:

log.debug("The value of my first object is %s and of my second object is %s", firstObject, secondObject).

The toString() of the given objects will then only be executed when logging is set to debug. Otherwise it will just ignore the parameters and return.

The answer is pretty simple: don't call expensive methods in the log call itself. Plus, use guards around the logging call, if you can't avoid it.

 if(logger.isDebugEnabled()) {
    logger.debug("this is an "+expensive()+" log call.");
 }

And as others have pointed out, if you have formatting available in your logging framework (ie, if you're using one modern enough to support it, which should be every one of them but isn't), you should rely on that to help defray expense at the point of logging. If your framework of choice does not support formatting already, then either switch or write your own wrapper.

You're right, evaluating the arguments to a log() call can add overhead that is unnecessary and could be expensive.

That's why most sane logging frameworks provide some string formatting functions as well, so that you can write stuff like this:

log.debug("Frobnicating {0}", objectWithExpensiveToString);

This way your only overhead is the call to debug() . If that level is deactivated, then nothing more is done and if it is activated, then the format string is interpreted, the toString() on objectWithExpensiveToString() is called and the result inserted into the format string before it is logged.

Some log statements use MessageFormat style placeholders ( {0} ), others use format() style placeholders ( %s ) and yet others might take a third approach.

You can use a funny way - a bit verbose, though - with assertions. With assertions turned on, there will be output and overhead, and with assertions turned off, no output and absolutely no overhead.

public static void main(String[] args) {
    assert returnsTrue(new Runnable() {
        @Override
        public void run() {
            // your logging code
        }
    });
}

public static boolean returnsTrue(Runnable r) {
    r.run();
    return true;
}

The returnsTrue() function is needed here because I know no better way of making an expression return true, and assert requires a boolean.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM