Since Sonarqube can import JaCoCo coverage report, you could use an annotation containing "Generated
" in its name, as explained here by nineninesevenfour.
In your case, you would replace your log calls with logDebug()
calls, and:
@Generated
void logDebug(String message) {
if(logger.isDebugEnabled()) {
logger.debug("...")
}
}
fbastien adds in the comments:
Using @Generated
is a nice trick.
Beware though not to use this when logging a message whose parameters are costly to retrieve, since encapsulating this in a method would require the parameters to be always computed, thus defeating the purpose of checking isDebugEnabled()
before retrieving and logging them.
That is a valid concern regarding the performance implications of encapsulating the logging statement inside a separate method.
In Java, method arguments are evaluated before the method is called. So, if you encapsulate the logging call inside a method, any expensive computation required to produce the log message will always be executed, regardless of whether logger.isDebugEnabled()
returns true or false.
For example, consider the following:
@Generated
void logDebug(String message) {
if (logger.isDebugEnabled()) {
logger.debug(message);
}
}
// Calling the method
logDebug(expensiveComputation()); // expensiveComputation() will always execute
The expensiveComputation()
method would execute irrespective of the logging level, which would potentially cause performance issues.
A possible alternative would be to lazily evaluate parameters.
Instead of passing the message directly, you could pass a supplier function that generates the message.
@Generated
void logDebug(Supplier<String> messageSupplier) {
if (logger.isDebugEnabled()) {
logger.debug(messageSupplier.get());
}
}
Then, call the function like this:
logDebug(() -> expensiveComputation());
That alternative using Supplier<String>
aligns well with the comment's concern by making sure the expensive computation only occurs when debugging is enabled, and preserving the performance benefits of the original isDebugEnabled()
check.
@Generated
void logDebug(Supplier<String> messageSupplier) {
if (logger.isDebugEnabled()) {
logger.debug(messageSupplier.get());
}
}
The logDebug
method is annotated with @Generated
to be excluded from code coverage analysis.
It accepts a Supplier<String>
to delay the potentially expensive computation of the log message until it is confirmed that debug logging is enabled. So the performance optimization of the original isDebugEnabled()
check is preserved.
isDebugEnabled()
.logger.debug()
will anyway check the current log level (at least in major logger implementations). Adding anisDebugEnabled()
condition is only useful for messages with parameters, in order to avoid unnecessary message formatting and/or data retrieval impacting performance. Although your code snippet is probably a simplified version of your actual code, when I see such pattern logging a message without parameters, IMO it's good for a no-brainer removal of theif
condition. – Dubuffet