Skip to content

Commit

Permalink
Call spring.factories customizers in the same way as one from props
Browse files Browse the repository at this point in the history
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
  • Loading branch information
wilkinsona committed Nov 28, 2024
1 parent f9aedf5 commit a1c1e32
Show file tree
Hide file tree
Showing 2 changed files with 43 additions and 11 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,6 @@
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;
Expand Down Expand Up @@ -108,11 +107,12 @@ private List<StructuredLoggingJsonMembersCustomizer<?>> loadStructuredLoggingJso
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);
}
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@

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;
Expand Down Expand Up @@ -104,18 +105,49 @@ void getUsingClassNameInjectsCustomParameter() {
}

@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 {

}
Expand Down Expand Up @@ -167,9 +199,9 @@ public String format(DifferentLogEvent event) {

}

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);
}

Expand Down

0 comments on commit a1c1e32

Please sign in to comment.