Add support for optional Log4J2 configuration
This commit adds support for the standard 'optional:' prefix in Log4j2 override file locations, ensuring missing files are ignored without throwing exceptions. See gh-44488 Signed-off-by: Dmytro Nosan <dimanosan@gmail.com>
This commit is contained in:
committed by
Stéphane Nicoll
parent
93dc0fb224
commit
48154a393f
@@ -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 exists.
|
||||
|
||||
@@ -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;
|
||||
@@ -68,6 +69,7 @@ import org.springframework.core.Conventions;
|
||||
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;
|
||||
@@ -85,6 +87,8 @@ import org.springframework.util.StringUtils;
|
||||
*/
|
||||
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";
|
||||
@@ -269,21 +273,42 @@ public class Log4J2LoggingSystem extends AbstractLoggingSystem {
|
||||
try {
|
||||
List<Configuration> configurations = new ArrayList<>();
|
||||
LoggerContext context = getLoggerContext();
|
||||
configurations.add(load(location, context));
|
||||
ResourceLoader resourceLoader = ApplicationResourceLoader.get();
|
||||
configurations.add(loadConfiguration(resourceLoader, location, context));
|
||||
for (String override : overrides) {
|
||||
configurations.add(load(override, context));
|
||||
Configuration overrideConfiguration = loadOptionalConfiguration(resourceLoader, override, context);
|
||||
if (overrideConfiguration != null) {
|
||||
configurations.add(overrideConfiguration);
|
||||
}
|
||||
}
|
||||
Configuration configuration = (configurations.size() > 1) ? createComposite(configurations)
|
||||
: configurations.iterator().next();
|
||||
context.start(configuration);
|
||||
context.start(createCompositeConfigurationIfNecessary(configurations));
|
||||
}
|
||||
catch (Exception ex) {
|
||||
throw new IllegalStateException("Could not initialize Log4J2 logging from " + location, ex);
|
||||
}
|
||||
}
|
||||
|
||||
private Configuration load(String location, LoggerContext context) throws IOException {
|
||||
Resource resource = ApplicationResourceLoader.get().getResource(location);
|
||||
private Configuration loadOptionalConfiguration(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()) ? loadConfiguration(resource, context) : null;
|
||||
}
|
||||
catch (FileNotFoundException ex) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
return loadConfiguration(resourceLoader, location, context);
|
||||
}
|
||||
|
||||
private Configuration loadConfiguration(ResourceLoader resourceLoader, String location, LoggerContext context)
|
||||
throws IOException {
|
||||
Resource resource = resourceLoader.getResource(location);
|
||||
return loadConfiguration(resource, context);
|
||||
}
|
||||
|
||||
private Configuration loadConfiguration(Resource resource, LoggerContext context) throws IOException {
|
||||
ConfigurationFactory factory = ConfigurationFactory.getInstance();
|
||||
if (resource.isFile()) {
|
||||
try (InputStream inputStream = resource.getInputStream()) {
|
||||
@@ -303,7 +328,10 @@ public class Log4J2LoggingSystem extends AbstractLoggingSystem {
|
||||
}
|
||||
}
|
||||
|
||||
private CompositeConfiguration createComposite(List<Configuration> configurations) {
|
||||
private Configuration createCompositeConfigurationIfNecessary(List<Configuration> configurations) {
|
||||
if (configurations.size() == 1) {
|
||||
return configurations.iterator().next();
|
||||
}
|
||||
return new CompositeConfiguration(configurations.stream().map(AbstractConfiguration.class::cast).toList());
|
||||
}
|
||||
|
||||
@@ -321,19 +349,21 @@ public class Log4J2LoggingSystem extends AbstractLoggingSystem {
|
||||
|
||||
private void reinitializeWithOverrides(List<String> overrides) {
|
||||
LoggerContext context = getLoggerContext();
|
||||
Configuration base = context.getConfiguration();
|
||||
List<AbstractConfiguration> configurations = new ArrayList<>();
|
||||
configurations.add((AbstractConfiguration) base);
|
||||
List<Configuration> configurations = new ArrayList<>();
|
||||
configurations.add(context.getConfiguration());
|
||||
ResourceLoader resourceLoader = ApplicationResourceLoader.get();
|
||||
for (String override : overrides) {
|
||||
try {
|
||||
configurations.add((AbstractConfiguration) load(override, context));
|
||||
Configuration overrideConfiguration = loadOptionalConfiguration(resourceLoader, override, context);
|
||||
if (overrideConfiguration != null) {
|
||||
configurations.add(overrideConfiguration);
|
||||
}
|
||||
}
|
||||
catch (IOException ex) {
|
||||
throw new RuntimeException("Failed to load overriding configuration from '" + override + "'", ex);
|
||||
}
|
||||
}
|
||||
CompositeConfiguration composite = new CompositeConfiguration(configurations);
|
||||
context.reconfigure(composite);
|
||||
context.reconfigure(createCompositeConfigurationIfNecessary(configurations));
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -43,6 +43,7 @@ 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.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,48 @@ class Log4J2LoggingSystemTests extends AbstractLoggingSystemTests {
|
||||
.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
|
||||
void loadOptionalOverrideConfigurationWhenDoesNotExistUponReinitialization() {
|
||||
this.environment.setProperty("logging.log4j2.config.override", "optional:classpath:override.xml");
|
||||
this.loggingSystem.beforeInitialize();
|
||||
this.loggingSystem.initialize(this.initializationContext, null, null);
|
||||
assertThat(this.loggingSystem.getConfiguration()).isInstanceOf(XmlConfiguration.class);
|
||||
this.loggingSystem.cleanUp();
|
||||
this.loggingSystem.beforeInitialize();
|
||||
this.loggingSystem.initialize(this.initializationContext, null, null);
|
||||
assertThat(this.loggingSystem.getConfiguration()).isInstanceOf(XmlConfiguration.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
@WithNonDefaultXmlResource
|
||||
@WithOverrideXmlResource
|
||||
void loadOptionalOverrideConfiguration() {
|
||||
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
|
||||
@WithOverrideXmlResource
|
||||
void loadOptionalOverrideConfigurationUponReinitialization() {
|
||||
this.environment.setProperty("logging.log4j2.config.override", "optional:classpath:override.xml");
|
||||
this.loggingSystem.beforeInitialize();
|
||||
this.loggingSystem.initialize(this.initializationContext, null, null);
|
||||
assertThat(this.loggingSystem.getConfiguration()).isInstanceOf(CompositeConfiguration.class);
|
||||
this.loggingSystem.cleanUp();
|
||||
this.loggingSystem.beforeInitialize();
|
||||
this.loggingSystem.initialize(this.initializationContext, null, null);
|
||||
assertThat(this.loggingSystem.getConfiguration()).isInstanceOf(CompositeConfiguration.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
@WithNonDefaultXmlResource
|
||||
@WithOverrideXmlResource
|
||||
|
||||
Reference in New Issue
Block a user