Use Log4jBridgeHandler to route JUL-based logging into Log4j 2

Since version 2.15.0 `log4j-jul` contains a `Log4jBridgeHandler`,
that forwards JUL to Log4j 2.x and synchronizes the logger levels of
the two frameworks.

This commmit adds support for the `Log4jBridgeHandler` and sets it as
the bridge handler for the Log4j 2.x stack, replacing the existing
JUL to SLF4J bridge that was used previously.

See gh-30003
pull/30890/head
Piotr P. Karwasz 3 years ago committed by Andy Wilkinson
parent 4a030343d3
commit 728e27d193

@ -8,5 +8,4 @@ dependencies {
api("org.apache.logging.log4j:log4j-slf4j-impl")
api("org.apache.logging.log4j:log4j-core")
api("org.apache.logging.log4j:log4j-jul")
api("org.slf4j:jul-to-slf4j")
}

@ -52,6 +52,7 @@ dependencies {
optional("org.apache.httpcomponents.client5:httpclient5")
optional("org.apache.logging.log4j:log4j-api")
optional("org.apache.logging.log4j:log4j-core")
optional("org.apache.logging.log4j:log4j-jul")
optional("org.apache.tomcat.embed:tomcat-embed-core")
optional("org.apache.tomcat.embed:tomcat-embed-jasper")
optional("org.apache.tomcat:tomcat-jdbc")

@ -87,7 +87,7 @@ public abstract class Slf4JLoggingSystem extends AbstractLoggingSystem {
return ClassUtils.isPresent(BRIDGE_HANDLER, getClassLoader());
}
private boolean isJulUsingASingleConsoleHandlerAtMost() {
protected boolean isJulUsingASingleConsoleHandlerAtMost() {
Logger rootLogger = LogManager.getLogManager().getLogger("");
Handler[] handlers = rootLogger.getHandlers();
return handlers.length == 0 || (handlers.length == 1 && handlers[0] instanceof ConsoleHandler);
@ -103,7 +103,7 @@ public abstract class Slf4JLoggingSystem extends AbstractLoggingSystem {
}
}
private void removeDefaultRootHandler() {
protected void removeDefaultRootHandler() {
try {
Logger rootLogger = LogManager.getLogManager().getLogger("");
Handler[] handlers = rootLogger.getHandlers();

@ -25,6 +25,7 @@ import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.logging.Handler;
import java.util.stream.Collectors;
import org.apache.logging.log4j.Level;
@ -42,6 +43,7 @@ import org.apache.logging.log4j.core.config.LoggerConfig;
import org.apache.logging.log4j.core.config.composite.CompositeConfiguration;
import org.apache.logging.log4j.core.filter.AbstractFilter;
import org.apache.logging.log4j.core.util.NameUtil;
import org.apache.logging.log4j.jul.Log4jBridgeHandler;
import org.apache.logging.log4j.message.Message;
import org.springframework.boot.context.properties.bind.BindResult;
@ -75,6 +77,10 @@ public class Log4J2LoggingSystem extends Slf4JLoggingSystem {
private static final String FILE_PROTOCOL = "file";
private static final String LOG4J_BRIDGE_HANDLER = "org.apache.logging.log4j.jul.Log4jBridgeHandler";
private static final String LOG4J_LOG_MANAGER = "org.apache.logging.log4j.jul.LogManager";
private static final LogLevels<Level> LEVELS = new LogLevels<>();
static {
@ -155,10 +161,51 @@ public class Log4J2LoggingSystem extends Slf4JLoggingSystem {
if (isAlreadyInitialized(loggerContext)) {
return;
}
super.beforeInitialize();
if (!configureJdkLoggingBridgeHandler()) {
super.beforeInitialize();
}
loggerContext.getConfiguration().addFilter(FILTER);
}
private boolean configureJdkLoggingBridgeHandler() {
try {
if (isJulUsingASingleConsoleHandlerAtMost() && !isLog4jLogManagerInstalled()) {
if (isLog4jBridgeHandlerAvailable()) {
removeDefaultRootHandler();
installLog4jBridgeHandler();
return true;
}
}
}
catch (Throwable ex) {
// Ignore. No java.util.logging bridge is installed.
}
return false;
}
private boolean isLog4jLogManagerInstalled() {
final String logManagerClassName = java.util.logging.LogManager.getLogManager().getClass().getName();
return LOG4J_LOG_MANAGER.equals(logManagerClassName);
}
private boolean isLog4jBridgeHandlerAvailable() {
return ClassUtils.isPresent(LOG4J_BRIDGE_HANDLER, getClassLoader());
}
private void installLog4jBridgeHandler() {
Log4jBridgeHandler.install(false, null, true);
}
private void removeLog4jBridgeHandler() {
java.util.logging.Logger rootLogger = java.util.logging.LogManager.getLogManager().getLogger("");
for (final Handler handler : rootLogger.getHandlers()) {
if (handler instanceof Log4jBridgeHandler) {
handler.close();
rootLogger.removeHandler(handler);
}
}
}
@Override
public void initialize(LoggingInitializationContext initializationContext, String configLocation, LogFile logFile) {
LoggerContext loggerContext = getLoggerContext();
@ -366,6 +413,9 @@ public class Log4J2LoggingSystem extends Slf4JLoggingSystem {
@Override
public void cleanUp() {
if (isLog4jBridgeHandlerAvailable()) {
removeLog4jBridgeHandler();
}
super.cleanUp();
LoggerContext loggerContext = getLoggerContext();
markAsUninitialized(loggerContext);

@ -25,6 +25,8 @@ import java.util.EnumSet;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.logging.Handler;
import java.util.logging.Level;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.apache.commons.logging.Log;
@ -37,10 +39,10 @@ import org.apache.logging.log4j.core.config.LoggerConfig;
import org.apache.logging.log4j.core.config.Reconfigurable;
import org.apache.logging.log4j.core.config.composite.CompositeConfiguration;
import org.apache.logging.log4j.core.util.ShutdownCallbackRegistry;
import org.apache.logging.log4j.jul.Log4jBridgeHandler;
import org.apache.logging.log4j.util.PropertiesUtil;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Disabled;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
@ -247,7 +249,6 @@ class Log4J2LoggingSystemTests extends AbstractLoggingSystemTests {
}
@Test
@Disabled("Uses Logback unintentionally")
void loggingThatUsesJulIsCaptured(CapturedOutput output) {
this.loggingSystem.beforeInitialize();
this.loggingSystem.initialize(this.initializationContext, null, null);
@ -381,6 +382,22 @@ class Log4J2LoggingSystemTests extends AbstractLoggingSystemTests {
.isEqualTo(new LoggerConfiguration("com.example.test", LogLevel.WARN, LogLevel.WARN));
}
@Test
void log4jLevelsArePropagatedToJul() {
this.loggingSystem.beforeInitialize();
java.util.logging.Logger rootLogger = java.util.logging.Logger.getLogger("");
// check if Log4jBridgeHandler is used
Handler[] handlers = rootLogger.getHandlers();
assertThat(handlers.length).isEqualTo(1);
assertThat(handlers[0]).isInstanceOf(Log4jBridgeHandler.class);
this.loggingSystem.initialize(this.initializationContext, null, null);
java.util.logging.Logger logger = java.util.logging.Logger.getLogger(Log4J2LoggingSystemTests.class.getName());
assertThat(logger.getLevel()).isNull();
this.loggingSystem.setLogLevel(Log4J2LoggingSystemTests.class.getName(), LogLevel.DEBUG);
assertThat(logger.getLevel()).isEqualTo(Level.FINE);
}
@Test
void shutdownHookIsDisabled() {
assertThat(

Loading…
Cancel
Save