Skip to content

Commit

Permalink
Add the KPI metrics handler (with no qualifying path) exactly once to…
Browse files Browse the repository at this point in the history
… each routing (#3255) (#3282)
  • Loading branch information
tjquinno authored Aug 17, 2021
1 parent 0cbcd1c commit 2f4693d
Show file tree
Hide file tree
Showing 4 changed files with 140 additions and 7 deletions.
4 changes: 4 additions & 0 deletions microprofile/metrics/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,8 @@
<configuration>
<excludes>
<exclude>**/HelloWorldAsyncResponseTest.java</exclude>
<exclude>**/TestExtendedKPIMetrics.java</exclude>
<exclude>**/TestMetricsOnOwnSocket.java</exclude>
</excludes>
</configuration>
</execution>
Expand All @@ -106,6 +108,8 @@
<configuration>
<includes>
<include>**/HelloWorldAsyncResponseTest.java</include>
<include>**/TestExtendedKPIMetrics.java</include>
<include>**/TestMetricsOnOwnSocket.java</include>
</includes>
</configuration>
</execution>
Expand Down
Original file line number Diff line number Diff line change
@@ -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<Class<?>> getClasses() {
return Set.of(HelloWorldResource.class);
}
}
Original file line number Diff line number Diff line change
@@ -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<Response> response1Future = webTarget
.path("helloworld")
.request()
.accept(MediaType.TEXT_PLAIN)
.buildGet()
.submit();

Future<Response> 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)));
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -103,6 +104,8 @@ public class ServerCdiExtension implements Extension {
private final Map<Bean<?>, RoutingConfiguration> serviceBeans
= Collections.synchronizedMap(new IdentityHashMap<>());

private final Set<Routing.Builder> 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
Expand Down Expand Up @@ -146,19 +149,16 @@ private void recordBeanServices(@Observes ProcessManagedBean<? extends Service>

private void registerKpiMetricsDeferrableRequestContextSetterHandler(JaxRsCdiExtension jaxRs,
JaxRsApplication applicationMeta) {
Optional<String> contextRoot = jaxRs.findContextRoot(config, applicationMeta);
Optional<String> 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("<unnamed>")));
}
}

Expand Down

0 comments on commit 2f4693d

Please sign in to comment.