Skip to content

Commit 08819f0

Browse files
committed
Merge pull request #47424 from ppkarwasz
* pr/47424: Polish 'Simplify Log4J2LoggingSystem' Simplify Log4J2LoggingSystem Closes gh-47424
2 parents 1526351 + 1a8402e commit 08819f0

File tree

6 files changed

+171
-383
lines changed

6 files changed

+171
-383
lines changed

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

Lines changed: 75 additions & 173 deletions
Original file line numberDiff line numberDiff line change
@@ -18,9 +18,6 @@
1818

1919
import java.io.FileNotFoundException;
2020
import java.io.IOException;
21-
import java.io.InputStream;
22-
import java.net.URL;
23-
import java.net.URLConnection;
2421
import java.util.ArrayList;
2522
import java.util.Collections;
2623
import java.util.LinkedHashMap;
@@ -37,15 +34,11 @@
3734
import org.apache.logging.log4j.core.LoggerContext;
3835
import org.apache.logging.log4j.core.config.AbstractConfiguration;
3936
import org.apache.logging.log4j.core.config.Configuration;
37+
import org.apache.logging.log4j.core.config.ConfigurationException;
4038
import org.apache.logging.log4j.core.config.ConfigurationFactory;
41-
import org.apache.logging.log4j.core.config.ConfigurationSource;
4239
import org.apache.logging.log4j.core.config.LoggerConfig;
4340
import org.apache.logging.log4j.core.config.composite.CompositeConfiguration;
4441
import org.apache.logging.log4j.core.filter.DenyAllFilter;
45-
import org.apache.logging.log4j.core.net.UrlConnectionFactory;
46-
import org.apache.logging.log4j.core.net.ssl.SslConfiguration;
47-
import org.apache.logging.log4j.core.net.ssl.SslConfigurationFactory;
48-
import org.apache.logging.log4j.core.util.AuthorizationProvider;
4942
import org.apache.logging.log4j.core.util.NameUtil;
5043
import org.apache.logging.log4j.jul.Log4jBridgeHandler;
5144
import org.apache.logging.log4j.status.StatusConsoleListener;
@@ -72,7 +65,6 @@
7265
import org.springframework.core.io.ResourceLoader;
7366
import org.springframework.util.Assert;
7467
import org.springframework.util.ClassUtils;
75-
import org.springframework.util.CollectionUtils;
7668
import org.springframework.util.StringUtils;
7769

7870
/**
@@ -100,43 +92,6 @@ public class Log4J2LoggingSystem extends AbstractLoggingSystem {
10092
*/
10193
static final String LOG4J_LOG_MANAGER = "org.apache.logging.log4j.jul.LogManager";
10294

103-
/**
104-
* JSON tree parser used by Log4j 2 (optional dependency).
105-
*/
106-
static final String JSON_TREE_PARSER_V2 = "com.fasterxml.jackson.databind.ObjectMapper";
107-
108-
/**
109-
* JSON tree parser embedded in Log4j 3.
110-
*/
111-
static final String JSON_TREE_PARSER_V3 = "org.apache.logging.log4j.kit.json.JsonReader";
112-
113-
/**
114-
* Configuration factory for properties files (Log4j 2).
115-
*/
116-
static final String PROPS_CONFIGURATION_FACTORY_V2 = "org.apache.logging.log4j.core.config.properties.PropertiesConfigurationFactory";
117-
118-
/**
119-
* Configuration factory for properties files (Log4j 3, optional dependency).
120-
*/
121-
static final String PROPS_CONFIGURATION_FACTORY_V3 = "org.apache.logging.log4j.config.properties.JavaPropsConfigurationFactory";
122-
123-
/**
124-
* YAML tree parser used by Log4j 2 (optional dependency).
125-
*/
126-
static final String YAML_TREE_PARSER_V2 = "com.fasterxml.jackson.dataformat.yaml.YAMLMapper";
127-
128-
/**
129-
* Configuration factory for YAML files (Log4j 2, embedded).
130-
*/
131-
static final String YAML_CONFIGURATION_FACTORY_V2 = "org.apache.logging.log4j.core.config.yaml.YamlConfigurationFactory";
132-
133-
/**
134-
* Configuration factory for YAML files (Log4j 3, optional dependency).
135-
*/
136-
static final String YAML_CONFIGURATION_FACTORY_V3 = "org.apache.logging.log4j.config.yaml.YamlConfigurationFactory";
137-
138-
private static final SpringEnvironmentPropertySource propertySource = new SpringEnvironmentPropertySource();
139-
14095
static final String ENVIRONMENT_KEY = Conventions.getQualifiedAttributeName(Log4J2LoggingSystem.class,
14196
"environment");
14297

@@ -157,78 +112,60 @@ public class Log4J2LoggingSystem extends AbstractLoggingSystem {
157112

158113
private static final Filter FILTER = DenyAllFilter.newBuilder().build();
159114

160-
public Log4J2LoggingSystem(ClassLoader classLoader) {
115+
private static final SpringEnvironmentPropertySource propertySource = new SpringEnvironmentPropertySource();
116+
117+
private static final org.apache.logging.log4j.Logger statusLogger = StatusLogger.getLogger();
118+
119+
private final LoggerContext loggerContext;
120+
121+
/**
122+
* Create a new {@link Log4J2LoggingSystem} instance.
123+
* @param classLoader the class loader to use.
124+
* @param loggerContext the {@link LoggerContext} to use.
125+
*/
126+
Log4J2LoggingSystem(ClassLoader classLoader, LoggerContext loggerContext) {
161127
super(classLoader);
128+
this.loggerContext = loggerContext;
162129
}
163130

164131
@Override
165132
protected String[] getStandardConfigLocations() {
166-
List<String> locations = new ArrayList<>();
167-
addLocationsFromProperties(locations);
168-
addStandardLocations(locations);
169-
return StringUtils.toStringArray(locations);
170-
}
171-
172-
private void addLocationsFromProperties(List<String> locations) {
173-
for (String property : List.of("log4j2.configurationFile", "log4j.configuration.location")) {
174-
String propertyDefinedLocation = PropertiesUtil.getProperties().getStringProperty(property);
175-
if (propertyDefinedLocation != null) {
176-
locations.add(propertyDefinedLocation);
177-
}
178-
}
133+
// With Log4J2 we use the ConfigurationFactory
134+
throw new IllegalStateException("Standard config locations cannot be used with Log4J2");
179135
}
180136

181-
private void addStandardLocations(List<String> locations) {
182-
LoggerContext loggerContext = getLoggerContext();
183-
String contextName = loggerContext.getName();
184-
List<String> extensions = getStandardConfigExtensions();
185-
addLocation(locations, "log4j2-test" + contextName, extensions);
186-
addLocation(locations, "log4j2-test", extensions);
187-
addLocation(locations, "log4j2" + contextName, extensions);
188-
addLocation(locations, "log4j2", extensions);
137+
@Override
138+
protected @Nullable String getSelfInitializationConfig() {
139+
return getConfigLocation(getLoggerContext().getConfiguration());
189140
}
190141

191-
private List<String> getStandardConfigExtensions() {
192-
List<String> extensions = new ArrayList<>();
193-
// These classes need to be visible by the classloader that loads Log4j Core.
194-
ClassLoader classLoader = LoggerContext.class.getClassLoader();
195-
// The order of the extensions corresponds to the order in which Log4j Core 2 and
196-
// 3 will try to load them, in decreasing value of @Order.
197-
if (isPresent(classLoader, PROPS_CONFIGURATION_FACTORY_V2)
198-
|| isPresent(classLoader, PROPS_CONFIGURATION_FACTORY_V3)) {
199-
extensions.add(".properties");
200-
}
201-
if (isPresent(classLoader, YAML_CONFIGURATION_FACTORY_V2, YAML_TREE_PARSER_V2)
202-
|| isPresent(classLoader, YAML_CONFIGURATION_FACTORY_V3)) {
203-
Collections.addAll(extensions, ".yaml", ".yml");
204-
}
205-
if (isPresent(classLoader, JSON_TREE_PARSER_V2) || isPresent(classLoader, JSON_TREE_PARSER_V3)) {
206-
Collections.addAll(extensions, ".json", ".jsn");
142+
@Override
143+
protected @Nullable String getSpringInitializationConfig() {
144+
ConfigurationFactory configurationFactory = ConfigurationFactory.getInstance();
145+
try {
146+
Configuration springConfiguration = configurationFactory.getConfiguration(getLoggerContext(), "-spring",
147+
null, getClassLoader());
148+
String configLocation = getConfigLocation(springConfiguration);
149+
return (configLocation != null && configLocation.contains("-spring")) ? configLocation : null;
207150
}
208-
extensions.add(".xml");
209-
return extensions;
210-
}
211-
212-
private void addLocation(List<String> locations, String location, List<String> extensions) {
213-
extensions.forEach((extension) -> locations.add(location + extension));
214-
}
215-
216-
private boolean isPresent(ClassLoader classLoader, String... classNames) {
217-
for (String className : classNames) {
218-
if (!isClassAvailable(classLoader, className)) {
219-
return false;
220-
}
151+
catch (ConfigurationException ex) {
152+
statusLogger.warn("Could not load Spring-specific Log4j Core configuration", ex);
153+
return null;
221154
}
222-
return true;
223-
}
224-
225-
protected boolean isClassAvailable(ClassLoader classLoader, String className) {
226-
return ClassUtils.isPresent(className, classLoader);
227155
}
228156

229-
@Deprecated(since = "4.0.0", forRemoval = true)
230-
protected boolean isClassAvailable(String className) {
231-
return ClassUtils.isPresent(className, getClassLoader());
157+
/**
158+
* Return the configuration location. The result may be:
159+
* <ul>
160+
* <li>{@code null}: if DefaultConfiguration is used (no explicit config loaded)</li>
161+
* <li>A file path: if provided explicitly by the user</li>
162+
* <li>A URI: if loaded from the classpath default or a custom location</li>
163+
* </ul>
164+
* @param configuration the source configuration
165+
* @return the config location or {@code null}
166+
*/
167+
private @Nullable String getConfigLocation(Configuration configuration) {
168+
return configuration.getConfigurationSource().getLocation();
232169
}
233170

234171
@Override
@@ -335,7 +272,7 @@ private void load(LoggingInitializationContext initializationContext, String loc
335272
Environment environment = initializationContext.getEnvironment();
336273
Assert.state(environment != null, "'environment' must not be null");
337274
applySystemProperties(environment, logFile);
338-
loadConfiguration(location, logFile, overrides);
275+
reconfigure(location, overrides);
339276
}
340277

341278
private List<String> getOverrides(LoggingInitializationContext initializationContext) {
@@ -346,66 +283,51 @@ private List<String> getOverrides(LoggingInitializationContext initializationCon
346283
return overrides.orElse(Collections.emptyList());
347284
}
348285

349-
/**
350-
* Load the configuration from the given {@code location}, creating a composite using
351-
* the configuration from the given {@code overrides}.
352-
* @param location the location
353-
* @param logFile log file configuration
354-
* @param overrides the overriding locations
355-
* @since 2.6.0
356-
*/
357-
protected void loadConfiguration(String location, @Nullable LogFile logFile, List<String> overrides) {
286+
private void reconfigure(String location, List<String> overrides) {
358287
Assert.notNull(location, "'location' must not be null");
359288
try {
360289
List<Configuration> configurations = new ArrayList<>();
361-
LoggerContext context = getLoggerContext();
362-
ResourceLoader resourceLoader = ApplicationResourceLoader.get();
363-
configurations.add(load(resourceLoader.getResource(location), context));
290+
ResourceLoader resourceLoader = ApplicationResourceLoader.get(getClassLoader());
291+
configurations.add(load(resourceLoader, location));
364292
for (String override : overrides) {
365-
Configuration overrideConfiguration = loadOverride(resourceLoader, override, context);
293+
Configuration overrideConfiguration = loadOverride(resourceLoader, override);
366294
if (overrideConfiguration != null) {
367295
configurations.add(overrideConfiguration);
368296
}
369297
}
370-
context.reconfigure(mergeConfigurations(configurations));
298+
this.loggerContext.reconfigure(mergeConfigurations(configurations));
371299
}
372300
catch (Exception ex) {
373-
throw new IllegalStateException("Could not initialize Log4J2 logging from " + location, ex);
301+
throw new IllegalStateException("Could not initialize Log4J2 logging from %s%s".formatted(location,
302+
(overrides.isEmpty() ? "" : " with overrides " + overrides)), ex);
374303
}
375304
}
376305

377-
private Configuration load(Resource resource, LoggerContext context) throws IOException {
378-
ConfigurationFactory factory = ConfigurationFactory.getInstance();
379-
if (resource.isFile()) {
380-
try (InputStream inputStream = resource.getInputStream()) {
381-
return factory.getConfiguration(context, new ConfigurationSource(inputStream, resource.getFile()));
382-
}
383-
}
384-
URL url = resource.getURL();
385-
AuthorizationProvider authorizationProvider = ConfigurationFactory
386-
.authorizationProvider(PropertiesUtil.getProperties());
387-
SslConfiguration sslConfiguration = url.getProtocol().equals("https")
388-
? SslConfigurationFactory.getSslConfiguration() : null;
389-
URLConnection connection = UrlConnectionFactory.createConnection(url, 0, sslConfiguration,
390-
authorizationProvider);
391-
try (InputStream inputStream = connection.getInputStream()) {
392-
return factory.getConfiguration(context,
393-
new ConfigurationSource(inputStream, url, connection.getLastModified()));
306+
private Configuration load(ResourceLoader resourceLoader, String location) throws IOException {
307+
ConfigurationFactory configurationFactory = ConfigurationFactory.getInstance();
308+
Resource resource = resourceLoader.getResource(location);
309+
Configuration configuration = configurationFactory.getConfiguration(getLoggerContext(), null, resource.getURI(),
310+
getClassLoader());
311+
// The error handling in Log4j Core 2.25.x is not consistent, some loading and
312+
// parsing errors result in a null configuration, others in an exception.
313+
if (configuration == null) {
314+
throw new ConfigurationException("Could not load Log4j Core configuration from " + location);
394315
}
316+
return configuration;
395317
}
396318

397-
private @Nullable Configuration loadOverride(ResourceLoader resourceLoader, String location, LoggerContext context)
398-
throws IOException {
319+
private @Nullable Configuration loadOverride(ResourceLoader resourceLoader, String location) throws IOException {
399320
if (location.startsWith(OPTIONAL_PREFIX)) {
400-
Resource resource = resourceLoader.getResource(location.substring(OPTIONAL_PREFIX.length()));
321+
String actualLocation = location.substring(OPTIONAL_PREFIX.length());
322+
Resource resource = resourceLoader.getResource(actualLocation);
401323
try {
402-
return (resource.exists()) ? load(resource, context) : null;
324+
return (resource.exists()) ? load(resourceLoader, actualLocation) : null;
403325
}
404326
catch (FileNotFoundException ex) {
405327
return null;
406328
}
407329
}
408-
return load(resourceLoader.getResource(location), context);
330+
return load(resourceLoader, location);
409331
}
410332

411333
private Configuration mergeConfigurations(List<Configuration> configurations) {
@@ -417,33 +339,9 @@ private Configuration mergeConfigurations(List<Configuration> configurations) {
417339

418340
@Override
419341
protected void reinitialize(LoggingInitializationContext initializationContext) {
420-
List<String> overrides = getOverrides(initializationContext);
421-
if (!CollectionUtils.isEmpty(overrides)) {
422-
reinitializeWithOverrides(overrides);
423-
}
424-
else {
425-
LoggerContext context = getLoggerContext();
426-
context.reconfigure();
427-
}
428-
}
429-
430-
private void reinitializeWithOverrides(List<String> overrides) {
431-
LoggerContext context = getLoggerContext();
432-
List<Configuration> configurations = new ArrayList<>();
433-
configurations.add(context.getConfiguration());
434-
ResourceLoader resourceLoader = ApplicationResourceLoader.get();
435-
for (String override : overrides) {
436-
try {
437-
Configuration overrideConfiguration = loadOverride(resourceLoader, override, context);
438-
if (overrideConfiguration != null) {
439-
configurations.add(overrideConfiguration);
440-
}
441-
}
442-
catch (IOException ex) {
443-
throw new RuntimeException("Failed to load overriding configuration from '" + override + "'", ex);
444-
}
445-
}
446-
context.reconfigure(mergeConfigurations(configurations));
342+
String currentLocation = getSelfInitializationConfig();
343+
Assert.notNull(currentLocation, "'currentLocation' must not be null");
344+
load(initializationContext, currentLocation, null);
447345
}
448346

449347
@Override
@@ -584,8 +482,8 @@ public void cleanUp() {
584482
return configuration.getLoggers().get(name);
585483
}
586484

587-
private LoggerContext getLoggerContext() {
588-
return (LoggerContext) LogManager.getContext(false);
485+
LoggerContext getLoggerContext() {
486+
return this.loggerContext;
589487
}
590488

591489
private boolean isAlreadyInitialized(LoggerContext loggerContext) {
@@ -630,7 +528,11 @@ public static class Factory implements LoggingSystemFactory {
630528
@Override
631529
public @Nullable LoggingSystem getLoggingSystem(ClassLoader classLoader) {
632530
if (PRESENT) {
633-
return new Log4J2LoggingSystem(classLoader);
531+
org.apache.logging.log4j.spi.LoggerContext spiLoggerContext = LogManager.getContext(classLoader, false);
532+
Assert.state(spiLoggerContext instanceof LoggerContext, "");
533+
if (spiLoggerContext instanceof LoggerContext coreLoggerContext) {
534+
return new Log4J2LoggingSystem(classLoader, coreLoggerContext);
535+
}
634536
}
635537
return null;
636538
}

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

Lines changed: 0 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -44,13 +44,6 @@ private void registerLog4j2Hints(RuntimeHints hints, @Nullable ClassLoader class
4444
hints.resources().registerPattern("org/springframework/boot/logging/log4j2/log4j2-file.xml");
4545
hints.resources().registerPattern("log4j2.springboot");
4646
// Declares the types that Log4j2LoggingSystem checks for existence reflectively.
47-
hints.reflection().registerTypeIfPresent(classLoader, Log4J2LoggingSystem.JSON_TREE_PARSER_V2);
48-
hints.reflection().registerTypeIfPresent(classLoader, Log4J2LoggingSystem.JSON_TREE_PARSER_V3);
49-
hints.reflection().registerTypeIfPresent(classLoader, Log4J2LoggingSystem.PROPS_CONFIGURATION_FACTORY_V2);
50-
hints.reflection().registerTypeIfPresent(classLoader, Log4J2LoggingSystem.PROPS_CONFIGURATION_FACTORY_V3);
51-
hints.reflection().registerTypeIfPresent(classLoader, Log4J2LoggingSystem.YAML_TREE_PARSER_V2);
52-
hints.reflection().registerTypeIfPresent(classLoader, Log4J2LoggingSystem.YAML_CONFIGURATION_FACTORY_V2);
53-
hints.reflection().registerTypeIfPresent(classLoader, Log4J2LoggingSystem.YAML_CONFIGURATION_FACTORY_V3);
5447
hints.reflection().registerTypeIfPresent(classLoader, Log4J2LoggingSystem.LOG4J_BRIDGE_HANDLER);
5548
hints.reflection().registerTypeIfPresent(classLoader, Log4J2LoggingSystem.LOG4J_LOG_MANAGER);
5649
}

0 commit comments

Comments
 (0)