An open API service indexing awesome lists of open source software.

https://github.com/openliberty/guide-microprofile-metrics

A guide on how to provide system and application metrics from a microservice with MicroProfile Metrics: https://openliberty.io/guides/microprofile-metrics.html
https://github.com/openliberty/guide-microprofile-metrics

Last synced: 10 months ago
JSON representation

A guide on how to provide system and application metrics from a microservice with MicroProfile Metrics: https://openliberty.io/guides/microprofile-metrics.html

Awesome Lists containing this project

README

          

// Copyright (c) 2018, 2025 IBM Corporation and others.
// Licensed under Creative Commons Attribution-NoDerivatives
// 4.0 International (CC BY-ND 4.0)
// https://creativecommons.org/licenses/by-nd/4.0/
//
// Contributors:
// IBM Corporation
:projectid: microprofile-metrics
:page-layout: guide-multipane
:page-duration: 15 minutes
:page-releasedate: 2018-03-15
:page-guide-category: microprofile
:page-essential: false
:page-description: Learn how to use MicroProfile Metrics to provide system and application metrics from a microservice.
:page-tags: ['microprofile']
:page-permalink: /guides/{projectid}
:page-related-guides: ['rest-intro', 'microprofile-health', 'cdi-intro']
:common-includes: https://raw.githubusercontent.com/OpenLiberty/guides-common/prod
:page-seo-title: Monitoring the metrics of Java microservices using Eclipse MicroProfile Metrics
:page-seo-description: A getting started tutorial and an example on how to expose system and application metrics from Java microservices and monitor metrics endpoints using Eclipse MicroProfile Metrics.
:source-highlighter: prettify
:guide-author: Open Liberty
= Providing metrics from a microservice

[.hidden]
NOTE: This repository contains the guide documentation source. To view the guide in published form, view it on the https://openliberty.io/guides/{projectid}.html[Open Liberty website].

You'll explore how to provide system and application metrics from a microservice with MicroProfile Metrics.

// =================================================================================================
// What you'll learn
// =================================================================================================

== What you'll learn

You will learn how to use MicroProfile Metrics to provide metrics from a microservice. You can monitor metrics to determine the performance and health of a service. You can also use them to pinpoint issues, collect data for capacity planning, or to decide when to scale a service to run with more or fewer resources.

The application that you will work with is an `inventory` service that stores information about various systems. The `inventory` service communicates with the `system` service on a particular host to retrieve its system properties when necessary.

You will use annotations provided by MicroProfile Metrics to instrument the `inventory` service to provide application-level metrics data. You will add counter, gauge, and timer metrics to the service.

You will also check well-known REST endpoints that are defined by MicroProfile Metrics to review the metrics data collected. Monitoring agents can access these endpoints to collect metrics.

// =================================================================================================
// Getting Started
// =================================================================================================
[role=command]
include::{common-includes}/gitclone.adoc[]

// =================================================================================================
// Try what you'll build
// =================================================================================================

[role='command']
include::{common-includes}/twyb-intro-mvnw.adoc[]

// static guide instructions:
ifndef::cloud-hosted[]
Point your browser to the http://localhost:9080/inventory/systems[http://localhost:9080/inventory/systems^] URL to access the `inventory` service. Because you just started the application, the inventory is empty. Access the http://localhost:9080/inventory/systems/localhost[http://localhost:9080/inventory/systems/localhost^] URL to add the localhost into the inventory.

Access the `inventory` service at the http://localhost:9080/inventory/systems[http://localhost:9080/inventory/systems^] URL at least once so that application metrics are collected. Otherwise, the metrics do not appear.

Next, point your browser to the https://localhost:9443/metrics[https://localhost:9443/metrics^] MicroProfile Metrics endpoint. Log in as the `admin` user with `adminpwd` as the password. You can see both the system and application metrics in a text format.

To see only the application metrics, point your browser to https://localhost:9443/metrics?scope=application[https://localhost:9443/metrics?scope=application^].
endif::[]

// cloud-hosted guide instructions:
ifdef::cloud-hosted[]
Open another command-line session by selecting ***Terminal*** > ***New Terminal*** from the menu of the IDE.

Run the following curl command to access the **inventory** service. Because you just started the application, the inventory is empty.
```bash
curl -s http://localhost:9080/inventory/systems | jq
```

Run the following curl command to add the ***localhost*** into the inventory.
```bash
curl -s http://localhost:9080/inventory/systems/localhost | jq
```

Access the ***inventory*** service at the ***http://localhost:9080/inventory/systems*** URL at least once so that application metrics are collected. Otherwise, the metrics do not appear.

Next, run the following curl command to visit the MicroProfile Metrics endpoint by the ***admin*** user with ***adminpwd*** as the password. You can see both the system and application metrics in a text format.
```bash
curl -k --user admin:adminpwd https://localhost:9443/metrics
```

To see only the application metrics, run the following curl command:
```bash
curl -k --user admin:adminpwd https://localhost:9443/metrics?scope=application
```
endif::[]

See the following sample outputs for the `@Timed`, `@Gauge`, and `@Counted` metrics:

[role="no_copy"]
----
# TYPE inventoryProcessingTime_seconds_max gauge
inventoryProcessingTime_seconds_max{method="list",mp_scope="application",} 3.0375E-5
inventoryProcessingTime_seconds_max{method="get",mp_scope="application",} 0.1997325
# HELP inventoryProcessingTime_seconds Time needed to process the inventory
# TYPE inventoryProcessingTime_seconds summary
inventoryProcessingTime_seconds{method="list",mp_scope="application",quantile="0.5",} 0.0
inventoryProcessingTime_seconds{method="list",mp_scope="application",quantile="0.75",} 0.0
...
inventoryProcessingTime_seconds_count{method="list",mp_scope="application",} 2.0
inventoryProcessingTime_seconds_sum{method="list",mp_scope="application",} 3.6792E-5
inventoryProcessingTime_seconds{method="get",mp_scope="application",quantile="0.5",} 0.0
inventoryProcessingTime_seconds{method="get",mp_scope="application",quantile="0.75",} 0.0
...
inventoryProcessingTime_seconds_count{method="get",mp_scope="application",} 1.0
inventoryProcessingTime_seconds_sum{method="get",mp_scope="application",} 0.1997325
...
# HELP inventoryAddingTime_seconds_max Time needed to add system properties to the inventory
# TYPE inventoryAddingTime_seconds_max gauge
inventoryAddingTime_seconds_max{mp_scope="application",} 3.1E-5
# HELP inventoryAddingTime_seconds Time needed to add system properties to the inventory
# TYPE inventoryAddingTime_seconds summary
inventoryAddingTime_seconds{mp_scope="application",quantile="0.5",} 0.0
inventoryAddingTime_seconds{mp_scope="application",quantile="0.75",} 0.0
...
inventoryAddingTime_seconds_count{mp_scope="application",} 1.0
inventoryAddingTime_seconds_sum{mp_scope="application",} 3.1E-5
...
----

[role="no_copy"]
----
# HELP inventorySizeGauge Number of systems in the inventory
# TYPE inventorySizeGauge gauge
inventorySizeGauge{mp_scope="application",} 1.0
----

[role="no_copy"]
----
# HELP inventoryAccessCount_total Number of times the list of systems method is requested
# TYPE inventoryAccessCount_total counter
inventoryAccessCount_total{mp_scope="application",} 2.0
----

// static guide instructions:
ifndef::cloud-hosted[]
To see only the system metrics, point your browser to https://localhost:9443/metrics?scope=base[https://localhost:9443/metrics?scope=base^].
endif::[]

// cloud-hosted guide instructions:
ifdef::cloud-hosted[]
To see only the system metrics, run the following curl command:
```bash
curl -k --user admin:adminpwd https://localhost:9443/metrics?scope=base
```
endif::[]

See the following sample output:

[role="no_copy"]
----
# HELP jvm_uptime_seconds Displays the time from the start of the Java virtual machine in seconds.
# TYPE jvm_uptime_seconds gauge
jvm_uptime_seconds{mp_scope="base",} 730.705
----
[role="no_copy"]
----
# HELP classloader_loadedClasses_count Displays the number of classes that are currently loaded in the Java virtual machine.
# TYPE classloader_loadedClasses_count gauge
classloader_loadedClasses_count{mp_scope="base",} 13033.0
----

// static guide instructions:
ifndef::cloud-hosted[]
To see only the vendor metrics, point your browser to https://localhost:9443/metrics?scope=vendor[https://localhost:9443/metrics?scope=vendor^].
endif::[]

// cloud-hosted guide instructions:
ifdef::cloud-hosted[]
To see only the vendor metrics, run the following curl command:
```bash
curl -k --user admin:adminpwd https://localhost:9443/metrics?scope=vendor
```
endif::[]

See the following sample output:

[role="no_copy"]
----
# HELP threadpool_size The size of the thread pool.
# TYPE threadpool_size gauge
threadpool_size{mp_scope="vendor",pool="Default_Executor",} 24.0
----
[role="no_copy"]
----
# HELP servlet_request_total The number of visits to this servlet ... the start of the server.
# TYPE servlet_request_total counter
servlet_request_total{mp_scope="vendor",servlet="guide_microprofile_metrics_io_openliberty_guides_system_SystemApplication",} 1.0
servlet_request_total{mp_scope="vendor",servlet="guide_microprofile_metrics_io_openliberty_guides_inventory_InventoryApplication",} 3.0
servlet_request_total{mp_scope="vendor",servlet="io_openliberty_microprofile_metrics_5_0_private_internal_PrivateMetricsRESTProxyServlet",} 3.0
----

include::{common-includes}/twyb-end-mvnw.adoc[]

// =================================================================================================
// Adding MicroProfile Metrics to the inventory service
// =================================================================================================

== Adding MicroProfile Metrics to the inventory service

pom.xml
[source, xml, linenums, role='code_column']
----
include::finish/pom.xml[]
----

// static guide instructions:
ifndef::cloud-hosted[]
Navigate to the `start` directory to begin.
endif::[]

// cloud-hosted guide instructions:
ifdef::cloud-hosted[]
To begin, run the following command to navigate to the **start** directory:
```bash
cd /home/project/guide-microprofile-metrics/start
```
endif::[]

include::{common-includes}/devmode-mvnw-start.adoc[]

The MicroProfile Metrics API is included in the MicroProfile dependency specified by your [hotspot file=0]`pom.xml` file. Look for the dependency with the [hotspot=microprofile file=0]`microprofile` artifact ID. This dependency provides a library that allows you to use the MicroProfile Metrics API in your code to provide metrics from your microservices.

[role="code_command hotspot file=1", subs="quotes"]
----
#Replace the Liberty `server.xml` configuration file.#
`src/main/liberty/config/server.xml`
----

server.xml
[source, xml, linenums, role='code_column']
----
include::finish/src/main/liberty/config/server.xml[]
----

The [hotspot=mpMetrics file=1]`mpMetrics` feature enables MicroProfile Metrics support in Open Liberty. Note that this feature requires SSL and the configuration has been provided for you.

The [hotspot=quickStartSecurity file=1]`quickStartSecurity` configuration element provides basic security to secure the Liberty. When you visit the `/metrics` endpoint, use the credentials defined in the Liberty's configuration to log in and view the data.

// =================================================================================================
// Adding the annotations
// =================================================================================================

=== Adding the annotations

[role="code_command hotspot", subs="quotes"]
----
#Replace the `InventoryManager` class.#
`src/main/java/io/openliberty/guides/inventory/InventoryManager.java`
----

InventoryManager.java
[source, java, linenums, role='code_column hide_tags=copyright']
----
include::finish/src/main/java/io/openliberty/guides/inventory/InventoryManager.java[]
----

Apply the [hotspot=timedForGet]`@Timed` annotation to the [hotspot=get]`get()` method,
and apply the [hotspot=timedForList]`@Timed` annotation to the [hotspot=list]`list()` method.

This annotation has these metadata fields:

|===
|[hotspot=nameForGet hotspot=nameForList]`name` | Optional. Use this field to name the metric.
|[hotspot=tagForGet hotspot=tagForList]`tags` | Optional. Use this field to add tags to the metric with the same [hotspot=nameForGet hotspot=nameForList]`name`.
|[hotspot=absoluteForGet hotspot=absoluteForList]`absolute` | Optional. Use this field to determine whether the metric name is the exact name that is specified in the [hotspot=nameForGet hotspot=nameForList]`name` field or that is specified with the package prefix.
|[hotspot=desForGet hotspot=desForList]`description` | Optional. Use this field to describe the purpose of the metric.
|===

The `@Timed` annotation tracks how frequently the method is invoked and how long it takes for each invocation of the method to complete. Both the [hotspot=get]`get()` and [hotspot=list]`list()` methods are annotated with the `@Timed` metric and have the same [hotspot=nameForGet hotspot=nameForList]`inventoryProcessingTime` name. The [hotspot=tagForGet]`method=get` and [hotspot=tagForList]`method=list` tags add a dimension that uniquely identifies the collected metric data from the inventory processing time in getting the system properties.

* The [hotspot=tagForGet]`method=get` tag identifies the [hotspot=nameForGet]`inventoryProcessingTime` metric that measures the elapsed time to get the system properties when you call the `system` service.
* The [hotspot=tagForList]`method=list` tag identifies the [hotspot=nameForList]`inventoryProcessingTime` metric that measures the elapsed time for the `inventory` service to list all of the system properties in the inventory.

The tags allow you to query the metrics together or separately based on the functionality of the monitoring tool of your choice. The `inventoryProcessingTime` metrics for example could be queried to display an aggregate time of both tagged metrics or individual times.

Apply the [hotspot=timedForAdd]`@Timed` annotation to the [hotspot=add]`add()` method to track how frequently the method is invoked and how long it takes for each invocation of the method to complete.

Apply the [hotspot=countedForList]`@Counted` annotation to the [hotspot=list]`list()` method to count how many times the `\http://localhost:9080/inventory/systems` URL is accessed monotonically, which is counting up sequentially.

Apply the [hotspot=gaugeForGetTotal]`@Gauge` annotation to the [hotspot=getTotal]`getTotal()` method to track the number of systems that are stored in the inventory. When the value of the gauge is retrieved, the underlying [hotspot=getTotal]`getTotal()` method is called to return the size of the inventory. Note the additional metadata field:

|===
| [hotspot=unitForGetTotal]`unit` | Set the unit of the metric. If it is [hotspot=unitForGetTotal]`MetricUnits.NONE`, the metric name is used without appending the unit name, no scaling is applied.
|===

Additional information about these annotations, relevant metadata fields, and more are available at
the https://openliberty.io/docs/latest/reference/javadoc/microprofile-6.1-javadoc.html?class=org/eclipse/microprofile/metrics/annotation/package-summary.html&package=allclasses-frame.html&path=microprofile-6.1-javadoc/org/eclipse/microprofile/metrics/annotation/package-summary.html[MicroProfile Metrics Annotation Javadoc^].

== Enabling vendor metrics for the microservices

server.xml
[source, xml, linenums, role='code_column']
----
include::finish/src/main/liberty/config/server.xml[]
----

MicroProfile Metrics API implementers can provide vendor metrics in the same forms as the base and application metrics do. Open Liberty as a vendor supplies server component metrics when the [hotspot=mpMetrics]`mpMetrics` feature is enabled in the [hotspot]`server.xml` configuration file.

You can see the vendor-only metrics in the `metrics?scope=vendor` endpoint. You see metrics from the runtime components, such as Web Application, ThreadPool and Session Management. Note that these metrics are specific to the Liberty instance. Different vendors may provide other metrics. Visit the https://openliberty.io/docs/ref/general/#metrics-list.html[Metrics reference list^] for more information.

// =================================================================================================
// Building and running the application
// =================================================================================================

== Building and running the application

The Open Liberty instance was started in dev mode at the beginning of the guide and all the changes were automatically picked up.

// static guide instructions:
ifndef::cloud-hosted[]
Point your browser to the https://localhost:9443/metrics[https://localhost:9443/metrics^] URL to review all the metrics that are enabled through MicroProfile Metrics. Log in with `admin` as your username and `adminpwd` as your password. You see only the system and vendor metrics because the Liberty instance just started, and the `inventory` service has not been accessed.

Next, point your browser to the http://localhost:9080/inventory/systems[http://localhost:9080/inventory/systems^] URL. Reload the https://localhost:9443/metrics[https://localhost:9443/metrics^] URL, or access only the application metrics at the https://localhost:9443/metrics?scope=application[https://localhost:9443/metrics?scope=application^] URL.

You can see the system metrics in the https://localhost:9443/metrics?scope=base[^] URL as well as see the vendor metrics in the https://localhost:9443/metrics?scope=vendor[^] URL.
endif::[]

// cloud-hosted guide instructions:
ifdef::cloud-hosted[]
Run the following curl command to review all the metrics that are enabled through MicroProfile Metrics. You see only the system and vendor metrics because the Liberty instance just started, and the ***inventory*** service has not been accessed.
```bash
curl -k --user admin:adminpwd https://localhost:9443/metrics
```

Next, run the following curl command to access the **inventory** service:
```bash
curl -s http://localhost:9080/inventory/systems | jq
```

Rerun the following curl command to access the all metrics:
```bash
curl -k --user admin:adminpwd https://localhost:9443/metrics
```

or access only the application metrics by running following curl command:
```bash
curl -k --user admin:adminpwd https://localhost:9443/metrics?scope=application
```

You can see the system metrics by running following curl command:
```bash
curl -k --user admin:adminpwd https://localhost:9443/metrics?scope=base
```

as well as see the vendor metrics by running following curl command:
```bash
curl -k --user admin:adminpwd https://localhost:9443/metrics?scope=vendor
```
endif::[]

// =================================================================================================
// Testing the metrics
// =================================================================================================

== Testing the metrics

You can test your application manually, but automated tests ensure code quality because they trigger a failure whenever a code change introduces a defect. JUnit and the Jakarta Restful Web Services Client API provide a simple environment for you to write tests.

[role="code_command hotspot file=0", subs="quotes"]
----
#Create the `MetricsIT` class.#
`src/test/java/it/io/openliberty/guides/metrics/MetricsIT.java`
----

MetricsIT.java
[source, java, linenums, role='code_column hide_tags=copyright']
----
include::finish/src/test/java/it/io/openliberty/guides/metrics/MetricsIT.java[]
----

InventoryManager.java
[source, java, linenums, role='code_column hide_tags=copyright']
----
include::finish/src/main/java/io/openliberty/guides/inventory/InventoryManager.java[]
----

* The [hotspot=testPropertiesRequestTimeMetric file=0]`testPropertiesRequestTimeMetric()` test case validates the [hotspot=timedForGet file=1]`@Timed` metric. The test case sends a request to the `\http://localhost:9080/inventory/systems/localhost` URL to access the `inventory` service, which adds the `localhost` host to the inventory. Next, the test case makes a connection to the `\https://localhost:9443/metrics?scope=application` URL to retrieve application metrics as plain text. Then, it asserts whether the time that is needed to retrieve the system properties for localhost is less than 4 seconds.

* The [hotspot=testInventoryAccessCountMetric file=0]`testInventoryAccessCountMetric()` test case validates the [hotspot=countedForList file=1]`@Counted` metric. The test case obtains metric data before and after a request to the `\http://localhost:9080/inventory/systems` URL. It then asserts that the metric was increased after the URL was accessed.

* The [hotspot=testInventorySizeGaugeMetric file=0]`testInventorySizeGaugeMetric()` test case validates the [hotspot=gaugeForGetTotal file=1]`@Gauge` metric. The test case first ensures that the localhost is in the inventory, then looks for the [hotspot=gaugeForGetTotal file=1]`@Gauge` metric and asserts that the inventory size is greater or equal to 1.

* The [hotspot=testPropertiesAddTimeMetric file=0]`testPropertiesAddTimeMetric()` test case validates the [hotspot=timedForAdd file=1]`@Timed` metric. The test case sends a request to the `\http://localhost:9080/inventory/systems/localhost` URL to access the `inventory` service, which adds the `localhost` host to the inventory. Next, the test case makes a connection to the `\https://localhost:9443/metrics?scope=application` URL to retrieve application metrics as plain text. Then, it looks for the [hotspot=timedForAdd file=1]`@Timed` metric and asserts true if the metric exists.

The [hotspot=oneTimeSetup file=0]`oneTimeSetup()` method retrieves the port number for the Liberty and builds a base URL string to set up the tests. Apply the [hotspot=BeforeAll file=0]`@BeforeAll` annotation to this method to run it before any of the test cases.

The [hotspot=setup file=0]`setup()` method creates a JAX-RS client that makes HTTP requests to the `inventory` service. The [hotspot=teardown file=0]`teardown()` method destroys this client instance. Apply the [hotspot=BeforeEach file=0]`@BeforeEach` annotation so that a method runs before a test case and apply the [hotspot=AfterEach file=0]`@AfterEach` annotation so that a method runs after a test case. Apply these annotations to methods that are generally used to perform any setup and teardown tasks before and after a test.

To force these test cases to run in a particular order, annotate your [hotspot file=0]`MetricsIT` test class with the [hotspot=TestMethodOrder file=0]`@TestMethodOrder(OrderAnnotation.class)` annotation. [hotspot=TestMethodOrder file=0]`OrderAnnotation.class` runs test methods in numerical order, according to the values specified in the [hotspot=Order1 hotspot=Order2 hotspot=Order3 file=0]`@Order` annotation. You can also create a custom `MethodOrderer` class or use built-in `MethodOrderer` implementations, such as `OrderAnnotation.class`, `Alphanumeric.class`, or `Random.class`. Label your test cases with the [hotspot=Test1 hotspot=Test2 hotspot=Test3 file=0]`@Test` annotation so that they automatically run when your test class runs.

In addition, the endpoint tests `src/test/java/it/io/openliberty/guides/inventory/InventoryEndpointIT.java` and `src/test/java/it/io/openliberty/guides/system/SystemEndpointIT.java` are provided for you to test the basic functionality of the `inventory` and `system` services. If a test failure occurs, then you might have introduced a bug into the code.

// =================================================================================================
// Running the tests
// =================================================================================================

=== Running the tests

Because you started Open Liberty in dev mode at the start of the guide, press the `enter/return` key to run the tests and see the following output:

[source, role="no_copy"]
----
-------------------------------------------------------
T E S T S
-------------------------------------------------------
Running it.io.openliberty.guides.system.SystemEndpointIT
Tests run: 1, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 1.4 sec - in it.io.openliberty.guides.system.SystemEndpointIT
Running it.io.openliberty.guides.metrics.MetricsIT
Tests run: 4, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 1.476 sec - in it.io.openliberty.guides.metrics.MetricsIT
Running it.io.openliberty.guides.inventory.InventoryEndpointIT
[WARNING ] Interceptor for {http://client.inventory.guides.openliberty.io/}SystemClient has thrown exception, unwinding now
Could not send Message.
[err] The specified host is unknown.
Tests run: 3, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 0.264 sec - in it.io.openliberty.guides.inventory.InventoryEndpointIT

Results :

Tests run: 8, Failures: 0, Errors: 0, Skipped: 0
----

The warning and error messages are expected and result from a request to a bad or an unknown hostname. This request is made in the `testUnknownHost()` test from the `InventoryEndpointIT` integration test.

To determine whether the tests detect a failure, go to the [hotspot file=0]`MetricsIT.java` file and change any of the assertions in the test methods. Then re-run the tests to see a test failure occur.

include::{common-includes}/devmode-quit-ctrlc.adoc[]

// =================================================================================================
// Great work! You're done!
// =================================================================================================

== Great work! You're done!

You learned how to enable system, application and vendor metrics for microservices by using MicroProfile Metrics
and wrote tests to validate them in Open Liberty.

include::{common-includes}/attribution.adoc[subs="attributes"]