Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implement Micrometer integration, with some new abstractions for interceptors and retrofit to MP metrics #2873

Merged
merged 79 commits into from
Apr 13, 2021
Merged
Show file tree
Hide file tree
Changes from 72 commits
Commits
Show all changes
79 commits
Select commit Hold shift + click to select a range
64dcb23
Combine the formerly-separate literal for the SyntheticSimplyTimed wi…
tjquinno Mar 20, 2021
17957a5
Introduce interface and general-purpose impl for intercept runner abs…
tjquinno Mar 20, 2021
277f318
Introduce InterceptInfo which gathers info about each intercepted exe…
tjquinno Mar 20, 2021
6f922a4
Convert the interceptors to work with the new approach; introduce an …
tjquinno Mar 20, 2021
e2e5c35
Remove a now-unused class
tjquinno Mar 20, 2021
4d71b97
Expand and add tests; add logging.properties with useful pre-commente…
tjquinno Mar 20, 2021
a679bc2
Remove leftover detailed logging setting from tests
tjquinno Mar 24, 2021
30da0cc
Review: fix unusual logging name
tjquinno Mar 24, 2021
3a510be
Review: fix method javadoc comment, parameter name
tjquinno Mar 24, 2021
39b4fc7
Review: some renaming; no need for Supplier<Iterable>, per Laird
tjquinno Mar 24, 2021
500c042
Fix test problem; clean up synthetic simple timers after tests to avo…
tjquinno Mar 25, 2021
97cee34
Disable vetoing tests until I can clean up between-test clean-up of r…
tjquinno Mar 25, 2021
68f9386
Copyright error in disabled test
tjquinno Mar 25, 2021
809e3f1
Return to original behavior if both class- and method-level annotatio…
tjquinno Mar 25, 2021
fa4b273
Review: some naming changes to enhance readability
tjquinno Mar 26, 2021
ec8e683
One additional renaming
tjquinno Mar 26, 2021
1b7d0c6
Abstract some common behavior from metrics/metrics/MetricsSupport to …
tjquinno Mar 22, 2021
7be9cab
Abstract some common behavior from MetricsCdiExtension and MetricUtil…
tjquinno Mar 23, 2021
7296e51
Abstract common interceptor behavior into service-common/rest-cdi; up…
tjquinno Mar 24, 2021
063f542
Add Micrometer integration: xxxSupport, CDI extension, interceptors, …
tjquinno Mar 24, 2021
d34a582
Straggler: updated dependencies pom
tjquinno Mar 24, 2021
7c3528d
Another straggler
tjquinno Mar 24, 2021
cd72419
Micrometer doc
tjquinno Mar 24, 2021
19f34ee
Straggler
tjquinno Mar 24, 2021
4b6c321
Renaming changes
tjquinno Mar 26, 2021
a960e8a
Some more renaming
tjquinno Mar 26, 2021
15aaf64
Typo
tjquinno Mar 26, 2021
231054c
Split Micrometer integrations into two modules
tjquinno Mar 26, 2021
81ba358
Typo
tjquinno Mar 26, 2021
264c4f3
Support multiple annotations of same type on annotated element
tjquinno Mar 26, 2021
f4c7da4
Store in context data any exception thrown by the intercepted executable
tjquinno Mar 26, 2021
673dfeb
Convert all interceptors to before-and-after to support update-only-o…
tjquinno Mar 26, 2021
46dca87
Clean up metrics extension, delegating to new superclass
tjquinno Mar 26, 2021
64c5b63
Merge branch 'master' into async-with-common
tjquinno Mar 26, 2021
26e6ea3
Formalize pre-invocation and post-completion handlers with their own …
tjquinno Mar 27, 2021
2dd1bf8
Adapt MP metrics to addition of Throwable to post-completion handler …
tjquinno Mar 27, 2021
d6cfb09
Straggler pom
tjquinno Mar 27, 2021
62f5987
Adapt to addition of Throwable to completion handler; update timer in…
tjquinno Mar 27, 2021
0bcde70
Remove incorrectly-preserved spotbugs exclusion property (never belon…
tjquinno Mar 27, 2021
8ef29e4
Straggler pom; tests
tjquinno Mar 27, 2021
e351acd
Copyright
tjquinno Mar 28, 2021
d481256
Further refine reusable API, reducing public surface area
tjquinno Mar 28, 2021
3a2df82
Migrate MP metrics to newly-refined reusable bits
tjquinno Mar 28, 2021
0508912
Migrate Micrometer to newly-refined reusable bits
tjquinno Mar 28, 2021
0d2b70c
Clean up some comments
tjquinno Mar 28, 2021
dc4f80a
Remove 'opens' left-over from when the SE and CDI modules were one
tjquinno Mar 28, 2021
9bc4717
Restore normal dependency nature of common CDI modules
tjquinno Mar 28, 2021
4ad422f
More pom clean-up
tjquinno Mar 28, 2021
30b8df6
Add some tests for proper set-up of metrics endpoints, including /met…
tjquinno Mar 29, 2021
41a5517
Resolve conflicts with scheduling merge
tjquinno Mar 29, 2021
89203d1
Improve directions to implementers of CDI extensions which extend Hel…
tjquinno Mar 29, 2021
d8ee834
Slight doc improvements; fix maven co-ords now that we have two separ…
tjquinno Mar 29, 2021
541f820
Merge remote-tracking branch 'origin/master' into async-with-common
tjquinno Mar 29, 2021
1819408
Further reduce common surface area (removing new AnnotationLookupResu…
tjquinno Mar 30, 2021
f5c76fe
Remove commented bit in method declaration
tjquinno Mar 30, 2021
fbb0cd4
Simplify MP metrics registration of metrics in response to finding me…
tjquinno Mar 30, 2021
0a5d040
Fix unchecked casting
tjquinno Mar 30, 2021
ac45245
Fix check that all metrics types are handled in anno processing
tjquinno Mar 30, 2021
f9c815b
Fix the metric type coverage testing again
tjquinno Mar 30, 2021
a044d62
Concurrent changes
tjquinno Mar 30, 2021
7269a91
Remove unused commented method
tjquinno Mar 30, 2021
fb5cfb2
Add examples
tjquinno Mar 30, 2021
710c354
Add Micrometer examples module to examples
tjquinno Mar 30, 2021
d1fbb19
Improve some javadoc
tjquinno Mar 30, 2021
14581e9
Make sure to register the handler for recording all requests before r…
tjquinno Mar 30, 2021
617a8c0
Refactor filtering of producers; make logic available as a protected …
tjquinno Mar 31, 2021
b7d66ec
Move filtering of process managed beans which have @Interceptor added…
tjquinno Mar 31, 2021
050e789
Remove unused builder type in HelidonRestCdiExtension declaration
tjquinno Mar 31, 2021
ce3e396
Further narrow exposed surface area
tjquinno Mar 31, 2021
94c85f1
Change directory name holding the shared Micrometer .adoc file from m…
tjquinno Mar 31, 2021
245cb9f
Correct minor logging issue; add @Dependent to HelidonInterceptor.Bas…
tjquinno Apr 2, 2021
c489f12
Improve error reporting when invoking post-invocation handlers
tjquinno Apr 2, 2021
62aacce
Review comments: move extension file from SE module to MP/CDI one whe…
tjquinno Apr 12, 2021
949decb
Review comment: use fully-qualified name in provides
tjquinno Apr 12, 2021
50eb4b3
Review comment: add javadoc to protected constructor of public class
tjquinno Apr 12, 2021
496d108
Review comment: use groupId io.helidon.integrations.micrometer (inste…
tjquinno Apr 12, 2021
d5bf9e9
Review comment: remove designforextension warning suppressions
tjquinno Apr 12, 2021
40504d3
Review comment: fix formatting glitch
tjquinno Apr 12, 2021
d995273
Concurrent changes
tjquinno Apr 12, 2021
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 20 additions & 0 deletions bom/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -583,6 +583,16 @@
<artifactId>helidon-common-context</artifactId>
<version>${helidon.version}</version>
</dependency>
<dependency>
<groupId>io.helidon.service-common</groupId>
<artifactId>helidon-service-common-rest</artifactId>
<version>${helidon.version}</version>
</dependency>
<dependency>
<groupId>io.helidon.service-common</groupId>
<artifactId>helidon-service-common-rest-cdi</artifactId>
<version>${helidon.version}</version>
</dependency>

<!-- db client -->
<dependency>
Expand Down Expand Up @@ -861,6 +871,16 @@
<artifactId>helidon-integrations-common-rest</artifactId>
<version>${helidon.version}</version>
</dependency>
<dependency>
<groupId>io.helidon.integrations</groupId>
<artifactId>helidon-integrations-micrometer</artifactId>
<version>${helidon.version}</version>
</dependency>
<dependency>
<groupId>io.helidon.integrations</groupId>
<artifactId>helidon-integrations-micrometer-cdi</artifactId>
<version>${helidon.version}</version>
</dependency>
<!-- OpenAPI support -->
<dependency>
<groupId>io.helidon.openapi</groupId>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -343,6 +343,15 @@ final class FeatureCatalog {
.experimental(true)
);

addSe("io.helidon.integrations.micrometer",
"Micrometer",
"Micrometer integration",
"Micrometer");
addMp("io.helidon.integrations.micrometer.cdi",
"Micrometer CDI",
"Micrometer CDI integration",
"Micrometer-CDI");

/*
* Common modules
*/
Expand Down
13 changes: 13 additions & 0 deletions dependencies/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,7 @@
<version.lib.logback>1.2.3</version.lib.logback>
<version.lib.mariadb-java-client>2.6.2</version.lib.mariadb-java-client>
<version.lib.maven-wagon>2.10</version.lib.maven-wagon>
<version.lib.micrometer>1.6.5</version.lib.micrometer>
<version.lib.micronaut>2.2.0</version.lib.micronaut>
<version.lib.micronaut.data>2.2.0</version.lib.micronaut.data>
<version.lib.micronaut.sql>3.3.1</version.lib.micronaut.sql>
Expand Down Expand Up @@ -553,6 +554,18 @@
<version>${version.lib.database.messaging}</version>
</dependency>

<!-- Micrometer related -->
<dependency>
<groupId>io.micrometer</groupId>
<artifactId>micrometer-core</artifactId>
<version>${version.lib.micrometer}</version>
</dependency>
<dependency>
<groupId>io.micrometer</groupId>
<artifactId>micrometer-registry-prometheus</artifactId>
<version>${version.lib.micrometer}</version>
</dependency>

<!-- Microprofile related -->
<dependency>
<groupId>org.eclipse.microprofile.config</groupId>
Expand Down
87 changes: 87 additions & 0 deletions docs/mp/metrics/02_micrometer.adoc
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
///////////////////////////////////////////////////////////////////////////////

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

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

= Micrometer Metrics
:description: Helidon Micrometer integration
:keywords: micrometer, helidon, metrics, integration, microprofile
:javadoc-base-url-api: {javadoc-base-url}io.helidon.integrations.micrometer/io/helidon/integrations/micrometer
:h1Prefix: MP
:common-page-prefix-inc: ../../shared/metrics/micrometer_shared.adoc
:isMP: true


include::{common-page-prefix-inc}[tags=intro]

include::{common-page-prefix-inc}[tags=prereq]

== Using Micrometer in Your Application
Add the Micrometer `@Timed` and `@Counted` annotations to methods in your application.

The examples below enhance the Helidon MP QuickStart application to track (by time and invocation count) all `GET` methods and to count all requests for a personalized greeting.

=== Add Micrometer annotations

[source,java]
.Adding Micrometer annotations to JAX-RS resource `GET` methods
----
import io.micrometer.core.annotation.Counted;
import io.micrometer.core.annotation.Timed;
// <1>
private static final String PERSONALIZED_GETS_COUNTER_NAME = "personalizedGets";
private static final String PERSONALIZED_GETS_COUNTER_DESCRIPTION = "Counts personalized GET operations";
private static final String GETS_TIMER_NAME = "allGets";
private static final String GETS_TIMER_DESCRIPTION = "Tracks all GET operations";

@GET
@Produces(MediaType.APPLICATION_JSON)
@Timed(value = GETS_TIMER_NAME, description = GETS_TIMER_DESCRIPTION, histogram = true) // <2>
public JsonObject getDefaultMessage() {
return createResponse("World");
}

@Path("/{name}")
@GET
@Produces(MediaType.APPLICATION_JSON)
@Counted(value = PERSONALIZED_GETS_COUNTER_NAME, description = PERSONALIZED_GETS_COUNTER_DESCRIPTION) // <3>
@Timed(value = GETS_TIMER_NAME, description = GETS_TIMER_DESCRIPTION, histogram = true) // <2>
public JsonObject getMessage(@PathParam("name") String name) {
return createResponse(name);
}
----
<1> Declare constants used in annotating multiple methods.
<2> Use `@Timed` to time and count both `GET` methods.
<3> Use `@Counted` to count the accesses to the `GET` method that returns a personalized greeting.

=== Using the Helidon-provided Micrometer `MeterRegistry` from Code

In addition to annotating your methods, you can create, look up, and update metrics explicitly in your code.

Add the following injection to a bean:
[source,java]
.Inject the `MeterRegistry`
----
@Inject
private MeterRegistry registry;
----
Helidon automatically injects a reference to the `MeterRegistry` it manages into your code. Your code can use the normal Micrometer API with this registry to create, find, update, and even delete meters.

include::{common-page-prefix-inc}[tags=overriding-intro;overriding-using-config]

include::{common-page-prefix-inc}[tags=accessing-endpoint-intro]

Within Helidon, each type of meter registry is paired with code that examines the incoming HTTP request and decides whether the request matches up with the associated meter registry. The first pairing that accepts the request returns the response.
178 changes: 178 additions & 0 deletions docs/se/metrics/02_micrometer.adoc
Original file line number Diff line number Diff line change
@@ -0,0 +1,178 @@
///////////////////////////////////////////////////////////////////////////////

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

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

= Micrometer Metrics
:description: Helidon Micrometer integration
:keywords: micrometer, helidon, metrics, integration
:javadoc-base-url-api: {javadoc-base-url}io.helidon.integrations.micrometer/io/helidon/integrations/micrometer
:h1Prefix: SE
:common-page-prefix-inc: ../../shared/metrics/micrometer_shared.adoc
:isSE: true

include::{common-page-prefix-inc}[tags=intro]

include::{common-page-prefix-inc}[tags=prereq]

== Using Micrometer in Your Application
You need to make two types of changes to your application to use Helidon SE integration with Micrometer:

. Register an instance of link:{javadoc-base-url-api}/MicrometerSupport.html[`MicrometerSupport`] with the web server.
. Create meters using the meter registry which `MicrometerSupport` manages and update those meters.

=== Register an Instance of `MicrometerSupport` with the Web Server

[source,java]
.Initialize Micrometer support
----
import io.helidon.integrations.micrometer.MicrometerSupport;
//...
MicrometerSupport micrometerSupport = MicrometerSupport.create(); // <1>

Routing.builder()
.register(micrometerSupport) // <2>
.register("/myapp", new MyService(micrometerSupport.registry())) // <3>
.build();
----
<1> Create the `MicrometerSupport` instance, using the default built-in Prometheus meter registry.
<2> Register the support instance as a service; by default, `MicrometerSupport` exposes the endpoint as `/micrometer`.
<3> Pass the `MicrometerSupport` object's meter registry to your service for use in creating and updating meters.

=== Create and Update Meters in your Application Service

[source,java]
.Define and use a `Counter`
----
import io.micrometer.core.instrument.Counter;

public class MyService implements Service {

private final Counter requestCounter;

public MyService(MicrometerMeterRegistry registry) {
requestCounter = registry.counter("allRequests"); // <1>
// ...
}

@Override
public void update(Routing.Rules rules) {
rules
.any(this::countRequests) // <2>
.get("/", this::myGet);
}

private void countRequests(ServerRequest request, ServerResponse response) {
requestCounter.increment(); // <3>
request.next();
}
}
----
<1> Use the Micrometer meter registry to create the request counter.
<2> Add routing for any request to invoke the method which counts requests by updating the counter.
<3> Update the counter and delegate the rest of the request processing to the next handler in the chain.

The example above enrolls the built-in Prometheus meter registry with the default Prometheus registry configuration.
You can change the default setup for built-in registries, and you can enroll other meter registries your application
creates itself.

include::{common-page-prefix-inc}[tag=overriding-intro]

You can override these defaults in either of two ways:

* Using the link:{javadoc-base-url-api}/MicrometerSupport.Builder.html[`MicrometerSupport.Builder`] class
* Using configuration

==== Using `MicrometerSupport.Builder`
Use the `MicrometerSupport.Builder` class to set up Micrometer support however your application needs.

The builder lets you:

* Provide your own Micrometer meter registry configuration that `MicrometerSupport` uses to create a built-in meter
registry, or
* Instantiate a Micrometer meter registry yourself, configured however you want, and add it to the `MicrometerSupport`
object's collection of meter registries

[source,java]
.Overriding defaults for built-in meter registries using `MicrometerSupport.Builder`
----
PrometheusConfig myPrometheusConfig = ...; // <1>
MicrometerSupport support = MicrometerSupport.builder()
.enrollBuiltInRegistry( // <2>
MicrometerSupport.BuiltInRegistryType.PROMETHEUS,
myPrometheusConfig)
.build();
----
<1> Create the meter registry configuration however you need.
<2> Enroll the `PROMETHEUS` built-in registry type with your custom configuration.


==== Using Configuration
include::{common-page-prefix-inc}[tag=overriding-using-config]


=== Enrolling other Micrometer meter registries
To create additional types of registries and enroll them with `MicrometerSupport`, you need to:

. Write a `Handler` +
+
Each meter registry has its own way of producing output.
Write your handler so that it has a reference to the meter registry it should use and so that
its `accept` method sets the payload in the HTTP response using the registry's mechanism for creating output.

. Write a `Function` which accepts a `ServerRequest` and returns an `Optional<Handler>` +
+
In general, your function looks at the request--the `Content-Type`, query parameters, etc.--to
decide whether your handler should respond to the request.
If so, your function should instantiate your `Handler` and return an `Optional.of(theHandlerInstance)`;
otherwise, your function should return `Optional.empty()`. +
+
When `MicrometerSupport` receives a request, it invokes the functions of all the enrolled registries,
stopping as soon as one function provides a handler.
`MicrometerSupport` then delegates to that handler to create and send the response.

. Pass the `Handler` and `Function` to the `MicrometerSupport.enrollRegistry` method to enroll them +
+
[source,java]
.Creating and enrolling your own Micrometer meter registry
----
MeterRegistry myRegistry = new PrometheusMeterRegistry(myPrometheusConfig); // <1>
MicrometerSupport support = MicrometerSupport.builder()
.enrollRegistry(myRegistry,
request -> request // <2>
.headers()
.bestAccepted(MediaType.TEXT_PLAIN).isPresent()
? Optional.of((req, resp) ->
resp.send(myRegistry.scrape())) // <3>
: Optional.empty())
.build();
----
<1> Create the meter registry. This example uses a Prometheus registry but it can be any extension of `MeterRegistry`.
<2> Provide the function that checks if the link:{javadoc-base-url-webserver}/ServerRequest.html[`ServerRequest`]
accepts content that your meter registry can produce (e.g., either `text/plain` or unspecified is normally an indication for Prometheus-style output)
and returns the appropriate `Optional<link:{:javadoc-base-url-webserver}/Handler.html[``Handler``]>`.
<3> A very simple in-line `Handler` that sets the response entity from the Prometheus registry's `scrape()` method.

include::{common-page-prefix-inc}[tag=accessing-endpoint-intro]

When `MicrometerSupport` receives a request at the endpoint, it looks for the first enrolled meter registry for which
the corresponding `Function<ServerRequest, Optional<Handler>>` returns a non-empty `Handler`.
Helidon invokes that `Handler` which must retrieve the metrics output from its meter registry and set
and send the response.
Note that the `Handler` which your function returns typically has a reference to the meter registry it will use
in preparing the response.


Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
///////////////////////////////////////////////////////////////////////////////

Copyright (c) 2019, 2020 Oracle and/or its affiliates.
Copyright (c) 2019, 2021 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
Loading