Skip to content

Commit bf6f36a

Browse files
committed
Apply any root URI to RestTemplate metric's URI tag
Previously, a root URI configured via RestTemplateBuilder's rootUri method and RootUriTemplateHandler was not taken into account when generated the URI tag for RestTemplate request metrics. This commit updates MetricsClientHttpRequestInterceptor to be aware of RootUriTemplateHandler and capture the URI template once the root URI has been applied. Fixes gh-25744
1 parent f8c1a73 commit bf6f36a

File tree

3 files changed

+60
-21
lines changed

3 files changed

+60
-21
lines changed

spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/metrics/web/client/RestTemplateMetricsConfigurationTests.java

Lines changed: 20 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2012-2019 the original author or authors.
2+
* Copyright 2012-2021 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -63,6 +63,16 @@ void restTemplateCreatedWithBuilderIsInstrumented() {
6363
});
6464
}
6565

66+
@Test
67+
void restTemplateWithRootUriIsInstrumented() {
68+
this.contextRunner.run((context) -> {
69+
MeterRegistry registry = context.getBean(MeterRegistry.class);
70+
RestTemplateBuilder builder = context.getBean(RestTemplateBuilder.class);
71+
builder = builder.rootUri("/root");
72+
validateRestTemplate(builder, registry, "/root");
73+
});
74+
}
75+
6676
@Test
6777
void restTemplateCanBeCustomizedManually() {
6878
this.contextRunner.run((context) -> {
@@ -130,17 +140,22 @@ private MeterRegistry getInitializedMeterRegistry(AssertableApplicationContext c
130140
}
131141

132142
private void validateRestTemplate(RestTemplateBuilder builder, MeterRegistry registry) {
133-
RestTemplate restTemplate = mockRestTemplate(builder);
143+
this.validateRestTemplate(builder, registry, "");
144+
}
145+
146+
private void validateRestTemplate(RestTemplateBuilder builder, MeterRegistry registry, String rootUri) {
147+
RestTemplate restTemplate = mockRestTemplate(builder, rootUri);
134148
assertThat(registry.find("http.client.requests").meter()).isNull();
135149
assertThat(restTemplate.getForEntity("/projects/{project}", Void.class, "spring-boot").getStatusCode())
136150
.isEqualTo(HttpStatus.OK);
137-
assertThat(registry.get("http.client.requests").tags("uri", "/projects/{project}").meter()).isNotNull();
151+
assertThat(registry.get("http.client.requests").tags("uri", rootUri + "/projects/{project}").meter())
152+
.isNotNull();
138153
}
139154

140-
private RestTemplate mockRestTemplate(RestTemplateBuilder builder) {
155+
private RestTemplate mockRestTemplate(RestTemplateBuilder builder, String rootUri) {
141156
RestTemplate restTemplate = builder.build();
142157
MockRestServiceServer server = MockRestServiceServer.createServer(restTemplate);
143-
server.expect(requestTo("/projects/spring-boot")).andRespond(withStatus(HttpStatus.OK));
158+
server.expect(requestTo(rootUri + "/projects/spring-boot")).andRespond(withStatus(HttpStatus.OK));
144159
return restTemplate;
145160
}
146161

spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/metrics/web/client/MetricsClientHttpRequestInterceptor.java

Lines changed: 27 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@
2929
import org.apache.commons.logging.LogFactory;
3030

3131
import org.springframework.boot.actuate.metrics.AutoTimer;
32+
import org.springframework.boot.web.client.RootUriTemplateHandler;
3233
import org.springframework.core.NamedThreadLocal;
3334
import org.springframework.http.HttpRequest;
3435
import org.springframework.http.client.ClientHttpRequestExecution;
@@ -100,21 +101,10 @@ public ClientHttpResponse intercept(HttpRequest request, byte[] body, ClientHttp
100101
}
101102

102103
UriTemplateHandler createUriTemplateHandler(UriTemplateHandler delegate) {
103-
return new UriTemplateHandler() {
104-
105-
@Override
106-
public URI expand(String url, Map<String, ?> arguments) {
107-
urlTemplate.get().push(url);
108-
return delegate.expand(url, arguments);
109-
}
110-
111-
@Override
112-
public URI expand(String url, Object... arguments) {
113-
urlTemplate.get().push(url);
114-
return delegate.expand(url, arguments);
115-
}
116-
117-
};
104+
if (delegate instanceof RootUriTemplateHandler) {
105+
return ((RootUriTemplateHandler) delegate).withHandlerWrapper(CapturingUriTemplateHandler::new);
106+
}
107+
return new CapturingUriTemplateHandler(delegate);
118108
}
119109

120110
private Timer.Builder getTimeBuilder(HttpRequest request, ClientHttpResponse response) {
@@ -123,6 +113,28 @@ private Timer.Builder getTimeBuilder(HttpRequest request, ClientHttpResponse res
123113
.description("Timer of RestTemplate operation");
124114
}
125115

116+
private static final class CapturingUriTemplateHandler implements UriTemplateHandler {
117+
118+
private final UriTemplateHandler delegate;
119+
120+
private CapturingUriTemplateHandler(UriTemplateHandler delegate) {
121+
this.delegate = delegate;
122+
}
123+
124+
@Override
125+
public URI expand(String url, Map<String, ?> arguments) {
126+
urlTemplate.get().push(url);
127+
return this.delegate.expand(url, arguments);
128+
}
129+
130+
@Override
131+
public URI expand(String url, Object... arguments) {
132+
urlTemplate.get().push(url);
133+
return this.delegate.expand(url, arguments);
134+
}
135+
136+
}
137+
126138
private static final class UrlTemplateThreadLocal extends NamedThreadLocal<Deque<String>> {
127139

128140
private UrlTemplateThreadLocal() {

spring-boot-project/spring-boot/src/main/java/org/springframework/boot/web/client/RootUriTemplateHandler.java

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2012-2019 the original author or authors.
2+
* Copyright 2012-2021 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -18,6 +18,7 @@
1818

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

2223
import org.springframework.util.Assert;
2324
import org.springframework.util.StringUtils;
@@ -84,6 +85,17 @@ public String getRootUri() {
8485
return this.rootUri;
8586
}
8687

88+
/**
89+
* Derives a new {@code RootUriTemplateHandler} from this one, wrapping its delegate
90+
* {link UriTemplateHandler} by applying the given {@code wrapper}.
91+
* @param wrapper the wrapper to apply to the delegate URI template handler
92+
* @return the new handler
93+
* @since 2.3.10
94+
*/
95+
public RootUriTemplateHandler withHandlerWrapper(Function<UriTemplateHandler, UriTemplateHandler> wrapper) {
96+
return new RootUriTemplateHandler(this.rootUri, wrapper.apply(this.handler));
97+
}
98+
8799
/**
88100
* Add a {@link RootUriTemplateHandler} instance to the given {@link RestTemplate}.
89101
* @param restTemplate the {@link RestTemplate} to add the handler to

0 commit comments

Comments
 (0)