Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -278,6 +278,35 @@ public StandardStackTracePrinter withHashes(@Nullable ToIntFunction<StackTraceEl
this.frameFilter, this.formatter, this.frameFormatter, frameHasher);
}

/**
* Compute a hash for the given throwable using the default frame hasher and return it
* as a zero-padded 8-character hex string.
* @param throwable the throwable to hash
* @return the hash as a hex string, or {@code null} if the throwable is {@code null}
* @since 4.0.0
*/
public static @Nullable String hashAsHexString(@Nullable Throwable throwable) {
return hashAsHexString(throwable, DEFAULT_FRAME_HASHER);
}

/**
* Compute a hash for the given throwable using the specified frame hasher and return
* it as a zero-padded 8-character hex string.
* @param throwable the throwable to hash
* @param frameHasher the function used to hash individual stack trace elements
* @return the hash as a hex string, or {@code null} if the throwable is {@code null}
* @since 4.0.0
*/
public static @Nullable String hashAsHexString(@Nullable Throwable throwable,
ToIntFunction<StackTraceElement> frameHasher) {
if (throwable == null) {
return null;
}
StackTrace stackTrace = new StackTrace(throwable);
int hash = stackTrace.hash(new HashSet<>(), frameHasher);
return String.format("%08x", hash);
}

private StandardStackTracePrinter withOption(Option option) {
EnumSet<Option> options = EnumSet.copyOf(this.options);
options.add(option);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@
import org.springframework.boot.logging.structured.ContextPairs.Pairs;
import org.springframework.boot.logging.structured.ElasticCommonSchemaProperties;
import org.springframework.boot.logging.structured.JsonWriterStructuredLogFormatter;
import org.springframework.boot.logging.structured.StackTraceHashFieldConfiguration;
import org.springframework.boot.logging.structured.StructuredLogFormatter;
import org.springframework.boot.logging.structured.StructuredLoggingJsonMembersCustomizer;
import org.springframework.core.env.Environment;
Expand All @@ -48,13 +49,15 @@
class ElasticCommonSchemaStructuredLogFormatter extends JsonWriterStructuredLogFormatter<LogEvent> {

ElasticCommonSchemaStructuredLogFormatter(Environment environment, @Nullable StackTracePrinter stackTracePrinter,
ContextPairs contextPairs, StructuredLoggingJsonMembersCustomizer.Builder<?> customizerBuilder) {
super((members) -> jsonMembers(environment, stackTracePrinter, contextPairs, members),
@Nullable StackTraceHashFieldConfiguration hashFieldConfiguration, ContextPairs contextPairs,
StructuredLoggingJsonMembersCustomizer.Builder<?> customizerBuilder) {
super((members) -> jsonMembers(environment, stackTracePrinter, hashFieldConfiguration, contextPairs, members),
customizerBuilder.nested().build());
}

private static void jsonMembers(Environment environment, @Nullable StackTracePrinter stackTracePrinter,
ContextPairs contextPairs, JsonWriter.Members<LogEvent> members) {
@Nullable StackTraceHashFieldConfiguration hashFieldConfiguration, ContextPairs contextPairs,
JsonWriter.Members<LogEvent> members) {
Extractor extractor = new Extractor(stackTracePrinter);
members.add("@timestamp", LogEvent::getInstant).as(ElasticCommonSchemaStructuredLogFormatter::asTimestamp);
members.add("log").usingMembers((log) -> {
Expand All @@ -76,6 +79,11 @@ private static void jsonMembers(Environment environment, @Nullable StackTracePri
error.add("message", Throwable::getMessage);
error.add("stack_trace", extractor::stackTrace);
}));
if (hashFieldConfiguration != null) {
members.add(hashFieldConfiguration.getFieldName(), LogEvent::getThrown)
.whenNotNull()
.as(hashFieldConfiguration::computeHash);
}
members.add("tags", LogEvent::getMarker)
.whenNotNull()
.as(ElasticCommonSchemaStructuredLogFormatter::getMarkers)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@
import org.springframework.boot.logging.structured.ContextPairs.Joiner;
import org.springframework.boot.logging.structured.GraylogExtendedLogFormatProperties;
import org.springframework.boot.logging.structured.JsonWriterStructuredLogFormatter;
import org.springframework.boot.logging.structured.StackTraceHashFieldConfiguration;
import org.springframework.boot.logging.structured.StructuredLogFormatter;
import org.springframework.boot.logging.structured.StructuredLoggingJsonMembersCustomizer;
import org.springframework.core.env.Environment;
Expand Down Expand Up @@ -74,13 +75,16 @@ class GraylogExtendedLogFormatStructuredLogFormatter extends JsonWriterStructure
private static final Set<String> ADDITIONAL_FIELD_ILLEGAL_KEYS = Set.of("id", "_id");

GraylogExtendedLogFormatStructuredLogFormatter(Environment environment,
@Nullable StackTracePrinter stackTracePrinter, ContextPairs contextPairs,
@Nullable StackTracePrinter stackTracePrinter,
@Nullable StackTraceHashFieldConfiguration hashFieldConfiguration, ContextPairs contextPairs,
@Nullable StructuredLoggingJsonMembersCustomizer<?> customizer) {
super((members) -> jsonMembers(environment, stackTracePrinter, contextPairs, members), customizer);
super((members) -> jsonMembers(environment, stackTracePrinter, hashFieldConfiguration, contextPairs, members),
customizer);
}

private static void jsonMembers(Environment environment, @Nullable StackTracePrinter stackTracePrinter,
ContextPairs contextPairs, JsonWriter.Members<LogEvent> members) {
@Nullable StackTraceHashFieldConfiguration hashFieldConfiguration, ContextPairs contextPairs,
JsonWriter.Members<LogEvent> members) {
Extractor extractor = new Extractor(stackTracePrinter);
members.add("version", "1.1");
members.add("short_message", LogEvent::getMessage)
Expand All @@ -103,6 +107,11 @@ private static void jsonMembers(Environment environment, @Nullable StackTracePri
members.add()
.whenNotNull(getThrown)
.usingMembers((thrownMembers) -> throwableMembers(thrownMembers, extractor));
if (hashFieldConfiguration != null) {
members.add(hashFieldConfiguration.getFieldName(), LogEvent::getThrown)
.whenNotNull()
.as(hashFieldConfiguration::computeHash);
}
}

private static String getMessageText(Message message) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@
import org.springframework.boot.logging.structured.CommonStructuredLogFormat;
import org.springframework.boot.logging.structured.ContextPairs;
import org.springframework.boot.logging.structured.JsonWriterStructuredLogFormatter;
import org.springframework.boot.logging.structured.StackTraceHashFieldConfiguration;
import org.springframework.boot.logging.structured.StructuredLogFormatter;
import org.springframework.boot.logging.structured.StructuredLoggingJsonMembersCustomizer;
import org.springframework.util.CollectionUtils;
Expand All @@ -47,12 +48,14 @@
*/
class LogstashStructuredLogFormatter extends JsonWriterStructuredLogFormatter<LogEvent> {

LogstashStructuredLogFormatter(@Nullable StackTracePrinter stackTracePrinter, ContextPairs contextPairs,
LogstashStructuredLogFormatter(@Nullable StackTracePrinter stackTracePrinter,
@Nullable StackTraceHashFieldConfiguration hashFieldConfiguration, ContextPairs contextPairs,
@Nullable StructuredLoggingJsonMembersCustomizer<?> customizer) {
super((members) -> jsonMembers(stackTracePrinter, contextPairs, members), customizer);
super((members) -> jsonMembers(stackTracePrinter, hashFieldConfiguration, contextPairs, members), customizer);
}

private static void jsonMembers(@Nullable StackTracePrinter stackTracePrinter, ContextPairs contextPairs,
private static void jsonMembers(@Nullable StackTracePrinter stackTracePrinter,
@Nullable StackTraceHashFieldConfiguration hashFieldConfiguration, ContextPairs contextPairs,
JsonWriter.Members<LogEvent> members) {
Extractor extractor = new Extractor(stackTracePrinter);
members.add("@timestamp", LogEvent::getInstant).as(LogstashStructuredLogFormatter::asTimestamp);
Expand All @@ -72,6 +75,11 @@ private static void jsonMembers(@Nullable StackTracePrinter stackTracePrinter, C
.as(LogstashStructuredLogFormatter::getMarkers)
.whenNot(collectionIsEmpty);
members.add("stack_trace", LogEvent::getThrown).whenNotNull().as(extractor::stackTrace);
if (hashFieldConfiguration != null) {
members.add(hashFieldConfiguration.getFieldName(), LogEvent::getThrown)
.whenNotNull()
.as(hashFieldConfiguration::computeHash);
}
}

private static String asTimestamp(Instant instant) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@
import org.springframework.boot.logging.StackTracePrinter;
import org.springframework.boot.logging.structured.CommonStructuredLogFormat;
import org.springframework.boot.logging.structured.ContextPairs;
import org.springframework.boot.logging.structured.StackTraceHashFieldConfiguration;
import org.springframework.boot.logging.structured.StructuredLogFormatter;
import org.springframework.boot.logging.structured.StructuredLogFormatterFactory;
import org.springframework.boot.logging.structured.StructuredLogFormatterFactory.CommonFormatters;
Expand Down Expand Up @@ -117,35 +118,42 @@ private void addCommonFormatters(CommonFormatters<LogEvent> commonFormatters) {
private ElasticCommonSchemaStructuredLogFormatter createEcsFormatter(Instantiator<?> instantiator) {
Environment environment = instantiator.getArg(Environment.class);
StackTracePrinter stackTracePrinter = instantiator.getArg(StackTracePrinter.class);
StackTraceHashFieldConfiguration hashFieldConfiguration = instantiator
.getArg(StackTraceHashFieldConfiguration.class);
ContextPairs contextPairs = instantiator.getArg(ContextPairs.class);
StructuredLoggingJsonMembersCustomizer.Builder<?> jsonMembersCustomizerBuilder = instantiator
.getArg(StructuredLoggingJsonMembersCustomizer.Builder.class);
Assert.state(environment != null, "'environment' must not be null");
Assert.state(contextPairs != null, "'contextPairs' must not be null");
Assert.state(jsonMembersCustomizerBuilder != null, "'jsonMembersCustomizerBuilder' must not be null");
return new ElasticCommonSchemaStructuredLogFormatter(environment, stackTracePrinter, contextPairs,
jsonMembersCustomizerBuilder);
return new ElasticCommonSchemaStructuredLogFormatter(environment, stackTracePrinter, hashFieldConfiguration,
contextPairs, jsonMembersCustomizerBuilder);
}

private GraylogExtendedLogFormatStructuredLogFormatter createGraylogFormatter(Instantiator<?> instantiator) {
Environment environment = instantiator.getArg(Environment.class);
StackTracePrinter stackTracePrinter = instantiator.getArg(StackTracePrinter.class);
StackTraceHashFieldConfiguration hashFieldConfiguration = instantiator
.getArg(StackTraceHashFieldConfiguration.class);
ContextPairs contextPairs = instantiator.getArg(ContextPairs.class);
StructuredLoggingJsonMembersCustomizer<?> jsonMembersCustomizer = instantiator
.getArg(StructuredLoggingJsonMembersCustomizer.class);
Assert.state(environment != null, "'environment' must not be null");
Assert.state(contextPairs != null, "'contextPairs' must not be null");
return new GraylogExtendedLogFormatStructuredLogFormatter(environment, stackTracePrinter, contextPairs,
jsonMembersCustomizer);
return new GraylogExtendedLogFormatStructuredLogFormatter(environment, stackTracePrinter,
hashFieldConfiguration, contextPairs, jsonMembersCustomizer);
}

private LogstashStructuredLogFormatter createLogstashFormatter(Instantiator<?> instantiator) {
StackTracePrinter stackTracePrinter = instantiator.getArg(StackTracePrinter.class);
StackTraceHashFieldConfiguration hashFieldConfiguration = instantiator
.getArg(StackTraceHashFieldConfiguration.class);
ContextPairs contextPairs = instantiator.getArg(ContextPairs.class);
StructuredLoggingJsonMembersCustomizer<?> jsonMembersCustomizer = instantiator
.getArg(StructuredLoggingJsonMembersCustomizer.class);
Assert.state(contextPairs != null, "'contextPairs' must not be null");
return new LogstashStructuredLogFormatter(stackTracePrinter, contextPairs, jsonMembersCustomizer);
return new LogstashStructuredLogFormatter(stackTracePrinter, hashFieldConfiguration, contextPairs,
jsonMembersCustomizer);
}

}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@
import org.springframework.boot.logging.structured.ContextPairs;
import org.springframework.boot.logging.structured.ElasticCommonSchemaProperties;
import org.springframework.boot.logging.structured.JsonWriterStructuredLogFormatter;
import org.springframework.boot.logging.structured.StackTraceHashFieldConfiguration;
import org.springframework.boot.logging.structured.StructuredLogFormatter;
import org.springframework.boot.logging.structured.StructuredLoggingJsonMembersCustomizer;
import org.springframework.core.env.Environment;
Expand All @@ -53,15 +54,16 @@ class ElasticCommonSchemaStructuredLogFormatter extends JsonWriterStructuredLogF
(pair) -> pair.value);

ElasticCommonSchemaStructuredLogFormatter(Environment environment, @Nullable StackTracePrinter stackTracePrinter,
ContextPairs contextPairs, ThrowableProxyConverter throwableProxyConverter,
@Nullable StackTraceHashFieldConfiguration hashFieldConfiguration, ContextPairs contextPairs,
ThrowableProxyConverter throwableProxyConverter,
StructuredLoggingJsonMembersCustomizer.Builder<?> customizerBuilder) {
super((members) -> jsonMembers(environment, stackTracePrinter, contextPairs, throwableProxyConverter, members),
customizerBuilder.nested().build());
super((members) -> jsonMembers(environment, stackTracePrinter, hashFieldConfiguration, contextPairs,
throwableProxyConverter, members), customizerBuilder.nested().build());
}

private static void jsonMembers(Environment environment, @Nullable StackTracePrinter stackTracePrinter,
ContextPairs contextPairs, ThrowableProxyConverter throwableProxyConverter,
JsonWriter.Members<ILoggingEvent> members) {
@Nullable StackTraceHashFieldConfiguration hashFieldConfiguration, ContextPairs contextPairs,
ThrowableProxyConverter throwableProxyConverter, JsonWriter.Members<ILoggingEvent> members) {
Extractor extractor = new Extractor(stackTracePrinter, throwableProxyConverter);
members.add("@timestamp", ILoggingEvent::getInstant);
members.add("log").usingMembers((log) -> {
Expand All @@ -87,13 +89,25 @@ private static void jsonMembers(Environment environment, @Nullable StackTracePri
error.add("stack_trace", extractor::stackTrace);
});
});
if (hashFieldConfiguration != null) {
members.add(hashFieldConfiguration.getFieldName(), (event) -> event)
.whenNotNull(getThrowableProxy)
.as((event) -> hashFieldConfiguration.computeHash(extractThrowable(event)));
}
members.add("tags", ILoggingEvent::getMarkerList)
.whenNotNull()
.as(ElasticCommonSchemaStructuredLogFormatter::getMarkers)
.whenNotEmpty();
members.add("ecs").usingMembers((ecs) -> ecs.add("version", "8.11"));
}

private static @Nullable Throwable extractThrowable(ILoggingEvent event) {
if (event.getThrowableProxy() instanceof ch.qos.logback.classic.spi.ThrowableProxy throwableProxy) {
return throwableProxy.getThrowable();
}
return null;
}

private static Set<String> getMarkers(List<Marker> markers) {
Set<String> result = new TreeSet<>();
addMarkers(result, markers.iterator());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@
import org.springframework.boot.logging.structured.ContextPairs.Joiner;
import org.springframework.boot.logging.structured.GraylogExtendedLogFormatProperties;
import org.springframework.boot.logging.structured.JsonWriterStructuredLogFormatter;
import org.springframework.boot.logging.structured.StackTraceHashFieldConfiguration;
import org.springframework.boot.logging.structured.StructuredLogFormatter;
import org.springframework.boot.logging.structured.StructuredLoggingJsonMembersCustomizer;
import org.springframework.core.env.Environment;
Expand Down Expand Up @@ -75,16 +76,17 @@ class GraylogExtendedLogFormatStructuredLogFormatter extends JsonWriterStructure
private static final Set<String> ADDITIONAL_FIELD_ILLEGAL_KEYS = Set.of("id", "_id");

GraylogExtendedLogFormatStructuredLogFormatter(Environment environment,
@Nullable StackTracePrinter stackTracePrinter, ContextPairs contextPairs,
@Nullable StackTracePrinter stackTracePrinter,
@Nullable StackTraceHashFieldConfiguration hashFieldConfiguration, ContextPairs contextPairs,
ThrowableProxyConverter throwableProxyConverter,
@Nullable StructuredLoggingJsonMembersCustomizer<?> customizer) {
super((members) -> jsonMembers(environment, stackTracePrinter, contextPairs, throwableProxyConverter, members),
customizer);
super((members) -> jsonMembers(environment, stackTracePrinter, hashFieldConfiguration, contextPairs,
throwableProxyConverter, members), customizer);
}

private static void jsonMembers(Environment environment, @Nullable StackTracePrinter stackTracePrinter,
ContextPairs contextPairs, ThrowableProxyConverter throwableProxyConverter,
JsonWriter.Members<ILoggingEvent> members) {
@Nullable StackTraceHashFieldConfiguration hashFieldConfiguration, ContextPairs contextPairs,
ThrowableProxyConverter throwableProxyConverter, JsonWriter.Members<ILoggingEvent> members) {
Extractor extractor = new Extractor(stackTracePrinter, throwableProxyConverter);
members.add("version", "1.1");
members.add("short_message", ILoggingEvent::getFormattedMessage)
Expand All @@ -106,6 +108,18 @@ private static void jsonMembers(Environment environment, @Nullable StackTracePri
members.add()
.whenNotNull(getThrowableProxy)
.usingMembers((throwableMembers) -> throwableMembers(throwableMembers, extractor));
if (hashFieldConfiguration != null) {
members.add(hashFieldConfiguration.getFieldName(), (event) -> event)
.whenNotNull(getThrowableProxy)
.as((event) -> hashFieldConfiguration.computeHash(extractThrowable(event)));
}
}

private static @Nullable Throwable extractThrowable(ILoggingEvent event) {
if (event.getThrowableProxy() instanceof ch.qos.logback.classic.spi.ThrowableProxy throwableProxy) {
return throwableProxy.getThrowable();
}
return null;
}

private static String getMessageText(String formattedMessage) {
Expand Down
Loading