git clone https://github.com/openliberty/guide-microprofile-metrics.git
cd guide-microprofile-metrics
Contents
Tags
Providing metrics from a microservice
Prerequisites:
You’ll explore how to provide system and application metrics from a microservice with MicroProfile Metrics.
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
The fastest way to work through this guide is to clone the Git repository and use the projects that are provided inside:
The start
directory contains the starting project that you will build upon.
The finish
directory contains the finished project that you will build.
Before you begin, make sure you have all the necessary prerequisites.
Try what you’ll build
The finish
directory in the root of this guide contains the finished application. Give it a try before you proceed.
To try out the application, first go to the finish
directory and run the following Maven goal to build the application and deploy it to Open Liberty:
cd finish
mvn liberty:run
After you see the following message, your Liberty instance is ready:
The defaultServer server is ready to run a smarter planet.
Point your browser to the 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 URL to add the localhost into the inventory.
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, point your browser to the 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.
See the following sample outputs for the @Timed
, @Gauge
, and @Counted
metrics:
# TYPE application_inventoryProcessingTime_rate_per_second gauge application_inventoryProcessingTime_rate_per_second{method="get"} 0.0019189661542898407 ... # TYPE application_inventoryProcessingTime_seconds summary # HELP application_inventoryProcessingTime_seconds Time needed to process the inventory application_inventoryProcessingTime_seconds_count{method="get"} 1 application_inventoryProcessingTime_seconds{method="get",quantile="0.5"} 0.127965469 ... # TYPE application_inventoryProcessingTime_rate_per_second gauge application_inventoryProcessingTime_rate_per_second{method="list"} 0.0038379320982686884 ... # TYPE application_inventoryProcessingTime_seconds summary # HELP application_inventoryProcessingTime_seconds Time needed to process the inventory application_inventoryProcessingTime_seconds_count{method="list"} 2 application_inventoryProcessingTime_seconds{method="list",quantile="0.5"} 2.2185000000000002E-5 ...
# TYPE application_inventorySizeGauge gauge # HELP application_inventorySizeGauge Number of systems in the inventory application_inventorySizeGauge 1
# TYPE application_inventoryAccessCount_total counter # HELP application_inventoryAccessCount_total Number of times the list of systems method is requested application_inventoryAccessCount_total 1
To see only the system metrics, point your browser to https://localhost:9443/metrics?scope=base.
See the following sample output:
# TYPE base_jvm_uptime_seconds gauge # HELP base_jvm_uptime_seconds Displays the start time of the Java virtual machine in milliseconds. This attribute displays the approximate time when the Java virtual machine started. base_jvm_uptime_seconds 30.342000000000002
# TYPE base_classloader_loadedClasses_count gauge # HELP base_classloader_loadedClasses_count Displays the number of classes that are currently loaded in the Java virtual machine. base_classloader_loadedClasses_count 11231
To see only the vendor metrics, point your browser to https://localhost:9443/metrics?scope=vendor.
See the following sample output:
# TYPE vendor_threadpool_size gauge # HELP vendor_threadpool_size The size of the thread pool. vendor_threadpool_size{pool="Default_Executor"} 32
# TYPE vendor_servlet_request_total counter # HELP vendor_servlet_request_total The number of visits to this servlet from the start of the server. vendor_servlet_request_total{servlet="microprofile_metrics_io_openliberty_guides_inventory_InventoryApplication"} 1
After you are finished checking out the application, stop the Liberty instance by pressing CTRL+C
in the command-line session where you ran Liberty. Alternatively, you can run the liberty:stop
goal from the finish
directory in another shell session:
mvn liberty:stop
Adding MicroProfile Metrics to the inventory service
pom.xml
1<?xml version='1.0' encoding='utf-8'?>
2<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
3
4 <modelVersion>4.0.0</modelVersion>
5
6 <groupId>io.openliberty.guides</groupId>
7 <artifactId>guide-microprofile-metrics</artifactId>
8 <version>1.0-SNAPSHOT</version>
9 <packaging>war</packaging>
10
11 <properties>
12 <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
13 <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
14 <maven.compiler.source>11</maven.compiler.source>
15 <maven.compiler.target>11</maven.compiler.target>
16 <!-- Liberty configuration -->
17 <liberty.var.http.port>9080</liberty.var.http.port>
18 <liberty.var.https.port>9443</liberty.var.https.port>
19 </properties>
20
21 <dependencies>
22 <!-- Provided dependencies -->
23 <dependency>
24 <groupId>jakarta.platform</groupId>
25 <artifactId>jakarta.jakartaee-api</artifactId>
26 <version>10.0.0</version>
27 <scope>provided</scope>
28 </dependency>
29 <dependency>
30 <groupId>org.eclipse.microprofile</groupId>
31 <!-- tag::microprofile[] -->
32 <artifactId>microprofile</artifactId>
33 <!-- end::microprofile[] -->
34 <version>6.1</version>
35 <type>pom</type>
36 <scope>provided</scope>
37 </dependency>
38 <!-- For tests -->
39 <dependency>
40 <groupId>org.junit.jupiter</groupId>
41 <artifactId>junit-jupiter</artifactId>
42 <version>5.11.3</version>
43 <scope>test</scope>
44 </dependency>
45 <dependency>
46 <groupId>org.jboss.resteasy</groupId>
47 <artifactId>resteasy-client</artifactId>
48 <version>6.2.10.Final</version>
49 <scope>test</scope>
50 </dependency>
51 <dependency>
52 <groupId>org.jboss.resteasy</groupId>
53 <artifactId>resteasy-json-binding-provider</artifactId>
54 <version>6.2.10.Final</version>
55 <scope>test</scope>
56 </dependency>
57 <dependency>
58 <groupId>org.glassfish</groupId>
59 <artifactId>jakarta.json</artifactId>
60 <version>2.0.1</version>
61 <scope>test</scope>
62 </dependency>
63 <!-- Java utility classes -->
64 <dependency>
65 <groupId>org.apache.commons</groupId>
66 <artifactId>commons-lang3</artifactId>
67 <version>3.17.0</version>
68 </dependency>
69 </dependencies>
70
71 <build>
72 <finalName>${project.artifactId}</finalName>
73 <plugins>
74 <plugin>
75 <groupId>org.apache.maven.plugins</groupId>
76 <artifactId>maven-war-plugin</artifactId>
77 <version>3.4.0</version>
78 </plugin>
79 <!-- Plugin to run unit tests -->
80 <plugin>
81 <groupId>org.apache.maven.plugins</groupId>
82 <artifactId>maven-surefire-plugin</artifactId>
83 <version>3.5.1</version>
84 </plugin>
85 <!-- Enable liberty-maven plugin -->
86 <plugin>
87 <groupId>io.openliberty.tools</groupId>
88 <artifactId>liberty-maven-plugin</artifactId>
89 <version>3.11.1</version>
90 </plugin>
91 <!-- Plugin to run functional tests -->
92 <plugin>
93 <groupId>org.apache.maven.plugins</groupId>
94 <artifactId>maven-failsafe-plugin</artifactId>
95 <version>3.5.1</version>
96 <configuration>
97 <systemPropertyVariables>
98 <http.port>${liberty.var.http.port}</http.port>
99 <https.port>${liberty.var.https.port}</https.port>
100 <javax.net.ssl.trustStore>${project.build.directory}/liberty/wlp/usr/servers/defaultServer/resources/security/key.jks</javax.net.ssl.trustStore>
101 </systemPropertyVariables>
102 </configuration>
103 </plugin>
104 </plugins>
105 </build>
106</project>
Navigate to the start
directory to begin.
When you run Open Liberty in dev mode, dev mode listens for file changes and automatically recompiles and deploys your updates whenever you save a new change. Run the following goal to start Open Liberty in dev mode:
mvn liberty:dev
After you see the following message, your Liberty instance is ready in dev mode:
************************************************************** * Liberty is running in dev mode.
Dev mode holds your command-line session to listen for file changes. Open another command-line session to continue, or open the project in your editor.
The MicroProfile Metrics API is included in the MicroProfile dependency specified by your pom.xml
file. Look for the dependency with the 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.
Replace the Libertyserver.xml
configuration file.src/main/liberty/config/server.xml
server.xml
1<server description="Sample Liberty server">
2
3 <featureManager>
4 <feature>restfulWS-3.1</feature>
5 <feature>jsonp-2.1</feature>
6 <feature>jsonb-3.0</feature>
7 <feature>cdi-4.0</feature>
8 <feature>mpConfig-3.1</feature>
9 <feature>mpMetrics-5.1</feature>
10 <feature>mpRestClient-3.0</feature>
11 </featureManager>
12
13 <variable name="http.port" defaultValue="9080"/>
14 <variable name="https.port" defaultValue="9443"/>
15
16 <applicationManager autoExpand="true" />
17 <!-- tag::quickStartSecurity[] -->
18 <quickStartSecurity userName="admin" userPassword="adminpwd"/>
19 <!-- end::quickStartSecurity[] -->
20 <httpEndpoint host="*" httpPort="${http.port}"
21 httpsPort="${https.port}" id="defaultHttpEndpoint"/>
22 <webApplication location="guide-microprofile-metrics.war" contextRoot="/"/>
23</server>
The mpMetrics
feature enables MicroProfile Metrics support in Open Liberty. Note that this feature requires SSL and the configuration has been provided for you.
The 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
Replace theInventoryManager
class.src/main/java/io/openliberty/guides/inventory/InventoryManager.java
InventoryManager.java
Apply the @Timed
annotation to the get()
method,
and apply the @Timed
annotation to the list()
method.
This annotation has these metadata fields:
|
Optional. Use this field to name the metric. |
|
Optional. Use this field to add tags to the metric with the same |
|
Optional. Use this field to determine whether the metric name is the exact name that is specified in the |
|
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 get()
and list()
methods are annotated with the @Timed
metric and have the same inventoryProcessingTime
name. The method=get
and method=list
tags add a dimension that uniquely identifies the collected metric data from the inventory processing time in getting the system properties.
-
The
method=get
tag identifies theinventoryProcessingTime
metric that measures the elapsed time to get the system properties when you call thesystem
service. -
The
method=list
tag identifies theinventoryProcessingTime
metric that measures the elapsed time for theinventory
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 @Timed
annotation to the 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 @Counted
annotation to the list()
method to count how many times the http://localhost:9080/inventory/systems
URL is accessed monotonically, which is counting up sequentially.
Apply the @Gauge
annotation to the getTotal()
method to track the number of systems that are stored in the inventory. When the value of the gauge is retrieved, the underlying getTotal()
method is called to return the size of the inventory. Note the additional metadata field:
|
Set the unit of the metric. If it is |
Additional information about these annotations, relevant metadata fields, and more are available at the MicroProfile Metrics Annotation Javadoc.
Enabling vendor metrics for the microservices
server.xml
1<server description="Sample Liberty server">
2
3 <featureManager>
4 <feature>restfulWS-3.1</feature>
5 <feature>jsonp-2.1</feature>
6 <feature>jsonb-3.0</feature>
7 <feature>cdi-4.0</feature>
8 <feature>mpConfig-3.1</feature>
9 <feature>mpMetrics-5.1</feature>
10 <feature>mpRestClient-3.0</feature>
11 </featureManager>
12
13 <variable name="http.port" defaultValue="9080"/>
14 <variable name="https.port" defaultValue="9443"/>
15
16 <applicationManager autoExpand="true" />
17 <!-- tag::quickStartSecurity[] -->
18 <quickStartSecurity userName="admin" userPassword="adminpwd"/>
19 <!-- end::quickStartSecurity[] -->
20 <httpEndpoint host="*" httpPort="${http.port}"
21 httpsPort="${https.port}" id="defaultHttpEndpoint"/>
22 <webApplication location="guide-microprofile-metrics.war" contextRoot="/"/>
23</server>
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 mpMetrics
feature is enabled in the 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 Metrics reference list for more information.
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.
Point your browser to the 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 URL. Reload the https://localhost:9443/metrics URL, or access only the application metrics at the 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.
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.
Create theMetricsIT
class.src/test/java/it/io/openliberty/guides/metrics/MetricsIT.java
MetricsIT.java
InventoryManager.java
-
The
testPropertiesRequestTimeMetric()
test case validates the@Timed
metric. The test case sends a request to thehttp://localhost:9080/inventory/systems/localhost
URL to access theinventory
service, which adds thelocalhost
host to the inventory. Next, the test case makes a connection to thehttps://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
testInventoryAccessCountMetric()
test case validates the@Counted
metric. The test case obtains metric data before and after a request to thehttp://localhost:9080/inventory/systems
URL. It then asserts that the metric was increased after the URL was accessed. -
The
testInventorySizeGaugeMetric()
test case validates the@Gauge
metric. The test case first ensures that the localhost is in the inventory, then looks for the@Gauge
metric and asserts that the inventory size is greater or equal to 1. -
The
testPropertiesAddTimeMetric()
test case validates the@Timed
metric. The test case sends a request to thehttp://localhost:9080/inventory/systems/localhost
URL to access theinventory
service, which adds thelocalhost
host to the inventory. Next, the test case makes a connection to thehttps://localhost:9443/metrics?scope=application
URL to retrieve application metrics as plain text. Then, it looks for the@Timed
metric and asserts true if the metric exists.
The oneTimeSetup()
method retrieves the port number for the Liberty and builds a base URL string to set up the tests. Apply the @BeforeAll
annotation to this method to run it before any of the test cases.
The setup()
method creates a JAX-RS client that makes HTTP requests to the inventory
service. The teardown()
method destroys this client instance. Apply the @BeforeEach
annotation so that a method runs before a test case and apply the @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 MetricsIT
test class with the @TestMethodOrder(OrderAnnotation.class)
annotation. OrderAnnotation.class
runs test methods in numerical order, according to the values specified in the @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 @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
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:
-------------------------------------------------------
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 MetricsIT.java
file and change any of the assertions in the test methods. Then re-run the tests to see a test failure occur.
When you are done checking out the service, exit dev mode by pressing CTRL+C
in the command-line session where you ran Liberty.
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.
Guide Attribution
Providing metrics from a microservice by Open Liberty is licensed under CC BY-ND 4.0
Prerequisites:
Nice work! Where to next?
What did you think of this guide?
Thank you for your feedback!
What could make this guide better?
Raise an issue to share feedback
Create a pull request to contribute to this guide
Need help?
Ask a question on Stack Overflow