diff --git a/microprofile/metrics/pom.xml b/microprofile/metrics/pom.xml index 35b8f731ad7..49ea791c52c 100644 --- a/microprofile/metrics/pom.xml +++ b/microprofile/metrics/pom.xml @@ -94,6 +94,8 @@ **/HelloWorldAsyncResponseTest.java + **/TestExtendedKPIMetrics.java + **/TestMetricsOnOwnSocket.java @@ -106,6 +108,8 @@ **/HelloWorldAsyncResponseTest.java + **/TestExtendedKPIMetrics.java + **/TestMetricsOnOwnSocket.java diff --git a/microprofile/metrics/src/test/java/io/helidon/microprofile/metrics/HelloWorldApp.java b/microprofile/metrics/src/test/java/io/helidon/microprofile/metrics/HelloWorldApp.java new file mode 100644 index 00000000000..e289f4b4894 --- /dev/null +++ b/microprofile/metrics/src/test/java/io/helidon/microprofile/metrics/HelloWorldApp.java @@ -0,0 +1,31 @@ +/* + * Copyright (c) 2021 Oracle and/or its affiliates. + * + * 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 + * + * http://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.helidon.microprofile.metrics; + +import javax.enterprise.context.ApplicationScoped; +import javax.ws.rs.ApplicationPath; +import javax.ws.rs.core.Application; +import java.util.Set; + +@ApplicationScoped +@ApplicationPath("/") +public class HelloWorldApp extends Application { + + @Override + public Set> getClasses() { + return Set.of(HelloWorldResource.class); + } +} diff --git a/microprofile/metrics/src/test/java/io/helidon/microprofile/metrics/TestExtendedKPIMetrics.java b/microprofile/metrics/src/test/java/io/helidon/microprofile/metrics/TestExtendedKPIMetrics.java new file mode 100644 index 00000000000..b0ed7e6ba6d --- /dev/null +++ b/microprofile/metrics/src/test/java/io/helidon/microprofile/metrics/TestExtendedKPIMetrics.java @@ -0,0 +1,98 @@ +/* + * Copyright (c) 2021 Oracle and/or its affiliates. + * + * 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 + * + * http://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.helidon.microprofile.metrics; + +import io.helidon.common.http.Http; +import io.helidon.microprofile.tests.junit5.AddBean; +import io.helidon.microprofile.tests.junit5.AddConfig; +import io.helidon.microprofile.tests.junit5.HelidonTest; +import org.junit.jupiter.api.Test; + +import javax.inject.Inject; +import javax.json.JsonObject; +import javax.ws.rs.client.WebTarget; +import javax.ws.rs.core.MediaType; +import javax.ws.rs.core.Response; + +import java.util.concurrent.ExecutionException; +import java.util.concurrent.Future; + +import static io.helidon.metrics.KeyPerformanceIndicatorMetricsSettings.Builder.KEY_PERFORMANCE_INDICATORS_CONFIG_KEY; +import static io.helidon.metrics.KeyPerformanceIndicatorMetricsSettings.Builder.KEY_PERFORMANCE_INDICATORS_EXTENDED_CONFIG_KEY; +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.greaterThan; +import static org.hamcrest.Matchers.is; +import static org.hamcrest.Matchers.notNullValue; + +@HelidonTest +@AddConfig(key = + "metrics." + + KEY_PERFORMANCE_INDICATORS_CONFIG_KEY + + "." + KEY_PERFORMANCE_INDICATORS_EXTENDED_CONFIG_KEY, + value = "true") +@AddConfig(key = "server.executor-service.core-pool-size", value = "1") +@AddConfig(key = "server.executor-service.max-pool-size", value = "1") +@AddBean(HelloWorldApp.class) +public class TestExtendedKPIMetrics { + + @Inject + WebTarget webTarget; + + + @Test + void checkNonZeroDeferredTime() throws ExecutionException, InterruptedException { + // Run two requests concurrently, with the server configured for one thread, to force one request to be deferred. + Future response1Future = webTarget + .path("helloworld") + .request() + .accept(MediaType.TEXT_PLAIN) + .buildGet() + .submit(); + + Future response2Future = webTarget + .path("helloworld") + .request() + .accept(MediaType.TEXT_PLAIN) + .buildGet() + .submit(); + + // Now wait for both requests to finish. + try (Response response1 = response1Future.get(); Response response2 = response2Future.get()) { + assertThat("Access to GET response 1", response1.getStatus(), is(Http.Status.OK_200.code())); + assertThat("Access to GET response 2", response2.getStatus(), is(Http.Status.OK_200.code())); + } + + try (Response metricsResponse = webTarget + .path("metrics/vendor") + .request() + .accept(MediaType.APPLICATION_JSON_TYPE) + .get()) { + + assertThat("Metrics /metrics/vendor URL HTTP status", metricsResponse.getStatus(), is(Http.Status.OK_200.code())); + + JsonObject vendorMetrics = metricsResponse.readEntity(JsonObject.class); + + assertThat("Extended KPI metric requests.deferred present", vendorMetrics.containsKey("requests.deferred"), + is(true)); + + JsonObject requestsDeferred = vendorMetrics.getJsonObject("requests.deferred"); + assertThat("requests.deferred", requestsDeferred, is(notNullValue())); + + int deferredCount = requestsDeferred.getInt("count"); + assertThat("Extended KPI metric requests.deferred->count value", deferredCount, is(greaterThan(0))); + } + } +} diff --git a/microprofile/server/src/main/java/io/helidon/microprofile/server/ServerCdiExtension.java b/microprofile/server/src/main/java/io/helidon/microprofile/server/ServerCdiExtension.java index 23a4eb490a7..1c5782f0d0b 100644 --- a/microprofile/server/src/main/java/io/helidon/microprofile/server/ServerCdiExtension.java +++ b/microprofile/server/src/main/java/io/helidon/microprofile/server/ServerCdiExtension.java @@ -23,6 +23,7 @@ import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; +import java.util.HashSet; import java.util.IdentityHashMap; import java.util.LinkedList; import java.util.List; @@ -103,6 +104,8 @@ public class ServerCdiExtension implements Extension { private final Map, RoutingConfiguration> serviceBeans = Collections.synchronizedMap(new IdentityHashMap<>()); + private final Set routingsWithKPIMetrics = new HashSet<>(); + private void buildTime(@Observes @BuildTimeStart Object event) { // update the status of server, as we may have been started without a builder being used // such as when cdi.Main or SeContainerInitializer are used @@ -146,19 +149,16 @@ private void recordBeanServices(@Observes ProcessManagedBean private void registerKpiMetricsDeferrableRequestContextSetterHandler(JaxRsCdiExtension jaxRs, JaxRsApplication applicationMeta) { - Optional contextRoot = jaxRs.findContextRoot(config, applicationMeta); Optional namedRouting = jaxRs.findNamedRouting(config, applicationMeta); boolean routingNameRequired = jaxRs.isNamedRoutingRequired(config, applicationMeta); Routing.Builder routing = routingBuilder(namedRouting, routingNameRequired, applicationMeta.appName()); - if (contextRoot.isPresent()) { - String contextRootString = contextRoot.get(); - routing.any(contextRootString, KeyPerformanceIndicatorSupport.DeferrableRequestContext.CONTEXT_SETTING_HANDLER); - } else { - LOGGER.finer(() -> - "JAX-RS application " + applicationMeta.appName() + " adding deferrable request KPI metrics context on '/'"); + if (!routingsWithKPIMetrics.contains(routing)) { + routingsWithKPIMetrics.add(routing); routing.any(KeyPerformanceIndicatorSupport.DeferrableRequestContext.CONTEXT_SETTING_HANDLER); + LOGGER.finer(() -> String.format("Adding deferrable request KPI metrics context for routing with name '%s'", + namedRouting.orElse(""))); } }