Skip to content

Commit b87e47e

Browse files
committed
Merge branch '3.1.x'
Closes gh-37485
2 parents cc214aa + e10ca23 commit b87e47e

File tree

3 files changed

+94
-1
lines changed

3 files changed

+94
-1
lines changed

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

+17-1
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@
4343
import org.slf4j.LoggerFactory;
4444
import org.slf4j.Marker;
4545
import org.slf4j.bridge.SLF4JBridgeHandler;
46+
import org.slf4j.helpers.SubstituteLoggerFactory;
4647

4748
import org.springframework.aot.AotDetector;
4849
import org.springframework.beans.factory.aot.BeanFactoryInitializationAotContribution;
@@ -386,7 +387,7 @@ private ch.qos.logback.classic.Logger getLogger(String name) {
386387
}
387388

388389
private LoggerContext getLoggerContext() {
389-
ILoggerFactory factory = LoggerFactory.getILoggerFactory();
390+
ILoggerFactory factory = getLoggerFactory();
390391
Assert.isInstanceOf(LoggerContext.class, factory,
391392
() -> String.format(
392393
"LoggerFactory is not a Logback LoggerContext but Logback is on "
@@ -398,6 +399,21 @@ private LoggerContext getLoggerContext() {
398399
return (LoggerContext) factory;
399400
}
400401

402+
private ILoggerFactory getLoggerFactory() {
403+
ILoggerFactory factory = LoggerFactory.getILoggerFactory();
404+
while (factory instanceof SubstituteLoggerFactory) {
405+
try {
406+
Thread.sleep(50);
407+
}
408+
catch (InterruptedException ex) {
409+
Thread.currentThread().interrupt();
410+
throw new IllegalStateException("Interrupted while waiting for non-subtitute logger factory", ex);
411+
}
412+
factory = LoggerFactory.getILoggerFactory();
413+
}
414+
return factory;
415+
}
416+
401417
private Object getLocation(ILoggerFactory factory) {
402418
try {
403419
ProtectionDomain protectionDomain = factory.getClass().getProtectionDomain();
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
/*
2+
* Copyright 2012-2023 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package org.springframework.boot.logging.logback;
18+
19+
import java.util.ArrayList;
20+
import java.util.List;
21+
import java.util.concurrent.CopyOnWriteArrayList;
22+
23+
import ch.qos.logback.classic.LoggerContext;
24+
import org.junit.jupiter.api.AfterEach;
25+
import org.junit.jupiter.api.Test;
26+
import org.slf4j.LoggerFactory;
27+
28+
import org.springframework.boot.logging.LoggingSystem;
29+
import org.springframework.boot.testsupport.classpath.ForkedClassPath;
30+
31+
import static org.assertj.core.api.Assertions.assertThat;
32+
33+
/**
34+
* Tests for parallel initialization of {@link LogbackLoggingSystem} that are separate
35+
* from {@link LogbackLoggingSystemTests}. This isolation allows them to have complete
36+
* control over how and when the logging system is initialized.
37+
*
38+
* @author Andy Wilkinson
39+
*/
40+
class LogbackLoggingSystemParallelInitializationTests {
41+
42+
private final LoggingSystem loggingSystem = LoggingSystem
43+
.get(LogbackLoggingSystemParallelInitializationTests.class.getClassLoader());
44+
45+
@AfterEach
46+
void cleanUp() {
47+
this.loggingSystem.cleanUp();
48+
((LoggerContext) LoggerFactory.getILoggerFactory()).stop();
49+
}
50+
51+
@Test
52+
@ForkedClassPath
53+
void noExceptionsAreThrownWhenBeforeInitializeIsCalledInParallel() {
54+
List<Thread> threads = new ArrayList<>();
55+
List<Throwable> exceptions = new CopyOnWriteArrayList<>();
56+
for (int i = 0; i < 10; i++) {
57+
Thread thread = new Thread(() -> this.loggingSystem.beforeInitialize());
58+
thread.setUncaughtExceptionHandler((t, ex) -> exceptions.add(ex));
59+
threads.add(thread);
60+
}
61+
threads.forEach(Thread::start);
62+
threads.forEach(this::join);
63+
assertThat(exceptions).isEmpty();
64+
}
65+
66+
private void join(Thread thread) {
67+
try {
68+
thread.join();
69+
}
70+
catch (InterruptedException ex) {
71+
Thread.currentThread().interrupt();
72+
throw new RuntimeException(ex);
73+
}
74+
}
75+
76+
}

Diff for: src/checkstyle/checkstyle-suppressions.xml

+1
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
<suppress files="LogbackInitializer\.java" checks="IllegalImport" />
1111
<suppress files="LogbackLoggingSystem\.java" checks="IllegalImport" />
1212
<suppress files="LogbackLoggingSystemTests\.java" checks="IllegalImport" />
13+
<suppress files="LogbackLoggingSystemParallelInitializationTests\.java" checks="IllegalImport" />
1314
<suppress files="LogbackConfigurationAotContributionTests\.java" checks="IllegalImport" />
1415
<suppress files="MetricsAutoConfigurationMeterRegistryPostProcessorIntegrationTests\.java" checks="IllegalImport" message="LoggerFactory"/>
1516
<suppress files="SpringApplicationTests\.java" checks="FinalClass" />

0 commit comments

Comments
 (0)