Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

how to properly configure gradle build to avoid including log4j and slf4j from the resulting jar?

I'm having an issue with my spring-boot application: I'm able to run it within Eclipse, but unable to run the jar file (built with gradle). I run the following command to build my project:

gradle buid

The build is successful:

 gradle build
:compileJava UP-TO-DATE
:processResources UP-TO-DATE
:classes UP-TO-DATE
:findMainClass
:jar
:bootRepackage
:assemble
:compileTestJava UP-TO-DATE
:processTestResources UP-TO-DATE
:testClasses UP-TO-DATE
:test UP-TO-DATE
:check UP-TO-DATE
:build

BUILD SUCCESSFUL

Total time: 1.214 secs

This is a fragment of the resulting jar file:

jar -tvf build/libs/springboot-receiver-api-0.1.0.jar | grep log
  9988 Mon Apr 04 20:37:48 CDT 2016 BOOT-INF/lib/slf4j-log4j12-1.7.21.jar
  2308 Wed Sep 21 07:11:50 CDT 2016 BOOT-INF/lib/spring-boot-starter-logging-1.4.1.RELEASE.jar
 66802 Thu May 28 09:49:34 CDT 2015 BOOT-INF/lib/jboss-logging-3.3.0.Final.jar
304075 Tue Mar 29 22:24:50 CDT 2016 BOOT-INF/lib/logback-classic-1.1.7.jar
 23646 Mon Apr 04 20:39:02 CDT 2016 BOOT-INF/lib/log4j-over-slf4j-1.7.21.jar
470782 Tue Mar 29 22:23:42 CDT 2016 BOOT-INF/lib/logback-core-1.1.7.jar
489884 Sun May 06 13:24:48 CDT 2012 BOOT-INF/lib/log4j-1.2.17.jar

when I attempt to run this jar file, I get this error:

  java -jar build/libs/springboot-receiver-api-0.1.0.jar
        SLF4J: Class path contains multiple SLF4J bindings.
        SLF4J: Found binding in [jar:file:/Users/eugene/.Trash/springboot-receiverapi/build/libs/springboot-receiver-api-0.1.0.jar!/BOOT-INF/lib/slf4j-log4j12-1.7.21.jar!/org/slf4j/impl/StaticLoggerBinder.class]
        SLF4J: Found binding in [jar:file:/Users/eugene/.Trash/springboot-receiverapi/build/libs/springboot-receiver-api-0.1.0.jar!/BOOT-INF/lib/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: Detected both log4j-over-slf4j.jar AND bound slf4j-log4j12.jar on the class path, preempting StackOverflowError.
    Exception in thread "main" java.lang.reflect.InvocationTargetException
        at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
        at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
        at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
        at java.lang.reflect.Method.invoke(Method.java:498)
        at org.springframework.boot.loader.MainMethodRunner.run(MainMethodRunner.java:48)
        at org.springframework.boot.loader.Launcher.launch(Launcher.java:87)
        at org.springframework.boot.loader.Launcher.launch(Launcher.java:50)
        at org.springframework.boot.loader.JarLauncher.main(JarLauncher.java:58)
    Caused by: java.lang.ExceptionInInitializerError
        at org.slf4j.impl.StaticLoggerBinder.<init>(StaticLoggerBinder.java:72)
Caused by: java.lang.IllegalStateException: Detected both log4j-over-slf4j.jar AND bound slf4j-log4j12.jar on the class path, preempting StackOverflowError. See also http://www.slf4j.org/codes.html#log4jDelegationLoop for more details.
    at org.slf4j.impl.Log4jLoggerFactory.<clinit>(Log4jLoggerFactory.java:54)
    ... 19 more

Here is my build.gradle file:

apply plugin: 'java'
apply plugin: 'maven'

group = 'uptake'
version = '0.0.1-snapshot'

description = """Spring-Boot-ReceiverAPI"""

sourceCompatibility = 1.8
targetCompatibility = 1.8
tasks.withType(JavaCompile) {
    options.encoding = 'UTF-8'
}



repositories {

     maven { url "http://repo.maven.apache.org/maven2" }
}
dependencies {
    compile(group: 'org.springframework.boot', name: 'spring-boot-starter-web', version:'1.2.3.RELEASE') {
exclude(module: 'log4j-over-slf4j')
    }
    compile group: 'org.springframework.boot', name: 'spring-boot-starter-data-jpa', version:'1.2.3.RELEASE'
    compile group: 'org.postgresql', name: 'postgresql', version:'9.3-1102-jdbc41'
    compile group: 'com.fasterxml.jackson.datatype', name: 'jackson-datatype-hibernate4', version:'2.8.1'
    compile group: 'com.fasterxml.jackson.core', name: 'jackson-core', version:'2.8.3'
    compile group: 'org.apache.kafka', name: 'kafka_2.10', version:'0.9.0.0'
    compile group: 'org.springframework.integration', name: 'spring-integration-kafka', version:'2.0.1.RELEASE'
    compile group: 'org.apache.zookeeper', name: 'zookeeper', version:'3.4.5'
    compile(group: 'commons-beanutils', name: 'commons-beanutils', version:'1.9.2') {
exclude(module: 'commons-logging')
    }
    compile group: 'org.json', name: 'json', version:'20090211'
    compile group: 'org.codehaus.jackson', name: 'jackson-mapper-asl', version:'1.5.0'
    compile(group: 'org.springframework', name: 'spring-core', version:'4.3.3.RELEASE') {
exclude(module: 'commons-logging')
    }
    compile group: 'com.spotify', name: 'docker-maven-plugin', version:'0.4.13'
}
configurations.all {
    exclude group: "org.slf4j", module: "slf4j-log4j12"
    exclude group: "log4j", module: "log4j"
}

What can I do, to avoid this clash of logging libraries, and to be able to run my jar file stand-alone?

like image 312
Eugene Goldberg Avatar asked Dec 24 '22 00:12

Eugene Goldberg


2 Answers

Couple things, it seems to me that your build.gradle should declare :

  • spring boot gradle plugin
  • apply spring boot plugin
  • let spring boot manage most of your dependency versions

This script works for me:

apply plugin: 'java'
apply plugin: 'maven'
apply plugin: 'spring-boot'

group = 'uptake'
version = '0.0.1-snapshot'

description = """Spring-Boot-ReceiverAPI"""

sourceCompatibility = 1.8
targetCompatibility = 1.8
tasks.withType(JavaCompile) {
    options.encoding = 'UTF-8'
}

buildscript {
    repositories {
        mavenCentral()
    }
    dependencies {
        classpath 'org.springframework.boot:spring-boot-gradle-plugin:1.4.1.RELEASE'
    }
}

repositories {
     maven { url "http://repo.maven.apache.org/maven2" }
}

dependencies {
    compile 'org.springframework.boot:spring-boot-starter'
    compile 'org.springframework.boot:spring-boot-starter-logging'
    compile 'org.springframework.boot:spring-boot-starter-web'
    compile 'org.springframework.boot:spring-boot-starter-data-jpa'
    compile 'org.postgresql:postgresql'
    compile 'com.fasterxml.jackson.datatype:jackson-datatype-hibernate4'
    compile 'com.fasterxml.jackson.core:jackson-core'
    compile 'org.apache.kafka:kafka_2.10:0.9.0.0'
    compile 'org.springframework.integration:spring-integration-kafka:2.0.1.RELEASE'
    compile 'org.apache.zookeeper:zookeeper:3.4.5'
    compile 'commons-beanutils:commons-beanutils'
    compile 'org.json:json'
    compile 'org.codehaus.jackson:jackson-mapper-asl:1.5.0'
    compile 'org.springframework:spring-core'
    compile 'com.spotify:docker-maven-plugin:0.4.13'
}

configurations.all {
    exclude module: 'slf4j-log4j12'
    exclude module: 'jms'
    exclude module: 'jmxtools'
    exclude module: 'jmxri'
}

Now, if you check both modules with gradle dependencyInsight, only log4j-over-slf4j will be found:

$ gradle dependencyInsight --dependency slf4j-log4j12
$ No dependencies matching given input were found...

$ gradle dependencyInsight --dependency log4j-over-slf4j
:dependencyInsight
org.slf4j:log4j-over-slf4j:1.7.21 (selected by rule)
\--- org.springframework.boot:spring-boot-starter-logging:1.4.1.RELEASE
     +--- compile
     \--- org.springframework.boot:spring-boot-starter:1.4.1.RELEASE
          +--- compile
          +--- org.springframework.boot:spring-boot-starter-web:1.4.1.RELEASE
          |    \--- compile
          +--- org.springframework.boot:spring-boot-starter-data-jpa:1.4.1.RELEASE
          |    \--- compile
          +--- org.springframework.boot:spring-boot-starter-aop:1.4.1.RELEASE
          |    \--- org.springframework.boot:spring-boot-starter-data-jpa:1.4.1.RELEASE (*)
          \--- org.springframework.boot:spring-boot-starter-jdbc:1.4.1.RELEASE
               \--- org.springframework.boot:spring-boot-starter-data-jpa:1.4.1.RELEASE (*)
like image 80
alexbt Avatar answered Jan 04 '23 22:01

alexbt


I solved this problem updating kafka to a new version.

Some old versions of Kafka had dependecies with jmxtools and jmxri (from log4j < 1.2.16). And this dependecies doesn't have available licences in maven and gradle.

You can resolve this doing one of this three options:

  • Setting kafka in a major version(2.12 works well!)
  • Setting log4j in a version upper to 1.2.16
  • Excluding jmxtools and jmxri

Like I previously said, I resolved this setting the kafka version in my build.gradle:

compile group: 'org.apache.kafka', name: 'kafka_2.12', version: '2.4.0'
like image 41
Yani Morales Avatar answered Jan 04 '23 23:01

Yani Morales