Merge pull request #44242 from nosan
* pr/44242: Polish "Add RuntimeHints for StackTracePrinter" Add RuntimeHints for StackTracePrinter Closes gh-44242
This commit is contained in:
@@ -87,8 +87,7 @@ record StructuredLoggingJsonProperties(Set<String> include, Set<String> exclude,
|
|||||||
Boolean includeCommonFrames, Boolean includeHashes) {
|
Boolean includeCommonFrames, Boolean includeHashes) {
|
||||||
|
|
||||||
StackTracePrinter createPrinter() {
|
StackTracePrinter createPrinter() {
|
||||||
String name = (printer() != null) ? printer() : "";
|
String name = sanitizePrinter();
|
||||||
name = name.toLowerCase(Locale.getDefault()).replace("-", "");
|
|
||||||
if ("loggingsystem".equals(name) || (name.isEmpty() && !hasAnyOtherProperty())) {
|
if ("loggingsystem".equals(name) || (name.isEmpty() && !hasAnyOtherProperty())) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
@@ -101,6 +100,18 @@ record StructuredLoggingJsonProperties(Set<String> include, Set<String> exclude,
|
|||||||
.instantiate(printer());
|
.instantiate(printer());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
boolean hasCustomPrinter() {
|
||||||
|
String name = sanitizePrinter();
|
||||||
|
if (name.isEmpty()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return !("loggingsystem".equals(name) || "standard".equals(name));
|
||||||
|
}
|
||||||
|
|
||||||
|
private String sanitizePrinter() {
|
||||||
|
return Objects.toString(printer(), "").toLowerCase(Locale.ROOT).replace("-", "");
|
||||||
|
}
|
||||||
|
|
||||||
private boolean hasAnyOtherProperty() {
|
private boolean hasAnyOtherProperty() {
|
||||||
return Stream.of(root(), maxLength(), maxThrowableDepth(), includeCommonFrames(), includeHashes())
|
return Stream.of(root(), maxLength(), maxThrowableDepth(), includeCommonFrames(), includeHashes())
|
||||||
.anyMatch(Objects::nonNull);
|
.anyMatch(Objects::nonNull);
|
||||||
|
|||||||
@@ -16,6 +16,7 @@
|
|||||||
|
|
||||||
package org.springframework.boot.logging.structured;
|
package org.springframework.boot.logging.structured;
|
||||||
|
|
||||||
|
import java.util.Optional;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
|
||||||
import org.springframework.aot.generate.GenerationContext;
|
import org.springframework.aot.generate.GenerationContext;
|
||||||
@@ -26,17 +27,18 @@ import org.springframework.beans.factory.aot.BeanFactoryInitializationAotContrib
|
|||||||
import org.springframework.beans.factory.aot.BeanFactoryInitializationAotProcessor;
|
import org.springframework.beans.factory.aot.BeanFactoryInitializationAotProcessor;
|
||||||
import org.springframework.beans.factory.aot.BeanFactoryInitializationCode;
|
import org.springframework.beans.factory.aot.BeanFactoryInitializationCode;
|
||||||
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
|
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
|
||||||
|
import org.springframework.boot.logging.structured.StructuredLoggingJsonProperties.StackTrace;
|
||||||
import org.springframework.core.env.Environment;
|
import org.springframework.core.env.Environment;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* {@link BeanFactoryInitializationAotProcessor} that registers {@link RuntimeHints} for
|
* {@link BeanFactoryInitializationAotProcessor} that registers {@link RuntimeHints} for
|
||||||
* {@link StructuredLoggingJsonPropertiesJsonMembersCustomizer}.
|
* {@link StructuredLoggingJsonProperties}.
|
||||||
*
|
*
|
||||||
* @author Dmytro Nosan
|
* @author Dmytro Nosan
|
||||||
* @author Yanming Zhou
|
* @author Yanming Zhou
|
||||||
* @author Phillip Webb
|
* @author Phillip Webb
|
||||||
*/
|
*/
|
||||||
class StructuredLoggingJsonMembersCustomizerBeanFactoryInitializationAotProcessor
|
class StructuredLoggingJsonPropertiesBeanFactoryInitializationAotProcessor
|
||||||
implements BeanFactoryInitializationAotProcessor {
|
implements BeanFactoryInitializationAotProcessor {
|
||||||
|
|
||||||
private static final String ENVIRONMENT_BEAN_NAME = "environment";
|
private static final String ENVIRONMENT_BEAN_NAME = "environment";
|
||||||
@@ -45,15 +47,36 @@ class StructuredLoggingJsonMembersCustomizerBeanFactoryInitializationAotProcesso
|
|||||||
public BeanFactoryInitializationAotContribution processAheadOfTime(ConfigurableListableBeanFactory beanFactory) {
|
public BeanFactoryInitializationAotContribution processAheadOfTime(ConfigurableListableBeanFactory beanFactory) {
|
||||||
Environment environment = beanFactory.getBean(ENVIRONMENT_BEAN_NAME, Environment.class);
|
Environment environment = beanFactory.getBean(ENVIRONMENT_BEAN_NAME, Environment.class);
|
||||||
StructuredLoggingJsonProperties properties = StructuredLoggingJsonProperties.get(environment);
|
StructuredLoggingJsonProperties properties = StructuredLoggingJsonProperties.get(environment);
|
||||||
return (properties != null) ? AotContribution.get(properties.customizer()) : null;
|
if (properties != null) {
|
||||||
|
Set<Class<? extends StructuredLoggingJsonMembersCustomizer<?>>> customizers = properties.customizer();
|
||||||
|
String stackTracePrinter = getCustomStackTracePrinter(properties);
|
||||||
|
if (stackTracePrinter != null || !customizers.isEmpty()) {
|
||||||
|
return new AotContribution(beanFactory.getBeanClassLoader(), customizers, stackTracePrinter);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static String getCustomStackTracePrinter(StructuredLoggingJsonProperties properties) {
|
||||||
|
return Optional.ofNullable(properties.stackTrace())
|
||||||
|
.filter(StackTrace::hasCustomPrinter)
|
||||||
|
.map(StackTrace::printer)
|
||||||
|
.orElse(null);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static final class AotContribution implements BeanFactoryInitializationAotContribution {
|
private static final class AotContribution implements BeanFactoryInitializationAotContribution {
|
||||||
|
|
||||||
|
private final ClassLoader classLoader;
|
||||||
|
|
||||||
private final Set<Class<? extends StructuredLoggingJsonMembersCustomizer<?>>> customizers;
|
private final Set<Class<? extends StructuredLoggingJsonMembersCustomizer<?>>> customizers;
|
||||||
|
|
||||||
private AotContribution(Set<Class<? extends StructuredLoggingJsonMembersCustomizer<?>>> customizers) {
|
private final String stackTracePrinter;
|
||||||
|
|
||||||
|
private AotContribution(ClassLoader classLoader,
|
||||||
|
Set<Class<? extends StructuredLoggingJsonMembersCustomizer<?>>> customizers, String stackTracePrinter) {
|
||||||
|
this.classLoader = classLoader;
|
||||||
this.customizers = customizers;
|
this.customizers = customizers;
|
||||||
|
this.stackTracePrinter = stackTracePrinter;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -62,10 +85,10 @@ class StructuredLoggingJsonMembersCustomizerBeanFactoryInitializationAotProcesso
|
|||||||
ReflectionHints reflection = generationContext.getRuntimeHints().reflection();
|
ReflectionHints reflection = generationContext.getRuntimeHints().reflection();
|
||||||
this.customizers.forEach((customizer) -> reflection.registerType(customizer,
|
this.customizers.forEach((customizer) -> reflection.registerType(customizer,
|
||||||
MemberCategory.INVOKE_DECLARED_CONSTRUCTORS, MemberCategory.INVOKE_PUBLIC_CONSTRUCTORS));
|
MemberCategory.INVOKE_DECLARED_CONSTRUCTORS, MemberCategory.INVOKE_PUBLIC_CONSTRUCTORS));
|
||||||
}
|
if (this.stackTracePrinter != null) {
|
||||||
|
reflection.registerTypeIfPresent(this.classLoader, this.stackTracePrinter,
|
||||||
static AotContribution get(Set<Class<? extends StructuredLoggingJsonMembersCustomizer<?>>> customizers) {
|
MemberCategory.INVOKE_DECLARED_CONSTRUCTORS, MemberCategory.INVOKE_PUBLIC_CONSTRUCTORS);
|
||||||
return (!customizers.isEmpty()) ? new AotContribution(customizers) : null;
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -20,7 +20,7 @@ org.springframework.beans.factory.aot.BeanFactoryInitializationAotProcessor=\
|
|||||||
org.springframework.boot.context.properties.ConfigurationPropertiesBeanFactoryInitializationAotProcessor,\
|
org.springframework.boot.context.properties.ConfigurationPropertiesBeanFactoryInitializationAotProcessor,\
|
||||||
org.springframework.boot.env.EnvironmentPostProcessorApplicationListener.EnvironmentBeanFactoryInitializationAotProcessor,\
|
org.springframework.boot.env.EnvironmentPostProcessorApplicationListener.EnvironmentBeanFactoryInitializationAotProcessor,\
|
||||||
org.springframework.boot.jackson.JsonComponentModule.JsonComponentBeanFactoryInitializationAotProcessor,\
|
org.springframework.boot.jackson.JsonComponentModule.JsonComponentBeanFactoryInitializationAotProcessor,\
|
||||||
org.springframework.boot.logging.structured.StructuredLoggingJsonMembersCustomizerBeanFactoryInitializationAotProcessor
|
org.springframework.boot.logging.structured.StructuredLoggingJsonPropertiesBeanFactoryInitializationAotProcessor
|
||||||
|
|
||||||
org.springframework.beans.factory.aot.BeanRegistrationAotProcessor=\
|
org.springframework.beans.factory.aot.BeanRegistrationAotProcessor=\
|
||||||
org.springframework.boot.context.properties.ConfigurationPropertiesBeanRegistrationAotProcessor,\
|
org.springframework.boot.context.properties.ConfigurationPropertiesBeanRegistrationAotProcessor,\
|
||||||
|
|||||||
@@ -16,7 +16,11 @@
|
|||||||
|
|
||||||
package org.springframework.boot.logging.structured;
|
package org.springframework.boot.logging.structured;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
|
||||||
import org.junit.jupiter.api.Test;
|
import org.junit.jupiter.api.Test;
|
||||||
|
import org.junit.jupiter.params.ParameterizedTest;
|
||||||
|
import org.junit.jupiter.params.provider.ValueSource;
|
||||||
|
|
||||||
import org.springframework.aot.hint.MemberCategory;
|
import org.springframework.aot.hint.MemberCategory;
|
||||||
import org.springframework.aot.hint.RuntimeHints;
|
import org.springframework.aot.hint.RuntimeHints;
|
||||||
@@ -26,6 +30,7 @@ import org.springframework.beans.factory.aot.AotServices;
|
|||||||
import org.springframework.beans.factory.aot.BeanFactoryInitializationAotContribution;
|
import org.springframework.beans.factory.aot.BeanFactoryInitializationAotContribution;
|
||||||
import org.springframework.beans.factory.aot.BeanFactoryInitializationAotProcessor;
|
import org.springframework.beans.factory.aot.BeanFactoryInitializationAotProcessor;
|
||||||
import org.springframework.boot.json.JsonWriter.Members;
|
import org.springframework.boot.json.JsonWriter.Members;
|
||||||
|
import org.springframework.boot.logging.StackTracePrinter;
|
||||||
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
|
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
|
||||||
import org.springframework.core.env.ConfigurableEnvironment;
|
import org.springframework.core.env.ConfigurableEnvironment;
|
||||||
import org.springframework.mock.env.MockEnvironment;
|
import org.springframework.mock.env.MockEnvironment;
|
||||||
@@ -33,21 +38,20 @@ import org.springframework.mock.env.MockEnvironment;
|
|||||||
import static org.assertj.core.api.Assertions.assertThat;
|
import static org.assertj.core.api.Assertions.assertThat;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Tests for
|
* Tests for {@link StructuredLoggingJsonPropertiesBeanFactoryInitializationAotProcessor}.
|
||||||
* {@link StructuredLoggingJsonMembersCustomizerBeanFactoryInitializationAotProcessor}.
|
|
||||||
*
|
*
|
||||||
* @author Dmytro Nosan
|
* @author Dmytro Nosan
|
||||||
*/
|
*/
|
||||||
class StructuredLoggingJsonMembersCustomizerBeanFactoryInitializationAotProcessorTests {
|
class StructuredLoggingJsonPropertiesBeanFactoryInitializationAotProcessorTests {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void structuredLoggingJsonMembersCustomizerBeanFactoryInitializationAotProcessorIsRegistered() {
|
void structuredLoggingJsonPropertiesBeanFactoryInitializationAotProcessorIsRegistered() {
|
||||||
assertThat(AotServices.factories().load(BeanFactoryInitializationAotProcessor.class))
|
assertThat(AotServices.factories().load(BeanFactoryInitializationAotProcessor.class))
|
||||||
.anyMatch(StructuredLoggingJsonMembersCustomizerBeanFactoryInitializationAotProcessor.class::isInstance);
|
.anyMatch(StructuredLoggingJsonPropertiesBeanFactoryInitializationAotProcessor.class::isInstance);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void shouldRegisterStructuredLoggingJsonMembersCustomizerRuntimeHints() {
|
void shouldRegisterRuntimeHintsWhenCustomizerIsPresent() {
|
||||||
MockEnvironment environment = new MockEnvironment();
|
MockEnvironment environment = new MockEnvironment();
|
||||||
environment.setProperty("logging.structured.json.customizer", TestCustomizer.class.getName());
|
environment.setProperty("logging.structured.json.customizer", TestCustomizer.class.getName());
|
||||||
|
|
||||||
@@ -63,14 +67,40 @@ class StructuredLoggingJsonMembersCustomizerBeanFactoryInitializationAotProcesso
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void shouldNotRegisterStructuredLoggingJsonMembersCustomizerRuntimeHintsWhenPropertiesAreNotSet() {
|
void shouldRegisterRuntimeHintsWhenCustomStackTracePrinterIsPresent() {
|
||||||
|
MockEnvironment environment = new MockEnvironment();
|
||||||
|
environment.setProperty("logging.structured.json.stacktrace.printer", TestStackTracePrinter.class.getName());
|
||||||
|
|
||||||
|
BeanFactoryInitializationAotContribution contribution = getContribution(environment);
|
||||||
|
assertThat(contribution).isNotNull();
|
||||||
|
|
||||||
|
RuntimeHints hints = getRuntimeHints(contribution);
|
||||||
|
assertThat(RuntimeHintsPredicates.reflection()
|
||||||
|
.onType(TestStackTracePrinter.class)
|
||||||
|
.withMemberCategories(MemberCategory.INVOKE_DECLARED_CONSTRUCTORS,
|
||||||
|
MemberCategory.INVOKE_PUBLIC_CONSTRUCTORS))
|
||||||
|
.accepts(hints);
|
||||||
|
}
|
||||||
|
|
||||||
|
@ParameterizedTest
|
||||||
|
@ValueSource(strings = { "logging-system", "standard" })
|
||||||
|
void shouldNotRegisterRuntimeHintsWhenStackTracePrinterIsNotCustomImplementation(String printer) {
|
||||||
|
MockEnvironment environment = new MockEnvironment();
|
||||||
|
environment.setProperty("logging.structured.json.stacktrace.printer", printer);
|
||||||
|
|
||||||
|
BeanFactoryInitializationAotContribution contribution = getContribution(environment);
|
||||||
|
assertThat(contribution).isNull();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void shouldNotRegisterRuntimeHintsWhenPropertiesAreNotSet() {
|
||||||
MockEnvironment environment = new MockEnvironment();
|
MockEnvironment environment = new MockEnvironment();
|
||||||
BeanFactoryInitializationAotContribution contribution = getContribution(environment);
|
BeanFactoryInitializationAotContribution contribution = getContribution(environment);
|
||||||
assertThat(contribution).isNull();
|
assertThat(contribution).isNull();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void shouldNotRegisterStructuredLoggingJsonMembersCustomizerRuntimeHintsWhenCustomizerIsNotSet() {
|
void shouldNotRegisterRuntimeHintsWhenCustomizerAndPrinterAreNotSet() {
|
||||||
MockEnvironment environment = new MockEnvironment();
|
MockEnvironment environment = new MockEnvironment();
|
||||||
environment.setProperty("logging.structured.json.exclude", "something");
|
environment.setProperty("logging.structured.json.exclude", "something");
|
||||||
BeanFactoryInitializationAotContribution contribution = getContribution(environment);
|
BeanFactoryInitializationAotContribution contribution = getContribution(environment);
|
||||||
@@ -81,7 +111,7 @@ class StructuredLoggingJsonMembersCustomizerBeanFactoryInitializationAotProcesso
|
|||||||
try (AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext()) {
|
try (AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext()) {
|
||||||
context.setEnvironment(environment);
|
context.setEnvironment(environment);
|
||||||
context.refresh();
|
context.refresh();
|
||||||
return new StructuredLoggingJsonMembersCustomizerBeanFactoryInitializationAotProcessor()
|
return new StructuredLoggingJsonPropertiesBeanFactoryInitializationAotProcessor()
|
||||||
.processAheadOfTime(context.getBeanFactory());
|
.processAheadOfTime(context.getBeanFactory());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -100,4 +130,13 @@ class StructuredLoggingJsonMembersCustomizerBeanFactoryInitializationAotProcesso
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static class TestStackTracePrinter implements StackTracePrinter {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void printStackTrace(Throwable throwable, Appendable out) throws IOException {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -93,6 +93,10 @@ class StructuredLoggingJsonPropertiesTests {
|
|||||||
.onConstructor(StructuredLoggingJsonProperties.class.getDeclaredConstructor(Set.class, Set.class, Map.class,
|
.onConstructor(StructuredLoggingJsonProperties.class.getDeclaredConstructor(Set.class, Set.class, Map.class,
|
||||||
Map.class, StackTrace.class, Set.class))
|
Map.class, StackTrace.class, Set.class))
|
||||||
.invoke()).accepts(hints);
|
.invoke()).accepts(hints);
|
||||||
|
assertThat(RuntimeHintsPredicates.reflection()
|
||||||
|
.onConstructor(StackTrace.class.getDeclaredConstructor(String.class, Root.class, Integer.class,
|
||||||
|
Integer.class, Boolean.class, Boolean.class))
|
||||||
|
.invoke()).accepts(hints);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@@ -184,6 +188,30 @@ class StructuredLoggingJsonPropertiesTests {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void shouldReturnFalseWhenPrinterIsEmpty() {
|
||||||
|
StackTrace stackTrace = new StackTrace("", null, null, null, null, null);
|
||||||
|
assertThat(stackTrace.hasCustomPrinter()).isFalse();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void hasCustomPrinterShouldReturnFalseWhenPrinterHasLoggingSystem() {
|
||||||
|
StackTrace stackTrace = new StackTrace("loggingsystem", null, null, null, null, null);
|
||||||
|
assertThat(stackTrace.hasCustomPrinter()).isFalse();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void hasCustomPrinterShouldReturnFalseWhenPrinterHasStandard() {
|
||||||
|
StackTrace stackTrace = new StackTrace("standard", null, null, null, null, null);
|
||||||
|
assertThat(stackTrace.hasCustomPrinter()).isFalse();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void hasCustomPrinterShouldReturnTrueWhenPrinterHasCustom() {
|
||||||
|
StackTrace stackTrace = new StackTrace("custom-printer", null, null, null, null, null);
|
||||||
|
assertThat(stackTrace.hasCustomPrinter()).isTrue();
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static class TestCustomizer implements StructuredLoggingJsonMembersCustomizer<String> {
|
static class TestCustomizer implements StructuredLoggingJsonMembersCustomizer<String> {
|
||||||
|
|||||||
Reference in New Issue
Block a user