diff --git a/microprofile-telemetry/pom.xml b/microprofile-telemetry/pom.xml new file mode 100644 index 00000000..9bd6c8c2 --- /dev/null +++ b/microprofile-telemetry/pom.xml @@ -0,0 +1,188 @@ + + + 4.0.0 + + + org.jboss.eap.qe + microprofile-test-suite + 1.0.0.Final-SNAPSHOT + + + microprofile-telemetry-metrics + + + + junit + junit + test + + + io.rest-assured + rest-assured + test + + + org.jboss.arquillian.junit + arquillian-junit-container + test + + + org.wildfly.arquillian + 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 + test + + + org.wildfly.arquillian + wildfly-arquillian-common + test + + + org.wildfly.core + wildfly-controller + + + + + + + bootablejar.profile + + + ts.bootable + + + + + + + org.wildfly.plugins + wildfly-jar-maven-plugin + + + + bootable-jar-packaging + + package + + process-test-resources + + test-microprofile-metrics-bootable.jar + true + false + ${galleon.log.time} + + ${galleon.fork.embedded} + + + + ${testsuite.galleon.pack.groupId} + ${testsuite.galleon.pack.artifactId} + ${testsuite.galleon.pack.version} + + + + cloud-server + undertow-https + + + + + + + org.apache.maven.plugins + maven-surefire-plugin + + + default-test + + test + + test + + + + ${project.build.directory}/jboss-as-bootable + ${project.build.directory}/test-microprofile-metrics-bootable.jar + arquillian-bootable.xml + ${jboss.modules.path} + + + + org.wildfly.arquillian:wildfly-arquillian-container-managed + + + + + org/jboss/eap/qe/microprofile/metrics/integration/ft/*Test.java + + org/jboss/eap/qe/microprofile/metrics/integration/config/CustomMetricCustomConfigSourceProviderTest + org/jboss/eap/qe/microprofile/metrics/integration/config/CustomMetricCustomConfigSourceTest + + + + + + + + + + diff --git a/microprofile-telemetry/src/main/java/org/jboss/eap/qe/microprofile/telemetry/metrics/namefellow/PingApplication.java b/microprofile-telemetry/src/main/java/org/jboss/eap/qe/microprofile/telemetry/metrics/namefellow/PingApplication.java new file mode 100644 index 00000000..777c5e84 --- /dev/null +++ b/microprofile-telemetry/src/main/java/org/jboss/eap/qe/microprofile/telemetry/metrics/namefellow/PingApplication.java @@ -0,0 +1,8 @@ +package org.jboss.eap.qe.microprofile.telemetry.metrics.namefellow; + +import jakarta.ws.rs.ApplicationPath; +import jakarta.ws.rs.core.Application; + +@ApplicationPath("/") +public class PingApplication extends Application { +} diff --git a/microprofile-telemetry/src/main/java/org/jboss/eap/qe/microprofile/telemetry/metrics/namefellow/PingOneResource.java b/microprofile-telemetry/src/main/java/org/jboss/eap/qe/microprofile/telemetry/metrics/namefellow/PingOneResource.java new file mode 100644 index 00000000..5bdf780d --- /dev/null +++ b/microprofile-telemetry/src/main/java/org/jboss/eap/qe/microprofile/telemetry/metrics/namefellow/PingOneResource.java @@ -0,0 +1,21 @@ +package org.jboss.eap.qe.microprofile.telemetry.metrics.namefellow; + +import jakarta.inject.Inject; +import jakarta.ws.rs.GET; +import jakarta.ws.rs.Path; +import jakarta.ws.rs.Produces; +import jakarta.ws.rs.core.MediaType; + +@Path("/" + PingOneResource.RESOURCE) +public class PingOneResource { + public static final String RESOURCE = "ping-one"; + + @Inject + private PingOneService ping; + + @GET + @Produces(MediaType.TEXT_PLAIN) + public String doGet() { + return ping.ping(); + } +} diff --git a/microprofile-telemetry/src/main/java/org/jboss/eap/qe/microprofile/telemetry/metrics/namefellow/PingOneService.java b/microprofile-telemetry/src/main/java/org/jboss/eap/qe/microprofile/telemetry/metrics/namefellow/PingOneService.java new file mode 100644 index 00000000..fc99197a --- /dev/null +++ b/microprofile-telemetry/src/main/java/org/jboss/eap/qe/microprofile/telemetry/metrics/namefellow/PingOneService.java @@ -0,0 +1,34 @@ +package org.jboss.eap.qe.microprofile.telemetry.metrics.namefellow; + +import jakarta.annotation.PostConstruct; +import jakarta.enterprise.context.ApplicationScoped; +import jakarta.inject.Inject; + +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"; + + @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/src/main/java/org/jboss/eap/qe/microprofile/telemetry/metrics/namefellow/PingTwoResource.java b/microprofile-telemetry/src/main/java/org/jboss/eap/qe/microprofile/telemetry/metrics/namefellow/PingTwoResource.java new file mode 100644 index 00000000..0d6a6ef0 --- /dev/null +++ b/microprofile-telemetry/src/main/java/org/jboss/eap/qe/microprofile/telemetry/metrics/namefellow/PingTwoResource.java @@ -0,0 +1,21 @@ +package org.jboss.eap.qe.microprofile.telemetry.metrics.namefellow; + +import jakarta.inject.Inject; +import jakarta.ws.rs.GET; +import jakarta.ws.rs.Path; +import jakarta.ws.rs.Produces; +import jakarta.ws.rs.core.MediaType; + +@Path("/" + PingTwoResource.RESOURCE) +public class PingTwoResource { + public static final String RESOURCE = "ping-one"; + + @Inject + private PingTwoService ping; + + @GET + @Produces(MediaType.TEXT_PLAIN) + public String doGet() { + return ping.ping(); + } +} diff --git a/microprofile-telemetry/src/main/java/org/jboss/eap/qe/microprofile/telemetry/metrics/namefellow/PingTwoService.java b/microprofile-telemetry/src/main/java/org/jboss/eap/qe/microprofile/telemetry/metrics/namefellow/PingTwoService.java new file mode 100644 index 00000000..750c4cf6 --- /dev/null +++ b/microprofile-telemetry/src/main/java/org/jboss/eap/qe/microprofile/telemetry/metrics/namefellow/PingTwoService.java @@ -0,0 +1,34 @@ +package org.jboss.eap.qe.microprofile.telemetry.metrics.namefellow; + +import jakarta.annotation.PostConstruct; +import jakarta.enterprise.context.ApplicationScoped; +import jakarta.inject.Inject; + +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"; + + @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/src/test/java/org/jboss/eap/qe/microprofile/telemetry/metrics/MPTelemetryServerSetupTask.java b/microprofile-telemetry/src/test/java/org/jboss/eap/qe/microprofile/telemetry/metrics/MPTelemetryServerSetupTask.java new file mode 100644 index 00000000..f3cdb427 --- /dev/null +++ b/microprofile-telemetry/src/test/java/org/jboss/eap/qe/microprofile/telemetry/metrics/MPTelemetryServerSetupTask.java @@ -0,0 +1,50 @@ +package org.jboss.eap.qe.microprofile.telemetry.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/src/test/java/org/jboss/eap/qe/microprofile/telemetry/metrics/namefellow/MultipleDeploymentsMetricsTest.java b/microprofile-telemetry/src/test/java/org/jboss/eap/qe/microprofile/telemetry/metrics/namefellow/MultipleDeploymentsMetricsTest.java new file mode 100644 index 00000000..cb0bfbca --- /dev/null +++ b/microprofile-telemetry/src/test/java/org/jboss/eap/qe/microprofile/telemetry/metrics/namefellow/MultipleDeploymentsMetricsTest.java @@ -0,0 +1,110 @@ +package org.jboss.eap.qe.microprofile.telemetry.metrics.namefellow; + +import static io.restassured.RestAssured.get; +import static org.hamcrest.Matchers.equalTo; + +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.as.arquillian.api.ServerSetup; +import org.jboss.eap.qe.microprofile.telemetry.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.Assert; +import org.junit.Test; +import org.junit.experimental.categories.Category; +import org.junit.runner.RunWith; + +/** + * 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) + .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) + .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 Prometheus 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) 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 + 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/src/test/resources/arquillian-bootable.xml b/microprofile-telemetry/src/test/resources/arquillian-bootable.xml new file mode 100644 index 00000000..870292ea --- /dev/null +++ b/microprofile-telemetry/src/test/resources/arquillian-bootable.xml @@ -0,0 +1,20 @@ + + + + + + + + + ${install.dir} + ${bootable.jar} + ${server.jvm.args} + ${jboss.args} + true + 127.0.0.1 + 9990 + + + + diff --git a/pom.xml b/pom.xml index 083d8e6f..47cc3df7 100644 --- a/pom.xml +++ b/pom.xml @@ -25,6 +25,7 @@ microprofile-fault-tolerance microprofile-jwt microprofile-lra + microprofile-telemetry microprofile-open-api micrometer tooling-observability diff --git a/tooling-observability/src/main/java/org/jboss/eap/qe/observability/containers/OpenTelemetryCollectorContainer.java b/tooling-observability/src/main/java/org/jboss/eap/qe/observability/containers/OpenTelemetryCollectorContainer.java index fbc67d04..12248fb5 100644 --- a/tooling-observability/src/main/java/org/jboss/eap/qe/observability/containers/OpenTelemetryCollectorContainer.java +++ b/tooling-observability/src/main/java/org/jboss/eap/qe/observability/containers/OpenTelemetryCollectorContainer.java @@ -172,7 +172,7 @@ public void start() { try { otelCollectorContainer.start(); } catch (Exception e) { - throw new IllegalStateException("Starting the OTel container failed: " + e); + throw new IllegalStateException("Starting the OTel container failed: " + e, e); } otlpGrpcEndpoint = "http://localhost:" + DOCKER_HOST_OTLP_GRPC_PORT; otlpHttpEndpoint = "http://localhost:" + DOCKER_HOST_OTLP_HTTP_PORT; 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 diff --git a/tooling-server-configuration/src/main/java/org/jboss/eap/qe/microprofile/common/setuptasks/MicroProfileTelemetryServerConfiguration.java b/tooling-server-configuration/src/main/java/org/jboss/eap/qe/microprofile/common/setuptasks/MicroProfileTelemetryServerConfiguration.java index ebc7cb51..2da296d6 100644 --- a/tooling-server-configuration/src/main/java/org/jboss/eap/qe/microprofile/common/setuptasks/MicroProfileTelemetryServerConfiguration.java +++ b/tooling-server-configuration/src/main/java/org/jboss/eap/qe/microprofile/common/setuptasks/MicroProfileTelemetryServerConfiguration.java @@ -94,6 +94,7 @@ public static void addOpenTelemetryCollectorConfiguration(final String otlpColle operations.writeAttribute(OPENTELEMETRY_SUBSYSTEM_ADDRESS, "exporter-type", "otlp"); operations.writeAttribute(OPENTELEMETRY_SUBSYSTEM_ADDRESS, "sampler-type", "on"); operations.writeAttribute(OPENTELEMETRY_SUBSYSTEM_ADDRESS, "max-export-batch-size", "512"); + operations.writeAttribute(OPENTELEMETRY_SUBSYSTEM_ADDRESS, "batch-delay", "1"); operations.writeAttribute(OPENTELEMETRY_SUBSYSTEM_ADDRESS, "max-queue-size", "1"); operations.writeAttribute(OPENTELEMETRY_SUBSYSTEM_ADDRESS, "endpoint", otlpCollectorEndpointUrl); new Administration(client).reloadIfRequired();