All Downloads are FREE. Search and download functionalities are using the official Maven repository.

mp.metrics.metrics.adoc Maven / Gradle / Ivy

There is a newer version: 4.1.4
Show newest version
///////////////////////////////////////////////////////////////////////////////

    Copyright (c) 2022, 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.

///////////////////////////////////////////////////////////////////////////////

= Metrics in Helidon MP
:toc:
:toclevels: 2
:toc-placement: preamble
:description: Helidon MP Metrics Support
:keywords: helidon, java, metrics, mp, configuration, services
:feature-name: metrics
:rootdir: {docdir}/../..

include::{rootdir}/includes/mp.adoc[]
:metric: metric
:metrics: metrics
:metric_uc: Metric
:metrics_uc: Metrics
:metrics-endpoint: /metrics

== Contents

- <>
- <>
- <>
- <>
- <>
- <>
- <>

== Overview
Helidon MP metrics implements the MicroProfile Metrics specification, providing:

include::{rootdir}/includes/metrics/metrics-shared.adoc[tag=overview]

include::{rootdir}/includes/dependencies.adoc[]

[source,xml,subs="attributes+"]
----

    io.helidon.microprofile.metrics
    helidon-microprofile-metrics

----

Adding this dependency packages the full-featured metrics implementation with your service.

== Usage

include::{rootdir}/includes/metrics/metrics-shared.adoc[tag=usage-body]

=== {Metric_uc} Registries
A _{metric} registry_ collects registered {metrics} of a given scope. Helidon supports one metrics registry for each scope.

When you add code to your service to create a metric programmatically, the code first locates the appropriate registry and then registers the metric with that registry.

include::{rootdir}/includes/metrics/metrics-shared.adoc[tag=usage-retrieving]

== API

The link:{microprofile-metrics-javadoc-url}/org/eclipse/microprofile/metrics/package-summary.html[MicroProfile Metrics API] prescribes all the standard interfaces related to metrics. This section summarizes a few key points about using that API and explains some Helidon-specific interfaces.

=== Metrics Annotations
You can very easily instrument your service and refer to registered metrics by annotating methods to be measured and injecting metrics which your code needs to observe.

==== Metric-defining Annotations
The MicroProfile Metrics specification describes several metric types you can create using annotations, summarized in the following table:

.Metrics Annotations
[%autowidth]
|====
| Annotation | Usage

| link:{microprofile-metrics-javadoc-annotation-url}/Counted.html[`@Counted`]
| Monotonically increasing count of events.

| link:{microprofile-metrics-javadoc-annotation-url}/Gauge.html[`@Gauge`]
| Access to a value managed by other code in the service.

| link:{microprofile-metrics-javadoc-annotation-url}/Timed.html[`@Timed`]
| Frequency of invocations and the distribution of how long the invocations take.

|====

Place annotations on constructors or methods to measure those specific executables. If you annotate the class instead, Helidon applies that annotation to all constructors and methods which the class declares.

==== Metric-referencing Annotations
To get a reference to a specific metric, use a metric-referencing annotation in any bean, including your REST resource classes.

You can `@Inject` a field of the correct type. Helidon uses the MicroProfile Metrics naming conventions to select which specific metric to inject. Use the link:{microprofile-metrics-javadoc-url}/org/eclipse/microprofile/metrics/annotation/Metric.html[`@Metric`] annotation to control that selection.

You can also add `@Metric` on a constructor or method parameter to trigger injection there.

Helidon automatically looks up the metric referenced from any injection site and provides a reference to the metric. Your code then simply invokes methods on the injected metric.

include::{rootdir}/includes/metrics/metrics-shared.adoc[tag=metric-registry-api]

=== Working with Metrics in CDI Extensions
You can work with metrics inside your own CDI extensions, but be careful to do so at the correct point in the CDI lifecycle.
Configuration can influence how the metrics system behaves, as the <> section below explains.
Your code should work with metrics only after the Helidon metrics system has initialized itself using configuration.
One way to accomplish this is to deal with metrics in a method that observes the Helidon `RuntimeStart` CDI event, which the xref:extension_example[extension example below] illustrates.

// Here's Configuration.

[[config-intro]]
include::{rootdir}/includes/metrics/metrics-config.adoc[tag=config-intro]

== Examples

Helidon MP includes a pre-written example application illustrating
link:{helidon-github-tree-url}/examples/metrics/filtering/mp[enabling/disabling metrics] using configuration.

The rest of this section contains other examples of working with metrics:

* <>
* <>

[#example-application-code]
=== Example Application Code

==== Adding Method-level Annotations
The following example adds a new resource class, `GreetingCards`, to the Helidon MP QuickStart example. It shows 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/metrics/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.

// tag::build-and-access-cards-example[]

// tag::build-cards-example[]
.Build and run the application
[source,bash]
----
mvn package
java -jar target/helidon-quickstart-mp.jar
----
// end::build-cards-example[]

// tag::access-cards-example[]
.Access the application endpoints
[source,bash]
----
curl http://localhost:8080/cards
curl http://localhost:8080/cards
curl -H "Accept: application/json"  'http://localhost:8080/metrics?scope=application'
----
// end::access-cards-example[]

// end::build-and-access-cards-example[]

[source,hocon]
.JSON response:
----
{
  "io.helidon.examples.quickstart.mp.GreetingCards.any-card": 2, //  <1>
  "personalizedGets": 0,
  "allGets": {
    "count": 0,
    "elapsedTime": 0,
    "max": 0,
    "mean": 0
  }
}
----
<1> The any-card count is two, since you invoked the endpoint twice. The other metrics are from the `SimpleGreetResource` class.

NOTE: Notice the counter name is fully qualified with the class and method names.  You can remove the prefix by using the `absolute=true` field in the `@Counted` annotation.
You must use  `absolute=false` (the default) for class-level annotations.

==== Additional Method-level Metrics

You can also use the @Timed` annotation with a method.  For the following example.
you can just annotate the same method with `@Timed`. Timers  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, although they do not have to be absolute.

[source,java]
.Update the `GreetingCards` class with the following code:
----
include::{sourcedir}/mp/metrics/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.

include::metrics.adoc[tag=build-and-access-cards-example]

[source,json]
.JSON response:
----
{
  "cardTimer": {
    "count": 2,
    "elapsedTime": 0.002941925,
    "max": 0.002919973,
    "mean": 0.0014709625
  },
  "personalizedGets": 0,
  "allGets": {
    "count": 0,
    "elapsedTime": 0,
    "max": 0,
    "mean": 0
  },
  "cardCount": 2
}
----

==== 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]
.Update the `GreetingCards` class with the following code:
----
include::{sourcedir}/mp/metrics/MetricsSnippets.java[tag=snippet_3, indent=0]
----
<1> This class is now 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.

include::metrics.adoc[tag=build-cards-example]
[source,bash]
.Access the application endpoints
----
curl http://localhost:8080/cards
curl http://localhost:8080/cards/birthday
curl -H "Accept: application/json"  'http://localhost:8080/metrics?scope=application'
----

[source,hocon]
.JSON response from `/metrics?scope=application`:
----
{
  "birthdayCard": 1,
  "personalizedGets": 0,
  "allGets": {
    "count": 0,
    "elapsedTime": 0,
    "max": 0,
    "mean": 0
  },
  "anyCard": 1,
  "io.helidon.examples.quickstart.mp.totalCards.GreetingCards": 2 // <1>
}

----
<1> The `totalCards.GreetingCards` 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]
.Update the `GreetingCards` class with the following code:
----
include::{sourcedir}/mp/metrics/MetricsSnippets.java[tag=snippet_4, 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]
.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?scope=application'
----

[source,hocon]
.JSON response from `/metrics/application`:
----
{
  "birthdayCard": 3,
  "personalizedGets": 0,
  "allGets": {
    "count": 0,
    "elapsedTime": 0,
    "max": 0,
    "mean": 0
  },
  "anyCard": 2,
  "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 metrics you have tested so far are updated in response to an application REST request, i.e. GET `/cards`.  These
metrics can be declared in a request scoped class and Helidon will store the metric in the `MetricRegistry`, so the value persists
across requests. When GET `/metrics?scope=application` is invoked, Helidon will return the current value of the metric stored in the `MetricRegistry`.

The `Gauge` annotation is different from the other metric annotations. The application must provide a method to return the gauge value in an
application-scoped class. When GET `/metrics?scope=application` is invoked, Helidon will call the `Gauge` method, using the returned value as the value of the gauge as part of the metrics response.

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/metrics/MetricsSnippets.java[tag=snippet_5, indent=0]
----
<1> This managed object must be application scoped to properly register and use the annotated `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/metrics/MetricsSnippets.java[tag=snippet_6, indent=0]
----

[source,bash]
.Build and run the application, then invoke the application metrics endpoint:
----
curl -H "Accept: application/json"  'http://localhost:8080/metrics?scope=application'
----

[source,hocon]
.JSON response from `/metrics/application`:
----
{
  "personalizedGets": 0,
  "allGets": {
    "count": 0,
    "elapsedTime": 0,
    "max": 0,
    "mean": 0
  },
  "io.helidon.examples.quickstart.mp.GreetingCardsAppMetrics.appUpTimeSeconds": 23, // <1>
  "cardCount": 0
}

----
<1> The application has been running for 23 seconds.

[[extension_example]]
==== Working with Metrics in CDI Extensions
You can work with metrics from your own CDI extension by observing the `RuntimeStart` event.
[source,java]
.CDI Extension that works correctly with metrics
----
include::{sourcedir}/mp/metrics/MetricsSnippets.java[tag=snippet_7, indent=0]
----
<1> Declares that your observer method responds to the `RuntimeStart` event. By this time, Helidon has initialized the metrics system.
<2> Injects a `MetricRegistry` (the application registry by default).
<3> Uses the injected registry to register a metric (a counter in this case).
[NOTE]
====
Helidon does not prevent you from working with metrics earlier than the `RuntimeStart` event, but, if you do so, then Helidon might ignore certain configuration settings that would otherwise control how metrics behaves.
Instead, consider writing your extension to use earlier lifecycle events (such as `ProcessAnnotatedType`) to gather and store information about metrics that you want to register.
Then your extension's `RuntimeStart` observer method would use that stored information to register the metrics you need.
====

// Config examples
include::{rootdir}/includes/metrics/metrics-config.adoc[tag=config-examples]

== Additional Information

include::{rootdir}/includes/guides/metrics.adoc[tag=k8s-and-prometheus-integration]

=== References
link:{microprofile-metrics-spec-url}[MicroProfile Metrics specification]

link:{microprofile-metrics-javadoc-url}/org/eclipse/microprofile/metrics/package-summary.html[MicroProfile Metrics API]




© 2015 - 2025 Weber Informatics LLC | Privacy Policy