diff --git a/config/checkstyle/checkstyle-suppressions.xml b/config/checkstyle/checkstyle-suppressions.xml
index 929c586a5c9..eac6829276e 100644
--- a/config/checkstyle/checkstyle-suppressions.xml
+++ b/config/checkstyle/checkstyle-suppressions.xml
@@ -79,4 +79,7 @@
+
+
+
diff --git a/core/spring-boot/build.gradle b/core/spring-boot/build.gradle
index a32fd204b66..01010c00564 100644
--- a/core/spring-boot/build.gradle
+++ b/core/spring-boot/build.gradle
@@ -87,6 +87,12 @@ tasks.named("checkFormatMain") {
source(fileTree("src/main/javaTemplates"))
}
+tasks.named("compileJava") {
+ // Provide the project coordinates to the `GraalVmProcessor`:
+ options.compilerArgs << '-Alog4j.graalvm.groupId=org.springframework.boot'
+ options.compilerArgs << '-Alog4j.graalvm.artifactId=spring-boot-log4j'
+}
+
plugins.withType(EclipsePlugin) {
eclipse {
synchronizationTasks syncJavaTemplates
diff --git a/core/spring-boot/src/main/java/org/springframework/boot/logging/log4j2/ElasticCommonSchemaStructuredLogFormatter.java b/core/spring-boot/src/main/java/org/springframework/boot/logging/log4j2/ElasticCommonSchemaStructuredLogFormatter.java
index 5fb2650825d..b098795aecf 100644
--- a/core/spring-boot/src/main/java/org/springframework/boot/logging/log4j2/ElasticCommonSchemaStructuredLogFormatter.java
+++ b/core/spring-boot/src/main/java/org/springframework/boot/logging/log4j2/ElasticCommonSchemaStructuredLogFormatter.java
@@ -22,7 +22,6 @@ import java.util.TreeSet;
import org.apache.logging.log4j.Level;
import org.apache.logging.log4j.Marker;
import org.apache.logging.log4j.core.LogEvent;
-import org.apache.logging.log4j.core.impl.ThrowableProxy;
import org.apache.logging.log4j.core.time.Instant;
import org.apache.logging.log4j.util.ReadOnlyStringMap;
import org.jspecify.annotations.Nullable;
@@ -70,13 +69,13 @@ class ElasticCommonSchemaStructuredLogFormatter extends JsonWriterStructuredLogF
members.add("message", LogEvent::getMessage).as(StructuredMessage::get);
members.from(LogEvent::getContextData)
.usingPairs(contextPairs.nested(ElasticCommonSchemaStructuredLogFormatter::addContextDataPairs));
- members.from(LogEvent::getThrownProxy).whenNotNull().usingMembers((thrownProxyMembers) -> {
- thrownProxyMembers.add("error").usingMembers((error) -> {
- error.add("type", ThrowableProxy::getThrowable).whenNotNull().as(ObjectUtils::nullSafeClassName);
- error.add("message", ThrowableProxy::getMessage);
+ members.from(LogEvent::getThrown)
+ .whenNotNull()
+ .usingMembers((thrownMembers) -> thrownMembers.add("error").usingMembers((error) -> {
+ error.add("type", ObjectUtils::nullSafeClassName);
+ error.add("message", Throwable::getMessage);
error.add("stack_trace", extractor::stackTrace);
- });
- });
+ }));
members.add("tags", LogEvent::getMarker)
.whenNotNull()
.as(ElasticCommonSchemaStructuredLogFormatter::getMarkers)
diff --git a/core/spring-boot/src/main/java/org/springframework/boot/logging/log4j2/ExtendedWhitespaceThrowablePatternConverter.java b/core/spring-boot/src/main/java/org/springframework/boot/logging/log4j2/ExtendedWhitespaceThrowablePatternConverter.java
index 3b45bb66293..a4d9f8adc97 100644
--- a/core/spring-boot/src/main/java/org/springframework/boot/logging/log4j2/ExtendedWhitespaceThrowablePatternConverter.java
+++ b/core/spring-boot/src/main/java/org/springframework/boot/logging/log4j2/ExtendedWhitespaceThrowablePatternConverter.java
@@ -21,6 +21,7 @@ import org.apache.logging.log4j.core.config.Configuration;
import org.apache.logging.log4j.core.config.plugins.Plugin;
import org.apache.logging.log4j.core.pattern.ConverterKeys;
import org.apache.logging.log4j.core.pattern.ExtendedThrowablePatternConverter;
+import org.apache.logging.log4j.core.pattern.LogEventPatternConverter;
import org.apache.logging.log4j.core.pattern.PatternConverter;
import org.apache.logging.log4j.core.pattern.ThrowablePatternConverter;
import org.jspecify.annotations.Nullable;
@@ -35,24 +36,32 @@ import org.jspecify.annotations.Nullable;
*/
@Plugin(name = "ExtendedWhitespaceThrowablePatternConverter", category = PatternConverter.CATEGORY)
@ConverterKeys({ "xwEx", "xwThrowable", "xwException" })
-public final class ExtendedWhitespaceThrowablePatternConverter extends ThrowablePatternConverter {
+public final class ExtendedWhitespaceThrowablePatternConverter extends LogEventPatternConverter {
private final ExtendedThrowablePatternConverter delegate;
- private ExtendedWhitespaceThrowablePatternConverter(Configuration configuration, String @Nullable [] options) {
- super("WhitespaceExtendedThrowable", "throwable", options, configuration);
+ private final String separator;
+
+ private ExtendedWhitespaceThrowablePatternConverter(Configuration configuration, @Nullable String[] options) {
+ super("WhitespaceExtendedThrowable", "throwable");
this.delegate = ExtendedThrowablePatternConverter.newInstance(configuration, options);
+ this.separator = this.delegate.getOptions().getSeparator();
}
@Override
public void format(LogEvent event, StringBuilder buffer) {
if (event.getThrown() != null) {
- buffer.append(this.options.getSeparator());
+ buffer.append(this.separator);
this.delegate.format(event, buffer);
- buffer.append(this.options.getSeparator());
+ buffer.append(this.separator);
}
}
+ @Override
+ public boolean handlesThrowable() {
+ return true;
+ }
+
/**
* Creates a new instance of the class. Required by Log4J2.
* @param configuration current configuration
@@ -61,7 +70,7 @@ public final class ExtendedWhitespaceThrowablePatternConverter extends Throwable
* @return a new {@code WhitespaceThrowablePatternConverter}
*/
public static ExtendedWhitespaceThrowablePatternConverter newInstance(Configuration configuration,
- String @Nullable [] options) {
+ @Nullable String[] options) {
return new ExtendedWhitespaceThrowablePatternConverter(configuration, options);
}
diff --git a/core/spring-boot/src/main/java/org/springframework/boot/logging/log4j2/Extractor.java b/core/spring-boot/src/main/java/org/springframework/boot/logging/log4j2/Extractor.java
index bda41b00561..0d9fa411e6e 100644
--- a/core/spring-boot/src/main/java/org/springframework/boot/logging/log4j2/Extractor.java
+++ b/core/spring-boot/src/main/java/org/springframework/boot/logging/log4j2/Extractor.java
@@ -16,13 +16,14 @@
package org.springframework.boot.logging.log4j2;
+import java.io.PrintWriter;
+import java.io.StringWriter;
+
import org.apache.logging.log4j.core.LogEvent;
-import org.apache.logging.log4j.core.impl.ThrowableProxy;
import org.jspecify.annotations.Nullable;
import org.slf4j.event.LoggingEvent;
import org.springframework.boot.logging.StackTracePrinter;
-import org.springframework.util.Assert;
/**
* Functions to extract items from {@link LoggingEvent}.
@@ -42,19 +43,23 @@ class Extractor {
}
@Nullable String stackTrace(LogEvent event) {
- return stackTrace(event.getThrownProxy());
+ return stackTrace(event.getThrown());
}
- @Nullable String stackTrace(@Nullable ThrowableProxy throwableProxy) {
- if (throwableProxy == null) {
+ @Nullable String stackTrace(@Nullable Throwable throwable) {
+ if (throwable == null) {
return null;
}
if (this.stackTracePrinter != null) {
- Throwable throwable = throwableProxy.getThrowable();
- Assert.state(throwable != null, "Proxy must return Throwable in order to print exception");
return this.stackTracePrinter.printStackTraceToString(throwable);
}
- return throwableProxy.getExtendedStackTraceAsString();
+ return printStackTrace(throwable);
+ }
+
+ private static String printStackTrace(Throwable throwable) {
+ StringWriter stringWriter = new StringWriter();
+ throwable.printStackTrace(new PrintWriter(stringWriter));
+ return stringWriter.toString();
}
}
diff --git a/core/spring-boot/src/main/java/org/springframework/boot/logging/log4j2/GraylogExtendedLogFormatStructuredLogFormatter.java b/core/spring-boot/src/main/java/org/springframework/boot/logging/log4j2/GraylogExtendedLogFormatStructuredLogFormatter.java
index f07b46e6ba4..acd254fdabc 100644
--- a/core/spring-boot/src/main/java/org/springframework/boot/logging/log4j2/GraylogExtendedLogFormatStructuredLogFormatter.java
+++ b/core/spring-boot/src/main/java/org/springframework/boot/logging/log4j2/GraylogExtendedLogFormatStructuredLogFormatter.java
@@ -98,11 +98,11 @@ class GraylogExtendedLogFormatStructuredLogFormatter extends JsonWriterStructure
.whenNot(mapIsEmpty)
.usingPairs(contextPairs.flat(additionalFieldJoiner(),
GraylogExtendedLogFormatStructuredLogFormatter::addContextDataPairs));
- Function<@Nullable LogEvent, @Nullable Object> getThrownProxy = (event) -> (event != null)
- ? event.getThrownProxy() : null;
+ Function<@Nullable LogEvent, @Nullable Object> getThrown = (event) -> (event != null) ? event.getThrown()
+ : null;
members.add()
- .whenNotNull(getThrownProxy)
- .usingMembers((thrownProxyMembers) -> throwableMembers(thrownProxyMembers, extractor));
+ .whenNotNull(getThrown)
+ .usingMembers((thrownMembers) -> throwableMembers(thrownMembers, extractor));
}
private static String getMessageText(Message message) {
@@ -134,11 +134,9 @@ class GraylogExtendedLogFormatStructuredLogFormatter extends JsonWriterStructure
private static void throwableMembers(Members members, Extractor extractor) {
members.add("full_message", extractor::messageAndStackTrace);
- members.add("_error_type", (event) -> event.getThrownProxy().getThrowable())
- .whenNotNull()
- .as(ObjectUtils::nullSafeClassName);
+ members.add("_error_type", LogEvent::getThrown).whenNotNull().as(ObjectUtils::nullSafeClassName);
members.add("_error_stack_trace", extractor::stackTrace);
- members.add("_error_message", (event) -> event.getThrownProxy().getMessage());
+ members.add("_error_message", (event) -> event.getThrown().getMessage());
}
private static void addContextDataPairs(ContextPairs.Pairs contextPairs) {
diff --git a/core/spring-boot/src/main/java/org/springframework/boot/logging/log4j2/LogstashStructuredLogFormatter.java b/core/spring-boot/src/main/java/org/springframework/boot/logging/log4j2/LogstashStructuredLogFormatter.java
index 82985179071..94651023f58 100644
--- a/core/spring-boot/src/main/java/org/springframework/boot/logging/log4j2/LogstashStructuredLogFormatter.java
+++ b/core/spring-boot/src/main/java/org/springframework/boot/logging/log4j2/LogstashStructuredLogFormatter.java
@@ -71,7 +71,7 @@ class LogstashStructuredLogFormatter extends JsonWriterStructuredLogFormatter deserialized = deserialize(json);
String stackTrace = (String) deserialized.get("stack_trace");
assertThat(stackTrace).startsWith(
- """
- java.lang.RuntimeException: Boom
- \tat org.springframework.boot.logging.log4j2.LogstashStructuredLogFormatterTests.shouldFormatException""");
+ "java.lang.RuntimeException: Boom%n\tat org.springframework.boot.logging.log4j2.LogstashStructuredLogFormatterTests.shouldFormatException"
+ .formatted());
assertThat(json).contains(
- """
- java.lang.RuntimeException: Boom\\n\\tat org.springframework.boot.logging.log4j2.LogstashStructuredLogFormatterTests.shouldFormatException""");
+ "java.lang.RuntimeException: Boom%n\\tat org.springframework.boot.logging.log4j2.LogstashStructuredLogFormatterTests.shouldFormatException"
+ .formatted()
+ .replace("\n", "\\n")
+ .replace("\r", "\\r"));
}
@Test
diff --git a/core/spring-boot/src/test/java/org/springframework/boot/logging/log4j2/WhitespaceThrowablePatternConverterTests.java b/core/spring-boot/src/test/java/org/springframework/boot/logging/log4j2/WhitespaceThrowablePatternConverterTests.java
index 970176a04b2..782cbeae15e 100644
--- a/core/spring-boot/src/test/java/org/springframework/boot/logging/log4j2/WhitespaceThrowablePatternConverterTests.java
+++ b/core/spring-boot/src/test/java/org/springframework/boot/logging/log4j2/WhitespaceThrowablePatternConverterTests.java
@@ -19,7 +19,7 @@ package org.springframework.boot.logging.log4j2;
import org.apache.logging.log4j.core.LogEvent;
import org.apache.logging.log4j.core.config.DefaultConfiguration;
import org.apache.logging.log4j.core.impl.Log4jLogEvent;
-import org.apache.logging.log4j.core.pattern.ThrowablePatternConverter;
+import org.apache.logging.log4j.core.pattern.LogEventPatternConverter;
import org.junit.jupiter.api.Test;
import static org.assertj.core.api.Assertions.assertThat;
@@ -31,7 +31,7 @@ import static org.assertj.core.api.Assertions.assertThat;
*/
class WhitespaceThrowablePatternConverterTests {
- private final ThrowablePatternConverter converter = WhitespaceThrowablePatternConverter
+ private final LogEventPatternConverter converter = WhitespaceThrowablePatternConverter
.newInstance(new DefaultConfiguration(), new String[] {});
@Test
diff --git a/core/spring-boot/src/test/java/org/springframework/boot/logging/logback/ExtractorTests.java b/core/spring-boot/src/test/java/org/springframework/boot/logging/logback/ExtractorTests.java
index 35f445bc000..b058a9dc26b 100644
--- a/core/spring-boot/src/test/java/org/springframework/boot/logging/logback/ExtractorTests.java
+++ b/core/spring-boot/src/test/java/org/springframework/boot/logging/logback/ExtractorTests.java
@@ -46,7 +46,7 @@ class ExtractorTests {
}
@Test
- void stackTraceWhenNoPrinterPrintsUsingLoggingSystem() {
+ void stackTraceWhenNoPrinterPrintsUsingFallback() {
Extractor extractor = new Extractor(null, createConverter());
assertThat(extractor.stackTrace(createEvent())).contains("java.lang.RuntimeException: Boom!");
}
diff --git a/platform/spring-boot-dependencies/build.gradle b/platform/spring-boot-dependencies/build.gradle
index e0a0f806b6e..fd772784771 100644
--- a/platform/spring-boot-dependencies/build.gradle
+++ b/platform/spring-boot-dependencies/build.gradle
@@ -1252,23 +1252,14 @@ bom {
releaseNotes("https://github.com/liquibase/liquibase/releases/tag/v{version}")
}
}
- library("Log4j2", "2.24.3") {
+ library("Log4j2", "2.25.1") {
prohibit {
contains "-alpha"
contains "-beta"
because "we don't want alphas or betas"
}
group("org.apache.logging.log4j") {
- bom("log4j-bom") {
- permit("biz.aQute.bnd:biz.aQute.bnd.annotation")
- permit("com.github.spotbugs:spotbugs-annotations")
- permit("org.apache.logging:logging-parent")
- permit("org.apache.maven.plugin-tools:maven-plugin-annotations")
- permit("org.jspecify:jspecify")
- permit("org.osgi:org.osgi.annotation.bundle")
- permit("org.osgi:org.osgi.annotation.versioning")
- permit("org.osgi:osgi.annotation")
- }
+ bom("log4j-bom")
}
links {
site("https://logging.apache.org/log4j")
diff --git a/smoke-test/spring-boot-smoke-test-structured-logging-log4j2/src/main/java/smoketest/structuredlogging/log4j2/CustomStructuredLogFormatter.java b/smoke-test/spring-boot-smoke-test-structured-logging-log4j2/src/main/java/smoketest/structuredlogging/log4j2/CustomStructuredLogFormatter.java
index 2e7af6c1d80..239c433b6b5 100644
--- a/smoke-test/spring-boot-smoke-test-structured-logging-log4j2/src/main/java/smoketest/structuredlogging/log4j2/CustomStructuredLogFormatter.java
+++ b/smoke-test/spring-boot-smoke-test-structured-logging-log4j2/src/main/java/smoketest/structuredlogging/log4j2/CustomStructuredLogFormatter.java
@@ -16,8 +16,10 @@
package smoketest.structuredlogging.log4j2;
+import java.io.PrintWriter;
+import java.io.StringWriter;
+
import org.apache.logging.log4j.core.LogEvent;
-import org.apache.logging.log4j.core.impl.ThrowableProxy;
import org.jspecify.annotations.Nullable;
import org.springframework.boot.logging.structured.StructuredLogFormatter;
@@ -39,9 +41,11 @@ public class CustomStructuredLogFormatter implements StructuredLogFormatter