Sonny
Sonny

Reputation: 2133

Jetty 9.1+: how to get my webapp logs with log4j/slf4j

I have a webapp running on Jetty 9.3.6 in ${jetty.base} which is set to /opt/mybase/. My webapp uses slf4j and I use log4j as the actual logging framework, so the logging statements in my webapp source looks like this:

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class SillyClass
{
    private static final Logger log = LoggerFactory.getLogger (SillyClass.class.getName ());

    ...

    public static void foo ()
    {
        if (error ())
        {
           log.error ("ERROR");
        }
        else
        {
           log.info ("NOT an error");
        }
   }

   ...

}

And in my gradle build file, I did this:

dependencies {

 ...
 compile 'org.slf4j:slf4j-api:1.7.12'
 compile 'org.slf4j:slf4j-log4j12:1.7.12'
 compile 'log4j:log4j:1.2.17'
 ...
}

This configuration works quite well for another (non-Jetty, non-webapp) Java project which uses the same logging framework; I can use ${project.home}/src/main/resources/log4j.properties to control the output of the log.

Anyway, when I moved to Jetty, I followed the instructions here to the dot; but as soon as I did that, I hit the multiple-bindings error. To correct that, I removed the slf4j references in my gradle build file, but that resulted in the error:

java.lang.LinkageError: loader constraint violation: when resolving method "org.slf4j.impl.StaticLoggerBinder.getLoggerFactory()Lorg/slf4j/ILoggerFactory;" the class loader (instance of org/eclipse/jetty/webapp/WebAppClassLoader) of the current class, org/slf4j/LoggerFactory, and the class loader (instance of org/eclipse/jetty/start/Classpath$Loader) for the method's defining class, org/slf4j/impl/StaticLoggerBinder, have different Class objects for the type org/slf4j/ILoggerFactory used in the signature

I then removed all references to slf4j and log4j (commented out the three lines I show in my build.gradle above), but the error I see is still the same. What am I doing wrong?

My /opt/mybase/resources/log4j.properties file:

log4j.rootLogger=TRACE, FILE
log4j.appender.FILE=org.apache.log4j.FileAppender
log4j.appender.FILE.File=/opt/mybase/logs/jetty.log
log4j.appender.FILE.Append=false
log4j.appender.FILE.layout=org.apache.log4j.PatternLayout
log4j.appender.FILE.layout.ConversionPattern= %d{dd MMM yyyy HH:mm:ss.SSS} %l %m%n

Any help is appreciated.

Upvotes: 1

Views: 3315

Answers (1)

Sonny
Sonny

Reputation: 2133

Well, this is a genuinely embarrassing error on my part. The main issue was not log4j or slf4j or configuration in jetty, I believe. I think that worked just fine. Also, despite multiple bindings, the jetty server actually chooses something shown here in the Jetty logs:

SLF4J: Class path contains multiple SLF4J bindings.
SLF4J: Found binding in [jar:file:/tmp/jetty-0.0.0.0-443-webapp.war-_webapp-any-8458003046853847474.dir/webapp/WEB-INF/lib/slf4j-log4j12- 1.7.12.jar!/org/slf4j/impl/StaticLoggerBinder.class]
SLF4J: Found binding in [jar:file:/opt/mybase/webapp/lib/logging/slf4j-log4j12-  1.6.6.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 [org.slf4j.impl.Log4jLoggerFactory]

Moreover, if a dependency has logging built in, you are out of luck, depending on how well maintained things are, I think you will have a clash anyway. At least, I know of no way to prevent this in that case.

Well, the real issue was that the webapp did not have its own log4j.properties (if you are using gradle, this would be in $(projectHome)/src/main/resources/log4j.properties). I added that to my project and my logging makes a lot more sense (i.e., I see it where it should be, in /opt/mybase/logs/my-silly-webapp-log.txt).

Lastly, the crash I reported is unrelated (in the comments, on Dec 29) and has to do with Jetty 9.3.6 bug which is as yet unresolved, it appears, despite being called resolved in the Jetty bug db for 9.3.6. What threw me off was that the logs appeared immediately after the multiple_bindings error I reported originally. They are not related, it appears. I cannot track down the exact bug in Jetty bug DB as I write this, but the last I checked that bug (about a week ago), the issue was when the Jetty log4j.properties (in /opt/mybase/resources/log4j.properties) has set rootLogger to DEBUG (got that from the comments on that bug). I am able to reproduce the error (the websocket app itself just works fine, despite the Jetty log related crash) when my rootLogger in Jetty log4j.properties is set to DEBUG. By moving the rootLogger for Jetty log4j.properties to INFO or lower, I am able to confirm that the problem goes away. I will raise it in Jetty's bug DB.

Hope this helps someone.

Upvotes: 1

Related Questions