diff --git a/spring-boot-project/spring-boot-docs/src/docs/antora/modules/how-to/pages/logging.adoc b/spring-boot-project/spring-boot-docs/src/docs/antora/modules/how-to/pages/logging.adoc index e053810ac613..4edba7af1d3a 100644 --- a/spring-boot-project/spring-boot-docs/src/docs/antora/modules/how-to/pages/logging.adoc +++ b/spring-boot-project/spring-boot-docs/src/docs/antora/modules/how-to/pages/logging.adoc @@ -201,3 +201,5 @@ To configure Log4j 2 to use an alternative configuration file format, add the ap Log4j 2 has support for combining multiple configuration files into a single composite configuration. To use this support in Spring Boot, configure configprop:logging.log4j2.config.override[] with the locations of one or more secondary configuration files. The secondary configuration files will be merged with the primary configuration, whether the primary's source is Spring Boot's defaults, a standard location such as `log4j.xml`, or the location configured by the configprop:logging.config[] property. + +NOTE: Log4j2 override configuration file locations can be prefixed with `optional:`, for example, `optional:classpath:log4j2-override.xml`, to indicate that the location is optional and should only be loaded if the resource is present. diff --git a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/logging/log4j2/Log4J2LoggingSystem.java b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/logging/log4j2/Log4J2LoggingSystem.java index 4693276a9e7d..52515a97b4fa 100644 --- a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/logging/log4j2/Log4J2LoggingSystem.java +++ b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/logging/log4j2/Log4J2LoggingSystem.java @@ -16,6 +16,7 @@ package org.springframework.boot.logging.log4j2; +import java.io.FileNotFoundException; import java.io.IOException; import java.io.InputStream; import java.net.URL; @@ -69,6 +70,7 @@ import org.springframework.core.annotation.Order; import org.springframework.core.env.Environment; 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; @@ -86,6 +88,8 @@ */ public class Log4J2LoggingSystem extends AbstractLoggingSystem { + private static final String OPTIONAL_PREFIX = "optional:"; + 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"; @@ -270,9 +274,13 @@ protected void loadConfiguration(String location, LogFile logFile, List try { List configurations = new ArrayList<>(); LoggerContext context = getLoggerContext(); - configurations.add(load(location, context)); + ResourceLoader resourceLoader = ApplicationResourceLoader.get(); + configurations.add(load(resourceLoader, location, context)); for (String override : overrides) { - configurations.add(load(override, context)); + Configuration overrideConfiguration = loadOptional(resourceLoader, override, context); + if (overrideConfiguration != null) { + configurations.add(overrideConfiguration); + } } Configuration configuration = (configurations.size() > 1) ? createComposite(configurations) : configurations.iterator().next(); @@ -283,8 +291,27 @@ protected void loadConfiguration(String location, LogFile logFile, List } } - private Configuration load(String location, LoggerContext context) throws IOException { - Resource resource = ApplicationResourceLoader.get().getResource(location); + private Configuration loadOptional(ResourceLoader resourceLoader, String location, LoggerContext context) + throws IOException { + if (location.startsWith(OPTIONAL_PREFIX)) { + Resource resource = resourceLoader.getResource(location.substring(OPTIONAL_PREFIX.length())); + try { + return (resource.exists()) ? load(resource, context) : null; + } + catch (FileNotFoundException ex) { + return null; + } + } + return load(resourceLoader, location, context); + } + + private Configuration load(ResourceLoader resourceLoader, String location, LoggerContext context) + throws IOException { + Resource resource = resourceLoader.getResource(location); + return load(resource, context); + } + + private Configuration load(Resource resource, LoggerContext context) throws IOException { ConfigurationFactory factory = ConfigurationFactory.getInstance(); if (resource.isFile()) { try (InputStream inputStream = resource.getInputStream()) { @@ -325,9 +352,13 @@ private void reinitializeWithOverrides(List overrides) { Configuration base = context.getConfiguration(); List configurations = new ArrayList<>(); configurations.add((AbstractConfiguration) base); + ResourceLoader resourceLoader = ApplicationResourceLoader.get(); for (String override : overrides) { try { - configurations.add((AbstractConfiguration) load(override, context)); + Configuration overrideConfiguration = loadOptional(resourceLoader, override, context); + if (overrideConfiguration != null) { + configurations.add((AbstractConfiguration) overrideConfiguration); + } } catch (IOException ex) { throw new RuntimeException("Failed to load overriding configuration from '" + override + "'", ex); diff --git a/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/logging/log4j2/Log4J2LoggingSystemTests.java b/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/logging/log4j2/Log4J2LoggingSystemTests.java index 2eb022235a6e..0a1397d4417a 100644 --- a/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/logging/log4j2/Log4J2LoggingSystemTests.java +++ b/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/logging/log4j2/Log4J2LoggingSystemTests.java @@ -43,6 +43,7 @@ import org.apache.logging.log4j.core.config.Reconfigurable; import org.apache.logging.log4j.core.config.composite.CompositeConfiguration; import org.apache.logging.log4j.core.config.plugins.util.PluginRegistry; +import org.apache.logging.log4j.core.config.xml.XmlConfiguration; import org.apache.logging.log4j.core.util.ShutdownCallbackRegistry; import org.apache.logging.log4j.jul.Log4jBridgeHandler; import org.apache.logging.log4j.status.StatusListener; @@ -453,6 +454,23 @@ void shutdownHookIsDisabled() { .isFalse(); } + @Test + @WithNonDefaultXmlResource + void loadOptionalOverrideConfigurationWhenDoesNotExist() { + this.environment.setProperty("logging.log4j2.config.override", "optional:classpath:override.xml"); + this.loggingSystem.initialize(this.initializationContext, "classpath:nondefault.xml", null); + assertThat(this.loggingSystem.getConfiguration()).isInstanceOf(XmlConfiguration.class); + } + + @Test + @WithNonDefaultXmlResource + @WithOverrideXmlResource + void loadOptionalOverrideConfigurationWhenExists() { + this.environment.setProperty("logging.log4j2.config.override", "optional:classpath:override.xml"); + this.loggingSystem.initialize(this.initializationContext, "classpath:nondefault.xml", null); + assertThat(this.loggingSystem.getConfiguration()).isInstanceOf(CompositeConfiguration.class); + } + @Test @WithNonDefaultXmlResource @WithOverrideXmlResource