Jul to Slf4J Bridge

JUL to SLF4J Bridge

You need to call SLF4JBridgeHandler.install(). You also need to enable all log levels at the root logger (reason in excerpt below) in java.util.logging and remove the default console appender.

This handler will redirect jul logging to SLF4J. However, only logs
enabled in j.u.l. will be redirected. For example, if a log statement
invoking a j.u.l. logger disabled that statement, by definition, will
not reach any SLF4JBridgeHandler instance and cannot be redirected.

The whole process can be accomplished like so

import java.util.logging.Logger;
import org.slf4j.bridge.SLF4JBridgeHandler;

SLF4JBridgeHandler.removeHandlersForRootLogger();
SLF4JBridgeHandler.install();
Logger.getLogger("").setLevel(Level.FINEST); // Root logger, for example.

You can set the level to something higher than finest for performance reasons, but you won't be able to turn those logs on without enabling them in java.util.logging first (for the reason mentioned above in the excerpt).

How to run jul-to-slf4j bridge once per JVM?

There are various ways to make some code run at the beginning of a test suite.

Here are 4 (I'm sure there are more):

  1. JUnit via RunWith Suite with Suite.SuiteClasses and BeforeClass (adapted from examples in SuiteTest):

    @RunWith(Suite.class)
    @SuiteClasses({FirstTest.class, SecondTest.class/*, ...*/, LastTest.class})
    public static class AllWithSLF4JBridgeHandler {
    @BeforeClass
    public static void registerRootLoggerHandlers() {
    SLF4JBridgeHandler.removeHandlersForRootLogger();
    SLF4JBridgeHandler.install();
    }
    }
  2. TestNG with BeforeSuite:

    /**
    * Base class for each test class (i.e. every test class should extend this class).
    */
    public abstract class BaseTest {
    @BeforeSuite
    public void registerRootLoggerHandlers() {
    SLF4JBridgeHandler.removeHandlersForRootLogger();
    SLF4JBridgeHandler.install();
    }
    }
  3. TestNG with Guice:

    /**
    * Test module. Each test class should be annotated with `@Guice(TestModule.class)`.
    */
    public class TestModule implements Module {
    @Override
    public void configure(Binder binder) {
    SLF4JBridgeHandler.removeHandlersForRootLogger();
    SLF4JBridgeHandler.install();
    }
    }
  4. Static initialization blocks (test-framework independent):

    /**
    * Base class for each test class (i.e. every test class should extend this class).
    */
    public abstract class BaseTest {
    static {
    SLF4JBridgeHandler.removeHandlersForRootLogger();
    SLF4JBridgeHandler.install();
    }
    }

I'm not sure how all of these methods work with Surefire's parallel mode. Methods 1 and 2 may not work there but I believe methods 3 and 4 should.


Another option would be to not use the programmatic installation of the SLF4JBridgeHandler but to use a java.util.logging.config file or class (see LogManager):

  1. "java.util.logging.config.file":

    logging.properties file:

    // register SLF4JBridgeHandler as handler for the j.u.l. root logger
    handlers = org.slf4j.bridge.SLF4JBridgeHandler

    System property assignment:

    java -Djava.util.logging.config.file=/path/to/logging.properties ...

    This works well if you know the path to your logging file beforehand.

  2. "java.util.logging.config.class":

    Using a file may not be a good option if you're deploying a WAR and don't know where the file will be, etc. so alternatively you can create a logging config class:

    public class SLF4JBridgeHandlerInitializer {
    public SLF4JBridgeHandlerInitializer() throws IOException {
    String loggingConfigurationString = "handlers = " + SLF4JBridgeHandler.class.getName();
    InputStream inputStream = new ByteArrayInputStream(loggingConfigurationString.getBytes());
    LogManager.getLogManager().readConfiguration(inputStream);
    }
    }

    System property assignment:

    java -Djava.util.logging.config.class=package.SLF4JBridgeHandlerInitializer ...

    I've done this before and it has worked well for me (SLF4JBridgeHandler.Initializer by mfulton26 · Pull Request #57 · qos-ch/slf4j).

These final two options should initialize each JVM instance as long as the appropriate system property is set.

What is impact of using jul-to-slf4j several times in Java EE / Spring container?

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.

Java logging: slf4j over jul and log4j2

The reason why Log4j 2 is still using its own configuration is because you are only using SLF4J's log4j-over-slf4j which is for Log4j 1 (despite not being that clearly documented). For Log4j 2 to SLF4J, you will have to add Log4j 2 to SLF4J Adapter (dependency information) as well.

For jul-to-slf4j you have to follow the steps described in the SLF4JBridgeHandler documentation:

  • Either specify it in the logging.properties file of your JRE. Though if you have no control over the environment your application is running, this is likely not an option.
  • Or programmatically install it:
    static {
    // Verify that it is not installed yet; other dependency might have already
    // installed it which would cause duplicate handler
    // Unfortunately SLF4J provides no built-in method for this procedure, so
    // race condition between different classes could happen
    if (!SLF4JBridgeHandler.isInstalled()) {
    // Remove default handler logging to System.err
    SLF4JBridgeHandler.removeHandlersForRootLogger();
    // Install the SLF4J handler
    SLF4JBridgeHandler.install();
    }
    }

Additionally you should configure the LevelChangePropagator for Logback. Otherwise, because the SLF4J class is only a handler, lowering the SLF4J log level (e.g. for debugging) would have no effect on java.util.logging.

Note however, that redirecting java.util.logging in general is a pain, especially if your project has multiple entry points. You will have to perform the SLF4J bridge initialization check in every one of these entry points.

As a side note: The situation for the java.util.logging to Log4j 2 bridge (Log4j JDK Logging Adapter) it is not much better. That library instead implements LogManager which therefore integrates more tightly into java.util.logging, but has the disadvantage that it has to be specified using a System property (or the logging.properties file) and must be specified before java.util.logging.LogManager is loaded (which might not be in your control), afterwards a custom LogManager implementation cannot be specified anymore.

There is also a JUL Handler class called Log4jBridgeHandler, but that only exists on the not yet released 3.0.0 branch and has not been backported yet, see backport pull request.

NPE when using JUL with SLF4J at trace level

This is bug FilterChainContext.toString crashes with NullPointerException #1959 in Grizzly.

There are two overloads of String.valueOf, with Object and char[]. (Java 8)
Because of the dangerous unbound generic in getMessage both overloads would seem applicable, however the char[] overload is chosen, probably because it is more specific.

[snip]

However String.valueOf(char[]) will fail if the result of getMessage is null. I would assume a ClassCastException could happen in other cases. This can be fixed by explicitly casting the result to object or ensuring that the correct generics are infered.

You need to upgrade to a newer version of Grizzly (2.4.3).

A regression bug was filed in Nov 10, 2020 under: FilterChainContext.toString causes NullPointerException #2110 which reports fixed in 4.0.0-M2-RELEASE.

Commit is listed as: https://github.com/eclipse-ee4j/grizzly/commit/d7a96f15083a55c38f63746007f664f1ede9f474

Send/redirect/route java.util.logging.Logger (JUL) to Logback using SLF4J?

It's very easy and not a performance issue anymore.

There are two ways documented in the SLF4J manual. There are also precise examples in the Javadocs

Add jul-to-slf4j.jar to your classpath. Or through maven dependency:

<dependency>
<groupId>org.slf4j</groupId>
<artifactId>jul-to-slf4j</artifactId>
<version>1.7.0</version>
</dependency>

If you don't have logging.properties (for java.util.logging), add this to your bootstrap code:

SLF4JBridgeHandler.removeHandlersForRootLogger();
SLF4JBridgeHandler.install();

If you have logging.properties (and want to keep it), add this to it:

handlers = org.slf4j.bridge.SLF4JBridgeHandler

In order to avoid performance penalty, add this contextListener to logback.xml (as of logback version 0.9.25):

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

<contextListener class="ch.qos.logback.classic.jul.LevelChangePropagator">
<!-- reset all previous level configurations of all j.u.l. loggers -->
<resetJUL>true</resetJUL>
</contextListener>

...

</configuration>


Related Topics



Leave a reply



Submit