2
votes

Usual instruction to bridge JUL to SLF4J is adding to pom.xml:

<dependency>
    <groupId>org.slf4j</groupId>
    <artifactId>jul-to-slf4j</artifactId>
    <version>${slf4j.version}</version>
</dependency>

adding to logback.xml:

<contextListener class="ch.qos.logback.classic.jul.LevelChangePropagator"/>

adding to Spring beans:

<bean class="org.slf4j.bridge.SLF4JBridgeHandler" init-method="install"/>

Note that I am don't include:

<bean class="org.slf4j.bridge.SLF4JBridgeHandler"
      init-method="removeHandlersForRootLogger"/>

because that remove Tomcat's/JBoss's etc logging registrations. One reason for containers to register to JUL - to redirect console output to file, but I am not sure if that true.

Ceki (author of Log4j, SLF4J and Logback) doesn't recommend installing SLF4JBridgeHandler multiply times. But he didn't explain why...

What does happen if two webapp register SLF4JBridgeHandler.install() simultaneously in single Tomcat? Do they conflict? Do one steal all log messages from other?

JAXB-RS implementation from com.sun.jersey.* uses JUL logging and I want redirect log statements per application instead of system catalina.log

Another strange recommendation is to put jul-to-slf4j.jar into lib/ dir of container and do not include with application. Why?

REFERENCE ON THE TOPIC:

2

2 Answers

2
votes

I would say that your answer is not correct (at least the last part of it).

(My answer is actually not about the impact of calling SLF4JBridgeHandler.install() multiple times, but covers the scenario you probably want to achieve - usage of jul-to-slf4j within the multiple applications in Tomcat.)

First of all you have to get familiar yourself with Tomcat's classloaders and how they are used. The main idea is that each application has its own classloader therefore could contain it's own jul-to-slf4j.jar.

Next in the line is JULI, Tomcat's logging implementation. Take a closer look at the section which talks about custom LogManager:

The key component there is a custom LogManager implementation, that is aware of different web applications running on Tomcat (and their different class loaders). It supports private per-application logging configurations.

Now let's take a look at the source code of jul-to-slf4j. This simple, yet very powerful class, does nothing out of the box. To enable the bridge you have to attach SLF4JBridgeHandler to the root logger. Ceki gave us two options how to do that:

  1. By calling static method org.slf4j.bridge.SLF4JBridgeHandler.install().
  2. By specifying the bridge handle within logging.properties file.

ad 1) The method process following code:

java.util.logging.LogManager.getLogManager().getLogger("").addHandler(new SLF4JBridgeHandler());

But hey, we are inside Tomcat's universe, we do not want to use that ugly java.util.logging.LogManager manager! We want that enhanced one comming with Tomcat. So forget this method and do not call it from within the Tomcat.

ad 2) You remember that enahnced Tomcat's LogManager allows all applications to have per-application logging configurations right? And remember that jul-to-slf4j could be enabled within logging.properties file right?


To enable/disable jul-to-slf4j bridge within the scope of single application running on Tomcat you have to:

  1. Put jul-to-slf4j.jar into /app/WEB-INF/lib.
  2. Create logging.properties file within /app/WEB-INF/classes with following content: handlers=org.slf4j.bridge.SLF4JBridgeHandler.

I was strugling with similar issue for a few days and this answer covers one part of my research. Hope it could help you and the others to set up the correct environment with SLF4J in charge.

0
votes

I open request to improve documentation: http://jira.qos.ch/browse/SLF4J-351

Response is:

There is one j.u.l.LogManager instance per JVM.
Thus, any manipulation of it impacts the whole JVM.

So in multi-application container environment jul-to-slf4j should be a single instance per JVM and loaded from system classes or you get hard to explain problems.