Skip to content

Commit 4f17a9c

Browse files
libetlmhalbritter
authored andcommitted
Allow JoranConfigurators to be passed ahead of time in logback
See gh-33643
1 parent e5bc9a2 commit 4f17a9c

File tree

4 files changed

+82
-6
lines changed

4 files changed

+82
-6
lines changed

spring-boot-project/spring-boot/src/main/java/org/springframework/boot/logging/logback/LogbackLoggingSystem.java

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,8 @@
2020
import java.security.CodeSource;
2121
import java.security.ProtectionDomain;
2222
import java.util.ArrayList;
23+
import java.util.Collection;
24+
import java.util.Collections;
2325
import java.util.List;
2426
import java.util.Set;
2527
import java.util.logging.ConsoleHandler;
@@ -83,6 +85,8 @@ public class LogbackLoggingSystem extends AbstractLoggingSystem implements BeanF
8385

8486
private static final LogLevels<Level> LEVELS = new LogLevels<>();
8587

88+
private Collection<JoranConfigurator> configurators = Collections.emptyList();
89+
8690
static {
8791
LEVELS.map(LogLevel.TRACE, Level.TRACE);
8892
LEVELS.map(LogLevel.TRACE, Level.ALL);
@@ -184,7 +188,7 @@ public void initialize(LoggingInitializationContext initializationContext, Strin
184188
if (isAlreadyInitialized(loggerContext)) {
185189
return;
186190
}
187-
if (!initializeFromAotGeneratedArtifactsIfPossible(initializationContext, logFile)) {
191+
if (!initializeFromAotGeneratedArtifactsIfPossible(initializationContext, this.configurators, logFile)) {
188192
super.initialize(initializationContext, configLocation, logFile);
189193
}
190194
loggerContext.getTurboFilterList().remove(FILTER);
@@ -196,7 +200,7 @@ public void initialize(LoggingInitializationContext initializationContext, Strin
196200
}
197201

198202
private boolean initializeFromAotGeneratedArtifactsIfPossible(LoggingInitializationContext initializationContext,
199-
LogFile logFile) {
203+
Collection<JoranConfigurator> configurators, LogFile logFile) {
200204
if (!AotDetector.useGeneratedArtifacts()) {
201205
return false;
202206
}
@@ -205,7 +209,8 @@ private boolean initializeFromAotGeneratedArtifactsIfPossible(LoggingInitializat
205209
}
206210
LoggerContext loggerContext = getLoggerContext();
207211
stopAndReset(loggerContext);
208-
SpringBootJoranConfigurator configurator = new SpringBootJoranConfigurator(initializationContext);
212+
SpringBootJoranConfigurator configurator = new SpringBootJoranConfigurator(initializationContext,
213+
configurators);
209214
configurator.setContext(loggerContext);
210215
return configurator.configureUsingAotGeneratedArtifacts();
211216
}
@@ -260,7 +265,7 @@ protected void loadConfiguration(LoggingInitializationContext initializationCont
260265
private void configureByResourceUrl(LoggingInitializationContext initializationContext, LoggerContext loggerContext,
261266
URL url) throws JoranException {
262267
if (url.toString().endsWith("xml")) {
263-
JoranConfigurator configurator = new SpringBootJoranConfigurator(initializationContext);
268+
JoranConfigurator configurator = new SpringBootJoranConfigurator(initializationContext, this.configurators);
264269
configurator.setContext(loggerContext);
265270
configurator.doConfigure(url);
266271
}
@@ -415,6 +420,8 @@ public BeanFactoryInitializationAotContribution processAheadOfTime(ConfigurableL
415420
BeanFactoryInitializationAotContribution contribution = (BeanFactoryInitializationAotContribution) context
416421
.getObject(key);
417422
context.removeObject(key);
423+
this.configurators = beanFactory.getBeansOfType(JoranConfigurator.class).values();
424+
this.configurators.forEach((configurator) -> configurator.setContext(context));
418425
return contribution;
419426
}
420427

spring-boot-project/spring-boot/src/main/java/org/springframework/boot/logging/logback/SpringBootJoranConfigurator.java

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626
import java.lang.reflect.Method;
2727
import java.lang.reflect.Modifier;
2828
import java.util.Collection;
29+
import java.util.Collections;
2930
import java.util.HashMap;
3031
import java.util.HashSet;
3132
import java.util.Map;
@@ -78,8 +79,16 @@ class SpringBootJoranConfigurator extends JoranConfigurator {
7879

7980
private final LoggingInitializationContext initializationContext;
8081

82+
private final Collection<JoranConfigurator> configurators;
83+
8184
SpringBootJoranConfigurator(LoggingInitializationContext initializationContext) {
85+
this(initializationContext, Collections.emptyList());
86+
}
87+
88+
SpringBootJoranConfigurator(LoggingInitializationContext initializationContext,
89+
Collection<JoranConfigurator> configurators) {
8290
this.initializationContext = initializationContext;
91+
this.configurators = configurators;
8392
}
8493

8594
@Override
@@ -105,6 +114,7 @@ public void addElementSelectorAndActionAssociations(RuleStore ruleStore) {
105114
ruleStore.addRule(new ElementSelector("configuration/springProperty"), SpringPropertyAction::new);
106115
ruleStore.addRule(new ElementSelector("*/springProfile"), SpringProfileAction::new);
107116
ruleStore.addTransparentPathPart("springProfile");
117+
this.configurators.forEach((configurator) -> configurator.addElementSelectorAndActionAssociations(ruleStore));
108118
}
109119

110120
boolean configureUsingAotGeneratedArtifacts() {
@@ -124,6 +134,7 @@ public void processModel(Model model) {
124134
getContext().putObject(BeanFactoryInitializationAotContribution.class.getName(),
125135
new LogbackConfigurationAotContribution(model, getModelInterpretationContext(), getContext()));
126136
}
137+
this.configurators.forEach((configurator) -> configurator.processModel(model));
127138
}
128139

129140
private boolean isAotProcessingInProgress() {

spring-boot-project/spring-boot/src/test/java/org/springframework/boot/logging/logback/LogbackLoggingSystemTests.java

Lines changed: 47 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -33,9 +33,14 @@
3333
import ch.qos.logback.classic.Level;
3434
import ch.qos.logback.classic.Logger;
3535
import ch.qos.logback.classic.LoggerContext;
36+
import ch.qos.logback.classic.joran.JoranConfigurator;
3637
import ch.qos.logback.classic.spi.LoggerContextListener;
3738
import ch.qos.logback.core.ConsoleAppender;
3839
import ch.qos.logback.core.encoder.LayoutWrappingEncoder;
40+
import ch.qos.logback.core.joran.action.Action;
41+
import ch.qos.logback.core.joran.spi.ElementSelector;
42+
import ch.qos.logback.core.joran.spi.RuleStore;
43+
import ch.qos.logback.core.joran.spi.SaxEventInterpretationContext;
3944
import ch.qos.logback.core.rolling.RollingFileAppender;
4045
import ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy;
4146
import ch.qos.logback.core.util.StatusPrinter;
@@ -46,8 +51,10 @@
4651
import org.slf4j.ILoggerFactory;
4752
import org.slf4j.LoggerFactory;
4853
import org.slf4j.bridge.SLF4JBridgeHandler;
54+
import org.xml.sax.Attributes;
4955

5056
import org.springframework.beans.factory.aot.BeanFactoryInitializationAotContribution;
57+
import org.springframework.beans.factory.support.DefaultListableBeanFactory;
5158
import org.springframework.boot.convert.ApplicationConversionService;
5259
import org.springframework.boot.logging.AbstractLoggingSystemTests;
5360
import org.springframework.boot.logging.LogFile;
@@ -651,7 +658,8 @@ void customCharset() {
651658

652659
@Test
653660
void whenContextHasNoAotContributionThenProcessAheadOfTimeReturnsNull() {
654-
BeanFactoryInitializationAotContribution contribution = this.loggingSystem.processAheadOfTime(null);
661+
BeanFactoryInitializationAotContribution contribution = this.loggingSystem
662+
.processAheadOfTime(new DefaultListableBeanFactory());
655663
assertThat(contribution).isNull();
656664
}
657665

@@ -660,11 +668,48 @@ void whenContextHasAotContributionThenProcessAheadOfTimeClearsAndReturnsIt() {
660668
LoggerContext context = ((LoggerContext) LoggerFactory.getILoggerFactory());
661669
context.putObject(BeanFactoryInitializationAotContribution.class.getName(),
662670
mock(BeanFactoryInitializationAotContribution.class));
663-
BeanFactoryInitializationAotContribution contribution = this.loggingSystem.processAheadOfTime(null);
671+
BeanFactoryInitializationAotContribution contribution = this.loggingSystem
672+
.processAheadOfTime(new DefaultListableBeanFactory());
664673
assertThat(context.getObject(BeanFactoryInitializationAotContribution.class.getName())).isNull();
665674
assertThat(contribution).isNotNull();
666675
}
667676

677+
@Test
678+
void whenContextHasConfiguratorsThenInitializeExecutesThem(CapturedOutput output) {
679+
LoggerContext context = ((LoggerContext) LoggerFactory.getILoggerFactory());
680+
context.putObject(BeanFactoryInitializationAotContribution.class.getName(),
681+
mock(BeanFactoryInitializationAotContribution.class));
682+
DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();
683+
beanFactory.registerSingleton("joranConfigurator1", new JoranConfigurator() {
684+
@Override
685+
public void addElementSelectorAndActionAssociations(RuleStore ruleStore) {
686+
ruleStore.addRule(new ElementSelector("*/rule1"), () -> new Action() {
687+
@Override
688+
public void begin(SaxEventInterpretationContext intercon, String name, Attributes attributes) {
689+
}
690+
691+
@Override
692+
public void end(SaxEventInterpretationContext intercon, String name) {
693+
}
694+
});
695+
ruleStore.addRule(new ElementSelector("*/rule2"), () -> new Action() {
696+
@Override
697+
public void begin(SaxEventInterpretationContext intercon, String name, Attributes attributes) {
698+
}
699+
700+
@Override
701+
public void end(SaxEventInterpretationContext intercon, String name) {
702+
}
703+
});
704+
}
705+
});
706+
this.loggingSystem.processAheadOfTime(beanFactory);
707+
this.loggingSystem.beforeInitialize();
708+
initialize(this.initializationContext, "classpath:logback-custom-rules.xml", null);
709+
assertThat(output).doesNotContain("Ignoring unknown property [rule1] in [ch.qos.logback.classic.LoggerContext]")
710+
.doesNotContain("Ignoring unknown property [rule2] in [ch.qos.logback.classic.LoggerContext]");
711+
}
712+
668713
@Test // gh-33610
669714
void springProfileIfNestedWithinSecondPhaseElementSanityChecker(CapturedOutput output) {
670715
this.loggingSystem.beforeInitialize();
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
<configuration>
3+
<appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender">
4+
<encoder>
5+
<pattern>%property{LOG_FILE} [%t] ${PID:-????} %c{1}: %m%n BOOTBOOT</pattern>
6+
</encoder>
7+
</appender>
8+
<rule1/>
9+
<rule2/>
10+
<root level="INFO">
11+
<appender-ref ref="CONSOLE" />
12+
</root>
13+
</configuration>

0 commit comments

Comments
 (0)