From 4972ba0c6350b813778e4f166747f45719ba65ef Mon Sep 17 00:00:00 2001 From: "alejandro.gonzalez" Date: Wed, 25 Mar 2026 12:49:42 +0100 Subject: [PATCH 1/7] Fix V0 HTTP client operation names to prevent duplicate endpoints in APM Endpoints view MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Common HTTP client libraries (Apache HttpClient, HttpURLConnection, etc.) were falling through to the generic "http.request" default in ClientNamingV0.operationForComponent(), causing the APM Endpoints view to show spurious duplicate endpoint rows — one for the real servlet.request server span and one for each client span with the same URL path. Each well-known HTTP client now has a distinct library-specific operation name consistent with the existing pattern for OkHttp ("okhttp.request"), Play-WS ("play-ws.request"), and Netty client ("netty.client.request"). The generic "http.request" fallback is kept for truly unknown/custom HTTP clients. Co-Authored-By: Claude Sonnet 4.6 --- .../ApacheHttpAsyncClientCallbackTest.groovy | 5 + ...acheHttpAsyncClientNullCallbackTest.groovy | 7 +- .../groovy/ApacheHttpAsyncClientTest.groovy | 4 + ...ApacheHttpClientResponseHandlerTest.groovy | 5 + .../test/groovy/ApacheHttpClientTest.groovy | 6 + .../groovy/ApacheHttpAsyncClient5Test.groovy | 4 + ...ApacheHttpClientResponseHandlerTest.groovy | 5 + .../test/groovy/ApacheHttpClientTest.groovy | 5 + .../test/groovy/CommonsHttpClientTest.groovy | 4 + .../groovy/GoogleHttpClientAsyncTest.groovy | 6 + .../test/groovy/GoogleHttpClientTest.groovy | 4 + .../groovy/GrizzlyAsyncHttpClientTest.groovy | 4 + .../HttpUrlConnectionConnectFirstTest.groovy | 7 +- ...tpUrlConnectionResponseCodeOnlyTest.groovy | 5 + .../test/groovy/HttpUrlConnectionTest.groovy | 7 +- ...HttpUrlConnectionUseCachesFalseTest.groovy | 5 + .../test/groovy/SpringRestTemplateTest.groovy | 4 + .../httpclient/JavaHttpClientAsyncTest.groovy | 2 +- .../httpclient/JavaHttpClientTest.groovy | 2 +- .../src/test/groovy/JettyClientTest.groovy | 4 + .../src/test/groovy/JettyClientTest.groovy | 4 + .../src/test/groovy/JettyClientTest.groovy | 4 + .../client/SpringWebfluxHttpClientBase.groovy | 5 + .../client/SpringWebfluxHttpClientBase.groovy | 5 + .../trace/api/naming/v0/ClientNamingV0.java | 11 ++ .../ClientNamingV0OperationNameTest.groovy | 133 ++++++++++++++++++ 26 files changed, 252 insertions(+), 5 deletions(-) create mode 100644 internal-api/src/test/groovy/datadog/trace/api/naming/ClientNamingV0OperationNameTest.groovy diff --git a/dd-java-agent/instrumentation/apache-httpclient/apache-httpasyncclient-4.0/src/test/groovy/ApacheHttpAsyncClientCallbackTest.groovy b/dd-java-agent/instrumentation/apache-httpclient/apache-httpasyncclient-4.0/src/test/groovy/ApacheHttpAsyncClientCallbackTest.groovy index 819d6a5cfe7..b407608d2ad 100644 --- a/dd-java-agent/instrumentation/apache-httpclient/apache-httpasyncclient-4.0/src/test/groovy/ApacheHttpAsyncClientCallbackTest.groovy +++ b/dd-java-agent/instrumentation/apache-httpclient/apache-httpasyncclient-4.0/src/test/groovy/ApacheHttpAsyncClientCallbackTest.groovy @@ -16,6 +16,11 @@ import java.util.concurrent.TimeUnit @Timeout(5) class ApacheHttpAsyncClientCallbackTest extends HttpClientTest implements TestingGenericHttpNamingConventions.ClientV0 { + @Override + String operation() { + "apache-httpasyncclient.request" + } + @Shared RequestConfig requestConfig = RequestConfig.custom() .setConnectTimeout(CONNECT_TIMEOUT_MS) diff --git a/dd-java-agent/instrumentation/apache-httpclient/apache-httpasyncclient-4.0/src/test/groovy/ApacheHttpAsyncClientNullCallbackTest.groovy b/dd-java-agent/instrumentation/apache-httpclient/apache-httpasyncclient-4.0/src/test/groovy/ApacheHttpAsyncClientNullCallbackTest.groovy index 3cd39faf5ae..b2ea939ff7f 100644 --- a/dd-java-agent/instrumentation/apache-httpclient/apache-httpasyncclient-4.0/src/test/groovy/ApacheHttpAsyncClientNullCallbackTest.groovy +++ b/dd-java-agent/instrumentation/apache-httpclient/apache-httpasyncclient-4.0/src/test/groovy/ApacheHttpAsyncClientNullCallbackTest.groovy @@ -11,7 +11,12 @@ import spock.lang.Timeout import java.util.concurrent.Future @Timeout(5) -class ApacheHttpAsyncClientNullCallbackTest extends HttpClientTest implements TestingGenericHttpNamingConventions.ClientV0{ +class ApacheHttpAsyncClientNullCallbackTest extends HttpClientTest implements TestingGenericHttpNamingConventions.ClientV0 { + + @Override + String operation() { + "apache-httpasyncclient.request" + } @Shared RequestConfig requestConfig = RequestConfig.custom() diff --git a/dd-java-agent/instrumentation/apache-httpclient/apache-httpasyncclient-4.0/src/test/groovy/ApacheHttpAsyncClientTest.groovy b/dd-java-agent/instrumentation/apache-httpclient/apache-httpasyncclient-4.0/src/test/groovy/ApacheHttpAsyncClientTest.groovy index bc57def64cf..fe65cfcc508 100644 --- a/dd-java-agent/instrumentation/apache-httpclient/apache-httpasyncclient-4.0/src/test/groovy/ApacheHttpAsyncClientTest.groovy +++ b/dd-java-agent/instrumentation/apache-httpclient/apache-httpasyncclient-4.0/src/test/groovy/ApacheHttpAsyncClientTest.groovy @@ -95,6 +95,10 @@ abstract class ApacheHttpAsyncClientTest extends HttpClientTest { } class ApacheHttpAsyncClientV0Test extends ApacheHttpAsyncClientTest implements TestingGenericHttpNamingConventions.ClientV0 { + @Override + String operation() { + "apache-httpasyncclient.request" + } } class ApacheHttpAsyncClientV1ForkedTest extends ApacheHttpAsyncClientTest implements TestingGenericHttpNamingConventions.ClientV1 { diff --git a/dd-java-agent/instrumentation/apache-httpclient/apache-httpclient-4.0/src/test/groovy/ApacheHttpClientResponseHandlerTest.groovy b/dd-java-agent/instrumentation/apache-httpclient/apache-httpclient-4.0/src/test/groovy/ApacheHttpClientResponseHandlerTest.groovy index 020ad6eee6c..467dfcf2d60 100644 --- a/dd-java-agent/instrumentation/apache-httpclient/apache-httpclient-4.0/src/test/groovy/ApacheHttpClientResponseHandlerTest.groovy +++ b/dd-java-agent/instrumentation/apache-httpclient/apache-httpclient-4.0/src/test/groovy/ApacheHttpClientResponseHandlerTest.groovy @@ -13,6 +13,11 @@ import spock.lang.Timeout @Timeout(5) class ApacheHttpClientResponseHandlerTest extends HttpClientTest implements TestingGenericHttpNamingConventions.ClientV0 { + @Override + String operation() { + "apache-httpclient.request" + } + @Shared def client = new DefaultHttpClient() diff --git a/dd-java-agent/instrumentation/apache-httpclient/apache-httpclient-4.0/src/test/groovy/ApacheHttpClientTest.groovy b/dd-java-agent/instrumentation/apache-httpclient/apache-httpclient-4.0/src/test/groovy/ApacheHttpClientTest.groovy index 1d34b88381e..efceb509af2 100644 --- a/dd-java-agent/instrumentation/apache-httpclient/apache-httpclient-4.0/src/test/groovy/ApacheHttpClientTest.groovy +++ b/dd-java-agent/instrumentation/apache-httpclient/apache-httpclient-4.0/src/test/groovy/ApacheHttpClientTest.groovy @@ -14,6 +14,12 @@ import spock.lang.Shared import spock.lang.Timeout abstract class ApacheHttpClientTest extends HttpClientTest implements TestingGenericHttpNamingConventions.ClientV0 { + + @Override + String operation() { + "apache-httpclient.request" + } + @Shared def client = new DefaultHttpClient() diff --git a/dd-java-agent/instrumentation/apache-httpclient/apache-httpclient-5.0/src/test/groovy/ApacheHttpAsyncClient5Test.groovy b/dd-java-agent/instrumentation/apache-httpclient/apache-httpclient-5.0/src/test/groovy/ApacheHttpAsyncClient5Test.groovy index f3f0dc1b9c6..8312aa5c8de 100644 --- a/dd-java-agent/instrumentation/apache-httpclient/apache-httpclient-5.0/src/test/groovy/ApacheHttpAsyncClient5Test.groovy +++ b/dd-java-agent/instrumentation/apache-httpclient/apache-httpclient-5.0/src/test/groovy/ApacheHttpAsyncClient5Test.groovy @@ -58,6 +58,10 @@ abstract class ApacheHttpAsyncClient5Test extends HttpCli } class ApacheHttpAsyncClient5NamingV0Test extends ApacheHttpAsyncClient5Test implements TestingGenericHttpNamingConventions.ClientV0 { + @Override + String operation() { + "apache-httpclient.request" + } } class ApacheHttpAsyncClient5NamingV1ForkedTest extends ApacheHttpAsyncClient5Test implements TestingGenericHttpNamingConventions.ClientV1 { diff --git a/dd-java-agent/instrumentation/apache-httpclient/apache-httpclient-5.0/src/test/groovy/ApacheHttpClientResponseHandlerTest.groovy b/dd-java-agent/instrumentation/apache-httpclient/apache-httpclient-5.0/src/test/groovy/ApacheHttpClientResponseHandlerTest.groovy index cee1a8e9770..58c3cf188e8 100644 --- a/dd-java-agent/instrumentation/apache-httpclient/apache-httpclient-5.0/src/test/groovy/ApacheHttpClientResponseHandlerTest.groovy +++ b/dd-java-agent/instrumentation/apache-httpclient/apache-httpclient-5.0/src/test/groovy/ApacheHttpClientResponseHandlerTest.groovy @@ -17,6 +17,11 @@ import java.util.concurrent.TimeUnit @Timeout(5) class ApacheHttpClientResponseHandlerTest extends HttpClientTest implements TestingGenericHttpNamingConventions.ClientV0 { + @Override + String operation() { + "apache-httpclient.request" + } + @Shared def client = HttpClients.custom() .setConnectionManager(new BasicHttpClientConnectionManager()) diff --git a/dd-java-agent/instrumentation/apache-httpclient/apache-httpclient-5.0/src/test/groovy/ApacheHttpClientTest.groovy b/dd-java-agent/instrumentation/apache-httpclient/apache-httpclient-5.0/src/test/groovy/ApacheHttpClientTest.groovy index c1a27127857..8168f3361b9 100644 --- a/dd-java-agent/instrumentation/apache-httpclient/apache-httpclient-5.0/src/test/groovy/ApacheHttpClientTest.groovy +++ b/dd-java-agent/instrumentation/apache-httpclient/apache-httpclient-5.0/src/test/groovy/ApacheHttpClientTest.groovy @@ -18,6 +18,11 @@ import java.util.concurrent.TimeUnit abstract class ApacheHttpClientTest extends HttpClientTest implements TestingGenericHttpNamingConventions.ClientV0 { + @Override + String operation() { + "apache-httpclient.request" + } + @Shared def client = HttpClients.custom() .setConnectionManager(new BasicHttpClientConnectionManager()) diff --git a/dd-java-agent/instrumentation/commons-httpclient-2.0/src/test/groovy/CommonsHttpClientTest.groovy b/dd-java-agent/instrumentation/commons-httpclient-2.0/src/test/groovy/CommonsHttpClientTest.groovy index 23ded9aa8b0..e52fad47f61 100644 --- a/dd-java-agent/instrumentation/commons-httpclient-2.0/src/test/groovy/CommonsHttpClientTest.groovy +++ b/dd-java-agent/instrumentation/commons-httpclient-2.0/src/test/groovy/CommonsHttpClientTest.groovy @@ -77,6 +77,10 @@ abstract class CommonsHttpClientTest extends HttpClientTest { } class CommonsHttpClientV0ForkedTest extends CommonsHttpClientTest implements TestingGenericHttpNamingConventions.ClientV0 { + @Override + String operation() { + "commons-http-client.request" + } } class CommonsHttpClientV1ForkedTest extends CommonsHttpClientTest implements TestingGenericHttpNamingConventions.ClientV1 { diff --git a/dd-java-agent/instrumentation/google-http-client-1.19/src/test/groovy/GoogleHttpClientAsyncTest.groovy b/dd-java-agent/instrumentation/google-http-client-1.19/src/test/groovy/GoogleHttpClientAsyncTest.groovy index ab173d90a7a..84e685f16ea 100644 --- a/dd-java-agent/instrumentation/google-http-client-1.19/src/test/groovy/GoogleHttpClientAsyncTest.groovy +++ b/dd-java-agent/instrumentation/google-http-client-1.19/src/test/groovy/GoogleHttpClientAsyncTest.groovy @@ -7,6 +7,12 @@ import spock.lang.Timeout @Flaky @Timeout(5) class GoogleHttpClientAsyncTest extends AbstractGoogleHttpClientTest implements TestingGenericHttpNamingConventions.ClientV0 { + + @Override + String operation() { + "google-http-client.request" + } + @Override HttpResponse executeRequest(HttpRequest request) { return request.executeAsync().get() diff --git a/dd-java-agent/instrumentation/google-http-client-1.19/src/test/groovy/GoogleHttpClientTest.groovy b/dd-java-agent/instrumentation/google-http-client-1.19/src/test/groovy/GoogleHttpClientTest.groovy index 5efcd6d7acb..f58d28bef27 100644 --- a/dd-java-agent/instrumentation/google-http-client-1.19/src/test/groovy/GoogleHttpClientTest.groovy +++ b/dd-java-agent/instrumentation/google-http-client-1.19/src/test/groovy/GoogleHttpClientTest.groovy @@ -13,6 +13,10 @@ abstract class GoogleHttpClientTest extends AbstractGoogleHttpClientTest { @Timeout(5) class GoogleHttpClientV0ForkedTest extends GoogleHttpClientTest implements TestingGenericHttpNamingConventions.ClientV0 { + @Override + String operation() { + "google-http-client.request" + } } @Timeout(5) diff --git a/dd-java-agent/instrumentation/grizzly/grizzly-client-1.9/src/test/groovy/GrizzlyAsyncHttpClientTest.groovy b/dd-java-agent/instrumentation/grizzly/grizzly-client-1.9/src/test/groovy/GrizzlyAsyncHttpClientTest.groovy index 3a492b30885..cd12fe81d23 100644 --- a/dd-java-agent/instrumentation/grizzly/grizzly-client-1.9/src/test/groovy/GrizzlyAsyncHttpClientTest.groovy +++ b/dd-java-agent/instrumentation/grizzly/grizzly-client-1.9/src/test/groovy/GrizzlyAsyncHttpClientTest.groovy @@ -105,6 +105,10 @@ abstract class GrizzlyAsyncHttpClientTest extends HttpClientTest { } class GrizzlyAsyncHttpClientV0Test extends GrizzlyAsyncHttpClientTest implements TestingGenericHttpNamingConventions.ClientV0 { + @Override + String operation() { + "grizzly-http-async-client.request" + } } class GrizzlyAsyncHttpClientV1ForkedTest extends GrizzlyAsyncHttpClientTest implements TestingGenericHttpNamingConventions.ClientV1 { diff --git a/dd-java-agent/instrumentation/java/java-net/java-net-1.8/src/test/groovy/HttpUrlConnectionConnectFirstTest.groovy b/dd-java-agent/instrumentation/java/java-net/java-net-1.8/src/test/groovy/HttpUrlConnectionConnectFirstTest.groovy index 19d9299a87b..824d401dc48 100644 --- a/dd-java-agent/instrumentation/java/java-net/java-net-1.8/src/test/groovy/HttpUrlConnectionConnectFirstTest.groovy +++ b/dd-java-agent/instrumentation/java/java-net/java-net-1.8/src/test/groovy/HttpUrlConnectionConnectFirstTest.groovy @@ -4,7 +4,12 @@ import datadog.trace.agent.test.naming.TestingGenericHttpNamingConventions import spock.lang.Timeout @Timeout(5) -class HttpUrlConnectionConnectFirstTest extends HttpUrlConnectionTest implements TestingGenericHttpNamingConventions.ClientV0{ +class HttpUrlConnectionConnectFirstTest extends HttpUrlConnectionTest implements TestingGenericHttpNamingConventions.ClientV0 { + + @Override + String operation() { + "http-url-connection.request" + } @Override int doRequest(String method, URI uri, Map headers, String body, Closure callback) { diff --git a/dd-java-agent/instrumentation/java/java-net/java-net-1.8/src/test/groovy/HttpUrlConnectionResponseCodeOnlyTest.groovy b/dd-java-agent/instrumentation/java/java-net/java-net-1.8/src/test/groovy/HttpUrlConnectionResponseCodeOnlyTest.groovy index 5f1e580872b..170e0aab34c 100644 --- a/dd-java-agent/instrumentation/java/java-net/java-net-1.8/src/test/groovy/HttpUrlConnectionResponseCodeOnlyTest.groovy +++ b/dd-java-agent/instrumentation/java/java-net/java-net-1.8/src/test/groovy/HttpUrlConnectionResponseCodeOnlyTest.groovy @@ -4,6 +4,11 @@ import spock.lang.Timeout @Timeout(5) class HttpUrlConnectionResponseCodeOnlyTest extends HttpUrlConnectionTest implements TestingGenericHttpNamingConventions.ClientV0 { + @Override + String operation() { + "http-url-connection.request" + } + @Override int doRequest(String method, URI uri, Map headers, String body, Closure callback) { HttpURLConnection connection = uri.toURL().openConnection() diff --git a/dd-java-agent/instrumentation/java/java-net/java-net-1.8/src/test/groovy/HttpUrlConnectionTest.groovy b/dd-java-agent/instrumentation/java/java-net/java-net-1.8/src/test/groovy/HttpUrlConnectionTest.groovy index 52d45844a33..9b1d458d17a 100644 --- a/dd-java-agent/instrumentation/java/java-net/java-net-1.8/src/test/groovy/HttpUrlConnectionTest.groovy +++ b/dd-java-agent/instrumentation/java/java-net/java-net-1.8/src/test/groovy/HttpUrlConnectionTest.groovy @@ -376,7 +376,12 @@ abstract class HttpUrlConnectionTest extends HttpClientTest { } } -class HttpUrlConnectionV0ForkedTest extends HttpUrlConnectionTest implements TestingGenericHttpNamingConventions.ClientV0 {} +class HttpUrlConnectionV0ForkedTest extends HttpUrlConnectionTest implements TestingGenericHttpNamingConventions.ClientV0 { + @Override + String operation() { + "http-url-connection.request" + } +} class HttpUrlConnectionV1ForkedTest extends HttpUrlConnectionTest implements TestingGenericHttpNamingConventions.ClientV1 { } diff --git a/dd-java-agent/instrumentation/java/java-net/java-net-1.8/src/test/groovy/HttpUrlConnectionUseCachesFalseTest.groovy b/dd-java-agent/instrumentation/java/java-net/java-net-1.8/src/test/groovy/HttpUrlConnectionUseCachesFalseTest.groovy index 41eac158fb5..9f49068c09d 100644 --- a/dd-java-agent/instrumentation/java/java-net/java-net-1.8/src/test/groovy/HttpUrlConnectionUseCachesFalseTest.groovy +++ b/dd-java-agent/instrumentation/java/java-net/java-net-1.8/src/test/groovy/HttpUrlConnectionUseCachesFalseTest.groovy @@ -6,6 +6,11 @@ import spock.lang.Timeout @Timeout(5) class HttpUrlConnectionUseCachesFalseTest extends HttpUrlConnectionTest implements TestingGenericHttpNamingConventions.ClientV0 { + @Override + String operation() { + "http-url-connection.request" + } + @Override int doRequest(String method, URI uri, Map headers, String body, Closure callback) { HttpURLConnection connection = uri.toURL().openConnection() diff --git a/dd-java-agent/instrumentation/java/java-net/java-net-1.8/src/test/groovy/SpringRestTemplateTest.groovy b/dd-java-agent/instrumentation/java/java-net/java-net-1.8/src/test/groovy/SpringRestTemplateTest.groovy index c34ac5c4eed..af8b84b2ad5 100644 --- a/dd-java-agent/instrumentation/java/java-net/java-net-1.8/src/test/groovy/SpringRestTemplateTest.groovy +++ b/dd-java-agent/instrumentation/java/java-net/java-net-1.8/src/test/groovy/SpringRestTemplateTest.groovy @@ -64,6 +64,10 @@ abstract class SpringRestTemplateTest extends HttpClientTest { } class SpringRestTemplateV0ForkedTest extends SpringRestTemplateTest implements TestingGenericHttpNamingConventions.ClientV0 { + @Override + String operation() { + "http-url-connection.request" + } } class SpringRestTemplateV1ForkedTest extends SpringRestTemplateTest implements TestingGenericHttpNamingConventions.ClientV1 { diff --git a/dd-java-agent/instrumentation/java/java-net/java-net-11.0/src/test/groovy/datadog/trace/instrumentation/httpclient/JavaHttpClientAsyncTest.groovy b/dd-java-agent/instrumentation/java/java-net/java-net-11.0/src/test/groovy/datadog/trace/instrumentation/httpclient/JavaHttpClientAsyncTest.groovy index 07750123db6..a78dea042f5 100644 --- a/dd-java-agent/instrumentation/java/java-net/java-net-11.0/src/test/groovy/datadog/trace/instrumentation/httpclient/JavaHttpClientAsyncTest.groovy +++ b/dd-java-agent/instrumentation/java/java-net/java-net-11.0/src/test/groovy/datadog/trace/instrumentation/httpclient/JavaHttpClientAsyncTest.groovy @@ -33,6 +33,6 @@ class JavaHttpClientAsyncTest extends JavaHttpClientTest { @Override String operation() { - return "http.request" + return "java-http-client.request" } } diff --git a/dd-java-agent/instrumentation/java/java-net/java-net-11.0/src/test/groovy/datadog/trace/instrumentation/httpclient/JavaHttpClientTest.groovy b/dd-java-agent/instrumentation/java/java-net/java-net-11.0/src/test/groovy/datadog/trace/instrumentation/httpclient/JavaHttpClientTest.groovy index c666e0d0efa..d4c1e12f9f7 100644 --- a/dd-java-agent/instrumentation/java/java-net/java-net-11.0/src/test/groovy/datadog/trace/instrumentation/httpclient/JavaHttpClientTest.groovy +++ b/dd-java-agent/instrumentation/java/java-net/java-net-11.0/src/test/groovy/datadog/trace/instrumentation/httpclient/JavaHttpClientTest.groovy @@ -86,7 +86,7 @@ class JavaHttpClientV0Test extends JavaHttpClientTest { @Override String operation() { - return "http.request" + return "java-http-client.request" } } diff --git a/dd-java-agent/instrumentation/jetty/jetty-client/jetty-client-10.0/src/test/groovy/JettyClientTest.groovy b/dd-java-agent/instrumentation/jetty/jetty-client/jetty-client-10.0/src/test/groovy/JettyClientTest.groovy index 4b4cf055765..77bdc485ade 100644 --- a/dd-java-agent/instrumentation/jetty/jetty-client/jetty-client-10.0/src/test/groovy/JettyClientTest.groovy +++ b/dd-java-agent/instrumentation/jetty/jetty-client/jetty-client-10.0/src/test/groovy/JettyClientTest.groovy @@ -101,6 +101,10 @@ abstract class JettyClientTest extends HttpClientTest { } class JettyClientV0Test extends JettyClientTest implements TestingGenericHttpNamingConventions.ClientV0 { + @Override + String operation() { + "jetty-client.request" + } } class JettyClientV1ForkedTest extends JettyClientTest implements TestingGenericHttpNamingConventions.ClientV1 { diff --git a/dd-java-agent/instrumentation/jetty/jetty-client/jetty-client-12.0/src/test/groovy/JettyClientTest.groovy b/dd-java-agent/instrumentation/jetty/jetty-client/jetty-client-12.0/src/test/groovy/JettyClientTest.groovy index 41aa79f1a94..fede236de6e 100644 --- a/dd-java-agent/instrumentation/jetty/jetty-client/jetty-client-12.0/src/test/groovy/JettyClientTest.groovy +++ b/dd-java-agent/instrumentation/jetty/jetty-client/jetty-client-12.0/src/test/groovy/JettyClientTest.groovy @@ -102,6 +102,10 @@ abstract class JettyClientTest extends HttpClientTest { } class JettyClientV0Test extends JettyClientTest implements TestingGenericHttpNamingConventions.ClientV0 { + @Override + String operation() { + "jetty-client.request" + } } class JettyClientV1ForkedTest extends JettyClientTest implements TestingGenericHttpNamingConventions.ClientV1 { diff --git a/dd-java-agent/instrumentation/jetty/jetty-client/jetty-client-9.1/src/test/groovy/JettyClientTest.groovy b/dd-java-agent/instrumentation/jetty/jetty-client/jetty-client-9.1/src/test/groovy/JettyClientTest.groovy index 0556d94f1bf..0c9b34d2c05 100644 --- a/dd-java-agent/instrumentation/jetty/jetty-client/jetty-client-9.1/src/test/groovy/JettyClientTest.groovy +++ b/dd-java-agent/instrumentation/jetty/jetty-client/jetty-client-9.1/src/test/groovy/JettyClientTest.groovy @@ -94,6 +94,10 @@ abstract class JettyClientTest extends HttpClientTest { } class JettyClientV0Test extends JettyClientTest implements TestingGenericHttpNamingConventions.ClientV0 { + @Override + String operation() { + "jetty-client.request" + } } class JettyClientV1ForkedTest extends JettyClientTest implements TestingGenericHttpNamingConventions.ClientV1 { diff --git a/dd-java-agent/instrumentation/spring/spring-webflux/spring-webflux-5.0/src/test/groovy/dd/trace/instrumentation/springwebflux/client/SpringWebfluxHttpClientBase.groovy b/dd-java-agent/instrumentation/spring/spring-webflux/spring-webflux-5.0/src/test/groovy/dd/trace/instrumentation/springwebflux/client/SpringWebfluxHttpClientBase.groovy index 8c60fddbff3..9735731722d 100644 --- a/dd-java-agent/instrumentation/spring/spring-webflux/spring-webflux-5.0/src/test/groovy/dd/trace/instrumentation/springwebflux/client/SpringWebfluxHttpClientBase.groovy +++ b/dd-java-agent/instrumentation/spring/spring-webflux/spring-webflux-5.0/src/test/groovy/dd/trace/instrumentation/springwebflux/client/SpringWebfluxHttpClientBase.groovy @@ -21,6 +21,11 @@ import static datadog.trace.bootstrap.instrumentation.api.AgentTracer.activeSpan abstract class SpringWebfluxHttpClientBase extends HttpClientTest implements TestingGenericHttpNamingConventions.ClientV0 { + @Override + String operation() { + "spring-webflux-client.request" + } + @Override boolean useStrictTraceWrites() { // TODO fix this by making sure that spans get closed properly diff --git a/dd-java-agent/instrumentation/spring/spring-webflux/spring-webflux-6.0/src/test/groovy/dd/trace/instrumentation/springwebflux6/client/SpringWebfluxHttpClientBase.groovy b/dd-java-agent/instrumentation/spring/spring-webflux/spring-webflux-6.0/src/test/groovy/dd/trace/instrumentation/springwebflux6/client/SpringWebfluxHttpClientBase.groovy index 0048091d901..f680e702969 100644 --- a/dd-java-agent/instrumentation/spring/spring-webflux/spring-webflux-6.0/src/test/groovy/dd/trace/instrumentation/springwebflux6/client/SpringWebfluxHttpClientBase.groovy +++ b/dd-java-agent/instrumentation/spring/spring-webflux/spring-webflux-6.0/src/test/groovy/dd/trace/instrumentation/springwebflux6/client/SpringWebfluxHttpClientBase.groovy @@ -22,6 +22,11 @@ import static datadog.trace.bootstrap.instrumentation.api.AgentTracer.activeSpan abstract class SpringWebfluxHttpClientBase extends HttpClientTest implements TestingGenericHttpNamingConventions.ClientV0 { + @Override + String operation() { + "spring-webflux-client.request" + } + @Override boolean useStrictTraceWrites() { // TODO fix this by making sure that spans get closed properly diff --git a/internal-api/src/main/java/datadog/trace/api/naming/v0/ClientNamingV0.java b/internal-api/src/main/java/datadog/trace/api/naming/v0/ClientNamingV0.java index df48b449d85..adf6322fccd 100644 --- a/internal-api/src/main/java/datadog/trace/api/naming/v0/ClientNamingV0.java +++ b/internal-api/src/main/java/datadog/trace/api/naming/v0/ClientNamingV0.java @@ -29,7 +29,18 @@ public String operationForComponent(@Nonnull String component) { switch (component) { case "play-ws": case "okhttp": + case "apache-httpasyncclient": + case "commons-http-client": + case "google-http-client": + case "http-url-connection": + case "java-http-client": + case "grizzly-http-async-client": + case "spring-webflux-client": + case "jetty-client": return component + ".request"; + case "apache-httpclient": + case "apache-httpclient5": + return "apache-httpclient.request"; case "netty-client": return "netty.client.request"; case "akka-http-client": diff --git a/internal-api/src/test/groovy/datadog/trace/api/naming/ClientNamingV0OperationNameTest.groovy b/internal-api/src/test/groovy/datadog/trace/api/naming/ClientNamingV0OperationNameTest.groovy new file mode 100644 index 00000000000..f5f10b7bcfc --- /dev/null +++ b/internal-api/src/test/groovy/datadog/trace/api/naming/ClientNamingV0OperationNameTest.groovy @@ -0,0 +1,133 @@ +package datadog.trace.api.naming + +import datadog.trace.api.naming.v0.ClientNamingV0 +import datadog.trace.api.naming.v0.ServerNamingV0 +import datadog.trace.test.util.DDSpecification + +/** + * Certifies the fix for APPSEC-61670: duplicate endpoints in the APM Endpoints view caused by + * common HTTP client libraries (Apache HttpClient, HttpURLConnection, etc.) sharing the generic + * "http.request" operation name, which was ambiguous and confused the Endpoints view into + * displaying them alongside "servlet.request" server spans as separate "endpoints" for the same + * URL path. + * + * After the fix, each well-known HTTP client library has its own distinct operation name, + * consistent with the existing pattern for OkHttp ("okhttp.request"), Play-WS ("play-ws.request"), + * and Netty client ("netty.client.request"). + */ +class ClientNamingV0OperationNameTest extends DDSpecification { + + def clientNaming = new ClientNamingV0() + def serverNaming = new ServerNamingV0() + + def "well-known HTTP client components have distinct operation names — not the generic http.request"() { + expect: + clientNaming.operationForComponent(component) == expectedOperation + + where: + component | expectedOperation + "apache-httpclient" | "apache-httpclient.request" + "apache-httpclient5" | "apache-httpclient.request" + "apache-httpasyncclient" | "apache-httpasyncclient.request" + "commons-http-client" | "commons-http-client.request" + "google-http-client" | "google-http-client.request" + "http-url-connection" | "http-url-connection.request" + "java-http-client" | "java-http-client.request" + "grizzly-http-async-client"| "grizzly-http-async-client.request" + "spring-webflux-client" | "spring-webflux-client.request" + "jetty-client" | "jetty-client.request" + } + + def "pre-existing explicit entries are not affected"() { + expect: + clientNaming.operationForComponent(component) == expectedOperation + + where: + component | expectedOperation + "okhttp" | "okhttp.request" + "play-ws" | "play-ws.request" + "netty-client" | "netty.client.request" + "akka-http-client" | "akka-http.client.request" + "pekko-http-client"| "pekko-http.client.request" + "jax-rs.client" | "jax-rs.client.call" + } + + def "unknown components still fall back to http.request"() { + expect: + clientNaming.operationForComponent("some-custom-http-client") == "http.request" + clientNaming.operationForComponent("unknown") == "http.request" + } + + def "APM Endpoints view: client and server spans for the same URL produce distinct aggregation keys"() { + given: "the Endpoints view groups spans by (operationName, httpMethod, resourcePath)" + def method = "GET" + def path = "/api/users" + + and: "the server-side operation name produced by a servlet span" + def serverKey = [serverNaming.operationForComponent("java-web-servlet"), method, path] + + and: "the client-side operation name produced by the HTTP client library" + def clientKey = [clientNaming.operationForComponent(component), method, path] + + expect: "the two keys are different — no duplicate row appears in the Endpoints view" + clientKey != serverKey + + where: "every fixed HTTP client component is tested" + component << [ + "apache-httpclient", + "apache-httpclient5", + "apache-httpasyncclient", + "commons-http-client", + "google-http-client", + "http-url-connection", + "java-http-client", + "grizzly-http-async-client", + "spring-webflux-client", + "jetty-client", + ] + } + + def "fixed client operation names do not collide with servlet.request server operation"() { + given: + def servletOpName = serverNaming.operationForComponent("java-web-servlet") + + expect: "no fixed client operation name equals servlet.request" + clientNaming.operationForComponent(component) != servletOpName + + where: + component << [ + "apache-httpclient", + "apache-httpclient5", + "apache-httpasyncclient", + "commons-http-client", + "google-http-client", + "http-url-connection", + "java-http-client", + "grizzly-http-async-client", + "spring-webflux-client", + "jetty-client", + ] + } + + def "fixed client operation names do not collide with server operationForProtocol(http)"() { + given: + def httpProtocolOpName = serverNaming.operationForProtocol("http") + + expect: "no fixed client operation name equals http.request (the server protocol op)" + clientNaming.operationForComponent(component) != httpProtocolOpName + + where: + component << [ + "apache-httpclient", + "apache-httpclient5", + "apache-httpasyncclient", + "commons-http-client", + "google-http-client", + "http-url-connection", + "java-http-client", + "grizzly-http-async-client", + "spring-webflux-client", + "jetty-client", + ] + } +} From 20b4258259f3001375ac4e2a0ed70534a5276793 Mon Sep 17 00:00:00 2001 From: "alejandro.gonzalez" Date: Wed, 25 Mar 2026 14:13:53 +0100 Subject: [PATCH 2/7] Fix operation name assertions in secondary tests affected by ClientNamingV0 change Tests for AWS SDK, Elasticsearch REST client, OpenSearch, and Mule 4 each include child HTTP client spans within their trace assertions. These spans had their operation name hardcoded as "http.request" because the underlying HTTP libraries (Apache HttpClient and Grizzly async client) previously fell through to that default. Now that these components have explicit V0 operation names, update the assertions to match: apache-httpclient.request, apache-httpasyncclient.request, and grizzly-http-async-client.request accordingly. Co-Authored-By: Claude Sonnet 4.6 --- .../src/test/groovy/LegacyAWS1ClientForkedTest.groovy | 6 +++--- .../groovy/LegacyAWS0ClientForkedTest.groovy | 6 +++--- .../src/test/groovy/LegacyAws2ClientForkedTest.groovy | 4 ++-- .../src/test/groovy/LegacySqsClientForkedTest.groovy | 10 +++++----- .../src/test/groovy/LegacySqsClientForkedTest.groovy | 10 +++++----- .../groovy/Elasticsearch6RestClientTest.groovy | 2 +- .../groovy/Elasticsearch6RestClientTest.groovy | 2 +- .../test/groovy/Elasticsearch6RestClientTest.groovy | 2 +- .../test/groovy/Elasticsearch7RestClientTest.groovy | 2 +- .../src/test/groovy/mule4/MuleForkedTest.groovy | 4 ++-- .../src/test/groovy/OpensearchRestClientTest.groovy | 2 +- 11 files changed, 25 insertions(+), 25 deletions(-) diff --git a/dd-java-agent/instrumentation/aws-java/aws-java-sdk-1.11/src/test/groovy/LegacyAWS1ClientForkedTest.groovy b/dd-java-agent/instrumentation/aws-java/aws-java-sdk-1.11/src/test/groovy/LegacyAWS1ClientForkedTest.groovy index 2a4a9d8a869..0a2ac08779c 100644 --- a/dd-java-agent/instrumentation/aws-java/aws-java-sdk-1.11/src/test/groovy/LegacyAWS1ClientForkedTest.groovy +++ b/dd-java-agent/instrumentation/aws-java/aws-java-sdk-1.11/src/test/groovy/LegacyAWS1ClientForkedTest.groovy @@ -179,7 +179,7 @@ class LegacyAWS1ClientForkedTest extends InstrumentationSpecification { } } span { - operationName "http.request" + operationName "apache-httpclient.request" resourceName "$method $path" spanType DDSpanTypes.HTTP_CLIENT errored false @@ -291,7 +291,7 @@ class LegacyAWS1ClientForkedTest extends InstrumentationSpecification { } } span { - operationName "http.request" + operationName "apache-httpclient.request" resourceName "$method /$url" spanType DDSpanTypes.HTTP_CLIENT errored true @@ -418,7 +418,7 @@ class LegacyAWS1ClientForkedTest extends InstrumentationSpecification { } (1..4).each { span { - operationName "http.request" + operationName "apache-httpclient.request" resourceName "GET /someBucket/someKey" spanType DDSpanTypes.HTTP_CLIENT errored true diff --git a/dd-java-agent/instrumentation/aws-java/aws-java-sdk-1.11/src/test_before_1_11_106/groovy/LegacyAWS0ClientForkedTest.groovy b/dd-java-agent/instrumentation/aws-java/aws-java-sdk-1.11/src/test_before_1_11_106/groovy/LegacyAWS0ClientForkedTest.groovy index 6c79836fae5..65511bb14de 100644 --- a/dd-java-agent/instrumentation/aws-java/aws-java-sdk-1.11/src/test_before_1_11_106/groovy/LegacyAWS0ClientForkedTest.groovy +++ b/dd-java-agent/instrumentation/aws-java/aws-java-sdk-1.11/src/test_before_1_11_106/groovy/LegacyAWS0ClientForkedTest.groovy @@ -125,7 +125,7 @@ class LegacyAWS0ClientForkedTest extends InstrumentationSpecification { } } span { - operationName "http.request" + operationName "apache-httpclient.request" resourceName "$method $path" spanType DDSpanTypes.HTTP_CLIENT errored false @@ -207,7 +207,7 @@ class LegacyAWS0ClientForkedTest extends InstrumentationSpecification { } } span { - operationName "http.request" + operationName "apache-httpclient.request" resourceName "$method /$url" spanType DDSpanTypes.HTTP_CLIENT errored true @@ -331,7 +331,7 @@ class LegacyAWS0ClientForkedTest extends InstrumentationSpecification { } (1..4).each { span { - operationName "http.request" + operationName "apache-httpclient.request" resourceName "GET /someBucket/someKey" spanType DDSpanTypes.HTTP_CLIENT errored true diff --git a/dd-java-agent/instrumentation/aws-java/aws-java-sdk-2.2/src/test/groovy/LegacyAws2ClientForkedTest.groovy b/dd-java-agent/instrumentation/aws-java/aws-java-sdk-2.2/src/test/groovy/LegacyAws2ClientForkedTest.groovy index d86991f0030..12b0bb49e13 100644 --- a/dd-java-agent/instrumentation/aws-java/aws-java-sdk-2.2/src/test/groovy/LegacyAws2ClientForkedTest.groovy +++ b/dd-java-agent/instrumentation/aws-java/aws-java-sdk-2.2/src/test/groovy/LegacyAws2ClientForkedTest.groovy @@ -156,7 +156,7 @@ class LegacyAws2ClientForkedTest extends InstrumentationSpecification { } } span { - operationName "http.request" + operationName "apache-httpclient.request" resourceName "$method $path" spanType DDSpanTypes.HTTP_CLIENT errored false @@ -421,7 +421,7 @@ class LegacyAws2ClientForkedTest extends InstrumentationSpecification { } (1..4).each { span { - operationName "http.request" + operationName "apache-httpclient.request" resourceName "GET /somebucket/somekey" spanType DDSpanTypes.HTTP_CLIENT errored true diff --git a/dd-java-agent/instrumentation/aws-java/aws-java-sqs-1.0/src/test/groovy/LegacySqsClientForkedTest.groovy b/dd-java-agent/instrumentation/aws-java/aws-java-sqs-1.0/src/test/groovy/LegacySqsClientForkedTest.groovy index 7104ab7cffe..48a5dfaa64b 100644 --- a/dd-java-agent/instrumentation/aws-java/aws-java-sqs-1.0/src/test/groovy/LegacySqsClientForkedTest.groovy +++ b/dd-java-agent/instrumentation/aws-java/aws-java-sqs-1.0/src/test/groovy/LegacySqsClientForkedTest.groovy @@ -100,7 +100,7 @@ class LegacySqsClientForkedTest extends InstrumentationSpecification { } } span { - operationName "http.request" + operationName "apache-httpclient.request" resourceName "POST /?/somequeue" spanType DDSpanTypes.HTTP_CLIENT errored false @@ -146,7 +146,7 @@ class LegacySqsClientForkedTest extends InstrumentationSpecification { } } span { - operationName "http.request" + operationName "apache-httpclient.request" resourceName "POST /?/somequeue" spanType DDSpanTypes.HTTP_CLIENT errored false @@ -229,7 +229,7 @@ class LegacySqsClientForkedTest extends InstrumentationSpecification { } } span { - operationName "http.request" + operationName "apache-httpclient.request" resourceName "POST /?/somequeue" spanType DDSpanTypes.HTTP_CLIENT errored false @@ -295,7 +295,7 @@ class LegacySqsClientForkedTest extends InstrumentationSpecification { } } span { - operationName "http.request" + operationName "apache-httpclient.request" resourceName "POST /?/somequeue" spanType DDSpanTypes.HTTP_CLIENT errored false @@ -340,7 +340,7 @@ class LegacySqsClientForkedTest extends InstrumentationSpecification { } } span { - operationName "http.request" + operationName "apache-httpclient.request" resourceName "POST /?/somequeue" spanType DDSpanTypes.HTTP_CLIENT errored false diff --git a/dd-java-agent/instrumentation/aws-java/aws-java-sqs-2.0/src/test/groovy/LegacySqsClientForkedTest.groovy b/dd-java-agent/instrumentation/aws-java/aws-java-sqs-2.0/src/test/groovy/LegacySqsClientForkedTest.groovy index 45733f7ac85..3219c2eb0c3 100644 --- a/dd-java-agent/instrumentation/aws-java/aws-java-sqs-2.0/src/test/groovy/LegacySqsClientForkedTest.groovy +++ b/dd-java-agent/instrumentation/aws-java/aws-java-sqs-2.0/src/test/groovy/LegacySqsClientForkedTest.groovy @@ -103,7 +103,7 @@ class LegacySqsClientForkedTest extends InstrumentationSpecification { } } span { - operationName "http.request" + operationName "apache-httpclient.request" resourceName "POST /" spanType DDSpanTypes.HTTP_CLIENT errored false @@ -149,7 +149,7 @@ class LegacySqsClientForkedTest extends InstrumentationSpecification { } } span { - operationName "http.request" + operationName "apache-httpclient.request" resourceName "POST /" spanType DDSpanTypes.HTTP_CLIENT errored false @@ -231,7 +231,7 @@ class LegacySqsClientForkedTest extends InstrumentationSpecification { } } span { - operationName "http.request" + operationName "apache-httpclient.request" resourceName "POST /" spanType DDSpanTypes.HTTP_CLIENT errored false @@ -294,7 +294,7 @@ class LegacySqsClientForkedTest extends InstrumentationSpecification { } } span { - operationName "http.request" + operationName "apache-httpclient.request" resourceName "POST /" spanType DDSpanTypes.HTTP_CLIENT errored false @@ -339,7 +339,7 @@ class LegacySqsClientForkedTest extends InstrumentationSpecification { } } span { - operationName "http.request" + operationName "apache-httpclient.request" resourceName "POST /" spanType DDSpanTypes.HTTP_CLIENT errored false diff --git a/dd-java-agent/instrumentation/elasticsearch/elasticsearch-rest/elasticsearch-rest-5.0/src/latestDepTest/groovy/Elasticsearch6RestClientTest.groovy b/dd-java-agent/instrumentation/elasticsearch/elasticsearch-rest/elasticsearch-rest-5.0/src/latestDepTest/groovy/Elasticsearch6RestClientTest.groovy index fdfeb3de76b..4c931b8a917 100644 --- a/dd-java-agent/instrumentation/elasticsearch/elasticsearch-rest/elasticsearch-rest-5.0/src/latestDepTest/groovy/Elasticsearch6RestClientTest.groovy +++ b/dd-java-agent/instrumentation/elasticsearch/elasticsearch-rest/elasticsearch-rest-5.0/src/latestDepTest/groovy/Elasticsearch6RestClientTest.groovy @@ -97,7 +97,7 @@ class Elasticsearch6RestClientTest extends InstrumentationSpecification { span { serviceName "elasticsearch" resourceName "GET /_cluster/health" - operationName "http.request" + operationName "apache-httpasyncclient.request" spanType DDSpanTypes.HTTP_CLIENT childOf span(0) tags { diff --git a/dd-java-agent/instrumentation/elasticsearch/elasticsearch-rest/elasticsearch-rest-6.4/src/latestDepTest/groovy/Elasticsearch6RestClientTest.groovy b/dd-java-agent/instrumentation/elasticsearch/elasticsearch-rest/elasticsearch-rest-6.4/src/latestDepTest/groovy/Elasticsearch6RestClientTest.groovy index a1a05791cf1..2a644fa2c39 100644 --- a/dd-java-agent/instrumentation/elasticsearch/elasticsearch-rest/elasticsearch-rest-6.4/src/latestDepTest/groovy/Elasticsearch6RestClientTest.groovy +++ b/dd-java-agent/instrumentation/elasticsearch/elasticsearch-rest/elasticsearch-rest-6.4/src/latestDepTest/groovy/Elasticsearch6RestClientTest.groovy @@ -101,7 +101,7 @@ class Elasticsearch6RestClientTest extends InstrumentationSpecification { span { serviceName "elasticsearch" resourceName "GET _cluster/health" - operationName "http.request" + operationName "apache-httpasyncclient.request" spanType DDSpanTypes.HTTP_CLIENT childOf span(0) tags { diff --git a/dd-java-agent/instrumentation/elasticsearch/elasticsearch-rest/elasticsearch-rest-6.4/src/test/groovy/Elasticsearch6RestClientTest.groovy b/dd-java-agent/instrumentation/elasticsearch/elasticsearch-rest/elasticsearch-rest-6.4/src/test/groovy/Elasticsearch6RestClientTest.groovy index b7eaa4ab9e0..c12dd6aa00a 100644 --- a/dd-java-agent/instrumentation/elasticsearch/elasticsearch-rest/elasticsearch-rest-6.4/src/test/groovy/Elasticsearch6RestClientTest.groovy +++ b/dd-java-agent/instrumentation/elasticsearch/elasticsearch-rest/elasticsearch-rest-6.4/src/test/groovy/Elasticsearch6RestClientTest.groovy @@ -97,7 +97,7 @@ class Elasticsearch6RestClientTest extends InstrumentationSpecification { span { serviceName "elasticsearch" resourceName "GET _cluster/health" - operationName "http.request" + operationName "apache-httpasyncclient.request" spanType DDSpanTypes.HTTP_CLIENT childOf span(0) tags { diff --git a/dd-java-agent/instrumentation/elasticsearch/elasticsearch-rest/elasticsearch-rest-7.0/src/test/groovy/Elasticsearch7RestClientTest.groovy b/dd-java-agent/instrumentation/elasticsearch/elasticsearch-rest/elasticsearch-rest-7.0/src/test/groovy/Elasticsearch7RestClientTest.groovy index 0247f543985..fa66fa34dad 100644 --- a/dd-java-agent/instrumentation/elasticsearch/elasticsearch-rest/elasticsearch-rest-7.0/src/test/groovy/Elasticsearch7RestClientTest.groovy +++ b/dd-java-agent/instrumentation/elasticsearch/elasticsearch-rest/elasticsearch-rest-7.0/src/test/groovy/Elasticsearch7RestClientTest.groovy @@ -133,7 +133,7 @@ class Elasticsearch7RestClientTest extends InstrumentationSpecification { span { serviceName "elasticsearch" resourceName "GET /_cluster/health" - operationName "http.request" + operationName "apache-httpasyncclient.request" spanType DDSpanTypes.HTTP_CLIENT childOf span(0) tags { diff --git a/dd-java-agent/instrumentation/mule-4.5/src/test/groovy/mule4/MuleForkedTest.groovy b/dd-java-agent/instrumentation/mule-4.5/src/test/groovy/mule4/MuleForkedTest.groovy index 07f201d3cae..02d64f3e76e 100644 --- a/dd-java-agent/instrumentation/mule-4.5/src/test/groovy/mule4/MuleForkedTest.groovy +++ b/dd-java-agent/instrumentation/mule-4.5/src/test/groovy/mule4/MuleForkedTest.groovy @@ -119,7 +119,7 @@ class MuleForkedTest extends WithHttpServer { muleSpan(it, "http:request", "Http Request") span { childOfPrevious() - operationName "http.request" + operationName "grizzly-http-async-client.request" resourceName "GET /remote-client-request" spanType DDSpanTypes.HTTP_CLIENT tags { @@ -188,7 +188,7 @@ class MuleForkedTest extends WithHttpServer { requestParents.each {parent -> traceAssert.span { childOf parent - operationName "http.request" + operationName "grizzly-http-async-client.request" resourceName "GET /remote-pfe-request" spanType DDSpanTypes.HTTP_CLIENT tags { diff --git a/dd-java-agent/instrumentation/opensearch/opensearch-rest-1.0/src/test/groovy/OpensearchRestClientTest.groovy b/dd-java-agent/instrumentation/opensearch/opensearch-rest-1.0/src/test/groovy/OpensearchRestClientTest.groovy index fe8a6e8d4e3..ab103536810 100644 --- a/dd-java-agent/instrumentation/opensearch/opensearch-rest-1.0/src/test/groovy/OpensearchRestClientTest.groovy +++ b/dd-java-agent/instrumentation/opensearch/opensearch-rest-1.0/src/test/groovy/OpensearchRestClientTest.groovy @@ -105,7 +105,7 @@ class OpensearchRestClientTest extends InstrumentationSpecification { span { serviceName "opensearch" resourceName "GET /_cluster/health" - operationName "http.request" + operationName "apache-httpasyncclient.request" spanType DDSpanTypes.HTTP_CLIENT childOf span(0) tags { From 2a59e770031015969455426a19f22f62c1a54845 Mon Sep 17 00:00:00 2001 From: "alejandro.gonzalez" Date: Wed, 25 Mar 2026 14:39:06 +0100 Subject: [PATCH 3/7] Fix http.request operation name in OpenLiberty smoke tests The Spring Boot OpenLiberty smoke app uses RestTemplate backed by HttpURLConnection, which now produces "http-url-connection.request" instead of the generic "http.request" after the ClientNamingV0 fix. Co-Authored-By: Claude Sonnet 4.6 --- .../datadog/smoketest/SpringBootOpenLibertySmokeTest.groovy | 2 +- .../datadog/smoketest/SpringBootOpenLibertySmokeTest.groovy | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/dd-smoke-tests/springboot-openliberty-20/src/test/groovy/datadog/smoketest/SpringBootOpenLibertySmokeTest.groovy b/dd-smoke-tests/springboot-openliberty-20/src/test/groovy/datadog/smoketest/SpringBootOpenLibertySmokeTest.groovy index 0caf3fe7da9..5a2ced5d1e6 100644 --- a/dd-smoke-tests/springboot-openliberty-20/src/test/groovy/datadog/smoketest/SpringBootOpenLibertySmokeTest.groovy +++ b/dd-smoke-tests/springboot-openliberty-20/src/test/groovy/datadog/smoketest/SpringBootOpenLibertySmokeTest.groovy @@ -87,7 +87,7 @@ class SpringBootOpenLibertySmokeTest extends AbstractServerSmokeTest { @Override protected Set expectedTraces() { return [ - "[servlet.request[spring.handler[http.request]]]", + "[servlet.request[spring.handler[http-url-connection.request]]]", "[servlet.request[spring.handler]]" ].toSet() } diff --git a/dd-smoke-tests/springboot-openliberty-23/src/test/groovy/datadog/smoketest/SpringBootOpenLibertySmokeTest.groovy b/dd-smoke-tests/springboot-openliberty-23/src/test/groovy/datadog/smoketest/SpringBootOpenLibertySmokeTest.groovy index d7844da8f75..3ae88e2947d 100644 --- a/dd-smoke-tests/springboot-openliberty-23/src/test/groovy/datadog/smoketest/SpringBootOpenLibertySmokeTest.groovy +++ b/dd-smoke-tests/springboot-openliberty-23/src/test/groovy/datadog/smoketest/SpringBootOpenLibertySmokeTest.groovy @@ -92,7 +92,7 @@ class SpringBootOpenLibertySmokeTest extends AbstractServerSmokeTest { @Override protected Set expectedTraces() { return [ - "[smoke-test:servlet.request[smoke-test:spring.handler[smoke-test:http.request]]]", + "[smoke-test:servlet.request[smoke-test:spring.handler[smoke-test:http-url-connection.request]]]", "[smoke-test:servlet.request[smoke-test:spring.handler]]" ].toSet() } From 7d861f5dbcf04d78cd49399dfe9cf7184418e751 Mon Sep 17 00:00:00 2001 From: "alejandro.gonzalez" Date: Wed, 25 Mar 2026 15:17:24 +0100 Subject: [PATCH 4/7] Fix remaining test assertions for renamed HTTP client operation names (round 3) Update Spring WebFlux bootTest, Spring Cloud Zuul, and AWS SNS tests to use the new component-specific operation names introduced for V0 schema. Co-Authored-By: Claude Sonnet 4.6 --- .../src/test/groovy/SnsClientTest.groovy | 4 ++-- .../test/groovy/ProxyRequestHelperTest.groovy | 8 ++++---- .../bootTest/groovy/SpringWebfluxTest.groovy | 18 ++++++++--------- .../bootTest/groovy/SpringWebfluxTest.groovy | 20 +++++++++---------- 4 files changed, 25 insertions(+), 25 deletions(-) diff --git a/dd-java-agent/instrumentation/aws-java/aws-java-sns-1.0/src/test/groovy/SnsClientTest.groovy b/dd-java-agent/instrumentation/aws-java/aws-java-sns-1.0/src/test/groovy/SnsClientTest.groovy index 4aa1e6e2ddc..9253a6b9069 100644 --- a/dd-java-agent/instrumentation/aws-java/aws-java-sns-1.0/src/test/groovy/SnsClientTest.groovy +++ b/dd-java-agent/instrumentation/aws-java/aws-java-sns-1.0/src/test/groovy/SnsClientTest.groovy @@ -244,7 +244,7 @@ class SnsClientV0Test extends SnsClientTest { if ("SNS" == awsService) { return "aws.http" } - return "http.request" + return "apache-httpclient.request" } @Override @@ -289,7 +289,7 @@ class SnsClientV0DataStreamsTest extends SnsClientTest { if ("SNS" == awsService) { return "aws.http" } - return "http.request" + return "apache-httpclient.request" } @Override diff --git a/dd-java-agent/instrumentation/spring/spring-cloud-zuul-2.0/src/test/groovy/ProxyRequestHelperTest.groovy b/dd-java-agent/instrumentation/spring/spring-cloud-zuul-2.0/src/test/groovy/ProxyRequestHelperTest.groovy index d53b740787d..43a15813e82 100644 --- a/dd-java-agent/instrumentation/spring/spring-cloud-zuul-2.0/src/test/groovy/ProxyRequestHelperTest.groovy +++ b/dd-java-agent/instrumentation/spring/spring-cloud-zuul-2.0/src/test/groovy/ProxyRequestHelperTest.groovy @@ -83,7 +83,7 @@ class ProxyRequestHelperTest extends WithHttpServer def traceParent trace(2) { - clientSpan(it, null, "http.request", "spring-webflux-client", "GET", URI.create(url)) + clientSpan(it, null, "spring-webflux-client.request", "spring-webflux-client", "GET", URI.create(url)) traceParent = clientSpan(it, span(0), "netty.client.request", "netty-client", "GET", URI.create(url)) } trace(2) { diff --git a/dd-java-agent/instrumentation/spring/spring-webflux/spring-webflux-6.0/src/bootTest/groovy/SpringWebfluxTest.groovy b/dd-java-agent/instrumentation/spring/spring-webflux/spring-webflux-6.0/src/bootTest/groovy/SpringWebfluxTest.groovy index f8ee39eabc8..9979a8e7319 100644 --- a/dd-java-agent/instrumentation/spring/spring-webflux/spring-webflux-6.0/src/bootTest/groovy/SpringWebfluxTest.groovy +++ b/dd-java-agent/instrumentation/spring/spring-webflux/spring-webflux-6.0/src/bootTest/groovy/SpringWebfluxTest.groovy @@ -70,7 +70,7 @@ class SpringWebfluxHttp11Test extends InstrumentationSpecification { def traceParent sortSpansByStart() trace(2) { - clientSpan(it, null, "http.request", "spring-webflux-client", "GET", URI.create(url)) + clientSpan(it, null, "spring-webflux-client.request", "spring-webflux-client", "GET", URI.create(url)) traceParent = clientSpan(it, span(0), "netty.client.request", "netty-client", "GET", URI.create(url)) } trace(2) { @@ -151,7 +151,7 @@ class SpringWebfluxHttp11Test extends InstrumentationSpecification { sortSpansByStart() def traceParent trace(2) { - clientSpan(it, null, "http.request", "spring-webflux-client", "GET", URI.create(url)) + clientSpan(it, null, "spring-webflux-client.request", "spring-webflux-client", "GET", URI.create(url)) traceParent = clientSpan(it, span(0), "netty.client.request", "netty-client", "GET", URI.create(url)) } trace(3) { @@ -245,7 +245,7 @@ class SpringWebfluxHttp11Test extends InstrumentationSpecification { sortSpansByStart() def traceParent trace(2) { - clientSpan(it, null, "http.request", "spring-webflux-client", "GET", URI.create(url)) + clientSpan(it, null, "spring-webflux-client.request", "spring-webflux-client", "GET", URI.create(url)) traceParent = clientSpan(it, span(0), "netty.client.request", "netty-client", "GET", URI.create(url)) } trace(3) { @@ -293,7 +293,7 @@ class SpringWebfluxHttp11Test extends InstrumentationSpecification { sortSpansByStart() def traceParent trace(2) { - clientSpan(it, null, "http.request", "spring-webflux-client", "GET", URI.create(url), 404, true) + clientSpan(it, null, "spring-webflux-client.request", "spring-webflux-client", "GET", URI.create(url), 404, true) traceParent = clientSpan(it, span(0), "netty.client.request", "netty-client", "GET", URI.create(url), 404, true) } trace(2) { @@ -349,7 +349,7 @@ class SpringWebfluxHttp11Test extends InstrumentationSpecification { sortSpansByStart() def traceParent trace(2) { - clientSpan(it, null, "http.request", "spring-webflux-client", "POST", URI.create(url), 202) + clientSpan(it, null, "spring-webflux-client.request", "spring-webflux-client", "POST", URI.create(url), 202) traceParent = clientSpan(it, span(0), "netty.client.request", "netty-client", "POST", URI.create(url), 202) } trace(3) { @@ -414,7 +414,7 @@ class SpringWebfluxHttp11Test extends InstrumentationSpecification { sortSpansByStart() def traceParent trace(2) { - clientSpan(it, null, "http.request", "spring-webflux-client", "GET", URI.create(url), 500) + clientSpan(it, null, "spring-webflux-client.request", "spring-webflux-client", "GET", URI.create(url), 500) traceParent = clientSpan(it, span(0), "netty.client.request", "netty-client", "GET", URI.create(url), 500) } trace(2) { @@ -503,7 +503,7 @@ class SpringWebfluxHttp11Test extends InstrumentationSpecification { trace(2) { sortSpansByStart() - clientSpan(it, null, "http.request", "spring-webflux-client", "GET", URI.create(url), 307) + clientSpan(it, null, "spring-webflux-client.request", "spring-webflux-client", "GET", URI.create(url), 307) traceParent1 = clientSpan(it, span(0), "netty.client.request", "netty-client", "GET", URI.create(url), 307) } @@ -549,7 +549,7 @@ class SpringWebfluxHttp11Test extends InstrumentationSpecification { } trace(2) { sortSpansByStart() - clientSpan(it, null, "http.request", "spring-webflux-client", "GET", URI.create(finalUrl)) + clientSpan(it, null, "spring-webflux-client.request", "spring-webflux-client", "GET", URI.create(finalUrl)) traceParent2 = clientSpan(it, span(0), "netty.client.request", "netty-client", "GET", URI.create(finalUrl)) } trace(2) { @@ -609,7 +609,7 @@ class SpringWebfluxHttp11Test extends InstrumentationSpecification { responses.eachWithIndex { def response, int i -> def traceParent trace(2) { - clientSpan(it, null, "http.request", "spring-webflux-client", "GET", URI.create(url)) + clientSpan(it, null, "spring-webflux-client.request", "spring-webflux-client", "GET", URI.create(url)) traceParent = clientSpan(it, span(0), "netty.client.request", "netty-client", "GET", URI.create(url)) } trace(2) { @@ -684,7 +684,7 @@ class SpringWebfluxHttp11Test extends InstrumentationSpecification { def traceParent sortSpansByStart() trace(2) { - clientSpan(it, null, "http.request", "spring-webflux-client", "GET", URI.create(url), null, false, null, false, + clientSpan(it, null, "spring-webflux-client.request", "spring-webflux-client", "GET", URI.create(url), null, false, null, false, ["message": "The subscription was cancelled", "event": "cancelled"]) traceParent = clientSpan(it, span(0), "netty.client.request", "netty-client", "GET", URI.create(url), null) } From 29a41b198e2523cf727909a1f2909d92fa27de80 Mon Sep 17 00:00:00 2001 From: "alejandro.gonzalez" Date: Wed, 25 Mar 2026 16:48:32 +0100 Subject: [PATCH 5/7] Fix Twilio test assertion for renamed apache-httpclient operation name (round 4) TwilioClientV0Test.httpClientOperation() was using the generic ClientV0.operation() which returned "http.request". Update to "apache-httpclient.request" directly since Twilio uses the apache-httpclient component for its underlying HTTP requests. Co-Authored-By: Claude Sonnet 4.6 --- .../twilio-0.0.1/src/test/groovy/test/TwilioClientTest.groovy | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dd-java-agent/instrumentation/twilio-0.0.1/src/test/groovy/test/TwilioClientTest.groovy b/dd-java-agent/instrumentation/twilio-0.0.1/src/test/groovy/test/TwilioClientTest.groovy index 8eddb50db30..d289499dbc8 100644 --- a/dd-java-agent/instrumentation/twilio-0.0.1/src/test/groovy/test/TwilioClientTest.groovy +++ b/dd-java-agent/instrumentation/twilio-0.0.1/src/test/groovy/test/TwilioClientTest.groovy @@ -855,7 +855,7 @@ class TwilioClientV0Test extends TwilioClientTest { @Override String httpClientOperation() { - return new TestingGenericHttpNamingConventions.ClientV0(){}.operation() + return "apache-httpclient.request" } } From 07cec72365b05732c6b3a541d001d0f387d406fa Mon Sep 17 00:00:00 2001 From: "alejandro.gonzalez" Date: Thu, 26 Mar 2026 10:28:10 +0100 Subject: [PATCH 6/7] Fix ProxyRequestHelperTest operation name assertions for mixed HTTP clients Zuul uses Apache HttpClient for direct proxy forwards, but the backend makes nested HTTP calls using HttpURLConnection. With the ClientNamingV0 change, these now produce different operation names, requiring per-trace assertions instead of one uniform operation name. Co-Authored-By: Claude Sonnet 4.6 --- .../src/test/groovy/ProxyRequestHelperTest.groovy | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dd-java-agent/instrumentation/spring/spring-cloud-zuul-2.0/src/test/groovy/ProxyRequestHelperTest.groovy b/dd-java-agent/instrumentation/spring/spring-cloud-zuul-2.0/src/test/groovy/ProxyRequestHelperTest.groovy index 43a15813e82..c10a352b599 100644 --- a/dd-java-agent/instrumentation/spring/spring-cloud-zuul-2.0/src/test/groovy/ProxyRequestHelperTest.groovy +++ b/dd-java-agent/instrumentation/spring/spring-cloud-zuul-2.0/src/test/groovy/ProxyRequestHelperTest.groovy @@ -119,7 +119,7 @@ class ProxyRequestHelperTest extends WithHttpServer Date: Fri, 27 Mar 2026 10:48:35 +0100 Subject: [PATCH 7/7] Fix UrlConnectionV0ForkedTest operation name for http-url-connection component The URLConnection tests use HttpUrlConnectionDecorator which calls operationForComponent("http-url-connection"), now returning "http-url-connection.request" instead of the protocol-based "http.request". Co-Authored-By: Claude Sonnet 4.6 --- .../java-net-1.8/src/test/groovy/UrlConnectionTest.groovy | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dd-java-agent/instrumentation/java/java-net/java-net-1.8/src/test/groovy/UrlConnectionTest.groovy b/dd-java-agent/instrumentation/java/java-net/java-net-1.8/src/test/groovy/UrlConnectionTest.groovy index aaa83a14977..2fbd78225ba 100644 --- a/dd-java-agent/instrumentation/java/java-net/java-net-1.8/src/test/groovy/UrlConnectionTest.groovy +++ b/dd-java-agent/instrumentation/java/java-net/java-net-1.8/src/test/groovy/UrlConnectionTest.groovy @@ -118,7 +118,7 @@ class UrlConnectionV0ForkedTest extends UrlConnectionTest { @Override String operation(String protocol) { - return "${protocol}.request" + return "http-url-connection.request" } }