Skip to content

Commit

Permalink
Add framework info for Spring Webflux (#3936)
Browse files Browse the repository at this point in the history
* Add framework info for Spring Webflux transactions

* Update changelog

* Fix failing Spring 6 tests
  • Loading branch information
Mistborn94 authored Jan 9, 2025
1 parent d888d5a commit 7b127ba
Show file tree
Hide file tree
Showing 8 changed files with 45 additions and 5 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.asciidoc
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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 {

Expand All @@ -27,5 +28,10 @@ public Spring6ServerAnnotatedInstrumentationTest() {
}

public static class Impl extends ServerAnnotatedInstrumentationTest {

@BeforeEach
void setUp() {
expectedFrameworkVersion = "6.2.0";
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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 {

Expand All @@ -27,5 +28,9 @@ public Spring6ServerFunctionalInstrumentationTest() {
}

public static class Impl extends ServerFunctionalInstrumentationTest {
@BeforeEach
void setUp() {
expectedFrameworkVersion = "6.2.0";
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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";
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand All @@ -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);
Expand Down Expand Up @@ -209,7 +209,7 @@ private void cancelTransaction() {
if (transaction == null) {
return;
}

WebfluxHelper.setFrameworkInfo(transaction);
WebfluxHelper.setTransactionName(transaction, exchange);
WebfluxHelper.endTransaction(null, transaction, exchange);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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);

Expand Down Expand Up @@ -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;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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);

Expand All @@ -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;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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() {
Expand Down Expand Up @@ -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");
Expand Down

0 comments on commit 7b127ba

Please sign in to comment.