How to ignore code blocks in Sonar code coverage analysis?
Asked Answered
R

3

15

In our code, there are quite a lot of logging fragments like this:

if(logger.isDebugEnabled()) {
    logger.debug("...")
}

Is it possible to configure SonarQube so that such code blocks are not included in the code coverage analysis? Writing tests to cover such debug statements does not seem to make much sense...

I found out how to:

  • ignore entire files from coverage analysis
  • ignore issues in code blocks

But I did not find a way of excluding a code block from the coverage analysis only.

Rudiment answered 18/5, 2015 at 9:49 Comment(1)
As a side note, people often overuse isDebugEnabled(). logger.debug() will anyway check the current log level (at least in major logger implementations). Adding an isDebugEnabled() 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 the if condition.Dubuffet
M
4

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.

Munn answered 8/4, 2022 at 7:18 Comment(2)
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.Dubuffet
@Dubuffet Good point. I have included your comment in the answer for more visibility, as well as a possible alternative approach to avoid that performance issue.Munn
P
-2

for files:

sonar-project.properties

sonar.exclusions=**/someglobalfolder*/** , **/someotherglobalfolder/*.js

For commenting out blocks:

See article: Turning Sonar off for certain code

Pertinent answered 10/4, 2022 at 14:50 Comment(1)
This doesn't answer the question... OP said (s)he already knows how to ignore coverage of whole files, and the article you mentioned regarding blocks of code is about ignoring issues, not ignoring test coverage as asked by OP. BTW, sonar.exclusions will exclude both issues and coverage, to exclude only coverage, one should rather use sonar.coverage.exclusions.Dubuffet
G
-3

I have also faced same problem.Rather ignoring it I used following two techniques

1)Mock These Logger Using any Mocking Framework example Mockito,Powermockito,PowerMock etc.Use same Mocking Code Across Test Classes wherever Applicable

2) Keep logback-test.xml(or logging config file for whatever logging framework you using) in class path and set Lower Log Level Like Trace.So let test classes to load logger to print these statements.

This will help in showing details about how test cases executing statement statement

Grangerize answered 25/5, 2015 at 4:36 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.