diff --git a/apm-agent-core/src/main/java/co/elastic/apm/agent/bci/IndyBootstrap.java b/apm-agent-core/src/main/java/co/elastic/apm/agent/bci/IndyBootstrap.java
index 659a5b8c23..3380bc1b06 100644
--- a/apm-agent-core/src/main/java/co/elastic/apm/agent/bci/IndyBootstrap.java
+++ b/apm-agent-core/src/main/java/co/elastic/apm/agent/bci/IndyBootstrap.java
@@ -219,6 +219,7 @@ public class IndyBootstrap {
static Method bootstrapLoggingMethod;
private static final CallDepth callDepth = CallDepth.get(IndyBootstrap.class);
+ private static final CallDepth loggingForNestedCall = CallDepth.get("indy-bootstrap-nested-logging");
private static Logger logger() {
// must not be a static field as it would initialize logging before it's ready
@@ -430,7 +431,12 @@ private static ConstantCallSite internalBootstrap(MethodHandles.Lookup lookup, S
// avoid re-entrancy and stack overflow errors
// may happen when bootstrapping an instrumentation that also gets triggered during the bootstrap
// for example, adding correlation ids to the thread context when executing logger.debug.
- logger().warn("Nested instrumented invokedynamic instruction linkage detected", new Throwable());
+ if (!loggingForNestedCall.isNestedCallAndIncrement()) {
+ // We might be unlucky and cause an infinite recurison through logger()
+ // for this reason we have the loggingForNestedCall in place to prevent this recursion
+ logger().warn("Nested instrumented invokedynamic instruction linkage detected", new Throwable());
+ }
+ loggingForNestedCall.decrement();
return null;
}
String adviceClassName = (String) args[0];
@@ -498,7 +504,7 @@ private static ConstantCallSite internalBootstrap(MethodHandles.Lookup lookup, S
// When calling findStatic now, the lookup class will be one that is loaded by the plugin class loader
MethodHandle methodHandle = indyLookup.findStatic(adviceInPluginCL, adviceMethodName, adviceMethodType);
return new ConstantCallSite(methodHandle);
- } catch (Exception e) {
+ } catch (Throwable e) {
logger().error(e.getMessage(), e);
return null;
} finally {
diff --git a/apm-agent-plugin-sdk/src/main/java/co/elastic/apm/agent/sdk/state/CallDepth.java b/apm-agent-plugin-sdk/src/main/java/co/elastic/apm/agent/sdk/state/CallDepth.java
index 72e8129253..3dcf1e8545 100644
--- a/apm-agent-plugin-sdk/src/main/java/co/elastic/apm/agent/sdk/state/CallDepth.java
+++ b/apm-agent-plugin-sdk/src/main/java/co/elastic/apm/agent/sdk/state/CallDepth.java
@@ -43,6 +43,10 @@ private CallDepth() {
public static CallDepth get(Class> adviceClass) {
// we want to return the same CallDepth instance even if the advice class has been loaded from different class loaders
String key = adviceClass.getName();
+ return get(key);
+ }
+
+ public static CallDepth get(String key) {
CallDepth callDepth = registry.get(key);
if (callDepth == null) {
registry.putIfAbsent(key, new CallDepth());
diff --git a/apm-agent-plugins/apm-spring-resttemplate/apm-spring-restclient-test/pom.xml b/apm-agent-plugins/apm-spring-resttemplate/apm-spring-restclient-test/pom.xml
index 54c2583ea9..11190aca4c 100644
--- a/apm-agent-plugins/apm-spring-resttemplate/apm-spring-restclient-test/pom.xml
+++ b/apm-agent-plugins/apm-spring-resttemplate/apm-spring-restclient-test/pom.xml
@@ -20,7 +20,7 @@
org.springframework.boot
spring-boot-dependencies
- 3.3.4
+ 3.4.0
pom
import
diff --git a/apm-agent-plugins/apm-spring-resttemplate/apm-spring-resttemplate-plugin/pom.xml b/apm-agent-plugins/apm-spring-resttemplate/apm-spring-resttemplate-plugin/pom.xml
index 95def4b7e8..415ee0ce9f 100644
--- a/apm-agent-plugins/apm-spring-resttemplate/apm-spring-resttemplate-plugin/pom.xml
+++ b/apm-agent-plugins/apm-spring-resttemplate/apm-spring-resttemplate-plugin/pom.xml
@@ -23,7 +23,7 @@
org.springframework.boot
spring-boot-dependencies
- 3.3.4
+ 3.4.0
pom
import
@@ -52,6 +52,7 @@
com.squareup.okhttp3
okhttp
+ ${version.okhttp}
test
diff --git a/apm-agent-plugins/apm-spring-webflux/apm-spring-webflux-spring5/src/main/java/co/elastic/apm/agent/springwebflux/WebfluxServletHelper.java b/apm-agent-plugins/apm-spring-webflux/apm-spring-webflux-spring5/src/main/java/co/elastic/apm/agent/springwebflux/WebfluxServletHelper.java
index cc525a7f3a..3ee10c86d7 100644
--- a/apm-agent-plugins/apm-spring-webflux/apm-spring-webflux-spring5/src/main/java/co/elastic/apm/agent/springwebflux/WebfluxServletHelper.java
+++ b/apm-agent-plugins/apm-spring-webflux/apm-spring-webflux-spring5/src/main/java/co/elastic/apm/agent/springwebflux/WebfluxServletHelper.java
@@ -18,12 +18,13 @@
*/
package co.elastic.apm.agent.springwebflux;
-import co.elastic.apm.agent.sdk.weakconcurrent.WeakKeySoftValueLoadingCache;
-import co.elastic.apm.agent.tracer.Transaction;
import co.elastic.apm.agent.sdk.logging.Logger;
import co.elastic.apm.agent.sdk.logging.LoggerFactory;
+import co.elastic.apm.agent.sdk.weakconcurrent.WeakKeySoftValueLoadingCache;
+import co.elastic.apm.agent.tracer.Transaction;
import org.springframework.http.server.reactive.AbstractServerHttpRequest;
import org.springframework.http.server.reactive.ServerHttpRequest;
+import org.springframework.http.server.reactive.ServerHttpRequestDecorator;
import org.springframework.web.server.ServerWebExchange;
import javax.annotation.Nullable;
@@ -60,19 +61,23 @@ public static Transaction> getServletTransaction(ServerWebExchange exchange) {
// While the active transaction is the one created by Servlet, it would rely on the fact that we are on the
// same thread as the one that created the transaction, which is an implementation detail.
-
- Transaction> transaction = null;
-
ServerHttpRequest exchangeRequest = exchange.getRequest();
+ return getTransactionFromRequest(exchangeRequest);
+ }
+
+ @Nullable
+ private static Transaction> getTransactionFromRequest(ServerHttpRequest exchangeRequest) {
if (exchangeRequest instanceof AbstractServerHttpRequest) {
Object nativeRequest = ((AbstractServerHttpRequest) exchangeRequest).getNativeRequest();
// note: attribute name is defined in Servlet plugin and should be kept in sync
- transaction = (Transaction>) getServletAttribute(nativeRequest, "co.elastic.apm.agent.servlet.ServletApiAdvice.transaction");
+ return (Transaction>) getServletAttribute(nativeRequest, "co.elastic.apm.agent.servlet.ServletApiAdvice.transaction");
+ } else if (exchangeRequest instanceof ServerHttpRequestDecorator) {
+ ServerHttpRequestDecorator decorator = (ServerHttpRequestDecorator) exchangeRequest;
+ return getTransactionFromRequest(decorator.getDelegate());
}
-
- return transaction;
+ return null;
}
/**
diff --git a/apm-agent-plugins/apm-spring-webflux/apm-spring-webflux-spring5/src/test/java/co/elastic/apm/agent/springwebflux/AbstractServerInstrumentationTest.java b/apm-agent-plugins/apm-spring-webflux/apm-spring-webflux-spring5/src/test/java/co/elastic/apm/agent/springwebflux/AbstractServerInstrumentationTest.java
index e27585baad..714171fc89 100644
--- a/apm-agent-plugins/apm-spring-webflux/apm-spring-webflux-spring5/src/test/java/co/elastic/apm/agent/springwebflux/AbstractServerInstrumentationTest.java
+++ b/apm-agent-plugins/apm-spring-webflux/apm-spring-webflux-spring5/src/test/java/co/elastic/apm/agent/springwebflux/AbstractServerInstrumentationTest.java
@@ -19,16 +19,16 @@
package co.elastic.apm.agent.springwebflux;
import co.elastic.apm.agent.AbstractInstrumentationTest;
+import co.elastic.apm.agent.common.util.WildcardMatcher;
import co.elastic.apm.agent.configuration.CoreConfigurationImpl;
import co.elastic.apm.agent.impl.context.RequestImpl;
import co.elastic.apm.agent.impl.context.UrlImpl;
-import co.elastic.apm.agent.impl.transaction.TransactionImpl;
-import co.elastic.apm.agent.tracer.configuration.WebConfiguration;
import co.elastic.apm.agent.impl.error.ErrorCaptureImpl;
-import co.elastic.apm.agent.common.util.WildcardMatcher;
+import co.elastic.apm.agent.impl.transaction.TransactionImpl;
import co.elastic.apm.agent.springwebflux.testapp.GreetingWebClient;
import co.elastic.apm.agent.springwebflux.testapp.WebFluxApplication;
import co.elastic.apm.agent.testutils.DisabledOnAppleSilicon;
+import co.elastic.apm.agent.tracer.configuration.WebConfiguration;
import co.elastic.apm.agent.tracer.metadata.PotentiallyMultiValuedMap;
import org.assertj.core.data.Offset;
import org.junit.jupiter.api.AfterAll;
@@ -202,7 +202,7 @@ private Predicate expectClientError(int expectedStatus) {
}
@ParameterizedTest
- @CsvSource({"GET", "POST", "PUT", "DELETE", "PATCH", "HEAD", "OPTIONS", "TRACE"})
+ @CsvSource({"GET", "POST", "PUT", "DELETE", "PATCH", "HEAD", "OPTIONS"})
void methodMapping(String method) {
client.setHeader("Authorization", BASIC_AUTH_HEADER_VALUE);
diff --git a/apm-agent-plugins/apm-spring-webflux/pom.xml b/apm-agent-plugins/apm-spring-webflux/pom.xml
index bc44112394..fb79d1aeb1 100644
--- a/apm-agent-plugins/apm-spring-webflux/pom.xml
+++ b/apm-agent-plugins/apm-spring-webflux/pom.xml
@@ -18,7 +18,7 @@
2.7.16
- 3.3.4
+ 3.4.0
diff --git a/apm-agent-plugins/apm-spring-webmvc/apm-spring-webmvc-plugin/pom.xml b/apm-agent-plugins/apm-spring-webmvc/apm-spring-webmvc-plugin/pom.xml
index f9bd1913a4..957d7c499f 100644
--- a/apm-agent-plugins/apm-spring-webmvc/apm-spring-webmvc-plugin/pom.xml
+++ b/apm-agent-plugins/apm-spring-webmvc/apm-spring-webmvc-plugin/pom.xml
@@ -22,7 +22,7 @@
org.springframework.boot
spring-boot-dependencies
- 3.3.4
+ 3.4.0
pom
import
diff --git a/apm-agent-plugins/apm-spring-webmvc/pom.xml b/apm-agent-plugins/apm-spring-webmvc/pom.xml
index a50c7f4787..cd6bb354ea 100644
--- a/apm-agent-plugins/apm-spring-webmvc/pom.xml
+++ b/apm-agent-plugins/apm-spring-webmvc/pom.xml
@@ -29,7 +29,7 @@
org.springframework.boot
spring-boot-dependencies
- 3.3.4
+ 3.4.0
pom
import
diff --git a/integration-tests/spring-boot-3/pom.xml b/integration-tests/spring-boot-3/pom.xml
index 0282518128..17417d1332 100644
--- a/integration-tests/spring-boot-3/pom.xml
+++ b/integration-tests/spring-boot-3/pom.xml
@@ -30,7 +30,7 @@
org.springframework.boot
spring-boot-dependencies
- 3.3.4
+ 3.4.0
pom
import