mirror of
https://github.com/spring-projects/spring-boot.git
synced 2026-09-25 02:49:03 +00:00
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
This commit is contained in:
committed by
Phillip Webb
parent
debbec8ee1
commit
7d34320401
+99
-172
@@ -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;
|
||||
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;
|
||||
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;
|
||||
*/
|
||||
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 {
|
||||
*/
|
||||
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 {
|
||||
|
||||
private static final Filter FILTER = DenyAllFilter.newBuilder().build();
|
||||
|
||||
public Log4J2LoggingSystem(ClassLoader classLoader) {
|
||||
private final LoggerContext loggerContext;
|
||||
|
||||
/**
|
||||
* 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;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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");
|
||||
}
|
||||
|
||||
/**
|
||||
* {@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" };
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String[] getStandardConfigLocations() {
|
||||
List<String> locations = new ArrayList<>();
|
||||
addLocationsFromProperties(locations);
|
||||
addStandardLocations(locations);
|
||||
return StringUtils.toStringArray(locations);
|
||||
protected @Nullable String getSelfInitializationConfig() {
|
||||
Configuration currentConfiguration = getLoggerContext().getConfiguration();
|
||||
return getConfigLocation(currentConfiguration);
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
@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;
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
private void addLocation(List<String> locations, String location, List<String> extensions) {
|
||||
extensions.forEach((extension) -> locations.add(location + extension));
|
||||
}
|
||||
|
||||
private boolean isPresent(ClassLoader classLoader, String... classNames) {
|
||||
for (String className : classNames) {
|
||||
if (!isClassAvailable(classLoader, className)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
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 {
|
||||
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 {
|
||||
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 {
|
||||
|
||||
@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 {
|
||||
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 {
|
||||
@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 {
|
||||
@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
@@ -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);
|
||||
}
|
||||
|
||||
+69
-140
@@ -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;
|
||||
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;
|
||||
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 {
|
||||
|
||||
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 {
|
||||
"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 {
|
||||
|
||||
@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 {
|
||||
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 {
|
||||
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 {
|
||||
|
||||
@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 {
|
||||
|
||||
@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 {
|
||||
|
||||
@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 {
|
||||
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 {
|
||||
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 {
|
||||
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 {
|
||||
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 {
|
||||
@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 {
|
||||
@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 {
|
||||
@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 {
|
||||
@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 {
|
||||
@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 {
|
||||
@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 {
|
||||
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 {
|
||||
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 {
|
||||
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 {
|
||||
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 {
|
||||
@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 {
|
||||
@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 {
|
||||
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 {
|
||||
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 {
|
||||
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 {
|
||||
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 {
|
||||
|
||||
}
|
||||
|
||||
@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 {
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+1
-4
@@ -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 {
|
||||
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 {
|
||||
.accepts(runtimeHints);
|
||||
assertThat(resourceHints.forResource("org/springframework/boot/logging/log4j2/log4j2-file.xml"))
|
||||
.accepts(runtimeHints);
|
||||
assertThat(resourceHints.forResource("log4j2.springboot")).accepts(runtimeHints);
|
||||
}
|
||||
|
||||
@Test
|
||||
|
||||
+17
-27
@@ -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 {
|
||||
|
||||
private CapturedOutput output;
|
||||
|
||||
private final TestLog4J2LoggingSystem loggingSystem = new TestLog4J2LoggingSystem();
|
||||
private TestLog4J2LoggingSystem loggingSystem;
|
||||
|
||||
private final MockEnvironment environment = new MockEnvironment();
|
||||
|
||||
@@ -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 {
|
||||
@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 {
|
||||
@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 {
|
||||
@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 {
|
||||
|
||||
+9
-32
@@ -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 boolean disableSelfInitialization = false;
|
||||
|
||||
private String @Nullable [] standardConfigLocations;
|
||||
|
||||
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);
|
||||
protected @Nullable String getSelfInitializationConfig() {
|
||||
return this.disableSelfInitialization ? null : super.getSelfInitializationConfig();
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
void setStandardConfigLocations(String[] standardConfigLocations) {
|
||||
this.standardConfigLocations = standardConfigLocations;
|
||||
void disableSelfInitialization() {
|
||||
this.disableSelfInitialization = true;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user