diff --git a/dd-java-agent/instrumentation/spring/spring-webmvc/spring-webmvc-6.0/build.gradle b/dd-java-agent/instrumentation/spring/spring-webmvc/spring-webmvc-6.0/build.gradle index 95fde4e327f..11e6c99222a 100644 --- a/dd-java-agent/instrumentation/spring/spring-webmvc/spring-webmvc-6.0/build.gradle +++ b/dd-java-agent/instrumentation/spring/spring-webmvc/spring-webmvc-6.0/build.gradle @@ -4,7 +4,7 @@ muzzle { module = 'spring-webmvc' versions = "[6,)" javaVersion = "17" - extraDependency "jakarta.servlet:jakarta.servlet-api:5.0.0" + extraDependency "jakarta.servlet:jakarta.servlet-api:6.1.0" } } @@ -55,10 +55,10 @@ dependencies { testImplementation(libs.spock.spring) - latestDepTestImplementation group: 'org.springframework.boot', name: 'spring-boot-starter-test', version: '3.+' - latestDepTestImplementation group: 'org.springframework.boot', name: 'spring-boot-starter-web', version: '3.+' - latestDepTestImplementation group: 'org.springframework.boot', name: 'spring-boot-starter-security', version: '3.+' - latestDepTestImplementation group: 'org.springframework.boot', name: 'spring-boot-starter-websocket', version: '3.+' + latestDepTestImplementation group: 'org.springframework.boot', name: 'spring-boot-starter-test', version: '4.+' + latestDepTestImplementation group: 'org.springframework.boot', name: 'spring-boot-starter-web', version: '4.+' + latestDepTestImplementation group: 'org.springframework.boot', name: 'spring-boot-starter-security', version: '4.+' + latestDepTestImplementation group: 'org.springframework.boot', name: 'spring-boot-starter-websocket', version: '4.+' } diff --git a/dd-java-agent/instrumentation/spring/spring-webmvc/spring-webmvc-6.0/src/main/java17/datadog/trace/instrumentation/springweb6/SpringWebHttpServerDecorator.java b/dd-java-agent/instrumentation/spring/spring-webmvc/spring-webmvc-6.0/src/main/java17/datadog/trace/instrumentation/springweb6/SpringWebHttpServerDecorator.java index 79b89c1aad5..83fca460611 100644 --- a/dd-java-agent/instrumentation/spring/spring-webmvc/spring-webmvc-6.0/src/main/java17/datadog/trace/instrumentation/springweb6/SpringWebHttpServerDecorator.java +++ b/dd-java-agent/instrumentation/spring/spring-webmvc/spring-webmvc-6.0/src/main/java17/datadog/trace/instrumentation/springweb6/SpringWebHttpServerDecorator.java @@ -101,6 +101,11 @@ protected int status(final HttpServletResponse httpServletResponse) { return httpServletResponse.getStatus(); } + // TODO Switch to HandlerMapping.API_VERSION_ATTRIBUTE once compile baseline moves to Spring + // Framework 7. + private static final String API_VERSION_ATTRIBUTE = + "org.springframework.web.servlet.HandlerMapping.apiVersion"; + @Override public AgentSpan onRequest( final AgentSpan span, @@ -117,6 +122,10 @@ public AgentSpan onRequest( request.setAttribute(DD_FILTERED_SPRING_ROUTE_ALREADY_APPLIED, true); HTTP_RESOURCE_DECORATOR.withRoute(span, method, bestMatchingPattern.toString()); } + final Object apiVersion = request.getAttribute(API_VERSION_ATTRIBUTE); + if (apiVersion instanceof String && !((String) apiVersion).isEmpty()) { + span.setTag("http.api_version", (String) apiVersion); + } } return span; } diff --git a/dd-java-agent/instrumentation/spring/spring-webmvc/spring-webmvc-6.0/src/test/java/datadog/trace/instrumentation/springweb6/SpringWebHttpServerDecoratorTest.java b/dd-java-agent/instrumentation/spring/spring-webmvc/spring-webmvc-6.0/src/test/java/datadog/trace/instrumentation/springweb6/SpringWebHttpServerDecoratorTest.java new file mode 100644 index 00000000000..dd037110dc3 --- /dev/null +++ b/dd-java-agent/instrumentation/spring/spring-webmvc/spring-webmvc-6.0/src/test/java/datadog/trace/instrumentation/springweb6/SpringWebHttpServerDecoratorTest.java @@ -0,0 +1,54 @@ +package datadog.trace.instrumentation.springweb6; + +import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import datadog.trace.bootstrap.instrumentation.api.AgentSpan; +import jakarta.servlet.http.HttpServletRequest; +import org.junit.jupiter.api.Test; + +class SpringWebHttpServerDecoratorTest { + + private static final String API_VERSION_ATTRIBUTE = + "org.springframework.web.servlet.HandlerMapping.apiVersion"; + + @Test + void setsHttpApiVersionTagWhenAttributePresent() { + HttpServletRequest request = mock(HttpServletRequest.class); + AgentSpan span = mock(AgentSpan.class); + when(request.getMethod()).thenReturn("GET"); + when(request.getAttribute(API_VERSION_ATTRIBUTE)).thenReturn("v1"); + + SpringWebHttpServerDecorator.DECORATE.onRequest(span, request, request, null); + + verify(span).setTag("http.api_version", "v1"); + } + + @Test + void doesNotSetHttpApiVersionTagWhenAttributeAbsent() { + HttpServletRequest request = mock(HttpServletRequest.class); + AgentSpan span = mock(AgentSpan.class); + when(request.getMethod()).thenReturn("GET"); + when(request.getAttribute(API_VERSION_ATTRIBUTE)).thenReturn(null); + + SpringWebHttpServerDecorator.DECORATE.onRequest(span, request, request, null); + + verify(span, never()).setTag(eq("http.api_version"), anyString()); + } + + @Test + void doesNotSetHttpApiVersionTagWhenAttributeEmpty() { + HttpServletRequest request = mock(HttpServletRequest.class); + AgentSpan span = mock(AgentSpan.class); + when(request.getMethod()).thenReturn("GET"); + when(request.getAttribute(API_VERSION_ATTRIBUTE)).thenReturn(""); + + SpringWebHttpServerDecorator.DECORATE.onRequest(span, request, request, null); + + verify(span, never()).setTag(eq("http.api_version"), anyString()); + } +}