mojohaus/cobertura-maven-plugin

Cobertura 2.1.1 uses slf4j binding logback-classic

Closed this issue · 18 comments

nomis commented

Cobertura 2.1.1 uses the slf4j binding logback-classic, which causes problems when the project has a different slf4j binding:

[INFO] --- cobertura-maven-plugin:2.7:instrument (report:cobertura) @ lightswitch-android ---
[INFO] Cobertura 2.1.1 - GNU GPL License (NO WARRANTY) - See COPYRIGHT file
[INFO] Cobertura: Saved information on 10 classes.
[INFO] Cobertura: Saved information on 10 classes.

[ERROR] SLF4J: Class path contains multiple SLF4J bindings.
SLF4J: Found binding in [jar:file:/mnt/ssd/m2/repository/ch/qos/logback/logback-classic/1.0.13/logback-classic-1.0.13.jar!/org/slf4j/impl/StaticLoggerBinder.class]
SLF4J: Found binding in [jar:file:/mnt/ssd/m2/repository/eu/lp0/slf4j/slf4j-android/1.7.12-0/slf4j-android-1.7.12-0.jar!/org/slf4j/impl/StaticLoggerBinder.class]
SLF4J: See http://www.slf4j.org/codes.html#multiple_bindings for an explanation.
SLF4J: Actual binding is of type [ch.qos.logback.classic.util.ContextSelectorStaticBinder]

I'm seeing this same issue. Upvote.

lp0 - Did you find a good workaround?

nomis commented

You should be able to exclude the dependency.

I've attempted to exclude it by adding the dependency below (this is just one of several variations attempted). While this seemed to make Maven reclassify the logback-classic dependency as 'provided' instead of 'compile' Maven still puts logback-classic on the classpath for the tests that get run for cobertura.

I realize that this isn't a Maven troubleshooting forum so I don't expect you to troubleshoot my method of excluding this dependency. I'm sharing the info to illustrate that even intermediate Maven users may find that this issue is difficult to resolve.

         <plugin>
          <groupId>org.codehaus.mojo</groupId>
          <artifactId>cobertura-maven-plugin</artifactId>
          <version>2.7</version>
          <dependencies>
           <dependency>
            <groupId>net.sourceforge.cobertura</groupId>
            <artifactId>cobertura-runtime</artifactId>
            <version>2.1.1</version>
            <type>pom</type>
            <exclusions>
             <exclusion>
              <groupId>ch.qos.logback</groupId>
              <artifactId>logback-classic</artifactId>
             </exclusion>
            </exclusions>  
           </dependency>
           <dependency>
            <groupId>net.sourceforge.cobertura</groupId>
            <artifactId>cobertura</artifactId>
            <version>2.1.1</version>
            <exclusions>
             <exclusion>
              <groupId>ch.qos.logback</groupId>
              <artifactId>logback-classic</artifactId>
             </exclusion>
            </exclusions>  
           </dependency>
          </dependencies>
         </plugin>

More info:

This file appears to have been modified to a new try/catch block around the spot where the exception at hand is thrown:

https://github.com/cobertura/cobertura/blob/master/cobertura/src/main/java/net/sourceforge/cobertura/coveragedata/TouchCollector.java

From the change history this is the issue that change intended to address:

cobertura/cobertura#52

Also, in addition to excluding logback-classic in the cobertura-maven-plugin definition in my POM file I've played with telling Cobertura to remove logback-classic from the classpath. That did work to get it off of the classpath but I still get this error:

java.lang.ExceptionInInitializerError: null
at net.sourceforge.cobertura.coveragedata.TouchCollector.registerClass(TouchCollector.java:49)
at net.sourceforge.cobertura.coveragedata.TouchCollector.registerClass(TouchCollector.java:85)

Here's how I told Cobertura to keep logback-classic out of the classpath:

mvn org.codehaus.mojo:cobertura-maven-plugin:2.7:cobertura -Dmaven.test.dependency.excludes=ch.qos.logback:logback-classic

The above didn't work as expected with maven-surefire-plugin v2.6 (the feature was first introduced in that version) but did work in version 2.18.1.

I spoke too soon. TouchCollector.java was modified to swallow only "NoClassDefFoundError" and we're dealing with "ExceptionInInitializerError". I noticed this only after building and running the latest snapshot version of cobertura and cobertura-runtime underneath of maven-cobertura-plugin. Using that latest master/trunk version of cobertura didn't resolve the issue.

This is also affecting many of our projects and I am keen to learn a work around or when a permanent solution will be available.

I think we may actually be dealing with two problems here. I'm not convinced that the inclusion of logback-classic is causing the "ExceptionInInitializerError" that (in my case at least) follows the "SLF4J: Class path contains multiple SLF4J bindings" error. I say this because after excluding logback from covertura-maven-plugin's dependencies AND instructing Surefire to keep it off the classpath the "SLF4J: Class path contains multiple SLF4J bindings" warning did go away but I still got the "ExceptionInInitializerError".

Is this issue solved. I am using 2.1.1 but still getting the same issue.
SLF4J: Class path contains multiple SLF4J bindings.
SLF4J: Found binding in [bundleresource://525.fwk387837533:1/org/slf4j/impl/StaticLoggerBinder.class]
SLF4J: Found binding in [bundleresource://525.fwk387837533:2/org/slf4j/impl/StaticLoggerBinder.class]
SLF4J: See http://www.slf4j.org/codes.html#multiple_bindings for an explanation.
SLF4J: Actual binding is of type [org.slf4j.impl.Log4jLoggerFactory]

As I understand it, this issue is still open. These SLF4J dependencies tripped me up with Ivy (same error v2.1.1, java.lang.NoClassDefFoundError: net/sourceforge/cobertura/coveragedata/TouchCollector), with no indication of what the true underlying exception was. My ant environment included SLF4J-simple (requirement from a different API), and cobertura specifically requires ch.logback. If the ch.logback libraries are not used, then the test cases break with the above exception and a misleading stack trace. This seems contrary to a principle of testing, which is to not interfere with the normal runtime environment. SLF4J is over-complicating the build process. Are there any reasons not to switch over to java.util.logging and eliminate these dependencies? I'd be happy to contribute these changes myself, but I'd like to hear from the development team first.

Any solution or a work around for this yet? I am using coverture 2.2.1. Running into a similar problem using cobertura command line.

In my case, it helped to add the following section to the dependencyManagement-Section of pom.xml as a workaround:

                   <dependency>
                           <groupId>net.sourceforge.cobertura</groupId>
                           <artifactId>cobertura</artifactId>
                           <version>2.1.1</version>
                           <scope>test</scope>
                           <exclusions>
                                  <exclusion>
                                        <groupId>ch.qos.logback</groupId>
                                        <artifactId>logback-classic</artifactId>
                                  </exclusion>
                           </exclusions>
                    </dependency>

The cobertura-maven-plugin programatically adds a dependency to cobertura-runtime when running the instrumented tests.The transitively included dependencies can be controlled via the dependency management declaration, where one can exclude the logback artifact.

caomu commented

+1,the same problem to me .i use
org.codehaus.mojo
cobertura-maven-plugin
2.7
and the error is

`[ERROR] SLF4J: Class path contains multiple SLF4J bindings.
SLF4J: Found binding in [jar:file:/D:/.m2/repository/ch/qos/logback/logback-clas
sic/1.0.13/logback-classic-1.0.13.jar!/org/slf4j/impl/StaticLoggerBinder.class]
SLF4J: Found binding in [jar:file:/D:/.m2/repository/ch/qos/logback/logback-clas
sic/1.1.7/logback-classic-1.1.7.jar!/org/slf4j/impl/StaticLoggerBinder.class]
SLF4J: See http://www.slf4j.org/codes.html#multiple_bindings for an explanation.

SLF4J: Actual binding is of type [ch.qos.logback.classic.util.ContextSelectorSta
ticBinder]`

You can also avoid this by overriding the version to match your codebase by doing something like this:

           <plugin>
                <groupId>org.codehaus.mojo</groupId>
                <artifactId>cobertura-maven-plugin</artifactId>
                <version>2.7</version>
                <configuration>
                    <check>
                        <!--<haltOnFailure>true</haltOnFailure>--><!-- optional -->
                        <!-- Per-class thresholds -->
                        <lineRate>60</lineRate>
                        <branchRate>40</branchRate>
                        <!-- Project-wide thresholds -->
                        <totalLineRate>60</totalLineRate>
                        <totalBranchRate>40</totalBranchRate>
                    </check>
                    <instrumentation>
                        <ignoreTrivial>true</ignoreTrivial>
                        <ignoreMethodAnnotation>com.cvent.codecoverage.CoverageIgnore</ignoreMethodAnnotation>
                    </instrumentation>
                </configuration>
                <dependencies>
                    <dependency>
                        <groupId>ch.qos.logback</groupId>
                        <artifactId>logback-classic</artifactId>
                        <version>1.1.2</version>
                    </dependency>
                    <dependency>
                        <groupId>ch.qos.logback</groupId>
                        <artifactId>logback-core</artifactId>
                        <version>1.1.2</version>
                    </dependency>
                </dependencies>
                <executions>
                    <execution>
                        <phase>verify</phase>
                        <goals>
                            <goal>clean</goal>
                            <goal>check</goal>
                        </goals>
                    </execution>
                </executions>
            </plugin>

I was able to solve this on my project using a combination of information provided within this thread. This solution is for a project that uses Log4J2 (via SLF4J) as its logging implementation, but feel free to use your own logging implementation dependencies instead as needed.

The first thing I had to do was to add a standard dependency to the cobertura artifact that excludes logback-classic as follows:

<!--
  This dependency will ensure that logback-classic isn't used when the Cobertura code base
  is referenced via the cobertura-maven-plugin.
 -->
<dependency>
   <groupId>net.sourceforge.cobertura</groupId>
   <artifactId>cobertura</artifactId>
   <version>2.1.1</version>
   <exclusions>
      <exclusion>
         <groupId>ch.qos.logback</groupId>
         <artifactId>logback-classic</artifactId>
      </exclusion>
   </exclusions>
</dependency>

The second thing I had to do was to modify the cobertura-maven-plugin configuration to also exclude logback-classic from the cobertura dependency and include our Log4J2 dependencies instead. In addition, I had to add a dependency to another local project that contains a valid Log4J2 configuration file as a resource (named log4j2.xml) that appends to the console. There were some flows that required this since some errors were displayed by Log4J2 that no configuration file was found. Here is an example of what the plugin configuration looks like:

<plugin>
   <groupId>org.codehaus.mojo</groupId>
   <artifactId>cobertura-maven-plugin</artifactId>
   <version>2.7</version>
   <executions>
      <execution>
         <phase>test</phase>
         <goals>
            <goal>cobertura</goal>
         </goals>
      </execution>
   </executions>
   <!--
     This set of dependencies excludes logback-classic from the Cobertura execution and uses
     Log4J2 (via SLF4J) instead. This is needed because our JUnits use Log4J2 and already have
     that JAR included in the classpath. If we let Cobertura use its default logback-classic,
     SLF4J will display errors on the console when the JUnits are run since two logging
     implementations will be found on the classpath.

     In addition, we added a dependency to log4j2-config which contains a basic Log4J2
     configuration file to use when running JUnits via Cobertura. Without it, Log4J2 will
     sometimes display errors on the console that no configuration file is present.
   -->
   <dependencies>
      <dependency>
         <groupId>org.apache.logging.log4j</groupId>
         <artifactId>log4j-slf4j-impl</artifactId>
         <version>2.6.2</version>
      </dependency>
      <dependency>
         <groupId>org.apache.logging.log4j</groupId>
         <artifactId>log4j-core</artifactId>
         <version>2.6.2</version>
      </dependency>
      <dependency>
         <groupId>my.project</groupId>
         <artifactId>log4j2-config</artifactId>
         <version>${project.version}</version>
      </dependency>
      <dependency>
         <groupId>net.sourceforge.cobertura</groupId>
         <artifactId>cobertura</artifactId>
         <version>2.1.1</version>
         <exclusions>
            <exclusion>
               <groupId>ch.qos.logback</groupId>
               <artifactId>logback-classic</artifactId>
            </exclusion>
         </exclusions>
      </dependency>
   </dependencies>
</plugin>

This was a tricky problem to solve so I hope this helps others. Good luck!

nomis commented

Please stop providing workarounds so that this bug will actually be fixed. The only workaround I am using is to continue to use the older version. If you're not going to contribute a fix to the issue then I will close it and reopen another one without all these unnecessary comments.

None of the suggestions in here worked for me, is there any update on this?

The Cobertura 2.7 was a red herring, my issue was caused by spring-boot-starter-logging. Read your dependency tree folks :)

ScGPS commented

I fixed it. Resolution:

    <dependencies>
                  ...
    <dependency>
        <groupId>org.codehaus.mojo</groupId>
        <artifactId>cobertura-maven-plugin</artifactId>
        <version>2.7</version>
        <scope>test</scope>
        <exclusions>
            <exclusion>
                <groupId>ch.qos.logback</groupId>
                <artifactId>logback-classic</artifactId>
            </exclusion>
            <exclusion>
                <artifactId>tools</artifactId>
                <groupId>com.sun</groupId>
            </exclusion>
        </exclusions>
      </dependency>
                  ...
      <dependencies>

        <!-- Add checkstyle , pmd report with xml or text format in site phase -->
        <plugin>
            <groupId>org.apache.maven.plugins</groupId>
            <artifactId>maven-site-plugin</artifactId>
            <version>3.6</version>
            <configuration>
                <reportPlugins>
                  ...
                    <plugin>
                        <groupId>org.codehaus.mojo</groupId>
                        <artifactId>cobertura-maven-plugin</artifactId>
                        <version>2.7</version>
                        <configuration>
                            <formats>
                                <format>html</format>
                                <format>xml</format>
                            </formats>
                        </configuration>
                    </plugin>
                  ...
                </reportPlugins>
            </configuration>
        </plugin>