From 066d4b96da78faa195752561e15d2f2a70d1f008 Mon Sep 17 00:00:00 2001 From: Tommy Ludwig <8924140+shakuzen@users.noreply.github.com> Date: Fri, 1 Jul 2022 14:33:02 +0900 Subject: [PATCH 01/10] WIP Http client instrumentation TCK Introduce test suite for HTTP client instrumentations to check that all implementations have the naming and tags expected. This may also help instrumentors ensure backwards compatibility of the HTTP client instrumentation across changes to it. --- micrometer-test/build.gradle | 2 + ...imingInstrumentationVerificationSuite.java | 87 +++++++++++++++++++ .../InstrumentationVerificationSuite.java | 28 ++++++ ...imingInstrumentationVerificationSuite.java | 48 ++++++++++ ...imingInstrumentationVerificationSuite.java | 50 +++++++++++ ...imingInstrumentationVerificationSuite.java | 41 +++++++++ 6 files changed, 256 insertions(+) create mode 100644 micrometer-test/src/main/java/io/micrometer/core/instrument/HttpClientTimingInstrumentationVerificationSuite.java create mode 100644 micrometer-test/src/main/java/io/micrometer/core/instrument/InstrumentationVerificationSuite.java create mode 100644 micrometer-test/src/test/java/io/micrometer/core/instrument/ApacheHttpClientTimingInstrumentationVerificationSuite.java create mode 100644 micrometer-test/src/test/java/io/micrometer/core/instrument/JettyClientTimingInstrumentationVerificationSuite.java create mode 100644 micrometer-test/src/test/java/io/micrometer/core/instrument/OkHttpClientTimingInstrumentationVerificationSuite.java diff --git a/micrometer-test/build.gradle b/micrometer-test/build.gradle index cda0a9329f..e9e8b7d29b 100644 --- a/micrometer-test/build.gradle +++ b/micrometer-test/build.gradle @@ -22,4 +22,6 @@ dependencies { testImplementation 'com.hazelcast:hazelcast' testImplementation 'com.squareup.okhttp3:okhttp' testImplementation 'io.projectreactor.netty:reactor-netty-http' + testImplementation 'org.apache.httpcomponents:httpclient' + testImplementation 'org.eclipse.jetty:jetty-client' } diff --git a/micrometer-test/src/main/java/io/micrometer/core/instrument/HttpClientTimingInstrumentationVerificationSuite.java b/micrometer-test/src/main/java/io/micrometer/core/instrument/HttpClientTimingInstrumentationVerificationSuite.java new file mode 100644 index 0000000000..f608420a87 --- /dev/null +++ b/micrometer-test/src/main/java/io/micrometer/core/instrument/HttpClientTimingInstrumentationVerificationSuite.java @@ -0,0 +1,87 @@ +/* + * 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 org.junit.jupiter.api.Test; + +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. WireMock is used as an HTTP + * server to receive real requests from an instrumented HTTP client. + */ +@WireMockTest +public abstract class HttpClientTimingInstrumentationVerificationSuite extends InstrumentationVerificationSuite { + + /** + * A default is provided that should be preferred by new instrumentations, but + * existing instrumentations that use a different value to maintain backwards + * compatibility may override this method to run tests with a different name. + * @return name of the meter timing http client requests + */ + protected String timerName() { + return "http.client.requests"; + } + + /** + * Send a GET request using the instrumented HTTP client to the given path on the + * locally running WireMock server. + * @param wmRuntimeInfo used to get the address/port info of where to send the request + * @param path the path portion of the URL after the host name and a forward slash + */ + abstract void sendGetRequest(WireMockRuntimeInfo wmRuntimeInfo, String path); + + @Test + void successful(WireMockRuntimeInfo wmRuntimeInfo) { + stubFor(get(anyUrl()).willReturn(ok())); + + sendGetRequest(wmRuntimeInfo, ""); + + Timer timer = getRegistry().get(timerName()).tags("method", "GET", "status", "200").timer(); + assertThat(timer.count()).isEqualTo(1); + assertThat(timer.totalTime(TimeUnit.NANOSECONDS)).isPositive(); + } + + @Test + void notFoundResponse(WireMockRuntimeInfo wmRuntimeInfo) { + stubFor(get(anyUrl()).willReturn(notFound())); + + sendGetRequest(wmRuntimeInfo, "notFound"); + + Timer timer = getRegistry().get(timerName()).tags("method", "GET", "status", "404").timer(); + assertThat(timer.count()).isEqualTo(1); + assertThat(timer.totalTime(TimeUnit.NANOSECONDS)).isPositive(); + } + + @Test + void badRequestResponse(WireMockRuntimeInfo wmRuntimeInfo) { + stubFor(get(anyUrl()).willReturn(badRequest())); + + sendGetRequest(wmRuntimeInfo, ""); + + Timer timer = getRegistry().get(timerName()).tags("method", "GET", "status", "400").timer(); + assertThat(timer.count()).isEqualTo(1); + assertThat(timer.totalTime(TimeUnit.NANOSECONDS)).isPositive(); + } + +} diff --git a/micrometer-test/src/main/java/io/micrometer/core/instrument/InstrumentationVerificationSuite.java b/micrometer-test/src/main/java/io/micrometer/core/instrument/InstrumentationVerificationSuite.java new file mode 100644 index 0000000000..7cd0a6c3de --- /dev/null +++ b/micrometer-test/src/main/java/io/micrometer/core/instrument/InstrumentationVerificationSuite.java @@ -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; + +public abstract class InstrumentationVerificationSuite { + + private final MeterRegistry registry = new SimpleMeterRegistry(); + + MeterRegistry getRegistry() { + return registry; + } + +} diff --git a/micrometer-test/src/test/java/io/micrometer/core/instrument/ApacheHttpClientTimingInstrumentationVerificationSuite.java b/micrometer-test/src/test/java/io/micrometer/core/instrument/ApacheHttpClientTimingInstrumentationVerificationSuite.java new file mode 100644 index 0000000000..9b6ef697e6 --- /dev/null +++ b/micrometer-test/src/test/java/io/micrometer/core/instrument/ApacheHttpClientTimingInstrumentationVerificationSuite.java @@ -0,0 +1,48 @@ +/* + * 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 io.micrometer.core.instrument.binder.httpcomponents.MicrometerHttpRequestExecutor; +import org.apache.http.client.HttpClient; +import org.apache.http.client.methods.HttpGet; +import org.apache.http.impl.client.HttpClientBuilder; +import org.apache.http.util.EntityUtils; + +import java.io.IOException; + +class ApacheHttpClientTimingInstrumentationVerificationSuite extends HttpClientTimingInstrumentationVerificationSuite { + + private HttpClient httpClient = HttpClientBuilder.create() + .setRequestExecutor(MicrometerHttpRequestExecutor.builder(getRegistry()).build()).build(); + + @Override + protected String timerName() { + return "httpcomponents.httpclient.request"; + } + + @Override + void sendGetRequest(WireMockRuntimeInfo wmRuntimeInfo, String path) { + try { + EntityUtils + .consume(httpClient.execute(new HttpGet(wmRuntimeInfo.getHttpBaseUrl() + "/" + path)).getEntity()); + } + catch (IOException e) { + throw new RuntimeException(e); + } + } + +} diff --git a/micrometer-test/src/test/java/io/micrometer/core/instrument/JettyClientTimingInstrumentationVerificationSuite.java b/micrometer-test/src/test/java/io/micrometer/core/instrument/JettyClientTimingInstrumentationVerificationSuite.java new file mode 100644 index 0000000000..ccc2a78e2f --- /dev/null +++ b/micrometer-test/src/test/java/io/micrometer/core/instrument/JettyClientTimingInstrumentationVerificationSuite.java @@ -0,0 +1,50 @@ +/* + * 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 io.micrometer.core.instrument.binder.jetty.JettyClientMetrics; +import org.eclipse.jetty.client.HttpClient; +import org.junit.jupiter.api.BeforeEach; + +class JettyClientTimingInstrumentationVerificationSuite extends HttpClientTimingInstrumentationVerificationSuite { + + private 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().getURI().getPath()).build()); + httpClient.start(); + } + + @Override + void sendGetRequest(WireMockRuntimeInfo wmRuntimeInfo, String path) { + try { + httpClient.GET(wmRuntimeInfo.getHttpBaseUrl() + "/" + path); + httpClient.stop(); + } + catch (Exception e) { + throw new RuntimeException(e); + } + } + +} diff --git a/micrometer-test/src/test/java/io/micrometer/core/instrument/OkHttpClientTimingInstrumentationVerificationSuite.java b/micrometer-test/src/test/java/io/micrometer/core/instrument/OkHttpClientTimingInstrumentationVerificationSuite.java new file mode 100644 index 0000000000..d82f5f2edd --- /dev/null +++ b/micrometer-test/src/test/java/io/micrometer/core/instrument/OkHttpClientTimingInstrumentationVerificationSuite.java @@ -0,0 +1,41 @@ +/* + * 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 io.micrometer.core.instrument.binder.okhttp3.OkHttpMetricsEventListener; +import okhttp3.OkHttpClient; +import okhttp3.Request; +import okhttp3.Response; + +import java.io.IOException; + +class OkHttpClientTimingInstrumentationVerificationSuite extends HttpClientTimingInstrumentationVerificationSuite { + + OkHttpClient httpClient = new OkHttpClient.Builder() + .eventListener(OkHttpMetricsEventListener.builder(getRegistry(), timerName()).build()).build(); + + @Override + void sendGetRequest(WireMockRuntimeInfo wmRuntimeInfo, String path) { + Request request = new Request.Builder().url(wmRuntimeInfo.getHttpBaseUrl() + "/" + path).build(); + try (Response response = httpClient.newCall(request).execute()) { + } + catch (IOException e) { + throw new RuntimeException(e); + } + } + +} From c950a2ca74c207c76d8437d9e0001d60a9966923 Mon Sep 17 00:00:00 2001 From: Tommy Ludwig <8924140+shakuzen@users.noreply.github.com> Date: Mon, 4 Jul 2022 22:38:11 +0900 Subject: [PATCH 02/10] Clean up API to be more versatile and less leaky Avoids putting WireMock classes in the API that instrumentors need to implement for the test suite. Also makes it more versatile by passing a method, templated path, and variable substitutions. How templated paths are handled will vary from client to client. --- ...imingInstrumentationVerificationSuite.java | 53 ++++++++++++++++--- ...imingInstrumentationVerificationSuite.java | 24 +++++++-- ...imingInstrumentationVerificationSuite.java | 14 +++-- ...imingInstrumentationVerificationSuite.java | 9 ++-- 4 files changed, 81 insertions(+), 19 deletions(-) diff --git a/micrometer-test/src/main/java/io/micrometer/core/instrument/HttpClientTimingInstrumentationVerificationSuite.java b/micrometer-test/src/main/java/io/micrometer/core/instrument/HttpClientTimingInstrumentationVerificationSuite.java index f608420a87..3cad70e51f 100644 --- a/micrometer-test/src/main/java/io/micrometer/core/instrument/HttpClientTimingInstrumentationVerificationSuite.java +++ b/micrometer-test/src/main/java/io/micrometer/core/instrument/HttpClientTimingInstrumentationVerificationSuite.java @@ -19,6 +19,7 @@ import com.github.tomakehurst.wiremock.junit5.WireMockTest; import org.junit.jupiter.api.Test; +import java.net.URI; import java.util.concurrent.TimeUnit; import static com.github.tomakehurst.wiremock.client.WireMock.*; @@ -33,6 +34,12 @@ @WireMockTest public abstract class HttpClientTimingInstrumentationVerificationSuite extends InstrumentationVerificationSuite { + enum HttpMethod { + + GET, POST; + + } + /** * A default is provided that should be preferred by new instrumentations, but * existing instrumentations that use a different value to maintain backwards @@ -44,18 +51,48 @@ protected String timerName() { } /** - * Send a GET request using the instrumented HTTP client to the given path on the - * locally running WireMock server. - * @param wmRuntimeInfo used to get the address/port info of where to send the request - * @param path the path portion of the URL after the host name and a forward slash + * Send an HTTP request using the instrumented HTTP client to the given base URL and + * path on the locally running WireMock server. The templated path should 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 sendGetRequest(WireMockRuntimeInfo wmRuntimeInfo, String path); + abstract void sendHttpRequest(HttpMethod method, 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. + * @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 successful(WireMockRuntimeInfo wmRuntimeInfo) { stubFor(get(anyUrl()).willReturn(ok())); - sendGetRequest(wmRuntimeInfo, ""); + sendHttpRequest(HttpMethod.GET, URI.create(wmRuntimeInfo.getHttpBaseUrl()), ""); Timer timer = getRegistry().get(timerName()).tags("method", "GET", "status", "200").timer(); assertThat(timer.count()).isEqualTo(1); @@ -66,7 +103,7 @@ void successful(WireMockRuntimeInfo wmRuntimeInfo) { void notFoundResponse(WireMockRuntimeInfo wmRuntimeInfo) { stubFor(get(anyUrl()).willReturn(notFound())); - sendGetRequest(wmRuntimeInfo, "notFound"); + sendHttpRequest(HttpMethod.GET, URI.create(wmRuntimeInfo.getHttpBaseUrl()), "/notFound"); Timer timer = getRegistry().get(timerName()).tags("method", "GET", "status", "404").timer(); assertThat(timer.count()).isEqualTo(1); @@ -77,7 +114,7 @@ void notFoundResponse(WireMockRuntimeInfo wmRuntimeInfo) { void badRequestResponse(WireMockRuntimeInfo wmRuntimeInfo) { stubFor(get(anyUrl()).willReturn(badRequest())); - sendGetRequest(wmRuntimeInfo, ""); + sendHttpRequest(HttpMethod.GET, URI.create(wmRuntimeInfo.getHttpBaseUrl()), ""); Timer timer = getRegistry().get(timerName()).tags("method", "GET", "status", "400").timer(); assertThat(timer.count()).isEqualTo(1); diff --git a/micrometer-test/src/test/java/io/micrometer/core/instrument/ApacheHttpClientTimingInstrumentationVerificationSuite.java b/micrometer-test/src/test/java/io/micrometer/core/instrument/ApacheHttpClientTimingInstrumentationVerificationSuite.java index 9b6ef697e6..96cbb52e9c 100644 --- a/micrometer-test/src/test/java/io/micrometer/core/instrument/ApacheHttpClientTimingInstrumentationVerificationSuite.java +++ b/micrometer-test/src/test/java/io/micrometer/core/instrument/ApacheHttpClientTimingInstrumentationVerificationSuite.java @@ -15,18 +15,20 @@ */ package io.micrometer.core.instrument; -import com.github.tomakehurst.wiremock.junit5.WireMockRuntimeInfo; import io.micrometer.core.instrument.binder.httpcomponents.MicrometerHttpRequestExecutor; import org.apache.http.client.HttpClient; import org.apache.http.client.methods.HttpGet; +import org.apache.http.client.methods.HttpPost; +import org.apache.http.client.methods.HttpUriRequest; import org.apache.http.impl.client.HttpClientBuilder; import org.apache.http.util.EntityUtils; import java.io.IOException; +import java.net.URI; class ApacheHttpClientTimingInstrumentationVerificationSuite extends HttpClientTimingInstrumentationVerificationSuite { - private HttpClient httpClient = HttpClientBuilder.create() + private final HttpClient httpClient = HttpClientBuilder.create() .setRequestExecutor(MicrometerHttpRequestExecutor.builder(getRegistry()).build()).build(); @Override @@ -35,14 +37,28 @@ protected String timerName() { } @Override - void sendGetRequest(WireMockRuntimeInfo wmRuntimeInfo, String path) { + void sendHttpRequest(HttpMethod method, URI baseUri, String templatedPath, String... pathVariables) { try { EntityUtils - .consume(httpClient.execute(new HttpGet(wmRuntimeInfo.getHttpBaseUrl() + "/" + path)).getEntity()); + .consume( + httpClient + .execute(makeRequest(method, + URI.create( + baseUri + substitutePathVariables(templatedPath, pathVariables)))) + .getEntity()); } catch (IOException e) { throw new RuntimeException(e); } } + private HttpUriRequest makeRequest(HttpMethod method, URI uri) { + switch (method) { + case POST: + return new HttpPost(uri); + default: + return new HttpGet(uri); + } + } + } diff --git a/micrometer-test/src/test/java/io/micrometer/core/instrument/JettyClientTimingInstrumentationVerificationSuite.java b/micrometer-test/src/test/java/io/micrometer/core/instrument/JettyClientTimingInstrumentationVerificationSuite.java index ccc2a78e2f..0fcef4d9c4 100644 --- a/micrometer-test/src/test/java/io/micrometer/core/instrument/JettyClientTimingInstrumentationVerificationSuite.java +++ b/micrometer-test/src/test/java/io/micrometer/core/instrument/JettyClientTimingInstrumentationVerificationSuite.java @@ -15,11 +15,12 @@ */ package io.micrometer.core.instrument; -import com.github.tomakehurst.wiremock.junit5.WireMockRuntimeInfo; import io.micrometer.core.instrument.binder.jetty.JettyClientMetrics; import org.eclipse.jetty.client.HttpClient; import org.junit.jupiter.api.BeforeEach; +import java.net.URI; + class JettyClientTimingInstrumentationVerificationSuite extends HttpClientTimingInstrumentationVerificationSuite { private HttpClient httpClient = new HttpClient(); @@ -37,9 +38,16 @@ void setup() throws Exception { } @Override - void sendGetRequest(WireMockRuntimeInfo wmRuntimeInfo, String path) { + void sendHttpRequest(HttpMethod method, URI baseUri, String templatedPath, String... pathVariables) { try { - httpClient.GET(wmRuntimeInfo.getHttpBaseUrl() + "/" + path); + switch (method) { + case GET: + httpClient.GET(baseUri + substitutePathVariables(templatedPath, pathVariables)); + break; + case POST: + httpClient.POST(baseUri + substitutePathVariables(templatedPath, pathVariables)); + break; + } httpClient.stop(); } catch (Exception e) { diff --git a/micrometer-test/src/test/java/io/micrometer/core/instrument/OkHttpClientTimingInstrumentationVerificationSuite.java b/micrometer-test/src/test/java/io/micrometer/core/instrument/OkHttpClientTimingInstrumentationVerificationSuite.java index d82f5f2edd..66e948281a 100644 --- a/micrometer-test/src/test/java/io/micrometer/core/instrument/OkHttpClientTimingInstrumentationVerificationSuite.java +++ b/micrometer-test/src/test/java/io/micrometer/core/instrument/OkHttpClientTimingInstrumentationVerificationSuite.java @@ -15,13 +15,13 @@ */ package io.micrometer.core.instrument; -import com.github.tomakehurst.wiremock.junit5.WireMockRuntimeInfo; import io.micrometer.core.instrument.binder.okhttp3.OkHttpMetricsEventListener; import okhttp3.OkHttpClient; import okhttp3.Request; import okhttp3.Response; import java.io.IOException; +import java.net.URI; class OkHttpClientTimingInstrumentationVerificationSuite extends HttpClientTimingInstrumentationVerificationSuite { @@ -29,9 +29,10 @@ class OkHttpClientTimingInstrumentationVerificationSuite extends HttpClientTimin .eventListener(OkHttpMetricsEventListener.builder(getRegistry(), timerName()).build()).build(); @Override - void sendGetRequest(WireMockRuntimeInfo wmRuntimeInfo, String path) { - Request request = new Request.Builder().url(wmRuntimeInfo.getHttpBaseUrl() + "/" + path).build(); - try (Response response = httpClient.newCall(request).execute()) { + void sendHttpRequest(HttpMethod method, URI baseUri, String templatedPath, String... pathVariables) { + Request request = new Request.Builder().method(method.name(), null) + .url(baseUri + substitutePathVariables(templatedPath, pathVariables)).build(); + try (Response ignored = httpClient.newCall(request).execute()) { } catch (IOException e) { throw new RuntimeException(e); From fed9baac2e0dc3e30befb0dd0122abff39da95b4 Mon Sep 17 00:00:00 2001 From: Tommy Ludwig <8924140+shakuzen@users.noreply.github.com> Date: Mon, 4 Jul 2022 22:45:17 +0900 Subject: [PATCH 03/10] Fix build check issues --- .../micrometer/core/instrument/package-info.java | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) create mode 100644 micrometer-test/src/main/java/io/micrometer/core/instrument/package-info.java diff --git a/micrometer-test/src/main/java/io/micrometer/core/instrument/package-info.java b/micrometer-test/src/main/java/io/micrometer/core/instrument/package-info.java new file mode 100644 index 0000000000..f557a35c2a --- /dev/null +++ b/micrometer-test/src/main/java/io/micrometer/core/instrument/package-info.java @@ -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; From ad8a7bb2e5df56090f2007a5b7ff616435703927 Mon Sep 17 00:00:00 2001 From: Tommy Ludwig <8924140+shakuzen@users.noreply.github.com> Date: Mon, 4 Jul 2022 22:50:26 +0900 Subject: [PATCH 04/10] Polish JavaDocs --- ...ClientTimingInstrumentationVerificationSuite.java | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/micrometer-test/src/main/java/io/micrometer/core/instrument/HttpClientTimingInstrumentationVerificationSuite.java b/micrometer-test/src/main/java/io/micrometer/core/instrument/HttpClientTimingInstrumentationVerificationSuite.java index 3cad70e51f..20810a0515 100644 --- a/micrometer-test/src/main/java/io/micrometer/core/instrument/HttpClientTimingInstrumentationVerificationSuite.java +++ b/micrometer-test/src/main/java/io/micrometer/core/instrument/HttpClientTimingInstrumentationVerificationSuite.java @@ -28,8 +28,8 @@ /** * 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. WireMock is used as an HTTP - * server to receive real requests from an instrumented HTTP client. + * your instrumentation has the expected naming and tags. A locally running server is used + * to receive real requests from an instrumented HTTP client. */ @WireMockTest public abstract class HttpClientTimingInstrumentationVerificationSuite extends InstrumentationVerificationSuite { @@ -41,9 +41,9 @@ enum HttpMethod { } /** - * A default is provided that should be preferred by new instrumentations, but - * existing instrumentations that use a different value to maintain backwards - * compatibility may override this method to run tests with a different name. + * 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() { @@ -52,7 +52,7 @@ protected String timerName() { /** * Send an HTTP request using the instrumented HTTP client to the given base URL and - * path on the locally running WireMock server. The templated path should contain path + * path on the locally running server. The templated path should 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 From 36ed1bd673de0632943493ea050cd4eed46de2b5 Mon Sep 17 00:00:00 2001 From: Tommy Ludwig <8924140+shakuzen@users.noreply.github.com> Date: Mon, 4 Jul 2022 23:48:41 +0900 Subject: [PATCH 05/10] Assert more specific behavior of expected tags Each implementation has been configured to tag with the URI pattern and that is checked now. Unmapped paths, on the other hand, should not be tagged as requested in the URI. This is currently failing for the Apache HTTP instrumentation. --- ...imingInstrumentationVerificationSuite.java | 25 ++++++++++------ ...imingInstrumentationVerificationSuite.java | 30 +++++++++---------- ...imingInstrumentationVerificationSuite.java | 14 +++------ ...imingInstrumentationVerificationSuite.java | 3 +- 4 files changed, 36 insertions(+), 36 deletions(-) diff --git a/micrometer-test/src/main/java/io/micrometer/core/instrument/HttpClientTimingInstrumentationVerificationSuite.java b/micrometer-test/src/main/java/io/micrometer/core/instrument/HttpClientTimingInstrumentationVerificationSuite.java index 20810a0515..a65c545002 100644 --- a/micrometer-test/src/main/java/io/micrometer/core/instrument/HttpClientTimingInstrumentationVerificationSuite.java +++ b/micrometer-test/src/main/java/io/micrometer/core/instrument/HttpClientTimingInstrumentationVerificationSuite.java @@ -89,34 +89,41 @@ protected String substitutePathVariables(String templatedPath, String... pathVar } @Test - void successful(WireMockRuntimeInfo wmRuntimeInfo) { + void getTemplatedPath(WireMockRuntimeInfo wmRuntimeInfo) { stubFor(get(anyUrl()).willReturn(ok())); - sendHttpRequest(HttpMethod.GET, URI.create(wmRuntimeInfo.getHttpBaseUrl()), ""); + String templatedPath = "/customers/{customerId}/carts/{cartId}"; + sendHttpRequest(HttpMethod.GET, URI.create(wmRuntimeInfo.getHttpBaseUrl()), templatedPath, "112", "5"); - Timer timer = getRegistry().get(timerName()).tags("method", "GET", "status", "200").timer(); + 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 - void notFoundResponse(WireMockRuntimeInfo wmRuntimeInfo) { + void unmappedUrisAreCardinalityLimited(WireMockRuntimeInfo wmRuntimeInfo) { stubFor(get(anyUrl()).willReturn(notFound())); - sendHttpRequest(HttpMethod.GET, URI.create(wmRuntimeInfo.getHttpBaseUrl()), "/notFound"); + String templatedPath = "/notFound404"; + sendHttpRequest(HttpMethod.GET, URI.create(wmRuntimeInfo.getHttpBaseUrl()), templatedPath); Timer timer = getRegistry().get(timerName()).tags("method", "GET", "status", "404").timer(); + // we should standardize on a value e.g. NOT_FOUND, but for backwards + // compatibility, assert is more lenient + assertThat(timer.getId().getTag("uri")).isNotEqualTo(templatedPath); + assertThat(timer.count()).isEqualTo(1); assertThat(timer.totalTime(TimeUnit.NANOSECONDS)).isPositive(); } @Test - void badRequestResponse(WireMockRuntimeInfo wmRuntimeInfo) { - stubFor(get(anyUrl()).willReturn(badRequest())); + void serverException(WireMockRuntimeInfo wmRuntimeInfo) { + stubFor(get(anyUrl()).willReturn(serverError())); - sendHttpRequest(HttpMethod.GET, URI.create(wmRuntimeInfo.getHttpBaseUrl()), ""); + sendHttpRequest(HttpMethod.GET, URI.create(wmRuntimeInfo.getHttpBaseUrl()), "/socks"); - Timer timer = getRegistry().get(timerName()).tags("method", "GET", "status", "400").timer(); + Timer timer = getRegistry().get(timerName()).tags("method", "GET", "status", "500").timer(); assertThat(timer.count()).isEqualTo(1); assertThat(timer.totalTime(TimeUnit.NANOSECONDS)).isPositive(); } diff --git a/micrometer-test/src/test/java/io/micrometer/core/instrument/ApacheHttpClientTimingInstrumentationVerificationSuite.java b/micrometer-test/src/test/java/io/micrometer/core/instrument/ApacheHttpClientTimingInstrumentationVerificationSuite.java index 96cbb52e9c..bb04adfd02 100644 --- a/micrometer-test/src/test/java/io/micrometer/core/instrument/ApacheHttpClientTimingInstrumentationVerificationSuite.java +++ b/micrometer-test/src/test/java/io/micrometer/core/instrument/ApacheHttpClientTimingInstrumentationVerificationSuite.java @@ -15,10 +15,10 @@ */ package io.micrometer.core.instrument; +import io.micrometer.core.instrument.binder.httpcomponents.DefaultUriMapper; import io.micrometer.core.instrument.binder.httpcomponents.MicrometerHttpRequestExecutor; import org.apache.http.client.HttpClient; -import org.apache.http.client.methods.HttpGet; -import org.apache.http.client.methods.HttpPost; +import org.apache.http.client.methods.HttpEntityEnclosingRequestBase; import org.apache.http.client.methods.HttpUriRequest; import org.apache.http.impl.client.HttpClientBuilder; import org.apache.http.util.EntityUtils; @@ -39,26 +39,24 @@ protected String timerName() { @Override void sendHttpRequest(HttpMethod method, URI baseUri, String templatedPath, String... pathVariables) { try { - EntityUtils - .consume( - httpClient - .execute(makeRequest(method, - URI.create( - baseUri + substitutePathVariables(templatedPath, pathVariables)))) - .getEntity()); + EntityUtils.consume( + httpClient.execute(makeRequest(method, baseUri, templatedPath, pathVariables)).getEntity()); } catch (IOException e) { throw new RuntimeException(e); } } - private HttpUriRequest makeRequest(HttpMethod method, URI uri) { - switch (method) { - case POST: - return new HttpPost(uri); - default: - return new HttpGet(uri); - } + private HttpUriRequest makeRequest(HttpMethod method, URI baseUri, String templatedPath, String... pathVariables) { + HttpEntityEnclosingRequestBase request = new HttpEntityEnclosingRequestBase() { + @Override + public String getMethod() { + return method.name(); + } + }; + request.setURI(URI.create(baseUri + substitutePathVariables(templatedPath, pathVariables))); + request.setHeader(DefaultUriMapper.URI_PATTERN_HEADER, templatedPath); + return request; } } diff --git a/micrometer-test/src/test/java/io/micrometer/core/instrument/JettyClientTimingInstrumentationVerificationSuite.java b/micrometer-test/src/test/java/io/micrometer/core/instrument/JettyClientTimingInstrumentationVerificationSuite.java index 0fcef4d9c4..e859564dc7 100644 --- a/micrometer-test/src/test/java/io/micrometer/core/instrument/JettyClientTimingInstrumentationVerificationSuite.java +++ b/micrometer-test/src/test/java/io/micrometer/core/instrument/JettyClientTimingInstrumentationVerificationSuite.java @@ -32,22 +32,16 @@ protected String timerName() { @BeforeEach void setup() throws Exception { - httpClient.getRequestListeners().add( - JettyClientMetrics.builder(getRegistry(), result -> result.getRequest().getURI().getPath()).build()); + httpClient.getRequestListeners().add(JettyClientMetrics + .builder(getRegistry(), result -> result.getRequest().getHeaders().get("URI_PATTERN")).build()); httpClient.start(); } @Override void sendHttpRequest(HttpMethod method, URI baseUri, String templatedPath, String... pathVariables) { try { - switch (method) { - case GET: - httpClient.GET(baseUri + substitutePathVariables(templatedPath, pathVariables)); - break; - case POST: - httpClient.POST(baseUri + substitutePathVariables(templatedPath, pathVariables)); - break; - } + httpClient.newRequest(baseUri + substitutePathVariables(templatedPath, pathVariables)).method(method.name()) + .header("URI_PATTERN", templatedPath).send(); httpClient.stop(); } catch (Exception e) { diff --git a/micrometer-test/src/test/java/io/micrometer/core/instrument/OkHttpClientTimingInstrumentationVerificationSuite.java b/micrometer-test/src/test/java/io/micrometer/core/instrument/OkHttpClientTimingInstrumentationVerificationSuite.java index 66e948281a..bb37838f29 100644 --- a/micrometer-test/src/test/java/io/micrometer/core/instrument/OkHttpClientTimingInstrumentationVerificationSuite.java +++ b/micrometer-test/src/test/java/io/micrometer/core/instrument/OkHttpClientTimingInstrumentationVerificationSuite.java @@ -31,7 +31,8 @@ class OkHttpClientTimingInstrumentationVerificationSuite extends HttpClientTimin @Override void sendHttpRequest(HttpMethod method, URI baseUri, String templatedPath, String... pathVariables) { Request request = new Request.Builder().method(method.name(), null) - .url(baseUri + substitutePathVariables(templatedPath, pathVariables)).build(); + .url(baseUri + substitutePathVariables(templatedPath, pathVariables)) + .header(OkHttpMetricsEventListener.URI_PATTERN, templatedPath).build(); try (Response ignored = httpClient.newCall(request).execute()) { } catch (IOException e) { From 292e62b8ebda8cfe6ff9cba1389aeb49f3138d29 Mon Sep 17 00:00:00 2001 From: Tommy Ludwig <8924140+shakuzen@users.noreply.github.com> Date: Tue, 12 Jul 2022 00:09:36 +0900 Subject: [PATCH 06/10] Use more standard naming, polish Rename classes to be standard test class names. Minimize visibility. Remove 404 test, add a disabled test for requests to a down server. We will need to improve the existing instrumentations to add more tests of expected behavior such as that test. --- ...mingInstrumentationVerificationTests.java} | 29 +++++++++++-------- ... => InstrumentationVerificationTests.java} | 2 +- ...mingInstrumentationVerificationTests.java} | 2 +- ...mingInstrumentationVerificationTests.java} | 2 +- ...mingInstrumentationVerificationTests.java} | 2 +- 5 files changed, 21 insertions(+), 16 deletions(-) rename micrometer-test/src/main/java/io/micrometer/core/instrument/{HttpClientTimingInstrumentationVerificationSuite.java => HttpClientTimingInstrumentationVerificationTests.java} (84%) rename micrometer-test/src/main/java/io/micrometer/core/instrument/{InstrumentationVerificationSuite.java => InstrumentationVerificationTests.java} (93%) rename micrometer-test/src/test/java/io/micrometer/core/instrument/{ApacheHttpClientTimingInstrumentationVerificationSuite.java => ApacheHttpClientTimingInstrumentationVerificationTests.java} (95%) rename micrometer-test/src/test/java/io/micrometer/core/instrument/{JettyClientTimingInstrumentationVerificationSuite.java => JettyClientTimingInstrumentationVerificationTests.java} (93%) rename micrometer-test/src/test/java/io/micrometer/core/instrument/{OkHttpClientTimingInstrumentationVerificationSuite.java => OkHttpClientTimingInstrumentationVerificationTests.java} (93%) diff --git a/micrometer-test/src/main/java/io/micrometer/core/instrument/HttpClientTimingInstrumentationVerificationSuite.java b/micrometer-test/src/main/java/io/micrometer/core/instrument/HttpClientTimingInstrumentationVerificationTests.java similarity index 84% rename from micrometer-test/src/main/java/io/micrometer/core/instrument/HttpClientTimingInstrumentationVerificationSuite.java rename to micrometer-test/src/main/java/io/micrometer/core/instrument/HttpClientTimingInstrumentationVerificationTests.java index a65c545002..c127e012c0 100644 --- a/micrometer-test/src/main/java/io/micrometer/core/instrument/HttpClientTimingInstrumentationVerificationSuite.java +++ b/micrometer-test/src/main/java/io/micrometer/core/instrument/HttpClientTimingInstrumentationVerificationTests.java @@ -17,8 +17,11 @@ import com.github.tomakehurst.wiremock.junit5.WireMockRuntimeInfo; import com.github.tomakehurst.wiremock.junit5.WireMockTest; +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; @@ -32,7 +35,7 @@ * to receive real requests from an instrumented HTTP client. */ @WireMockTest -public abstract class HttpClientTimingInstrumentationVerificationSuite extends InstrumentationVerificationSuite { +public abstract class HttpClientTimingInstrumentationVerificationTests extends InstrumentationVerificationTests { enum HttpMethod { @@ -52,7 +55,7 @@ protected String timerName() { /** * Send an HTTP request using the instrumented HTTP client to the given base URL and - * path on the locally running server. The templated path should contain path + * 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 @@ -70,7 +73,7 @@ protected String timerName() { /** * 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. + * 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 @@ -89,7 +92,7 @@ protected String substitutePathVariables(String templatedPath, String... pathVar } @Test - void getTemplatedPath(WireMockRuntimeInfo wmRuntimeInfo) { + void getTemplatedPathForUri(WireMockRuntimeInfo wmRuntimeInfo) { stubFor(get(anyUrl()).willReturn(ok())); String templatedPath = "/customers/{customerId}/carts/{cartId}"; @@ -102,16 +105,18 @@ void getTemplatedPath(WireMockRuntimeInfo wmRuntimeInfo) { } @Test - void unmappedUrisAreCardinalityLimited(WireMockRuntimeInfo wmRuntimeInfo) { - stubFor(get(anyUrl()).willReturn(notFound())); + @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(); + } - String templatedPath = "/notFound404"; - sendHttpRequest(HttpMethod.GET, URI.create(wmRuntimeInfo.getHttpBaseUrl()), templatedPath); + try { + sendHttpRequest(HttpMethod.GET, URI.create("http://localhost:" + unusedPort), "/anything"); + } catch (Throwable ignore) {} - Timer timer = getRegistry().get(timerName()).tags("method", "GET", "status", "404").timer(); - // we should standardize on a value e.g. NOT_FOUND, but for backwards - // compatibility, assert is more lenient - assertThat(timer.getId().getTag("uri")).isNotEqualTo(templatedPath); + Timer timer = getRegistry().get(timerName()).tags("method", "GET").timer(); assertThat(timer.count()).isEqualTo(1); assertThat(timer.totalTime(TimeUnit.NANOSECONDS)).isPositive(); diff --git a/micrometer-test/src/main/java/io/micrometer/core/instrument/InstrumentationVerificationSuite.java b/micrometer-test/src/main/java/io/micrometer/core/instrument/InstrumentationVerificationTests.java similarity index 93% rename from micrometer-test/src/main/java/io/micrometer/core/instrument/InstrumentationVerificationSuite.java rename to micrometer-test/src/main/java/io/micrometer/core/instrument/InstrumentationVerificationTests.java index 7cd0a6c3de..b3b349f5e3 100644 --- a/micrometer-test/src/main/java/io/micrometer/core/instrument/InstrumentationVerificationSuite.java +++ b/micrometer-test/src/main/java/io/micrometer/core/instrument/InstrumentationVerificationTests.java @@ -17,7 +17,7 @@ import io.micrometer.core.instrument.simple.SimpleMeterRegistry; -public abstract class InstrumentationVerificationSuite { +abstract class InstrumentationVerificationTests { private final MeterRegistry registry = new SimpleMeterRegistry(); diff --git a/micrometer-test/src/test/java/io/micrometer/core/instrument/ApacheHttpClientTimingInstrumentationVerificationSuite.java b/micrometer-test/src/test/java/io/micrometer/core/instrument/ApacheHttpClientTimingInstrumentationVerificationTests.java similarity index 95% rename from micrometer-test/src/test/java/io/micrometer/core/instrument/ApacheHttpClientTimingInstrumentationVerificationSuite.java rename to micrometer-test/src/test/java/io/micrometer/core/instrument/ApacheHttpClientTimingInstrumentationVerificationTests.java index bb04adfd02..c7397ab1b7 100644 --- a/micrometer-test/src/test/java/io/micrometer/core/instrument/ApacheHttpClientTimingInstrumentationVerificationSuite.java +++ b/micrometer-test/src/test/java/io/micrometer/core/instrument/ApacheHttpClientTimingInstrumentationVerificationTests.java @@ -26,7 +26,7 @@ import java.io.IOException; import java.net.URI; -class ApacheHttpClientTimingInstrumentationVerificationSuite extends HttpClientTimingInstrumentationVerificationSuite { +class ApacheHttpClientTimingInstrumentationVerificationTests extends HttpClientTimingInstrumentationVerificationTests { private final HttpClient httpClient = HttpClientBuilder.create() .setRequestExecutor(MicrometerHttpRequestExecutor.builder(getRegistry()).build()).build(); diff --git a/micrometer-test/src/test/java/io/micrometer/core/instrument/JettyClientTimingInstrumentationVerificationSuite.java b/micrometer-test/src/test/java/io/micrometer/core/instrument/JettyClientTimingInstrumentationVerificationTests.java similarity index 93% rename from micrometer-test/src/test/java/io/micrometer/core/instrument/JettyClientTimingInstrumentationVerificationSuite.java rename to micrometer-test/src/test/java/io/micrometer/core/instrument/JettyClientTimingInstrumentationVerificationTests.java index e859564dc7..1f40223205 100644 --- a/micrometer-test/src/test/java/io/micrometer/core/instrument/JettyClientTimingInstrumentationVerificationSuite.java +++ b/micrometer-test/src/test/java/io/micrometer/core/instrument/JettyClientTimingInstrumentationVerificationTests.java @@ -21,7 +21,7 @@ import java.net.URI; -class JettyClientTimingInstrumentationVerificationSuite extends HttpClientTimingInstrumentationVerificationSuite { +class JettyClientTimingInstrumentationVerificationTests extends HttpClientTimingInstrumentationVerificationTests { private HttpClient httpClient = new HttpClient(); diff --git a/micrometer-test/src/test/java/io/micrometer/core/instrument/OkHttpClientTimingInstrumentationVerificationSuite.java b/micrometer-test/src/test/java/io/micrometer/core/instrument/OkHttpClientTimingInstrumentationVerificationTests.java similarity index 93% rename from micrometer-test/src/test/java/io/micrometer/core/instrument/OkHttpClientTimingInstrumentationVerificationSuite.java rename to micrometer-test/src/test/java/io/micrometer/core/instrument/OkHttpClientTimingInstrumentationVerificationTests.java index bb37838f29..6a018545ae 100644 --- a/micrometer-test/src/test/java/io/micrometer/core/instrument/OkHttpClientTimingInstrumentationVerificationSuite.java +++ b/micrometer-test/src/test/java/io/micrometer/core/instrument/OkHttpClientTimingInstrumentationVerificationTests.java @@ -23,7 +23,7 @@ import java.io.IOException; import java.net.URI; -class OkHttpClientTimingInstrumentationVerificationSuite extends HttpClientTimingInstrumentationVerificationSuite { +class OkHttpClientTimingInstrumentationVerificationTests extends HttpClientTimingInstrumentationVerificationTests { OkHttpClient httpClient = new OkHttpClient.Builder() .eventListener(OkHttpMetricsEventListener.builder(getRegistry(), timerName()).build()).build(); From 1d909962d89bbb6963f7b010316c51724b3bbf44 Mon Sep 17 00:00:00 2001 From: Tommy Ludwig <8924140+shakuzen@users.noreply.github.com> Date: Tue, 12 Jul 2022 00:17:44 +0900 Subject: [PATCH 07/10] Fix formatting --- ...lientTimingInstrumentationVerificationTests.java | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/micrometer-test/src/main/java/io/micrometer/core/instrument/HttpClientTimingInstrumentationVerificationTests.java b/micrometer-test/src/main/java/io/micrometer/core/instrument/HttpClientTimingInstrumentationVerificationTests.java index c127e012c0..d230c97ae0 100644 --- a/micrometer-test/src/main/java/io/micrometer/core/instrument/HttpClientTimingInstrumentationVerificationTests.java +++ b/micrometer-test/src/main/java/io/micrometer/core/instrument/HttpClientTimingInstrumentationVerificationTests.java @@ -55,10 +55,11 @@ protected String timerName() { /** * 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 + * 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. @@ -114,7 +115,9 @@ void timedWhenServerIsMissing() throws IOException { try { sendHttpRequest(HttpMethod.GET, URI.create("http://localhost:" + unusedPort), "/anything"); - } catch (Throwable ignore) {} + } + catch (Throwable ignore) { + } Timer timer = getRegistry().get(timerName()).tags("method", "GET").timer(); From 3c74cfd7fc93d02a3fb3d3b2afe044575009df76 Mon Sep 17 00:00:00 2001 From: Tommy Ludwig <8924140+shakuzen@users.noreply.github.com> Date: Tue, 12 Jul 2022 00:21:17 +0900 Subject: [PATCH 08/10] Mark as incubating, add client exception --- ...lientTimingInstrumentationVerificationTests.java | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/micrometer-test/src/main/java/io/micrometer/core/instrument/HttpClientTimingInstrumentationVerificationTests.java b/micrometer-test/src/main/java/io/micrometer/core/instrument/HttpClientTimingInstrumentationVerificationTests.java index d230c97ae0..09a6ed5422 100644 --- a/micrometer-test/src/main/java/io/micrometer/core/instrument/HttpClientTimingInstrumentationVerificationTests.java +++ b/micrometer-test/src/main/java/io/micrometer/core/instrument/HttpClientTimingInstrumentationVerificationTests.java @@ -17,6 +17,7 @@ import com.github.tomakehurst.wiremock.junit5.WireMockRuntimeInfo; import com.github.tomakehurst.wiremock.junit5.WireMockTest; +import io.micrometer.core.annotation.Incubating; import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.Test; @@ -35,6 +36,7 @@ * to receive real requests from an instrumented HTTP client. */ @WireMockTest +@Incubating(since = "1.9.2") public abstract class HttpClientTimingInstrumentationVerificationTests extends InstrumentationVerificationTests { enum HttpMethod { @@ -136,4 +138,15 @@ void serverException(WireMockRuntimeInfo wmRuntimeInfo) { assertThat(timer.totalTime(TimeUnit.NANOSECONDS)).isPositive(); } + @Test + void clientException(WireMockRuntimeInfo wmRuntimeInfo) { + stubFor(get(anyUrl()).willReturn(badRequest())); + + sendHttpRequest(HttpMethod.GET, URI.create(wmRuntimeInfo.getHttpBaseUrl()), "/socks"); + + Timer timer = getRegistry().get(timerName()).tags("method", "GET", "status", "400").timer(); + assertThat(timer.count()).isEqualTo(1); + assertThat(timer.totalTime(TimeUnit.NANOSECONDS)).isPositive(); + } + } From aa07c38cb3fba3f170992da4645557ea7d0ce671 Mon Sep 17 00:00:00 2001 From: Tommy Ludwig <8924140+shakuzen@users.noreply.github.com> Date: Tue, 12 Jul 2022 00:48:32 +0900 Subject: [PATCH 09/10] Add body parameter for potential future use We want to test more than the GET method, and some HTTP clients require a body for POST requests. --- ...ntTimingInstrumentationVerificationTests.java | 16 +++++++++------- ...ntTimingInstrumentationVerificationTests.java | 16 +++++++++++++--- ...ntTimingInstrumentationVerificationTests.java | 16 ++++++++++++---- ...ntTimingInstrumentationVerificationTests.java | 7 +++++-- 4 files changed, 39 insertions(+), 16 deletions(-) diff --git a/micrometer-test/src/main/java/io/micrometer/core/instrument/HttpClientTimingInstrumentationVerificationTests.java b/micrometer-test/src/main/java/io/micrometer/core/instrument/HttpClientTimingInstrumentationVerificationTests.java index 09a6ed5422..d60f3ab435 100644 --- a/micrometer-test/src/main/java/io/micrometer/core/instrument/HttpClientTimingInstrumentationVerificationTests.java +++ b/micrometer-test/src/main/java/io/micrometer/core/instrument/HttpClientTimingInstrumentationVerificationTests.java @@ -18,6 +18,7 @@ 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; @@ -71,7 +72,8 @@ protected String timerName() { * forward slash, and optionally containing path variable placeholders * @param pathVariables optional variables to substitute into the templatedPath */ - abstract void sendHttpRequest(HttpMethod method, URI baseUrl, String templatedPath, String... pathVariables); + abstract void sendHttpRequest(HttpMethod method, @Nullable byte[] body, URI baseUrl, String templatedPath, + String... pathVariables); /** * Convenience method provided to substitute the template placeholders for the @@ -99,7 +101,7 @@ void getTemplatedPathForUri(WireMockRuntimeInfo wmRuntimeInfo) { stubFor(get(anyUrl()).willReturn(ok())); String templatedPath = "/customers/{customerId}/carts/{cartId}"; - sendHttpRequest(HttpMethod.GET, URI.create(wmRuntimeInfo.getHttpBaseUrl()), templatedPath, "112", "5"); + sendHttpRequest(HttpMethod.GET, null, URI.create(wmRuntimeInfo.getHttpBaseUrl()), templatedPath, "112", "5"); Timer timer = getRegistry().get(timerName()).tags("method", "GET", "status", "200", "uri", templatedPath) .timer(); @@ -116,7 +118,7 @@ void timedWhenServerIsMissing() throws IOException { } try { - sendHttpRequest(HttpMethod.GET, URI.create("http://localhost:" + unusedPort), "/anything"); + sendHttpRequest(HttpMethod.GET, null, URI.create("http://localhost:" + unusedPort), "/anything"); } catch (Throwable ignore) { } @@ -131,7 +133,7 @@ void timedWhenServerIsMissing() throws IOException { void serverException(WireMockRuntimeInfo wmRuntimeInfo) { stubFor(get(anyUrl()).willReturn(serverError())); - sendHttpRequest(HttpMethod.GET, URI.create(wmRuntimeInfo.getHttpBaseUrl()), "/socks"); + 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); @@ -140,11 +142,11 @@ void serverException(WireMockRuntimeInfo wmRuntimeInfo) { @Test void clientException(WireMockRuntimeInfo wmRuntimeInfo) { - stubFor(get(anyUrl()).willReturn(badRequest())); + stubFor(post(anyUrl()).willReturn(badRequest())); - sendHttpRequest(HttpMethod.GET, URI.create(wmRuntimeInfo.getHttpBaseUrl()), "/socks"); + sendHttpRequest(HttpMethod.POST, new byte[0], URI.create(wmRuntimeInfo.getHttpBaseUrl()), "/socks"); - Timer timer = getRegistry().get(timerName()).tags("method", "GET", "status", "400").timer(); + Timer timer = getRegistry().get(timerName()).tags("method", "POST", "status", "400").timer(); assertThat(timer.count()).isEqualTo(1); assertThat(timer.totalTime(TimeUnit.NANOSECONDS)).isPositive(); } diff --git a/micrometer-test/src/test/java/io/micrometer/core/instrument/ApacheHttpClientTimingInstrumentationVerificationTests.java b/micrometer-test/src/test/java/io/micrometer/core/instrument/ApacheHttpClientTimingInstrumentationVerificationTests.java index c7397ab1b7..f7f8e7ac68 100644 --- a/micrometer-test/src/test/java/io/micrometer/core/instrument/ApacheHttpClientTimingInstrumentationVerificationTests.java +++ b/micrometer-test/src/test/java/io/micrometer/core/instrument/ApacheHttpClientTimingInstrumentationVerificationTests.java @@ -17,12 +17,15 @@ 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; @@ -37,23 +40,30 @@ protected String timerName() { } @Override - void sendHttpRequest(HttpMethod method, URI baseUri, String templatedPath, String... pathVariables) { + void sendHttpRequest(HttpMethod method, @Nullable byte[] body, URI baseUri, String templatedPath, + String... pathVariables) { try { EntityUtils.consume( - httpClient.execute(makeRequest(method, baseUri, templatedPath, pathVariables)).getEntity()); + httpClient.execute(makeRequest(method, body, baseUri, templatedPath, pathVariables)).getEntity()); } catch (IOException e) { throw new RuntimeException(e); } } - private HttpUriRequest makeRequest(HttpMethod method, URI baseUri, String templatedPath, String... pathVariables) { + 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; diff --git a/micrometer-test/src/test/java/io/micrometer/core/instrument/JettyClientTimingInstrumentationVerificationTests.java b/micrometer-test/src/test/java/io/micrometer/core/instrument/JettyClientTimingInstrumentationVerificationTests.java index 1f40223205..df85afbe9f 100644 --- a/micrometer-test/src/test/java/io/micrometer/core/instrument/JettyClientTimingInstrumentationVerificationTests.java +++ b/micrometer-test/src/test/java/io/micrometer/core/instrument/JettyClientTimingInstrumentationVerificationTests.java @@ -16,14 +16,17 @@ 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 HttpClient httpClient = new HttpClient(); + private final HttpClient httpClient = new HttpClient(); @Override protected String timerName() { @@ -38,10 +41,15 @@ void setup() throws Exception { } @Override - void sendHttpRequest(HttpMethod method, URI baseUri, String templatedPath, String... pathVariables) { + void sendHttpRequest(HttpMethod method, @Nullable byte[] body, URI baseUri, String templatedPath, + String... pathVariables) { try { - httpClient.newRequest(baseUri + substitutePathVariables(templatedPath, pathVariables)).method(method.name()) - .header("URI_PATTERN", templatedPath).send(); + 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) { diff --git a/micrometer-test/src/test/java/io/micrometer/core/instrument/OkHttpClientTimingInstrumentationVerificationTests.java b/micrometer-test/src/test/java/io/micrometer/core/instrument/OkHttpClientTimingInstrumentationVerificationTests.java index 6a018545ae..3aa24fe76f 100644 --- a/micrometer-test/src/test/java/io/micrometer/core/instrument/OkHttpClientTimingInstrumentationVerificationTests.java +++ b/micrometer-test/src/test/java/io/micrometer/core/instrument/OkHttpClientTimingInstrumentationVerificationTests.java @@ -16,8 +16,10 @@ package io.micrometer.core.instrument; import io.micrometer.core.instrument.binder.okhttp3.OkHttpMetricsEventListener; +import io.micrometer.core.lang.Nullable; import okhttp3.OkHttpClient; import okhttp3.Request; +import okhttp3.RequestBody; import okhttp3.Response; import java.io.IOException; @@ -29,8 +31,9 @@ class OkHttpClientTimingInstrumentationVerificationTests extends HttpClientTimin .eventListener(OkHttpMetricsEventListener.builder(getRegistry(), timerName()).build()).build(); @Override - void sendHttpRequest(HttpMethod method, URI baseUri, String templatedPath, String... pathVariables) { - Request request = new Request.Builder().method(method.name(), null) + void sendHttpRequest(HttpMethod method, @Nullable byte[] body, URI baseUri, String templatedPath, + String... pathVariables) { + Request request = new Request.Builder().method(method.name(), body == null ? null : RequestBody.create(body)) .url(baseUri + substitutePathVariables(templatedPath, pathVariables)) .header(OkHttpMetricsEventListener.URI_PATTERN, templatedPath).build(); try (Response ignored = httpClient.newCall(request).execute()) { From b5c3e49a6b2ac2f577254d7b4b56b524b1afdd4a Mon Sep 17 00:00:00 2001 From: Tommy Ludwig <8924140+shakuzen@users.noreply.github.com> Date: Tue, 12 Jul 2022 01:05:51 +0900 Subject: [PATCH 10/10] Write locks for 1.8.x --- micrometer-core/gradle.lockfile | 12 ++++++++---- micrometer-test/gradle.lockfile | 9 ++++++++- samples/micrometer-samples-javalin/gradle.lockfile | 12 ++++++++---- 3 files changed, 24 insertions(+), 9 deletions(-) diff --git a/micrometer-core/gradle.lockfile b/micrometer-core/gradle.lockfile index 3fc4452f7d..7f0f019e54 100644 --- a/micrometer-core/gradle.lockfile +++ b/micrometer-core/gradle.lockfile @@ -131,11 +131,15 @@ org.assertj:assertj-core:3.22.0=testCompileClasspath,testRuntimeClasspath org.awaitility:awaitility:4.2.0=testCompileClasspath,testRuntimeClasspath org.checkerframework:checker-qual:2.11.1=nohttp org.checkerframework:checker-qual:3.12.0=checkstyle,compileClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath -org.eclipse.jetty:jetty-client:9.4.44.v20210927=compileClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath -org.eclipse.jetty:jetty-http:9.4.44.v20210927=compileClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath -org.eclipse.jetty:jetty-io:9.4.44.v20210927=compileClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath +org.eclipse.jetty:jetty-client:9.4.44.v20210927=runtimeClasspath +org.eclipse.jetty:jetty-client:9.4.48.v20220622=compileClasspath,testCompileClasspath,testRuntimeClasspath +org.eclipse.jetty:jetty-http:9.4.44.v20210927=runtimeClasspath +org.eclipse.jetty:jetty-http:9.4.48.v20220622=compileClasspath,testCompileClasspath,testRuntimeClasspath +org.eclipse.jetty:jetty-io:9.4.44.v20210927=runtimeClasspath +org.eclipse.jetty:jetty-io:9.4.48.v20220622=compileClasspath,testCompileClasspath,testRuntimeClasspath org.eclipse.jetty:jetty-server:9.4.44.v20210927=compileClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath -org.eclipse.jetty:jetty-util:9.4.44.v20210927=compileClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath +org.eclipse.jetty:jetty-util:9.4.44.v20210927=runtimeClasspath +org.eclipse.jetty:jetty-util:9.4.48.v20220622=compileClasspath,testCompileClasspath,testRuntimeClasspath org.ehcache:ehcache:3.9.7=testCompileClasspath,testRuntimeClasspath org.glassfish.hk2.external:aopalliance-repackaged:2.6.1=testRuntimeClasspath org.glassfish.hk2.external:jakarta.inject:2.6.1=compileClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath diff --git a/micrometer-test/gradle.lockfile b/micrometer-test/gradle.lockfile index e3d711d01d..9ad631e56f 100644 --- a/micrometer-test/gradle.lockfile +++ b/micrometer-test/gradle.lockfile @@ -20,8 +20,9 @@ com.puppycrawl.tools:checkstyle:9.0.1=checkstyle com.squareup.okhttp3:okhttp:5.0.0-alpha.2=testCompileClasspath,testRuntimeClasspath com.squareup.okio:okio:2.9.0=testCompileClasspath,testRuntimeClasspath commons-beanutils:commons-beanutils:1.9.4=checkstyle,nohttp +commons-codec:commons-codec:1.11=testCompileClasspath,testRuntimeClasspath commons-collections:commons-collections:3.2.2=checkstyle,nohttp -commons-logging:commons-logging:1.2=nohttp +commons-logging:commons-logging:1.2=nohttp,testCompileClasspath,testRuntimeClasspath info.picocli:picocli:3.9.5=nohttp-cli info.picocli:picocli:4.3.1=nohttp info.picocli:picocli:4.6.1=checkstyle @@ -62,10 +63,16 @@ net.sf.saxon:Saxon-HE:10.6=checkstyle net.sf.saxon:Saxon-HE:9.9.1-7=nohttp org.antlr:antlr4-runtime:4.8-1=nohttp org.antlr:antlr4-runtime:4.9.2=checkstyle +org.apache.httpcomponents:httpclient:4.5.13=testCompileClasspath,testRuntimeClasspath +org.apache.httpcomponents:httpcore:4.4.13=testCompileClasspath,testRuntimeClasspath org.apiguardian:apiguardian-api:1.1.2=compileClasspath,testCompileClasspath org.assertj:assertj-core:3.22.0=compileClasspath,default,runtimeClasspath,testCompileClasspath,testRuntimeClasspath org.checkerframework:checker-qual:2.11.1=nohttp org.checkerframework:checker-qual:3.12.0=checkstyle,testCompileClasspath,testRuntimeClasspath +org.eclipse.jetty:jetty-client:9.4.48.v20220622=testCompileClasspath,testRuntimeClasspath +org.eclipse.jetty:jetty-http:9.4.48.v20220622=testCompileClasspath,testRuntimeClasspath +org.eclipse.jetty:jetty-io:9.4.48.v20220622=testCompileClasspath,testRuntimeClasspath +org.eclipse.jetty:jetty-util:9.4.48.v20220622=testCompileClasspath,testRuntimeClasspath org.hdrhistogram:HdrHistogram:2.1.12=compileClasspath,default,runtimeClasspath,testCompileClasspath,testRuntimeClasspath org.javassist:javassist:3.26.0-GA=checkstyle org.jboss.spec.javax.interceptor:jboss-interceptors-api_1.1_spec:1.0.0.Beta1=testCompileClasspath,testRuntimeClasspath diff --git a/samples/micrometer-samples-javalin/gradle.lockfile b/samples/micrometer-samples-javalin/gradle.lockfile index d08bbd6b02..3a6ee36e04 100644 --- a/samples/micrometer-samples-javalin/gradle.lockfile +++ b/samples/micrometer-samples-javalin/gradle.lockfile @@ -46,14 +46,18 @@ org.eclipse.jetty.websocket:websocket-client:9.4.44.v20210927=compileClasspath,d org.eclipse.jetty.websocket:websocket-common:9.4.44.v20210927=compileClasspath,default,runtimeClasspath,testCompileClasspath,testRuntimeClasspath org.eclipse.jetty.websocket:websocket-server:9.4.44.v20210927=compileClasspath,default,runtimeClasspath,testCompileClasspath,testRuntimeClasspath org.eclipse.jetty.websocket:websocket-servlet:9.4.44.v20210927=compileClasspath,default,runtimeClasspath,testCompileClasspath,testRuntimeClasspath -org.eclipse.jetty:jetty-client:9.4.44.v20210927=compileClasspath,default,runtimeClasspath,testCompileClasspath,testRuntimeClasspath -org.eclipse.jetty:jetty-http:9.4.44.v20210927=compileClasspath,default,runtimeClasspath,testCompileClasspath,testRuntimeClasspath -org.eclipse.jetty:jetty-io:9.4.44.v20210927=compileClasspath,default,runtimeClasspath,testCompileClasspath,testRuntimeClasspath +org.eclipse.jetty:jetty-client:9.4.44.v20210927=default,runtimeClasspath,testCompileClasspath,testRuntimeClasspath +org.eclipse.jetty:jetty-client:9.4.48.v20220622=compileClasspath +org.eclipse.jetty:jetty-http:9.4.44.v20210927=default,runtimeClasspath,testCompileClasspath,testRuntimeClasspath +org.eclipse.jetty:jetty-http:9.4.48.v20220622=compileClasspath +org.eclipse.jetty:jetty-io:9.4.44.v20210927=default,runtimeClasspath,testCompileClasspath,testRuntimeClasspath +org.eclipse.jetty:jetty-io:9.4.48.v20220622=compileClasspath org.eclipse.jetty:jetty-security:9.4.44.v20210927=compileClasspath,default,runtimeClasspath,testCompileClasspath,testRuntimeClasspath org.eclipse.jetty:jetty-server:9.4.44.v20210927=compileClasspath,default,runtimeClasspath,testCompileClasspath,testRuntimeClasspath org.eclipse.jetty:jetty-servlet:9.4.44.v20210927=compileClasspath,default,runtimeClasspath,testCompileClasspath,testRuntimeClasspath org.eclipse.jetty:jetty-util-ajax:9.4.44.v20210927=compileClasspath,default,runtimeClasspath,testCompileClasspath,testRuntimeClasspath -org.eclipse.jetty:jetty-util:9.4.44.v20210927=compileClasspath,default,runtimeClasspath,testCompileClasspath,testRuntimeClasspath +org.eclipse.jetty:jetty-util:9.4.44.v20210927=default,runtimeClasspath,testCompileClasspath,testRuntimeClasspath +org.eclipse.jetty:jetty-util:9.4.48.v20220622=compileClasspath org.eclipse.jetty:jetty-webapp:9.4.44.v20210927=compileClasspath,default,runtimeClasspath,testCompileClasspath,testRuntimeClasspath org.eclipse.jetty:jetty-xml:9.4.44.v20210927=compileClasspath,default,runtimeClasspath,testCompileClasspath,testRuntimeClasspath org.hdrhistogram:HdrHistogram:2.1.12=compileClasspath,default,runtimeClasspath,testCompileClasspath,testRuntimeClasspath