Skip to content

Commit 3bd9eea

Browse files
committed
Merge pull request #19464 from nosan
* pr/19464: Support nested requests in MetricsClientHttpRequestInterceptor Closes gh-19464
2 parents 3f3bac9 + 25838b4 commit 3bd9eea

File tree

2 files changed

+70
-7
lines changed

2 files changed

+70
-7
lines changed

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

Lines changed: 23 additions & 6 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-2020 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,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

@@ -41,7 +43,7 @@
4143
*/
4244
class MetricsClientHttpRequestInterceptor implements ClientHttpRequestInterceptor {
4345

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

4648
private final MeterRegistry meterRegistry;
4749

@@ -96,7 +98,9 @@ public ClientHttpResponse intercept(HttpRequest request, byte[] body, ClientHttp
9698
finally {
9799
getTimeBuilder(request, response).register(this.meterRegistry).record(System.nanoTime() - startTime,
98100
TimeUnit.NANOSECONDS);
99-
urlTemplate.remove();
101+
if (urlTemplate.get().isEmpty()) {
102+
urlTemplate.remove();
103+
}
100104
}
101105
}
102106

@@ -105,13 +109,13 @@ UriTemplateHandler createUriTemplateHandler(UriTemplateHandler delegate) {
105109

106110
@Override
107111
public URI expand(String url, Map<String, ?> arguments) {
108-
urlTemplate.set(url);
112+
urlTemplate.get().push(url);
109113
return delegate.expand(url, arguments);
110114
}
111115

112116
@Override
113117
public URI expand(String url, Object... arguments) {
114-
urlTemplate.set(url);
118+
urlTemplate.get().push(url);
115119
return delegate.expand(url, arguments);
116120
}
117121

@@ -120,8 +124,21 @@ public URI expand(String url, Object... arguments) {
120124

121125
private Timer.Builder getTimeBuilder(HttpRequest request, ClientHttpResponse response) {
122126
return this.autoTimer.builder(this.metricName)
123-
.tags(this.tagProvider.getTags(urlTemplate.get(), request, response))
127+
.tags(this.tagProvider.getTags(urlTemplate.get().poll(), request, response))
124128
.description("Timer of RestTemplate operation");
125129
}
126130

131+
private static final class UrlTemplateThreadLocal extends NamedThreadLocal<Deque<String>> {
132+
133+
private UrlTemplateThreadLocal() {
134+
super("Rest Template URL Template");
135+
}
136+
137+
@Override
138+
protected Deque<String> initialValue() {
139+
return new LinkedList<>();
140+
}
141+
142+
}
143+
127144
}

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

Lines changed: 47 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-2020 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.
@@ -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)