Merge pull request #30003 from ppkarwasz
* gh-30003: Polish "Use Log4jBridgeHandler to route JUL-based logging into Log4j 2" Use Log4jBridgeHandler to route JUL-based logging into Log4j 2 Closes gh-30003
This commit is contained in:
@@ -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")
|
||||
|
||||
@@ -1,119 +0,0 @@
|
||||
/*
|
||||
* Copyright 2012-2019 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.boot.logging;
|
||||
|
||||
import java.util.logging.ConsoleHandler;
|
||||
import java.util.logging.Handler;
|
||||
import java.util.logging.LogManager;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
import org.slf4j.bridge.SLF4JBridgeHandler;
|
||||
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.ClassUtils;
|
||||
|
||||
/**
|
||||
* Abstract base class for {@link LoggingSystem} implementations that utilize SLF4J.
|
||||
*
|
||||
* @author Andy Wilkinson
|
||||
* @since 1.2.0
|
||||
*/
|
||||
public abstract class Slf4JLoggingSystem extends AbstractLoggingSystem {
|
||||
|
||||
private static final String BRIDGE_HANDLER = "org.slf4j.bridge.SLF4JBridgeHandler";
|
||||
|
||||
public Slf4JLoggingSystem(ClassLoader classLoader) {
|
||||
super(classLoader);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void beforeInitialize() {
|
||||
super.beforeInitialize();
|
||||
configureJdkLoggingBridgeHandler();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void cleanUp() {
|
||||
if (isBridgeHandlerAvailable()) {
|
||||
removeJdkLoggingBridgeHandler();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void loadConfiguration(LoggingInitializationContext initializationContext, String location,
|
||||
LogFile logFile) {
|
||||
Assert.notNull(location, "Location must not be null");
|
||||
if (initializationContext != null) {
|
||||
applySystemProperties(initializationContext.getEnvironment(), logFile);
|
||||
}
|
||||
}
|
||||
|
||||
private void configureJdkLoggingBridgeHandler() {
|
||||
try {
|
||||
if (isBridgeJulIntoSlf4j()) {
|
||||
removeJdkLoggingBridgeHandler();
|
||||
SLF4JBridgeHandler.install();
|
||||
}
|
||||
}
|
||||
catch (Throwable ex) {
|
||||
// Ignore. No java.util.logging bridge is installed.
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Return whether bridging JUL into SLF4J or not.
|
||||
* @return whether bridging JUL into SLF4J or not
|
||||
* @since 2.0.4
|
||||
*/
|
||||
protected final boolean isBridgeJulIntoSlf4j() {
|
||||
return isBridgeHandlerAvailable() && isJulUsingASingleConsoleHandlerAtMost();
|
||||
}
|
||||
|
||||
protected final boolean isBridgeHandlerAvailable() {
|
||||
return ClassUtils.isPresent(BRIDGE_HANDLER, getClassLoader());
|
||||
}
|
||||
|
||||
private boolean isJulUsingASingleConsoleHandlerAtMost() {
|
||||
Logger rootLogger = LogManager.getLogManager().getLogger("");
|
||||
Handler[] handlers = rootLogger.getHandlers();
|
||||
return handlers.length == 0 || (handlers.length == 1 && handlers[0] instanceof ConsoleHandler);
|
||||
}
|
||||
|
||||
private void removeJdkLoggingBridgeHandler() {
|
||||
try {
|
||||
removeDefaultRootHandler();
|
||||
SLF4JBridgeHandler.uninstall();
|
||||
}
|
||||
catch (Throwable ex) {
|
||||
// Ignore and continue
|
||||
}
|
||||
}
|
||||
|
||||
private void removeDefaultRootHandler() {
|
||||
try {
|
||||
Logger rootLogger = LogManager.getLogManager().getLogger("");
|
||||
Handler[] handlers = rootLogger.getHandlers();
|
||||
if (handlers.length == 1 && handlers[0] instanceof ConsoleHandler) {
|
||||
rootLogger.removeHandler(handlers[0]);
|
||||
}
|
||||
}
|
||||
catch (Throwable ex) {
|
||||
// Ignore and continue
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -25,6 +25,8 @@ import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.logging.ConsoleHandler;
|
||||
import java.util.logging.Handler;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import org.apache.logging.log4j.Level;
|
||||
@@ -42,18 +44,19 @@ 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;
|
||||
import org.springframework.boot.context.properties.bind.Bindable;
|
||||
import org.springframework.boot.context.properties.bind.Binder;
|
||||
import org.springframework.boot.logging.AbstractLoggingSystem;
|
||||
import org.springframework.boot.logging.LogFile;
|
||||
import org.springframework.boot.logging.LogLevel;
|
||||
import org.springframework.boot.logging.LoggerConfiguration;
|
||||
import org.springframework.boot.logging.LoggingInitializationContext;
|
||||
import org.springframework.boot.logging.LoggingSystem;
|
||||
import org.springframework.boot.logging.LoggingSystemFactory;
|
||||
import org.springframework.boot.logging.Slf4JLoggingSystem;
|
||||
import org.springframework.core.Ordered;
|
||||
import org.springframework.core.annotation.Order;
|
||||
import org.springframework.util.Assert;
|
||||
@@ -71,10 +74,14 @@ import org.springframework.util.StringUtils;
|
||||
* @author Ben Hale
|
||||
* @since 1.2.0
|
||||
*/
|
||||
public class Log4J2LoggingSystem extends Slf4JLoggingSystem {
|
||||
public class Log4J2LoggingSystem extends AbstractLoggingSystem {
|
||||
|
||||
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 +162,66 @@ 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()
|
||||
&& isLog4jBridgeHandlerAvailable()) {
|
||||
removeDefaultRootHandler();
|
||||
Log4jBridgeHandler.install(false, null, true);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
catch (Throwable ex) {
|
||||
// Ignore. No java.util.logging bridge is installed.
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private boolean isJulUsingASingleConsoleHandlerAtMost() {
|
||||
java.util.logging.Logger rootLogger = java.util.logging.LogManager.getLogManager().getLogger("");
|
||||
Handler[] handlers = rootLogger.getHandlers();
|
||||
return handlers.length == 0 || (handlers.length == 1 && handlers[0] instanceof ConsoleHandler);
|
||||
}
|
||||
|
||||
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 removeLog4jBridgeHandler() {
|
||||
removeDefaultRootHandler();
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void removeDefaultRootHandler() {
|
||||
try {
|
||||
java.util.logging.Logger rootLogger = java.util.logging.LogManager.getLogManager().getLogger("");
|
||||
Handler[] handlers = rootLogger.getHandlers();
|
||||
if (handlers.length == 1 && handlers[0] instanceof ConsoleHandler) {
|
||||
rootLogger.removeHandler(handlers[0]);
|
||||
}
|
||||
}
|
||||
catch (Throwable ex) {
|
||||
// Ignore and continue
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void initialize(LoggingInitializationContext initializationContext, String configLocation, LogFile logFile) {
|
||||
LoggerContext loggerContext = getLoggerContext();
|
||||
@@ -189,7 +252,9 @@ public class Log4J2LoggingSystem extends Slf4JLoggingSystem {
|
||||
@Override
|
||||
protected void loadConfiguration(LoggingInitializationContext initializationContext, String location,
|
||||
LogFile logFile) {
|
||||
super.loadConfiguration(initializationContext, location, logFile);
|
||||
if (initializationContext != null) {
|
||||
applySystemProperties(initializationContext.getEnvironment(), logFile);
|
||||
}
|
||||
loadConfiguration(location, logFile, getOverrides(initializationContext));
|
||||
}
|
||||
|
||||
@@ -366,6 +431,9 @@ public class Log4J2LoggingSystem extends Slf4JLoggingSystem {
|
||||
|
||||
@Override
|
||||
public void cleanUp() {
|
||||
if (isLog4jBridgeHandlerAvailable()) {
|
||||
removeLog4jBridgeHandler();
|
||||
}
|
||||
super.cleanUp();
|
||||
LoggerContext loggerContext = getLoggerContext();
|
||||
markAsUninitialized(loggerContext);
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2012-2021 the original author or authors.
|
||||
* Copyright 2012-2022 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -22,6 +22,7 @@ import java.security.ProtectionDomain;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import java.util.logging.ConsoleHandler;
|
||||
import java.util.logging.Handler;
|
||||
import java.util.logging.LogManager;
|
||||
|
||||
@@ -42,6 +43,7 @@ import org.slf4j.Marker;
|
||||
import org.slf4j.bridge.SLF4JBridgeHandler;
|
||||
import org.slf4j.impl.StaticLoggerBinder;
|
||||
|
||||
import org.springframework.boot.logging.AbstractLoggingSystem;
|
||||
import org.springframework.boot.logging.LogFile;
|
||||
import org.springframework.boot.logging.LogLevel;
|
||||
import org.springframework.boot.logging.LoggerConfiguration;
|
||||
@@ -49,7 +51,6 @@ import org.springframework.boot.logging.LoggingInitializationContext;
|
||||
import org.springframework.boot.logging.LoggingSystem;
|
||||
import org.springframework.boot.logging.LoggingSystemFactory;
|
||||
import org.springframework.boot.logging.LoggingSystemProperties;
|
||||
import org.springframework.boot.logging.Slf4JLoggingSystem;
|
||||
import org.springframework.core.Ordered;
|
||||
import org.springframework.core.SpringProperties;
|
||||
import org.springframework.core.annotation.Order;
|
||||
@@ -69,7 +70,9 @@ import org.springframework.util.StringUtils;
|
||||
* @author Ben Hale
|
||||
* @since 1.0.0
|
||||
*/
|
||||
public class LogbackLoggingSystem extends Slf4JLoggingSystem {
|
||||
public class LogbackLoggingSystem extends AbstractLoggingSystem {
|
||||
|
||||
private static final String BRIDGE_HANDLER = "org.slf4j.bridge.SLF4JBridgeHandler";
|
||||
|
||||
// Static final field to facilitate code removal by Graal
|
||||
private static final boolean XML_ENABLED = !SpringProperties.getFlag("spring.xml.ignore");
|
||||
@@ -120,9 +123,59 @@ public class LogbackLoggingSystem extends Slf4JLoggingSystem {
|
||||
return;
|
||||
}
|
||||
super.beforeInitialize();
|
||||
configureJdkLoggingBridgeHandler();
|
||||
loggerContext.getTurboFilterList().add(FILTER);
|
||||
}
|
||||
|
||||
private void configureJdkLoggingBridgeHandler() {
|
||||
try {
|
||||
if (isBridgeJulIntoSlf4j()) {
|
||||
removeJdkLoggingBridgeHandler();
|
||||
SLF4JBridgeHandler.install();
|
||||
}
|
||||
}
|
||||
catch (Throwable ex) {
|
||||
// Ignore. No java.util.logging bridge is installed.
|
||||
}
|
||||
}
|
||||
|
||||
private boolean isBridgeJulIntoSlf4j() {
|
||||
return isBridgeHandlerAvailable() && isJulUsingASingleConsoleHandlerAtMost();
|
||||
}
|
||||
|
||||
private boolean isBridgeHandlerAvailable() {
|
||||
return ClassUtils.isPresent(BRIDGE_HANDLER, getClassLoader());
|
||||
}
|
||||
|
||||
private boolean isJulUsingASingleConsoleHandlerAtMost() {
|
||||
java.util.logging.Logger rootLogger = LogManager.getLogManager().getLogger("");
|
||||
Handler[] handlers = rootLogger.getHandlers();
|
||||
return handlers.length == 0 || (handlers.length == 1 && handlers[0] instanceof ConsoleHandler);
|
||||
}
|
||||
|
||||
private void removeJdkLoggingBridgeHandler() {
|
||||
try {
|
||||
removeDefaultRootHandler();
|
||||
SLF4JBridgeHandler.uninstall();
|
||||
}
|
||||
catch (Throwable ex) {
|
||||
// Ignore and continue
|
||||
}
|
||||
}
|
||||
|
||||
private void removeDefaultRootHandler() {
|
||||
try {
|
||||
java.util.logging.Logger rootLogger = LogManager.getLogManager().getLogger("");
|
||||
Handler[] handlers = rootLogger.getHandlers();
|
||||
if (handlers.length == 1 && handlers[0] instanceof ConsoleHandler) {
|
||||
rootLogger.removeHandler(handlers[0]);
|
||||
}
|
||||
}
|
||||
catch (Throwable ex) {
|
||||
// Ignore and continue
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void initialize(LoggingInitializationContext initializationContext, String configLocation, LogFile logFile) {
|
||||
LoggerContext loggerContext = getLoggerContext();
|
||||
@@ -158,7 +211,9 @@ public class LogbackLoggingSystem extends Slf4JLoggingSystem {
|
||||
@Override
|
||||
protected void loadConfiguration(LoggingInitializationContext initializationContext, String location,
|
||||
LogFile logFile) {
|
||||
super.loadConfiguration(initializationContext, location, logFile);
|
||||
if (initializationContext != null) {
|
||||
applySystemProperties(initializationContext.getEnvironment(), logFile);
|
||||
}
|
||||
LoggerContext loggerContext = getLoggerContext();
|
||||
stopAndReset(loggerContext);
|
||||
try {
|
||||
@@ -221,6 +276,9 @@ public class LogbackLoggingSystem extends Slf4JLoggingSystem {
|
||||
LoggerContext context = getLoggerContext();
|
||||
markAsUninitialized(context);
|
||||
super.cleanUp();
|
||||
if (isBridgeHandlerAvailable()) {
|
||||
removeJdkLoggingBridgeHandler();
|
||||
}
|
||||
context.getStatusManager().clear();
|
||||
context.getTurboFilterList().remove(FILTER);
|
||||
}
|
||||
|
||||
@@ -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(
|
||||
|
||||
Reference in New Issue
Block a user