Skip to content

Commit

Permalink
Add MP metrics annotation support to MP REST clients (#9534)
Browse files Browse the repository at this point in the history
* Add MP metrics annotation support to MP REST clients
  • Loading branch information
tjquinno authored Dec 2, 2024
1 parent 5c2cca8 commit 42f8ce6
Show file tree
Hide file tree
Showing 23 changed files with 1,685 additions and 15 deletions.
5 changes: 5 additions & 0 deletions bom/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -738,6 +738,11 @@
<artifactId>helidon-microprofile-rest-client</artifactId>
<version>${helidon.version}</version>
</dependency>
<dependency>
<groupId>io.helidon.microprofile.rest-client-metrics</groupId>
<artifactId>helidon-microprofile-rest-client-metrics</artifactId>
<version>${helidon.version}</version>
</dependency>

<!-- Reactive Streams Operators -->
<dependency>
Expand Down
2 changes: 1 addition & 1 deletion docs/src/main/asciidoc/includes/pages.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -27,5 +27,5 @@ ifdef::mp-flavor[]
endif::[]
:webclient-page: {rootdir}/se/webclient.adoc
:restclient-page: {rootdir}/mp/restclient.adoc
:restclient-page: {rootdir}/mp/restclient/restclient.adoc
:cli-page: {rootdir}/about/cli.adoc
4 changes: 2 additions & 2 deletions docs/src/main/asciidoc/mp/introduction.adoc
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
///////////////////////////////////////////////////////////////////////////////

Copyright (c) 2019, 2023 Oracle and/or its affiliates.
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.
Expand Down Expand Up @@ -121,7 +121,7 @@ overhead server built on Java virtual threads.
| link:{microprofile-rs-operators-spec-url}[{version-lib-microprofile-rs-operators-api}]
| Control flow and error processing for event streams
| xref:{rootdir}/mp/restclient.adoc[MicroProfile REST Client]
| xref:{rootdir}/mp/restclient/restclient.adoc[MicroProfile REST Client]
| link:{microprofile-rest-client-spec-url}[{version-lib-microprofile-rest-client}]
| Type-safe API for RESTful Web Services
Expand Down
2 changes: 1 addition & 1 deletion docs/src/main/asciidoc/mp/jaxrs/jaxrs-client.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ include::{rootdir}/includes/mp.adoc[]
The Jakarta REST Client defines a programmatic API to access
REST resources. This API sits at a higher level than traditional HTTP client APIs and
provides full integration with server-side API concepts like providers. It differs
from the xref:../restclient.adoc[Rest Client API] in that it does not support
from the xref:../restclient/restclient.adoc[Rest Client API] in that it does not support
annotations or proxies, but instead uses builders and a fluent API to
create and execute requests.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@
:feature-name: MicroProfile Rest Client
:microprofile-bundle: true
:keywords: helidon, rest, client, microprofile, micro-profile
:rootdir: {docdir}/..
:rootdir: {docdir}/../..
include::{rootdir}/includes/mp.adoc[]
Expand All @@ -43,16 +43,19 @@ Helidon will automatically create a _proxy_ class for the interface and map loca
For more information, see link:{microprofile-rest-client-spec-url}[Rest Client For MicroProfile Specification].
You can also use metrics annotations on your Rest Client methods as described in xref:restclientmetrics.adoc[this related page.]
include::{rootdir}/includes/dependencies.adoc[]
// tag::helidon-restclient-dep[]
[source,xml]
----
<dependency>
<groupId>io.helidon.microprofile.rest-client</groupId>
<artifactId>helidon-microprofile-rest-client</artifactId>
</dependency>
----
// end::helidon-restclient-dep[]
== API
[cols="1,2"]
|===
Expand Down Expand Up @@ -86,7 +89,7 @@ the provided configuration.
[source,java]
.Example
----
include::{sourcedir}/mp/RestclientSnippets.java[tag=snippet_1, indent=0]
include::{sourcedir}/mp/restclient/RestclientSnippets.java[tag=snippet_1, indent=0]
----
The `RestClientBuilder` interface extends the `Configurable` interface from Jakarta REST (JAX-RS),
Expand All @@ -95,7 +98,7 @@ enabling direct registration of _providers_ such as filters, param converters, e
[source,java]
.Example
----
include::{sourcedir}/mp/RestclientSnippets.java[tag=snippet_2, indent=0]
include::{sourcedir}/mp/restclient/RestclientSnippets.java[tag=snippet_2, indent=0]
----
=== Creating a New Client Using CDI
Expand All @@ -107,7 +110,7 @@ to access the service.
[source,java]
.Example
----
include::{sourcedir}/mp/RestclientSnippets.java[tag=snippet_3, indent=0]
include::{sourcedir}/mp/restclient/RestclientSnippets.java[tag=snippet_3, indent=0]
----
Any Jakarta REST (JAX-RS) providers for a client can be registered using the (repeatable)
Expand All @@ -116,7 +119,7 @@ Any Jakarta REST (JAX-RS) providers for a client can be registered using the (re
[source,java]
.Example
----
include::{sourcedir}/mp/RestclientSnippets.java[tag=snippet_4, indent=0]
include::{sourcedir}/mp/restclient/RestclientSnippets.java[tag=snippet_4, indent=0]
----
Once a client interface is annotated, it can be injected into any CDI bean.
Expand All @@ -126,7 +129,7 @@ All properties in annotation `RegisterRestClient` can be overridden via configur
[source,java]
.Example
----
include::{sourcedir}/mp/RestclientSnippets.java[tag=snippet_5, indent=0]
include::{sourcedir}/mp/restclient/RestclientSnippets.java[tag=snippet_5, indent=0]
----
== Configuration
Expand Down Expand Up @@ -189,14 +192,15 @@ Configuration options affecting CDI and programmatically created clients:
|===
== Examples
To be able to run and test this example, use the xref:guides/quickstart.adoc[Helidon MP examples/quickstarts].
To be able to run and test this example, use the xref:../guides/quickstart.adoc[Helidon MP examples/quickstarts].
Add a dependency on the Helidon Rest Client implementation and create the following client interface:
[source,java]
.client interface
----
include::{sourcedir}/mp/RestclientSnippets.java[tag=snippet_6, indent=0]
include::{sourcedir}/mp/restclient/RestclientSnippets.java[tag=snippet_6, indent=0]
----
[[example-after-client-interface]]
Then create a runnable method as described in <<creating-a-new-client, Creating new client>>, but with baseUri
`http://localhost:8080/greet` and the above interface.
Expand Down
194 changes: 194 additions & 0 deletions docs/src/main/asciidoc/mp/restclient/restclientmetrics.adoc
Original file line number Diff line number Diff line change
@@ -0,0 +1,194 @@
///////////////////////////////////////////////////////////////////////////////

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

///////////////////////////////////////////////////////////////////////////////
= Rest Client Metrics
:description: Helidon MP Rest Client Metrics
:feature-name: MicroProfile Rest Client Metrics
:microprofile-bundle: true
:keywords: helidon, rest, client, microprofile, micro-profile, metrics
:rootdir: {docdir}/../..
include::{rootdir}/includes/mp.adoc[]
== Contents
- <<Overview, Overview>>
- <<Maven Coordinates, Maven Coordinates>>
- <<Usage, Usage>>
- <<API, API>>
- <<Configuration, Configuration>>
- <<Reference, Reference>>
== Overview
Helidon supports MicroProfile REST Client metrics by registering metrics automatically when developers add MicroProfile Metrics annotations to REST client interfaces and methods.
MicroProfile neither mandates nor specifies how metrics and the REST client work together. Support in Helidon for metrics on REST clients uses the MicroProfile Metrics spec for inspiration where appropriate.
For more information about support for REST clients in Helidon see xref:restclient.adoc[REST Client].
include::{rootdir}/includes/dependencies.adoc[]
// tag::helidon-restclientmetrics-dep[]
[source,xml]
----
<dependency>
<groupId>io.helidon.microprofile.rest-client-metrics</groupId>
<artifactId>helidon-microprofile-rest-client-metrics</artifactId>
</dependency>
----
// end::helidon-restclientmetrics-dep[]
== Usage
Add the MicroProfile Metrics `@Counted` and `@Timed` annotations to REST client interfaces and interface methods to trigger counting or timing, respectively, of REST client method invocations.
Helidon determines metric names according to the link:https://download.eclipse.org/microprofile/microprofile-metrics-5.1.1/microprofile-metrics-spec-5.1.1.html#annotated-naming-convention[MicroProfile Metrics naming convention] and supports the following metrics naming features:
* absolute and relative names
* explicit and inferred names
* type-level annotations
When you place annotations at the type level of a REST client interface Helidon registers _different_ metrics for each of the REST methods on the interface. This is the same behavior as in normal MicroProfile Metrics when you add metrics annotations at the type level.
When you use the annotations at the type level on a superinterface Helidon acts as if those annotations appear at the type-level of any REST client subinterface which extends the superinterface. In keeping with the naming conventions enforced by the MicroProfile Metrics TCK, relative metric names use the _subinterface_ name not the declaring interface name.
(Note that the
MicroProfile Metrics specification states that the _declaring_ class name is used, while as written the MicroProfile Metrics TCK requires that implementations use the _subclass_ name. For consistency the Helidon REST client metrics implementation follows the enforced metrics TCK behavior.)
=== Understanding How and When Helidon Registers REST Client Metrics
Helidon registers the metrics associated with a REST client interface when that interface becomes known to Helidon as a REST client.
The link:https://download.eclipse.org/microprofile/microprofile-rest-client-3.0/microprofile-rest-client-spec-3.0.html#_microprofile_rest_client[MicroProfile REST Client spec] describes how your application can inject a REST client interface or prepare it programmatically. Either action makes the REST client known to Helidon, at which time Helidon registers the metrics associated with that interface's methods. As a result, depending on how your application works, REST client metrics might be registered well after your code initially starts up.
=== Using REST Client Metrics in Standalone Clients vs. in Servers
Helidon registers and updates REST client metrics whether the REST client is standalone or is embedded in a Helidon server.
Helidon _does not_ provide a `/metrics` endpoint for standalone clients, nor does it provide any built-in way to transmit metrics data from a client to a backend system. If needed, you can write your client code to access the application `MetricRegistry` and retrieve the REST client metrics Helidon has registered.
In contrast, when REST clients run inside Helidon servers the REST client metrics for REST clients known to Helidon appear in the `/metrics` output.
=== Turning on Logging
Set `io.helidon.microprofile.restclientmetrics.level=DEBUG` in your logging settings to see some of the inner workings of the REST client metrics implementation.
During start-up the logging reports analysis of candidate REST client interfaces and the creation of metric registration entries, including the metric annotation and where Helidon found each.
When a REST client is made known to Helidon the logging reports the actual registration of the metrics derived from that REST client interface.
== API
Use the following annotations from `org.eclipse.microprofile.metrics.annotation` listed in the following table to trigger REST client metrics.
[cols="1,2"]
|===
| Annotation | Description
| `@Counted` | Counts the invocations of a REST client method.
| `@Timed` | Times the invocations of a REST client method.
|===
Type-level annotations trigger registration of separate metrics for each REST client method in the REST client interface.
== Configuration
Optional configuration options:
[cols="3,3,2,5"]
|===
|key |type |default value |description
|`enabled` | string | `true` | Whether to use REST client metrics.
|===
The `enabled` configuration setting allows developers to build REST client metrics into an application while permitting end users to disable the feature at their discretion.
== Examples
This example is similar to the xref:restclient.adoc#_examples[Helidon REST Client doc example] which starts with the xref:../guides/quickstart.adoc[Helidon MP QuickStart example].
This sample app adds a new resource which mimics the functionality of the `GreetResource` but delegates each incoming request to its counterpart on the `GreetResource` using a REST client interface for that `GreetResource`. In short, the example application delegates to itself. Of course no production application would operate this way, but this contrived situation helps illustrate how to use REST client metrics simply with a single runnable project.
To create this REST client metrics example follow these steps.
1. Starting with the Helidon MP QuickStart example, add dependencies for both the Helidon REST client component and the Helidon REST client metrics component, as shown below.
+
include::restclient.adoc[tag=helidon-restclient-dep]
+
include::restclientmetrics.adoc[tag=helidon-restclientmetrics-dep]
2. Add the following REST client interface which includes MicroProfile Metrics annotations to count and time various REST client method invocations.
+
[source,java]
----
include::{sourcedir}/mp/restclient/RestclientMetricsSnippets.java[tag=snippet_1, indent=0]
----
<1> Times all outbound method invocations using separate timers for each method.
<2> Counts the number of times a request is sent to get the default greeting message.
3. Add a new resource class, similar to the `GreetService` resource class, but which delegates all incoming requests using the REST client.
+
[source,java]
----
include::{sourcedir}/mp/restclient/RestclientMetricsSnippets.java[tag=snippet_2, indent=0]
----
<1> Holds the prepared REST client for use by the delegating methods.
<2> Prepares the REST client. The example shows only one of many ways of doing this step.
<3> Each delegating method invokes the corresponding REST client method and returns the result from it.
+
By default, resource classes such as `DelegatingResource` are instantiated for each incoming request, but generally a Helidon server making outbound requests reuses the client data structures and connections. To create and reuse only a single REST client instance this example resource uses the Helidon `LazyValue` utility class so even as the system creates multiple instances of `DelegatingResource` they all reuse the same REST client.
4. Build and run the application.
+
[source,bash]
----
mvn clean package
java -jar target/helidon-quickstart-mp.jar
----
5. Access the delegating endpoints.
+
[source,bash]
----
curl http://localhost:8080/delegate
curl http://localhost:8080/delegate
curl http://localhost:8080/delegate/Joe
----
6. Retrieve the application metrics for the `getDefaultMessage` operation.
+
[source,bash]
----
curl 'http://localhost:8080/metrics?scope=application' | grep getDefault
----
7. Look for two types of metrics:
a. Counter:
+
[source,list]
----
# TYPE io_helidon_examples_quickstart_mp_GreetRestClient_getDefaultMessage_total counter
io_helidon_examples_quickstart_mp_GreetRestClient_getDefaultMessage_total{mp_scope="application",} 2.0
----
+
This is the counter resulting from the `@Counted` annotation on the `getDefaultMessage` method of the REST client interface. The name is relative to the annotated method's class and is automatically set to the method name because neither `name` nor `absolute` were specified with the annotation.
b. Timer:
+
[source,list]
----
# TYPE timedGreet_getDefaultMessage_seconds summary
timedGreet_getDefaultMessage_seconds{mp_scope="application",quantile="0.5",} 0.003407872
timedGreet_getDefaultMessage_seconds{mp_scope="application",quantile="0.75",} 0.092143616
timedGreet_getDefaultMessage_seconds_count{mp_scope="application",} 2.0
----
+
This excerpt shows the output for only one timer, but the full output includes timers for each method.
+
The `@Timed` annotation at the type level triggers the registration of timers for each REST method in the REST client interface. The `name` setting overrides the default of the type name, and the `absolute` setting means the selected name _is not_ relative to the fully-qualified class name.
== Reference
* xref:restclient.adoc[Helidon REST Client documentation]
* link:{microprofile-rest-client-spec-url}[MicroProfile RestClient specification]
* link:{microprofile-metrics-spec-url}[MicroProfile Metrics specification]
7 changes: 5 additions & 2 deletions docs/src/main/asciidoc/sitegen.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -234,12 +234,15 @@ backend:
sources:
- "engine.adoc"
- "rsoperators.adoc"
- type: "PAGE"
- type: "MENU"
title: "REST Client"
source: "restclient.adoc"
dir: "restclient"
glyph:
type: "icon"
value: "airplay"
sources:
- "restclient.adoc"
- "restclientmetrics.adoc"
- type: "PAGE"
title: "Scheduling"
source: "scheduling.adoc"
Expand Down
Loading

0 comments on commit 42f8ce6

Please sign in to comment.