mp.guides.metrics.adoc Maven / Gradle / Ivy
///////////////////////////////////////////////////////////////////////////////
Copyright (c) 2019, 2024 Oracle and/or its affiliates.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
///////////////////////////////////////////////////////////////////////////////
= Helidon MP Metrics Guide
:description: Helidon metrics
:keywords: helidon, metrics, microprofile, guide
:rootdir: {docdir}/../..
:intro-project-name: MicroProfile (MP)
include::{rootdir}/includes/mp.adoc[]
:metric: metric
:metrics: metrics
:metric_uc: Metric
:metrics_uc: Metrics
:metrics-endpoint: /metrics
include::{rootdir}/includes/guides/metrics.adoc[tag=intro]
include::{rootdir}/includes/guides/metrics.adoc[tag=create-sample-project]
include::{rootdir}/includes/guides/metrics.adoc[tag=using-built-in-metrics-intro]
include::{rootdir}/includes/guides/metrics.adoc[tag=build-and-run-intro]
[source,text]
.Text response: (partial)
----
# HELP classloader_loadedClasses_total Displays the total number of classes that have been loaded since the Java virtual machine has started execution.
# TYPE classloader_loadedClasses_total counter
classloader_loadedClasses_total{mp_scope="base",} 8146.0
# HELP requests_count_total Each request (regardless of HTTP method) will increase this counter
# TYPE requests_count_total counter
requests_count_total{mp_scope="vendor",} 1.0
# HELP jvm_uptime_seconds Displays the start time of the Java virtual machine in seconds. This attribute displays the approximate time when the Java virtual machine started.
# TYPE jvm_uptime_seconds gauge
jvm_uptime_seconds{mp_scope="base",} 7.3770
----
include::{rootdir}/includes/guides/metrics.adoc[tag=curl-metrics-json]
[source,json]
.JSON response (partial):
----
{
"application": {
"personalizedGets": 0,
"allGets": {
"count": 0,
"elapsedTime": 0,
"max": 0,
"mean": 0
}
},
"vendor": {
"requests.count": 2
},
"base": {
"gc.total;name=G1 Concurrent GC": 2,
"cpu.systemLoadAverage": 10.3388671875,
"classloader.loadedClasses.count": 8224,
"thread.count": 19,
"classloader.unloadedClasses.total": 0,
"jvm.uptime": 36.8224
}
}
----
include::{rootdir}/includes/guides/metrics.adoc[tag=get-single-metric]
include::{rootdir}/includes/guides/metrics.adoc[tag=built-in-metrics-discussion]
include::{rootdir}/includes/guides/metrics.adoc[tag=controlling-intro-part-1]
* <>
include::{rootdir}/includes/guides/metrics.adoc[tag=controlling-intro-part-2]
[[controlling-rest-request-metrics]]
include::{rootdir}/includes/guides/metrics.adoc[tag=disabling-whole]
include::{rootdir}/includes/guides/metrics.adoc[tag=KPI]
[[controlling-rest-request-metrics]]
==== Controlling `REST.request` Metrics
Helidon MP implements the optional family of metrics, all with the name `REST.request`, as described in the
link:{microprofile-metrics-spec-url}#_optional_rest[MicroProfile Metrics specification].
Each instance is a `Timer` with tags `class` and `method` identifying exactly which REST endpoint Java
method that instance measures.
By default, Helidon MP does _not_ enable this feature.
Enable it by editing your application configuration to set `metrics.rest-request.enabled` to `true`.
Note that the applications you generate using the full Helidon archetype _do_ enable this feature in the
generated config file.
You can see the results in the sample output shown in earlier example runs.
include::{rootdir}/includes/guides/metrics.adoc[tag=metrics-metadata]
=== Application-Specific Metrics Data
You can create application-specific metrics and integrate them with Helidon using CDI.
To add a new metric, simply annotate the JAX-RS resource with one of the metric annotations. Metrics can
be injected at the class, method, and field-levels. This document shows examples of all three.
Helidon will automatically create and register annotated application metrics and store them in the application `MetricRegistry`, which
also contains the metric metadata. The metrics will exist for the lifetime of the application.
Each metric annotation has mandatory and optional fields. The name field, for example, is optional.
==== Method Level Metrics
There are two metrics that you can use by annotating a method:
1. `@Counted` - Register a `Counter` metric
2. `@Timed` - Register a `Timer` metric
The following example will demonstrate how to use the `@Counted` annotation to track the number of times
the `/cards` endpoint is called.
[source,java]
.Create a new class `GreetingCards` with the following code:
----
include::{sourcedir}/mp/guides/MetricsSnippets.java[tag=snippet_1, indent=0]
----
<1> This class is annotated with `Path` which sets the path for this resource
as `/cards`.
<2> The `@RequestScoped` annotation defines that this bean is
request scoped. The request scope is active only for the duration of
one web service invocation, and it is destroyed at the end of that
invocation.
<3> The annotation `@Counted` will register a `Counter` metric for this method, creating it if needed.
The counter is incremented each time the anyCards method is called. The `name` attribute is optional.
[source,bash,subs="attributes+"]
.Build and run the application, then invoke the application endpoints below:
----
curl http://localhost:8080/cards
curl http://localhost:8080/cards
curl -H "Accept: application/json" 'http://localhost:8080{metrics-endpoint}?scope=application'
----
[source,hocon]
.JSON response (partial):
----
{
"io.helidon.examples.quickstart.mp.GreetingCards.any-card":2 // <1>
}
----
<1> The any-card count is two, since you invoked the endpoint twice.
NOTE: Notice the counter is fully qualified. You can remove the package prefix by using the `absolute=true` field in the `@Counted` annotation.
You must use `absolute=false` for class-level annotations.
==== Additional Method Level Metrics
The `@Timed` annotation can also be used with a method. For the following example.
you can just annotate the same method with `@Timed`. These metrics collect significant
information about the measured methods, but at a cost of some overhead and more complicated output.
Note that when using multiple annotations on a method, you *must* give the metrics different names as shown below.
[source,java]
.Replace the `GreetingCards` class with the following code:
----
include::{sourcedir}/mp/guides/MetricsSnippets.java[tag=snippet_2, indent=0]
----
<1> Specify a custom name for the `Counter` metric and set `absolute=true` to remove the path prefix from the name.
<2> Add the `@Timed` annotation to get a `Timer` metric.
[source,bash,subs="attributes+"]
.Build and run the application, then invoke the application endpoints below:
----
curl http://localhost:8080/cards
curl http://localhost:8080/cards
curl -H "Accept: application/json" 'http://localhost:8080{metrics-endpoint}?scope=application'
----
[source,json]
.JSON response (partial):
----
{
"cardTimer": {
"count": 2,
"max": 0.002921992,
"mean": 0.0014682555,
"elapsedTime": 0.002936511,
"p0.5": 1.4336e-05,
"p0.75": 0.003014144,
"p0.95": 0.003014144,
"p0.98": 0.003014144,
"p0.99": 0.003014144,
"p0.999": 0.003014144
},
"cardCount": 2
}
----
==== Reusing Metrics
You can share a metric across multiple endpoints simply by specifying the same metric annotation as
demonstrated below.
[source,java]
.Replace the `GreetingCards` class with the following code:
----
include::{sourcedir}/mp/guides/MetricsSnippets.java[tag=snippet_3, indent=0]
----
<1> The `/birthday` endpoint uses a `Counter` metric, named `specialEventCard`.
<2> The `/wedding` endpoint uses the same `Counter` metric, named `specialEventCard`.
[source,bash,subs="attributes+"]
.Build and run the application, then invoke the following endpoints:
----
curl http://localhost:8080/cards/wedding
curl http://localhost:8080/cards/birthday
curl http://localhost:8080/cards
curl -H "Accept: application/json" 'http://localhost:8080{metrics-endpoint}?scope=application'
----
[source,hocon]
.JSON response (partial):
----
{
"anyCard": 1,
"specialEventCard": 2 // <1>
}
----
<1> Notice that `specialEventCard` count is two, since you accessed `/cards/wedding` and `/cards/birthday`.
==== Class Level Metrics
You can collect metrics at the class-level to aggregate data from all methods in that class using the same metric.
The following example introduces a metric to count all card queries. In the following example, the method-level metrics are not
needed to aggregate the counts, but they are left in the example to demonstrate the combined output of all three metrics.
[source,java]
.Replace the `GreetingCards` class with the following code:
----
include::{sourcedir}/mp/guides/MetricsSnippets.java[tag=snippet_4, indent=0]
----
<1> This class is annotated with `@Counted`, which aggregates count data from all the method that have a `Count` annotation.
<2> Use `absolute=true` to remove path prefix for method-level annotations.
<3> Add a method with a `Counter` metric to get birthday cards.
[source,bash,subs="attributes+"]
.Build and run the application, then invoke the following endpoints:
----
curl http://localhost:8080/cards
curl http://localhost:8080/cards/birthday
curl -H "Accept: application/json" 'http://localhost:8080{metrics-endpoint}?scope=application'
----
[source,hocon]
.JSON response (partial):
----
{
"anyCard": 1,
"birthdayCard": 1,
"io.helidon.examples.quickstart.mp.totalCards.GreetingCards": 2 // <1>
}
----
<1> The `totalCards` count is a total of all the method-level `Counter` metrics. Class level metric names are always
fully qualified.
==== Field Level Metrics
Field level metrics can be injected into managed objects, but they need to be updated by the application code.
This annotation can be used on fields of type `Timer`, `Counter`, and `Histogram`.
The following example shows how to use a field-level `Counter` metric to track cache hits.
[source,java]
.Replace the `GreetingCards` class with the following code:
----
include::{sourcedir}/mp/guides/MetricsSnippets.java[tag=snippet_5, indent=0]
----
<1> A `Counter` metric field, `cacheHits`, is automatically injected by Helidon.
<2> Call `updateStats()` to update the cache hits.
<3> Call `updateStats()` to update the cache hits.
<4> Randomly increment the `cacheHits` counter.
[source,bash,subs="attributes+"]
.Build and run the application, then invoke the following endpoints:
----
curl http://localhost:8080/cards
curl http://localhost:8080/cards
curl http://localhost:8080/cards/birthday
curl http://localhost:8080/cards/birthday
curl http://localhost:8080/cards/birthday
curl -H "Accept: application/json" 'http://localhost:8080{metrics-endpoint}?scope=application'
----
[source,hocon]
.JSON response (partial):
----
{
"anyCard": 2,
"birthdayCard": 3,
"cacheHits": 2, // <1>
"io.helidon.examples.quickstart.mp.totalCards.GreetingCards": 5
}
----
<1> The cache was hit two times out of five queries.
==== Gauge Metric
The `Gauge` {metric} measures a value that is maintained by code outside the metrics subsystem. As with other {metrics}, the application explicitly registers a gauge. When the `{metrics-endpoint}` endpoint
is invoked, Helidon retrieves the value of each registered `Gauge`.
The following example demonstrates how to use a `Gauge` to track application up-time.
[source,java]
.Create a new `GreetingCardsAppMetrics` class with the following code:
----
include::{sourcedir}/mp/guides/MetricsSnippets.java[tag=snippet_6, indent=0]
----
<1> This managed object must be application scoped to properly register and use the `Gauge` metric.
<2> Declare an `AtomicLong` field to hold the start time of the application.
<3> Initialize the application start time.
<4> Return the application `appUpTimeSeconds` metric, which will be included in the application metrics.
[source,java]
.Update the `GreetingCards` class with the following code to simplify the metrics output:
----
include::{sourcedir}/mp/guides/MetricsSnippets.java[tag=snippet_7, indent=0]
----
[source,bash]
.Build and run the application, then invoke the application metrics endpoint:
----
curl -H "Accept: application/json" http://localhost:8080/metrics/application
----
[source,hocon]
.JSON response from `/metrics/application`:
----
{
"cardCount": 0,
"io.helidon.examples.quickstart.mp.GreetingCardsAppMetrics.appUpTimeSeconds": 6 // <1>
}
----
<1> The application has been running for 6 seconds.
include::{rootdir}/includes/guides/metrics.adoc[tag=k8s-and-prometheus-integration]
=== Summary
This guide demonstrated how to use metrics in a Helidon MP application using various combinations of
metrics and scopes.
* Access metrics for all three scopes: base, vendor, and application
* Configure application metrics at the class, method, and field-level
* Integrate Helidon metrics with Kubernetes and Prometheus
Refer to the following references for additional information:
* link:{microprofile-metrics-spec-url}[MicroProfile Metrics specification]
* link:{microprofile-metrics-javadoc-url}[MicroProfile Metrics Javadoc]
* Helidon Javadoc at {javadoc-base-url}/index.html?overview-summary.html
© 2015 - 2025 Weber Informatics LLC | Privacy Policy