-
Notifications
You must be signed in to change notification settings - Fork 961
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Http client instrumentation TCK #3258
Merged
shakuzen
merged 10 commits into
micrometer-metrics:1.8.x
from
shakuzen:http-client-instrumentation-tck
Jul 11, 2022
Merged
Changes from all commits
Commits
Show all changes
10 commits
Select commit
Hold shift + click to select a range
066d4b9
WIP Http client instrumentation TCK
shakuzen c950a2c
Clean up API to be more versatile and less leaky
shakuzen fed9baa
Fix build check issues
shakuzen ad8a7bb
Polish JavaDocs
shakuzen 36ed1bd
Assert more specific behavior of expected tags
shakuzen 292e62b
Use more standard naming, polish
shakuzen 1d90996
Fix formatting
shakuzen 3c74cfd
Mark as incubating, add client exception
shakuzen aa07c38
Add body parameter for potential future use
shakuzen b5c3e49
Write locks for 1.8.x
shakuzen File filter
Filter by extension
Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
There are no files selected for viewing
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
154 changes: 154 additions & 0 deletions
154
.../java/io/micrometer/core/instrument/HttpClientTimingInstrumentationVerificationTests.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,154 @@ | ||
/* | ||
* Copyright 2022 VMware, Inc. | ||
* | ||
* Licensed under the Apache License, Version 2.0 (the "License"); | ||
* you may not use this file except in compliance with the License. | ||
* You may obtain a copy of the License at | ||
* | ||
* https://www.apache.org/licenses/LICENSE-2.0 | ||
* | ||
* Unless required by applicable law or agreed to in writing, software | ||
* distributed under the License is distributed on an "AS IS" BASIS, | ||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
* See the License for the specific language governing permissions and | ||
* limitations under the License. | ||
*/ | ||
package io.micrometer.core.instrument; | ||
|
||
import com.github.tomakehurst.wiremock.junit5.WireMockRuntimeInfo; | ||
import com.github.tomakehurst.wiremock.junit5.WireMockTest; | ||
import io.micrometer.core.annotation.Incubating; | ||
import io.micrometer.core.lang.Nullable; | ||
import org.junit.jupiter.api.Disabled; | ||
import org.junit.jupiter.api.Test; | ||
|
||
import java.io.IOException; | ||
import java.net.ServerSocket; | ||
import java.net.URI; | ||
import java.util.concurrent.TimeUnit; | ||
|
||
import static com.github.tomakehurst.wiremock.client.WireMock.*; | ||
import static org.assertj.core.api.Assertions.assertThat; | ||
|
||
/** | ||
* Test suite for HTTP client timing instrumentation that verifies the expected metrics | ||
* are registered and recorded after different scenarios. Use this suite to ensure that | ||
* your instrumentation has the expected naming and tags. A locally running server is used | ||
* to receive real requests from an instrumented HTTP client. | ||
*/ | ||
@WireMockTest | ||
@Incubating(since = "1.9.2") | ||
public abstract class HttpClientTimingInstrumentationVerificationTests extends InstrumentationVerificationTests { | ||
|
||
enum HttpMethod { | ||
|
||
GET, POST; | ||
|
||
} | ||
|
||
/** | ||
* A default is provided that should be preferred by new instrumentations. Existing | ||
* instrumentations that use a different value to maintain backwards compatibility may | ||
* override this method to run tests with a different name used in assertions. | ||
* @return name of the meter timing http client requests | ||
*/ | ||
protected String timerName() { | ||
return "http.client.requests"; | ||
} | ||
|
||
/** | ||
* Send an HTTP request using the instrumented HTTP client to the given base URL and | ||
* path on the locally running server. The HTTP client instrumentation must be | ||
* configured to tag the templated path to pass this test suite. The templated path | ||
* will contain path variables surrounded by curly brackets to be substituted. For | ||
* example, for the full templated URL {@literal http://localhost:8080/cart/{cartId}} | ||
* the baseUrl would be {@literal http://localhost:8080}, the templatedPath would be | ||
* {@literal /cart/{cartId}}. One string pathVariables argument is expected for | ||
* substituting the cartId path variable. The number of pathVariables arguments SHOULD | ||
* exactly match the number of path variables in the templatedPath. | ||
* @param method http method to use to send the request | ||
* @param baseUrl portion of the URL before the path where to send the request | ||
* @param templatedPath the path portion of the URL after the baseUrl, starting with a | ||
* forward slash, and optionally containing path variable placeholders | ||
* @param pathVariables optional variables to substitute into the templatedPath | ||
*/ | ||
abstract void sendHttpRequest(HttpMethod method, @Nullable byte[] body, URI baseUrl, String templatedPath, | ||
String... pathVariables); | ||
|
||
/** | ||
* Convenience method provided to substitute the template placeholders for the | ||
* provided path variables. The number of pathVariables argument SHOULD match the | ||
* number of placeholders in the templatedPath. Substitutions will be made in order. | ||
* @param templatedPath a URL path optionally containing placeholders in curly | ||
* brackets | ||
* @param pathVariables path variable values for which placeholders should be | ||
* substituted | ||
* @return path string with substitutions, if any, performed | ||
*/ | ||
protected String substitutePathVariables(String templatedPath, String... pathVariables) { | ||
if (pathVariables.length == 0) { | ||
return templatedPath; | ||
} | ||
String substituted = templatedPath; | ||
for (String substitution : pathVariables) { | ||
substituted = substituted.replaceFirst("\\{.*?}", substitution); | ||
} | ||
return substituted; | ||
} | ||
|
||
@Test | ||
void getTemplatedPathForUri(WireMockRuntimeInfo wmRuntimeInfo) { | ||
stubFor(get(anyUrl()).willReturn(ok())); | ||
|
||
String templatedPath = "/customers/{customerId}/carts/{cartId}"; | ||
sendHttpRequest(HttpMethod.GET, null, URI.create(wmRuntimeInfo.getHttpBaseUrl()), templatedPath, "112", "5"); | ||
|
||
Timer timer = getRegistry().get(timerName()).tags("method", "GET", "status", "200", "uri", templatedPath) | ||
.timer(); | ||
assertThat(timer.count()).isEqualTo(1); | ||
assertThat(timer.totalTime(TimeUnit.NANOSECONDS)).isPositive(); | ||
} | ||
|
||
@Test | ||
@Disabled("apache/jetty http client instrumentation currently fails this test") | ||
void timedWhenServerIsMissing() throws IOException { | ||
int unusedPort = 0; | ||
try (ServerSocket server = new ServerSocket(0)) { | ||
unusedPort = server.getLocalPort(); | ||
} | ||
|
||
try { | ||
sendHttpRequest(HttpMethod.GET, null, URI.create("http://localhost:" + unusedPort), "/anything"); | ||
} | ||
catch (Throwable ignore) { | ||
} | ||
|
||
Timer timer = getRegistry().get(timerName()).tags("method", "GET").timer(); | ||
|
||
assertThat(timer.count()).isEqualTo(1); | ||
assertThat(timer.totalTime(TimeUnit.NANOSECONDS)).isPositive(); | ||
} | ||
|
||
@Test | ||
void serverException(WireMockRuntimeInfo wmRuntimeInfo) { | ||
stubFor(get(anyUrl()).willReturn(serverError())); | ||
|
||
sendHttpRequest(HttpMethod.GET, null, URI.create(wmRuntimeInfo.getHttpBaseUrl()), "/socks"); | ||
|
||
Timer timer = getRegistry().get(timerName()).tags("method", "GET", "status", "500").timer(); | ||
assertThat(timer.count()).isEqualTo(1); | ||
assertThat(timer.totalTime(TimeUnit.NANOSECONDS)).isPositive(); | ||
} | ||
|
||
@Test | ||
void clientException(WireMockRuntimeInfo wmRuntimeInfo) { | ||
stubFor(post(anyUrl()).willReturn(badRequest())); | ||
|
||
sendHttpRequest(HttpMethod.POST, new byte[0], URI.create(wmRuntimeInfo.getHttpBaseUrl()), "/socks"); | ||
|
||
Timer timer = getRegistry().get(timerName()).tags("method", "POST", "status", "400").timer(); | ||
assertThat(timer.count()).isEqualTo(1); | ||
assertThat(timer.totalTime(TimeUnit.NANOSECONDS)).isPositive(); | ||
} | ||
|
||
} |
28 changes: 28 additions & 0 deletions
28
...er-test/src/main/java/io/micrometer/core/instrument/InstrumentationVerificationTests.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,28 @@ | ||
/* | ||
* Copyright 2022 VMware, Inc. | ||
* | ||
* Licensed under the Apache License, Version 2.0 (the "License"); | ||
* you may not use this file except in compliance with the License. | ||
* You may obtain a copy of the License at | ||
* | ||
* https://www.apache.org/licenses/LICENSE-2.0 | ||
* | ||
* Unless required by applicable law or agreed to in writing, software | ||
* distributed under the License is distributed on an "AS IS" BASIS, | ||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
* See the License for the specific language governing permissions and | ||
* limitations under the License. | ||
*/ | ||
package io.micrometer.core.instrument; | ||
|
||
import io.micrometer.core.instrument.simple.SimpleMeterRegistry; | ||
|
||
abstract class InstrumentationVerificationTests { | ||
|
||
private final MeterRegistry registry = new SimpleMeterRegistry(); | ||
|
||
MeterRegistry getRegistry() { | ||
return registry; | ||
} | ||
|
||
} |
16 changes: 16 additions & 0 deletions
16
micrometer-test/src/main/java/io/micrometer/core/instrument/package-info.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,16 @@ | ||
/* | ||
* Copyright 2022 VMware, Inc. | ||
* | ||
* Licensed under the Apache License, Version 2.0 (the "License"); | ||
* you may not use this file except in compliance with the License. | ||
* You may obtain a copy of the License at | ||
* | ||
* https://www.apache.org/licenses/LICENSE-2.0 | ||
* | ||
* Unless required by applicable law or agreed to in writing, software | ||
* distributed under the License is distributed on an "AS IS" BASIS, | ||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
* See the License for the specific language governing permissions and | ||
* limitations under the License. | ||
*/ | ||
package io.micrometer.core.instrument; |
72 changes: 72 additions & 0 deletions
72
...io/micrometer/core/instrument/ApacheHttpClientTimingInstrumentationVerificationTests.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,72 @@ | ||
/* | ||
* Copyright 2022 VMware, Inc. | ||
* | ||
* Licensed under the Apache License, Version 2.0 (the "License"); | ||
* you may not use this file except in compliance with the License. | ||
* You may obtain a copy of the License at | ||
* | ||
* https://www.apache.org/licenses/LICENSE-2.0 | ||
* | ||
* Unless required by applicable law or agreed to in writing, software | ||
* distributed under the License is distributed on an "AS IS" BASIS, | ||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
* See the License for the specific language governing permissions and | ||
* limitations under the License. | ||
*/ | ||
package io.micrometer.core.instrument; | ||
|
||
import io.micrometer.core.instrument.binder.httpcomponents.DefaultUriMapper; | ||
import io.micrometer.core.instrument.binder.httpcomponents.MicrometerHttpRequestExecutor; | ||
import io.micrometer.core.lang.Nullable; | ||
import org.apache.http.client.HttpClient; | ||
import org.apache.http.client.methods.HttpEntityEnclosingRequestBase; | ||
import org.apache.http.client.methods.HttpUriRequest; | ||
import org.apache.http.entity.BasicHttpEntity; | ||
import org.apache.http.impl.client.HttpClientBuilder; | ||
import org.apache.http.util.EntityUtils; | ||
|
||
import java.io.ByteArrayInputStream; | ||
import java.io.IOException; | ||
import java.net.URI; | ||
|
||
class ApacheHttpClientTimingInstrumentationVerificationTests extends HttpClientTimingInstrumentationVerificationTests { | ||
|
||
private final HttpClient httpClient = HttpClientBuilder.create() | ||
.setRequestExecutor(MicrometerHttpRequestExecutor.builder(getRegistry()).build()).build(); | ||
|
||
@Override | ||
protected String timerName() { | ||
return "httpcomponents.httpclient.request"; | ||
} | ||
|
||
@Override | ||
void sendHttpRequest(HttpMethod method, @Nullable byte[] body, URI baseUri, String templatedPath, | ||
String... pathVariables) { | ||
try { | ||
EntityUtils.consume( | ||
httpClient.execute(makeRequest(method, body, baseUri, templatedPath, pathVariables)).getEntity()); | ||
} | ||
catch (IOException e) { | ||
throw new RuntimeException(e); | ||
} | ||
} | ||
|
||
private HttpUriRequest makeRequest(HttpMethod method, @Nullable byte[] body, URI baseUri, String templatedPath, | ||
String... pathVariables) { | ||
HttpEntityEnclosingRequestBase request = new HttpEntityEnclosingRequestBase() { | ||
@Override | ||
public String getMethod() { | ||
return method.name(); | ||
} | ||
}; | ||
if (body != null) { | ||
BasicHttpEntity entity = new BasicHttpEntity(); | ||
entity.setContent(new ByteArrayInputStream(body)); | ||
request.setEntity(entity); | ||
} | ||
request.setURI(URI.create(baseUri + substitutePathVariables(templatedPath, pathVariables))); | ||
request.setHeader(DefaultUriMapper.URI_PATTERN_HEADER, templatedPath); | ||
return request; | ||
} | ||
|
||
} |
60 changes: 60 additions & 0 deletions
60
...java/io/micrometer/core/instrument/JettyClientTimingInstrumentationVerificationTests.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,60 @@ | ||
/* | ||
* Copyright 2022 VMware, Inc. | ||
* | ||
* Licensed under the Apache License, Version 2.0 (the "License"); | ||
* you may not use this file except in compliance with the License. | ||
* You may obtain a copy of the License at | ||
* | ||
* https://www.apache.org/licenses/LICENSE-2.0 | ||
* | ||
* Unless required by applicable law or agreed to in writing, software | ||
* distributed under the License is distributed on an "AS IS" BASIS, | ||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
* See the License for the specific language governing permissions and | ||
* limitations under the License. | ||
*/ | ||
package io.micrometer.core.instrument; | ||
|
||
import io.micrometer.core.instrument.binder.jetty.JettyClientMetrics; | ||
import io.micrometer.core.lang.Nullable; | ||
import org.eclipse.jetty.client.HttpClient; | ||
import org.eclipse.jetty.client.api.Request; | ||
import org.eclipse.jetty.client.util.BytesContentProvider; | ||
import org.junit.jupiter.api.BeforeEach; | ||
|
||
import java.net.URI; | ||
|
||
class JettyClientTimingInstrumentationVerificationTests extends HttpClientTimingInstrumentationVerificationTests { | ||
|
||
private final HttpClient httpClient = new HttpClient(); | ||
|
||
@Override | ||
protected String timerName() { | ||
return "jetty.client.requests"; | ||
} | ||
|
||
@BeforeEach | ||
void setup() throws Exception { | ||
httpClient.getRequestListeners().add(JettyClientMetrics | ||
.builder(getRegistry(), result -> result.getRequest().getHeaders().get("URI_PATTERN")).build()); | ||
httpClient.start(); | ||
} | ||
|
||
@Override | ||
void sendHttpRequest(HttpMethod method, @Nullable byte[] body, URI baseUri, String templatedPath, | ||
String... pathVariables) { | ||
try { | ||
Request request = httpClient.newRequest(baseUri + substitutePathVariables(templatedPath, pathVariables)) | ||
.method(method.name()).header("URI_PATTERN", templatedPath); | ||
if (body != null) { | ||
request.content(new BytesContentProvider(body)); | ||
} | ||
request.send(); | ||
httpClient.stop(); | ||
} | ||
catch (Exception e) { | ||
throw new RuntimeException(e); | ||
} | ||
} | ||
|
||
} |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
UNENCRYPTED_SERVER_SOCKET: Unencrypted server socket (instead of SSLServerSocket)
Reply with "@sonatype-lift help" for info about LiftBot commands.
Reply with "@sonatype-lift ignore" to tell LiftBot to leave out the above finding from this PR.
Reply with "@sonatype-lift ignoreall" to tell LiftBot to leave out all the findings from this PR and from the status bar in Github.
When talking to LiftBot, you need to refresh the page to see its response. Click here to get to know more about LiftBot commands.
Was this a good recommendation?
[ 🙁 Not relevant ] - [ 😕 Won't fix ] - [ 😑 Not critical, will fix ] - [ 🙂 Critical, will fix ] - [ 😊 Critical, fixing now ]