Allow JoranConfigurators to be passed ahead of time in logback

See gh-33643
This commit is contained in:
lionel benychou 2022-12-28 18:42:31 +01:00 committed by Moritz Halbritter
parent e5bc9a2fcb
commit 4f17a9cc9d
4 changed files with 82 additions and 6 deletions

View File

@ -20,6 +20,8 @@ import java.net.URL;
import java.security.CodeSource;
import java.security.ProtectionDomain;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.Set;
import java.util.logging.ConsoleHandler;
@ -83,6 +85,8 @@ public class LogbackLoggingSystem extends AbstractLoggingSystem implements BeanF
private static final LogLevels<Level> LEVELS = new LogLevels<>();
private Collection<JoranConfigurator> configurators = Collections.emptyList();
static {
LEVELS.map(LogLevel.TRACE, Level.TRACE);
LEVELS.map(LogLevel.TRACE, Level.ALL);
@ -184,7 +188,7 @@ public class LogbackLoggingSystem extends AbstractLoggingSystem implements BeanF
if (isAlreadyInitialized(loggerContext)) {
return;
}
if (!initializeFromAotGeneratedArtifactsIfPossible(initializationContext, logFile)) {
if (!initializeFromAotGeneratedArtifactsIfPossible(initializationContext, this.configurators, logFile)) {
super.initialize(initializationContext, configLocation, logFile);
}
loggerContext.getTurboFilterList().remove(FILTER);
@ -196,7 +200,7 @@ public class LogbackLoggingSystem extends AbstractLoggingSystem implements BeanF
}
private boolean initializeFromAotGeneratedArtifactsIfPossible(LoggingInitializationContext initializationContext,
LogFile logFile) {
Collection<JoranConfigurator> configurators, LogFile logFile) {
if (!AotDetector.useGeneratedArtifacts()) {
return false;
}
@ -205,7 +209,8 @@ public class LogbackLoggingSystem extends AbstractLoggingSystem implements BeanF
}
LoggerContext loggerContext = getLoggerContext();
stopAndReset(loggerContext);
SpringBootJoranConfigurator configurator = new SpringBootJoranConfigurator(initializationContext);
SpringBootJoranConfigurator configurator = new SpringBootJoranConfigurator(initializationContext,
configurators);
configurator.setContext(loggerContext);
return configurator.configureUsingAotGeneratedArtifacts();
}
@ -260,7 +265,7 @@ public class LogbackLoggingSystem extends AbstractLoggingSystem implements BeanF
private void configureByResourceUrl(LoggingInitializationContext initializationContext, LoggerContext loggerContext,
URL url) throws JoranException {
if (url.toString().endsWith("xml")) {
JoranConfigurator configurator = new SpringBootJoranConfigurator(initializationContext);
JoranConfigurator configurator = new SpringBootJoranConfigurator(initializationContext, this.configurators);
configurator.setContext(loggerContext);
configurator.doConfigure(url);
}
@ -415,6 +420,8 @@ public class LogbackLoggingSystem extends AbstractLoggingSystem implements BeanF
BeanFactoryInitializationAotContribution contribution = (BeanFactoryInitializationAotContribution) context
.getObject(key);
context.removeObject(key);
this.configurators = beanFactory.getBeansOfType(JoranConfigurator.class).values();
this.configurators.forEach((configurator) -> configurator.setContext(context));
return contribution;
}

View File

@ -26,6 +26,7 @@ import java.io.Serializable;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
@ -78,8 +79,16 @@ class SpringBootJoranConfigurator extends JoranConfigurator {
private final LoggingInitializationContext initializationContext;
private final Collection<JoranConfigurator> configurators;
SpringBootJoranConfigurator(LoggingInitializationContext initializationContext) {
this(initializationContext, Collections.emptyList());
}
SpringBootJoranConfigurator(LoggingInitializationContext initializationContext,
Collection<JoranConfigurator> configurators) {
this.initializationContext = initializationContext;
this.configurators = configurators;
}
@Override
@ -105,6 +114,7 @@ class SpringBootJoranConfigurator extends JoranConfigurator {
ruleStore.addRule(new ElementSelector("configuration/springProperty"), SpringPropertyAction::new);
ruleStore.addRule(new ElementSelector("*/springProfile"), SpringProfileAction::new);
ruleStore.addTransparentPathPart("springProfile");
this.configurators.forEach((configurator) -> configurator.addElementSelectorAndActionAssociations(ruleStore));
}
boolean configureUsingAotGeneratedArtifacts() {
@ -124,6 +134,7 @@ class SpringBootJoranConfigurator extends JoranConfigurator {
getContext().putObject(BeanFactoryInitializationAotContribution.class.getName(),
new LogbackConfigurationAotContribution(model, getModelInterpretationContext(), getContext()));
}
this.configurators.forEach((configurator) -> configurator.processModel(model));
}
private boolean isAotProcessingInProgress() {

View File

@ -33,9 +33,14 @@ import java.util.logging.LogManager;
import ch.qos.logback.classic.Level;
import ch.qos.logback.classic.Logger;
import ch.qos.logback.classic.LoggerContext;
import ch.qos.logback.classic.joran.JoranConfigurator;
import ch.qos.logback.classic.spi.LoggerContextListener;
import ch.qos.logback.core.ConsoleAppender;
import ch.qos.logback.core.encoder.LayoutWrappingEncoder;
import ch.qos.logback.core.joran.action.Action;
import ch.qos.logback.core.joran.spi.ElementSelector;
import ch.qos.logback.core.joran.spi.RuleStore;
import ch.qos.logback.core.joran.spi.SaxEventInterpretationContext;
import ch.qos.logback.core.rolling.RollingFileAppender;
import ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy;
import ch.qos.logback.core.util.StatusPrinter;
@ -46,8 +51,10 @@ import org.junit.jupiter.api.extension.ExtendWith;
import org.slf4j.ILoggerFactory;
import org.slf4j.LoggerFactory;
import org.slf4j.bridge.SLF4JBridgeHandler;
import org.xml.sax.Attributes;
import org.springframework.beans.factory.aot.BeanFactoryInitializationAotContribution;
import org.springframework.beans.factory.support.DefaultListableBeanFactory;
import org.springframework.boot.convert.ApplicationConversionService;
import org.springframework.boot.logging.AbstractLoggingSystemTests;
import org.springframework.boot.logging.LogFile;
@ -651,7 +658,8 @@ class LogbackLoggingSystemTests extends AbstractLoggingSystemTests {
@Test
void whenContextHasNoAotContributionThenProcessAheadOfTimeReturnsNull() {
BeanFactoryInitializationAotContribution contribution = this.loggingSystem.processAheadOfTime(null);
BeanFactoryInitializationAotContribution contribution = this.loggingSystem
.processAheadOfTime(new DefaultListableBeanFactory());
assertThat(contribution).isNull();
}
@ -660,11 +668,48 @@ class LogbackLoggingSystemTests extends AbstractLoggingSystemTests {
LoggerContext context = ((LoggerContext) LoggerFactory.getILoggerFactory());
context.putObject(BeanFactoryInitializationAotContribution.class.getName(),
mock(BeanFactoryInitializationAotContribution.class));
BeanFactoryInitializationAotContribution contribution = this.loggingSystem.processAheadOfTime(null);
BeanFactoryInitializationAotContribution contribution = this.loggingSystem
.processAheadOfTime(new DefaultListableBeanFactory());
assertThat(context.getObject(BeanFactoryInitializationAotContribution.class.getName())).isNull();
assertThat(contribution).isNotNull();
}
@Test
void whenContextHasConfiguratorsThenInitializeExecutesThem(CapturedOutput output) {
LoggerContext context = ((LoggerContext) LoggerFactory.getILoggerFactory());
context.putObject(BeanFactoryInitializationAotContribution.class.getName(),
mock(BeanFactoryInitializationAotContribution.class));
DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();
beanFactory.registerSingleton("joranConfigurator1", new JoranConfigurator() {
@Override
public void addElementSelectorAndActionAssociations(RuleStore ruleStore) {
ruleStore.addRule(new ElementSelector("*/rule1"), () -> new Action() {
@Override
public void begin(SaxEventInterpretationContext intercon, String name, Attributes attributes) {
}
@Override
public void end(SaxEventInterpretationContext intercon, String name) {
}
});
ruleStore.addRule(new ElementSelector("*/rule2"), () -> new Action() {
@Override
public void begin(SaxEventInterpretationContext intercon, String name, Attributes attributes) {
}
@Override
public void end(SaxEventInterpretationContext intercon, String name) {
}
});
}
});
this.loggingSystem.processAheadOfTime(beanFactory);
this.loggingSystem.beforeInitialize();
initialize(this.initializationContext, "classpath:logback-custom-rules.xml", null);
assertThat(output).doesNotContain("Ignoring unknown property [rule1] in [ch.qos.logback.classic.LoggerContext]")
.doesNotContain("Ignoring unknown property [rule2] in [ch.qos.logback.classic.LoggerContext]");
}
@Test // gh-33610
void springProfileIfNestedWithinSecondPhaseElementSanityChecker(CapturedOutput output) {
this.loggingSystem.beforeInitialize();

View File

@ -0,0 +1,13 @@
<?xml version="1.0" encoding="UTF-8"?>
<configuration>
<appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender">
<encoder>
<pattern>%property{LOG_FILE} [%t] ${PID:-????} %c{1}: %m%n BOOTBOOT</pattern>
</encoder>
</appender>
<rule1/>
<rule2/>
<root level="INFO">
<appender-ref ref="CONSOLE" />
</root>
</configuration>