diff --git a/micrometer-core/src/main/java/io/micrometer/core/instrument/binder/httpcomponents/MicrometerHttpClientInterceptor.java b/micrometer-core/src/main/java/io/micrometer/core/instrument/binder/httpcomponents/MicrometerHttpClientInterceptor.java index 04800d4463..243a79c8d5 100644 --- a/micrometer-core/src/main/java/io/micrometer/core/instrument/binder/httpcomponents/MicrometerHttpClientInterceptor.java +++ b/micrometer-core/src/main/java/io/micrometer/core/instrument/binder/httpcomponents/MicrometerHttpClientInterceptor.java @@ -68,11 +68,12 @@ public class MicrometerHttpClientInterceptor { * @param uriMapper URI mapper to create {@code uri} tag * @param extraTags extra tags * @param exportTagsForRoute whether to export tags for route + * @param meterName meter name */ public MicrometerHttpClientInterceptor(MeterRegistry meterRegistry, Function uriMapper, - Iterable extraTags, boolean exportTagsForRoute) { + Iterable extraTags, boolean exportTagsForRoute, String meterName) { this.requestInterceptor = (request, context) -> timerByHttpContext.put(context, - Timer.resource(meterRegistry, METER_NAME) + Timer.resource(meterRegistry, meterName) .tags("method", request.getRequestLine().getMethod(), "uri", uriMapper.apply(request))); this.responseInterceptor = (response, context) -> { @@ -84,6 +85,31 @@ public MicrometerHttpClientInterceptor(MeterRegistry meterRegistry, Function uriMapper, + Iterable extraTags, boolean exportTagsForRoute) { + this(meterRegistry, uriMapper, extraTags, exportTagsForRoute, METER_NAME); + } + + /** + * Create a {@code MicrometerHttpClientInterceptor} instance with + * {@link DefaultUriMapper}. + * @param meterRegistry meter registry to bind + * @param extraTags extra tags + * @param exportTagsForRoute whether to export tags for route + * @param meterName meter name + */ + public MicrometerHttpClientInterceptor(MeterRegistry meterRegistry, Iterable extraTags, + boolean exportTagsForRoute, String meterName) { + this(meterRegistry, new DefaultUriMapper(), extraTags, exportTagsForRoute, meterName); + } + /** * Create a {@code MicrometerHttpClientInterceptor} instance with * {@link DefaultUriMapper}. @@ -93,7 +119,7 @@ public MicrometerHttpClientInterceptor(MeterRegistry meterRegistry, Function extraTags, boolean exportTagsForRoute) { - this(meterRegistry, new DefaultUriMapper(), extraTags, exportTagsForRoute); + this(meterRegistry, extraTags, exportTagsForRoute, METER_NAME); } public HttpRequestInterceptor getRequestInterceptor() { diff --git a/micrometer-core/src/main/java/io/micrometer/core/instrument/binder/httpcomponents/MicrometerHttpRequestExecutor.java b/micrometer-core/src/main/java/io/micrometer/core/instrument/binder/httpcomponents/MicrometerHttpRequestExecutor.java index fda9dcfa7a..29506522a0 100644 --- a/micrometer-core/src/main/java/io/micrometer/core/instrument/binder/httpcomponents/MicrometerHttpRequestExecutor.java +++ b/micrometer-core/src/main/java/io/micrometer/core/instrument/binder/httpcomponents/MicrometerHttpRequestExecutor.java @@ -71,6 +71,8 @@ public class MicrometerHttpRequestExecutor extends HttpRequestExecutor { private final MeterRegistry registry; + private final String meterName; + private final ObservationRegistry observationRegistry; @Nullable @@ -85,12 +87,13 @@ public class MicrometerHttpRequestExecutor extends HttpRequestExecutor { /** * Use {@link #builder(MeterRegistry)} to create an instance of this class. */ - private MicrometerHttpRequestExecutor(int waitForContinue, MeterRegistry registry, + private MicrometerHttpRequestExecutor(int waitForContinue, MeterRegistry registry, String meterName, Function uriMapper, Iterable extraTags, boolean exportTagsForRoute, ObservationRegistry observationRegistry, @Nullable ApacheHttpClientObservationConvention convention) { super(waitForContinue); this.registry = Optional.ofNullable(registry) .orElseThrow(() -> new IllegalArgumentException("registry is required but has been initialized with null")); + this.meterName = meterName; this.uriMapper = Optional.ofNullable(uriMapper) .orElseThrow( () -> new IllegalArgumentException("uriMapper is required but has been initialized with null")); @@ -132,7 +135,7 @@ public HttpResponse execute(HttpRequest request, HttpClientConnection conn, Http } finally { String status = statusCodeOrError; - sample.stop(METER_NAME, "Duration of Apache HttpClient request execution", + sample.stop(this.meterName, "Duration of Apache HttpClient request execution", () -> Tags .of("method", DefaultApacheHttpClientObservationConvention.INSTANCE.getMethodString(request), "uri", uriMapper.apply(request), "status", status) @@ -145,6 +148,8 @@ public static class Builder { private final MeterRegistry registry; + private String requestsMeterName = MicrometerHttpRequestExecutor.METER_NAME; + private ObservationRegistry observationRegistry = ObservationRegistry.NOOP; private int waitForContinue = HttpRequestExecutor.DEFAULT_WAIT_FOR_CONTINUE; @@ -246,13 +251,25 @@ public Builder observationConvention(ApacheHttpClientObservationConvention conve return this; } + /** + * Provide a name to override the default meter name + * @param name Meter name to use when instrumentation is done with Timer. This + * does not have any effect when an + * {@link #observationRegistry(ObservationRegistry)} is configured + * @return This builder instance + */ + public Builder meterName(String name) { + this.requestsMeterName = name; + return this; + } + /** * Creates an instance of {@link MicrometerHttpRequestExecutor} with all the * configured properties. * @return an instance of {@link MicrometerHttpRequestExecutor} */ public MicrometerHttpRequestExecutor build() { - return new MicrometerHttpRequestExecutor(waitForContinue, registry, uriMapper, extraTags, + return new MicrometerHttpRequestExecutor(waitForContinue, registry, requestsMeterName, uriMapper, extraTags, exportTagsForRoute, observationRegistry, observationConvention); } diff --git a/micrometer-core/src/main/java/io/micrometer/core/instrument/binder/httpcomponents/hc5/MicrometerHttpClientInterceptor.java b/micrometer-core/src/main/java/io/micrometer/core/instrument/binder/httpcomponents/hc5/MicrometerHttpClientInterceptor.java index 2ef9ecc60f..8d57038bfe 100644 --- a/micrometer-core/src/main/java/io/micrometer/core/instrument/binder/httpcomponents/hc5/MicrometerHttpClientInterceptor.java +++ b/micrometer-core/src/main/java/io/micrometer/core/instrument/binder/httpcomponents/hc5/MicrometerHttpClientInterceptor.java @@ -62,11 +62,12 @@ public class MicrometerHttpClientInterceptor { * @param uriMapper URI mapper to create {@code uri} tag * @param extraTags extra tags * @param exportTagsForRoute whether to export tags for route + * @param meterName meter name */ public MicrometerHttpClientInterceptor(MeterRegistry meterRegistry, Function uriMapper, - Iterable extraTags, boolean exportTagsForRoute) { + Iterable extraTags, boolean exportTagsForRoute, String meterName) { this.requestInterceptor = (request, entityDetails, context) -> timerByHttpContext.put(context, - Timer.resource(meterRegistry, METER_NAME) + Timer.resource(meterRegistry, meterName) .tags("method", request.getMethod(), "uri", uriMapper.apply(request))); this.responseInterceptor = (response, entityDetails, context) -> { @@ -78,6 +79,31 @@ public MicrometerHttpClientInterceptor(MeterRegistry meterRegistry, Function uriMapper, + Iterable extraTags, boolean exportTagsForRoute) { + this(meterRegistry, uriMapper, extraTags, exportTagsForRoute, METER_NAME); + } + + /** + * Create a {@code MicrometerHttpClientInterceptor} instance with + * {@link DefaultUriMapper}. + * @param meterRegistry meter registry to bind + * @param extraTags extra tags + * @param exportTagsForRoute whether to export tags for route + * @param meterName meter name + */ + public MicrometerHttpClientInterceptor(MeterRegistry meterRegistry, Iterable extraTags, + boolean exportTagsForRoute, String meterName) { + this(meterRegistry, new DefaultUriMapper(), extraTags, exportTagsForRoute, meterName); + } + /** * Create a {@code MicrometerHttpClientInterceptor} instance with * {@link DefaultUriMapper}. @@ -87,7 +113,7 @@ public MicrometerHttpClientInterceptor(MeterRegistry meterRegistry, Function extraTags, boolean exportTagsForRoute) { - this(meterRegistry, new DefaultUriMapper(), extraTags, exportTagsForRoute); + this(meterRegistry, extraTags, exportTagsForRoute, METER_NAME); } public HttpRequestInterceptor getRequestInterceptor() { diff --git a/micrometer-core/src/main/java/io/micrometer/core/instrument/binder/httpcomponents/hc5/MicrometerHttpRequestExecutor.java b/micrometer-core/src/main/java/io/micrometer/core/instrument/binder/httpcomponents/hc5/MicrometerHttpRequestExecutor.java index b378c63add..c9775e08ef 100644 --- a/micrometer-core/src/main/java/io/micrometer/core/instrument/binder/httpcomponents/hc5/MicrometerHttpRequestExecutor.java +++ b/micrometer-core/src/main/java/io/micrometer/core/instrument/binder/httpcomponents/hc5/MicrometerHttpRequestExecutor.java @@ -58,6 +58,8 @@ public class MicrometerHttpRequestExecutor extends HttpRequestExecutor { private final MeterRegistry registry; + private final String meterName; + private final ObservationRegistry observationRegistry; @Nullable @@ -72,12 +74,13 @@ public class MicrometerHttpRequestExecutor extends HttpRequestExecutor { /** * Use {@link #builder(MeterRegistry)} to create an instance of this class. */ - private MicrometerHttpRequestExecutor(Timeout waitForContinue, MeterRegistry registry, + private MicrometerHttpRequestExecutor(Timeout waitForContinue, MeterRegistry registry, String meterName, Function uriMapper, Iterable extraTags, boolean exportTagsForRoute, ObservationRegistry observationRegistry, @Nullable ApacheHttpClientObservationConvention convention) { super(waitForContinue, null, null); this.registry = Optional.ofNullable(registry) .orElseThrow(() -> new IllegalArgumentException("registry is required but has been initialized with null")); + this.meterName = meterName; this.uriMapper = Optional.ofNullable(uriMapper) .orElseThrow( () -> new IllegalArgumentException("uriMapper is required but has been initialized with null")); @@ -119,7 +122,7 @@ public ClassicHttpResponse execute(ClassicHttpRequest request, HttpClientConnect } finally { String status = statusCodeOrError; - sample.stop(METER_NAME, "Duration of Apache HttpClient request execution", + sample.stop(meterName, "Duration of Apache HttpClient request execution", () -> Tags .of("method", DefaultApacheHttpClientObservationConvention.INSTANCE.getMethodString(request), "uri", uriMapper.apply(request), "status", status) @@ -132,6 +135,8 @@ public static class Builder { private final MeterRegistry registry; + private String requestsMeterName = MicrometerHttpRequestExecutor.METER_NAME; + private ObservationRegistry observationRegistry = ObservationRegistry.NOOP; private Timeout waitForContinue = HttpRequestExecutor.DEFAULT_WAIT_FOR_CONTINUE; @@ -231,12 +236,24 @@ public Builder observationConvention(ApacheHttpClientObservationConvention conve return this; } + /** + * Provide a name to override the default meter name + * @param name Meter name to use when instrumentation is done with Timer. This + * does not have any effect when an + * {@link #observationRegistry(ObservationRegistry)} is configured + * @return This builder instance + */ + public Builder meterName(String name) { + this.requestsMeterName = name; + return this; + } + /** * @return Creates an instance of {@link MicrometerHttpRequestExecutor} with all * the configured properties. */ public MicrometerHttpRequestExecutor build() { - return new MicrometerHttpRequestExecutor(waitForContinue, registry, uriMapper, extraTags, + return new MicrometerHttpRequestExecutor(waitForContinue, registry, requestsMeterName, uriMapper, extraTags, exportTagsForRoute, observationRegistry, observationConvention); } diff --git a/micrometer-core/src/test/java/io/micrometer/core/instrument/binder/httpcomponents/MicrometerHttpClientInterceptorTest.java b/micrometer-core/src/test/java/io/micrometer/core/instrument/binder/httpcomponents/MicrometerHttpClientInterceptorTest.java index b6a35671ce..3ad27e1d92 100644 --- a/micrometer-core/src/test/java/io/micrometer/core/instrument/binder/httpcomponents/MicrometerHttpClientInterceptorTest.java +++ b/micrometer-core/src/test/java/io/micrometer/core/instrument/binder/httpcomponents/MicrometerHttpClientInterceptorTest.java @@ -90,6 +90,25 @@ void uriIsReadFromHttpHeader(@WiremockResolver.Wiremock WireMockServer server) t client.close(); } + @Test + void overridesDefaultMeterName(@WiremockResolver.Wiremock WireMockServer server) throws Exception { + String meterName = "http.client.requests"; + server.stubFor(any(anyUrl())); + MicrometerHttpClientInterceptor interceptor = new MicrometerHttpClientInterceptor(registry, Tags.empty(), true, + meterName); + CloseableHttpAsyncClient client = asyncClient(interceptor); + client.start(); + HttpGet request = new HttpGet(server.baseUrl()); + + Future future = client.execute(request, null); + HttpResponse response = future.get(); + + assertThat(response.getStatusLine().getStatusCode()).isEqualTo(200); + assertThat(registry.get(meterName).timer().count()).isEqualTo(1); + + client.close(); + } + private CloseableHttpAsyncClient asyncClient() { MicrometerHttpClientInterceptor interceptor = new MicrometerHttpClientInterceptor(registry, request -> request.getRequestLine().getUri(), Tags.empty(), true); diff --git a/micrometer-core/src/test/java/io/micrometer/core/instrument/binder/httpcomponents/MicrometerHttpRequestExecutorTest.java b/micrometer-core/src/test/java/io/micrometer/core/instrument/binder/httpcomponents/MicrometerHttpRequestExecutorTest.java index f78b03c08e..fd7a9b26ce 100644 --- a/micrometer-core/src/test/java/io/micrometer/core/instrument/binder/httpcomponents/MicrometerHttpRequestExecutorTest.java +++ b/micrometer-core/src/test/java/io/micrometer/core/instrument/binder/httpcomponents/MicrometerHttpRequestExecutorTest.java @@ -173,6 +173,17 @@ void waitForContinueGetsPassedToSuper() { assertThat(requestExecutor).hasFieldOrPropertyWithValue("waitForContinue", 1000); } + @Test + void overridesDefaultMeterName(@WiremockResolver.Wiremock WireMockServer server) throws IOException { + String meterName = "http.client.requests"; + MicrometerHttpRequestExecutor executor = MicrometerHttpRequestExecutor.builder(registry) + .meterName(meterName) + .build(); + HttpClient client = client(executor); + EntityUtils.consume(client.execute(new HttpGet(server.baseUrl())).getEntity()); + assertThat(registry.get(meterName).timer().count()).isEqualTo(1L); + } + @ParameterizedTest @ValueSource(booleans = { false, true }) void uriMapperWorksAsExpected(boolean configureObservationRegistry, diff --git a/micrometer-core/src/test/java/io/micrometer/core/instrument/binder/httpcomponents/hc5/MicrometerHttpClientInterceptorTest.java b/micrometer-core/src/test/java/io/micrometer/core/instrument/binder/httpcomponents/hc5/MicrometerHttpClientInterceptorTest.java index 3465e09b2a..ac85ba9434 100644 --- a/micrometer-core/src/test/java/io/micrometer/core/instrument/binder/httpcomponents/hc5/MicrometerHttpClientInterceptorTest.java +++ b/micrometer-core/src/test/java/io/micrometer/core/instrument/binder/httpcomponents/hc5/MicrometerHttpClientInterceptorTest.java @@ -93,6 +93,27 @@ void uriIsReadFromHttpHeader(@WiremockResolver.Wiremock WireMockServer server) t client.close(); } + @Test + void overridesDefaultMeterName(@WiremockResolver.Wiremock WireMockServer server) throws Exception { + String meterName = "http.client.requests"; + server.stubFor(any(anyUrl())); + MicrometerHttpClientInterceptor interceptor = new MicrometerHttpClientInterceptor(registry, Tags.empty(), true, + meterName); + CloseableHttpAsyncClient client = asyncClient(interceptor); + client.start(); + SimpleHttpRequest request = SimpleRequestBuilder.get(server.baseUrl()).build(); + request.addHeader(DefaultUriMapper.URI_PATTERN_HEADER, "/some/pattern"); + + Future future = client.execute(request, null); + HttpResponse response = future.get(); + + assertThat(response.getCode()).isEqualTo(200); + assertThat(registry.get(meterName).tag("uri", "/some/pattern").tag("status", "200").timer().count()) + .isEqualTo(1); + + client.close(); + } + private CloseableHttpAsyncClient asyncClient() { MicrometerHttpClientInterceptor interceptor = new MicrometerHttpClientInterceptor(registry, HttpRequest::getRequestUri, Tags.empty(), true); diff --git a/micrometer-core/src/test/java/io/micrometer/core/instrument/binder/httpcomponents/hc5/MicrometerHttpRequestExecutorTest.java b/micrometer-core/src/test/java/io/micrometer/core/instrument/binder/httpcomponents/hc5/MicrometerHttpRequestExecutorTest.java index 5768cc7bc0..3b8019a4b0 100644 --- a/micrometer-core/src/test/java/io/micrometer/core/instrument/binder/httpcomponents/hc5/MicrometerHttpRequestExecutorTest.java +++ b/micrometer-core/src/test/java/io/micrometer/core/instrument/binder/httpcomponents/hc5/MicrometerHttpRequestExecutorTest.java @@ -184,6 +184,17 @@ void waitForContinueGetsPassedToSuper() { assertThat(requestExecutor).hasFieldOrPropertyWithValue("waitForContinue", Timeout.ofMilliseconds(1000)); } + @Test + void overridesDefaultMeterName(@WiremockResolver.Wiremock WireMockServer server) throws IOException { + String meterName = "http.client.requests"; + MicrometerHttpRequestExecutor executor = MicrometerHttpRequestExecutor.builder(registry) + .meterName(meterName) + .build(); + CloseableHttpClient client = client(executor); + execute(client, new HttpGet(server.baseUrl() + "/foo")); + assertThat(registry.get(meterName).timer().count()).isEqualTo(1L); + } + @ParameterizedTest @ValueSource(booleans = { false, true }) void uriMapperWorksAsExpected(boolean configureObservationRegistry,