Browse Source

Simplify Log4J2LoggingSystem

Streamline and harden `Log4J2LoggingSystem` by delegating more
functionality. The commit makes updates in two key areas:

Previously, each method fetched the `LoggerContext` directly from
`LogManager` and cast it to `....core.LoggerContext`. This approach
has several issues:

* ClassCastException risks:
  - When Log4j Core is on the classpath but not the active implementation
    (e.g. when `log4j-to-slf4j` is used).
  - During shutdown, when `LogManager` may return a `SimpleLoggerContext`
    (see spring-projects/spring-boot#26953).

* Unexpected reinitialization:
  - If the logger context had already been stopped, `Log4J2LoggingSystem`
    would trigger creation of a new context, even mid-shutdown.

Configuration file detection was previously hardcoded in
`Log4J2LoggingSystem`, which limited flexibility:

* Harder to support additional configuration formats.

* Coupled Spring Boot to internal Log4j Core classes such as
  `AuthorizationProvider`.

This change now delegates configuration resolution to Log4j. This reduces
reliance on internal APIs and allows Log4j Core to handle configuration
formats and factories more naturally.

Signed-off-by: Piotr P. Karwasz <piotr@github.copernik.eu>

See gh-47424
pull/47784/head
Piotr P. Karwasz 4 months ago committed by Phillip Webb
parent
commit
7d34320401
  1. 263
      core/spring-boot/src/main/java/org/springframework/boot/logging/log4j2/Log4J2LoggingSystem.java
  2. 7
      core/spring-boot/src/main/java/org/springframework/boot/logging/log4j2/Log4J2RuntimeHints.java
  3. 209
      core/spring-boot/src/test/java/org/springframework/boot/logging/log4j2/Log4J2LoggingSystemTests.java
  4. 5
      core/spring-boot/src/test/java/org/springframework/boot/logging/log4j2/Log4J2RuntimeHintsTests.java
  5. 44
      core/spring-boot/src/test/java/org/springframework/boot/logging/log4j2/SpringProfileArbiterTests.java
  6. 41
      core/spring-boot/src/test/java/org/springframework/boot/logging/log4j2/TestLog4J2LoggingSystem.java

263
core/spring-boot/src/main/java/org/springframework/boot/logging/log4j2/Log4J2LoggingSystem.java

@ -16,11 +16,7 @@ @@ -16,11 +16,7 @@
package org.springframework.boot.logging.log4j2;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
import java.net.URLConnection;
import java.util.ArrayList;
import java.util.Collections;
import java.util.LinkedHashMap;
@ -37,15 +33,11 @@ import org.apache.logging.log4j.core.Logger; @@ -37,15 +33,11 @@ import org.apache.logging.log4j.core.Logger;
import org.apache.logging.log4j.core.LoggerContext;
import org.apache.logging.log4j.core.config.AbstractConfiguration;
import org.apache.logging.log4j.core.config.Configuration;
import org.apache.logging.log4j.core.config.ConfigurationException;
import org.apache.logging.log4j.core.config.ConfigurationFactory;
import org.apache.logging.log4j.core.config.ConfigurationSource;
import org.apache.logging.log4j.core.config.LoggerConfig;
import org.apache.logging.log4j.core.config.composite.CompositeConfiguration;
import org.apache.logging.log4j.core.filter.DenyAllFilter;
import org.apache.logging.log4j.core.net.UrlConnectionFactory;
import org.apache.logging.log4j.core.net.ssl.SslConfiguration;
import org.apache.logging.log4j.core.net.ssl.SslConfigurationFactory;
import org.apache.logging.log4j.core.util.AuthorizationProvider;
import org.apache.logging.log4j.core.util.NameUtil;
import org.apache.logging.log4j.jul.Log4jBridgeHandler;
import org.apache.logging.log4j.status.StatusConsoleListener;
@ -72,7 +64,6 @@ import org.springframework.core.io.Resource; @@ -72,7 +64,6 @@ import org.springframework.core.io.Resource;
import org.springframework.core.io.ResourceLoader;
import org.springframework.util.Assert;
import org.springframework.util.ClassUtils;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;
/**
@ -88,6 +79,8 @@ import org.springframework.util.StringUtils; @@ -88,6 +79,8 @@ import org.springframework.util.StringUtils;
*/
public class Log4J2LoggingSystem extends AbstractLoggingSystem {
private static final org.apache.logging.log4j.Logger STATUS_LOGGER = StatusLogger.getLogger();
private static final String OPTIONAL_PREFIX = "optional:";
/**
@ -100,41 +93,6 @@ public class Log4J2LoggingSystem extends AbstractLoggingSystem { @@ -100,41 +93,6 @@ public class Log4J2LoggingSystem extends AbstractLoggingSystem {
*/
static final String LOG4J_LOG_MANAGER = "org.apache.logging.log4j.jul.LogManager";
/**
* JSON tree parser used by Log4j 2 (optional dependency).
*/
static final String JSON_TREE_PARSER_V2 = "com.fasterxml.jackson.databind.ObjectMapper";
/**
* JSON tree parser embedded in Log4j 3.
*/
static final String JSON_TREE_PARSER_V3 = "org.apache.logging.log4j.kit.json.JsonReader";
/**
* Configuration factory for properties files (Log4j 2).
*/
static final String PROPS_CONFIGURATION_FACTORY_V2 = "org.apache.logging.log4j.core.config.properties.PropertiesConfigurationFactory";
/**
* Configuration factory for properties files (Log4j 3, optional dependency).
*/
static final String PROPS_CONFIGURATION_FACTORY_V3 = "org.apache.logging.log4j.config.properties.JavaPropsConfigurationFactory";
/**
* YAML tree parser used by Log4j 2 (optional dependency).
*/
static final String YAML_TREE_PARSER_V2 = "com.fasterxml.jackson.dataformat.yaml.YAMLMapper";
/**
* Configuration factory for YAML files (Log4j 2, embedded).
*/
static final String YAML_CONFIGURATION_FACTORY_V2 = "org.apache.logging.log4j.core.config.yaml.YamlConfigurationFactory";
/**
* Configuration factory for YAML files (Log4j 3, optional dependency).
*/
static final String YAML_CONFIGURATION_FACTORY_V3 = "org.apache.logging.log4j.config.yaml.YamlConfigurationFactory";
private static final SpringEnvironmentPropertySource propertySource = new SpringEnvironmentPropertySource();
static final String ENVIRONMENT_KEY = Conventions.getQualifiedAttributeName(Log4J2LoggingSystem.class,
@ -157,73 +115,69 @@ public class Log4J2LoggingSystem extends AbstractLoggingSystem { @@ -157,73 +115,69 @@ public class Log4J2LoggingSystem extends AbstractLoggingSystem {
private static final Filter FILTER = DenyAllFilter.newBuilder().build();
public Log4J2LoggingSystem(ClassLoader classLoader) {
super(classLoader);
}
private final LoggerContext loggerContext;
@Override
protected String[] getStandardConfigLocations() {
List<String> locations = new ArrayList<>();
addLocationsFromProperties(locations);
addStandardLocations(locations);
return StringUtils.toStringArray(locations);
/**
* Create a new {@link Log4J2LoggingSystem} instance.
* @param classLoader the class loader to use.
* @param loggerContext the {@link LoggerContext} to use.
*/
Log4J2LoggingSystem(ClassLoader classLoader, LoggerContext loggerContext) {
super(classLoader);
this.loggerContext = loggerContext;
}
private void addLocationsFromProperties(List<String> locations) {
for (String property : List.of("log4j2.configurationFile", "log4j.configuration.location")) {
String propertyDefinedLocation = PropertiesUtil.getProperties().getStringProperty(property);
if (propertyDefinedLocation != null) {
locations.add(propertyDefinedLocation);
}
/**
* Create a new {@link Log4J2LoggingSystem} instance.
* @param classLoader the class loader to use
* @return a new {@link Log4J2LoggingSystem} instance
* @throws IllegalStateException if Log4j Core is not the active Log4j API provider.
*/
private static Log4J2LoggingSystem createLoggingSystem(ClassLoader classLoader) {
org.apache.logging.log4j.spi.LoggerContext loggerContext = LogManager.getContext(classLoader, false);
if (loggerContext instanceof LoggerContext) {
return new Log4J2LoggingSystem(classLoader, (LoggerContext) loggerContext);
}
throw new IllegalStateException("Log4j Core is not the active Log4j API provider");
}
private void addStandardLocations(List<String> locations) {
LoggerContext loggerContext = getLoggerContext();
String contextName = loggerContext.getName();
List<String> extensions = getStandardConfigExtensions();
addLocation(locations, "log4j2-test" + contextName, extensions);
addLocation(locations, "log4j2-test", extensions);
addLocation(locations, "log4j2" + contextName, extensions);
addLocation(locations, "log4j2", extensions);
}
private List<String> getStandardConfigExtensions() {
List<String> extensions = new ArrayList<>();
// These classes need to be visible by the classloader that loads Log4j Core.
ClassLoader classLoader = LoggerContext.class.getClassLoader();
// The order of the extensions corresponds to the order in which Log4j Core 2 and
// 3 will try to load them, in decreasing value of @Order.
if (isPresent(classLoader, PROPS_CONFIGURATION_FACTORY_V2)
|| isPresent(classLoader, PROPS_CONFIGURATION_FACTORY_V3)) {
extensions.add(".properties");
}
if (isPresent(classLoader, YAML_CONFIGURATION_FACTORY_V2, YAML_TREE_PARSER_V2)
|| isPresent(classLoader, YAML_CONFIGURATION_FACTORY_V3)) {
Collections.addAll(extensions, ".yaml", ".yml");
}
if (isPresent(classLoader, JSON_TREE_PARSER_V2) || isPresent(classLoader, JSON_TREE_PARSER_V3)) {
Collections.addAll(extensions, ".json", ".jsn");
}
extensions.add(".xml");
return extensions;
/**
* {@inheritDoc}
* @deprecated Since 4.0.0, in favor of the {@link ConfigurationFactory} SPI.
*/
@Override
@Deprecated(since = "4.0.0", forRemoval = true)
protected String[] getStandardConfigLocations() {
return new String[] { "log4j2.xml" };
}
private void addLocation(List<String> locations, String location, List<String> extensions) {
extensions.forEach((extension) -> locations.add(location + extension));
@Override
protected @Nullable String getSelfInitializationConfig() {
Configuration currentConfiguration = getLoggerContext().getConfiguration();
return getConfigLocation(currentConfiguration);
}
private boolean isPresent(ClassLoader classLoader, String... classNames) {
for (String className : classNames) {
if (!isClassAvailable(classLoader, className)) {
return false;
}
@Override
protected @Nullable String getSpringInitializationConfig() {
ConfigurationFactory configurationFactory = ConfigurationFactory.getInstance();
try {
Configuration springConfiguration = configurationFactory.getConfiguration(getLoggerContext(), "-spring",
null, getClassLoader());
String configLocation = getConfigLocation(springConfiguration);
return (configLocation != null && configLocation.contains("-spring")) ? configLocation : null;
}
catch (ConfigurationException ex) {
STATUS_LOGGER.warn("Could not load Spring-specific Log4j Core configuration", ex);
return null;
}
return true;
}
protected boolean isClassAvailable(ClassLoader classLoader, String className) {
return ClassUtils.isPresent(className, classLoader);
private @Nullable String getConfigLocation(Configuration configuration) {
// The location may be:
// - null: if DefaultConfiguration is used (no explicit config loaded)
// - a file path: if provided explicitly by the user
// - a URI: if loaded from the classpath default or a custom location
return configuration.getConfigurationSource().getLocation();
}
@Deprecated(since = "4.0.0", forRemoval = true)
@ -335,7 +289,7 @@ public class Log4J2LoggingSystem extends AbstractLoggingSystem { @@ -335,7 +289,7 @@ public class Log4J2LoggingSystem extends AbstractLoggingSystem {
Environment environment = initializationContext.getEnvironment();
Assert.state(environment != null, "'environment' must not be null");
applySystemProperties(environment, logFile);
loadConfiguration(location, logFile, overrides);
reconfigure(location, overrides);
}
private List<String> getOverrides(LoggingInitializationContext initializationContext) {
@ -346,66 +300,56 @@ public class Log4J2LoggingSystem extends AbstractLoggingSystem { @@ -346,66 +300,56 @@ public class Log4J2LoggingSystem extends AbstractLoggingSystem {
return overrides.orElse(Collections.emptyList());
}
/**
* Load the configuration from the given {@code location}, creating a composite using
* the configuration from the given {@code overrides}.
* @param location the location
* @param logFile log file configuration
* @param overrides the overriding locations
* @since 2.6.0
*/
protected void loadConfiguration(String location, @Nullable LogFile logFile, List<String> overrides) {
private void reconfigure(String location, List<String> overrides) {
Assert.notNull(location, "'location' must not be null");
try {
List<Configuration> configurations = new ArrayList<>();
LoggerContext context = getLoggerContext();
ResourceLoader resourceLoader = ApplicationResourceLoader.get();
configurations.add(load(resourceLoader.getResource(location), context));
ResourceLoader resourceLoader = ApplicationResourceLoader.get(getClassLoader());
configurations.add(load(resourceLoader, location));
for (String override : overrides) {
Configuration overrideConfiguration = loadOverride(resourceLoader, override, context);
Configuration overrideConfiguration = loadOverride(resourceLoader, override);
if (overrideConfiguration != null) {
configurations.add(overrideConfiguration);
}
}
context.reconfigure(mergeConfigurations(configurations));
this.loggerContext.reconfigure(mergeConfigurations(configurations));
}
catch (Exception ex) {
throw new IllegalStateException("Could not initialize Log4J2 logging from " + location, ex);
String message = "Could not initialize Log4J2 logging from " + location;
if (!overrides.isEmpty()) {
message += " with overrides " + overrides;
}
throw new IllegalStateException(message, ex);
}
}
private Configuration load(Resource resource, LoggerContext context) throws IOException {
private Configuration load(ResourceLoader resourceLoader, String location) throws IOException {
ConfigurationFactory factory = ConfigurationFactory.getInstance();
if (resource.isFile()) {
try (InputStream inputStream = resource.getInputStream()) {
return factory.getConfiguration(context, new ConfigurationSource(inputStream, resource.getFile()));
}
}
URL url = resource.getURL();
AuthorizationProvider authorizationProvider = ConfigurationFactory
.authorizationProvider(PropertiesUtil.getProperties());
SslConfiguration sslConfiguration = url.getProtocol().equals("https")
? SslConfigurationFactory.getSslConfiguration() : null;
URLConnection connection = UrlConnectionFactory.createConnection(url, 0, sslConfiguration,
authorizationProvider);
try (InputStream inputStream = connection.getInputStream()) {
return factory.getConfiguration(context,
new ConfigurationSource(inputStream, url, connection.getLastModified()));
Resource resource = resourceLoader.getResource(location);
Configuration configuration = factory.getConfiguration(getLoggerContext(), null, resource.getURI(),
getClassLoader());
// The error handling in Log4j Core 2.25.x is not consistent:
// some loading and parsing errors result in a null configuration,
// others in an exception.
if (configuration == null) {
throw new ConfigurationException("Could not load Log4j Core configuration from " + location);
}
return configuration;
}
private @Nullable Configuration loadOverride(ResourceLoader resourceLoader, String location, LoggerContext context)
throws IOException {
private @Nullable Configuration loadOverride(ResourceLoader resourceLoader, String location) throws IOException {
if (location.startsWith(OPTIONAL_PREFIX)) {
Resource resource = resourceLoader.getResource(location.substring(OPTIONAL_PREFIX.length()));
String actualLocation = location.substring(OPTIONAL_PREFIX.length());
Resource resource = resourceLoader.getResource(actualLocation);
try {
return (resource.exists()) ? load(resource, context) : null;
return (resource.exists()) ? load(resourceLoader, actualLocation) : null;
}
catch (FileNotFoundException ex) {
catch (ConfigurationException | IOException ex) {
STATUS_LOGGER.debug("Could not load optional Log4j2 override from {}", actualLocation, ex);
return null;
}
}
return load(resourceLoader.getResource(location), context);
return load(resourceLoader, location);
}
private Configuration mergeConfigurations(List<Configuration> configurations) {
@ -417,33 +361,11 @@ public class Log4J2LoggingSystem extends AbstractLoggingSystem { @@ -417,33 +361,11 @@ public class Log4J2LoggingSystem extends AbstractLoggingSystem {
@Override
protected void reinitialize(LoggingInitializationContext initializationContext) {
List<String> overrides = getOverrides(initializationContext);
if (!CollectionUtils.isEmpty(overrides)) {
reinitializeWithOverrides(overrides);
}
else {
LoggerContext context = getLoggerContext();
context.reconfigure();
}
}
private void reinitializeWithOverrides(List<String> overrides) {
LoggerContext context = getLoggerContext();
List<Configuration> configurations = new ArrayList<>();
configurations.add(context.getConfiguration());
ResourceLoader resourceLoader = ApplicationResourceLoader.get();
for (String override : overrides) {
try {
Configuration overrideConfiguration = loadOverride(resourceLoader, override, context);
if (overrideConfiguration != null) {
configurations.add(overrideConfiguration);
}
}
catch (IOException ex) {
throw new RuntimeException("Failed to load overriding configuration from '" + override + "'", ex);
}
}
context.reconfigure(mergeConfigurations(configurations));
String currentLocation = getSelfInitializationConfig();
// `reinitialize` is only triggered when `getSelfInitializationConfig` returns a
// non-null value
Assert.notNull(currentLocation, "'currentLocation' must not be null");
load(initializationContext, currentLocation, null);
}
@Override
@ -584,8 +506,8 @@ public class Log4J2LoggingSystem extends AbstractLoggingSystem { @@ -584,8 +506,8 @@ public class Log4J2LoggingSystem extends AbstractLoggingSystem {
return configuration.getLoggers().get(name);
}
private LoggerContext getLoggerContext() {
return (LoggerContext) LogManager.getContext(false);
LoggerContext getLoggerContext() {
return this.loggerContext;
}
private boolean isAlreadyInitialized(LoggerContext loggerContext) {
@ -622,7 +544,7 @@ public class Log4J2LoggingSystem extends AbstractLoggingSystem { @@ -622,7 +544,7 @@ public class Log4J2LoggingSystem extends AbstractLoggingSystem {
@Order(0)
public static class Factory implements LoggingSystemFactory {
static final String LOG4J_CORE_CONTEXT_FACTORY = "org.apache.logging.log4j.core.impl.Log4jContextFactory";
private static final String LOG4J_CORE_CONTEXT_FACTORY = "org.apache.logging.log4j.core.impl.Log4jContextFactory";
private static final boolean PRESENT = ClassUtils.isPresent(LOG4J_CORE_CONTEXT_FACTORY,
Factory.class.getClassLoader());
@ -630,7 +552,12 @@ public class Log4J2LoggingSystem extends AbstractLoggingSystem { @@ -630,7 +552,12 @@ public class Log4J2LoggingSystem extends AbstractLoggingSystem {
@Override
public @Nullable LoggingSystem getLoggingSystem(ClassLoader classLoader) {
if (PRESENT) {
return new Log4J2LoggingSystem(classLoader);
try {
return createLoggingSystem(classLoader);
}
catch (IllegalStateException ex) {
// Log4j Core is not the active Log4j API provider
}
}
return null;
}

7
core/spring-boot/src/main/java/org/springframework/boot/logging/log4j2/Log4J2RuntimeHints.java

@ -44,13 +44,6 @@ class Log4J2RuntimeHints implements RuntimeHintsRegistrar { @@ -44,13 +44,6 @@ class Log4J2RuntimeHints implements RuntimeHintsRegistrar {
hints.resources().registerPattern("org/springframework/boot/logging/log4j2/log4j2-file.xml");
hints.resources().registerPattern("log4j2.springboot");
// Declares the types that Log4j2LoggingSystem checks for existence reflectively.
hints.reflection().registerTypeIfPresent(classLoader, Log4J2LoggingSystem.JSON_TREE_PARSER_V2);
hints.reflection().registerTypeIfPresent(classLoader, Log4J2LoggingSystem.JSON_TREE_PARSER_V3);
hints.reflection().registerTypeIfPresent(classLoader, Log4J2LoggingSystem.PROPS_CONFIGURATION_FACTORY_V2);
hints.reflection().registerTypeIfPresent(classLoader, Log4J2LoggingSystem.PROPS_CONFIGURATION_FACTORY_V3);
hints.reflection().registerTypeIfPresent(classLoader, Log4J2LoggingSystem.YAML_TREE_PARSER_V2);
hints.reflection().registerTypeIfPresent(classLoader, Log4J2LoggingSystem.YAML_CONFIGURATION_FACTORY_V2);
hints.reflection().registerTypeIfPresent(classLoader, Log4J2LoggingSystem.YAML_CONFIGURATION_FACTORY_V3);
hints.reflection().registerTypeIfPresent(classLoader, Log4J2LoggingSystem.LOG4J_BRIDGE_HANDLER);
hints.reflection().registerTypeIfPresent(classLoader, Log4J2LoggingSystem.LOG4J_LOG_MANAGER);
}

209
core/spring-boot/src/test/java/org/springframework/boot/logging/log4j2/Log4J2LoggingSystemTests.java

@ -23,16 +23,12 @@ import java.lang.annotation.ElementType; @@ -23,16 +23,12 @@ import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import java.net.ProtocolException;
import java.util.ArrayList;
import java.util.Collections;
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 java.util.stream.Stream;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
@ -41,14 +37,9 @@ import org.apache.logging.log4j.Logger; @@ -41,14 +37,9 @@ import org.apache.logging.log4j.Logger;
import org.apache.logging.log4j.core.LoggerContext;
import org.apache.logging.log4j.core.config.Configuration;
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.config.json.JsonConfigurationFactory;
import org.apache.logging.log4j.core.config.plugins.util.PluginRegistry;
import org.apache.logging.log4j.core.config.properties.PropertiesConfigurationBuilder;
import org.apache.logging.log4j.core.config.properties.PropertiesConfigurationFactory;
import org.apache.logging.log4j.core.config.xml.XmlConfiguration;
import org.apache.logging.log4j.core.config.yaml.YamlConfigurationFactory;
import org.apache.logging.log4j.core.util.ShutdownCallbackRegistry;
import org.apache.logging.log4j.jul.Log4jBridgeHandler;
import org.apache.logging.log4j.status.StatusListener;
@ -58,10 +49,8 @@ import org.jspecify.annotations.Nullable; @@ -58,10 +49,8 @@ import org.jspecify.annotations.Nullable;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.TestInfo;
import org.junit.jupiter.api.extension.ExtendWith;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.Arguments;
import org.junit.jupiter.params.provider.MethodSource;
import org.slf4j.MDC;
import org.springframework.boot.logging.AbstractLoggingSystemTests;
@ -113,29 +102,18 @@ class Log4J2LoggingSystemTests extends AbstractLoggingSystemTests { @@ -113,29 +102,18 @@ class Log4J2LoggingSystemTests extends AbstractLoggingSystemTests {
private Logger logger;
private Configuration configuration;
private String contextName;
@BeforeEach
void setup() {
void setup(TestInfo testInfo) {
PluginRegistry.getInstance().clear();
this.loggingSystem = new TestLog4J2LoggingSystem();
this.loggingSystem = new TestLog4J2LoggingSystem(testInfo.getDisplayName());
this.environment = new MockEnvironment();
this.initializationContext = new LoggingInitializationContext(this.environment);
LoggerContext loggerContext = (LoggerContext) LogManager.getContext(false);
this.configuration = loggerContext.getConfiguration();
this.loggingSystem.cleanUp();
this.logger = LogManager.getLogger(getClass());
this.contextName = loggerContext.getName();
this.logger = this.loggingSystem.getLoggerContext().getLogger(getClass().getName());
}
@AfterEach
void cleanUp() {
this.loggingSystem.cleanUp();
LoggerContext loggerContext = (LoggerContext) LogManager.getContext(false);
loggerContext.stop();
loggerContext.start(((Reconfigurable) this.configuration).reconfigure());
PluginRegistry.getInstance().clear();
}
@ -185,6 +163,17 @@ class Log4J2LoggingSystemTests extends AbstractLoggingSystemTests { @@ -185,6 +163,17 @@ class Log4J2LoggingSystemTests extends AbstractLoggingSystemTests {
"classpath:log4j2-nonexistent.xml", null));
}
@Test
@WithSpringXmlResource
void testSpringBootConfigLocation() {
this.loggingSystem.disableSelfInitialization();
this.loggingSystem.beforeInitialize();
this.loggingSystem.initialize(this.initializationContext, null, null);
Configuration configuration = this.loggingSystem.getConfiguration();
assertThat(configuration.getName()).isEqualTo("SpringBoot");
assertThat(configuration.getConfigurationSource().getLocation()).endsWith("log4j2-test-spring.xml");
}
@Test
void getSupportedLevels() {
assertThat(this.loggingSystem.getSupportedLogLevels()).isEqualTo(EnumSet.allOf(LogLevel.class));
@ -224,7 +213,8 @@ class Log4J2LoggingSystemTests extends AbstractLoggingSystemTests { @@ -224,7 +213,8 @@ class Log4J2LoggingSystemTests extends AbstractLoggingSystemTests {
@Test
void getLoggerConfigurationsShouldReturnAllLoggers() {
LogManager.getLogger("org.springframework.boot.logging.log4j2.Log4J2LoggingSystemTests$Nested");
this.loggingSystem.getLoggerContext()
.getLogger("org.springframework.boot.logging.log4j2.Log4J2LoggingSystemTests$Nested");
this.loggingSystem.beforeInitialize();
this.loggingSystem.initialize(this.initializationContext, null, null);
this.loggingSystem.setLogLevel(getClass().getName(), LogLevel.DEBUG);
@ -243,7 +233,7 @@ class Log4J2LoggingSystemTests extends AbstractLoggingSystemTests { @@ -243,7 +233,7 @@ class Log4J2LoggingSystemTests extends AbstractLoggingSystemTests {
void getLoggerConfigurationWhenHasCustomLevel() {
this.loggingSystem.beforeInitialize();
this.loggingSystem.initialize(this.initializationContext, null, null);
LoggerContext loggerContext = (LoggerContext) LogManager.getContext(false);
LoggerContext loggerContext = this.loggingSystem.getLoggerContext();
String loggerName = getClass().getName();
org.apache.logging.log4j.Level level = org.apache.logging.log4j.Level.forName("CUSTOM_LEVEL", 1000);
loggerContext.getConfiguration().addLogger(loggerName, new LoggerConfig(loggerName, level, true));
@ -307,82 +297,6 @@ class Log4J2LoggingSystemTests extends AbstractLoggingSystemTests { @@ -307,82 +297,6 @@ class Log4J2LoggingSystemTests extends AbstractLoggingSystemTests {
assertThat(output).contains("Hello world");
}
static Stream<String> configLocationsWithConfigurationFileSystemProperty() {
return Stream.of("log4j2.configurationFile", "log4j.configuration.location");
}
@ParameterizedTest
@MethodSource
void configLocationsWithConfigurationFileSystemProperty(String propertyName) {
System.setProperty(propertyName, "custom-log4j2.properties");
try {
assertThat(this.loggingSystem.getStandardConfigLocations()).containsExactly("custom-log4j2.properties",
"log4j2-test" + this.contextName + ".xml", "log4j2-test.xml", "log4j2" + this.contextName + ".xml",
"log4j2.xml");
}
finally {
System.clearProperty(propertyName);
}
}
static Stream<Arguments> standardConfigLocations() {
// For each configuration file format we make "available" to the
// Log4j2LoggingSystem:
// - The Log4j Core `ConfigurationFactory` class
// - The tree parser used internally by that configuration factory
return Stream.of(
// No classes, only XML
Arguments.of(Collections.emptyList(), List.of(".xml")),
// Log4j Core 2
Arguments.of(List.of(JsonConfigurationFactory.class.getName(),
"com.fasterxml.jackson.databind.ObjectMapper"), List.of(".json", ".jsn", ".xml")),
Arguments.of(List.of(PropertiesConfigurationFactory.class.getName(),
PropertiesConfigurationBuilder.class.getName()), List.of(".properties", ".xml")),
Arguments.of(List.of(YamlConfigurationFactory.class.getName(),
"com.fasterxml.jackson.dataformat.yaml.YAMLMapper"), List.of(".yaml", ".yml", ".xml")),
Arguments.of(List.of(JsonConfigurationFactory.class.getName(),
"com.fasterxml.jackson.databind.ObjectMapper", PropertiesConfigurationFactory.class.getName(),
PropertiesConfigurationBuilder.class.getName(), YamlConfigurationFactory.class.getName(),
"com.fasterxml.jackson.dataformat.yaml.YAMLMapper"),
List.of(".properties", ".yaml", ".yml", ".json", ".jsn", ".xml")),
// Log4j Core 3
Arguments.of(List.of(JsonConfigurationFactory.class.getName(),
"org.apache.logging.log4j.kit.json.JsonReader"), List.of(".json", ".jsn", ".xml")),
Arguments.of(List.of("org.apache.logging.log4j.config.properties.JavaPropsConfigurationFactory",
"tools.jackson.dataformat.javaprop.JavaPropsMapper"), List.of(".properties", ".xml")),
Arguments.of(List.of("org.apache.logging.log4j.config.yaml.YamlConfigurationFactory",
"tools.jackson.dataformat.yaml.YAMLMapper"), List.of(".yaml", ".yml", ".xml")),
Arguments.of(
List.of(JsonConfigurationFactory.class.getName(),
"org.apache.logging.log4j.kit.json.JsonReader",
"org.apache.logging.log4j.config.properties.JavaPropsConfigurationFactory",
"tools.jackson.dataformat.javaprop.JavaPropsMapper",
"org.apache.logging.log4j.config.yaml.YamlConfigurationFactory",
"tools.jackson.dataformat.yaml.YAMLMapper"),
List.of(".properties", ".yaml", ".yml", ".json", ".jsn", ".xml")));
}
@ParameterizedTest
@MethodSource
void standardConfigLocations(List<String> availableClasses, List<String> expectedSuffixes) {
this.loggingSystem.availableClasses(availableClasses.toArray(new String[0]));
String[] locations = this.loggingSystem.getStandardConfigLocations();
assertThat(locations).hasSize(4 * expectedSuffixes.size());
List<String> expected = new ArrayList<>();
expectedSuffixes.forEach((s) -> expected.add("log4j2-test" + this.contextName + s));
expectedSuffixes.forEach((s) -> expected.add("log4j2-test" + s));
expectedSuffixes.forEach((s) -> expected.add("log4j2" + this.contextName + s));
expectedSuffixes.forEach((s) -> expected.add("log4j2" + s));
assertThat(locations).containsExactlyElementsOf(expected);
}
@Test
void springConfigLocations() {
String[] locations = getSpringConfigLocations(this.loggingSystem);
assertThat(locations).containsExactly("log4j2-test" + this.contextName + "-spring.xml",
"log4j2-test-spring.xml", "log4j2" + this.contextName + "-spring.xml", "log4j2-spring.xml");
}
@Test
void exceptionsIncludeClassPackaging(CapturedOutput output) {
this.loggingSystem.beforeInitialize();
@ -421,7 +335,7 @@ class Log4J2LoggingSystemTests extends AbstractLoggingSystemTests { @@ -421,7 +335,7 @@ class Log4J2LoggingSystemTests extends AbstractLoggingSystemTests {
@Test
void initializationIsOnlyPerformedOnceUntilCleanedUp() {
LoggerContext loggerContext = (LoggerContext) LogManager.getContext(false);
LoggerContext loggerContext = this.loggingSystem.getLoggerContext();
PropertyChangeListener listener = mock(PropertyChangeListener.class);
loggerContext.addPropertyChangeListener(listener);
this.loggingSystem.beforeInitialize();
@ -453,7 +367,7 @@ class Log4J2LoggingSystemTests extends AbstractLoggingSystemTests { @@ -453,7 +367,7 @@ class Log4J2LoggingSystemTests extends AbstractLoggingSystemTests {
@Test
void getLoggerConfigurationWithResetLevelWhenAlreadyConfiguredReturnsParentConfiguredLevel() {
LoggerContext loggerContext = (LoggerContext) LogManager.getContext(false);
LoggerContext loggerContext = this.loggingSystem.getLoggerContext();
this.loggingSystem.beforeInitialize();
this.loggingSystem.initialize(this.initializationContext, null, null);
loggerContext.getConfiguration()
@ -472,18 +386,21 @@ class Log4J2LoggingSystemTests extends AbstractLoggingSystemTests { @@ -472,18 +386,21 @@ class Log4J2LoggingSystemTests extends AbstractLoggingSystemTests {
@Test
void log4jLevelsArePropagatedToJul() {
this.loggingSystem.beforeInitialize();
// In this test we need to use the global logging system, since JUL is a global
// singleton.
LoggingSystem loggingSystem = LoggingSystem.get(getClass().getClassLoader());
loggingSystem.beforeInitialize();
java.util.logging.Logger rootLogger = java.util.logging.Logger.getLogger("");
// check if Log4jBridgeHandler is used
Handler[] handlers = rootLogger.getHandlers();
assertThat(handlers).hasSize(1);
assertThat(handlers[0]).isInstanceOf(Log4jBridgeHandler.class);
this.loggingSystem.initialize(this.initializationContext, null, null);
loggingSystem.initialize(this.initializationContext, null, null);
java.util.logging.Logger logger = java.util.logging.Logger.getLogger(Log4J2LoggingSystemTests.class.getName());
logger.info("Log to trigger level propagation");
assertThat(logger.getLevel()).isNull();
this.loggingSystem.setLogLevel(Log4J2LoggingSystemTests.class.getName(), LogLevel.DEBUG);
loggingSystem.setLogLevel(Log4J2LoggingSystemTests.class.getName(), LogLevel.DEBUG);
assertThat(logger.getLevel()).isEqualTo(Level.FINE);
}
@ -557,7 +474,7 @@ class Log4J2LoggingSystemTests extends AbstractLoggingSystemTests { @@ -557,7 +474,7 @@ class Log4J2LoggingSystemTests extends AbstractLoggingSystemTests {
void initializeAttachesEnvironmentToLoggerContext() {
this.loggingSystem.beforeInitialize();
this.loggingSystem.initialize(this.initializationContext, null, null);
LoggerContext loggerContext = (LoggerContext) LogManager.getContext(false);
LoggerContext loggerContext = this.loggingSystem.getLoggerContext();
Environment environment = Log4J2LoggingSystem.getEnvironment(loggerContext);
assertThat(environment).isSameAs(this.environment);
}
@ -566,7 +483,7 @@ class Log4J2LoggingSystemTests extends AbstractLoggingSystemTests { @@ -566,7 +483,7 @@ class Log4J2LoggingSystemTests extends AbstractLoggingSystemTests {
void initializeRegisterStatusListenerAndAttachToLoggerContext() {
this.loggingSystem.beforeInitialize();
this.loggingSystem.initialize(this.initializationContext, null, null);
LoggerContext loggerContext = (LoggerContext) LogManager.getContext(false);
LoggerContext loggerContext = this.loggingSystem.getLoggerContext();
StatusListener statusListener = (StatusListener) loggerContext
.getObject(Log4J2LoggingSystem.STATUS_LISTENER_KEY);
assertThat(statusListener).isNotNull();
@ -578,7 +495,7 @@ class Log4J2LoggingSystemTests extends AbstractLoggingSystemTests { @@ -578,7 +495,7 @@ class Log4J2LoggingSystemTests extends AbstractLoggingSystemTests {
this.loggingSystem.beforeInitialize();
this.loggingSystem.initialize(this.initializationContext, null, null);
// listener should be registered
LoggerContext loggerContext = (LoggerContext) LogManager.getContext(false);
LoggerContext loggerContext = this.loggingSystem.getLoggerContext();
StatusListener statusListener = (StatusListener) loggerContext
.getObject(Log4J2LoggingSystem.STATUS_LISTENER_KEY);
assertThat(statusListener).isNotNull();
@ -621,24 +538,13 @@ class Log4J2LoggingSystemTests extends AbstractLoggingSystemTests { @@ -621,24 +538,13 @@ class Log4J2LoggingSystemTests extends AbstractLoggingSystemTests {
assertThat(PropertiesUtil.getProperties().getStringProperty("spring")).isEqualTo("boot: two");
}
@Test
void nonFileUrlsAreResolvedUsingLog4J2UrlConnectionFactory() {
this.loggingSystem.beforeInitialize();
assertThatIllegalStateException()
.isThrownBy(() -> this.loggingSystem.initialize(this.initializationContext,
"http://localhost:8080/shouldnotwork", null))
.havingCause()
.isInstanceOf(ProtocolException.class)
.withMessageContaining("http has not been enabled");
}
@Test
void correlationLoggingToFileWhenExpectCorrelationIdTrueAndMdcContent() {
this.environment.setProperty(LoggingSystem.EXPECT_CORRELATION_ID_PROPERTY, "true");
new LoggingSystemProperties(this.environment).apply();
File file = new File(tmpDir(), "log4j2-test.log");
LogFile logFile = getLogFile(file.getPath(), null);
this.loggingSystem.setStandardConfigLocations(false);
this.loggingSystem.disableSelfInitialization();
this.loggingSystem.beforeInitialize();
this.loggingSystem.initialize(this.initializationContext, null, logFile);
MDC.setContextMap(Map.of("traceId", "01234567890123456789012345678901", "spanId", "0123456789012345"));
@ -650,7 +556,7 @@ class Log4J2LoggingSystemTests extends AbstractLoggingSystemTests { @@ -650,7 +556,7 @@ class Log4J2LoggingSystemTests extends AbstractLoggingSystemTests {
@Test
void correlationLoggingToConsoleWhenExpectCorrelationIdTrueAndMdcContent(CapturedOutput output) {
this.environment.setProperty(LoggingSystem.EXPECT_CORRELATION_ID_PROPERTY, "true");
this.loggingSystem.setStandardConfigLocations(false);
this.loggingSystem.disableSelfInitialization();
this.loggingSystem.beforeInitialize();
this.loggingSystem.initialize(this.initializationContext, null, null);
MDC.setContextMap(Map.of("traceId", "01234567890123456789012345678901", "spanId", "0123456789012345"));
@ -662,7 +568,7 @@ class Log4J2LoggingSystemTests extends AbstractLoggingSystemTests { @@ -662,7 +568,7 @@ class Log4J2LoggingSystemTests extends AbstractLoggingSystemTests {
@Test
void correlationLoggingToConsoleWhenExpectCorrelationIdFalseAndMdcContent(CapturedOutput output) {
this.environment.setProperty(LoggingSystem.EXPECT_CORRELATION_ID_PROPERTY, "false");
this.loggingSystem.setStandardConfigLocations(false);
this.loggingSystem.disableSelfInitialization();
this.loggingSystem.beforeInitialize();
this.loggingSystem.initialize(this.initializationContext, null, null);
MDC.setContextMap(Map.of("traceId", "01234567890123456789012345678901", "spanId", "0123456789012345"));
@ -673,7 +579,7 @@ class Log4J2LoggingSystemTests extends AbstractLoggingSystemTests { @@ -673,7 +579,7 @@ class Log4J2LoggingSystemTests extends AbstractLoggingSystemTests {
@Test
void correlationLoggingToConsoleWhenExpectCorrelationIdTrueAndNoMdcContent(CapturedOutput output) {
this.environment.setProperty(LoggingSystem.EXPECT_CORRELATION_ID_PROPERTY, "true");
this.loggingSystem.setStandardConfigLocations(false);
this.loggingSystem.disableSelfInitialization();
this.loggingSystem.beforeInitialize();
this.loggingSystem.initialize(this.initializationContext, null, null);
this.logger.info("Hello world");
@ -684,7 +590,7 @@ class Log4J2LoggingSystemTests extends AbstractLoggingSystemTests { @@ -684,7 +590,7 @@ class Log4J2LoggingSystemTests extends AbstractLoggingSystemTests {
@Test
void correlationLoggingToConsoleWhenHasCorrelationPattern(CapturedOutput output) {
this.environment.setProperty("logging.pattern.correlation", "%correlationId{spanId(0),traceId(0)}");
this.loggingSystem.setStandardConfigLocations(false);
this.loggingSystem.disableSelfInitialization();
this.loggingSystem.beforeInitialize();
this.loggingSystem.initialize(this.initializationContext, null, null);
MDC.setContextMap(Map.of("traceId", "01234567890123456789012345678901", "spanId", "0123456789012345"));
@ -696,7 +602,7 @@ class Log4J2LoggingSystemTests extends AbstractLoggingSystemTests { @@ -696,7 +602,7 @@ class Log4J2LoggingSystemTests extends AbstractLoggingSystemTests {
@Test
void applicationNameLoggingToConsoleWhenHasApplicationName(CapturedOutput output) {
this.environment.setProperty("spring.application.name", "myapp");
this.loggingSystem.setStandardConfigLocations(false);
this.loggingSystem.disableSelfInitialization();
this.loggingSystem.beforeInitialize();
this.loggingSystem.initialize(this.initializationContext, null, null);
this.logger.info("Hello world");
@ -706,7 +612,7 @@ class Log4J2LoggingSystemTests extends AbstractLoggingSystemTests { @@ -706,7 +612,7 @@ class Log4J2LoggingSystemTests extends AbstractLoggingSystemTests {
@Test
void applicationNameLoggingToConsoleWhenHasApplicationNameWithParenthesis(CapturedOutput output) {
this.environment.setProperty("spring.application.name", "myapp (dev)");
this.loggingSystem.setStandardConfigLocations(false);
this.loggingSystem.disableSelfInitialization();
this.loggingSystem.beforeInitialize();
this.loggingSystem.initialize(this.initializationContext, null, null);
this.logger.info("Hello world");
@ -717,7 +623,7 @@ class Log4J2LoggingSystemTests extends AbstractLoggingSystemTests { @@ -717,7 +623,7 @@ class Log4J2LoggingSystemTests extends AbstractLoggingSystemTests {
void applicationNameLoggingToConsoleWhenDisabled(CapturedOutput output) {
this.environment.setProperty("spring.application.name", "myapp");
this.environment.setProperty("logging.include-application-name", "false");
this.loggingSystem.setStandardConfigLocations(false);
this.loggingSystem.disableSelfInitialization();
this.loggingSystem.beforeInitialize();
this.loggingSystem.initialize(this.initializationContext, null, null);
this.logger.info("Hello world");
@ -732,7 +638,7 @@ class Log4J2LoggingSystemTests extends AbstractLoggingSystemTests { @@ -732,7 +638,7 @@ class Log4J2LoggingSystemTests extends AbstractLoggingSystemTests {
new LoggingSystemProperties(this.environment).apply();
File file = new File(tmpDir(), "log4j2-test.log");
LogFile logFile = getLogFile(file.getPath(), null);
this.loggingSystem.setStandardConfigLocations(false);
this.loggingSystem.disableSelfInitialization();
this.loggingSystem.beforeInitialize();
this.loggingSystem.initialize(this.initializationContext, null, logFile);
this.logger.info("Hello world");
@ -745,7 +651,7 @@ class Log4J2LoggingSystemTests extends AbstractLoggingSystemTests { @@ -745,7 +651,7 @@ class Log4J2LoggingSystemTests extends AbstractLoggingSystemTests {
new LoggingSystemProperties(this.environment).apply();
File file = new File(tmpDir(), "log4j2-test.log");
LogFile logFile = getLogFile(file.getPath(), null);
this.loggingSystem.setStandardConfigLocations(false);
this.loggingSystem.disableSelfInitialization();
this.loggingSystem.beforeInitialize();
this.loggingSystem.initialize(this.initializationContext, null, logFile);
this.logger.info("Hello world");
@ -759,7 +665,7 @@ class Log4J2LoggingSystemTests extends AbstractLoggingSystemTests { @@ -759,7 +665,7 @@ class Log4J2LoggingSystemTests extends AbstractLoggingSystemTests {
new LoggingSystemProperties(this.environment).apply();
File file = new File(tmpDir(), "log4j2-test.log");
LogFile logFile = getLogFile(file.getPath(), null);
this.loggingSystem.setStandardConfigLocations(false);
this.loggingSystem.disableSelfInitialization();
this.loggingSystem.beforeInitialize();
this.loggingSystem.initialize(this.initializationContext, null, logFile);
this.logger.info("Hello world");
@ -771,7 +677,7 @@ class Log4J2LoggingSystemTests extends AbstractLoggingSystemTests { @@ -771,7 +677,7 @@ class Log4J2LoggingSystemTests extends AbstractLoggingSystemTests {
@Test
void applicationGroupLoggingToConsoleWhenHasApplicationGroup(CapturedOutput output) {
this.environment.setProperty("spring.application.group", "mygroup");
this.loggingSystem.setStandardConfigLocations(false);
this.loggingSystem.disableSelfInitialization();
this.loggingSystem.beforeInitialize();
this.loggingSystem.initialize(this.initializationContext, null, null);
this.logger.info("Hello world");
@ -781,7 +687,7 @@ class Log4J2LoggingSystemTests extends AbstractLoggingSystemTests { @@ -781,7 +687,7 @@ class Log4J2LoggingSystemTests extends AbstractLoggingSystemTests {
@Test
void applicationGroupLoggingToConsoleWhenHasApplicationGroupWithParenthesis(CapturedOutput output) {
this.environment.setProperty("spring.application.group", "mygroup (dev)");
this.loggingSystem.setStandardConfigLocations(false);
this.loggingSystem.disableSelfInitialization();
this.loggingSystem.beforeInitialize();
this.loggingSystem.initialize(this.initializationContext, null, null);
this.logger.info("Hello world");
@ -792,7 +698,7 @@ class Log4J2LoggingSystemTests extends AbstractLoggingSystemTests { @@ -792,7 +698,7 @@ class Log4J2LoggingSystemTests extends AbstractLoggingSystemTests {
void applicationGroupLoggingToConsoleWhenDisabled(CapturedOutput output) {
this.environment.setProperty("spring.application.group", "mygroup");
this.environment.setProperty("logging.include-application-group", "false");
this.loggingSystem.setStandardConfigLocations(false);
this.loggingSystem.disableSelfInitialization();
this.loggingSystem.beforeInitialize();
this.loggingSystem.initialize(this.initializationContext, null, null);
this.logger.info("Hello world");
@ -806,7 +712,7 @@ class Log4J2LoggingSystemTests extends AbstractLoggingSystemTests { @@ -806,7 +712,7 @@ class Log4J2LoggingSystemTests extends AbstractLoggingSystemTests {
new LoggingSystemProperties(this.environment).apply();
File file = new File(tmpDir(), "log4j2-test.log");
LogFile logFile = getLogFile(file.getPath(), null);
this.loggingSystem.setStandardConfigLocations(false);
this.loggingSystem.disableSelfInitialization();
this.loggingSystem.beforeInitialize();
this.loggingSystem.initialize(this.initializationContext, null, logFile);
this.logger.info("Hello world");
@ -819,7 +725,7 @@ class Log4J2LoggingSystemTests extends AbstractLoggingSystemTests { @@ -819,7 +725,7 @@ class Log4J2LoggingSystemTests extends AbstractLoggingSystemTests {
new LoggingSystemProperties(this.environment).apply();
File file = new File(tmpDir(), "log4j2-test.log");
LogFile logFile = getLogFile(file.getPath(), null);
this.loggingSystem.setStandardConfigLocations(false);
this.loggingSystem.disableSelfInitialization();
this.loggingSystem.beforeInitialize();
this.loggingSystem.initialize(this.initializationContext, null, logFile);
this.logger.info("Hello world");
@ -833,7 +739,7 @@ class Log4J2LoggingSystemTests extends AbstractLoggingSystemTests { @@ -833,7 +739,7 @@ class Log4J2LoggingSystemTests extends AbstractLoggingSystemTests {
new LoggingSystemProperties(this.environment).apply();
File file = new File(tmpDir(), "log4j2-test.log");
LogFile logFile = getLogFile(file.getPath(), null);
this.loggingSystem.setStandardConfigLocations(false);
this.loggingSystem.disableSelfInitialization();
this.loggingSystem.beforeInitialize();
this.loggingSystem.initialize(this.initializationContext, null, logFile);
this.logger.info("Hello world");
@ -909,4 +815,27 @@ class Log4J2LoggingSystemTests extends AbstractLoggingSystemTests { @@ -909,4 +815,27 @@ class Log4J2LoggingSystemTests extends AbstractLoggingSystemTests {
}
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@WithResource(name = "log4j2-test-spring.xml",
content = """
<?xml version="1.0" encoding="UTF-8"?>
<Configuration xmlns="https://logging.apache.org/xml/ns"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="https://logging.apache.org/xml/ns https://logging.apache.org/xml/ns/log4j-config-2.xsd"
name="SpringBoot">
<Appenders>
<Console name="CONSOLE"/>
</Appenders>
<Loggers>
<Root level="INFO">
<AppenderRef ref="CONSOLE"/>
</Root>
</Loggers>
</Configuration>
""")
private @interface WithSpringXmlResource {
}
}

5
core/spring-boot/src/test/java/org/springframework/boot/logging/log4j2/Log4J2RuntimeHintsTests.java

@ -20,8 +20,6 @@ import java.net.URL; @@ -20,8 +20,6 @@ import java.net.URL;
import java.net.URLClassLoader;
import java.util.Arrays;
import org.apache.logging.log4j.core.config.properties.PropertiesConfigurationFactory;
import org.apache.logging.log4j.core.config.yaml.YamlConfigurationFactory;
import org.apache.logging.log4j.core.impl.Log4jContextFactory;
import org.apache.logging.log4j.jul.Log4jBridgeHandler;
import org.apache.logging.log4j.jul.LogManager;
@ -52,8 +50,6 @@ class Log4J2RuntimeHintsTests { @@ -52,8 +50,6 @@ class Log4J2RuntimeHintsTests {
assertThat(reflectionHints.onType(Log4jContextFactory.class)).accepts(runtimeHints);
assertThat(reflectionHints.onType(Log4jBridgeHandler.class)).accepts(runtimeHints);
assertThat(reflectionHints.onType(LogManager.class)).accepts(runtimeHints);
assertThat(reflectionHints.onType(PropertiesConfigurationFactory.class)).accepts(runtimeHints);
assertThat(reflectionHints.onType(YamlConfigurationFactory.class)).accepts(runtimeHints);
}
@Test
@ -63,6 +59,7 @@ class Log4J2RuntimeHintsTests { @@ -63,6 +59,7 @@ class Log4J2RuntimeHintsTests {
.accepts(runtimeHints);
assertThat(resourceHints.forResource("org/springframework/boot/logging/log4j2/log4j2-file.xml"))
.accepts(runtimeHints);
assertThat(resourceHints.forResource("log4j2.springboot")).accepts(runtimeHints);
}
@Test

44
core/spring-boot/src/test/java/org/springframework/boot/logging/log4j2/SpringProfileArbiterTests.java

@ -22,16 +22,13 @@ import java.lang.annotation.RetentionPolicy; @@ -22,16 +22,13 @@ import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import java.util.Set;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.apache.logging.log4j.core.LoggerContext;
import org.apache.logging.log4j.core.config.Configuration;
import org.apache.logging.log4j.core.config.Reconfigurable;
import org.apache.logging.log4j.util.PropertiesUtil;
import org.apache.logging.log4j.util.PropertySource;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.TestInfo;
import org.junit.jupiter.api.extension.ExtendWith;
import org.springframework.boot.logging.LoggingInitializationContext;
@ -57,7 +54,7 @@ class SpringProfileArbiterTests { @@ -57,7 +54,7 @@ class SpringProfileArbiterTests {
private CapturedOutput output;
private final TestLog4J2LoggingSystem loggingSystem = new TestLog4J2LoggingSystem();
private TestLog4J2LoggingSystem loggingSystem;
private final MockEnvironment environment = new MockEnvironment();
@ -66,24 +63,17 @@ class SpringProfileArbiterTests { @@ -66,24 +63,17 @@ class SpringProfileArbiterTests {
private Logger logger;
private Configuration configuration;
@BeforeEach
void setup(CapturedOutput output) {
void setup(CapturedOutput output, TestInfo testInfo) {
this.output = output;
LoggerContext loggerContext = (LoggerContext) LogManager.getContext(false);
this.configuration = loggerContext.getConfiguration();
this.loggingSystem.cleanUp();
this.logger = LogManager.getLogger(getClass());
this.loggingSystem = new TestLog4J2LoggingSystem(testInfo.getDisplayName());
this.logger = this.loggingSystem.getLoggerContext().getLogger(getClass().getName());
cleanUpPropertySources();
}
@AfterEach
void cleanUp() {
this.loggingSystem.cleanUp();
LoggerContext loggerContext = (LoggerContext) LogManager.getContext(false);
loggerContext.stop();
loggerContext.start(((Reconfigurable) this.configuration).reconfigure());
cleanUpPropertySources();
}
@ -170,11 +160,11 @@ class SpringProfileArbiterTests { @@ -170,11 +160,11 @@ class SpringProfileArbiterTests {
@WithResource(name = "multi-profile-names.xml", content = """
<?xml version="1.0" encoding="UTF-8"?>
<Configuration>
<SpringProfile name="production, test">
<Loggers>
<Loggers>
<SpringProfile name="production, test">
<Logger name="org.springframework.boot.logging.log4j2" level="TRACE" />
</Loggers>
</SpringProfile>
</SpringProfile>
</Loggers>
</Configuration>
""")
private @interface WithMultiProfileNamesXmlResource {
@ -186,11 +176,11 @@ class SpringProfileArbiterTests { @@ -186,11 +176,11 @@ class SpringProfileArbiterTests {
@WithResource(name = "profile-expression.xml", content = """
<?xml version="1.0" encoding="UTF-8"?>
<Configuration>
<SpringProfile name="production | test">
<Loggers>
<Loggers>
<SpringProfile name="production | test">
<Logger name="org.springframework.boot.logging.log4j2" level="TRACE" />
</Loggers>
</SpringProfile>
</SpringProfile>
</Loggers>
</Configuration>
""")
private @interface WithProfileExpressionXmlResource {
@ -202,11 +192,11 @@ class SpringProfileArbiterTests { @@ -202,11 +192,11 @@ class SpringProfileArbiterTests {
@WithResource(name = "production-profile.xml", content = """
<?xml version="1.0" encoding="UTF-8"?>
<Configuration>
<SpringProfile name="production">
<Loggers>
<Loggers>
<SpringProfile name="production">
<Logger name="org.springframework.boot.logging.log4j2" level="TRACE" />
</Loggers>
</SpringProfile>
</SpringProfile>
</Loggers>
</Configuration>
""")
private @interface WithProductionProfileXmlResource {

41
core/spring-boot/src/test/java/org/springframework/boot/logging/log4j2/TestLog4J2LoggingSystem.java

@ -16,54 +16,31 @@ @@ -16,54 +16,31 @@
package org.springframework.boot.logging.log4j2;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.core.LoggerContext;
import org.apache.logging.log4j.core.config.Configuration;
import org.jspecify.annotations.Nullable;
class TestLog4J2LoggingSystem extends Log4J2LoggingSystem {
private final List<String> availableClasses = new ArrayList<>();
private String @Nullable [] standardConfigLocations;
private boolean disableSelfInitialization = false;
TestLog4J2LoggingSystem() {
super(TestLog4J2LoggingSystem.class.getClassLoader());
TestLog4J2LoggingSystem(String contextName) {
// Tests add resources to the thread context classloader
super(Thread.currentThread().getContextClassLoader(), new LoggerContext(contextName));
getLoggerContext().start();
}
Configuration getConfiguration() {
return getLoggerContext().getConfiguration();
}
private LoggerContext getLoggerContext() {
return (LoggerContext) LogManager.getContext(false);
}
@Override
protected boolean isClassAvailable(ClassLoader classLoader, String className) {
return this.availableClasses.contains(className);
}
void availableClasses(String... classNames) {
Collections.addAll(this.availableClasses, classNames);
}
@Override
protected String[] getStandardConfigLocations() {
return (this.standardConfigLocations != null) ? this.standardConfigLocations
: super.getStandardConfigLocations();
}
void setStandardConfigLocations(boolean standardConfigLocations) {
this.standardConfigLocations = (!standardConfigLocations) ? new String[0] : null;
protected @Nullable String getSelfInitializationConfig() {
return this.disableSelfInitialization ? null : super.getSelfInitializationConfig();
}
void setStandardConfigLocations(String[] standardConfigLocations) {
this.standardConfigLocations = standardConfigLocations;
void disableSelfInitialization() {
this.disableSelfInitialization = true;
}
}

Loading…
Cancel
Save