diff --git a/docs/src/main/asciidoc/cache.adoc b/docs/src/main/asciidoc/cache.adoc
index aa8fbc85f2cbf..54e28da668b2f 100644
--- a/docs/src/main/asciidoc/cache.adoc
+++ b/docs/src/main/asciidoc/cache.adoc
@@ -764,7 +764,7 @@ Each cache declared using the <<#annotations-api,annotations caching API>> can b
[NOTE]
====
The cache metrics collection will only work if your application depends on a `quarkus-micrometer-registry-*` extension.
-See the xref:micrometer.adoc[Micrometer metrics guide] to learn how to use Micrometer in Quarkus.
+See the xref:telemetry-micrometer.adoc[Micrometer metrics guide] to learn how to use Micrometer in Quarkus.
====
The cache metrics collection is disabled by default.
diff --git a/docs/src/main/asciidoc/deploying-to-kubernetes.adoc b/docs/src/main/asciidoc/deploying-to-kubernetes.adoc
index 100ad90fe3abe..1a88f7badbb5a 100644
--- a/docs/src/main/asciidoc/deploying-to-kubernetes.adoc
+++ b/docs/src/main/asciidoc/deploying-to-kubernetes.adoc
@@ -3,9 +3,10 @@ This guide is maintained in the main Quarkus repository
and pull requests should be submitted there:
https://github.com/quarkusio/quarkus/tree/main/docs/src/main/asciidoc
////
+[id="deploy-kubernetes"]
= Kubernetes extension
include::_attributes.adoc[]
-:categories: cloud
+:categories: cloud, native
:summary: This guide covers how to deploy a native application on Kubernetes.
Quarkus offers the ability to automatically generate Kubernetes resources based on sane defaults and user-supplied configuration using https://github.com/dekorateio/dekorate/[dekorate].
diff --git a/docs/src/main/asciidoc/deploying-to-openshift.adoc b/docs/src/main/asciidoc/deploying-to-openshift.adoc
index 5768aa7f4a0de..97283afc18cea 100644
--- a/docs/src/main/asciidoc/deploying-to-openshift.adoc
+++ b/docs/src/main/asciidoc/deploying-to-openshift.adoc
@@ -3,9 +3,10 @@ This guide is maintained in the main Quarkus repository
and pull requests should be submitted there:
https://github.com/quarkusio/quarkus/tree/main/docs/src/main/asciidoc
////
+[id="deploy-openshift"]
= Deploying on OpenShift
include::_attributes.adoc[]
-:categories: cloud
+:categories: cloud, native
:summary: This guide covers how to deploy a native application on OpenShift.
This guide covers generating and deploying OpenShift resources based on sane default and user supplied configuration.
diff --git a/docs/src/main/asciidoc/getting-started-testing.adoc b/docs/src/main/asciidoc/getting-started-testing.adoc
index 50673af86f6d6..31421ab89e6f2 100644
--- a/docs/src/main/asciidoc/getting-started-testing.adoc
+++ b/docs/src/main/asciidoc/getting-started-testing.adoc
@@ -3,9 +3,10 @@ This guide is maintained in the main Quarkus repository
and pull requests should be submitted there:
https://github.com/quarkusio/quarkus/tree/main/docs/src/main/asciidoc
////
+[id="testing"]
= Testing Your Application
include::_attributes.adoc[]
-:categories: core
+:categories: core, native, tooling
:summary: This guide covers testing in JVM mode, native mode, and injection of resources into tests
:numbered:
:sectnums:
diff --git a/docs/src/main/asciidoc/getting-started.adoc b/docs/src/main/asciidoc/getting-started.adoc
index 8a9099bda0f1d..38b24e00ed295 100644
--- a/docs/src/main/asciidoc/getting-started.adoc
+++ b/docs/src/main/asciidoc/getting-started.adoc
@@ -472,7 +472,7 @@ Furthermore, users can supply a custom banner by placing the banner file in `src
== Non Application endpoints
Various Quarkus extensions contribute non-application endpoints that provide different kinds of information about the application.
-Examples of such extensions are the xref:smallrye-health.adoc[health], xref:micrometer.adoc[metrics], xref:openapi-swaggerui.adoc[OpenAPI] and info extensions.
+Examples of such extensions are the xref:smallrye-health.adoc[health], xref:telemetry-micrometer.adoc[metrics], xref:openapi-swaggerui.adoc[OpenAPI] and info extensions.
These non application endpoints are normally accessible under the `/q` prefix like so:
diff --git a/docs/src/main/asciidoc/gradle-tooling.adoc b/docs/src/main/asciidoc/gradle-tooling.adoc
index 1ee21accf7085..1c4bbdefc7697 100644
--- a/docs/src/main/asciidoc/gradle-tooling.adoc
+++ b/docs/src/main/asciidoc/gradle-tooling.adoc
@@ -3,12 +3,15 @@ This guide is maintained in the main Quarkus repository
and pull requests should be submitted there:
https://github.com/quarkusio/quarkus/tree/main/docs/src/main/asciidoc
////
-= Building Quarkus apps with Gradle
+[id="gradle-tooling"]
+= Quarkus and Gradle
include::_attributes.adoc[]
-:categories: tooling
-:summary: This guide covers: Gradle configuration, creating a new project, dealing with extensions, development mode, debugging, import in your IDE, building a native image, and build a container friendly executable
+:categories: tooling, native
+:summary: Develop and build your Quarkus application with Gradle
:devtools-no-maven:
+Use Gradle to create a new project, add or remove extensions, launch development mode, debug your application, and build your application into a jar, native executable, or container-friendly executable. Import your project into your favorite IDE using Gradle project metadata.
+
[[project-creation]]
== Creating a new project
diff --git a/docs/src/main/asciidoc/grpc-service-consumption.adoc b/docs/src/main/asciidoc/grpc-service-consumption.adoc
index 58728faf34e8a..21a5b22ff689a 100644
--- a/docs/src/main/asciidoc/grpc-service-consumption.adoc
+++ b/docs/src/main/asciidoc/grpc-service-consumption.adoc
@@ -372,7 +372,7 @@ The default priority, used if the interceptor does not implement the `Prioritize
=== Enabling metrics collection
-gRPC client metrics are automatically enabled when the application also uses the xref:micrometer.adoc[`quarkus-micrometer`] extension.
+gRPC client metrics are automatically enabled when the application also uses the xref:telemetry-micrometer.adoc[`quarkus-micrometer`] extension.
Micrometer collects the metrics of all the gRPC clients used by the application.
As an example, if you export the metrics to Prometheus, you will get:
diff --git a/docs/src/main/asciidoc/grpc-service-implementation.adoc b/docs/src/main/asciidoc/grpc-service-implementation.adoc
index f3dd6fa72a6fa..eaaab4f790e80 100644
--- a/docs/src/main/asciidoc/grpc-service-implementation.adoc
+++ b/docs/src/main/asciidoc/grpc-service-implementation.adoc
@@ -371,7 +371,7 @@ dependencies {
=== Enabling metrics collection
-gRPC server metrics are automatically enabled when the application also uses the xref:micrometer.adoc[`quarkus-micrometer`] extension.
+gRPC server metrics are automatically enabled when the application also uses the xref:telemetry-micrometer.adoc[`quarkus-micrometer`] extension.
Micrometer collects the metrics of all the gRPC services implemented by the application.
As an example, if you export the metrics to Prometheus, you will get:
diff --git a/docs/src/main/asciidoc/hibernate-orm.adoc b/docs/src/main/asciidoc/hibernate-orm.adoc
index 421523ec1f7eb..8958a25296d43 100644
--- a/docs/src/main/asciidoc/hibernate-orm.adoc
+++ b/docs/src/main/asciidoc/hibernate-orm.adoc
@@ -814,7 +814,7 @@ For more information about Hibernate Envers, see link:https://hibernate.org/orm/
[[metrics]]
== Metrics
-Either xref:micrometer.adoc[Micrometer] or xref:microprofile-metrics.adoc[SmallRye Metrics] are
+Either xref:telemetry-micrometer.adoc[Micrometer] or xref:microprofile-metrics.adoc[SmallRye Metrics] are
capable of exposing metrics that Hibernate ORM collects at runtime. To enable exposure of Hibernate metrics
on the `/q/metrics` endpoint, make sure your project depends on a metrics extension and set the configuration property `quarkus.hibernate-orm.metrics.enabled` to `true`.
When using link:microprofile-metrics[SmallRye Metrics], metrics will be available under the `vendor` scope.
diff --git a/docs/src/main/asciidoc/kafka-streams.adoc b/docs/src/main/asciidoc/kafka-streams.adoc
index cee41992e1571..99d2a06836854 100644
--- a/docs/src/main/asciidoc/kafka-streams.adoc
+++ b/docs/src/main/asciidoc/kafka-streams.adoc
@@ -1249,7 +1249,7 @@ content-length: 265
This guide has shown how you can build stream processing applications using Quarkus and the Kafka Streams APIs,
both in JVM and native modes.
For running your KStreams application in production, you could also add health checks and metrics for the data pipeline.
-Refer to the Quarkus guides on xref:micrometer.adoc[Micrometer], xref:smallrye-metrics.adoc[SmallRye Metrics], and xref:smallrye-health.adoc[SmallRye Health] to learn more.
+Refer to the Quarkus guides on xref:telemetry-micrometer.adoc[Micrometer], xref:smallrye-metrics.adoc[SmallRye Metrics], and xref:smallrye-health.adoc[SmallRye Health] to learn more.
== Configuration Reference
diff --git a/docs/src/main/asciidoc/maven-tooling.adoc b/docs/src/main/asciidoc/maven-tooling.adoc
index 50cfb25486352..6e4e0abafdd52 100644
--- a/docs/src/main/asciidoc/maven-tooling.adoc
+++ b/docs/src/main/asciidoc/maven-tooling.adoc
@@ -3,12 +3,15 @@ This guide is maintained in the main Quarkus repository
and pull requests should be submitted there:
https://github.com/quarkusio/quarkus/tree/main/docs/src/main/asciidoc
////
-= Building applications with Maven
+[id="maven-tooling"]
+= Quarkus and Maven
include::_attributes.adoc[]
-:categories: tooling
-:summary: This guide covers: Maven configuration, creating a new project, dealing with extensions, development mode, debugging, import in your IDE, building a native image, and build a container friendly executable
+:categories: tooling, native
+:summary: Develop and build your Quarkus application with Maven
:devtools-no-gradle:
+Use Maven to create a new project, add or remove extensions, launch development mode, debug your application, and build your application into a jar, native executable, or container-friendly executable. Import your project into your favorite IDE using Maven project metadata.
+
[[project-creation]]
== Creating a new project
diff --git a/docs/src/main/asciidoc/micrometer.adoc b/docs/src/main/asciidoc/micrometer.adoc
deleted file mode 100644
index b264eb2f62da7..0000000000000
--- a/docs/src/main/asciidoc/micrometer.adoc
+++ /dev/null
@@ -1,633 +0,0 @@
-////
-This guide is maintained in the main Quarkus repository
-and pull requests should be submitted there:
-https://github.com/quarkusio/quarkus/tree/main/docs/src/main/asciidoc
-////
-= Micrometer Metrics
-include::_attributes.adoc[]
-:categories: observability
-:summary: This guide demonstrates how your Quarkus application can collect metrics using the Micrometer extension.
-
-This guide demonstrates how your Quarkus application can utilize the Micrometer metrics library for runtime and
-application metrics.
-
-Apart from application-specific metrics, which are described in this guide, you may also utilize built-in metrics
-exposed by various Quarkus extensions. These are described in the guide for each particular extension that supports
-built-in metrics.
-
-IMPORTANT: Micrometer is the recommended approach to metrics for Quarkus.
-
-== Prerequisites
-
-include::{includes}/prerequisites.adoc[]
-
-== Architecture
-
-Micrometer defines a core library providing a registration mechanism for Metrics, and core metric types (Counters,
-Gauges, Timers, Distribution Summaries, etc.). These core types provide an abstraction layer that can be adapted to
-different backend monitoring systems. In essence, your application (or a library) can `register` a `Counter`,
-`Gauge`, `Timer`, or `DistributionSummary` with a `MeterRegistry`. Micrometer will then delegate that registration to
-one or more implementations, where each implementation handles the unique considerations for the associated
-monitoring stack.
-
-Micrometer uses naming conventions to translate between registered Meters and the conventions used by various backend
-registries. Meter names, for example, should be created and named using dots to separate segments, `a.name.like.this`.
-Micrometer then translates that name into the format that the selected registry prefers. Prometheus
-uses underscores, which means the previous name will appear as `a_name_like_this` in Prometheus-formatted metrics
-output.
-
-== Solution
-
-We recommend that you follow the instructions in the next sections and create the application step by step.
-You can skip right to the solution if you prefer. Either:
-
-* 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
-
-Quarkus Micrometer extensions are structured similarly to Micrometer itself: `quarkus-micrometer` provides core
-micrometer support and runtime integration and other Quarkus and Quarkiverse extensions bring in additional
-dependencies and requirements to support specific monitoring systems.
-
-For this example, we'll use the Prometheus registry.
-
-First, we need a new project. Create a new project with the following command:
-
-:create-app-artifact-id: micrometer-quickstart
-:create-app-extensions: resteasy-reactive,micrometer-registry-prometheus
-include::{includes}/devtools/create-app.adoc[]
-
-This command generates a Maven project, that imports the `micrometer-registry-prometheus` extension as a dependency.
-This extension will load the core `micrometer` extension as well as additional library dependencies required to support
-prometheus.
-
-If you already have your Quarkus project configured, you can add the `micrometer-registry-prometheus` extension
-to your project by running the following command in your project base directory:
-
-:add-extension-extensions: micrometer-registry-prometheus
-include::{includes}/devtools/extension-add.adoc[]
-
-This will add the following to your build file:
-
-[source,xml,role="primary asciidoc-tabs-target-sync-cli asciidoc-tabs-target-sync-maven"]
-.pom.xml
-----
-
- io.quarkus
- quarkus-micrometer-registry-prometheus
-
-----
-
-[source,gradle,role="secondary asciidoc-tabs-target-sync-gradle"]
-.build.gradle
-----
-implementation("io.quarkus:quarkus-micrometer-registry-prometheus")
-----
-
-== Writing the application
-
-Micrometer provides an API that allows you to construct your own custom metrics. The most common types of
-meters supported by monitoring systems are gauges, counters, and summaries. The following sections build
-an example endpoint, and observes endpoint behavior using these basic meter types.
-
-To register meters, you need a reference to a `MeterRegistry`, which is configured and maintained by the Micrometer
-extension. The `MeterRegistry` can be injected into your application as follows:
-
-[source,java]
-----
-package org.acme.micrometer;
-
-import io.micrometer.core.instrument.MeterRegistry;
-
-import jakarta.ws.rs.GET;
-import jakarta.ws.rs.Path;
-import jakarta.ws.rs.Produces;
-
-@Path("/example")
-@Produces("text/plain")
-public class ExampleResource {
-
- private final MeterRegistry registry;
-
- ExampleResource(MeterRegistry registry) {
- this.registry = registry;
- }
-}
-----
-
-Micrometer maintains an internal mapping between unique metric identifier and tag combinations and specific meter
-instances. Using `register`, `counter`, or other methods to increment counters or record values does not create
-a new instance of a meter unless that combination of identifier and tag/label values hasn't been seen before.
-
-=== Gauges
-
-Gauges measure a value that can increase or decrease over time, like the speedometer on a car. Gauges can be
-useful when monitoring the statistics for a cache or collection. Consider the following simple example that
-observes the size of a list:
-
-[source,java]
-----
- LinkedList list = new LinkedList<>();
-
- // Update the constructor to create the gauge
- ExampleResource(MeterRegistry registry) {
- this.registry = registry;
- registry.gaugeCollectionSize("example.list.size", Tags.empty(), list);
- }
-
- @GET
- @Path("gauge/{number}")
- public Long checkListSize(long number) {
- if (number == 2 || number % 2 == 0) {
- // add even numbers to the list
- list.add(number);
- } else {
- // remove items from the list for odd numbers
- try {
- number = list.removeFirst();
- } catch (NoSuchElementException nse) {
- number = 0;
- }
- }
- return number;
- }
-----
-
-Note that even numbers are added to the list, and odd numbers remove an element from the list.
-
-Start your application in dev mode:
-
-include::{includes}/devtools/dev.adoc[]
-
-Then try the following sequence and look for `example_list_size` in the plain text output:
-
-[source,shell]
-----
-curl http://localhost:8080/example/gauge/1
-curl http://localhost:8080/example/gauge/2
-curl http://localhost:8080/example/gauge/4
-curl http://localhost:8080/q/metrics
-curl http://localhost:8080/example/gauge/6
-curl http://localhost:8080/example/gauge/5
-curl http://localhost:8080/example/gauge/7
-curl http://localhost:8080/q/metrics
-----
-
-It is important to note that gauges are sampled rather than set; there is no record of how the value associated with a
-gauge might have changed between measurements. In this example, the size of the list is observed when the Prometheus
-endpoint is visited.
-
-Micrometer provides a few additional mechanisms for creating gauges. Note that Micrometer does not create strong
-references to the objects it observes by default. Depending on the registry, Micrometer either omits gauges that observe
-objects that have been garbage-collected entirely or uses `NaN` (not a number) as the observed value.
-
-When should you use a Gauge? Only if you can't use something else. Never gauge something you can count. Gauges can be
-less straight-forward to use than counters. If what you are measuring can be counted (because the value always
-increments), use a counter instead.
-
-[NOTE]
-.Management interface
-====
-By default, the metrics are exposed on the main HTTP server.
-You can expose them on a separate network interface and port by enabling the management interface with the
-`quarkus.management.enabled=true` property.
-Refer to the xref:./management-interface-reference.adoc[management interface reference] for more information.
-====
-
-=== Counters
-
-Counters are used to measure values that only increase. In the example below, you will count the number of times you
-test a number to see if it is prime:
-
-[source,java]
-----
- @GET
- @Path("prime/{number}")
- public String checkIfPrime(long number) {
- if (number < 1) {
- return "Only natural numbers can be prime numbers.";
- }
- if (number == 1 || number == 2 || number % 2 == 0) {
- return number + " is not prime.";
- }
-
- if ( testPrimeNumber(number) ) {
- return number + " is prime.";
- } else {
- return number + " is not prime.";
- }
- }
-
- protected boolean testPrimeNumber(long number) {
- // Count the number of times we test for a prime number
- registry.counter("example.prime.number").increment();
- for (int i = 3; i < Math.floor(Math.sqrt(number)) + 1; i = i + 2) {
- if (number % i == 0) {
- return false;
- }
- }
- return true;
- }
-----
-
-It might be tempting to add a label or tag to the counter indicating what value was checked, but remember that each
-unique combination of metric name (`example.prime.number`) and label value produces a unique time series. Using an
-unbounded set of data as label values can lead to a "cardinality explosion", an exponential increase in the creation
-of new time series.
-
-[NOTE]
-====
-Label and tag can be used interchangeably. You may also see "attribute" used in this context in some documentation.
-The gist is each that each label or tag or attribute defines an additional bit of information associated with the
-single numerical measurement that helps you classify, group, or aggregate the measured value later. The Micrometer API
-uses `Tag` as the mechanism for specifying this additional data.
-====
-
-It is possible to add a tag that would convey a little more information, however. Let's adjust our code, and move
-the counter to add some tags to convey additional information.
-
-[source,java]
-----
- @GET
- @Path("prime/{number}")
- public String checkIfPrime(long number) {
- if (number < 1) {
- registry.counter("example.prime.number", "type", "not-natural").increment();
- return "Only natural numbers can be prime numbers.";
- }
- if (number == 1 ) {
- registry.counter("example.prime.number", "type", "one").increment();
- return number + " is not prime.";
- }
- if (number == 2 || number % 2 == 0) {
- registry.counter("example.prime.number", "type", "even").increment();
- return number + " is not prime.";
- }
-
- if ( testPrimeNumber(number) ) {
- registry.counter("example.prime.number", "type", "prime").increment();
- return number + " is prime.";
- } else {
- registry.counter("example.prime.number", "type", "not-prime").increment();
- return number + " is not prime.";
- }
- }
-
- protected boolean testPrimeNumber(long number) {
- for (int i = 3; i < Math.floor(Math.sqrt(number)) + 1; i = i + 2) {
- if (number % i == 0) {
- return false;
- }
- }
- return true;
- }
-----
-
-Looking at the data produced by this counter, you can tell how often a negative number was checked, or the number one,
-or an even number, and so on. Try the following sequence and look for `example_prime_number_total` in the plain text
-output. Note that the `_total` suffix is added when Micrometer applies Prometheus naming conventions to
-`example.prime.number`, the originally specified counter name.
-
-If you did not leave Quarkus running in dev mode, start it again:
-
-include::{includes}/devtools/dev.adoc[]
-
-Then execute the following sequence:
-
-[source,shell]
-----
-curl http://localhost:8080/example/prime/-1
-curl http://localhost:8080/example/prime/0
-curl http://localhost:8080/example/prime/1
-curl http://localhost:8080/example/prime/2
-curl http://localhost:8080/example/prime/3
-curl http://localhost:8080/example/prime/15
-curl http://localhost:8080/q/metrics
-----
-
-When should you use a counter? Only if you are doing something that can not be either timed (or summarized).
-Counters only record a count, which may be all that is needed. However, if you want to understand more about how a
-value is changing, a timer (when the base unit of measurement is time) or a distribution summary might be
-more appropriate.
-
-=== Summaries and Timers
-
-Timers and distribution summaries in Micrometer are very similar. Both allow you to record an observed value, which
-will be aggregated with other recorded values and stored as a sum. Micrometer also increments a counter to indicate the
-number of measurements that have been recorded and tracks the maximum observed value (within a decaying interval).
-
-Distribution summaries are populated by calling the `record` method to record observed values, while timers provide
-additional capabilities specific to working with time and measuring durations. For example, we can use a timer to
-measure how long it takes to calculate prime numbers using one of the `record` methods that wraps the invocation of a
-Supplier function:
-
-[source,java]
-----
- protected boolean testPrimeNumber(long number) {
- Timer timer = registry.timer("example.prime.number.test");
- return timer.record(() -> {
- for (int i = 3; i < Math.floor(Math.sqrt(number)) + 1; i = i + 2) {
- if (number % i == 0) {
- return false;
- }
- }
- return true;
- });
- }
-----
-
-Micrometer will apply Prometheus conventions when emitting metrics for this timer. Prometheus measures time in seconds.
-Micrometer converts measured durations into seconds and includes the unit in the metric name, per convention. After
-visiting the prime endpoint a few more times, look in the plain text output for the following three entries:
-`example_prime_number_test_seconds_count`, `example_prime_number_test_seconds_sum`, and
-`example_prime_number_test_seconds_max`.
-
-If you did not leave Quarkus running in dev mode, start it again:
-
-include::{includes}/devtools/dev.adoc[]
-
-Then execute the following sequence:
-
-[source,shell]
-----
-curl http://localhost:8080/example/prime/256
-curl http://localhost:8080/q/metrics
-curl http://localhost:8080/example/prime/7919
-curl http://localhost:8080/q/metrics
-----
-
-Both timers and distribution summaries can be configured to emit additional statistics, like histogram data,
-precomputed percentiles, or service level objective (SLO) boundaries. Note that the count, sum, and histogram data
-can be re-aggregated across dimensions (or across a series of instances), while precomputed percentile values cannot.
-
-=== Review automatically generated metrics
-
-To view metrics, execute `curl localhost:8080/q/metrics/`
-
-The Micrometer extension automatically times HTTP server requests. Following Prometheus naming conventions for
-timers, look for `http_server_requests_seconds_count`, `http_server_requests_seconds_sum`, and
-`http_server_requests_seconds_max`. Dimensional labels have been added for the requested uri, the HTTP method
-(GET, POST, etc.), the status code (200, 302, 404, etc.), and a more general outcome field.
-
-[source,text]
-----
-# HELP http_server_requests_seconds
-# TYPE http_server_requests_seconds summary
-http_server_requests_seconds_count{method="GET",outcome="SUCCESS",status="200",uri="/example/prime/{number}"} 1.0
-http_server_requests_seconds_sum{method="GET",outcome="SUCCESS",status="200",uri="/example/prime/{number}"} 0.017385896
-# 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="/example/prime/{number}"} 0.017385896
-#
-----
-
-Note that metrics appear lazily, you often won't see any data for your endpoint until
-something tries to access it, etc.
-
-.Ignoring endpoints
-
-You can disable measurement of HTTP endpoints using the `quarkus.micrometer.binder.http-server.ignore-patterns`
-property. This property accepts a comma-separated list of simple regex match patterns identifying URI paths that should
-be ignored. For example, setting `quarkus.micrometer.binder.http-server.ignore-patterns=/example/prime/[0-9]+` will
-ignore a request to `http://localhost:8080/example/prime/7919`. A request to `http://localhost:8080/example/gauge/7919`
-would still be measured.
-
-.URI templates
-
-The micrometer extension will make a best effort at representing URIs containing path parameters in templated form.
-Using examples from above, a request to `http://localhost:8080/example/prime/7919` should appear as an attribute of
-`http_server_requests_seconds_*` metrics with a value of `uri=/example/prime/{number}`.
-
-Use the `quarkus.micrometer.binder.http-server.match-patterns` property if the correct URL can not be determined. This
-property accepts a comma-separated list defining an association between a simple regex match pattern and a replacement
-string. For example, setting
-`quarkus.micrometer.binder.http-server.match-patterns=/example/prime/[0-9]+=/example/{jellybeans}` would use the value
-`/example/{jellybeans}` for the uri attribute any time the requested uri matches `/example/prime/[0-9]+`.
-
-.Exported metrics format
-
-By default, the metrics are exported using the Prometheus format `application/openmetrics-text`,
-you can revert to the former format by specifying the `Accept` request header to `text/plain` (`curl -H "Accept: text/plain" localhost:8080/q/metrics/`).
-
-== Using MeterFilter to configure metrics
-
-Micrometer uses `MeterFilter` instances to customize the metrics emitted by `MeterRegistry` instances.
-The Micrometer extension will detect `MeterFilter` CDI beans and use them when initializing `MeterRegistry`
-instances.
-
-[source,java]
-----
-@Singleton
-public class CustomConfiguration {
-
- @ConfigProperty(name = "deployment.env")
- String deploymentEnv;
-
- /** Define common tags that apply only to a Prometheus Registry */
- @Produces
- @Singleton
- @MeterFilterConstraint(applyTo = PrometheusMeterRegistry.class)
- public MeterFilter configurePrometheusRegistries() {
- return MeterFilter.commonTags(Arrays.asList(
- Tag.of("registry", "prometheus")));
- }
-
- /** Define common tags that apply globally */
- @Produces
- @Singleton
- public MeterFilter configureAllRegistries() {
- return MeterFilter.commonTags(Arrays.asList(
- Tag.of("env", deploymentEnv)));
- }
-
- /** Enable histogram buckets for a specific timer */
- @Produces
- @Singleton
- public MeterFilter enableHistogram() {
- return new MeterFilter() {
- @Override
- public DistributionStatisticConfig configure(Meter.Id id, DistributionStatisticConfig config) {
- if(id.getName().startsWith("myservice")) {
- return DistributionStatisticConfig.builder()
- .percentiles(0.5, 0.95) // median and 95th percentile, not aggregable
- .percentilesHistogram(true) // histogram buckets (e.g. prometheus histogram_quantile)
- .build()
- .merge(config);
- }
- return config;
- }
- };
- }
-}
-----
-
-In this example, a singleton CDI bean will produce two different `MeterFilter` beans. One will be applied only to
-Prometheus `MeterRegistry` instances (using the `@MeterFilterConstraint` qualifier), and another will be applied
-to all `MeterRegistry` instances. An application configuration property is also injected and used as a tag value.
-Additional examples of MeterFilters can be found in the
-link:https://micrometer.io/docs/concepts[official documentation].
-
-== Does Micrometer support annotations?
-
-Micrometer does define two annotations, `@Counted` and `@Timed`, that can be added to methods. The `@Timed` annotation
-will wrap the execution of a method and will emit the following tags in addition to any tags defined on the
-annotation itself: class, method, and exception (either "none" or the simple class name of a detected exception).
-
-Using annotations is limited, as you can't dynamically assign meaningful tag values. Also note that many methods, e.g.
-REST endpoint methods or Vert.x Routes, are counted and timed by the micrometer extension out of the box.
-
-== Using other Registry implementations
-
-If you aren't using Prometheus, you have a few options. Some Micrometer registry implementations
-have been wrapped in
-https://github.com/quarkiverse/quarkiverse-micrometer-registry[Quarkiverse extensions].
-To use the Micrometer StackDriver MeterRegistry, for example, you would use the
-`quarkus-micrometer-registry-stackdriver` extension:
-
-[source,xml,role="primary asciidoc-tabs-target-sync-cli asciidoc-tabs-target-sync-maven"]
-.pom.xml
-----
-
- io.quarkus
- quarkus-micrometer-registry-stackdriver
-
-----
-
-[source,gradle,role="secondary asciidoc-tabs-target-sync-gradle"]
-.build.gradle
-----
-implementation("io.quarkus:quarkus-micrometer-registry-stackdriver")
-----
-
-If the Micrometer registry you would like to use does not yet have an associated extension,
-use the `quarkus-micrometer` extension and bring in the packaged MeterRegistry dependency directly:
-
-[source,xml,role="primary asciidoc-tabs-target-sync-cli asciidoc-tabs-target-sync-maven"]
-.pom.xml
-----
-
- io.quarkus
- quarkus-micrometer
-
-
- com.acme
- custom-micrometer-registry
-
-----
-
-[source,gradle,role="secondary asciidoc-tabs-target-sync-gradle"]
-.build.gradle
-----
-implementation("io.quarkus:quarkus-micrometer")
-implementation("com.acme:custom-micrometer-registry")
-----
-
-You will then need to specify your own provider to configure and initialize the
-MeterRegistry, as discussed in the next section.
-
-== Creating a customized MeterRegistry
-
-Use a custom `@Produces` method to create and configure a customized `MeterRegistry` if you need to.
-
-The following example customizes the line format used for StatsD:
-
-[source,java]
-----
-@Produces
-@Singleton
-public StatsdMeterRegistry createStatsdMeterRegistry(StatsdConfig statsdConfig, Clock clock) {
- // define what to do with lines
- Consumer lineLogger = line -> logger.info(line);
-
- // inject a configuration object, and then customize the line builder
- return StatsdMeterRegistry.builder(statsdConfig)
- .clock(clock)
- .lineSink(lineLogger)
- .build();
-}
-----
-
-This example corresponds to the following instructions in the Micrometer documentation:
-https://micrometer.io/docs/registry/statsD#_customizing_the_metrics_sink
-
-Note that the method returns the specific type of `MeterRegistry` as a `@Singleton`. Use MicroProfile Config
-to inject any configuration attributes you need to configure the registry. Most Micrometer registry extensions,
-like `quarkus-micrometer-registry-statsd`, define a producer for registry-specific configuration objects
-that are integrated with the Quarkus configuration model.
-
-== Support for the MicroProfile Metrics API
-
-If you use the MicroProfile Metrics API in your application, the Micrometer extension will create an adaptive
-layer to map those metrics into the Micrometer registry. Note that naming conventions between the two
-systems is different, so the metrics that are emitted when using MP Metrics with Micrometer will change.
-You can use a `MeterFilter` to remap names or tags according to your conventions.
-
-[source,java]
-----
-@Produces
-@Singleton
-public MeterFilter renameApplicationMeters() {
- final String targetMetric = MPResourceClass.class.getName() + ".mpAnnotatedMethodName";
-
- return MeterFilter() {
- @Override
- public Meter.Id map(Meter.Id id) {
- if (id.getName().equals(targetMetric)) {
- // Drop the scope tag (MP Registry type: application, vendor, base)
- List tags = id.getTags().stream().filter(x -> !"scope".equals(x.getKey()))
- .collect(Collectors.toList());
- // rename the metric
- return id.withName("my.metric.name").replaceTags(tags);
- }
- return id;
- }
- };
-}
-----
-
-Ensure the following dependency is present in your build file if you require the MicroProfile Metrics API:
-
-[source,xml,role="primary asciidoc-tabs-target-sync-cli asciidoc-tabs-target-sync-maven"]
-.pom.xml
-----
-
- org.eclipse.microprofile.metrics
- microprofile-metrics-api
-
-----
-
-[source,gradle,role="secondary asciidoc-tabs-target-sync-gradle"]
-.build.gradle
-----
-implementation("org.eclipse.microprofile.metrics:microprofile-metrics-api")
-----
-
-NOTE: The MP Metrics API compatibility layer will be moved to a different extension in the future.
-
-== Management interface
-
-By default, the metrics are exposed on the main HTTP server.
-You can expose them on a separate network interface and port by setting `quarkus.management.enabled=true` in your application configuration.
-Note that this property is a build-time property.
-The value cannot be overridden at runtime.
-
-If you enable the management interface without customizing the management network interface and port, the metrics are exposed under: `http://0.0.0.0:9000/q/metrics`.
-You can configure the path of each exposed format using:
-[source, properties]
-----
-quarkus.micrometer.export.json.enabled=true # Enable json metrics
-quarkus.micrometer.export.json.path=metrics/json
-quarkus.micrometer.export.prometheus.path=metrics/prometheus
-----
-
-With such a configuration, the json metrics will be available from `http://0.0.0.0:9000/q/metrics/json`.
-The prometheus metrics will be available from `http://0.0.0.0:9000/q/metrics/prometheus`.
-
-Refer to the xref:./management-interface-reference.adoc[management interface reference] for more information.
-
-== Configuration Reference
-
-include::{generated-dir}/config/quarkus-micrometer.adoc[opts=optional, leveloffset=+1]
diff --git a/docs/src/main/asciidoc/native-reference.adoc b/docs/src/main/asciidoc/native-reference.adoc
index 7a95f4deeab4d..c70057b1daf87 100644
--- a/docs/src/main/asciidoc/native-reference.adoc
+++ b/docs/src/main/asciidoc/native-reference.adoc
@@ -3,8 +3,8 @@ This guide is maintained in the main Quarkus repository
and pull requests should be submitted there:
https://github.com/quarkusio/quarkus/tree/main/docs/src/main/asciidoc
////
+[id="native-reference"]
= Native Reference Guide
-
include::_attributes.adoc[]
:categories: native
diff --git a/docs/src/main/asciidoc/redis-reference.adoc b/docs/src/main/asciidoc/redis-reference.adoc
index 55fba59026da1..57d006fe1016f 100644
--- a/docs/src/main/asciidoc/redis-reference.adoc
+++ b/docs/src/main/asciidoc/redis-reference.adoc
@@ -809,7 +809,7 @@ See xref:redis-dev-services.adoc[Redis Dev Service].
=== Enable metrics collection
-Redis client metrics are automatically enabled when the application also uses the xref:micrometer.adoc[`quarkus-micrometer`] extension.
+Redis client metrics are automatically enabled when the application also uses the xref:telemetry-micrometer.adoc[`quarkus-micrometer`] extension.
Micrometer collects the metrics of all the Redis clients implemented by the application.
As an example, if you export the metrics to Prometheus, you will get:
diff --git a/docs/src/main/asciidoc/scheduler-reference.adoc b/docs/src/main/asciidoc/scheduler-reference.adoc
index af1697f72b792..93ea045bf50bd 100644
--- a/docs/src/main/asciidoc/scheduler-reference.adoc
+++ b/docs/src/main/asciidoc/scheduler-reference.adoc
@@ -409,7 +409,7 @@ You can even disable the scheduler for particular < Find or create a counter called `example.prime.number` that has a `type` label with the specified value.
@@ -155,11 +159,12 @@ You can also count how often a number was checked (generally) by aggregating all
== Add a Timer
-Timers are a specialized abstraction for measuring duration. Let's add a timer to measure how long it takes to determine if a number is prime.
+Timers are a specialized abstraction for measuring duration.
+Let's add a timer to measure how long it takes to determine if a number is prime.
[source,java]
----
-include::{code-examples}/telemetry-micrometer-tutorial-example-resource.java[tags=timed;!ignore;!default]
+include::{code-examples}/telemetry-micrometer-tutorial-example-resource.java[tags=primeMethod;counted;timed;!ignore;!default]
----
<1> Find or create a counter called `example.prime.number` that has a `type` label with the specified value.
@@ -182,7 +187,8 @@ Try the following sequence and look for the following entries in the plain text
- `example_prime_number_test_seconds_count` -- how many times the method was called
- `example_prime_number_test_seconds_sum` -- the total duration of all method calls
-- `example_prime_number_test_seconds_max` -- the maximum observed duration within a decaying interval. This value will return to 0 if the method is not invoked frequently.
+- `example_prime_number_test_seconds_max` -- the maximum observed duration within a decaying interval.
+This value will return to 0 if the method is not invoked frequently.
[source,shell]
----
@@ -192,14 +198,54 @@ curl http://localhost:8080/example/prime/7919
curl http://localhost:8080/q/metrics
----
-Looking at the dimensional data produced by this counter, you can use the sum and the count to calculate how long (on average) it takes to determine if a number is prime. Using the dimensional label, you might be able to understand if there is a significant difference in duration for numbers that are prime when compared with numbers that are not.
+Looking at the dimensional data produced by this counter, you can use the sum and the count to calculate how long (on average) it takes to determine if a number is prime.
+Using the dimensional label, you might be able to understand if there is a significant difference in duration for numbers that are prime when compared with numbers that are not.
+
+== Add a Gauge
+
+Gauges measure a value that can increase or decrease over time, like the speedometer on a car.
+The value of a gauge is not accumulated, it is observed at collection time.
+Use a gauge to observe the size of a collection, or the value returned from a function.
+
+[source,java]
+----
+include::{code-examples}/telemetry-micrometer-tutorial-example-resource.java[tags=ctor;gauge]
+----
+
+<1> Define list that will hold arbitrary numbers.
+<2> Register a gauge that will track the size of the list.
+<3> Create a REST endpoint to populate the list.
+Even numbers are added to the list, and odd numbers remove an element from the list.
+
+=== Review collected metrics
+
+If you did not leave Quarkus running in dev mode, start it again:
+
+include::{includes}/devtools/dev.adoc[]
+
+Then try the following sequence and look for `example_list_size` in the plain text output:
+
+[source,shell]
+----
+curl http://localhost:8080/example/gauge/1
+curl http://localhost:8080/example/gauge/2
+curl http://localhost:8080/example/gauge/4
+curl http://localhost:8080/q/metrics
+curl http://localhost:8080/example/gauge/6
+curl http://localhost:8080/example/gauge/5
+curl http://localhost:8080/example/gauge/7
+curl http://localhost:8080/q/metrics
+----
+
:sectnums!:
== Summary
Congratulations!
-You have created a project that uses the Micrometer and Prometheus Meter Registry extensions to collect metrics. You've observed some of the metrics that Quarkus captures automatically, and have added a `Counter` and `Timer` that are unique to the application. You've also added dimensional labels to metrics, and have observed how those labels shape the data emitted by the prometheus endpoint.
+You have created a project that uses the Micrometer and Prometheus Meter Registry extensions to collect metrics.
+You've observed some of the metrics that Quarkus captures automatically, and have added a `Counter` and `Timer` that are unique to the application.
+You've also added dimensional labels to metrics, and have observed how those labels shape the data emitted by the prometheus endpoint.
diff --git a/docs/src/main/asciidoc/telemetry-micrometer.adoc b/docs/src/main/asciidoc/telemetry-micrometer.adoc
new file mode 100644
index 0000000000000..b6d6e004b0a36
--- /dev/null
+++ b/docs/src/main/asciidoc/telemetry-micrometer.adoc
@@ -0,0 +1,669 @@
+////
+This guide is maintained in the main Quarkus repository
+and pull requests should be submitted there:
+https://github.com/quarkusio/quarkus/tree/main/docs/src/main/asciidoc
+////
+[id=telemetry-micrometer]
+= Micrometer Metrics
+include::_attributes.adoc[]
+:categories: observability
+:summary: Use Micrometer to collect metrics produced by Quarkus, its extensions, and your application.
+:base-units: https://github.com/micrometer-metrics/micrometer/blob/main/micrometer-core/src/main/java/io/micrometer/core/instrument/binder/BaseUnits.java
+:concepts: https://micrometer.io/docs/concepts
+
+Micrometer provides an abstraction layer for metrics collection.
+It defines an API for basic meter types, like counters, gauges, timers, and distribution summaries, along with a `MeterRegistry` API that generalizes metrics collection and propagation for different backend monitoring systems.
+
+IMPORTANT: Micrometer is the recommended approach to metrics for Quarkus.
+
+NOTE: By default, the metrics are exposed on the main HTTP server.
+If you would like to surface metrics from a separate management port, see the <> section.
+
+== Micrometer and monitoring system extensions
+
+Quarkus Micrometer extensions are structured in the same way as the Micrometer project.
+The `quarkus-micrometer` extension provides core Micrometer support and runtime integration.
+Other Quarkus and Quarkiverse extensions use the Quarkus Micrometer extension to provide support for other monitoring systems.
+
+Quarkus extensions:
+
+- micrometer
+- micrometer-registry-prometheus
+
+link:https://github.com/quarkiverse/quarkus-micrometer-registry[Quarkiverse extensions] (may be incomplete):
+
+- micrometer-registry-azure-monitor
+- micrometer-registry-datadog
+- micrometer-registry-graphite
+- micrometer-registry-influx
+- micrometer-registry-jmx
+- micrometer-registry-newrelic-telemetry
+- micrometer-registry-otlp
+- micrometer-registry-signalfx
+- micrometer-registry-stackdriver
+- micrometer-registry-statsd
+
+To add support for Prometheus metrics to your application, for example, use the `micrometer-registry-prometheus` extension.
+It will bring in the Quarkus Micrometer extension and Micrometer core libraries as dependencies.
+
+Add the extension to your project using following command (from your project directory):
+
+:add-extension-extensions: micrometer-registry-prometheus
+include::{includes}/devtools/extension-add.adoc[]
+
+This will add the following to your build file:
+
+[source,xml,role="primary asciidoc-tabs-target-sync-cli asciidoc-tabs-target-sync-maven"]
+.pom.xml
+----
+
+ io.quarkus
+ quarkus-micrometer-registry-prometheus
+
+----
+
+[source,gradle,role="secondary asciidoc-tabs-target-sync-gradle"]
+.build.gradle
+----
+implementation("io.quarkus:quarkus-micrometer-registry-prometheus")
+----
+
+And you're all set!
+
+A similar process applies for other meter registry extensions.
+To use the Micrometer StackDriver MeterRegistry, for example, you would use the
+`quarkus-micrometer-registry-stackdriver` extension from the Quarkiverse:
+
+:add-extension-extensions: io.quarkiverse.micrometer.registry:quarkus-micrometer-registry-stackdriver
+include::{includes}/devtools/extension-add.adoc[]
+
+[source,xml,role="primary asciidoc-tabs-target-sync-cli asciidoc-tabs-target-sync-maven"]
+.pom.xml
+----
+
+ io.quarkiverse.micrometer.registry
+ quarkus-micrometer-registry-stackdriver
+
+----
+
+[source,gradle,role="secondary asciidoc-tabs-target-sync-gradle"]
+.build.gradle
+----
+implementation("io.quarkiverse.micrometer.registry:quarkus-micrometer-registry-stackdriver")
+----
+
+=== Other registry implementations
+
+If the Micrometer registry you would like to use does not yet have an associated extension,
+use the `quarkus-micrometer` extension and bring in the Micrometer meter registry dependency directly:
+
+[source,xml,role="primary asciidoc-tabs-target-sync-cli asciidoc-tabs-target-sync-maven"]
+.pom.xml
+----
+
+ io.quarkus
+ quarkus-micrometer
+
+
+ com.acme
+ custom-micrometer-registry
+ ...
+
+----
+
+[source,gradle,role="secondary asciidoc-tabs-target-sync-gradle"]
+.build.gradle
+----
+implementation("io.quarkus:quarkus-micrometer")
+implementation("com.acme:custom-micrometer-registry")
+----
+
+You will then need to specify your own provider to configure and initialize the
+MeterRegistry, as discussed in the next section.
+
+=== Create a customized MeterRegistry
+
+Use a custom `@Produces` method to create and configure a your own `MeterRegistry` if you need to.
+
+The following example customizes the line format used for StatsD:
+
+[source,java]
+----
+@Produces
+@Singleton // <1>
+public StatsdMeterRegistry createStatsdMeterRegistry(StatsdConfig statsdConfig, Clock clock) { // <2>
+ // define what to do with lines
+ Consumer lineLogger = line -> logger.info(line);
+
+ // inject a configuration object, and then customize the line builder
+ return StatsdMeterRegistry.builder(statsdConfig)
+ .clock(clock)
+ .lineSink(lineLogger)
+ .build();
+}
+----
+
+<1> The method returns a `@Singleton`.
+<2> The method returns the specific type of `MeterRegistry`
+
+This example corresponds to the following instructions in the Micrometer documentation: link:https://micrometer.io/docs/registry/statsD#_customizing_the_metrics_sink[Micrometer StatsD: Customizing the Metrics Sink]
+
+Use MicroProfile Config to inject any configuration attributes you need to configure the registry.
+Most Micrometer registry extensions, like `quarkus-micrometer-registry-statsd`, provide registry-specific configuration objects that are integrated with the Quarkus configuration model.
+The link:https://github.com/quarkiverse/quarkus-micrometer-registry[Quarkiverse GitHub Repository] can be a useful implementation reference.
+
+== Create your own metrics
+
+Metrics data is used in the aggregate to observe how data changes over time.
+This data is used for trend analysis, anomaly detection, and alerting.
+Data is stored by backend monitoring systems in time series databases, with new values appended to the end of the series.
+
+NOTE: Metrics are constructed lazily. You may not see any data for the metric you're looking for until you've performed an action that will create it, like visiting an endpoint.
+
+=== Naming conventions
+
+Meter names should use dots to separate segments, `a.name.like.this`.
+Micrometer applies naming conventions to convert registered meter names to match the expectations of backend monitoring systems.
+
+Given the following declaration of a timer: `registry.timer("http.server.requests")`, applied naming conventions will emit the following metrics for different monitoring systems:
+
+- Prometheus: `http_server_requests_duration_seconds`
+- Atlas: `httpServerRequests`
+- Graphite: `http.server.requests`
+- InfluxDB: `http_server_requests`
+
+[[define-tags]]
+=== Define dimensions for aggregation
+
+Metrics, single numerical measurements, often have additional data captured with them. This ancillary data is used to group or aggregate metrics for analysis.
+The Micrometer API refers to this dimensional data as tags, but you may it referred to as "labels" or "attributes" in other documentation sources.
+
+Micrometer is built primariliy for backend monitoring systems that support dimensional data (metric names that are enchriched with key/value pairs).
+For heirarchical systems that only support a flat metric name, Micrometer will flatten the set of key/value pairs (sorted by key) and add them to the name.
+
+Tags can be specified when a meter is registered with a `MeterRegistry` or using a <>.
+
+See the Micrometer documentation for additional advice on link:{concepts}#_tag_naming[tag naming].
+
+IMPORTANT: Each unique combination of metric name and dimension produces a unique time series.
+Using an unbounded set of dimensional data can lead to a "cardinality explosion", an exponential increase in the creation of new time series.
+
+=== Obtain a reference to a MeterRegistry
+
+To register meters, you need a reference to a `MeterRegistry`, which is configured and maintained by the Micrometer extension.
+
+Use one of the following methods to obtain a reference to a `MeterRegistry`:
+
+1. Use CDI Constructor injection:
++
+[source,java]
+----
+package org.acme.micrometer;
+
+import io.micrometer.core.instrument.MeterRegistry;
+
+import jakarta.ws.rs.GET;
+import jakarta.ws.rs.Path;
+import jakarta.ws.rs.Produces;
+
+@Path("/example")
+@Produces("text/plain")
+public class ExampleResource {
+
+ private final MeterRegistry registry;
+
+ ExampleResource(MeterRegistry registry) {
+ this.registry = registry;
+ }
+}
+----
+
+2. Use a `MeterRegistry` member variable and use `@Inject`:
++
+[source,java]
+----
+ @Inject
+ MeterRegistry registry;
+----
+
+3. Use the global `MeterRegistry`:
++
+[source,java]
+----
+ MeterRegistry registry = Metrics.globalRegistry;
+----
+
+=== Gauges
+
+Gauges measure a value that can increase or decrease over time, like the speedometer on a car.
+Gauges can be useful when monitoring the statistics for a cache or collection.
+
+Gauge values are sampled rather than set;
+there is no record of how the value associated with a gauge may have changed between measurements.
+
+Micrometer provides a few mechanisms for creating gauges:
+
+1. Wrap construction of a collection to monitor its size:
++
+[source,java]
+----
+List list = registry.gaugeCollectionSize("fantastic.list", // <1>
+ Tags.of("key", "value") // optional <2>
+ new ArrayList<>()); // <3>
+----
++
+<1> Create a new gauge, `list.size`, using the dot-separated convention.
+<2> Associate <> with the gauge.
+Gauge tag values are constant, and must be assigned at construction time.
+<3> Construct the array list whose size should be observed.
+
+2. Use a builder to create a Gauge that will call a function:
++
+[source,java]
+----
+Gauge.builder("jvm.threads.peak", threadBean, ThreadMXBean::getPeakThreadCount) // <1>
+ .baseUnit(BaseUnits.THREADS) // optional <2>
+ .description("The peak live thread count...") // optional <3>
+ .tags("key", "value") // optional <4>
+ .register(registry); // <5>
+----
++
+<1> Create a new gauge called `jvm.threads.peak` that will call `getPeakThreadCount` on `threadBean`, an instance of `ThreadMXBean`
+<2> Define the base unit, see link:{base-units}[BaseUnits.java] for predefined values.
+<3> Provide a description of the Gauge
+<4> Associate <> with the gauge
+<5> Register the Gauge with the MeterRegistry
+
+See link:{concepts}#_gauges[Gauges] in the Micrometer documentation for more information and examples.
+Of note are two special cases: `TimeGauge` for measuring time, and a `MultiGauge` for reporting several criteria together.
+
+NOTE: Micrometer does not create strong references to the objects it observes by default.
+Depending on the registry, Micrometer either omits gauges that observe
+objects that have been garbage-collected entirely or uses `NaN` (not a number) as the observed value.
+
+When should you use a gauge?
+Only use a gauge when you can't use something else.
+Gauges can be less straight-forward to use than other meters.
+If what you are measuring can be counted (because the value always increments), use a counter instead.
+
+=== Counters
+
+Counters measure values that only increase.
+Use one of the methods below to create a counter.
+
+1. Use a convenience method on the `MeterRegistry`:
++
+[source,java]
+----
+registry.counter("example.prime.number", "type", "prime"); // <1> <2>
+----
++
+<1> `example.prime.number` is the counter name.
+<2> `type` is a dimensional tag with value `prime`.
+
+
+2. Use `Counter.builder` to provide a description and units:
++
+[source,java]
+----
+Counter.builder("count.me") // <1>
+ .baseUnit("beans") // optional <2>
+ .description("a description") // optional <3>
+ .tags("region", "test") // optional <4>
+ .register(registry);
+----
++
+<1> Create a new counter called `count.me`
+<2> Define a custom base unit. See link:{base-units}[BaseUnits.java] for predefined values.
+<3> Provide a description for the counter
+<4> Associate <> with the counter
+
+3. <> a method
++
+[source,java]
+----
+@Counted(value = "counted.method", extraTags = { "extra", "annotated" }) // <1> <2>
+void countThisMethod(){
+ ...
+}
+----
++
+<1> A CDI interceptor will create and register a counter called `counted.method`
+<2> The interceptor-created counter will have the "extra" dimension tag with value "annotated"
+
+See link:{concepts}#_counters[Counters] in the Micrometer documentation for more information and examples, including the less common `FunctionCounter` that can be used to measure the result returned by an always increasing function.
+
+When should you use a counter?
+Use a counter if you are doing something that can not be either timed or summarized.
+If you want to understand more about how a value is changing,
+a timer (when the base unit of measurement is time) or a distribution summary might be
+more appropriate.
+
+=== Summaries and Timers
+
+Timers and distribution summaries in Micrometer are very similar. Both meters record data, and can capture additional histogram or percentile data. While distribution summaries can be use for arbitrary types of data, timers are optimized for measuring time and durations.
+
+Timers and distribution summaries store at least three values internally:
+
+- the aggregation of all recorded values as a sum
+- the number of values that have been recorded (a counter)
+- the highest value seen within a decaying time window (a gauge).
+
+==== Create a distribution summary
+
+Use a distribution summary to record a value, not time.
+Use one of the following methods to create a distribution summary.
+
+1. Use a convenience method on the `MeterRegistry`:
++
+[source,java]
+----
+registry.summary("bytes.written", "protocol", "http"); // <1> <2>
+----
++
+<1> `bytes.written` is the summary name
+<2> `protocol` is a dimensional tag with value `http`.
+
+
+2. Use `DistributionSummary.builder` to provide a description and units:
++
+[source,java]
+----
+DistributionSummary.builder("response.size") // <1>
+ .baseUnit("bytes") // optional <2>
+ .description("a description") // optional <3>
+ .tags("protocol", "http") // optional <4>
+ .register(registry);
+----
++
+<1> Create a new distribution summary called `response.size`
+<2> Use `bytes` as a base unit. See link:{base-units}[BaseUnits.java] for predefined values.
+<3> Provide a description for the distribution summary
+<4> Associate <> with the distribution summary
+
+==== Create a timer
+
+Timers measure short-duration latencies and how often they occur. Negative values are not supported, and longer durations could cause an overflow of the total time (Long.MAX_VALUE nanoseconds (292.3 years)).
+
+Use one of the following methods to construct a timer.
+
+1. Use a convenience method on the `MeterRegistry`:
++
+[source,java]
+----
+registry.timer("fabric.selection", "primary", "blue"); // <1> <2>
+----
++
+<1> `fabric.selection` is the summary name
+<2> `primary` is a dimensional tag with value `blue`.
+
+
+2. Use `Timer.builder` to provide a description and units:
++
+[source,java]
+----
+Timer.builder("my.timer") // <1> <2>
+ .description("description ") // optional <3>
+ .tags("region", "test") // optional <4>
+ .register(registry);
+----
++
+<1> Create a new timer called `my.timer`
+<2> Timers measure time, and will convert it into the units required by the monitoring backend
+<3> Provide a description for the distribution summary
+<4> Associate <> with the timer
+
+3. <> a method
++
+[source,java]
+----
+@Timed(value = "call", extraTags = {"region", "test"}) // <1> <2>
+----
++
+<1> A CDI interceptor will create and register a timer called `call`
+<2> The interceptor-created timer will have the "region" dimension tag with value "test"
+
+==== Measure durations with Timers
+
+Micrometer provides the following convenience mechanisms for recording durations.
+
+1. Wrap the invocation of a `Runnable`:
++
+[source,java]
+----
+timer.record(() -> noReturnValue());
+----
+
+2. Wrap the invocation of a `Callable`:
++
+[source,java]
+----
+timer.recordCallable(() -> returnValue());
+----
+
+3. Create a wrapped `Runnable` for repeated invocation:
++
+[source,java]
+----
+Runnable r = timer.wrap(() -> noReturnValue());
+----
+
+4. Create a wrapped `Callable` for repeated invocation:
++
+[source,java]
+----
+Callable c = timer.wrap(() -> returnValue());
+----
+
+5. Use a `Sample` for more complex code paths:
++
+[source,java]
+----
+Sample sample = Timer.start(registry); // <1>
+
+doStuff; // <2>
+
+sample.stop(registry.timer("my.timer", "response", response.status())); // <3>
+----
++
+<1> We create a sample, which records the start of the timer.
+<2> The sample can be passed along as context
+<3> We can choose the timer when the sample is stopped. This example uses a response status as a tag identifying the timer, which won't be known until processing is complete.
+
+==== Histograms and percentiles
+
+Both timers and distribution summaries can be configured to emit additional statistics, like histogram data, precomputed percentiles, or service level objective (SLO) boundaries.
+See link:{concepts}#_timers[Timers] and link:{concepts}#_distribution_summaries[Distribution Summaries] in the Micrometer documentation for more information and examples, including memory footprint estimation for both types.
+
+[IMPORTANT]
+====
+The count, sum, and histogram data associated with timers and distribution summaries can be re-aggregated across dimensions (or across a series of instances).
+
+Precomputed percentile values can not. Percentiles are unique to each dataset (the 90th percentile of this collection of measurements).
+====
+
+== Automatically generated metrics
+
+The Micrometer extension automatically times HTTP server requests. Following Prometheus naming conventions for
+timers, look for `http_server_requests_seconds_count`, `http_server_requests_seconds_sum`, and
+`http_server_requests_seconds_max`. Dimensional labels have been added for the requested uri, the HTTP method
+(GET, POST, etc.), the status code (200, 302, 404, etc.), and a more general outcome field.
+
+.Ignoring endpoints
+
+You can disable measurement of HTTP endpoints using the `quarkus.micrometer.binder.http-server.ignore-patterns`
+property.
+This property accepts a comma-separated list of simple regex match patterns identifying URI paths that should
+be ignored.
+For example, setting `quarkus.micrometer.binder.http-server.ignore-patterns=/example/prime/[0-9]+` will
+ignore a request to `http://localhost:8080/example/prime/7919`.
+A request to `http://localhost:8080/example/gauge/7919`
+would still be measured.
+
+.URI templates
+
+The micrometer extension will make a best effort at representing URIs containing path parameters in templated form.
+Using examples from above, a request to `http://localhost:8080/example/prime/7919` should appear as an attribute of
+`http_server_requests_seconds_*` metrics with a value of `uri=/example/prime/{number}`.
+
+Use the `quarkus.micrometer.binder.http-server.match-patterns` property if the correct URL can not be determined.
+This property accepts a comma-separated list defining an association between a simple regex match pattern and a replacement
+string.
+For example, setting
+`quarkus.micrometer.binder.http-server.match-patterns=/example/prime/[0-9]+=/example/{jellybeans}` would use the value
+`/example/{jellybeans}` for the uri attribute any time the requested uri matches `/example/prime/[0-9]+`.
+
+.Exported metrics format
+
+By default, the metrics are exported using the Prometheus format `application/openmetrics-text`,
+you can revert to the former format by specifying the `Accept` request header to `text/plain` (`curl -H "Accept: text/plain" localhost:8080/q/metrics/`).
+
+[[meter-filter]]
+== Use `MeterFilter` to customize emitted tags and metrics
+
+Micrometer uses `MeterFilter` instances to customize the metrics emitted by `MeterRegistry` instances.
+The Micrometer extension will detect `MeterFilter` CDI beans and use them when initializing `MeterRegistry`
+instances.
+
+[source,java]
+----
+@Singleton
+public class CustomConfiguration {
+
+ @ConfigProperty(name = "deployment.env")
+ String deploymentEnv;
+
+ /** Define common tags that apply only to a Prometheus Registry */
+ @Produces
+ @Singleton
+ @MeterFilterConstraint(applyTo = PrometheusMeterRegistry.class)
+ public MeterFilter configurePrometheusRegistries() {
+ return MeterFilter.commonTags(Arrays.asList(
+ Tag.of("registry", "prometheus")));
+ }
+
+ /** Define common tags that apply globally */
+ @Produces
+ @Singleton
+ public MeterFilter configureAllRegistries() {
+ return MeterFilter.commonTags(Arrays.asList(
+ Tag.of("env", deploymentEnv)));
+ }
+
+ /** Enable histogram buckets for a specific timer */
+ @Produces
+ @Singleton
+ public MeterFilter enableHistogram() {
+ return new MeterFilter() {
+ @Override
+ public DistributionStatisticConfig configure(Meter.Id id, DistributionStatisticConfig config) {
+ if(id.getName().startsWith("myservice")) {
+ return DistributionStatisticConfig.builder()
+ .percentiles(0.5, 0.95) // median and 95th percentile, not aggregable
+ .percentilesHistogram(true) // histogram buckets (e.g. prometheus histogram_quantile)
+ .build()
+ .merge(config);
+ }
+ return config;
+ }
+ };
+ }
+}
+----
+
+In this example, a singleton CDI bean will produce two different `MeterFilter` beans.
+One will be applied only to
+Prometheus `MeterRegistry` instances (using the `@MeterFilterConstraint` qualifier), and another will be applied
+to all `MeterRegistry` instances.
+An application configuration property is also injected and used as a tag value.
+Additional examples of MeterFilters can be found in the
+link:https://micrometer.io/docs/concepts[official documentation].
+
+[[annotations]]
+== Does Micrometer support annotations?
+
+Micrometer does define two annotations, `@Counted` and `@Timed`, that can be added to methods.
+The `@Timed` annotation will wrap the execution of a method and will emit the following tags
+in addition to any tags defined on the annotation itself:
+class, method, and exception (either "none" or the simple class name of a detected exception).
+
+Using annotations is limited, as you can't dynamically assign meaningful tag values.
+Also note that many methods, e.g. REST endpoint methods or Vert.x Routes, are counted and timed by the micrometer extension out of the box.
+
+== Support for the MicroProfile Metrics API
+
+If you use the MicroProfile Metrics API in your application,
+the Micrometer extension will create an adaptive layer to map those metrics into the Micrometer registry.
+Note that naming conventions between the two systems is different, so the metrics that are emitted when using MP Metrics with Micrometer will change.
+
+Use a `MeterFilter` to remap names or tags according to your conventions.
+
+[source,java]
+----
+@Produces
+@Singleton
+public MeterFilter renameApplicationMeters() {
+ final String targetMetric = MPResourceClass.class.getName() + ".mpAnnotatedMethodName";
+
+ return MeterFilter() {
+ @Override
+ public Meter.Id map(Meter.Id id) {
+ if (id.getName().equals(targetMetric)) {
+ // Drop the scope tag (MP Registry type: application, vendor, base)
+ List tags = id.getTags().stream().filter(x -> !"scope".equals(x.getKey()))
+ .collect(Collectors.toList());
+ // rename the metric
+ return id.withName("my.metric.name").replaceTags(tags);
+ }
+ return id;
+ }
+ };
+}
+----
+
+Ensure the following dependency is present if you require the MicroProfile Metrics API:
+
+[source,xml,role="primary asciidoc-tabs-target-sync-cli asciidoc-tabs-target-sync-maven"]
+.pom.xml
+----
+
+ org.eclipse.microprofile.metrics
+ microprofile-metrics-api
+
+----
+
+[source,gradle,role="secondary asciidoc-tabs-target-sync-gradle"]
+.build.gradle
+----
+implementation("org.eclipse.microprofile.metrics:microprofile-metrics-api")
+----
+
+NOTE: The MP Metrics API compatibility layer may be moved to a different extension in the future.
+
+[[management-interface]]
+== Management interface
+
+By default, the metrics are exposed on the main HTTP server.
+
+You can expose them on a separate network interface and port by setting `quarkus.management.enabled=true` in your application configuration.
+Note that this property is a build-time property.
+The value cannot be overridden at runtime.
+
+If you enable the management interface without customizing the management network interface and port, the metrics are exposed under: `http://0.0.0.0:9000/q/metrics`.
+
+You can configure the path of each exposed format using:
+[source, properties]
+----
+quarkus.micrometer.export.json.enabled=true # Enable json metrics
+quarkus.micrometer.export.json.path=metrics/json
+quarkus.micrometer.export.prometheus.path=metrics/prometheus
+----
+
+With such a configuration, the json metrics will be available from `http://0.0.0.0:9000/q/metrics/json`.
+The prometheus metrics will be available from `http://0.0.0.0:9000/q/metrics/prometheus`.
+
+Refer to the xref:./management-interface-reference.adoc[management interface reference] for more information.
+
+== Configuration Reference
+
+include::{generated-dir}/config/quarkus-micrometer.adoc[opts=optional, leveloffset=+1]
diff --git a/docs/src/main/java/io/quarkus/docs/generation/YamlMetadataGenerator.java b/docs/src/main/java/io/quarkus/docs/generation/YamlMetadataGenerator.java
index f7b6a52138f0f..582b5c7b00f24 100644
--- a/docs/src/main/java/io/quarkus/docs/generation/YamlMetadataGenerator.java
+++ b/docs/src/main/java/io/quarkus/docs/generation/YamlMetadataGenerator.java
@@ -47,7 +47,7 @@
*
*/
public class YamlMetadataGenerator {
- private static Errors errors = new Errors();
+ private static Messages messages = new Messages();
final static String INCL_ATTRIBUTES = "include::_attributes.adoc[]\n";
final static String YAML_FRONTMATTER = "---\n";
@@ -117,8 +117,8 @@ public void writeYamlFiles() throws StreamWriteException, DatabindException, IOE
om.writeValue(targetDir.resolve("indexByType.yaml").toFile(), index);
om.writeValue(targetDir.resolve("indexByFile.yaml").toFile(), metadata);
- om.writeValue(targetDir.resolve("errorsByType.yaml").toFile(), errors);
- om.writeValue(targetDir.resolve("errorsByFile.yaml").toFile(), errors.messagesByFile);
+ om.writeValue(targetDir.resolve("errorsByType.yaml").toFile(), messages);
+ om.writeValue(targetDir.resolve("errorsByFile.yaml").toFile(), messages.allByFile());
}
public Index generateIndex() throws IOException {
@@ -130,7 +130,7 @@ public Index generateIndex() throws IOException {
throw new IllegalStateException(
String.format("Target directory (%s) does not exist. Exiting.%n", targetDir.toAbsolutePath()));
}
- errors.setRoot(srcDir);
+ messages.setRoot(srcDir);
Options options = Options.builder()
.docType("book")
@@ -147,7 +147,7 @@ public Index generateIndex() throws IOException {
try {
str = Files.readString(path);
} catch (IOException e) {
- errors.record("ioexception", path);
+ messages.record("ioexception", path);
return;
}
@@ -162,11 +162,11 @@ public Index generateIndex() throws IOException {
// it should be part of the document header
int includeAttr = str.indexOf(INCL_ATTRIBUTES);
if (includeAttr < 0) {
- errors.record("missing-attributes", path);
+ messages.record("missing-attributes", path);
} else {
String prefix = str.substring(0, includeAttr);
if (prefix.contains("\n\n")) {
- errors.record("detached-attributes", path);
+ messages.record("detached-attributes", path);
}
}
@@ -174,7 +174,7 @@ public Index generateIndex() throws IOException {
int documentHeaderEnd = str.indexOf("\n\n", titlePos);
String documentHeader = str.substring(0, documentHeaderEnd);
if (documentHeader.contains(":toc:")) {
- errors.record("toc", path);
+ messages.record("toc", path);
}
String title = doc.getDoctitle();
@@ -200,18 +200,18 @@ public Index generateIndex() throws IOException {
if (content.isPresent()) {
index.add(new DocMetadata(title, path, summaryString, categories, keywords, id));
} else {
- errors.record("empty-preamble", path);
+ messages.record("empty-preamble", path);
index.add(new DocMetadata(title, path, summaryString, categories, keywords, id));
}
} else {
- errors.record("missing-preamble", path);
+ messages.record("missing-preamble", path);
summaryString = getSummary(summary, Optional.empty());
index.add(new DocMetadata(title, path, summaryString, categories, keywords, id));
}
long spaceCount = summaryString.chars().filter(c -> c == (int) ' ').count();
if (spaceCount > 26) {
- errors.record("summary-too-long", path);
+ messages.record("summary-too-long", path);
}
});
}
@@ -297,11 +297,14 @@ public static void addAll(Set set, Object source, Path path) {
return;
}
for (String c : source.toString().split("\\s*,\\s*")) {
- try {
- Category cat = Category.valueOf(c.toLowerCase().replace("-", "_"));
- set.add(cat);
- } catch (IllegalArgumentException ex) {
- errors.record("unknown-category", path, "Unknown category: " + c);
+ String lower = c.toLowerCase();
+ Optional match = Stream.of(Category.values())
+ .filter(cat -> cat.id.equals(lower))
+ .findFirst();
+ if (match.isEmpty()) {
+ messages.record("unknown-category", path, "Unknown category: " + c);
+ } else {
+ set.add(match.get());
}
}
}
@@ -329,15 +332,17 @@ enum Type {
}
}
- private static class Errors {
+ private static class Messages {
String root;
- Map> messagesByFile = new TreeMap<>();
+ Map> errorsByFile = new TreeMap<>();
+ Map> warningsByFile = new TreeMap<>();
public final Map> errors = new TreeMap<>();
void setRoot(Path root) {
this.root = root.toString();
errors.clear();
- messagesByFile.clear();
+ errorsByFile.clear();
+ warningsByFile.clear();
}
void record(String errorKey, Path path) {
@@ -349,10 +354,24 @@ void record(String errorKey, Path path, String message) {
if (message == null) {
message = getMessageforKey(errorKey);
}
- messagesByFile.computeIfAbsent(filename, k -> new ArrayList<>()).add(message);
+
+ if (isWarning(errorKey)) {
+ warningsByFile.computeIfAbsent(filename, k -> new ArrayList<>()).add(message);
+ } else {
+ errorsByFile.computeIfAbsent(filename, k -> new ArrayList<>()).add(message);
+ }
errors.computeIfAbsent(errorKey, k -> new HashSet<>()).add(filename);
}
+ private boolean isWarning(String errorKey) {
+ switch (errorKey) {
+ case "missing-id":
+ case "not-diataxis-type":
+ return true;
+ }
+ return false;
+ }
+
private String getMessageforKey(String errorKey) {
switch (errorKey) {
case "missing-attributes":
@@ -377,8 +396,26 @@ private String getMessageforKey(String errorKey) {
return errorKey;
}
+ public Map allByFile() {
+ Map result = new TreeMap<>();
+ errorsByFile.forEach((k, v) -> {
+ FileMessages mr = result.computeIfAbsent(k, x -> new FileMessages());
+ mr.errors = v;
+ });
+ warningsByFile.forEach((k, v) -> {
+ FileMessages mr = result.computeIfAbsent(k, x -> new FileMessages());
+ mr.warnings = v;
+ });
+
+ return result;
+ }
+
Map> errorsByFile() {
- return messagesByFile;
+ return errorsByFile;
+ }
+
+ Map> warningsByFile() {
+ return warningsByFile;
}
}
@@ -407,8 +444,8 @@ public Map metadataByFile() {
}
// convenience
- public Map> errorsByFile() {
- return errors.errorsByFile();
+ public Map messagesByFile() {
+ return messages.allByFile();
}
}
@@ -464,20 +501,20 @@ static class DocMetadata implements Comparable {
this.type = Type.reference;
} else {
this.type = Type.other;
- errors.record("not-diataxis-type", path);
+ messages.record("not-diataxis-type", path);
}
if (id == null) {
- errors.record("missing-id", path);
+ messages.record("missing-id", path);
} else if (type != Type.other && !id.endsWith(type.suffix)) {
- errors.record("incorrect-id", path,
+ messages.record("incorrect-id", path,
String.format(
"The document id (%s) does not end with the correct suffix, should end with '-%s'%n",
id, type.suffix));
}
if (this.categories.isEmpty()) {
- errors.record("missing-categories", path);
+ messages.record("missing-categories", path);
}
}
@@ -520,4 +557,25 @@ public int compareTo(DocMetadata that) {
return this.title.compareTo(that.title);
}
}
+
+ @JsonInclude(value = Include.NON_EMPTY)
+ public static class FileMessages {
+ Collection errors;
+ Collection warnings;
+
+ Collection warnings() {
+ return warnings == null ? List.of() : warnings;
+ }
+
+ Collection errors() {
+ return errors == null ? List.of() : errors;
+ }
+
+ public boolean listAll(StringBuilder sb) {
+ errors().forEach(e -> sb.append(" [ ERR] ").append(e).append("\n"));
+ warnings().forEach(e -> sb.append(" [WARN] ").append(e).append("\n"));
+ sb.append("\n");
+ return !errors().isEmpty();
+ }
+ }
}
diff --git a/docs/src/test/java/io/quarkus/docs/YamlMetadataGeneratorTest.java b/docs/src/test/java/io/quarkus/docs/YamlMetadataGeneratorTest.java
index 36ecc3c7cc85b..d5d99f40d2506 100644
--- a/docs/src/test/java/io/quarkus/docs/YamlMetadataGeneratorTest.java
+++ b/docs/src/test/java/io/quarkus/docs/YamlMetadataGeneratorTest.java
@@ -2,13 +2,13 @@
import java.nio.file.Path;
import java.util.Collection;
-import java.util.List;
import java.util.Map;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.condition.DisabledIfSystemProperty;
import io.quarkus.docs.generation.YamlMetadataGenerator;
+import io.quarkus.docs.generation.YamlMetadataGenerator.FileMessages;
import io.quarkus.docs.generation.YamlMetadataGenerator.Index;
public class YamlMetadataGeneratorTest {
@@ -39,21 +39,22 @@ public void testAsciidocFiles() throws Exception {
// Generate YAML: doc requirements
Index index = metadataGenerator.generateIndex();
- Map> metadataErrors = index.errorsByFile();
+ Map messages = index.messagesByFile();
+ boolean hasErrors = false;
StringBuilder sb = new StringBuilder("\n");
- for (String fileName : metadataErrors.keySet()) {
- sb.append(fileName).append(": ").append("\n");
-
- Collection mErrors = metadataErrors.getOrDefault(fileName, List.of());
- mErrors.forEach(e -> sb.append(" ").append(e).append("\n"));
- sb.append("\n");
+ for (String fileName : messages.keySet()) {
+ FileMessages fm = messages.get(fileName);
+ if (fm != null) {
+ sb.append(fileName).append(": ").append("\n");
+ hasErrors |= fm.listAll(sb);
+ }
}
String result = sb.toString().trim();
- if (result.length() > 0) {
- System.err.println(result);
- // throw new LintException("target/errorsByFile.yaml");
+ System.err.println(result);
+ if (hasErrors) {
+ throw new LintException("target/errorsByFile.yaml");
} else {
System.out.println("🥳 OK");
}
diff --git a/docs/src/test/java/io/quarkus/docs/vale/LocalValeLintTest.java b/docs/src/test/java/io/quarkus/docs/vale/LocalValeLintTest.java
index d256658fcc192..f45f2164d9717 100644
--- a/docs/src/test/java/io/quarkus/docs/vale/LocalValeLintTest.java
+++ b/docs/src/test/java/io/quarkus/docs/vale/LocalValeLintTest.java
@@ -2,7 +2,6 @@
import java.nio.file.Path;
import java.util.Collection;
-import java.util.List;
import java.util.Map;
import org.junit.jupiter.api.Test;
@@ -11,6 +10,7 @@
import io.quarkus.docs.ChangedFiles;
import io.quarkus.docs.LintException;
import io.quarkus.docs.generation.YamlMetadataGenerator;
+import io.quarkus.docs.generation.YamlMetadataGenerator.FileMessages;
import io.quarkus.docs.generation.YamlMetadataGenerator.Index;
import io.quarkus.docs.vale.ValeAsciidocLint.ChecksBySeverity;
@@ -61,26 +61,28 @@ public void testAsciidocFiles() throws Exception {
// Generate YAML: doc requirements
Index index = metadataGenerator.generateIndex();
- Map> metadataErrors = index.errorsByFile();
+ Map messages = index.messagesByFile();
// Find Vale errors
Map lintResults = linter.lintFiles();
// Write vale.yaml
- linter.resultsToYaml(lintResults, metadataErrors);
+ linter.resultsToYaml(lintResults, messages);
+ boolean hasErrors = false;
StringBuilder sb = new StringBuilder("\n");
for (String fileName : lintResults.keySet()) {
sb.append(fileName).append(": ").append("\n");
- if (!metadataErrors.isEmpty()) {
+ FileMessages fm = messages.get(fileName);
+ if (fm != null) {
sb.append("\n metadata\n");
- Collection mErrors = metadataErrors.getOrDefault(fileName, List.of());
- mErrors.forEach(e -> sb.append(" ").append(e).append("\n"));
+ hasErrors |= fm.listAll(sb);
}
ChecksBySeverity lErrors = lintResults.get(fileName);
if (lErrors != null) {
+ hasErrors = true; // always fail in this purposeful case
lErrors.checksBySeverity.entrySet().forEach(e -> {
sb.append("\n ").append(e.getKey()).append("\n");
e.getValue().forEach(c -> sb.append(" ").append(c).append("\n"));
@@ -90,8 +92,8 @@ public void testAsciidocFiles() throws Exception {
}
String result = sb.toString().trim();
- if (result.length() > 0) {
- System.err.println(result);
+ System.err.println(result);
+ if (hasErrors) {
throw new LintException("target/vale.yaml");
} else {
System.out.println("🥳 OK");
diff --git a/docs/src/test/java/io/quarkus/docs/vale/ValeAsciidocLint.java b/docs/src/test/java/io/quarkus/docs/vale/ValeAsciidocLint.java
index 52d95769af80e..4ce6a94197ae4 100644
--- a/docs/src/test/java/io/quarkus/docs/vale/ValeAsciidocLint.java
+++ b/docs/src/test/java/io/quarkus/docs/vale/ValeAsciidocLint.java
@@ -38,6 +38,8 @@
import com.fasterxml.jackson.dataformat.yaml.YAMLFactory;
import com.fasterxml.jackson.dataformat.yaml.YAMLGenerator;
+import io.quarkus.docs.generation.YamlMetadataGenerator.FileMessages;
+
public class ValeAsciidocLint {
public static TypeReference