From 7b127ba8404b51dd93938880a9b8ee92cf22faa0 Mon Sep 17 00:00:00 2001 From: Renette Ros Date: Thu, 9 Jan 2025 14:39:26 +0200 Subject: [PATCH] Add framework info for Spring Webflux (#3936) * Add framework info for Spring Webflux transactions * Update changelog * Fix failing Spring 6 tests --- CHANGELOG.asciidoc | 1 + .../Spring6ServerAnnotatedInstrumentationTest.java | 6 ++++++ .../Spring6ServerFunctionalInstrumentationTest.java | 5 +++++ .../springwebflux/Spring6ServletContainerTest.java | 6 ++++++ .../springwebflux/TransactionAwareSubscriber.java | 6 +++--- .../apm/agent/springwebflux/WebfluxHelper.java | 10 ++++++++++ .../AbstractServerInstrumentationTest.java | 13 ++++++++++++- .../agent/springwebflux/ServletContainerTest.java | 3 ++- 8 files changed, 45 insertions(+), 5 deletions(-) diff --git a/CHANGELOG.asciidoc b/CHANGELOG.asciidoc index 6337ec9797..3f7f70dc4d 100644 --- a/CHANGELOG.asciidoc +++ b/CHANGELOG.asciidoc @@ -36,6 +36,7 @@ Use subheadings with the "=====" level for adding notes for unreleased changes: * Prevent NPE in OpenTelemetry metrics bridge in case of asynchronous agent start - {pull}3880[#3880] * Fix random Weblogic ClassNotFoundException related to thread context classloader - {pull}3870[#3870] * Skips using NOFOLLOW_LINKS file open option when running on OS/400 as it's unsupported there - {pull}3905[#3905] +* Add framework name and version for Spring Webflux transactions - {pull}3936[#3936] [[release-notes-1.x]] === Java Agent version 1.x diff --git a/apm-agent-plugins/apm-spring-webflux/apm-spring-webflux-plugin/src/test/java/co/elastic/apm/agent/springwebflux/Spring6ServerAnnotatedInstrumentationTest.java b/apm-agent-plugins/apm-spring-webflux/apm-spring-webflux-plugin/src/test/java/co/elastic/apm/agent/springwebflux/Spring6ServerAnnotatedInstrumentationTest.java index b30ca5b06f..e8573ff2b5 100644 --- a/apm-agent-plugins/apm-spring-webflux/apm-spring-webflux-plugin/src/test/java/co/elastic/apm/agent/springwebflux/Spring6ServerAnnotatedInstrumentationTest.java +++ b/apm-agent-plugins/apm-spring-webflux/apm-spring-webflux-plugin/src/test/java/co/elastic/apm/agent/springwebflux/Spring6ServerAnnotatedInstrumentationTest.java @@ -19,6 +19,7 @@ package co.elastic.apm.agent.springwebflux; import co.elastic.apm.agent.testutils.Java17OnlyTest; +import org.junit.jupiter.api.BeforeEach; public class Spring6ServerAnnotatedInstrumentationTest extends Java17OnlyTest { @@ -27,5 +28,10 @@ public Spring6ServerAnnotatedInstrumentationTest() { } public static class Impl extends ServerAnnotatedInstrumentationTest { + + @BeforeEach + void setUp() { + expectedFrameworkVersion = "6.2.0"; + } } } diff --git a/apm-agent-plugins/apm-spring-webflux/apm-spring-webflux-plugin/src/test/java/co/elastic/apm/agent/springwebflux/Spring6ServerFunctionalInstrumentationTest.java b/apm-agent-plugins/apm-spring-webflux/apm-spring-webflux-plugin/src/test/java/co/elastic/apm/agent/springwebflux/Spring6ServerFunctionalInstrumentationTest.java index c593194b20..552cdab016 100644 --- a/apm-agent-plugins/apm-spring-webflux/apm-spring-webflux-plugin/src/test/java/co/elastic/apm/agent/springwebflux/Spring6ServerFunctionalInstrumentationTest.java +++ b/apm-agent-plugins/apm-spring-webflux/apm-spring-webflux-plugin/src/test/java/co/elastic/apm/agent/springwebflux/Spring6ServerFunctionalInstrumentationTest.java @@ -19,6 +19,7 @@ package co.elastic.apm.agent.springwebflux; import co.elastic.apm.agent.testutils.Java17OnlyTest; +import org.junit.jupiter.api.BeforeEach; public class Spring6ServerFunctionalInstrumentationTest extends Java17OnlyTest { @@ -27,5 +28,9 @@ public Spring6ServerFunctionalInstrumentationTest() { } public static class Impl extends ServerFunctionalInstrumentationTest { + @BeforeEach + void setUp() { + expectedFrameworkVersion = "6.2.0"; + } } } diff --git a/apm-agent-plugins/apm-spring-webflux/apm-spring-webflux-plugin/src/test/java/co/elastic/apm/agent/springwebflux/Spring6ServletContainerTest.java b/apm-agent-plugins/apm-spring-webflux/apm-spring-webflux-plugin/src/test/java/co/elastic/apm/agent/springwebflux/Spring6ServletContainerTest.java index a62c7a2e4b..bbb72e829b 100644 --- a/apm-agent-plugins/apm-spring-webflux/apm-spring-webflux-plugin/src/test/java/co/elastic/apm/agent/springwebflux/Spring6ServletContainerTest.java +++ b/apm-agent-plugins/apm-spring-webflux/apm-spring-webflux-plugin/src/test/java/co/elastic/apm/agent/springwebflux/Spring6ServletContainerTest.java @@ -18,9 +18,15 @@ */ package co.elastic.apm.agent.springwebflux; +import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.condition.EnabledForJreRange; import org.junit.jupiter.api.condition.JRE; @EnabledForJreRange(min = JRE.JAVA_17) public class Spring6ServletContainerTest extends ServletContainerTest { + + @BeforeEach + void setUp() { + expectedFrameworkVersion = "6.2.0"; + } } diff --git a/apm-agent-plugins/apm-spring-webflux/apm-spring-webflux-spring5/src/main/java/co/elastic/apm/agent/springwebflux/TransactionAwareSubscriber.java b/apm-agent-plugins/apm-spring-webflux/apm-spring-webflux-spring5/src/main/java/co/elastic/apm/agent/springwebflux/TransactionAwareSubscriber.java index e3bc75bda2..e2d0cfa7a7 100644 --- a/apm-agent-plugins/apm-spring-webflux/apm-spring-webflux-spring5/src/main/java/co/elastic/apm/agent/springwebflux/TransactionAwareSubscriber.java +++ b/apm-agent-plugins/apm-spring-webflux/apm-spring-webflux-spring5/src/main/java/co/elastic/apm/agent/springwebflux/TransactionAwareSubscriber.java @@ -145,7 +145,7 @@ public void onError(Throwable t) { Transaction transaction = getTransaction(); doEnter("onError", transaction); try { - + WebfluxHelper.setFrameworkInfo(transaction); // We have to capture the transaction name just before it's actually ended to prevent // concurrency issues as the transaction is accessed from multiple threads when created by a servlet. WebfluxHelper.setTransactionName(transaction, exchange); @@ -167,7 +167,7 @@ public void onComplete() { Transaction transaction = getTransaction(); doEnter("onComplete", transaction); try { - + WebfluxHelper.setFrameworkInfo(transaction); // We have to capture the transaction name just before it's actually ended to prevent // concurrency issues as the transaction is accessed from multiple threads when created by a servlet. WebfluxHelper.setTransactionName(transaction, exchange); @@ -209,7 +209,7 @@ private void cancelTransaction() { if (transaction == null) { return; } - + WebfluxHelper.setFrameworkInfo(transaction); WebfluxHelper.setTransactionName(transaction, exchange); WebfluxHelper.endTransaction(null, transaction, exchange); diff --git a/apm-agent-plugins/apm-spring-webflux/apm-spring-webflux-spring5/src/main/java/co/elastic/apm/agent/springwebflux/WebfluxHelper.java b/apm-agent-plugins/apm-spring-webflux/apm-spring-webflux-spring5/src/main/java/co/elastic/apm/agent/springwebflux/WebfluxHelper.java index 01a68fd110..3cdcdc3fb1 100644 --- a/apm-agent-plugins/apm-spring-webflux/apm-spring-webflux-spring5/src/main/java/co/elastic/apm/agent/springwebflux/WebfluxHelper.java +++ b/apm-agent-plugins/apm-spring-webflux/apm-spring-webflux-spring5/src/main/java/co/elastic/apm/agent/springwebflux/WebfluxHelper.java @@ -19,6 +19,7 @@ package co.elastic.apm.agent.springwebflux; import co.elastic.apm.agent.httpserver.HttpServerHelper; +import co.elastic.apm.agent.sdk.internal.util.VersionUtils; import co.elastic.apm.agent.tracer.GlobalTracer; import co.elastic.apm.agent.tracer.metadata.PotentiallyMultiValuedMap; import co.elastic.apm.agent.tracer.util.ResultUtil; @@ -65,6 +66,8 @@ public class WebfluxHelper { + private static final String FRAMEWORK_NAME = "Spring Webflux"; + private static final Logger log = LoggerFactory.getLogger(WebfluxHelper.class); private static final Logger oneTimeResponseCodeErrorLogger = LoggerUtils.logOnce(log); @@ -182,6 +185,13 @@ public static void endTransaction(@Nullable Throwable thrown, @Nullable Transact } } + public static void setFrameworkInfo(@Nullable Transaction transaction) { + if (transaction != null) { + transaction.setFrameworkName(FRAMEWORK_NAME); + transaction.setFrameworkVersion(VersionUtils.getVersion(HandlerMethod.class, "org.springframework", "webflux")); + } + } + public static void setTransactionName(@Nullable Transaction transaction, ServerWebExchange exchange) { if (transaction == null) { return; 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 714171fc89..fe5d641a1e 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 @@ -58,6 +58,7 @@ public abstract class AbstractServerInstrumentationTest extends AbstractInstrume private static final String BASIC_AUTH_HEADER_VALUE = "Basic ZWxhc3RpYzpjaGFuZ2VtZQ=="; protected static WebFluxApplication.App app; + protected String expectedFrameworkVersion = "5.3.30"; protected GreetingWebClient client; @BeforeAll @@ -451,7 +452,11 @@ protected ErrorCaptureImpl getFirstError() { return reporter.getFirstError(200); } - static TransactionImpl checkTransaction(TransactionImpl transaction, String expectedName, String expectedMethod, int expectedStatus) { + TransactionImpl checkTransaction(TransactionImpl transaction, String expectedName, String expectedMethod, int expectedStatus) { + return checkTransaction(transaction, expectedName, expectedMethod, expectedStatus, expectedFrameworkVersion); + } + + static TransactionImpl checkTransaction(TransactionImpl transaction, String expectedName, String expectedMethod, int expectedStatus, String expectedFrameworkVersion) { assertThat(transaction.getType()).isEqualTo("request"); assertThat(transaction.getNameAsString()).isEqualTo(expectedName); @@ -464,6 +469,12 @@ static TransactionImpl checkTransaction(TransactionImpl transaction, String expe assertThat(transaction.getResult()) .isEqualTo(String.format("HTTP %dxx", expectedStatus / 100)); + assertThat(transaction.getFrameworkName()) + .isEqualTo("Spring Webflux"); + + assertThat(transaction.getFrameworkVersion()) + .isEqualTo(expectedFrameworkVersion); + return transaction; } diff --git a/apm-agent-plugins/apm-spring-webflux/apm-spring-webflux-spring5/src/test/java/co/elastic/apm/agent/springwebflux/ServletContainerTest.java b/apm-agent-plugins/apm-spring-webflux/apm-spring-webflux-spring5/src/test/java/co/elastic/apm/agent/springwebflux/ServletContainerTest.java index f1c1c75e9c..23cd8d9432 100644 --- a/apm-agent-plugins/apm-spring-webflux/apm-spring-webflux-spring5/src/test/java/co/elastic/apm/agent/springwebflux/ServletContainerTest.java +++ b/apm-agent-plugins/apm-spring-webflux/apm-spring-webflux-spring5/src/test/java/co/elastic/apm/agent/springwebflux/ServletContainerTest.java @@ -34,6 +34,7 @@ public class ServletContainerTest extends AbstractInstrumentationTest { protected static WebFluxApplication.App app; protected static GreetingWebClient client; + protected String expectedFrameworkVersion = "5.3.30"; @BeforeAll static void startApp() { @@ -74,7 +75,7 @@ void shouldOnlyCreateOneTransaction() throws InterruptedException { TransactionImpl transaction = reporter.getFirstTransaction(200); // transaction naming should be set by webflux instrumentation - AbstractServerInstrumentationTest.checkTransaction(transaction, "GET /functional/with-parameters/{id}", "GET", 200); + AbstractServerInstrumentationTest.checkTransaction(transaction, "GET /functional/with-parameters/{id}", "GET", 200, expectedFrameworkVersion); // transaction HTTP part should be provided by servlet instrumentation AbstractServerInstrumentationTest.checkUrl(client, transaction, "/with-parameters/42");