Browse Source

Call spring.factories customizers in the same way as one from props

Previously, customizers loaded from spring.factories were called
using LambdaSafe. This resulted in customizers with a generic type
more specific than Object being ignored. A customizer loaded from
the logging.structured.json.customizer property was not affected as
it was called directly rather than through LambdaSafe.

This commit aligns the way in which customizers loaded from
spring.factories are called with the way in which any customizer
specified using the logging.structured.json.customizer property is
called.

Closes gh-43312
pull/43316/head
Andy Wilkinson 1 year ago
parent
commit
a1c1e32947
  1. 8
      spring-boot-project/spring-boot/src/main/java/org/springframework/boot/logging/structured/StructuredLogFormatterFactory.java
  2. 46
      spring-boot-project/spring-boot/src/test/java/org/springframework/boot/logging/structured/StructuredLogFormatterFactoryTests.java

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

@ -27,7 +27,6 @@ import org.springframework.boot.json.JsonWriter.Members; @@ -27,7 +27,6 @@ import org.springframework.boot.json.JsonWriter.Members;
import org.springframework.boot.util.Instantiator;
import org.springframework.boot.util.Instantiator.AvailableParameters;
import org.springframework.boot.util.Instantiator.FailureHandler;
import org.springframework.boot.util.LambdaSafe;
import org.springframework.core.GenericTypeResolver;
import org.springframework.core.env.Environment;
import org.springframework.core.io.support.SpringFactoriesLoader;
@ -108,11 +107,12 @@ public class StructuredLogFormatterFactory<E> { @@ -108,11 +107,12 @@ public class StructuredLogFormatterFactory<E> {
ArgumentResolver.from(this.instantiator::getArg));
}
@SuppressWarnings("unchecked")
@SuppressWarnings({ "unchecked", "rawtypes" })
private void invokeCustomizers(List<StructuredLoggingJsonMembersCustomizer<?>> customizers,
Members<Object> members) {
LambdaSafe.callbacks(StructuredLoggingJsonMembersCustomizer.class, customizers, members)
.invoke((customizer) -> customizer.customize(members));
for (StructuredLoggingJsonMembersCustomizer<?> customizer : customizers) {
((StructuredLoggingJsonMembersCustomizer) customizer).customize(members);
}
}
/**

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

@ -20,6 +20,7 @@ import java.util.List; @@ -20,6 +20,7 @@ import java.util.List;
import org.junit.jupiter.api.Test;
import org.springframework.boot.json.JsonWriter.Members;
import org.springframework.boot.json.JsonWriter.ValueProcessor;
import org.springframework.boot.logging.structured.StructuredLogFormatterFactory.CommonFormatters;
import org.springframework.boot.util.Instantiator.AvailableParameters;
@ -104,18 +105,49 @@ class StructuredLogFormatterFactoryTests { @@ -104,18 +105,49 @@ class StructuredLogFormatterFactoryTests {
}
@Test
void getInjectCustomizers() {
void getInjectStringMembersCustomizer() {
this.environment.setProperty("logging.structured.json.rename.spring", "test");
SpringFactoriesLoader factoriesLoader = mock(SpringFactoriesLoader.class);
StructuredLoggingJsonMembersCustomizer<?> customizer = (members) -> members
.applyingValueProcessor(ValueProcessor.of(String.class, String::toUpperCase));
given(factoriesLoader.load(any(), any(ArgumentResolver.class))).willReturn(List.of(customizer));
given(factoriesLoader.load(any(), any(ArgumentResolver.class)))
.willReturn(List.of(new StringMembersStructuredLoggingJsonMembersCustomizer()));
StructuredLogFormatterFactory<LogEvent> factory = new StructuredLogFormatterFactory<>(factoriesLoader,
LogEvent.class, this.environment, this::addAvailableParameters, this::addCommonFormatters);
CutomizedFormatter formatter = (CutomizedFormatter) factory.get(CutomizedFormatter.class.getName());
CustomizedFormatter formatter = (CustomizedFormatter) factory.get(CustomizedFormatter.class.getName());
assertThat(formatter.format(new LogEvent())).contains("\"test\":\"BOOT\"");
}
@Test
void getInjectObjectMembersCustomizer() {
this.environment.setProperty("logging.structured.json.rename.spring", "test");
SpringFactoriesLoader factoriesLoader = mock(SpringFactoriesLoader.class);
given(factoriesLoader.load(any(), any(ArgumentResolver.class)))
.willReturn(List.of(new ObjectMembersStructuredLoggingJsonMembersCustomizer()));
StructuredLogFormatterFactory<LogEvent> factory = new StructuredLogFormatterFactory<>(factoriesLoader,
LogEvent.class, this.environment, this::addAvailableParameters, this::addCommonFormatters);
CustomizedFormatter formatter = (CustomizedFormatter) factory.get(CustomizedFormatter.class.getName());
assertThat(formatter.format(new LogEvent())).contains("\"test\":\"BOOT\"");
}
static class StringMembersStructuredLoggingJsonMembersCustomizer
implements StructuredLoggingJsonMembersCustomizer<String> {
@Override
public void customize(Members<String> members) {
members.applyingValueProcessor(ValueProcessor.of(String.class, String::toUpperCase));
}
}
static class ObjectMembersStructuredLoggingJsonMembersCustomizer
implements StructuredLoggingJsonMembersCustomizer<Object> {
@Override
public void customize(Members<Object> members) {
members.applyingValueProcessor(ValueProcessor.of(String.class, String::toUpperCase));
}
}
static class LogEvent {
}
@ -167,9 +199,9 @@ class StructuredLogFormatterFactoryTests { @@ -167,9 +199,9 @@ class StructuredLogFormatterFactoryTests {
}
static class CutomizedFormatter extends JsonWriterStructuredLogFormatter<LogEvent> {
static class CustomizedFormatter extends JsonWriterStructuredLogFormatter<LogEvent> {
CutomizedFormatter(StructuredLoggingJsonMembersCustomizer<?> customizer) {
CustomizedFormatter(StructuredLoggingJsonMembersCustomizer<?> customizer) {
super((members) -> members.add("spring", "boot"), customizer);
}

Loading…
Cancel
Save