diff --git a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/logging/structured/StructuredLoggingJsonMembersCustomizerBeanFactoryInitializationAotProcessor.java b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/logging/structured/StructuredLoggingJsonMembersCustomizerBeanFactoryInitializationAotProcessor.java index a60819e292e4..bf707d78f3b5 100644 --- a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/logging/structured/StructuredLoggingJsonMembersCustomizerBeanFactoryInitializationAotProcessor.java +++ b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/logging/structured/StructuredLoggingJsonMembersCustomizerBeanFactoryInitializationAotProcessor.java @@ -17,6 +17,7 @@ package org.springframework.boot.logging.structured; import java.util.Optional; +import java.util.Set; import org.springframework.aot.generate.GenerationContext; import org.springframework.aot.hint.MemberCategory; @@ -32,6 +33,7 @@ * {@link StructuredLoggingJsonPropertiesJsonMembersCustomizer}. * * @author Dmytro Nosan + * @author Yanming Zhou */ class StructuredLoggingJsonMembersCustomizerBeanFactoryInitializationAotProcessor implements BeanFactoryInitializationAotProcessor { @@ -49,19 +51,19 @@ public BeanFactoryInitializationAotContribution processAheadOfTime(ConfigurableL private static final class AotContribution implements BeanFactoryInitializationAotContribution { - private final Class> customizer; + private final Set>> customizer; - private AotContribution(Class> customizer) { + private AotContribution(Set>> customizer) { this.customizer = customizer; } @Override public void applyTo(GenerationContext generationContext, BeanFactoryInitializationCode beanFactoryInitializationCode) { - generationContext.getRuntimeHints() + this.customizer.forEach((it) -> generationContext.getRuntimeHints() .reflection() - .registerType(this.customizer, MemberCategory.INVOKE_DECLARED_CONSTRUCTORS, - MemberCategory.INVOKE_PUBLIC_CONSTRUCTORS); + .registerType(it, MemberCategory.INVOKE_DECLARED_CONSTRUCTORS, + MemberCategory.INVOKE_PUBLIC_CONSTRUCTORS)); } } diff --git a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/logging/structured/StructuredLoggingJsonProperties.java b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/logging/structured/StructuredLoggingJsonProperties.java index 82a4f55e4091..a5b076a00aa1 100644 --- a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/logging/structured/StructuredLoggingJsonProperties.java +++ b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/logging/structured/StructuredLoggingJsonProperties.java @@ -33,9 +33,10 @@ * @param customizer the fully qualified name of a * {@link StructuredLoggingJsonMembersCustomizer} * @author Phillip Webb + * @author Yanming Zhou */ record StructuredLoggingJsonProperties(Set include, Set exclude, Map rename, - Map add, Class> customizer) { + Map add, Set>> customizer) { static StructuredLoggingJsonProperties get(Environment environment) { return Binder.get(environment) diff --git a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/logging/structured/StructuredLoggingJsonPropertiesJsonMembersCustomizer.java b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/logging/structured/StructuredLoggingJsonPropertiesJsonMembersCustomizer.java index a33f2ff9df6c..b24aecf83826 100644 --- a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/logging/structured/StructuredLoggingJsonPropertiesJsonMembersCustomizer.java +++ b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/logging/structured/StructuredLoggingJsonPropertiesJsonMembersCustomizer.java @@ -17,6 +17,7 @@ package org.springframework.boot.logging.structured; import java.util.Map; +import java.util.Set; import org.springframework.boot.json.JsonWriter.MemberPath; import org.springframework.boot.json.JsonWriter.Members; @@ -28,6 +29,7 @@ * {@link StructuredLoggingJsonProperties}. * * @author Phillip Webb + * @author Yanming Zhou */ class StructuredLoggingJsonPropertiesJsonMembersCustomizer implements StructuredLoggingJsonMembersCustomizer { @@ -49,9 +51,9 @@ public void customize(Members members) { if (!CollectionUtils.isEmpty(add)) { add.forEach(members::add); } - Class> customizer = this.properties.customizer(); + Set>> customizer = this.properties.customizer(); if (customizer != null) { - createAndApplyCustomizer(members, customizer); + customizer.forEach((c) -> createAndApplyCustomizer(members, c)); } } diff --git a/spring-boot-project/spring-boot/src/main/resources/META-INF/additional-spring-configuration-metadata.json b/spring-boot-project/spring-boot/src/main/resources/META-INF/additional-spring-configuration-metadata.json index d7b29b110d02..b5527211043c 100644 --- a/spring-boot-project/spring-boot/src/main/resources/META-INF/additional-spring-configuration-metadata.json +++ b/spring-boot-project/spring-boot/src/main/resources/META-INF/additional-spring-configuration-metadata.json @@ -268,8 +268,8 @@ }, { "name": "logging.structured.json.customizer", - "type": "java.lang.Class>", - "description": "The fully qualified class name of a StructuredLoggingJsonMembersCustomizer" + "type": "java.util.Set>>", + "description": "The fully qualified class names of a StructuredLoggingJsonMembersCustomizer" }, { "name": "logging.structured.json.exclude", diff --git a/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/logging/structured/StructuredLoggingJsonPropertiesJsonMembersCustomizerTests.java b/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/logging/structured/StructuredLoggingJsonPropertiesJsonMembersCustomizerTests.java index 9ba25407f16f..454d15a8f9ea 100644 --- a/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/logging/structured/StructuredLoggingJsonPropertiesJsonMembersCustomizerTests.java +++ b/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/logging/structured/StructuredLoggingJsonPropertiesJsonMembersCustomizerTests.java @@ -37,6 +37,7 @@ * Tests for {@link StructuredLoggingJsonPropertiesJsonMembersCustomizer}. * * @author Phillip Webb + * @author Yanming Zhou */ @ExtendWith(MockitoExtension.class) class StructuredLoggingJsonPropertiesJsonMembersCustomizerTests { @@ -102,12 +103,25 @@ void customizeWhenHasCustomizerCustomizesMember() { .applyingNameProcessor(NameProcessor.of(String::toUpperCase)); given(((Instantiator) this.instantiator).instantiateType(TestCustomizer.class)).willReturn(uppercaseCustomizer); StructuredLoggingJsonProperties properties = new StructuredLoggingJsonProperties(Collections.emptySet(), - Collections.emptySet(), Collections.emptyMap(), Collections.emptyMap(), TestCustomizer.class); + Collections.emptySet(), Collections.emptyMap(), Collections.emptyMap(), Set.of(TestCustomizer.class)); StructuredLoggingJsonPropertiesJsonMembersCustomizer customizer = new StructuredLoggingJsonPropertiesJsonMembersCustomizer( this.instantiator, properties); assertThat(writeSampleJson(customizer)).contains("\"A\":\"a\""); } + @Test + @SuppressWarnings({ "rawtypes", "unchecked" }) + void multipleCustomizers() { + given(((Instantiator) this.instantiator).instantiateType(FooCustomizer.class)).willReturn(new FooCustomizer()); + given(((Instantiator) this.instantiator).instantiateType(BarCustomizer.class)).willReturn(new BarCustomizer()); + StructuredLoggingJsonProperties properties = new StructuredLoggingJsonProperties(Collections.emptySet(), + Collections.emptySet(), Collections.emptyMap(), Collections.emptyMap(), + Set.of(FooCustomizer.class, BarCustomizer.class)); + StructuredLoggingJsonPropertiesJsonMembersCustomizer customizer = new StructuredLoggingJsonPropertiesJsonMembersCustomizer( + this.instantiator, properties); + assertThat(writeSampleJson(customizer)).contains("\"foo\":\"foo\"").contains("\"bar\":\"bar\""); + } + @SuppressWarnings({ "rawtypes", "unchecked" }) private String writeSampleJson(StructuredLoggingJsonMembersCustomizer customizer) { return JsonWriter.of((members) -> { @@ -126,4 +140,22 @@ public void customize(Members members) { } + static class FooCustomizer implements StructuredLoggingJsonMembersCustomizer { + + @Override + public void customize(Members members) { + members.add("foo", "foo"); + } + + } + + static class BarCustomizer implements StructuredLoggingJsonMembersCustomizer { + + @Override + public void customize(Members members) { + members.add("bar", "bar"); + } + + } + } diff --git a/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/logging/structured/StructuredLoggingJsonPropertiesTests.java b/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/logging/structured/StructuredLoggingJsonPropertiesTests.java index a893d4a936f7..ceeaf63edf6f 100644 --- a/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/logging/structured/StructuredLoggingJsonPropertiesTests.java +++ b/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/logging/structured/StructuredLoggingJsonPropertiesTests.java @@ -48,7 +48,7 @@ void getBindsFromEnvironment() { environment.setProperty("logging.structured.json.customizer", TestCustomizer.class.getName()); StructuredLoggingJsonProperties properties = StructuredLoggingJsonProperties.get(environment); assertThat(properties).isEqualTo(new StructuredLoggingJsonProperties(Set.of("a", "b"), Set.of("c", "d"), - Map.of("e", "f"), Map.of("g", "h"), TestCustomizer.class)); + Map.of("e", "f"), Map.of("g", "h"), Set.of(TestCustomizer.class))); } @Test @@ -64,7 +64,7 @@ void shouldRegisterRuntimeHints() throws Exception { assertThat(RuntimeHintsPredicates.reflection().onType(StructuredLoggingJsonProperties.class)).accepts(hints); assertThat(RuntimeHintsPredicates.reflection() .onConstructor(StructuredLoggingJsonProperties.class.getDeclaredConstructor(Set.class, Set.class, Map.class, - Map.class, Class.class)) + Map.class, Set.class)) .invoke()).accepts(hints); }