diff --git a/spring-cloud-loadbalancer/src/main/java/org/springframework/cloud/loadbalancer/stats/LoadBalancerTags.java b/spring-cloud-loadbalancer/src/main/java/org/springframework/cloud/loadbalancer/stats/LoadBalancerTags.java index d21672ce8..b72575952 100644 --- a/spring-cloud-loadbalancer/src/main/java/org/springframework/cloud/loadbalancer/stats/LoadBalancerTags.java +++ b/spring-cloud-loadbalancer/src/main/java/org/springframework/cloud/loadbalancer/stats/LoadBalancerTags.java @@ -25,17 +25,21 @@ import org.springframework.cloud.client.loadbalancer.RequestDataContext; import org.springframework.cloud.client.loadbalancer.ResponseData; import org.springframework.util.StringUtils; +import org.springframework.web.reactive.function.client.WebClient; /** * Utility class for building metrics tags for load-balanced calls. * * @author Olga Maciaszek-Sharma + * @author Jaroslaw Dembek * @since 3.0.0 */ final class LoadBalancerTags { static final String UNKNOWN = "UNKNOWN"; + static final String URI_TEMPLATE_ATTRIBUTE = WebClient.class.getName() + ".uriTemplate"; + private LoadBalancerTags() { throw new UnsupportedOperationException("Cannot instantiate utility class"); } @@ -70,6 +74,12 @@ private static int statusValue(ResponseData responseData) { } private static String getPath(RequestData requestData) { + if (requestData.getAttributes() != null) { + var uriTemplate = (String) requestData.getAttributes().get(URI_TEMPLATE_ATTRIBUTE); + if (uriTemplate != null) { + return uriTemplate; + } + } return requestData.getUrl() != null ? requestData.getUrl().getPath() : UNKNOWN; } diff --git a/spring-cloud-loadbalancer/src/test/java/org/springframework/cloud/loadbalancer/stats/MicrometerStatsLoadBalancerLifecycleTests.java b/spring-cloud-loadbalancer/src/test/java/org/springframework/cloud/loadbalancer/stats/MicrometerStatsLoadBalancerLifecycleTests.java index 49d8714c7..700aa7f92 100644 --- a/spring-cloud-loadbalancer/src/test/java/org/springframework/cloud/loadbalancer/stats/MicrometerStatsLoadBalancerLifecycleTests.java +++ b/spring-cloud-loadbalancer/src/test/java/org/springframework/cloud/loadbalancer/stats/MicrometerStatsLoadBalancerLifecycleTests.java @@ -18,6 +18,7 @@ import java.net.URI; import java.util.HashMap; +import java.util.Map; import io.micrometer.core.instrument.MeterRegistry; import io.micrometer.core.instrument.Tag; @@ -43,11 +44,13 @@ import static org.assertj.core.api.Assertions.assertThat; import static org.springframework.cloud.loadbalancer.stats.LoadBalancerTags.UNKNOWN; +import static org.springframework.cloud.loadbalancer.stats.LoadBalancerTags.URI_TEMPLATE_ATTRIBUTE; /** * Tests for {@link MicrometerStatsLoadBalancerLifecycle}. * * @author Olga Maciaszek-Sharma + * @author Jaroslaw Dembek */ class MicrometerStatsLoadBalancerLifecycleTests { @@ -80,6 +83,34 @@ void shouldRecordSuccessfulTimedRequest() { Tag.of("serviceInstance.port", "8080"), Tag.of("status", "200"), Tag.of("uri", "/test")); } + @Test + void shouldRecordSuccessfulTimedRequestWithUriTemplate() { + Map attributes = new HashMap<>(); + String uriTemplate = "/test/{pathParam}/test"; + attributes.put(URI_TEMPLATE_ATTRIBUTE, uriTemplate); + RequestData requestData = new RequestData(HttpMethod.GET, URI.create("http://test.org/test/123/test"), + new HttpHeaders(), new HttpHeaders(), attributes); + Request lbRequest = new DefaultRequest<>(new RequestDataContext(requestData)); + Response lbResponse = new DefaultResponse( + new DefaultServiceInstance("test-1", "test", "test.org", 8080, false, new HashMap<>())); + ResponseData responseData = new ResponseData(HttpStatus.OK, new HttpHeaders(), + new MultiValueMapAdapter<>(new HashMap<>()), requestData); + statsLifecycle.onStartRequest(lbRequest, lbResponse); + assertThat(meterRegistry.get("loadbalancer.requests.active").gauge().value()).isEqualTo(1); + + statsLifecycle.onComplete( + new CompletionContext<>(CompletionContext.Status.SUCCESS, lbRequest, lbResponse, responseData)); + + assertThat(meterRegistry.getMeters()).hasSize(2); + assertThat(meterRegistry.get("loadbalancer.requests.active").gauge().value()).isEqualTo(0); + assertThat(meterRegistry.get("loadbalancer.requests.success").timers()).hasSize(1); + assertThat(meterRegistry.get("loadbalancer.requests.success").timer().count()).isEqualTo(1); + assertThat(meterRegistry.get("loadbalancer.requests.success").timer().getId().getTags()).contains( + Tag.of("method", "GET"), Tag.of("outcome", "SUCCESS"), Tag.of("serviceId", "test"), + Tag.of("serviceInstance.host", "test.org"), Tag.of("serviceInstance.instanceId", "test-1"), + Tag.of("serviceInstance.port", "8080"), Tag.of("status", "200"), Tag.of("uri", uriTemplate)); + } + @Test void shouldRecordFailedTimedRequest() { RequestData requestData = new RequestData(HttpMethod.GET, URI.create("http://test.org/test"), new HttpHeaders(),