From 405a6f03ef3f0716706725e82e40dd61de6532a2 Mon Sep 17 00:00:00 2001 From: Erin Schnabel Date: Wed, 12 Aug 2020 17:36:17 -0400 Subject: [PATCH] Micrometer guide --- .../src/main/asciidoc/micrometer-metrics.adoc | 282 ++++++++++++++++++ .../runtime/config/runtime/ExportConfig.java | 50 ++-- .../resources/META-INF/quarkus-extension.yaml | 5 +- 3 files changed, 311 insertions(+), 26 deletions(-) create mode 100644 docs/src/main/asciidoc/micrometer-metrics.adoc diff --git a/docs/src/main/asciidoc/micrometer-metrics.adoc b/docs/src/main/asciidoc/micrometer-metrics.adoc new file mode 100644 index 0000000000000..d79cbe9ee155e --- /dev/null +++ b/docs/src/main/asciidoc/micrometer-metrics.adoc @@ -0,0 +1,282 @@ +//// +This guide is maintained in the main Quarkus repository +and pull requests should be submitted there: +https://github.com/quarkusio/quarkus/tree/master/docs/src/main/asciidoc +//// += Quarkus - Micrometer Metrics + +include::./attributes.adoc[] + +This guide demonstrates how your Quarkus application can utilize the Micrometer +metrics library for runtime and application metrics. + + +== Prerequisites + +To complete this guide, you need: + +* less than 15 minutes +* an IDE +* JDK 1.8+ installed with `JAVA_HOME` configured appropriately +* Apache Maven {maven-version} + +== Architecture + +In this example, we build a very simple microservice which offers one REST endpoint and that determines +if a number is prime. + +== Solution + +We recommend that you follow the instructions in the next sections and create the application step by step. +However, you can go right to the completed example. + +Clone the Git repository: `git clone {quickstarts-clone-url}`, or download an {quickstarts-archive-url}[archive]. + +The solution is located in the `micrometer-quickstart` {quickstarts-tree-url}/micrometer-quickstart[directory]. + +== Creating the Maven Project + +First, we need a new project. Create a new project with the following command: + +[source, subs=attributes+] +---- +mvn io.quarkus:quarkus-maven-plugin:{quarkus-version}:create \ + -DprojectGroupId=org.acme \ + -DprojectArtifactId=micrometer-quickstart \ + -Dextensions="micrometer" +cd micrometer-quickstart +---- + +This command generates a Maven project, that imports the `micrometer` extension as a dependency. + +If you already have your Quarkus project configured, you can add the `micrometer` extension +to your project by running the following command in your project base directory: + +[source,bash] +---- +./mvnw quarkus:add-extension -Dextensions="micrometer" +---- + +This will add the following to your `pom.xml`: + +[source,xml] +---- + + io.quarkus + quarkus-micrometer + +---- + +You should also add a micrometer dependency for the registry of your choosing, e.g. + +[source,xml] +---- + + io.micrometer + micrometer-registry-prometheus + +---- + +== Writing the application + +The application consists of a single class that implements an algorithm for checking whether a number is prime. +This algorithm is exposed over a REST interface. With the micrometer extension enabled, metrics for all http +server requests are collected automatically. + +We do want to add a few other metrics to demonstrate how those types work: + +* A counter will be incremented for every prime number discovered +* A gauge will store the highest prime number discovered +* A timer will record the time spent testing if ia number is prime. + +[source,java] +---- +package org.acme.micrometer; + +import io.micrometer.core.instrument.MeterRegistry; + +import javax.ws.rs.GET; +import javax.ws.rs.Path; +import javax.ws.rs.PathParam; +import javax.ws.rs.Produces; +import java.util.concurrent.atomic.LongAccumulator; +import java.util.function.Supplier; + +@Path("/") +public class PrimeNumberResource { + + private final LongAccumulator highestPrime = new LongAccumulator(Long::max, 0); + private final MeterRegistry registry; + + PrimeNumberResource(MeterRegistry registry) { + this.registry = registry; + + // Create a gauge that uses the highestPrimeNumberSoFar method + // to obtain the highest observed prime number + registry.gauge("prime.number.max", this, + PrimeNumberResource::highestObservedPrimeNumber); + } + + @GET + @Path("/{number}") + @Produces("text/plain") + public String checkIfPrime(@PathParam("number") int number) { + 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."; + } + + Supplier supplier = () -> { + for (int i = 3; i < Math.floor(Math.sqrt(number)) + 1; i = i + 2) { + if (number % i == 0) { + return number + " is not prime, is divisible by " + i + "."; + } + } + highestPrime.accumulate(number); + return number + " is prime."; + }; + + return registry.timer("prime.number.test").wrap(supplier).get(); + } + + /** + * This method is called by the registered {@code highest.prime.number} gauge. + * @return the highest observed prime value + */ + long highestObservedPrimeNumber() { + return highestPrime.get(); + } +} +---- + +== Running and using the application + +To run the microservice in dev mode, use `./mvnw clean compile quarkus:dev` + +=== Generate some values for the metrics + +First, ask the endpoint whether some numbers are prime numbers. + +[source,shell] +---- +curl localhost:8080/350 +---- + +The application will respond that 350 is not a prime number because it can be divided by 2. + +Now for some large prime number so that the test takes a bit more time: + +[source,shell] +---- +curl localhost:8080/629521085409773 +---- + +The application will respond that 629521085409773 is a prime number. +If you want, try some more calls with numbers of your choice. + +=== Review the generated metrics + +To view the metrics, execute `curl localhost:8080/metrics/` + +Prometheus-formatted metrics will be returned in plain text in no particular order. + +The application above has only one custom gauge that measures the time +spent determining whether or not a number is prime. The micrometer extension +enables additional system, jvm, and http metrics. A subset of the collected metrics +are shown below. + +[source,shell] +---- +# HELP http_server_requests_seconds +# TYPE http_server_requests_seconds summary +http_server_requests_seconds_count{method="GET",outcome="SUCCESS",status="200",uri="/{number}",} 4.0 +http_server_requests_seconds_sum{method="GET",outcome="SUCCESS",status="200",uri="/{number}",} 0.041501773 +# HELP http_server_requests_seconds_max +# TYPE http_server_requests_seconds_max gauge +http_server_requests_seconds_max{method="GET",outcome="SUCCESS",status="200",uri="/{number}",} 0.038066359 +# HELP jvm_threads_peak_threads The peak live thread count since the Java virtual machine started or peak was reset +# TYPE jvm_threads_peak_threads gauge +jvm_threads_peak_threads 56.0 +# HELP http_server_connections_seconds_max +# TYPE http_server_connections_seconds_max gauge +http_server_connections_seconds_max 0.102580737 +# HELP http_server_connections_seconds +# TYPE http_server_connections_seconds summary +http_server_connections_seconds_active_count 5.0 +http_server_connections_seconds_duration_sum 0.175032815 +# HELP system_load_average_1m The sum of the number of runnable entities queued to available processors and the number of runnable entities running on the available processors averaged over a period of time +# TYPE system_load_average_1m gauge +system_load_average_1m 2.4638671875 +# HELP http_server_bytes_written_max +# TYPE http_server_bytes_written_max gauge +http_server_bytes_written_max 39.0 +# HELP http_server_bytes_written +# TYPE http_server_bytes_written summary +http_server_bytes_written_count 4.0 +http_server_bytes_written_sum 99.0 +# HELP jvm_classes_loaded_classes The number of classes that are currently loaded in the Java virtual machine +# TYPE jvm_classes_loaded_classes gauge +jvm_classes_loaded_classes 9341.0 +# HELP prime_number_max +# TYPE prime_number_max gauge +prime_number_max 887.0 +# HELP jvm_info JVM version info +# TYPE jvm_info gauge +jvm_info{runtime="OpenJDK Runtime Environment",vendor="Oracle Corporation",version="13.0.2+8",} 1.0 +# HELP jvm_classes_unloaded_classes_total The total number of classes unloaded since the Java virtual machine has started execution +# TYPE jvm_classes_unloaded_classes_total counter +jvm_classes_unloaded_classes_total 28.0 +# HELP prime_number_test_seconds +# TYPE prime_number_test_seconds summary +prime_number_test_seconds_count 3.0 +prime_number_test_seconds_sum 1.94771E-4 +# HELP prime_number_test_seconds_max +# TYPE prime_number_test_seconds_max gauge +prime_number_test_seconds_max 1.76162E-4 +# HELP http_server_bytes_read +# TYPE http_server_bytes_read summary +http_server_bytes_read_count 4.0 +http_server_bytes_read_sum 0.0 +# HELP http_server_bytes_read_max +# TYPE http_server_bytes_read_max gauge +http_server_bytes_read_max 0.0 +# HELP jvm_threads_states_threads The current number of threads having NEW state +# TYPE jvm_threads_states_threads gauge +jvm_threads_states_threads{state="runnable",} 37.0 +jvm_threads_states_threads{state="blocked",} 0.0 +jvm_threads_states_threads{state="waiting",} 15.0 +jvm_threads_states_threads{state="timed-waiting",} 4.0 +jvm_threads_states_threads{state="new",} 0.0 +jvm_threads_states_threads{state="terminated",} 0.0 +# HELP jvm_buffer_memory_used_bytes An estimate of the memory that the Java virtual machine is using for this buffer pool +# TYPE jvm_buffer_memory_used_bytes gauge +jvm_buffer_memory_used_bytes{id="mapped",} 0.0 +jvm_buffer_memory_used_bytes{id="direct",} 149521.0 +# HELP jvm_memory_committed_bytes The amount of memory in bytes that is committed for the Java virtual machine to use +# TYPE jvm_memory_committed_bytes gauge +jvm_memory_committed_bytes{area="nonheap",id="CodeHeap 'profiled nmethods'",} 1.1403264E7 +jvm_memory_committed_bytes{area="heap",id="G1 Survivor Space",} 4194304.0 +jvm_memory_committed_bytes{area="heap",id="G1 Old Gen",} 9.2274688E7 +jvm_memory_committed_bytes{area="nonheap",id="Metaspace",} 4.9803264E7 +jvm_memory_committed_bytes{area="nonheap",id="CodeHeap 'non-nmethods'",} 2555904.0 +jvm_memory_committed_bytes{area="heap",id="G1 Eden Space",} 6.9206016E7 +jvm_memory_committed_bytes{area="nonheap",id="Compressed Class Space",} 6815744.0 +jvm_memory_committed_bytes{area="nonheap",id="CodeHeap 'non-profiled nmethods'",} 2555904.0 +---- + +Note that metrics appear lazily, you often won't see any data for your endpoint until +something tries to access it, etc. + + + +== Configuration Reference + +include::{generated-dir}/config/quarkus-micrometer.adoc[opts=optional, leveloffset=+1] diff --git a/extensions/micrometer/runtime/src/main/java/io/quarkus/micrometer/runtime/config/runtime/ExportConfig.java b/extensions/micrometer/runtime/src/main/java/io/quarkus/micrometer/runtime/config/runtime/ExportConfig.java index 561068b8e650d..a4c4f4fad2bf5 100644 --- a/extensions/micrometer/runtime/src/main/java/io/quarkus/micrometer/runtime/config/runtime/ExportConfig.java +++ b/extensions/micrometer/runtime/src/main/java/io/quarkus/micrometer/runtime/config/runtime/ExportConfig.java @@ -28,22 +28,23 @@ public class ExportConfig { * A property source for configuration of the Datadog MeterRegistry to push * metrics using the Datadog API, see https://micrometer.io/docs/registry/datadog. * - * [cols="1,2", options="header"] - * .Properties - * |=== - * | Property=Default - * | Description + * Available values: * - * | `apiKey=YOUR_KEY` - * | Define the key used to push data using the Datadog API + * [cols=2] + * !=== + * h!Property=Default + * h!Description * - * | `publish=true` - * | By default, gathered metrics will be published to Datadog when the MeterRegistry is enabled. + * !`apiKey=YOUR_KEY` + * !Define the key used to push data using the Datadog API + * + * !`publish=true` + * !By default, gathered metrics will be published to Datadog when the MeterRegistry is enabled. * Use this attribute to selectively disable publication of metrics in some environments. * - * | `step=1m` - * | The interval at which metrics are sent to Datadog. The default is 1 minute. - * |=== + * !`step=1m` + * !The interval at which metrics are sent to Datadog. The default is 1 minute. + * !=== * * Other micrometer configuration attributes can also be specified. * @@ -86,22 +87,23 @@ public class ExportConfig { * A property source for configuration of the Stackdriver MeterRegistry, * see https://micrometer.io/docs/registry/stackdriver. * - * [cols="1,2", options="header"] - * .Properties - * |=== - * | Property=Default - * | Description + * Available values: + * + * [cols=2] + * !=== + * h!Property=Default + * h!Description * - * | `project-id=MY_PROJECT_ID` - * | Define the project id used to push data to Stackdriver Monitoring + * !`project-id=MY_PROJECT_ID` + * !Define the project id used to push data to Stackdriver Monitoring * - * | `publish=true` - * | By default, gathered metrics will be published to Datadog when the MeterRegistry is enabled. + * !`publish=true` + * !By default, gathered metrics will be published to Datadog when the MeterRegistry is enabled. * Use this attribute to selectively disable publication of metrics in some environments. * - * | `step=1m` - * | The interval at which metrics are sent to Stackdriver Monitoring. The default is 1 minute. - * |=== + * !`step=1m` + * !The interval at which metrics are sent to Stackdriver Monitoring. The default is 1 minute. + * !=== * * Other micrometer configuration attributes can also be specified. * diff --git a/extensions/micrometer/runtime/src/main/resources/META-INF/quarkus-extension.yaml b/extensions/micrometer/runtime/src/main/resources/META-INF/quarkus-extension.yaml index 8b8e223401f5c..12e36ee463162 100644 --- a/extensions/micrometer/runtime/src/main/resources/META-INF/quarkus-extension.yaml +++ b/extensions/micrometer/runtime/src/main/resources/META-INF/quarkus-extension.yaml @@ -1,11 +1,12 @@ -name: "Micrometer - SLF4J for metrics" +name: "Micrometer metrics" metadata: keywords: - "micrometer" - "metrics" - "metric" - "prometheus" - guide: "https://github.com/ebullient/quarkus-micrometer-extension" + - "monitoring" + guide: "https://quarkus.io/guides/micrometer-metrics" categories: - "observability" status: "experimental"