[JAVA] How jul-to-slf4j works

What is jul-to-slf4j?

how to use

SLF4JBridgeHandler.install();

Just hit it.

Example of use

In Spring Boot

[Reference URL](https://github.com/spring-projects/spring-boot/blob/master/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/logging/Slf4JLoggingSystem .java)

Slf4jLoggingSystem.java


...
	private void configureJdkLoggingBridgeHandler() {
		try {
			if (isBridgeHandlerAvailable()) {
				removeJdkLoggingBridgeHandler();
				SLF4JBridgeHandler.install();
			}
		}
		catch (Throwable ex) {
			// Ignore. No java.util.logging bridge is installed.
		}
	}
...

In the Play framework

Reference URL

LogbackLoggerConfigurator.scala


...
  def configure(properties: Map[String, String], config: Option[URL]): Unit = {
    // Touching LoggerContext is not thread-safe, and so if you run several
    // application tests at the same time (spec2 / scalatest with "new WithApplication()")
    // then you will see NullPointerException as the array list loggerContextListenerList
    // is accessed concurrently from several different threads.
    //
    // The workaround is to use a synchronized block around a singleton
    // instance -- in this case, we use the StaticLoggerBinder's loggerFactory.
    loggerFactory.synchronized {
      // Redirect JUL -> SL4FJ

      // Remove existing handlers from JUL
      SLF4JBridgeHandler.removeHandlersForRootLogger()

      // Configure logback
      val ctx = loggerFactory.asInstanceOf[LoggerContext]

      // Set a level change propagator to minimize the overhead of JUL
      //
      // Please note that translating a java.util.logging event into SLF4J incurs the
      // cost of constructing LogRecord instance regardless of whether the SLF4J logger
      // is disabled for the given level. Consequently, j.u.l. to SLF4J translation can
      // seriously increase the cost of disabled logging statements (60 fold or 6000%
      // increase) and measurably impact the performance of enabled log statements
      // (20% overall increase). Please note that as of logback-version 0.9.25,
      // it is possible to completely eliminate the 60 fold translation overhead for
      // disabled log statements with the help of LevelChangePropagator.
      //
      // https://www.slf4j.org/api/org/slf4j/bridge/SLF4JBridgeHandler.html
      // https://logback.qos.ch/manual/configuration.html#LevelChangePropagator
      val levelChangePropagator = new LevelChangePropagator()
      levelChangePropagator.setContext(ctx)
      levelChangePropagator.setResetJUL(true)
      ctx.addListener(levelChangePropagator)
      SLF4JBridgeHandler.install()

      ctx.reset()

      // Ensure that play.Logger and play.api.Logger are ignored when detecting file name and line number for
      // logging
      val frameworkPackages = ctx.getFrameworkPackages
      frameworkPackages.add(classOf[play.Logger].getName)
      frameworkPackages.add(classOf[play.api.Logger].getName)

      properties.foreach { case (k, v) => ctx.putProperty(k, v) }

      config match {
        case Some(url) =>
          val initializer = new ContextInitializer(ctx)
          initializer.configureByResource(url)
        case None =>
          System.err.println("Could not detect a logback configuration file, not configuring logback")
      }

      StatusPrinter.printIfErrorsOccured(ctx)

    }
  }
...

Ugh, scala ...

So how do you guys do it?

Implementation of jul-to-slf4j

It looks like this.

SLF4JBridgeHandler.java


    public static void install() {
        LogManager.getLogManager().getLogger("").addHandler(new SLF4JBridgeHandler());
    }

...

    public void publish(LogRecord record) {
        // Silently ignore null records.
        if (record == null) {
            return;
        }

        Logger slf4jLogger = getSLF4JLogger(record);
        // this is a check to avoid calling the underlying logging system
        // with a null message. While it is legitimate to invoke j.u.l. with
        // a null message, other logging frameworks do not support this.
        // see also http://jira.qos.ch/browse/SLF4J-99
        if (record.getMessage() == null) {
            record.setMessage("");
        }
        if (slf4jLogger instanceof LocationAwareLogger) {
            callLocationAwareLogger((LocationAwareLogger) slf4jLogger, record);
        } else {
            callPlainSLF4JLogger(slf4jLogger, record);
        }
    }

From the conclusion, if you follow the steps of what is being done

Let's take a closer look

Who is java.util.logging.Handler by JavaDoc When,

The Handler object receives log messages from Logger and exports them. For example, this object writes to the console or file, sends it to the network log service, performs transfers to OS logs, and so on.

There is. In other words, Logger is an interface seen from the application, and it seems that this Handler actually outputs the physical log. I somehow understood that it was like an appender in logback.

And what is LogManager? As far as I read the ʻimport` statement, it is a class of jul.

SLF4JBridgeHandler.java


import java.util.logging.LogManager;

Also, you can see that the empty string is specified as the logger name, such as getLogger ("") . What is this? It's not that I can't imagine it because it doesn't have a name, but it seems that you can get a root logger by specifying an empty string logger name.

SLF4JBridgeHandler.java


    private static java.util.logging.Logger getRootLogger() {
        return LogManager.getLogManager().getLogger("");
    }

As a premise here, all loggers have a descendant relationship with jul as well as other logging API examples, and always inherit the route. This means that adding a SLF4JBridgeHandler to the root logger on jul means that all logging via jul will be delegated to slf4j.

reference

Recommended Posts

How jul-to-slf4j works
[Java] How Spring DI works
How the JVM JIT compiler works
How memory works in object-oriented languages
How Dispatcher servlet works in Spring MVC
Java Performance Chapter 4 How the JIT Compiler Works
Deep dive into how HashMap works in Java
How Docker works ~ Implement the container in 60 lines