Our traditional way to do logging is :
if (logger.isDebugEnabled()) {
logger.debug("something to log, and barbarbar {}", object);
}
But what about using lambda expression to do logging to reduce lines of codes and complexity?
logger.debug(() -> return "something to log, and barbarbar " + object);
Is there any reason that slf4j or other logger do not provide this way to do logging? Object creation overhead caused?
Our traditional way to do logging is ...
It is not obligatory to use condition logger.isXXXXEnabled() in every use case. It's intended to be used only when you log an object that takes significant time to be created. For example when you need to put into log result of some DB query or complex structure converted to XML/JSON, etc.
For other cases this check is excessive.
Related
Although in some tutorials, for example here (Parametrized logging section), said that Logback message {} parametrization help us to avoid unnecessary calculation in logging data (if logging level is not DEBUG):
logger.debug("The bonus for employee {} is {}",
employee.getName(), employeeService.calculateBonus(employee));
I tested (on logback version 1.2.3) that this optimization works only for unnecessary toString() of parameter object - as this works for log4j.
Logback documentation doesn't cover this detail.
So, we have to use isDebugEnabled() for all 'expensive' logging, do we?
Take a look at the example here
Since 2.4, methods have been added to the Logger interface to support lambda expressions. The new methods allow client code to lazily log messages without explicitly checking if the requested log level is enabled. For example, previously one would write:
// pre-Java 8 style optimization: explicitly check the log level
// to make sure the expensiveOperation() method is only called if necessary
if (logger.isTraceEnabled()) {
logger.trace("Some long-running operation returned {}", expensiveOperation());
}
With Java 8, the same effect can be achieved with a lambda expression:
// Java-8 style optimization: no need to explicitly check the log level:
// the lambda expression is not evaluated if the TRACE level is not enabled
logger.trace("Some long-running operation returned {}", () -> expensiveOperation());
When you make a method call, e.g. employeeService.calculateBonus(employee), you're calling that method. Simple as that. So you are calculating the employee bonus every time this line is hit. There is no lazy evaluation here.
Whether or not to use log.isDebugEnabled() depends on the situation. In this situation, if that method call is expensive, you should wrap that in a debug enabled check.
In the case of getters, this isn't usually necessary. So, for instance, I would not wrap this in an isDebugEnabled check:
log.debug("Calculating bonus for employee {} {}", employee.firstName(), employee.lastName());
These are simple getters that return a String, so no expensive calculations are done.
Is there any advantage of using {} instead of string concatenation?
An example from slf4j
logger.debug("Temperature set to {}. Old temperature was {}.", t, oldT);
instead of
logger.debug("Temperature set to"+ t + ". Old temperature was " + oldT);
I think it's about speed optimization because parameters evaluation (and string concatenation) could be avoided in runtime depending on a config file. But only two parameters are possible, then sometimes there is no other choice than string concatenation. Needing views on this issue.
It is about string concatenation performance. It's potentially significant if your have dense logging statements.
(Prior to SLF4J 1.7) But only two parameters are possible
Because the vast majority of logging statements have 2 or fewer parameters, so SLF4J API up to version 1.6 covers (only) the majority of use cases. The API designers have provided overloaded methods with varargs parameters since API version 1.7.
For those cases where you need more than 2 and you're stuck with pre-1.7 SLF4J, then just use either string concatenation or new Object[] { param1, param2, param3, ... }. There should be few enough of them that the performance is not as important.
Short version: Yes it is faster, with less code!
String concatenation does a lot of work without knowing if it is needed or not (the traditional "is debugging enabled" test known from log4j), and should be avoided if possible, as the {} allows delaying the toString() call and string construction to after it has been decided if the event needs capturing or not. By having the logger format a single string the code becomes cleaner in my opinion.
You can provide any number of arguments. Note that if you use an old version of sljf4j and you have more than two arguments to {}, you must use the new Object[]{a,b,c,d} syntax to pass an array instead. See e.g. http://slf4j.org/apidocs/org/slf4j/Logger.html#debug(java.lang.String, java.lang.Object[]).
Regarding the speed: Ceki posted a benchmark a while back on one of the lists.
Since, String is immutable in Java, so the left and right String have to be copied into the new String for every pair of concatenation. So, better go for the placeholder.
Another alternative is String.format(). We are using it in jcabi-log (static utility wrapper around slf4j).
Logger.debug(this, "some variable = %s", value);
It's much more maintainable and extendable. Besides, it's easy to translate.
I think from the author's point of view, the main reason is to reduce the overhead for string concatenation.I just read the logger's documentation, you could find following words:
/**
* <p>This form avoids superfluous string concatenation when the logger
* is disabled for the DEBUG level. However, this variant incurs the hidden
* (and relatively small) cost of creating an <code>Object[]</code> before
invoking the method,
* even if this logger is disabled for DEBUG. The variants taking
* {#link #debug(String, Object) one} and {#link #debug(String, Object, Object) two}
* arguments exist solely in order to avoid this hidden cost.</p>
*/
*
* #param format the format string
* #param arguments a list of 3 or more arguments
*/
public void debug(String format, Object... arguments);
Concatenation is expensive, so you want it to happen only when needed. By using {}, slf4j performs the concatenation only if the trace is needed. In production, you may configure the log level to INFO, thus ignoring all debug traces.
A trace like this will concatenate the string even if the trace will be ignored, which is a waste of time :
logger.debug("Temperature set to"+ t + ". Old temperature was " + oldT);
A trace like this will be ignored at no cost :
logger.debug("Temperature set to {}. Old temperature was {}.", t, oldT);
If you have a lot of debug traces that you ignore in production, using {} is definitely better as it has no impact on performance.
Compliant logging is highly important for application development, as it affects performance.
The mentioned non-compliant logging is resulting with redundant toString() method invocation on each call, and is resulting with redundant temporary memory allocation and CPU processing, as can be seen at example high scale test execution, where we can take a look on redundant allocated temporary memory:
Look on method profiling:
Note: I am the author of this blog post, Logging impact on application performance.
As system evolves, the logging statements will be changed to meet the new requirements, and ideally the logging statements which have identical or very similar context should be changed consistently. But in many cases it's hard for developers to remember the existence of all of them. Then they may only change a portion of them, and forget to change the other ones consistently.
Take this Java code snippet as an example, there are two sibling classes (ChildClassA, ChildClassB) which both extend the same superclass (ParentClass), and they have a pair of similar methods which have similar functions and contain the same logging statements.
public class ChildClassA implements ParentClass{
public void processShellCommand(){
...
logger.error("Error initializing command, field " + field.getName() + " is not accessible.");
...
}
public class ChildClassB implements ParentClass{
public void processNetworkCommand(){
...
logger.error("Error initializing command, field " + field.getName() + " is not accessible.");
...
}
Is there a solution such as a tool, or some documents, etc. that can help the consistent changing of them?
When it comes to logging I think you really should try to avoid putting details in the log.[whatever_leve].([message_text]] statement (at least when in comes to errors), instead you want to create your own Exception classes and put message details in them. Have a filter/interceptor for dealing with logging of unexpected exceptions is also a good practice.
So in you code example that would be that the sub-classes thrown a typed Exception, lets call it InitializingException(...). It is then up the the caller or a filter to deal with it and log it.
You want to care about the logging part of your code base in the same way as you do for business-logic code (one can argue it is part of it), so salute DRY (do-not-repeat-yourself).
Same logic applies for Debug and Trace statements as well, you don't want to copy-paste the same message across the system. So general refactoring should be applied to avoid it. But I generally think the a debug or trace message is likely to change (it is up to the developer).
I am trying to implement logger using apache commonn logging and log4j.xml file as configuration file .
So in the actual java code i am writing log as
I am using appache common logging
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
and i am logging information such as in . I am creating a instance of log for each class
private static Log logger = LogFactory.getLog( MyClass.class );
private static final String name ="SAM";
logger.info("name= "+name);
So now my question is does implementing logger like this create a performanace issue?
Is it necessary to check log like
if ( log.isInfoEnabled( ) )
{
log.info( "Info message" );
}
The confusion is mainly because in Apache common logging they have mentioned to do this and in log4j they have mentioned it is unnecessary.
Each time when we write log in the files?
-Sam
yes and no, logging always reduces your performance, but some functions are more expensive then others, e.g. getting the calling class/method-Name uses reflection and is very slow. But a normal logfuntion is not that expensive if you do not have an expensive statement in the calling logging function (this will be evaluated every time before the log-Level is checked. In this case you can use the .isLevelEnabledcheck to prevent the evaluation). Also logging to the console takes longer for the output than logging to a file. You will find more information about this by googling and in the FAQ/manual of log4j.
You do not have to check the Log-Level before logging. This is done within the log-function itself. Therefore are the different methods for every Level or the Level-Argument in the generic log-Method.
A big potential performance problem with logging is usually if you have something passed into the log method that is very expensive to convert to a string. That's why you have methods like isInfoEnabled(), so that the code can avoid creating the message string from the parameter (otherwise the check within the info() method is too late, the conversion is already done). If your objects passed into the log methods are strings or are not very involved then the is*Enabled() methods won't be that useful.
SLF4J is worth checking out. It doesn't depend on classloader tricks (which is a big part of why commons-logging is reviled), and it has a different way of creating log messages which delays when the message string gets created so that the enabled check can take place within the logging method.
There's a better way to do all of this. You can get really great performance without having to add the clutter of ifDebugEnabled, etc methods. Check out something like Logback (or SLF4J). Here's the great documentation about what kind of an API you want. Note that Log4J and Commons-Logging doesn't have an API like this. Use Parameterized Logging.
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 (i.e., 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.