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.
Related
I am trying to upgrade log4j to log4j2. The particular line of code I am trying to upgrade is:
log(targetClass, Priority.DEBUG_INT, message, null);
The static field Priority.DEBUG_INT is no longer available in the new Priority. Instead it looks like the getPriority(Facility facility, org.apache.logging.log4j.Level level) static method is used to access priority int value, to which DEBUG can be specified as the Level.
However, this method also requires a Facility to be specified. How do I know which Facility to specify when calling getPriority?
old Priority: https://logging.apache.org/log4j/1.2/apidocs/org/apache/log4j/Priority.html
new Priority: https://logging.apache.org/log4j/2.x/log4j-core/apidocs/org/apache/logging/log4j/core/net/Priority.html
new Facility: https://logging.apache.org/log4j/log4j-2.8/log4j-core/apidocs/org/apache/logging/log4j/core/net/Facility.html
Assuming you are talking about Log4j 1's Category.log(String, Priority, Object, Throwable), it appears these Priority classes are pretty (if not completely) unrelated. Log4j 1's Priority is actually the level (indeed it has a subclass Level).
So you would have to look at Log4j 2's Logger class to see if any method with Level parameter matches, but there appears to be no identical alternative (except logMessage maybe, but that appears to be pretty low level).
However, you should check whether that targetClass argument for parameter callerFQCN is actually needed. It looks like it is intended to find the caller of the logger method and might mainly be intended for usage by logging libraries extending Log4j 1. Unless you are indeed upgrading such library, I would assume that the method is misused and a regular Log4j 2 logger.debug(message) would do equally well. Though it would help nonetheless if you could provide more context.
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.
slf4j's advantage is that it avoids overhead of string concatenation, by providing {}.
Why can't this be done with existing System.out.println() method by overloading it?
The performance advantage you mention exists only when the message that would be created is actually ignored afterwards and the message is discarded without being used.
Whenever the string will actually be used (e.g., logged or printed), there is no performance advantage of using format strings.
Thus this advantage only exists for logging methods, when the level of the log message is lower than the current log level set in the logger. In this situation, the string concatenation can be avoided completely if using format strings and passing the arguments separately. This is a common case, because most of the log messages typically have a low level, and most of the time the actual log level is set to something higher. So in production, where performance is important, most of the log messages can be ignored and need not have their full string created.
For System.out.println(), this advantage does not exist, because standard out is always active. Every call to it will lead to the string for the message being created and printed. There is no log level for System.out.println() which could be set to a higher value to ignore some of the calls to this message.
Thus the string concatenation always has to be done, and it is irrelevant whether it is done by the calling code, or by the method itself.
Furthermore, as Oliver mentioned, a similar method exists with System.out.printf(), only the syntax of the format string is different.
I'd like to put the logger.debug(...) call into a helper method, and call that helper method from anywhere that needs to write the log. While this mostly works fine, the log entry itself shows the helper method as the source of the call, which is understandable since log4j isn't aware of me using a helper method for logging.
Is there any way to tell it to skip the helper method when figuring out the source of the logger.debug(...) call and instead use its caller?
By source, I mean %F:%L of org.apache.log4j.PatternLayout: https://logging.apache.org/log4j/1.2/apidocs/org/apache/log4j/PatternLayout.html
To illustrate what I mean, here's an example stack trace:
1. logger.debug(msg, throwable)
2. logHelper(logger, msg, throwable) <-- currently shows as the source of the call, since it calls logger.debug directly
3. someFunction <-- where the real logable event occurs, so I'd want this to be logged as the source
The reason I'm doing this is because I'd like an exception logged only by name (e.toString) if the log level is INFO, or with a full stack trace if the level is DEBUG. I'm open to suggestions for alternatives.
Thanks! :)
You can't instruct Log4J to "skip" a level in its source detection.
What you can do, though, is subclass PatternLayout and override the format(LoggingEvent) method to your liking (i.e. only produce the exception's toString() when in INFO mode, etc).
One way to do this is to have the helper class call:
logger.log(LogHelper.class.getName(), Level.DEBUG, message, t);
... instead of:
logger.debug(message, t);
So when log4j tries to determine the source of the event, it will stop when it reaches the parent of LogHelper instead of the parent of Logger.
Verified working as expected.
For Log4j2 the answer is provided completely by the use of logger wrappers as described in the Log4j2 manual under Example Usage of a Generated Logger Wrapper. One can simply generate (using the org.apache.logging.log4j.core.tools.Generate$ExtendedLogger tools illustrated there) a logger wrapper with a single STUB level, and then adapt that to create custom logging methods mimicking the use of the logIfEnabled(FQCN, LEVEL, Marker, message, Throwable) - possibly ignoring the STUB level and using the regular ones - then if desired, deleting or commenting out the STUB level and its methods). For this purpose the FormattedMessage can be helpful.
The method and source line, while expensive, can then be easily shown as part of the full location information by using the %l location conversion pattern element in the PatternLayout given in the configuration, or more specifically using the %L line number and/or the %M method conversion.
Now with complete example at: Java Logging: Log4j Version2.x: show the method of an end-client caller (not an intermediate logging helper method)
For log4j2, you can write a simple wrapper class or generate one as Webel suggests. See my answer for log4j2:
https://stackoverflow.com/a/39045963/116810
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.