From ca78e9f5c1e90fddb08254ea67b75e19fdc36ad4 Mon Sep 17 00:00:00 2001 From: Jason Lee Date: Thu, 20 Apr 2023 08:56:42 -0500 Subject: [PATCH] [WFLY-17678] Add Micrometer quickstart Add module Develop demo app Document QS --- .../micrometer/overridable-functions.sh | 31 ++ .../workflows/quickstart_micrometer_ci.yml | 15 + README.adoc | 1 + micrometer/README.adoc | 259 ++++++++++++++ micrometer/charts/helm.yaml | 9 + .../charts/opentelemetry-collector.yaml | 110 ++++++ micrometer/configure-micrometer.cli | 9 + micrometer/docker-compose.yaml | 17 + micrometer/otel-collector-config.yaml | 29 ++ micrometer/pom.xml | 327 ++++++++++++++++++ micrometer/restore-configuration.cli | 8 + .../micrometer/JaxRsApplication.java | 8 + .../quickstarts/micrometer/RootResource.java | 124 +++++++ .../micrometer/BasicRuntimeIT.java | 63 ++++ .../quickstarts/micrometer/MicrometerIT.java | 52 +++ pom.xml | 1 + 16 files changed, 1063 insertions(+) create mode 100644 .ci/openshift-ci/build-root/scripts/qs-overrides/micrometer/overridable-functions.sh create mode 100644 .github/workflows/quickstart_micrometer_ci.yml create mode 100644 micrometer/README.adoc create mode 100644 micrometer/charts/helm.yaml create mode 100644 micrometer/charts/opentelemetry-collector.yaml create mode 100644 micrometer/configure-micrometer.cli create mode 100644 micrometer/docker-compose.yaml create mode 100644 micrometer/otel-collector-config.yaml create mode 100644 micrometer/pom.xml create mode 100644 micrometer/restore-configuration.cli create mode 100644 micrometer/src/main/java/org/wildfly/quickstarts/micrometer/JaxRsApplication.java create mode 100644 micrometer/src/main/java/org/wildfly/quickstarts/micrometer/RootResource.java create mode 100644 micrometer/src/test/java/org/wildfly/quickstarts/micrometer/BasicRuntimeIT.java create mode 100644 micrometer/src/test/java/org/wildfly/quickstarts/micrometer/MicrometerIT.java diff --git a/.ci/openshift-ci/build-root/scripts/qs-overrides/micrometer/overridable-functions.sh b/.ci/openshift-ci/build-root/scripts/qs-overrides/micrometer/overridable-functions.sh new file mode 100644 index 0000000000..3f2f356e3f --- /dev/null +++ b/.ci/openshift-ci/build-root/scripts/qs-overrides/micrometer/overridable-functions.sh @@ -0,0 +1,31 @@ +function runPostHelmInstallCommands() { + oc apply -f charts/opentelemetry-collector.yaml +} + + +function getMvnVerifyExtraArguments() +{ + mgmtHostRoute=$(oc get route otelcol-grpc --template='{{ .spec.host }}') + echo "-Dopentelemetry.collector.host=https://opentelemetry" +} + + +function cleanPrerequisites() +{ + echo "Removing all opentelemetry-tracing resources" + oc delete route otelcol-grpc + oc delete route otelcol-prometheus + oc delete service opentelemetrycollector + oc delete deployment opentelemetrycollector + oc delete configmap collector-config +} + + +function testsFailed() { + echo "----> Getting status of all pods" + oc get pods + echo "----> Checking logs for postgres pod" + echo oc logs todo-backend-postgresql-0 + echo "----> Checking events" + oc get events +} diff --git a/.github/workflows/quickstart_micrometer_ci.yml b/.github/workflows/quickstart_micrometer_ci.yml new file mode 100644 index 0000000000..50fd337419 --- /dev/null +++ b/.github/workflows/quickstart_micrometer_ci.yml @@ -0,0 +1,15 @@ +name: WildFly Micrometer Quickstart CI + +on: + pull_request: + types: [opened, synchronize, reopened, ready_for_review] + paths: + - 'micrometer/**' + - '.github/workflows/quickstart_ci.yml' +jobs: + call-quickstart_ci: + uses: ./.github/workflows/quickstart_ci.yml + with: + QUICKSTART_PATH: micrometer + TEST_PROVISIONED_SERVER: true + TEST_BOOTABLE_JAR: true diff --git a/README.adoc b/README.adoc index 8ac1cf238d..d8b305f01f 100644 --- a/README.adoc +++ b/README.adoc @@ -176,6 +176,7 @@ NOTE: Some of these quickstarts use the H2 database included with {productNameFu | link:logging/README{outfilesuffix}[logging]|Logging | The `logging` quickstart demonstrates how to configure different logging levels in {productName}. | Intermediate | _none_ | link:mail/README{outfilesuffix}[mail]|JavaMail, CDI, JSF | The `mail` quickstart demonstrates how to send and receive emails using CDI and JSF and with custom Mail provider configured in {productName}. | Beginner | _none_ | link:messaging-clustering-singleton/README{outfilesuffix}[messaging-clustering-singleton]|JMS, MDB, Clustering | The `messaging-clustering-singleton` quickstart uses a JMS topic and a queue to demonstrate clustering using {productName} messaging with MDB singleton configuration where only one node in the cluster will be active. | Advanced | _none_ +| link:micrometer/README{outfilesuffix}[micrometer]|Micrometer | The `micrometer` quickstart demonstrates the use of the Micrometer library in {productName}. | Beginner | _none_ | link:microprofile-config/README{outfilesuffix}[microprofile-config]|MicroProfile Config | The `microprofile-config` quickstart demonstrates the use of the MicroProfile Config specification in {productName}. | Beginner | _none_ | link:microprofile-fault-tolerance/README{outfilesuffix}[microprofile-fault-tolerance]|MicroProfile, Fault Tolerance | The `microprofile-fault-tolerance` quickstart demonstrates how to use Eclipse MicroProfile Fault Tolerance in {productName}. | Intermediate | _none_ | link:microprofile-health/README{outfilesuffix}[microprofile-health]|MicroProfile Health | The `microprofile-health` quickstart demonstrates the use of the MicroProfile Health specification in {productName}. | Beginner | _none_ diff --git a/micrometer/README.adoc b/micrometer/README.adoc new file mode 100644 index 0000000000..87d833fdf4 --- /dev/null +++ b/micrometer/README.adoc @@ -0,0 +1,259 @@ +include::../shared-doc/attributes.adoc[] + += micrometer: Micrometer QuickStart +:author: Jason Lee +:level: Beginner +:technologies: Micrometer +:openshift: true + +[abstract] +The `micrometer` quickstart demonstrates the use of the Micrometer library in {productName}. + +:standalone-server-type: default +:archiveType: war +:archiveName: {artifactId} +:restoreScriptName: restore-configuration.cli + +== What is it? + +https://micrometer.io[Micrometer] is a vendor-neutral facade that allows application developers to collect and report application and system metrics to the backend of their choice in an entirely portable manner. By simply replacing the `MeterRegistry` used, or combining them in Micrometer's `CompositeRegistry` data can be exported a variety of monitoring systems with no application code changes. + +== Architecture + +In this quickstart, we will build a small, simple application that shows the usage of a number of Micrometer's `Meter` implementations. We will also demonstrate the means by which {productName} exports the metrics data, which is via the https://opentelemetry.io/docs/reference/specification/protocol/otlp/[OpenTelemetry Protocol (OTLP)] to the https://opentelemetry.io/docs/collector/[OpenTelemetry Collector]. To provide simpler access to the published metrics, the Collector will be configured with a Prometheus endpoint, from which we can scrape data. + +== Prerequisites + +To complete this guide, you will need: + +* less than 15 minutes +* JDK 11+ installed with `JAVA_HOME` configured appropriately +* Apache Maven 3.5.3+ + +include::../shared-doc/use-of-jboss-home-name.adoc[leveloffset=+1] + +== Steps + +[[configure_the_server]] +=== Configure the Server + +You enable Micrometer by running JBoss CLI commands. For your convenience, this quickstart batches the commands into a `configure-micrometer.cli` script provided in the root directory of this quickstart. + +. Before you begin, make sure you do the following: + +* xref:back_up_standalone_server_configuration[Back up the {productName} standalone server configuration] as described above. +* xref:start_the_eap_standalone_server[Start the {productName} server with the standalone default profile] as described above. + +. Review the `configure-micrometer.cli` file in the root of this quickstart directory. This script adds the configuration that enables Micrometer for the quickstart components. Comments in the script describe the purpose of each block of commands. +. Open a new terminal, navigate to the root directory of this quickstart, and run the following command, replacing `__{jbossHomeName}__` with the path to your server: ++ +[source,subs="+quotes,attributes+",options="nowrap"] +---- +$ __{jbossHomeName}__/bin/jboss-cli.sh --connect --file=configure-micrometer.cli +---- ++ +NOTE: For Windows, use the `__{jbossHomeName}__\bin\jboss-cli.bat` script. ++ + +You should see the following result when you run the script: ++ +[source,options="nowrap"] +---- +The batch executed successfully +process-state: reload-required +---- + +. You'll need to reload the configuration after that: ++ +[source,subs="+quotes,attributes+",options="nowrap"] +---- +$ __{jbossHomeName}__/bin/jboss-cli.sh --connect --commands=reload +---- + +[[start_opentelemetry_collector]] +=== Starting the OpenTelemetry Collector + +By default, {productName} will publish metrics every 10 seconds, so you will soon start seeing errors about a refused connection. This is because we told {productName} to publish to a server that is not there, so we need to fix that. To make that as simple as possible, you can use Docker Compose to start an instance of the OpenTelemetry Collector: + +[source,yaml] +---- +include::docker-compose.yml[] +---- + +We also need to provide a configuration file for the collector: + +[source,yaml] +---- +include::otel-collector-config.yml[] +---- + +We can now bring up the collector instance: + +[source,bash] +---- +$ docker-compose up +---- + +The service should be available almost immediately, which you can verify by looking at the Prometheus endpoint we've configured by pointing your browser at http://localhost:1234/metrics[]. You should see quite a few metrics listed, none of which are what our application has registered. What you're seeing are the system and JVM metrics automatically registered and published by {productName} to give systems/applications administrators a comprehensive view of system health and performance. + + +=== Registering metrics + +Micrometer uses a programmatic approach to metrics definition, as opposed the more declarative, annotation-based approach of other libraries. Because of that, we need to explicitly register our `Meter` s before they can be used: + +[source,java] +---- +@Path("/") +@ApplicationScoped +public class RootResource { + // ... + @Inject + private MeterRegistry registry; + + private Counter performCheckCounter; + private Counter originalCounter; + private Counter duplicatedCounter; + + @PostConstruct + private void createMeters() { + Gauge.builder("prime.highestSoFar", () -> highestPrimeNumberSoFar) + .description("Highest prime number so far.") + .register(registry); + performCheckCounter = Counter + .builder("prime.performedChecks") + .description("How many prime checks have been performed.") + .register(registry); + originalCounter = Counter + .builder("prime.duplicatedCounter") + .tags(List.of(Tag.of("type", "original"))) + .register(registry); + duplicatedCounter = Counter + .builder("prime.duplicatedCounter") + .tags(List.of(Tag.of("type", "copy"))) + .register(registry); + } + // ... +} +---- + +Notice that we start by `@Inject` ing the `MeterRegistry`. This is a {productName}-managed instance, so all applications need to do it inject it and start using. Once we have that, we can use to build and register our meters, which we do in `@PostConstuct private void createMeters()` + +[NOTE] +==== +This must be done _post_-construction, as the `MeterRegistry` must be injected before it can be used to register the meters. +==== + +In this example, we register several different types to demonstrate their use. With those registered, we can start writing application logic: + +[source,java] +---- +@GET +@Path("/prime/{number}") +public String checkIfPrime(@PathParam("number") long number) throws Exception { + performCheckCounter.increment(); + + Timer timer = registry.timer("prime.timer"); + + return timer.recordCallable(() -> { + + if (number < 1) { + return "Only natural numbers can be prime numbers."; + } + + if (number == 1) { + return "1 is not prime."; + } + + if (number == 2) { + return "2 is prime."; + } + + if (number % 2 == 0) { + return number + " is not prime, it is divisible by 2."; + } + + for (int i = 3; i < Math.floor(Math.sqrt(number)) + 1; i = i + 2) { + try { + Thread.sleep(10); + } catch (InterruptedException e) { + // + } + if (number % i == 0) { + return number + " is not prime, is divisible by " + i + "."; + } + } + + if (number > highestPrimeNumberSoFar) { + highestPrimeNumberSoFar = number; + } + + return number + " is prime."; + }); +} +---- + +This method represents a simple REST endpoint that is able to determine whether the number passed as a path parameter is a prime number. + +include::../shared-doc/build-and-deploy-the-quickstart.adoc[leveloffset=+1] + +=== Access the quickstart application + +You can either access the application via your browser at http://localhost:8080/micrometer/prime/13[], or from the command line: + +[source,bash] +---- +$ curl http://localhost:8080/micrometer/prime/13 +---- + +It should return a simple document: + +[source] +----- +13 is prime. +----- + +Once given enough time to allow {productName} to publish metrics updates, you now see your application's meters reported in the http://localhost:1234/metrics[Prometheus export]. You can also view them via the command-line: + +[source,bash] +---- +$ curl -s http://localhost:1234/metrics | grep "prime_" +# HELP prime_duplicatedCounter +# TYPE prime_duplicatedCounter counter +prime_duplicatedCounter{job="wildfly",type="copy"} 0 +prime_duplicatedCounter{job="wildfly",type="original"} 0 +# HELP prime_highestSoFar Highest prime number so far. +# TYPE prime_highestSoFar gauge +prime_highestSoFar{job="wildfly"} 13 +# HELP prime_performedChecks How many prime checks have been performed. +# TYPE prime_performedChecks counter +prime_performedChecks{job="wildfly"} 1 +# HELP prime_timer +# TYPE prime_timer histogram +prime_timer_bucket{job="wildfly",le="+Inf"} 1 +prime_timer_sum{job="wildfly"} 10.941035 +prime_timer_count{job="wildfly"} 1 + +---- + +Notice that all four meters registered in the `@PostConstruct` method as well as the `Timer` in our endpoint method have all been published. + +// Server Distribution Testing +include::../shared-doc/run-integration-tests-with-server-distribution.adoc[leveloffset=+2] +// Undeploy the Quickstart +include::../shared-doc/undeploy-the-quickstart.adoc[leveloffset=+2] +// Restore the {productName} Standalone Server Configuration +include::../shared-doc/restore-standalone-server-configuration.adoc[leveloffset=+2] +// Restore the {productName} Standalone Server Configuration Manually +include::../shared-doc/restore-standalone-server-configuration-manual.adoc[leveloffset=+3] +// Build and run sections for other environments/builds +ifndef::ProductRelease,EAPXPRelease[] +include::../shared-doc/build-and-run-the-quickstart-with-provisioned-server.adoc[leveloffset=+1] +endif::[] +// Bootable JAR +include::../shared-doc/build-and-run-the-quickstart-with-bootable-jar.adoc[leveloffset=+1] +// OpenShift +include::../shared-doc/build-and-run-the-quickstart-with-openshift.adoc[leveloffset=+1] + +== Conclusion + +Micrometer provides a de facto standard way of capturing and publishing metrics to the monitoring solution of your choice. {productName} provides a convenient, out-of-the-box integration of Micrometer to make it easier to capture those metrics and monitor your application's health and performance. For more information on Micrometer, please refer to the project's https://micrometer.io[website]. diff --git a/micrometer/charts/helm.yaml b/micrometer/charts/helm.yaml new file mode 100644 index 0000000000..c0bbbce795 --- /dev/null +++ b/micrometer/charts/helm.yaml @@ -0,0 +1,9 @@ +build: + uri: https://github.com/wildfly/quickstart.git + ref: main + contextDir: micrometer +deploy: + replicas: 1 + env: + - name: OTEL_COLLECTOR_HOST + value: "opentelemetrycollector" diff --git a/micrometer/charts/opentelemetry-collector.yaml b/micrometer/charts/opentelemetry-collector.yaml new file mode 100644 index 0000000000..c163a8b874 --- /dev/null +++ b/micrometer/charts/opentelemetry-collector.yaml @@ -0,0 +1,110 @@ +--- +apiVersion: v1 +kind: ConfigMap +metadata: + name: collector-config +data: + collector.yml: | + receivers: + otlp: + protocols: + grpc: + http: + processors: + exporters: + logging: + verbosity: detailed + prometheus: + endpoint: "0.0.0.0:1234" + service: + pipelines: + metrics: + receivers: [otlp] + processors: [] + exporters: [logging,prometheus] +--- +apiVersion: apps/v1 +kind: Deployment +metadata: + name: opentelemetrycollector +spec: + replicas: 1 + selector: + matchLabels: + app.kubernetes.io/name: opentelemetrycollector + template: + metadata: + labels: + app.kubernetes.io/name: opentelemetrycollector + spec: + containers: + - name: otelcol + args: + - --config=/conf/collector.yml + image: otel/opentelemetry-collector:0.89.0 + volumeMounts: + - mountPath: /conf + name: collector-config + volumes: + - configMap: + items: + - key: collector.yml + path: collector.yml + name: collector-config + name: collector-config +--- +apiVersion: v1 +kind: Service +metadata: + name: opentelemetrycollector +spec: + ports: + - name: grpc-otlp + port: 4317 + protocol: TCP + targetPort: 4317 + - name: grpc-http + port: 4318 + protocol: TCP + targetPort: 4318 + - name: prometheus + port: 1234 + protocol: TCP + targetPort: 1234 + selector: + app.kubernetes.io/name: opentelemetrycollector + type: ClusterIP +--- +apiVersion: route.openshift.io/v1 +kind: Route +metadata: + name: otelcol-grpc + labels: + app.kubernetes.io/name: microprofile +spec: + port: + targetPort: grpc-http + to: + kind: Service + name: opentelemetrycollector + tls: + termination: edge + insecureEdgeTerminationPolicy: Redirect + wildcardPolicy: None +--- +apiVersion: route.openshift.io/v1 +kind: Route +metadata: + name: otelcol-prometheus + labels: + app.kubernetes.io/name: microprofile +spec: + port: + targetPort: prometheus + to: + kind: Service + name: opentelemetrycollector + tls: + termination: edge + insecureEdgeTerminationPolicy: Redirect + wildcardPolicy: None diff --git a/micrometer/configure-micrometer.cli b/micrometer/configure-micrometer.cli new file mode 100644 index 0000000000..58ff2e4966 --- /dev/null +++ b/micrometer/configure-micrometer.cli @@ -0,0 +1,9 @@ +# CLI script to enable micrometer for the quickstart application in the application server +if (outcome != success) of /extension=org.wildfly.extension.micrometer:read-resource + /extension=org.wildfly.extension.micrometer:add + /subsystem=micrometer:add() +end-if + +# Configure the endpoint for metrics publication +/subsystem=micrometer:write-attribute(name=endpoint, value="http://${opentelemetry.collector.host:localhost}:4318/v1/metrics") +/subsystem=micrometer:write-attribute(name="step",value="1") diff --git a/micrometer/docker-compose.yaml b/micrometer/docker-compose.yaml new file mode 100644 index 0000000000..5bb18439c0 --- /dev/null +++ b/micrometer/docker-compose.yaml @@ -0,0 +1,17 @@ +version: "3" + +services: + otel-collector: + image: otel/opentelemetry-collector + command: [--config=/etc/otel-collector-config.yml] + volumes: + - ./otel-collector-config.yml:/etc/otel-collector-config.yml + ports: + - 1888:1888 # pprof extension + - 8888:8888 # Prometheus metrics exposed by the collector + - 8889:8889 # Prometheus exporter metrics + - 13133:13133 # health_check extension + - 4317:4317 # OTLP gRPC receiver + - 4318:4318 # OTLP http receiver + - 55679:55679 # zpages extension + - 1234:1234 # /metrics endpoint diff --git a/micrometer/otel-collector-config.yaml b/micrometer/otel-collector-config.yaml new file mode 100644 index 0000000000..aa603ad2e6 --- /dev/null +++ b/micrometer/otel-collector-config.yaml @@ -0,0 +1,29 @@ +extensions: + health_check: + pprof: + endpoint: 0.0.0.0:1777 + zpages: + endpoint: 0.0.0.0:55679 + +receivers: + otlp: + protocols: + grpc: + http: + +processors: + batch: + +exporters: + prometheus: + endpoint: "0.0.0.0:1234" + +service: + pipelines: + metrics: + receivers: [otlp] + processors: [batch] + exporters: [prometheus] + + extensions: [health_check, pprof, zpages] + diff --git a/micrometer/pom.xml b/micrometer/pom.xml new file mode 100644 index 0000000000..a1c647da02 --- /dev/null +++ b/micrometer/pom.xml @@ -0,0 +1,327 @@ + + + 4.0.0 + + org.wildfly.quickstarts + wildfly-quickstart-parent + + 6 + + + + micrometer + 31.0.0.Final-SNAPSHOT + war + + Quickstart: micrometer + + + + 31.0.0.Beta1 + + ${version.server} + 4.2.1.Final + 10.0.0.Final + 5.0.0.Final + + + + + jboss-public-maven-repository + JBoss Public Maven Repository + https://repository.jboss.org/nexus/content/groups/public/ + + true + never + + + true + never + + default + + + redhat-ga-maven-repository + Red Hat GA Maven Repository + https://maven.repository.redhat.com/ga/ + + true + never + + + true + never + + default + + + + + + jboss-public-maven-repository + JBoss Public Maven Repository + https://repository.jboss.org/nexus/content/groups/public/ + + true + + + true + + + + redhat-ga-maven-repository + Red Hat GA Maven Repository + https://maven.repository.redhat.com/ga/ + + true + + + true + + + + + + + + org.wildfly.bom + wildfly-ee-with-tools + ${version.bom.ee} + pom + import + + + + + org.wildfly + wildfly-common-expansion-dependency-management + ${version.server} + pom + import + + + + + + + + io.micrometer + micrometer-core + provided + + + + + jakarta.enterprise + jakarta.enterprise.cdi-api + provided + + + jakarta.inject + jakarta.inject-api + provided + + + jakarta.annotation + jakarta.annotation-api + provided + + + + jakarta.ws.rs + jakarta.ws.rs-api + provided + + + + + junit + junit + test + + + + + + ${project.artifactId} + + + + org.wildfly.plugins + wildfly-maven-plugin + ${version.plugin.wildfly} + + + org.wildfly.plugins + wildfly-jar-maven-plugin + ${version.plugin.wildfly-jar} + + + + + + org.apache.maven.plugins + maven-compiler-plugin + + 11 + 11 + + + + + + + + provisioned-server + + + + org.wildfly.plugins + wildfly-maven-plugin + + + + org.wildfly:wildfly-galleon-pack:${version.server} + + + + + cdi + jaxrs-server + micrometer + + + + + + + + true + + + + + ROOT.war + + + + + package + + + + + + + + + + bootable-jar + + + + org.wildfly.plugins + wildfly-jar-maven-plugin + + wildfly@maven(org.jboss.universe:community-universe)#${version.server} + + cdi + jaxrs-server + micrometer + + + true + + + + + + + + + + + + + package + + + + + + + + + + openshift + + + + org.wildfly.plugins + wildfly-maven-plugin + + + + org.wildfly:wildfly-galleon-pack:${version.server} + + + org.wildfly.cloud:wildfly-cloud-galleon-pack:${version.pack.cloud} + + + + cdi + jaxrs-server + micrometer + + ROOT.war + + + + + + true + + + + + + + package + + + + + + + + + + integration-testing + + + + org.apache.maven.plugins + maven-failsafe-plugin + + + **/*IT + + + + + + integration-test + + + + + + + + + diff --git a/micrometer/restore-configuration.cli b/micrometer/restore-configuration.cli new file mode 100644 index 0000000000..697b49eed2 --- /dev/null +++ b/micrometer/restore-configuration.cli @@ -0,0 +1,8 @@ +# CLI script to restore the application server configuration that was modified to run the quickstart + +# Remove the WildFly Micrometer extension +/subsystem=micrometer:remove +/extension=org.wildfly.extension.micrometer:remove + +# Reload the server configuration +reload diff --git a/micrometer/src/main/java/org/wildfly/quickstarts/micrometer/JaxRsApplication.java b/micrometer/src/main/java/org/wildfly/quickstarts/micrometer/JaxRsApplication.java new file mode 100644 index 0000000000..a3f6fde6de --- /dev/null +++ b/micrometer/src/main/java/org/wildfly/quickstarts/micrometer/JaxRsApplication.java @@ -0,0 +1,8 @@ +package org.wildfly.quickstarts.micrometer; + +import jakarta.ws.rs.ApplicationPath; +import jakarta.ws.rs.core.Application; + +@ApplicationPath("/") +public class JaxRsApplication extends Application { +} diff --git a/micrometer/src/main/java/org/wildfly/quickstarts/micrometer/RootResource.java b/micrometer/src/main/java/org/wildfly/quickstarts/micrometer/RootResource.java new file mode 100644 index 0000000000..6f342a4ad6 --- /dev/null +++ b/micrometer/src/main/java/org/wildfly/quickstarts/micrometer/RootResource.java @@ -0,0 +1,124 @@ +package org.wildfly.quickstarts.micrometer; + +import jakarta.annotation.PostConstruct; +import jakarta.enterprise.context.ApplicationScoped; +import jakarta.inject.Inject; +import jakarta.ws.rs.GET; +import jakarta.ws.rs.Path; +import jakarta.ws.rs.PathParam; +import jakarta.ws.rs.Produces; +import jakarta.ws.rs.core.MediaType; +import io.micrometer.core.instrument.Counter; +import io.micrometer.core.instrument.Gauge; +import io.micrometer.core.instrument.MeterRegistry; +import io.micrometer.core.instrument.Tag; +import io.micrometer.core.instrument.Timer; + +import java.util.List; + +@Path("/") +@ApplicationScoped +public class RootResource { + static final String METER_HIGHEST_SO_FAR = "prime_highestSoFar"; + static final String METER_PERFORMED_CHECKS = "prime_performedChecks"; + static final String METER_DUPLICATED_COUNTER = "prime_duplicatedCounter"; + private long highestPrimeNumberSoFar = 2; + + @Inject + private MeterRegistry registry; + + private Counter performCheckCounter; + private Counter originalCounter; + private Counter duplicatedCounter; + + @PostConstruct + private void createMeters() { + Gauge.builder(METER_HIGHEST_SO_FAR, () -> highestPrimeNumberSoFar) + .description("Highest prime number so far.") + .register(registry); + performCheckCounter = Counter + .builder(METER_PERFORMED_CHECKS) + .description("How many prime checks have been performed.") + .register(registry); + originalCounter = Counter + .builder(METER_DUPLICATED_COUNTER) + .tags(List.of(Tag.of("type", "original"))) + .register(registry); + duplicatedCounter = Counter + .builder(METER_DUPLICATED_COUNTER) + .tags(List.of(Tag.of("type", "copy"))) + .register(registry); + } + + @GET + @Produces(MediaType.TEXT_PLAIN) + public String getRootResponse() { + return "Micrometer quickstart deployed successfully. You can find the available operations in the included README file."; + } + + @GET + @Path("/prime/{number}") + public String checkIfPrime(@PathParam("number") long number) throws Exception { + performCheckCounter.increment(); + + Timer timer = registry.timer("prime.timer"); + + return timer.recordCallable(() -> { + + if (number < 1) { + return "Only natural numbers can be prime numbers."; + } + + if (number == 1) { + return "1 is not prime."; + } + + if (number == 2) { + return "2 is prime."; + } + + if (number % 2 == 0) { + return number + " is not prime, it is divisible by 2."; + } + + for (int i = 3; i < Math.floor(Math.sqrt(number)) + 1; i = i + 2) { + try { + Thread.sleep(10); + } catch (InterruptedException e) { + // + } + if (number % i == 0) { + return number + " is not prime, is divisible by " + i + "."; + } + } + + if (number > highestPrimeNumberSoFar) { + highestPrimeNumberSoFar = number; + } + + return number + " is prime."; + }); + } + + @GET + @Path("/prime/highest") + public String highestPrimeNumberSoFar() { + return "The highest prime number so far is " + highestPrimeNumberSoFar + "."; + } + + @GET + @Path("/duplicates") + @Produces(MediaType.TEXT_PLAIN) + public String duplicates() { + originalCounter.increment(); + return "duplicated metrics"; + } + + @GET + @Path("/duplicates2") + @Produces(MediaType.TEXT_PLAIN) + public String duplicates2() { + duplicatedCounter.increment(); + return "duplicated metrics"; + } +} diff --git a/micrometer/src/test/java/org/wildfly/quickstarts/micrometer/BasicRuntimeIT.java b/micrometer/src/test/java/org/wildfly/quickstarts/micrometer/BasicRuntimeIT.java new file mode 100644 index 0000000000..7a08a62fb5 --- /dev/null +++ b/micrometer/src/test/java/org/wildfly/quickstarts/micrometer/BasicRuntimeIT.java @@ -0,0 +1,63 @@ +/* + * Copyright 2023 Red Hat, Inc. + * + * 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 org.wildfly.quickstarts.micrometer; + +import static org.junit.Assert.assertEquals; + +import java.io.IOException; +import java.net.URI; +import java.net.URISyntaxException; +import java.net.http.HttpClient; +import java.net.http.HttpRequest; +import java.net.http.HttpResponse; +import java.time.Duration; + +import org.junit.Test; + +public class BasicRuntimeIT { + private static final String DEFAULT_SERVER_HOST = "http://localhost:8080/micrometer"; + + @Test + public void testHTTPEndpointIsAvailable() throws IOException, InterruptedException, URISyntaxException { + String applicationUrl = getApplicationUrl(); + + final HttpRequest request = HttpRequest.newBuilder() + .uri(new URI(applicationUrl)) + .GET() + .build(); + + final HttpResponse response = getHttpClient().send(request, HttpResponse.BodyHandlers.ofString()); + assertEquals(200, response.statusCode()); + } + + static String getApplicationUrl() { + String serverHost = System.getenv("SERVER_HOST"); + if (serverHost == null) { + serverHost = System.getProperty("server.host"); + } + if (serverHost == null) { + serverHost = DEFAULT_SERVER_HOST; + } + return serverHost; + } + + static HttpClient getHttpClient() { + return HttpClient.newBuilder() + .followRedirects(HttpClient.Redirect.ALWAYS) + .connectTimeout(Duration.ofMinutes(1)) + .build(); + } +} diff --git a/micrometer/src/test/java/org/wildfly/quickstarts/micrometer/MicrometerIT.java b/micrometer/src/test/java/org/wildfly/quickstarts/micrometer/MicrometerIT.java new file mode 100644 index 0000000000..54d5ba845b --- /dev/null +++ b/micrometer/src/test/java/org/wildfly/quickstarts/micrometer/MicrometerIT.java @@ -0,0 +1,52 @@ +/* + * Copyright 2023 Red Hat, Inc. + * + * 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 org.wildfly.quickstarts.micrometer; + +import static org.junit.Assert.assertEquals; + +import java.io.IOException; +import java.net.URI; +import java.net.URISyntaxException; +import java.net.http.HttpClient; +import java.net.http.HttpRequest; +import java.net.http.HttpResponse; + +import org.junit.Test; + +public class MicrometerIT { + public static final int MAX_NUMBER = 15; + + @Test + public void makeSeveralApplicationRequests() throws IOException, InterruptedException, URISyntaxException { + String applicationUrl = BasicRuntimeIT.getApplicationUrl(); + + final HttpClient client = BasicRuntimeIT.getHttpClient(); + final HttpRequest requestPrime = HttpRequest.newBuilder().uri(new URI(applicationUrl + "/prime/13")).GET().build(); + final HttpRequest requestHighest = HttpRequest.newBuilder().uri(new URI(applicationUrl + "/prime/highest")).GET().build(); + final HttpRequest requestDuplicates = HttpRequest.newBuilder().uri(new URI(applicationUrl + "/duplicates")).GET().build(); + final HttpRequest requestDuplicates2 = HttpRequest.newBuilder().uri(new URI(applicationUrl + "/duplicates2")).GET().build(); + + + + // Make several requests to make the metrics more interesting + for (int i = 0; i < MAX_NUMBER; i++) { + assertEquals(200, client.send(requestPrime, HttpResponse.BodyHandlers.ofString()).statusCode()); + assertEquals(200, client.send(requestHighest, HttpResponse.BodyHandlers.ofString()).statusCode()); + assertEquals(200, client.send(requestDuplicates, HttpResponse.BodyHandlers.ofString()).statusCode()); + assertEquals(200, client.send(requestDuplicates2, HttpResponse.BodyHandlers.ofString()).statusCode()); + } + } +} diff --git a/pom.xml b/pom.xml index b8838f1d73..76e442269a 100644 --- a/pom.xml +++ b/pom.xml @@ -330,6 +330,7 @@ logging mail messaging-clustering-singleton + micrometer microprofile-config microprofile-fault-tolerance microprofile-health