Skip to content

Commit 0573988

Browse files
committed
Use URI_TEMPLATE_ATTRIBUTE when available with feature flag.
1 parent 91bbde0 commit 0573988

File tree

4 files changed

+69
-15
lines changed

4 files changed

+69
-15
lines changed

spring-cloud-loadbalancer/src/main/java/org/springframework/cloud/loadbalancer/config/LoadBalancerStatsAutoConfiguration.java

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818

1919
import io.micrometer.core.instrument.MeterRegistry;
2020

21+
import org.springframework.beans.factory.annotation.Value;
2122
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
2223
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
2324
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
@@ -38,8 +39,9 @@ public class LoadBalancerStatsAutoConfiguration {
3839

3940
@Bean
4041
@ConditionalOnBean(MeterRegistry.class)
41-
public MicrometerStatsLoadBalancerLifecycle micrometerStatsLifecycle(MeterRegistry meterRegistry) {
42-
return new MicrometerStatsLoadBalancerLifecycle(meterRegistry);
42+
public MicrometerStatsLoadBalancerLifecycle micrometerStatsLifecycle(MeterRegistry meterRegistry,
43+
@Value("${spring.cloud.loadbalancer.stats.micrometer.use-uri-template-attribute: false}") boolean useUriTemplateAttribute) {
44+
return new MicrometerStatsLoadBalancerLifecycle(meterRegistry, useUriTemplateAttribute);
4345
}
4446

4547
}

spring-cloud-loadbalancer/src/main/java/org/springframework/cloud/loadbalancer/stats/LoadBalancerTags.java

Lines changed: 21 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -25,30 +25,35 @@
2525
import org.springframework.cloud.client.loadbalancer.RequestDataContext;
2626
import org.springframework.cloud.client.loadbalancer.ResponseData;
2727
import org.springframework.util.StringUtils;
28+
import org.springframework.web.reactive.function.client.WebClient;
2829

2930
/**
3031
* Utility class for building metrics tags for load-balanced calls.
3132
*
3233
* @author Olga Maciaszek-Sharma
34+
* @author Jaroslaw Dembek
3335
* @since 3.0.0
3436
*/
3537
final class LoadBalancerTags {
3638

3739
static final String UNKNOWN = "UNKNOWN";
3840

41+
static final String URI_TEMPLATE_ATTRIBUTE = WebClient.class.getName() + ".uriTemplate";
42+
3943
private LoadBalancerTags() {
4044
throw new UnsupportedOperationException("Cannot instantiate utility class");
4145
}
4246

43-
static Iterable<Tag> buildSuccessRequestTags(CompletionContext<Object, ServiceInstance, Object> completionContext) {
47+
static Iterable<Tag> buildSuccessRequestTags(CompletionContext<Object, ServiceInstance, Object> completionContext,
48+
boolean useUriTemplateAttribute) {
4449
ServiceInstance serviceInstance = completionContext.getLoadBalancerResponse().getServer();
4550
Tags tags = Tags.of(buildServiceInstanceTags(serviceInstance));
4651
Object clientResponse = completionContext.getClientResponse();
4752
if (clientResponse instanceof ResponseData responseData) {
4853
RequestData requestData = responseData.getRequestData();
4954
if (requestData != null) {
5055
tags = tags.and(valueOrUnknown("method", requestData.getHttpMethod()),
51-
valueOrUnknown("uri", getPath(requestData)));
56+
valueOrUnknown("uri", getPath(requestData, useUriTemplateAttribute)));
5257
}
5358
else {
5459
tags = tags.and(Tag.of("method", UNKNOWN), Tag.of("uri", UNKNOWN));
@@ -69,18 +74,25 @@ private static int statusValue(ResponseData responseData) {
6974
return responseData.getHttpStatus() != null ? responseData.getHttpStatus().value() : 200;
7075
}
7176

72-
private static String getPath(RequestData requestData) {
77+
private static String getPath(RequestData requestData, boolean useUriTemplateAttribute) {
78+
if (useUriTemplateAttribute && requestData.getAttributes() != null) {
79+
var uriTemplate = (String) requestData.getAttributes().get(URI_TEMPLATE_ATTRIBUTE);
80+
if (uriTemplate != null) {
81+
return uriTemplate;
82+
}
83+
}
7384
return requestData.getUrl() != null ? requestData.getUrl().getPath() : UNKNOWN;
7485
}
7586

76-
static Iterable<Tag> buildDiscardedRequestTags(
77-
CompletionContext<Object, ServiceInstance, Object> completionContext) {
87+
static Iterable<Tag> buildDiscardedRequestTags(CompletionContext<Object, ServiceInstance, Object> completionContext,
88+
boolean useUriTemplateAttribute) {
7889
if (completionContext.getLoadBalancerRequest().getContext() instanceof RequestDataContext) {
7990
RequestData requestData = ((RequestDataContext) completionContext.getLoadBalancerRequest().getContext())
8091
.getClientRequest();
8192
if (requestData != null) {
8293
return Tags.of(valueOrUnknown("method", requestData.getHttpMethod()),
83-
valueOrUnknown("uri", getPath(requestData)), valueOrUnknown("serviceId", getHost(requestData)));
94+
valueOrUnknown("uri", getPath(requestData, useUriTemplateAttribute)),
95+
valueOrUnknown("serviceId", getHost(requestData)));
8496
}
8597
}
8698
return Tags.of(valueOrUnknown("method", UNKNOWN), valueOrUnknown("uri", UNKNOWN),
@@ -92,15 +104,16 @@ private static String getHost(RequestData requestData) {
92104
return requestData.getUrl() != null ? requestData.getUrl().getHost() : UNKNOWN;
93105
}
94106

95-
static Iterable<Tag> buildFailedRequestTags(CompletionContext<Object, ServiceInstance, Object> completionContext) {
107+
static Iterable<Tag> buildFailedRequestTags(CompletionContext<Object, ServiceInstance, Object> completionContext,
108+
boolean useUriTemplateAttribute) {
96109
ServiceInstance serviceInstance = completionContext.getLoadBalancerResponse().getServer();
97110
Tags tags = Tags.of(buildServiceInstanceTags(serviceInstance)).and(exception(completionContext.getThrowable()));
98111
if (completionContext.getLoadBalancerRequest().getContext() instanceof RequestDataContext) {
99112
RequestData requestData = ((RequestDataContext) completionContext.getLoadBalancerRequest().getContext())
100113
.getClientRequest();
101114
if (requestData != null) {
102115
return tags.and(Tags.of(valueOrUnknown("method", requestData.getHttpMethod()),
103-
valueOrUnknown("uri", getPath(requestData))));
116+
valueOrUnknown("uri", getPath(requestData, useUriTemplateAttribute))));
104117
}
105118
}
106119
return tags.and(Tags.of(valueOrUnknown("method", UNKNOWN), valueOrUnknown("uri", UNKNOWN)));

spring-cloud-loadbalancer/src/main/java/org/springframework/cloud/loadbalancer/stats/MicrometerStatsLoadBalancerLifecycle.java

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -42,16 +42,20 @@
4242
* load-balanced calls.
4343
*
4444
* @author Olga Maciaszek-Sharma
45+
* @author Jaroslaw Dembek
4546
* @since 3.0.0
4647
*/
4748
public class MicrometerStatsLoadBalancerLifecycle implements LoadBalancerLifecycle<Object, Object, ServiceInstance> {
4849

4950
private final MeterRegistry meterRegistry;
5051

52+
private final boolean useUriTemplateAttribute;
53+
5154
private final ConcurrentHashMap<ServiceInstance, AtomicLong> activeRequestsPerInstance = new ConcurrentHashMap<>();
5255

53-
public MicrometerStatsLoadBalancerLifecycle(MeterRegistry meterRegistry) {
56+
public MicrometerStatsLoadBalancerLifecycle(MeterRegistry meterRegistry, boolean useUriTemplateAttribute) {
5457
this.meterRegistry = meterRegistry;
58+
this.useUriTemplateAttribute = useUriTemplateAttribute;
5559
}
5660

5761
@Override
@@ -88,7 +92,7 @@ public void onComplete(CompletionContext<Object, ServiceInstance, Object> comple
8892
long requestFinishedTimestamp = System.nanoTime();
8993
if (CompletionContext.Status.DISCARD.equals(completionContext.status())) {
9094
Counter.builder("loadbalancer.requests.discard")
91-
.tags(buildDiscardedRequestTags(completionContext))
95+
.tags(buildDiscardedRequestTags(completionContext, useUriTemplateAttribute))
9296
.register(meterRegistry)
9397
.increment();
9498
return;
@@ -102,15 +106,15 @@ public void onComplete(CompletionContext<Object, ServiceInstance, Object> comple
102106
if (requestHasBeenTimed(loadBalancerRequestContext)) {
103107
if (CompletionContext.Status.FAILED.equals(completionContext.status())) {
104108
Timer.builder("loadbalancer.requests.failed")
105-
.tags(buildFailedRequestTags(completionContext))
109+
.tags(buildFailedRequestTags(completionContext, useUriTemplateAttribute))
106110
.register(meterRegistry)
107111
.record(requestFinishedTimestamp
108112
- ((TimedRequestContext) loadBalancerRequestContext).getRequestStartTime(),
109113
TimeUnit.NANOSECONDS);
110114
return;
111115
}
112116
Timer.builder("loadbalancer.requests.success")
113-
.tags(buildSuccessRequestTags(completionContext))
117+
.tags(buildSuccessRequestTags(completionContext, useUriTemplateAttribute))
114118
.register(meterRegistry)
115119
.record(requestFinishedTimestamp
116120
- ((TimedRequestContext) loadBalancerRequestContext).getRequestStartTime(),

spring-cloud-loadbalancer/src/test/java/org/springframework/cloud/loadbalancer/stats/MicrometerStatsLoadBalancerLifecycleTests.java

Lines changed: 36 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818

1919
import java.net.URI;
2020
import java.util.HashMap;
21+
import java.util.Map;
2122

2223
import io.micrometer.core.instrument.MeterRegistry;
2324
import io.micrometer.core.instrument.Tag;
@@ -43,17 +44,23 @@
4344

4445
import static org.assertj.core.api.Assertions.assertThat;
4546
import static org.springframework.cloud.loadbalancer.stats.LoadBalancerTags.UNKNOWN;
47+
import static org.springframework.cloud.loadbalancer.stats.LoadBalancerTags.URI_TEMPLATE_ATTRIBUTE;
4648

4749
/**
4850
* Tests for {@link MicrometerStatsLoadBalancerLifecycle}.
4951
*
5052
* @author Olga Maciaszek-Sharma
53+
* @author Jaroslaw Dembek
5154
*/
5255
class MicrometerStatsLoadBalancerLifecycleTests {
5356

5457
MeterRegistry meterRegistry = new SimpleMeterRegistry();
5558

56-
MicrometerStatsLoadBalancerLifecycle statsLifecycle = new MicrometerStatsLoadBalancerLifecycle(meterRegistry);
59+
MicrometerStatsLoadBalancerLifecycle statsLifecycle = new MicrometerStatsLoadBalancerLifecycle(meterRegistry,
60+
false);
61+
62+
MicrometerStatsLoadBalancerLifecycle statsLifecycleWithUriTemplateAttributeUse = new MicrometerStatsLoadBalancerLifecycle(
63+
meterRegistry, true);
5764

5865
@Test
5966
void shouldRecordSuccessfulTimedRequest() {
@@ -80,6 +87,34 @@ void shouldRecordSuccessfulTimedRequest() {
8087
Tag.of("serviceInstance.port", "8080"), Tag.of("status", "200"), Tag.of("uri", "/test"));
8188
}
8289

90+
@Test
91+
void shouldRecordSuccessfulTimedRequestWithUriTemplate() {
92+
Map<String, Object> attributes = new HashMap<>();
93+
String uriTemplate = "/test/{pathParam}/test";
94+
attributes.put(URI_TEMPLATE_ATTRIBUTE, uriTemplate);
95+
RequestData requestData = new RequestData(HttpMethod.GET, URI.create("http://test.org/test/123/test"),
96+
new HttpHeaders(), new HttpHeaders(), attributes);
97+
Request<Object> lbRequest = new DefaultRequest<>(new RequestDataContext(requestData));
98+
Response<ServiceInstance> lbResponse = new DefaultResponse(
99+
new DefaultServiceInstance("test-1", "test", "test.org", 8080, false, new HashMap<>()));
100+
ResponseData responseData = new ResponseData(HttpStatus.OK, new HttpHeaders(),
101+
new MultiValueMapAdapter<>(new HashMap<>()), requestData);
102+
statsLifecycleWithUriTemplateAttributeUse.onStartRequest(lbRequest, lbResponse);
103+
assertThat(meterRegistry.get("loadbalancer.requests.active").gauge().value()).isEqualTo(1);
104+
105+
statsLifecycleWithUriTemplateAttributeUse.onComplete(
106+
new CompletionContext<>(CompletionContext.Status.SUCCESS, lbRequest, lbResponse, responseData));
107+
108+
assertThat(meterRegistry.getMeters()).hasSize(2);
109+
assertThat(meterRegistry.get("loadbalancer.requests.active").gauge().value()).isEqualTo(0);
110+
assertThat(meterRegistry.get("loadbalancer.requests.success").timers()).hasSize(1);
111+
assertThat(meterRegistry.get("loadbalancer.requests.success").timer().count()).isEqualTo(1);
112+
assertThat(meterRegistry.get("loadbalancer.requests.success").timer().getId().getTags()).contains(
113+
Tag.of("method", "GET"), Tag.of("outcome", "SUCCESS"), Tag.of("serviceId", "test"),
114+
Tag.of("serviceInstance.host", "test.org"), Tag.of("serviceInstance.instanceId", "test-1"),
115+
Tag.of("serviceInstance.port", "8080"), Tag.of("status", "200"), Tag.of("uri", uriTemplate));
116+
}
117+
83118
@Test
84119
void shouldRecordFailedTimedRequest() {
85120
RequestData requestData = new RequestData(HttpMethod.GET, URI.create("http://test.org/test"), new HttpHeaders(),

0 commit comments

Comments
 (0)