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();