Skip to content

Commit b50d833

Browse files
committed
Add support for nested requests in MetricsClientHttpRequestInterceptor
See gh-19381
1 parent d64b719 commit b50d833

File tree

2 files changed

+70
-5
lines changed

2 files changed

+70
-5
lines changed

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

Lines changed: 24 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,8 @@
1818

1919
import java.io.IOException;
2020
import java.net.URI;
21+
import java.util.Deque;
22+
import java.util.LinkedList;
2123
import java.util.Map;
2224
import java.util.concurrent.TimeUnit;
2325

@@ -30,6 +32,7 @@
3032
import org.springframework.http.client.ClientHttpRequestExecution;
3133
import org.springframework.http.client.ClientHttpRequestInterceptor;
3234
import org.springframework.http.client.ClientHttpResponse;
35+
import org.springframework.util.CollectionUtils;
3336
import org.springframework.web.util.UriTemplateHandler;
3437

3538
/**
@@ -41,7 +44,7 @@
4144
*/
4245
class MetricsClientHttpRequestInterceptor implements ClientHttpRequestInterceptor {
4346

44-
private static final ThreadLocal<String> urlTemplate = new NamedThreadLocal<>("Rest Template URL Template");
47+
private static final ThreadLocal<Deque<String>> urlTemplate = new NamedThreadLocal<>("Rest Template URL Template");
4548

4649
private final MeterRegistry meterRegistry;
4750

@@ -96,7 +99,9 @@ public ClientHttpResponse intercept(HttpRequest request, byte[] body, ClientHttp
9699
finally {
97100
getTimeBuilder(request, response).register(this.meterRegistry).record(System.nanoTime() - startTime,
98101
TimeUnit.NANOSECONDS);
99-
urlTemplate.remove();
102+
if (CollectionUtils.isEmpty(urlTemplate.get())) {
103+
urlTemplate.remove();
104+
}
100105
}
101106
}
102107

@@ -105,13 +110,13 @@ UriTemplateHandler createUriTemplateHandler(UriTemplateHandler delegate) {
105110

106111
@Override
107112
public URI expand(String url, Map<String, ?> arguments) {
108-
urlTemplate.set(url);
113+
addFirstUrl(url);
109114
return delegate.expand(url, arguments);
110115
}
111116

112117
@Override
113118
public URI expand(String url, Object... arguments) {
114-
urlTemplate.set(url);
119+
addFirstUrl(url);
115120
return delegate.expand(url, arguments);
116121
}
117122

@@ -120,8 +125,22 @@ public URI expand(String url, Object... arguments) {
120125

121126
private Timer.Builder getTimeBuilder(HttpRequest request, ClientHttpResponse response) {
122127
return this.autoTimer.builder(this.metricName)
123-
.tags(this.tagProvider.getTags(urlTemplate.get(), request, response))
128+
.tags(this.tagProvider.getTags(removeFirstUrl(), request, response))
124129
.description("Timer of RestTemplate operation");
125130
}
126131

132+
private static void addFirstUrl(String url) {
133+
Deque<String> templates = urlTemplate.get();
134+
if (templates == null) {
135+
templates = new LinkedList<>();
136+
urlTemplate.set(templates);
137+
}
138+
templates.offerFirst(url);
139+
}
140+
141+
private static String removeFirstUrl() {
142+
Deque<String> templates = urlTemplate.get();
143+
return (templates != null) ? templates.pollFirst() : null;
144+
}
145+
127146
}

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

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616

1717
package org.springframework.boot.actuate.metrics.web.client;
1818

19+
import java.io.IOException;
1920
import java.net.URI;
2021
import java.net.URISyntaxException;
2122

@@ -29,7 +30,11 @@
2930

3031
import org.springframework.boot.actuate.metrics.AutoTimer;
3132
import org.springframework.http.HttpMethod;
33+
import org.springframework.http.HttpRequest;
3234
import org.springframework.http.MediaType;
35+
import org.springframework.http.client.ClientHttpRequestExecution;
36+
import org.springframework.http.client.ClientHttpRequestInterceptor;
37+
import org.springframework.http.client.ClientHttpResponse;
3338
import org.springframework.test.web.client.MockRestServiceServer;
3439
import org.springframework.test.web.client.match.MockRestRequestMatchers;
3540
import org.springframework.test.web.client.response.MockRestResponseCreators;
@@ -107,4 +112,45 @@ void interceptRestTemplateWithUri() throws URISyntaxException {
107112
this.mockServer.verify();
108113
}
109114

115+
@Test
116+
void interceptNestedRequest() {
117+
this.mockServer.expect(MockRestRequestMatchers.requestTo("/test/123"))
118+
.andExpect(MockRestRequestMatchers.method(HttpMethod.GET))
119+
.andRespond(MockRestResponseCreators.withSuccess("OK", MediaType.APPLICATION_JSON));
120+
121+
RestTemplate nestedRestTemplate = new RestTemplate();
122+
MockRestServiceServer nestedMockServer = MockRestServiceServer.createServer(nestedRestTemplate);
123+
nestedMockServer.expect(MockRestRequestMatchers.requestTo("/nestedTest/124"))
124+
.andExpect(MockRestRequestMatchers.method(HttpMethod.GET))
125+
.andRespond(MockRestResponseCreators.withSuccess("OK", MediaType.APPLICATION_JSON));
126+
this.customizer.customize(nestedRestTemplate);
127+
128+
TestInterceptor testInterceptor = new TestInterceptor(nestedRestTemplate);
129+
this.restTemplate.getInterceptors().add(testInterceptor);
130+
131+
this.restTemplate.getForObject("/test/{id}", String.class, 123);
132+
this.registry.get("http.client.requests").tags("uri", "/test/{id}").timer();
133+
this.registry.get("http.client.requests").tags("uri", "/nestedTest/{nestedId}").timer();
134+
135+
this.mockServer.verify();
136+
nestedMockServer.verify();
137+
}
138+
139+
private static final class TestInterceptor implements ClientHttpRequestInterceptor {
140+
141+
private final RestTemplate restTemplate;
142+
143+
private TestInterceptor(RestTemplate restTemplate) {
144+
this.restTemplate = restTemplate;
145+
}
146+
147+
@Override
148+
public ClientHttpResponse intercept(HttpRequest request, byte[] body, ClientHttpRequestExecution execution)
149+
throws IOException {
150+
this.restTemplate.getForObject("/nestedTest/{nestedId}", String.class, 124);
151+
return execution.execute(request, body);
152+
}
153+
154+
}
155+
110156
}

0 commit comments

Comments
 (0)