diff --git a/microprofile-telemetry-metrics/pom.xml b/microprofile-telemetry-metrics/pom.xml index 7bcde4e0..9bd6c8c2 100644 --- a/microprofile-telemetry-metrics/pom.xml +++ b/microprofile-telemetry-metrics/pom.xml @@ -10,7 +10,7 @@ 1.0.0.Final-SNAPSHOT - microprofile-metrics + microprofile-telemetry-metrics @@ -33,11 +33,57 @@ wildfly-arquillian-container-managed test + + jakarta.annotation + jakarta.annotation-api + provided + + + jakarta.enterprise + jakarta.enterprise.cdi-api + provided + + + jakarta.inject + jakarta.inject-api + provided + + + jakarta.json + jakarta.json-api + provided + jakarta.servlet jakarta.servlet-api provided + + org.eclipse.microprofile.config + microprofile-config-api + provided + + + org.eclipse.microprofile.telemetry + microprofile-telemetry-api + pom + provided + + + jakarta.ws.rs + jakarta.ws.rs-api + provided + + + org.jboss.eap.qe + tooling-docker + test + + + org.jboss.eap.qe + tooling-observability + test + org.jboss.eap.qe tooling-server-configuration diff --git a/microprofile-telemetry-metrics/src/main/java/org/jboss/eap/qe/microprofile/metrics/namefellow/PingOneService.java b/microprofile-telemetry-metrics/src/main/java/org/jboss/eap/qe/microprofile/metrics/namefellow/PingOneService.java index c79716cf..bc4f43cf 100644 --- a/microprofile-telemetry-metrics/src/main/java/org/jboss/eap/qe/microprofile/metrics/namefellow/PingOneService.java +++ b/microprofile-telemetry-metrics/src/main/java/org/jboss/eap/qe/microprofile/metrics/namefellow/PingOneService.java @@ -1,17 +1,34 @@ package org.jboss.eap.qe.microprofile.metrics.namefellow; +import jakarta.annotation.PostConstruct; import jakarta.enterprise.context.ApplicationScoped; +import jakarta.inject.Inject; -import org.eclipse.microprofile.metrics.annotation.Counted; +import io.opentelemetry.api.common.AttributeKey; +import io.opentelemetry.api.common.Attributes; +import io.opentelemetry.api.metrics.LongCounter; +import io.opentelemetry.api.metrics.Meter; @ApplicationScoped public class PingOneService { public static final String MESSAGE = "pong one"; public static final String PING_ONE_SERVICE_TAG = "ping-one-service-tag"; - @Counted(name = "ping-count", absolute = true, displayName = "Pong Count", description = "Number of ping invocations", tags = "_app=" - + PING_ONE_SERVICE_TAG) + @Inject + private Meter meter; + private LongCounter longCounter; + + @PostConstruct + public void init() { + longCounter = meter + .counterBuilder("ping_count") + .setDescription("Number of ping invocations") + .build(); + } + public String ping() { + longCounter.add(1, Attributes.of( + AttributeKey.stringKey("_app"), PING_ONE_SERVICE_TAG)); return MESSAGE; } } diff --git a/microprofile-telemetry-metrics/src/main/java/org/jboss/eap/qe/microprofile/metrics/namefellow/PingTwoService.java b/microprofile-telemetry-metrics/src/main/java/org/jboss/eap/qe/microprofile/metrics/namefellow/PingTwoService.java index 5d0ad901..00db4d6c 100644 --- a/microprofile-telemetry-metrics/src/main/java/org/jboss/eap/qe/microprofile/metrics/namefellow/PingTwoService.java +++ b/microprofile-telemetry-metrics/src/main/java/org/jboss/eap/qe/microprofile/metrics/namefellow/PingTwoService.java @@ -1,17 +1,34 @@ package org.jboss.eap.qe.microprofile.metrics.namefellow; +import jakarta.annotation.PostConstruct; import jakarta.enterprise.context.ApplicationScoped; +import jakarta.inject.Inject; -import org.eclipse.microprofile.metrics.annotation.Counted; +import io.opentelemetry.api.common.AttributeKey; +import io.opentelemetry.api.common.Attributes; +import io.opentelemetry.api.metrics.LongCounter; +import io.opentelemetry.api.metrics.Meter; @ApplicationScoped public class PingTwoService { public static final String MESSAGE = "pong two"; public static final String PING_TWO_SERVICE_TAG = "ping-two-service-tag"; - @Counted(name = "ping-count", absolute = true, displayName = "Pong Count", description = "Number of ping invocations", tags = "_app=" - + PING_TWO_SERVICE_TAG) + @Inject + private Meter meter; + private LongCounter longCounter; + + @PostConstruct + public void init() { + longCounter = meter + .counterBuilder("ping_count") + .setDescription("Number of ping invocations") + .build(); + } + public String ping() { + longCounter.add(1, Attributes.of( + AttributeKey.stringKey("_app"), PING_TWO_SERVICE_TAG)); return MESSAGE; } } diff --git a/microprofile-telemetry-metrics/src/test/java/org/jboss/eap/qe/microprofile/metrics/MPTelemetryServerSetupTask.java b/microprofile-telemetry-metrics/src/test/java/org/jboss/eap/qe/microprofile/metrics/MPTelemetryServerSetupTask.java new file mode 100644 index 00000000..689f9801 --- /dev/null +++ b/microprofile-telemetry-metrics/src/test/java/org/jboss/eap/qe/microprofile/metrics/MPTelemetryServerSetupTask.java @@ -0,0 +1,50 @@ +package org.jboss.eap.qe.microprofile.metrics; + +import org.jboss.as.arquillian.api.ServerSetupTask; +import org.jboss.as.arquillian.container.ManagementClient; +import org.jboss.eap.qe.microprofile.common.setuptasks.MicroProfileTelemetryServerConfiguration; +import org.jboss.eap.qe.microprofile.common.setuptasks.MicrometerServerConfiguration; +import org.jboss.eap.qe.observability.containers.OpenTelemetryCollectorContainer; +import org.jboss.eap.qe.ts.common.docker.Docker; + +/** + * Server setup task for configuration of MicroProfile Telemetry and otel collector + */ +public class MPTelemetryServerSetupTask implements ServerSetupTask { + + private static OpenTelemetryCollectorContainer otelCollector; + + /** + * Start otel collector in container and configure OpenTelemetry and MP Telemetry in application server + */ + @Override + public void setup(ManagementClient managementClient, String containerId) throws Exception { + // we need a Docker container for The OTel collector here, so throw an exception if a docker service is not available + try { + Docker.checkDockerPresent(); + } catch (Exception e) { + throw new IllegalStateException("Cannot verify Docker availability: " + e.getMessage()); + } + // disable micrometer + MicrometerServerConfiguration.disableMicrometer(); + // start the OTel collector container + otelCollector = OpenTelemetryCollectorContainer.getInstance(); + otelCollector.start(); + // Enable MP Telemetry based metrics, which rely on OpenTelemetry subsystem + MicroProfileTelemetryServerConfiguration.enableOpenTelemetry(); + MicroProfileTelemetryServerConfiguration.addOpenTelemetryCollectorConfiguration(otelCollector.getOtlpGrpcEndpoint()); + MicroProfileTelemetryServerConfiguration.enableMicroProfileTelemetry(); + } + + /** + * Stop otel collector in container and disable OpenTelemetry and MP Telemetry in application server + */ + @Override + public void tearDown(ManagementClient managementClient, String containerId) throws Exception { + // disable MP Telemetry based metrics + MicroProfileTelemetryServerConfiguration.disableMicroProfileTelemetry(); + MicroProfileTelemetryServerConfiguration.disableOpenTelemetry(); + // stop the OTel collector container + otelCollector.stop(); + } +} diff --git a/microprofile-telemetry-metrics/src/test/java/org/jboss/eap/qe/microprofile/metrics/namefellow/MultipleDeploymentsMetricsTest.java b/microprofile-telemetry-metrics/src/test/java/org/jboss/eap/qe/microprofile/metrics/namefellow/MultipleDeploymentsMetricsTest.java index 82444104..714743e7 100644 --- a/microprofile-telemetry-metrics/src/test/java/org/jboss/eap/qe/microprofile/metrics/namefellow/MultipleDeploymentsMetricsTest.java +++ b/microprofile-telemetry-metrics/src/test/java/org/jboss/eap/qe/microprofile/metrics/namefellow/MultipleDeploymentsMetricsTest.java @@ -1,152 +1,110 @@ package org.jboss.eap.qe.microprofile.metrics.namefellow; import static io.restassured.RestAssured.get; -import static io.restassured.RestAssured.given; -import static org.hamcrest.Matchers.contains; -import static org.hamcrest.Matchers.containsString; import static org.hamcrest.Matchers.equalTo; -import static org.hamcrest.Matchers.hasKey; -import static org.hamcrest.Matchers.hasSize; import java.net.URL; +import java.util.List; import org.jboss.arquillian.container.test.api.Deployment; import org.jboss.arquillian.container.test.api.OperateOnDeployment; import org.jboss.arquillian.container.test.api.RunAsClient; import org.jboss.arquillian.junit.Arquillian; import org.jboss.arquillian.test.api.ArquillianResource; -import org.jboss.eap.qe.microprofile.tooling.server.configuration.ConfigurationException; -import org.jboss.eap.qe.microprofile.tooling.server.configuration.arquillian.ArquillianContainerProperties; -import org.jboss.eap.qe.microprofile.tooling.server.configuration.arquillian.ArquillianDescriptorWrapper; +import org.jboss.as.arquillian.api.ServerSetup; +import org.jboss.eap.qe.microprofile.metrics.MPTelemetryServerSetupTask; +import org.jboss.eap.qe.microprofile.tooling.server.configuration.deployment.ConfigurationUtil; +import org.jboss.eap.qe.observability.containers.OpenTelemetryCollectorContainer; +import org.jboss.eap.qe.observability.prometheus.model.PrometheusMetric; +import org.jboss.eap.qe.ts.common.docker.junit.DockerRequiredTests; import org.jboss.shrinkwrap.api.ShrinkWrap; +import org.jboss.shrinkwrap.api.asset.StringAsset; import org.jboss.shrinkwrap.api.spec.WebArchive; -import org.junit.BeforeClass; +import org.junit.Assert; import org.junit.Test; +import org.junit.experimental.categories.Category; import org.junit.runner.RunWith; -import io.restassured.http.ContentType; -import io.restassured.specification.RequestSpecification; - /** * Multiple deployment scenario. */ @RunWith(Arquillian.class) +@Category(DockerRequiredTests.class) +@ServerSetup(MPTelemetryServerSetupTask.class) public class MultipleDeploymentsMetricsTest { public static final String PING_ONE_SERVICE = "ping-one-service"; public static final String PING_TWO_SERVICE = "ping-two-service"; + private static final String DEFAULT_MP_CONFIG = "otel.sdk.disabled=false\n" + + "otel.metric.export.interval=100"; @Deployment(name = PING_ONE_SERVICE, order = 1) public static WebArchive createDeployment1() { + String mpConfig = "otel.service.name=MultipleDeploymentsMetricsTest-first-deployment\n" + DEFAULT_MP_CONFIG; return ShrinkWrap.create(WebArchive.class, PING_ONE_SERVICE + ".war") - .addClasses(PingApplication.class, PingOneService.class, PingOneResource.class); + .addClasses(PingApplication.class, PingOneService.class, PingOneResource.class) + .addAsManifestResource(ConfigurationUtil.BEANS_XML_FILE_LOCATION, "beans.xml") + .addAsManifestResource(new StringAsset(mpConfig), "microprofile-config.properties"); } @Deployment(name = PING_TWO_SERVICE, order = 2) public static WebArchive createDeployment2() { + String mpConfig = "otel.service.name=MultipleDeploymentsMetricsTest-second-deployment\n" + DEFAULT_MP_CONFIG; return ShrinkWrap.create(WebArchive.class, PING_TWO_SERVICE + ".war") - .addClasses(PingApplication.class, PingTwoService.class, PingTwoResource.class); - } - - private static RequestSpecification jsonMetricsRequest; - private static RequestSpecification textMetricsRequest; - - @BeforeClass - public static void prepare() throws ConfigurationException { - ArquillianContainerProperties arqProps = new ArquillianContainerProperties( - ArquillianDescriptorWrapper.getArquillianDescriptor()); - String url = "http://" + arqProps.getDefaultManagementAddress() + ":" + arqProps.getDefaultManagementPort() - + "/metrics"; - jsonMetricsRequest = given() - .baseUri(url) - .accept(ContentType.JSON); - textMetricsRequest = given() - .baseUri(url) - .accept(ContentType.TEXT); - } - - /** - * @tpTestDetails High level scenario to verify two none-reusable counter metrics of the same name are registered - * and tagged properly. The information is available under {@code /metrics} endpoint via HTTP OPTIONS. - * Metrics are in separate archives - multiple-deployment. - * @tpPassCrit Metrics are tagged properly - * @tpSince EAP 7.4.0.CD19 - */ - @Test - @RunAsClient - public void applicationMetricsAreRegisteredAtDeploymentTime() { - jsonMetricsRequest.options().then() - .contentType(ContentType.JSON) - .header("Content-Type", containsString("application/json")) - .body("$", hasKey("application"), - "application", hasKey("ping-count"), - "application.ping-count", hasKey("tags"), // 11 at `/` + 1 at `/another-hello` - "application.ping-count.tags", hasSize(2), - "application.ping-count.tags[0]", hasSize(1), - "application.ping-count.tags[1]", hasSize(1), - "application.ping-count.tags.flatten()", - contains("_app=" + PingOneService.PING_ONE_SERVICE_TAG, "_app=" + PingTwoService.PING_TWO_SERVICE_TAG)); + .addClasses(PingApplication.class, PingTwoService.class, PingTwoResource.class) + .addAsManifestResource(ConfigurationUtil.BEANS_XML_FILE_LOCATION, "beans.xml") + .addAsManifestResource(new StringAsset(mpConfig), "microprofile-config.properties"); } /** * @tpTestDetails High level scenario to verify two none-reusable counter metrics of the same name are incremented * properly according to the number of a CDI beans invocation. * Metrics are in separate archives - multiple-deployment. - * @tpPassCrit Counters have correct values (according to number of the CDI bean invocations) in JSON and prometheus format. + * @tpPassCrit Counters have correct values (according to number of the CDI bean invocations) in Jprometheus format. * @tpSince EAP 7.4.0.CD19 */ @Test @RunAsClient public void dataTest(@ArquillianResource @OperateOnDeployment(PING_ONE_SERVICE) URL pingOneUrl, - @ArquillianResource @OperateOnDeployment(PING_TWO_SERVICE) URL pingTwoUrl) { - + @ArquillianResource @OperateOnDeployment(PING_TWO_SERVICE) URL pingTwoUrl) throws Exception { + // increase metrics counters get(pingOneUrl.toString() + PingOneResource.RESOURCE) .then() .statusCode(200) .body(equalTo(PingOneService.MESSAGE)); - get(pingTwoUrl.toString() + PingTwoResource.RESOURCE) .then() .statusCode(200) .body(equalTo(PingTwoService.MESSAGE)); - - get(pingTwoUrl.toString() + PingTwoResource.RESOURCE).then().statusCode(200); - get(pingTwoUrl.toString() + PingTwoResource.RESOURCE).then().statusCode(200); - get(pingTwoUrl.toString() + PingTwoResource.RESOURCE).then().statusCode(200); - - get(pingOneUrl.toString() + PingOneResource.RESOURCE).then().statusCode(200); - - jsonDataTest(); - prometheusDataTest(); - } - - /** - * Verify correct data of counters in JSON format. ping one: 2, ping-two: 4 - */ - private void jsonDataTest() { - jsonMetricsRequest.get().then() - .contentType(ContentType.JSON) - .header("Content-Type", containsString("application/json")) - .body("$", hasKey("application"), - "application", hasKey("ping-count;_app=" + PingOneService.PING_ONE_SERVICE_TAG), - "application.ping-count;_app=" + PingOneService.PING_ONE_SERVICE_TAG, equalTo(2), - - "application", hasKey("ping-count;_app=" + PingTwoService.PING_TWO_SERVICE_TAG), - "application.ping-count;_app=" + PingTwoService.PING_TWO_SERVICE_TAG, equalTo(4)); - } - - /** - * Verify correct data of counters in prometheus format. ping one: 2, ping-two: 4 - */ - private void prometheusDataTest() { - textMetricsRequest.get().then() - .contentType(ContentType.TEXT) - .header("Content-Type", containsString("text/plain")) - .body( - containsString( - "application_ping_count_total{_app=\"" + PingTwoService.PING_TWO_SERVICE_TAG + "\"} 4.0"), - containsString( - "application_ping_count_total{_app=\"" + PingOneService.PING_ONE_SERVICE_TAG + "\"} 2.0")); + get(pingTwoUrl + PingTwoResource.RESOURCE).then().statusCode(200); + get(pingTwoUrl + PingTwoResource.RESOURCE).then().statusCode(200); + get(pingTwoUrl + PingTwoResource.RESOURCE).then().statusCode(200); + get(pingOneUrl + PingOneResource.RESOURCE).then().statusCode(200); + + // give it some time to actually be able and report some metrics via the Pmetheus URL + Thread.sleep(1_000); + + // get metrics + List metrics = OpenTelemetryCollectorContainer.getInstance().fetchMetrics(""); + + // verify metrics + Assert.assertTrue("\"ping_count\" metric for deployment one not found or not expected", + metrics.stream() + .filter(m -> "ping_count_total".equals(m.getKey())) + .filter(m -> m.getTags().entrySet().stream().anyMatch( + t -> "key_app".equals(t.getKey()) + && "ping-one-service-tag" + .equals(t.getValue()))) + .anyMatch(m -> "2".equals(m.getValue()))); + + Assert.assertTrue("\"ping_count\" metric for deployment two not found or not expected", + metrics.stream() + .filter(m -> "ping_count_total".equals(m.getKey())) + .filter(m -> m.getTags().entrySet().stream().anyMatch( + t -> "key_app".equals(t.getKey()) + && "ping-two-service-tag" + .equals(t.getValue()))) + .anyMatch(m -> "4".equals(m.getValue()))); } } diff --git a/microprofile-telemetry-metrics/src/test/resources/arquillian-bootable.xml b/microprofile-telemetry-metrics/src/test/resources/arquillian-bootable.xml index c7de7366..870292ea 100644 --- a/microprofile-telemetry-metrics/src/test/resources/arquillian-bootable.xml +++ b/microprofile-telemetry-metrics/src/test/resources/arquillian-bootable.xml @@ -17,4 +17,4 @@ - \ No newline at end of file + diff --git a/microprofile-fault-tolerance/src/test/resources/otel-collector-config.yaml b/tooling-observability/src/main/resources/otel-collector-config.yaml similarity index 100% rename from microprofile-fault-tolerance/src/test/resources/otel-collector-config.yaml rename to tooling-observability/src/main/resources/otel-collector-config.yaml