JUL-to-Log4j bridge

The JUL-to-Log4j bridge provides components that allow application and library that use java.util.logging.Logger (JUL) to log to the Log4j API instead.

This chapter covers advanced usage scenarios of the JUL-to-Log4j bridge. For the installation procedure and basic configuration see Using JUL-to-Log4j section of our Installation guide.

Configuration

Struggling with the logging API, implementation, and bridge concepts? Click for an introduction.
Logging API

A logging API is an interface your code or your dependencies directly logs against. It is required at compile-time. It is implementation agnostic to ensure that your application can write logs, but is not tied to a specific logging implementation. Log4j API, SLF4J, JUL (Java Logging), JCL (Apache Commons Logging), JPL (Java Platform Logging) and JBoss Logging are major logging APIs.

Logging implementation

A logging implementation is only required at runtime and can be changed without the need to recompile your software. Log4j Core, JUL (Java Logging), Logback are the most well-known logging implementations.

Logging bridge

Logging implementations accept input from a single logging API of their preference; Log4j Core from Log4j API, Logback from SLF4J, etc. A logging bridge is a simple logging implementation of a logging API that forwards all messages to a foreign logging API. Logging bridges allow a logging implementation to accept input from other logging APIs that are not their primary logging API. For instance, log4j-slf4j2-impl bridges SLF4J calls to Log4 API and effectively enables Log4j Core to accept input from SLF4J.

To make things a little bit more tangible, consider the following visualization of a typical Log4j Core installation with bridges for an application:

Visualization of a typical Log4j Core installation with SLF4J, JUL, and JPL bridges
@startuml

frame "Compile time" {
  [Application] --> [Log4j API] : logs to

  [Log4j API] #Cyan

  [SLF4J] #Cyan

  [Library 1] --> [SLF4J] : logs to
  [Application] --> [Library 1] : uses
  [Application] --> [Library 2] : uses
  [Application] --> [Library 3] : uses
}

frame Runtime {

  [Log4j Core] <.. [Log4j API] : is implemented by
  [Log4j Core] <.. (log4j2.xml) : is provided to
  [Log4j Core] #LightGreen

  [JPL-to-Log4j] ..> [Log4j Core] : forwards to
  [JPL-to-Log4j] #Yellow

  [SLF4J-to-Log4j] ..> [Log4j Core] : forwards to
  [SLF4J-to-Log4j] #Yellow

  [JUL-to-Log4j] ..> [Log4j Core] : forwards to
  [JUL-to-Log4j] #Yellow

  frame JRE {
    [JPL] #Cyan
    [JUL] #Cyan
  }

}

[Library 2] --> [JUL] : logs to
[Library 3] --> [JPL] : logs to

[JPL] ..> [JPL-to-Log4j] : is implemented by
[JUL] ..> [JUL-to-Log4j] : is implemented by
[SLF4J] ..> [SLF4J-to-Log4j] : is implemented by

legend top right
  | <#LightGreen> | Logging implementation |
  | <#Yellow> | Logging bridge |
  | <#Cyan> | Logging API |
  | <size:18><U+2192></size> | Compile-time usage |
  | <size:18><U+21E2></size> | Runtime usage |
endlegend

@enduml

The java.util.logging logging API, available since JRE 1.4, shares many similarities with other logging API, such as SLF4J or Log4j API. Similarly to other APIs, it allows users to change the underlying LogManager implementation, but unlike other APIs, it has two big limitations:

  • it is part of JRE, which means that each JVM can contain only one instance of the LogManager class and all the applications of an application server must use the same LogManager implementation,

  • it does not support auto-detection of the logging backend through ServiceLoader or a similar mechanim (see JDK-8262741 ). In order to switch to an alternate LogManager implementation you must be able to set the java.util.logging.manager system property before the first logging call.

To work around the limitations of JUL, the JUL-to-Log4j bridge offers two installation options:

  1. If you are able to modify the java.util.logging.manager system property very early in the JVM startup process, you can replace the default LogManager implementation with a Log4j-specific one. This option gives the best performance. See Using LogManager for details.

  2. If JUL initializes before your application does, which is a typical behavior in application servers, you can still configure JUL to use Log4j as appender. See Using Log4jBridgeHandler for details.

Using LogManager

The best way to install the JUL-to-Log4j bridge on your system is to set the value of the java.util.logging.manager Java system property to

org.apache.logging.log4j.jul.LogManager

This property must be set very early in an application initialization process, e.g. using the -D<property>=<value> command line option of the java executable or by adding:

static {
  if (System.getProperty("java.util.logging.manager") == null) {
    System.setProperty("java.util.logging.manager", "org.apache.logging.log4j.jul.LogManager");
  }
}

at the top of your main class.

Setting this property will replace the default JUL LogManager implementation with a custom implementation that translates JUL Logger method calls into Log4j Logger calls with a minimal overhead.

LogManager-specific features

The use of a java.util.logging.Filter is supported on a per-Logger basis. However, it is recommended to use the standard Filters feature in Log4j instead.

The use of java.util.logging.Handler classes is not supported. Custom handlers should be replaced with the appropriate Log4j Appender.

Using Log4jBridgeHandler

Are you a Spring Boot user?

Spring Boot will automatically configure Log4jBridgeHandler.

If setting the java.util.logging.manager system property is not possible, the JUL-to-Log4j bridge offers an implementation of JUL’s Handler abstract class, which redirects all log events to Log4j Core: org.apache.logging.log4j.jul.Log4jBridgeHandler.

The Log4jBridgeHandler requires Log4j Core as logging implementation and will fail with other Log4j API implementations.

In order to use Log4jBridgeHandler you can either:

  • modify the default JUL configuration file logging.properties to only contain:

    # Set Log4jBridgeHandler as only handler for all JUL loggers
    handlers = org.apache.logging.log4j.jul.Log4jBridgeHandler

    See the JRE documentation for details about the format and location of the logging.properties file.

  • or call the Log4jBridgeHandler.install() method in your code.

Usage of Log4jBridgeHandler introduces a considerably higher overhead that the usage of LogManager, since logging events need to traverse the entire JUL logging pipeline followed by the logging pipeline of the Log4j API implementation.

Consider setting propagateLevels to true to reduce the overhead.

You can tune the behavior of Log4jBridgeHandler by adding the following properties to the logging.properties configuration file, which are also available as parameters to the install() method call:

sysoutDebug

Property name

org.apache.logging.log4j.jul.Log4jBridgeHandler.sysoutDebug

install() parameter

N/A

Type

boolean

Default value

false

If set to true the bridge will print diagnostic information on the standard output.

appendSuffix

Property name

org.apache.logging.log4j.jul.Log4jBridgeHandler.appendSuffix

install() parameter

suffixToAppend

Type

String

Default value

null

Specifies the suffix to append to the name of all JUL loggers, which allows to differentiate JUL log messages from native Log4j API messages.

propagateLevels

Property name

org.apache.logging.log4j.jul.Log4jBridgeHandler.propagateLevels

install() parameter

propagateLevels

Type

boolean

Default value

false

The additional overhead of Log4jBridgeHandler can be especially heavy for disabled log statements. This is why you must ensure that log event filtering of the Log4j implementation and JUL are aligned. You can do it by either:

  • configuring JUL loggers with the same levels as the Log4j loggers,

  • or setting this property to true, which will perform the synchronization automatically.

Common configuration

Independently of the way you install the JUL-to-Log4j bridge, you can finely tune the behavior of the bridge using the following configuration properties. See Configuration properties for more details.

log4j2.julLevelConverter

Env. variable

LOG4J_JUL_LEVEL_CONVERTER

Type

Class<? extends LevelConverter>

Default value

org.apache.logging.log4j.jul.DefaultLevelConverter

Fully qualified name of an alternative org.apache.logging.log4j.jul.LevelConverter implementation.

Default level conversions
Java Level Log4j Level

OFF

OFF

SEVERE

ERROR

WARNING

WARN

INFO

INFO

CONFIG

custom CONFIG level with a numeric value of 450

FINE

DEBUG

FINER

TRACE

FINEST

custom FINEST level with a numeric value of 700

ALL

ALL

log4j2.julLoggerAdapter

Env. variable

LOG4J_JUL_LOGGER_ADAPTER

Type

Class<? extends AbstractLoggerAdapter>

Default value

org.apache.logging.log4j.jul.ApiLoggerAdapter

Fully qualified class name of the org.apache.logging.log4j.jul.AbstractLoggerAdapter implementation to use.

This property allows users to choose between two implementations of the logging bridge:

org.apache.logging.log4j.jul.CoreLoggerAdapter

It allows users to modify the Log4j Core configuration through the JUL Logger interface. It requires the usage of the Log4j Core implementation.

org.apache.logging.log4j.jul.ApiLoggerAdapter

It disables the level mutators in the JUL Logger interface.

Since version 2.24.0 the default value changed to ApiLoggerAdapter. If you need to modify log levels via JUL, you need to select CoreLoggerAdapter explicitly.