Tomcat: java.lang.NoClassDefFoundError: org/apache/commons/logging/LogFactory

Datetime:2016-08-23 03:53:54          Topic: Tomcat           Share

Imagine this: you have laid down the foundation of your brand new Spring-based web application . You have just downloaded and installed a mint Tomcat server to deploy you app on it and…

The web application just won’t start. Tomcat’s logs display an “ Encountered exception org.apache.catalina.LifecycleException ” and the logs point to a ”  java.lang.NoClassDefFoundError: org/apache/commons/logging/LogFactory ” exception. Bummer!

It’s probably…

At this point most of us will look into their web application and make sure their logger has been set up correctly to use Logback or Log4j .

Maybe we have forgotten our slf4j bridging module ?

Then you notice that by adding Commons Logging to Tomcat’s libs/ folder it just seems to get rid of the problem. That’s it: Tomcat forgot to pack a library. Problem solved!

Gotcha!

Well it’s a bit more complicated than that… And it’s not Tomcat’s fault either!

Looking closely at the logs indicates that it is Spring who is requesting the dependency :

Exception thrown by Spring

Caused by: java.lang.NoClassDefFoundError: org/apache/commons/logging/LogFactory
	at org.springframework.context.support.AbstractApplicationContext.<init>(AbstractApplicationContext.java:159)
Causedby: java.lang.NoClassDefFoundError: org/apache/commons/logging/LogFactory
    atorg.springframework.context.support.AbstractApplicationContext.<init>(AbstractApplicationContext.java:159)

Yep, Spring has one mandatory external dependency, and it’s related to logging . Their choice was to rely on Jakarta Commons Logging (JCL) by default. This is very convenient in itself, but makes switching to a different logger a bit trickier than the developers of Spring had wished (though I think they’re being hard on themselves. Come on, give them some credit for their work!).

It amounts to this. First we make sure that we exclude commons-logging from the Spring core:

Excluding commons-logging

XHTML

<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-core</artifactId>
    <exclusions>
        <exclusion>
            <groupId>commons-logging</groupId>
            <artifactId>commons-logging</artifactId>
        </exclusion>
    </exclusions>
</dependency>
<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-core</artifactId>
    <exclusions>
        <exclusion>
            <groupId>commons-logging</groupId>
            <artifactId>commons-logging</artifactId>
        </exclusion>
    </exclusions>
</dependency>

Then we bring in our own logger, for example Logback on SLF4J . We make sure we bridge JCL over to SLF4J , like this:

Adding Logback on SLF4J

XHTML

<dependency>
    <groupId>org.slf4j</groupId>
    <artifactId>slf4j-api</artifactId>
</dependency>
<dependency>
    <groupId>org.slf4j</groupId>
    <artifactId>jcl-over-slf4j</artifactId>
</dependency>
<dependency>
    <groupId>ch.qos.logback</groupId>
    <artifactId>logback-classic</artifactId>
</dependency>
<dependency>
    <groupId>org.slf4j</groupId>
    <artifactId>slf4j-api</artifactId>
</dependency>
<dependency>
    <groupId>org.slf4j</groupId>
    <artifactId>jcl-over-slf4j</artifactId>
</dependency>
<dependency>
    <groupId>ch.qos.logback</groupId>
    <artifactId>logback-classic</artifactId>
</dependency>

(note that I have omitted the <version> tags: in my case they are handled by Spring IO Platform )

With this, Spring’s logging requests will be redirected on SLF4J, which we asked to rely upon Logback.

Just add a logback.xml configuration, and Bob’s your uncle :

A very basic logback.xml configuration

<?xml version="1.0" encoding="UTF-8"?>
<configuration>

  <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
    <layout class="ch.qos.logback.classic.PatternLayout">
      <Pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</Pattern>
    </layout>
  </appender>
  
  <logger name="my.fantastic.application" level="DEBUG"/>

  <root level="info">
    <appender-ref ref="STDOUT" />
  </root>
</configuration>
<?xmlversion="1.0" encoding="UTF-8"?>
<configuration>
 
  <appendername="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
    <layoutclass="ch.qos.logback.classic.PatternLayout">
      <Pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</Pattern>
    </layout>
  </appender>
  
  <loggername="my.fantastic.application" level="DEBUG"/>
 
  <rootlevel="info">
    <appender-refref="STDOUT" />
  </root>
</configuration>

Now you should be able to re-deploy your Spring web application on your Tomcat server without any problems.

Cheers!





About List