Skip to content

Commit 849242e

Browse files
committed
Merge pull request #44242 from nosan
* pr/44242: Polish "Add RuntimeHints for StackTracePrinter" Add RuntimeHints for StackTracePrinter Closes gh-44242
2 parents 9f920be + f866e20 commit 849242e

File tree

5 files changed

+121
-20
lines changed

5 files changed

+121
-20
lines changed

spring-boot-project/spring-boot/src/main/java/org/springframework/boot/logging/structured/StructuredLoggingJsonProperties.java

+13-2
Original file line numberDiff line numberDiff line change
@@ -87,8 +87,7 @@ record StackTrace(String printer, Root root, Integer maxLength, Integer maxThrow
8787
Boolean includeCommonFrames, Boolean includeHashes) {
8888

8989
StackTracePrinter createPrinter() {
90-
String name = (printer() != null) ? printer() : "";
91-
name = name.toLowerCase(Locale.getDefault()).replace("-", "");
90+
String name = sanitizePrinter();
9291
if ("loggingsystem".equals(name) || (name.isEmpty() && !hasAnyOtherProperty())) {
9392
return null;
9493
}
@@ -101,6 +100,18 @@ StackTracePrinter createPrinter() {
101100
.instantiate(printer());
102101
}
103102

103+
boolean hasCustomPrinter() {
104+
String name = sanitizePrinter();
105+
if (name.isEmpty()) {
106+
return false;
107+
}
108+
return !("loggingsystem".equals(name) || "standard".equals(name));
109+
}
110+
111+
private String sanitizePrinter() {
112+
return Objects.toString(printer(), "").toLowerCase(Locale.ROOT).replace("-", "");
113+
}
114+
104115
private boolean hasAnyOtherProperty() {
105116
return Stream.of(root(), maxLength(), maxThrowableDepth(), includeCommonFrames(), includeHashes())
106117
.anyMatch(Objects::nonNull);
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616

1717
package org.springframework.boot.logging.structured;
1818

19+
import java.util.Optional;
1920
import java.util.Set;
2021

2122
import org.springframework.aot.generate.GenerationContext;
@@ -26,17 +27,18 @@
2627
import org.springframework.beans.factory.aot.BeanFactoryInitializationAotProcessor;
2728
import org.springframework.beans.factory.aot.BeanFactoryInitializationCode;
2829
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
30+
import org.springframework.boot.logging.structured.StructuredLoggingJsonProperties.StackTrace;
2931
import org.springframework.core.env.Environment;
3032

3133
/**
3234
* {@link BeanFactoryInitializationAotProcessor} that registers {@link RuntimeHints} for
33-
* {@link StructuredLoggingJsonPropertiesJsonMembersCustomizer}.
35+
* {@link StructuredLoggingJsonProperties}.
3436
*
3537
* @author Dmytro Nosan
3638
* @author Yanming Zhou
3739
* @author Phillip Webb
3840
*/
39-
class StructuredLoggingJsonMembersCustomizerBeanFactoryInitializationAotProcessor
41+
class StructuredLoggingJsonPropertiesBeanFactoryInitializationAotProcessor
4042
implements BeanFactoryInitializationAotProcessor {
4143

4244
private static final String ENVIRONMENT_BEAN_NAME = "environment";
@@ -45,15 +47,36 @@ class StructuredLoggingJsonMembersCustomizerBeanFactoryInitializationAotProcesso
4547
public BeanFactoryInitializationAotContribution processAheadOfTime(ConfigurableListableBeanFactory beanFactory) {
4648
Environment environment = beanFactory.getBean(ENVIRONMENT_BEAN_NAME, Environment.class);
4749
StructuredLoggingJsonProperties properties = StructuredLoggingJsonProperties.get(environment);
48-
return (properties != null) ? AotContribution.get(properties.customizer()) : null;
50+
if (properties != null) {
51+
Set<Class<? extends StructuredLoggingJsonMembersCustomizer<?>>> customizers = properties.customizer();
52+
String stackTracePrinter = getCustomStackTracePrinter(properties);
53+
if (stackTracePrinter != null || !customizers.isEmpty()) {
54+
return new AotContribution(beanFactory.getBeanClassLoader(), customizers, stackTracePrinter);
55+
}
56+
}
57+
return null;
58+
}
59+
60+
private static String getCustomStackTracePrinter(StructuredLoggingJsonProperties properties) {
61+
return Optional.ofNullable(properties.stackTrace())
62+
.filter(StackTrace::hasCustomPrinter)
63+
.map(StackTrace::printer)
64+
.orElse(null);
4965
}
5066

5167
private static final class AotContribution implements BeanFactoryInitializationAotContribution {
5268

69+
private final ClassLoader classLoader;
70+
5371
private final Set<Class<? extends StructuredLoggingJsonMembersCustomizer<?>>> customizers;
5472

55-
private AotContribution(Set<Class<? extends StructuredLoggingJsonMembersCustomizer<?>>> customizers) {
73+
private final String stackTracePrinter;
74+
75+
private AotContribution(ClassLoader classLoader,
76+
Set<Class<? extends StructuredLoggingJsonMembersCustomizer<?>>> customizers, String stackTracePrinter) {
77+
this.classLoader = classLoader;
5678
this.customizers = customizers;
79+
this.stackTracePrinter = stackTracePrinter;
5780
}
5881

5982
@Override
@@ -62,10 +85,10 @@ public void applyTo(GenerationContext generationContext,
6285
ReflectionHints reflection = generationContext.getRuntimeHints().reflection();
6386
this.customizers.forEach((customizer) -> reflection.registerType(customizer,
6487
MemberCategory.INVOKE_DECLARED_CONSTRUCTORS, MemberCategory.INVOKE_PUBLIC_CONSTRUCTORS));
65-
}
66-
67-
static AotContribution get(Set<Class<? extends StructuredLoggingJsonMembersCustomizer<?>>> customizers) {
68-
return (!customizers.isEmpty()) ? new AotContribution(customizers) : null;
88+
if (this.stackTracePrinter != null) {
89+
reflection.registerTypeIfPresent(this.classLoader, this.stackTracePrinter,
90+
MemberCategory.INVOKE_DECLARED_CONSTRUCTORS, MemberCategory.INVOKE_PUBLIC_CONSTRUCTORS);
91+
}
6992
}
7093

7194
}

spring-boot-project/spring-boot/src/main/resources/META-INF/spring/aot.factories

+1-1
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ org.springframework.beans.factory.aot.BeanFactoryInitializationAotProcessor=\
2020
org.springframework.boot.context.properties.ConfigurationPropertiesBeanFactoryInitializationAotProcessor,\
2121
org.springframework.boot.env.EnvironmentPostProcessorApplicationListener.EnvironmentBeanFactoryInitializationAotProcessor,\
2222
org.springframework.boot.jackson.JsonComponentModule.JsonComponentBeanFactoryInitializationAotProcessor,\
23-
org.springframework.boot.logging.structured.StructuredLoggingJsonMembersCustomizerBeanFactoryInitializationAotProcessor
23+
org.springframework.boot.logging.structured.StructuredLoggingJsonPropertiesBeanFactoryInitializationAotProcessor
2424

2525
org.springframework.beans.factory.aot.BeanRegistrationAotProcessor=\
2626
org.springframework.boot.context.properties.ConfigurationPropertiesBeanRegistrationAotProcessor,\
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,11 @@
1616

1717
package org.springframework.boot.logging.structured;
1818

19+
import java.io.IOException;
20+
1921
import org.junit.jupiter.api.Test;
22+
import org.junit.jupiter.params.ParameterizedTest;
23+
import org.junit.jupiter.params.provider.ValueSource;
2024

2125
import org.springframework.aot.hint.MemberCategory;
2226
import org.springframework.aot.hint.RuntimeHints;
@@ -26,28 +30,28 @@
2630
import org.springframework.beans.factory.aot.BeanFactoryInitializationAotContribution;
2731
import org.springframework.beans.factory.aot.BeanFactoryInitializationAotProcessor;
2832
import org.springframework.boot.json.JsonWriter.Members;
33+
import org.springframework.boot.logging.StackTracePrinter;
2934
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
3035
import org.springframework.core.env.ConfigurableEnvironment;
3136
import org.springframework.mock.env.MockEnvironment;
3237

3338
import static org.assertj.core.api.Assertions.assertThat;
3439

3540
/**
36-
* Tests for
37-
* {@link StructuredLoggingJsonMembersCustomizerBeanFactoryInitializationAotProcessor}.
41+
* Tests for {@link StructuredLoggingJsonPropertiesBeanFactoryInitializationAotProcessor}.
3842
*
3943
* @author Dmytro Nosan
4044
*/
41-
class StructuredLoggingJsonMembersCustomizerBeanFactoryInitializationAotProcessorTests {
45+
class StructuredLoggingJsonPropertiesBeanFactoryInitializationAotProcessorTests {
4246

4347
@Test
44-
void structuredLoggingJsonMembersCustomizerBeanFactoryInitializationAotProcessorIsRegistered() {
48+
void structuredLoggingJsonPropertiesBeanFactoryInitializationAotProcessorIsRegistered() {
4549
assertThat(AotServices.factories().load(BeanFactoryInitializationAotProcessor.class))
46-
.anyMatch(StructuredLoggingJsonMembersCustomizerBeanFactoryInitializationAotProcessor.class::isInstance);
50+
.anyMatch(StructuredLoggingJsonPropertiesBeanFactoryInitializationAotProcessor.class::isInstance);
4751
}
4852

4953
@Test
50-
void shouldRegisterStructuredLoggingJsonMembersCustomizerRuntimeHints() {
54+
void shouldRegisterRuntimeHintsWhenCustomizerIsPresent() {
5155
MockEnvironment environment = new MockEnvironment();
5256
environment.setProperty("logging.structured.json.customizer", TestCustomizer.class.getName());
5357

@@ -63,14 +67,40 @@ void shouldRegisterStructuredLoggingJsonMembersCustomizerRuntimeHints() {
6367
}
6468

6569
@Test
66-
void shouldNotRegisterStructuredLoggingJsonMembersCustomizerRuntimeHintsWhenPropertiesAreNotSet() {
70+
void shouldRegisterRuntimeHintsWhenCustomStackTracePrinterIsPresent() {
71+
MockEnvironment environment = new MockEnvironment();
72+
environment.setProperty("logging.structured.json.stacktrace.printer", TestStackTracePrinter.class.getName());
73+
74+
BeanFactoryInitializationAotContribution contribution = getContribution(environment);
75+
assertThat(contribution).isNotNull();
76+
77+
RuntimeHints hints = getRuntimeHints(contribution);
78+
assertThat(RuntimeHintsPredicates.reflection()
79+
.onType(TestStackTracePrinter.class)
80+
.withMemberCategories(MemberCategory.INVOKE_DECLARED_CONSTRUCTORS,
81+
MemberCategory.INVOKE_PUBLIC_CONSTRUCTORS))
82+
.accepts(hints);
83+
}
84+
85+
@ParameterizedTest
86+
@ValueSource(strings = { "logging-system", "standard" })
87+
void shouldNotRegisterRuntimeHintsWhenStackTracePrinterIsNotCustomImplementation(String printer) {
6788
MockEnvironment environment = new MockEnvironment();
89+
environment.setProperty("logging.structured.json.stacktrace.printer", printer);
90+
6891
BeanFactoryInitializationAotContribution contribution = getContribution(environment);
6992
assertThat(contribution).isNull();
7093
}
7194

7295
@Test
73-
void shouldNotRegisterStructuredLoggingJsonMembersCustomizerRuntimeHintsWhenCustomizerIsNotSet() {
96+
void shouldNotRegisterRuntimeHintsWhenPropertiesAreNotSet() {
97+
MockEnvironment environment = new MockEnvironment();
98+
BeanFactoryInitializationAotContribution contribution = getContribution(environment);
99+
assertThat(contribution).isNull();
100+
}
101+
102+
@Test
103+
void shouldNotRegisterRuntimeHintsWhenCustomizerAndPrinterAreNotSet() {
74104
MockEnvironment environment = new MockEnvironment();
75105
environment.setProperty("logging.structured.json.exclude", "something");
76106
BeanFactoryInitializationAotContribution contribution = getContribution(environment);
@@ -81,7 +111,7 @@ private BeanFactoryInitializationAotContribution getContribution(ConfigurableEnv
81111
try (AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext()) {
82112
context.setEnvironment(environment);
83113
context.refresh();
84-
return new StructuredLoggingJsonMembersCustomizerBeanFactoryInitializationAotProcessor()
114+
return new StructuredLoggingJsonPropertiesBeanFactoryInitializationAotProcessor()
85115
.processAheadOfTime(context.getBeanFactory());
86116
}
87117
}
@@ -100,4 +130,13 @@ public void customize(Members<String> members) {
100130

101131
}
102132

133+
static class TestStackTracePrinter implements StackTracePrinter {
134+
135+
@Override
136+
public void printStackTrace(Throwable throwable, Appendable out) throws IOException {
137+
138+
}
139+
140+
}
141+
103142
}

spring-boot-project/spring-boot/src/test/java/org/springframework/boot/logging/structured/StructuredLoggingJsonPropertiesTests.java

+28
Original file line numberDiff line numberDiff line change
@@ -93,6 +93,10 @@ void shouldRegisterRuntimeHints() throws Exception {
9393
.onConstructor(StructuredLoggingJsonProperties.class.getDeclaredConstructor(Set.class, Set.class, Map.class,
9494
Map.class, StackTrace.class, Set.class))
9595
.invoke()).accepts(hints);
96+
assertThat(RuntimeHintsPredicates.reflection()
97+
.onConstructor(StackTrace.class.getDeclaredConstructor(String.class, Root.class, Integer.class,
98+
Integer.class, Boolean.class, Boolean.class))
99+
.invoke()).accepts(hints);
96100
}
97101

98102
@Test
@@ -184,6 +188,30 @@ void createPrinterWhenClassNameInjectsConfiguredPrinter() {
184188

185189
}
186190

191+
@Test
192+
void shouldReturnFalseWhenPrinterIsEmpty() {
193+
StackTrace stackTrace = new StackTrace("", null, null, null, null, null);
194+
assertThat(stackTrace.hasCustomPrinter()).isFalse();
195+
}
196+
197+
@Test
198+
void hasCustomPrinterShouldReturnFalseWhenPrinterHasLoggingSystem() {
199+
StackTrace stackTrace = new StackTrace("loggingsystem", null, null, null, null, null);
200+
assertThat(stackTrace.hasCustomPrinter()).isFalse();
201+
}
202+
203+
@Test
204+
void hasCustomPrinterShouldReturnFalseWhenPrinterHasStandard() {
205+
StackTrace stackTrace = new StackTrace("standard", null, null, null, null, null);
206+
assertThat(stackTrace.hasCustomPrinter()).isFalse();
207+
}
208+
209+
@Test
210+
void hasCustomPrinterShouldReturnTrueWhenPrinterHasCustom() {
211+
StackTrace stackTrace = new StackTrace("custom-printer", null, null, null, null, null);
212+
assertThat(stackTrace.hasCustomPrinter()).isTrue();
213+
}
214+
187215
}
188216

189217
static class TestCustomizer implements StructuredLoggingJsonMembersCustomizer<String> {

0 commit comments

Comments
 (0)