Specification: Metrics for Eclipse MicroProfile Version: 4.0-RC4 Status: Draft Release: September 29, 2021
Copyright
Copyright (c) 2017 , 2021 Eclipse Foundation.
Eclipse Foundation Specification License
By using and/or copying this document, or the Eclipse Foundation document from which this statement is linked, you (the licensee) agree that you have read, understood, and will comply with the following terms and conditions:
Permission to copy, and distribute the contents of this document, or the Eclipse Foundation document from which this statement is linked, in any medium for any purpose and without fee or royalty is hereby granted, provided that you include the following on ALL copies of the document, or portions thereof, that you use:
-
link or URL to the original Eclipse Foundation document.
-
All existing copyright notices, or if one does not exist, a notice (hypertext is preferred, but a textual representation is permitted) of the form: "Copyright (c) [$date-of-document] Eclipse Foundation, Inc. <<url to this license>>"
Inclusion of the full text of this NOTICE must be provided. We request that authorship attribution be provided in any software, documents, or other items or products that you create pursuant to the implementation of the contents of this document, or any portion thereof.
No right to create modifications or derivatives of Eclipse Foundation documents is granted pursuant to this license, except anyone may prepare and distribute derivative works and portions of this document in software that implements the specification, in supporting materials accompanying such software, and in documentation of such software, PROVIDED that all such works include the notice below. HOWEVER, the publication of derivative works of this document for use as a technical specification is expressly prohibited.
The notice is:
"Copyright (c) [$date-of-document] Eclipse Foundation. This software or document includes material copied from or derived from [title and URI of the Eclipse Foundation specification document]."
Disclaimers
THIS DOCUMENT IS PROVIDED "AS IS," AND THE COPYRIGHT HOLDERS AND THE ECLIPSE FOUNDATION MAKE NO REPRESENTATIONS OR WARRANTIES, EXPRESS OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, NON-INFRINGEMENT, OR TITLE; THAT THE CONTENTS OF THE DOCUMENT ARE SUITABLE FOR ANY PURPOSE; NOR THAT THE IMPLEMENTATION OF SUCH CONTENTS WILL NOT INFRINGE ANY THIRD PARTY PATENTS, COPYRIGHTS, TRADEMARKS OR OTHER RIGHTS.
THE COPYRIGHT HOLDERS AND THE ECLIPSE FOUNDATION WILL NOT BE LIABLE FOR ANY DIRECT, INDIRECT, SPECIAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF ANY USE OF THE DOCUMENT OR THE PERFORMANCE OR IMPLEMENTATION OF THE CONTENTS THEREOF.
The name and trademarks of the copyright holders or the Eclipse Foundation may NOT be used in advertising or publicity pertaining to this document or its contents without specific, written prior permission. Title to copyright in this document will at all times remain with copyright holders.
Introduction
To ensure reliable operation of software it is necessary to monitor essential system parameters. This enhancement proposes the addition of well-known monitoring endpoints and metrics for each process adhering to the Eclipse MicroProfile standard.
This proposal does not talk about health checks. There is a separate specification for Health Checks.
Future Direction
Over the last few months the group involved in metrics has been investigating Micrometer. This investigation will lead to changes to MP Metrics for releases in 2021. What exactly will change in the specification and what level of compatibility will be maintained between MP Metrics 3.0 and 4.x releases hasn’t been determined. The intent will be to shift towards Micrometer as a solution suited to providing metrics for Site Reliability tooling.
Motivation
Reliable service of a platform needs monitoring. There is already JMX as standard to expose metrics, but remote-JMX is not easy to deal with and especially does not fit well in a polyglot environment where other services are not running on the JVM. To enable monitoring in an easy fashion it is necessary that all MicroProfile implementations follow a certain standard with respect to (base) API path, data types involved, always available metrics and return codes used.
Difference to health checks
Health checks are primarily targeted at a quick yes/no response to the question "Is my application still running ok?". Modern systems that schedule the starting of applications (e.g. Kubernetes) use this information to restart the application if the answer is 'no'.
Metrics on the other hand can help to determine the health. Beyond this they serve to pinpoint issues, provide long term trend data for capacity planning and pro-active discovery of issues (e.g. disk usage growing without bounds). Metrics can also help those scheduling systems decide when to scale the application to run on more or fewer machines.
Architecture
This chapter describes the architectural overview of how metrics are setup, stored and exposed for consumption. This chapter also lists the various scopes of metrics.
See section Required Metrics for more information regarding metrics that are required for each vendor.
See section Application Metrics Programming Model for more information regarding the application metrics programming model.
Metrics Setup
Metrics that are exposed need to be configured in the server. On top of the pure metrics, metadata needs to be provided.
The following three sets of sub-resource (scopes) are exposed.
-
base: metrics that all MicroProfile vendors have to provide
-
vendor: vendor specific metrics (optional)
-
application: application-specific metrics (optional)
Scopes
Required Base metrics
Required base metrics describe a set of metrics that all MicroProfile-compliant servers have to provide. Each vendor can implement the set-up of the metrics in the base scope in a vendor-specific way. The metrics can be hard coded into the server or read from a configuration file or supplied via the Java-API described in Application Metrics Programming Model. The Appendix shows a possible data format for such a configuration. The configuration and set up of the base scope is thus an implementation detail and is not expected to be portable across vendors.
Section Required Metrics lists the required metrics. This list also includes a few items marked as optional. These are listed here as they are dependent on the underlying JVM and not the server and thus fit better in base scope than the vendor one.
The optional REST metrics are listed as base metrics as they are expected to be portable between different implementations. If the implementation provides REST metrics, it is up to the implementation to decide how to enable the REST metrics.
Required base metrics are exposed under /metrics/base
.
The base
scope is used for, and only for, any metrics that are defined in MicroProfile specifications.
Metrics in the base scope are intended to be portable between different MicroProfile-compatible runtimes.
Application metrics
Application specific metrics can not be baked into the server as they are supposed to be provided by the application at runtime. Therefore a Java API is provided. Application specific metrics are supposed to be portable to other MicroProfile implementations. That means that an application written to this specification which exposes metrics, can expose the same metrics on a different compliant server without change.
Details of this Java API are described in Application Metrics Programming Model.
Application specific metrics are exposed under /metrics/application
.
Vendor specific Metrics
It is possible for MicroProfile server implementors to supply their specific metrics data on top
of the basic set of required metrics.
Vendor specific metrics are exposed under /metrics/vendor
.
Examples for vendor specific data could be metrics like:
-
OSGi statistics if the MicroProfile-enabled container internally runs on top of OSGi.
-
Statistics of some internal caching modules
-
Any other metrics that are generated by application frameworks, but not directly declared in application code, if these metrics are not based on any specification and therefore not expected to be portable between different runtimes that might support the same application framework.
Vendor specific metrics are not supposed to be portable between different implementations of MicroProfile servers, even if they are compliant with the same version of this specification.
Tags
Tags (or labels) play an important role in modern microservices and microservice scheduling systems (like e.g. Kubernetes). Application code can run on any node and can be re-scheduled to a different node at any time. Each container in such an environment gets its own ID; when the container is stopped and a new one started for the same image, it will get a different id. The classical mapping of host/node and application runtime on it, therefore no longer works.
Tags have taken over the role to, for example, identify an application (app=myShop
), the tier inside the application
(tier=database
or tier=app_server
) and also the node/container id. Metric value aggregation can then work over label
queries (Give me the API hit count for app=myShop && tier=app_server
).
In MicroProfile Metrics, tags add an additional dimension to metrics that share a common basis. For example, a metric named
carCount
can be further differentiated by the car type (sedan, SUV, coupe, and etc) and the colour (red, blue, white, black,
and etc). Rather than incorporating this in the metric name, tags can be used to capture this information in separate metrics.
carCount{type=sedan,colour=red}
carCount{type=sedan,colour=blue}
carCount{type=suv,colour=red}
carCount{type=coupe,colour=blue}
For portability reasons, the key name for the tag must match the regex [a-zA-Z_][a-zA-Z0-9_]*
(Ascii alphabet, numbers and underscore).
If an illegal character is used, the implementation must throw an IllegalArgumentException
.
If a duplicate tag is used, the last occurrence of the tag is used.
The tag value may contain any UTF-8 encoded character.
The REST endpoints provided by MicroProfile Metrics have different reserved characters based on the format. The characters are only escaped as needed when exposed through the REST endpoints. See REST endpoints for more information on the reserved characters. |
Tags can be supplied in two ways:
-
At the level of a metric as described in Application Metrics Programming Model.
-
At the application server level by using MicroProfile Config and setting a configuration property of the name
mp.metrics.tags
. The implementation MUST make sure that an implementation of MicroProfile Config version at least 2.0 is available at runtime. If it is supplied as an environment variable rather than system property, it can be namedMP_METRICS_TAGS
and will be picked up too.-
Tag values set through
mp.metrics.tags
MUST escape equal symbols=
and commas,
with a backslash\
-
export MP_METRICS_TAGS=app=shop,tier=integration,special=deli\=ver\,y
Global tags and tags registered with the metric are included in the output returned from the REST API.
Global tags MUST NOT be added to the MetricID
objects. Global tags must be included in list of tags when metrics are exported.
In application servers with multiple applications deployed, there is one reserved tag name: _app , which serves for
distinguishing metrics from different applications and must not be used for any other purpose. For details,
see section Usage of MicroProfile Metrics in application servers with multiple applications.
|
Metadata
Metadata can be specified for metrics in any scope. For base metrics, metadata must be provided by the implementation. Metadata is exposed by the REST handler.
While technically it is possible to expose metrics without (some) of the metadata, it helps tooling and also operators when correct metadata is provided, as this helps getting a context and an explanation of the metric. |
The Metadata:
-
name: The name of the metric.
-
unit: a fixed set of string units
-
type:
-
counter: a monotonically increasing numeric value (e.g. total number of requests received).
-
concurrent gauge: an incrementally increasing or decreasing numeric value (e.g. number of parallel invocations of a method).
This type exposes three values: current count, highest count within the previous completed full minute and lowest count within the previous completed full minute.
Full minute is the minute from second 0 to just before second 0 on the next minute ( eg. from [10:46:00-10:46:59.99999999] ).
-
gauge: a metric that is sampled to obtain its value (e.g. cpu temperature or disk usage).
-
meter: a metric which tracks mean throughput and one-, five-, and fifteen-minute exponentially-weighted moving average throughput.
-
histogram: a metric which calculates the distribution of a value.
-
timer: a metric which aggregates timing durations and provides duration statistics, plus throughput statistics.
-
simple timer: a lightweight alternative to the timer metric that only tracks the elapsed time duration, invocation counts, highest recorded time duration within the previous completed full minute and lowest recorded time duration within the previous completed full minute. The simple timer may be preferrable over the timer when used with Prometheus as the statistical calculations can be deferred to Prometheus using the simple timer’s available values.
-
-
description (optional): A human readable description of the metric.
-
displayName (optional): A human readable name of the metric for display purposes if the metric name is not human readable. This could e.g. be the case when the metric name is a uuid.
Metadata must not change over the lifetime of a process (i.e. it is not allowed to return the units as seconds in one retrieval and as hours in a subsequent one). The reason behind it is that e.g. a monitoring agent on Kubernetes may read the metadata once it sees the new container and store it. It may not periodically re-query the process for the metadata.
In fact, metadata should not change during the life-time of the whole container image or an application, as all containers spawned from it will be "the same" and form part of an app, where it would be confusing in an overall view if the same metric has different metadata. |
Metric Registry
The MetricRegistry
stores the metrics and metadata information.
There is one MetricRegistry
instance for each of the scopes listed in Scopes.
Metrics can be added to or retrieved from the registry either using the @Metric
annotation
(see Metrics Annotations) or using the MetricRegistry
object directly.
A metric is uniquely identified by the MetricRegistry
if the MetricID
associated with the metric is unique. That is to say, there are no other metrics with the same combination of metric name and tags. However, all metrics of the same name must be of the same type otherwise an IllegalArgumentException
will be thrown. This exception will be thrown during registration.
The metadata information is registered under a unique metric name and is immutable. All metrics of the same name must be registered with the same metadata information otherwise an "IllegalArgumentException" will be thrown. This exception will be thrown during registration.
MetricID
The MetricID consists of the metric’s name and tags (if supplied). This is used by the MetricRegistry to uniquely identify a metric and its corresponding metadata.
The MetricID:
-
name: The name of the metric.
-
tags (optional): A list of Tag objects. See also Tags.
Reusing Metrics
For metrics declared using annotations, it is allowed to reference one metric by multiple annotations.
The prerequisite for this is that the annotations carry the same metadata. If multiple annotations declare the same
metric, but contain different metadata, an IllegalArgumentException
must be thrown during startup.
Reusability does not apply to gauges though. The implementation must throw an IllegalArgumentException
during startup if it detects multiple
@Gauge
annotations referring to the same gauge (with the same MetricID
).
@Counted(name = "countMe", absolute = true, tags={"tag1=value1"})
public void countMeA() { }
@Counted(name = "countMe", absolute = true, tags={"tag1=value1"})
public void countMeB() { }
In the above examples both countMeA()
and countMeB()
will share a single Counter with registered name countMe
and the same tags in application scope.
Metrics and CDI scopes
Depending on CDI bean scope, there may be multiple instances of the CDI bean created over the lifecycle of an application.
In these cases, where multiple bean instances exist, only one instance of the corresponding metric will be created (per annotated method), and updates
to that metric will be combined from all related invocations regardless of the bean instance where the invocation happens.
For example, calls to a method annotated with @Counted
will increase the value of the same counter no matter which bean
instance is the one where the counted method is being invoked.
Concurrent gauges will watch the number of parallel invocations of a method even if the invocations are on different instances.
The only exception from this are gauges (not concurrent gauges), which don’t support multiple instances of the underlying bean to be created,
because in that case it would not be clear which instance should be used for obtaining the gauge value. For this reason,
gauges should only be used with beans that create only one instance, in CDI terms this means @ApplicationScoped
and @Singleton
beans.
The implementation may employ validation checks that throw an error eagerly when it is detected that there is a @Gauge
on a bean
that will probably have multiple instances.
Exposing metrics via REST API
Data is exposed via REST over HTTP under the /metrics
base path in two different data formats for GET
requests:
-
JSON format - used when the HTTP Accept header best matches
application/json
. -
OpenMetrics text format - used when the HTTP Accept header best matches
text/plain
or when Accept header would equally accept bothtext/plain
andapplication/json
and there is no other higher precedence format. This format is also returned when no media type is requested (i.e. no Accept header is provided in the request)
Implementations and/or future versions of this specification may allow for more export formats that are triggered by their specific media type. The OpenMetrics text format will stay as fall-back. |
Formats are detailed below.
Data access must honour the HTTP response codes, especially
-
200 for successful retrieval of an object
-
204 when retrieving a subtree that would exist, but has no content. E.g. when the application-specific subtree has no application specific metrics defined.
-
404 if a directly-addressed item does not exist. This may be a non-existing sub-tree or non-existing object
-
406 if the HTTP Accept Header in the request cannot be handled by the server.
-
500 to indicate that a request failed due to "bad health". The body SHOULD contain details if possible { "details": <text> }
The API MUST NOT return a 500 Internal Server Error code to represent a non-existing resource.
Endpoint | Request Type | Supported Formats | Description |
---|---|---|---|
|
GET |
JSON, OpenMetrics |
Returns all registered metrics |
|
GET |
JSON, OpenMetrics |
Returns metrics registered for the respective scope. Scopes are listed in Metrics Setup |
|
GET |
JSON, OpenMetrics |
Returns the metric that matches the metric name for the respective scope |
|
OPTIONS |
JSON |
Returns all registered metrics' metadata |
|
OPTIONS |
JSON |
Returns metrics' metadata registered for the respective scope. Scopes are listed in Metrics Setup |
|
OPTIONS |
JSON |
Returns the metric’s metadata that matches the metric name for the respective scope |
The implementation must return a 406 response code if the request’s HTTP Accept header for an OPTIONS request
does not match application/json .
|
Usage of MicroProfile Metrics in application servers with multiple applications
Even though multi-app servers are generally outside the scope of MicroProfile, this section describes recommendations how such application servers should behave if they want to support MicroProfile Metrics.
Metrics from all applications and scopes should be available under a single REST endpoint ending with /metrics
similarly as
in case of single-application deployments (microservices).
To help distinguish between metrics pertaining to each deployed application, a tag named _app
should be appended to each metric.
The value of the _app
tag should be passed by the application server to the application via a MicroProfile Config property named mp.metrics.appName
.
It should be possible to override this value by bundling the file META-INF/microprofile-config.properties
within the application archive
and setting a custom value for the property mp.metrics.appName
inside it.
It is allowed for application servers to choose to not add the _app tag at all. Implementations may differ in how they handle cases where metrics are registered with the same name from two or more applications running in the same server. This behavior is not expected to be portable across vendors.
REST endpoints
This section describes the REST-API, that monitoring agents would use to retrieve the collected metrics. (Java-) methods mentioned refer to the respective Objects in the Java API. See also Application Metrics Programming Model
JSON format
-
When using JSON format, the REST API will respond to GET requests with data formatted in a tree like fashion with sub-trees for the sub-resources. A sub-tree that does not contain data must be omitted.
-
A 'shadow tree' that responds to OPTIONS will provide the metadata and tags associated to a metric name.
Translation rules for metric names and handling of tags
The following rules apply only to GET requests:
-
Tags are appended to the leaf element of the metric’s JSON tree.
-
For metrics with tags, the metric name must be appended with a semicolon
;
followed by a semicolon-separated list of tag key/value pairs. -
For compound metrics (those with child JSON attributes) with tags, only the "leaf" metric names are decorated with tags.
-
Semicolons
;
present in tag values must be converted to underscores_
in JSON output.
For example:
{
"carsCounter;colour=red": 0,
"carsCounter;car=sedan;colour=blue": 0,
"carsMeter": {
"count;colour=red": 0,
"meanRate;colour=red": 0,
"oneMinRate;colour=red": 0,
"fiveMinRate;colour=red": 0,
"fifteenMinRate;colour=red": 0,
"count;colour=blue": 0,
"meanRate;colour=blue": 0,
"oneMinRate;colour=blue": 0,
"fiveMinRate;colour=blue": 0,
"fifteenMinRate;colour=blue": 0
}
}
The following apply to both GET and OPTION requests:
-
Each tag is a key-value-pair in the format of
<key>=<value>
. The list of tags must be sorted alphabetically by key name. -
If the metric name or tag value contains a special reserved JSON character, these characters must be escaped in the JSON response.
If the metric has no tags, the semicolon ;
must be omitted.
For example,
{
"metricWithoutTags": 192
}
REST-API Objects
API-objects MAY include one or more metrics as in
{
"thread.count": 33,
"thread.max.count": 47,
"memory.maxHeap": 3817863211,
"memory.usedHeap": 16859081,
"memory.committedHeap": 64703546
}
or
{
"hitCount;type=yes": 45
}
In case /metrics
is requested, then the data for the scopes are wrapped in the scope name:
{
"application": {
"hitCount": 45
},
"base": {
"thread.count": 33,
"thread.max.count": 47
},
"vendor": {...}
}
If there is a scope that contains no metrics, then it can be either present with an empty object as its value, or it can be omitted completely.
Gauge JSON Format
The value of the gauge must be equivalent to a call to the instance Gauge’s getValue()
.
The JSON leaf is named <metric-name>[';'<tag-name>'='<tag-value>]+
with tags in alphabetical order.
{
"responsePercentage": 48.45632,
"responsePercentage;servlet=two": 26.23654,
"responsePercentage;servlet=three;store=webshop": 29.24554
}
Counter JSON Format
The value of the counter must be equivalent to a call to the instance Counter’s getCount()
.
The JSON leaf is named <metric-name>[';'<tag-name>'='<tag-value>]+
with tags in alphabetical order.
{
"hitCount": 45,
"hitCount;servlet=two": 3,
"hitCount;servlet=three;store=webshop": 4
}
Concurrent Gauge JSON Format
ConcurrentGauge
is a complex metric type comprised of multiple key/values. The format is specified by the table below.
The JSON node is named <metric-name>
. The JSON leaves are named <key>[';'<tag-name>'='<tag-value>]+
with tags in alphabetical order and keys according to below table.
JSON Key | Value (Equivalent ConcurrentGauge method) |
---|---|
|
|
|
|
|
|
{
"callCount": {
"current" : 48,
"min": 4,
"max": 50,
"current;component=backend" : 23,
"min;component=backend": 1,
"max;component=backend": 29
}
}
Meter JSON Format
Meter
is a complex metric type comprised of multiple key/values. The format is specified by the table below.
The JSON node is named <metric-name>
. The JSON leafs are named <key>[';'<tag-name>'='<tag-value>]+
with tags in alphabetical order and keys according to below table.
JSON Key | Value (Equivalent Meter method) |
---|---|
|
|
|
|
|
|
|
|
|
|
{
"requests": {
"count": 29382,
"meanRate": 12.223,
"oneMinRate": 12.563,
"fiveMinRate": 12.364,
"fifteenMinRate": 12.126,
"count;servlet=one": 29382,
"meanRate;servlet=one": 12.223,
"oneMinRate;servlet=one": 12.563,
"fiveMinRate;servlet=one": 12.364,
"fifteenMinRate;servlet=one": 12.126,
"count;servlet=two": 29382,
"meanRate;servlet=two": 12.223,
"oneMinRate;servlet=two": 12.563,
"fiveMinRate;servlet=two": 12.364,
"fifteenMinRate;servlet=two": 12.126
}
}
Histogram JSON Format
Histogram
is a complex metric type comprised of multiple key/values. The format is specified by the table below.
The JSON node is named <metric-name>
. The JSON leaves are named <key>[';'<tag-name>'='<tag-value>]+
with tags in alphabetical order and keys according to below table.
JSON Key | Value (Equivalent Histogram method) |
---|---|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
{
"daily_value_changes": {
"count": 2,
"sum": -1598,
"min": -1624,
"max": 26,
"mean": -799.0,
"stddev": 825.0,
"p50": 26.0,
"p75": 26.0,
"p95": 26.0,
"p98": 26.0,
"p99": 26.0,
"p999": 26.0,
"count;servlet=two": 2,
"sum;servlet=two": -1598,
"min;servlet=two": -1624,
"max;servlet=two": 26,
"mean;servlet=two": -799.0,
"stddev;servlet=two": 825.0,
"p50;servlet=two": 26.0,
"p75;servlet=two": 26.0,
"p95;servlet=two": 26.0,
"p98;servlet=two": 26.0,
"p99;servlet=two": 26.0,
"p999;servlet=two": 26.0
}
}
Timer JSON Format
Timer
is a complex metric type comprised of multiple key/values. The format is specified by the table below.
The JSON node is named <metric-name>
. The JSON leaves are named <key>[';'<tag-name>'='<tag-value>]+
with tags in alphabetical order and keys according to below table.
JSON Key | Value (Equivalent Timer method) |
---|---|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
{
"responseTime": {
"count": 29382,
"elapsedTime": 25608694,
"meanRate": 12.185627192860734,
"oneMinRate": 12.563,
"fiveMinRate": 12.364,
"fifteenMinRate": 12.126,
"min": 169916,
"max": 5608694,
"mean": 415041.00024926325,
"stddev": 652907.9633011606,
"p50": 293324.0,
"p75": 344914.0,
"p95": 543647.0,
"p98": 2706543.0,
"p99": 5608694.0,
"p999": 5608694.0,
"count;servlet=two": 29382,
"elapsedTime;servlet=two": 25608694,
"meanRate;servlet=two":12.185627192860734,
"oneMinRate;servlet=two": 12.563,
"fiveMinRate;servlet=two": 12.364,
"fifteenMinRate;servlet=two": 12.126,
"min;servlet=two": 169916,
"max;servlet=two": 5608694,
"mean;servlet=two": 415041.00024926325,
"stddev;servlet=two": 652907.9633011606,
"p50;servlet=two": 293324.0,
"p75;servlet=two": 344914.0,
"p95;servlet=two": 543647.0,
"p98;servlet=two": 2706543.0,
"p99;servlet=two": 5608694.0,
"p999;servlet=two": 5608694.0
}
}
Simple Timer JSON Format
Simple Timer
is a complex metric type comprised of multiple key/values. The format is specified by the table below.
The JSON node is named <metric-name>
. The JSON leaves are named <key>[';'<tag-name>'='<tag-value>]+
with tags in alphabetical order and keys according to below table.
JSON Key | Value (Equivalent SimpleTimer method) |
---|---|
|
|
|
|
|
|
|
|
{
"simple_responseTime": {
"count": 1,
"elapsedTime": 12300000000,
"maxTimeDuration": 3231000000, //(1)
"minTimeDuration": 25600000 //(1)
}
}
1 | The minTimeDuration and maxTimeDuration will display a null value if no values were recorded. |
Metadata
Metadata is exposed in a tree-like fashion with sub-trees for the sub-resources mentioned previously. Tags from metrics associated with the metric name are also included. The 'tags' attribute is an array of nested arrays which hold tags from different metrics that are associated with the metadata. Tags in each inner array are in alphabetical order.
Example:
If GET /metrics/base/fooVal
exposes:
{
"fooVal;store=webshop": 12345
}
then OPTIONS /metrics/base/fooVal
will expose:
{
"fooVal": {
"unit": "milliseconds",
"type": "gauge",
"description": "The size of foo after each request",
"displayName": "Size of foo",
"tags": [
[
"store=webshop"
]
]
}
}
If GET /metrics/base
exposes multiple values like this:
{
"fooVal;store=webshop": 12345,
"barVal;component=backend;store=webshop": 42,
"barVal;component=frontend;store=webshop": 63
}
then OPTIONS /metrics/base
exposes:
{
"fooVal": {
"unit": "milliseconds",
"type": "gauge",
"description": "The average duration of foo requests during last 5 minutes",
"displayName": "Duration of foo",
"tags": [
[
"store=webshop"
]
]
},
"barVal": {
"unit": "megabytes",
"type": "gauge",
"tags": [
[
"component=backend",
"store=webshop"
],
[
"component=frontend",
"store=webshop"
]
]
}
}
OpenMetrics format
Data is exposed in the OpenMetrics text format, version 0.0.4 as described in OpenMetrics text format.
The metadata will be included as part of the normal OpenMetrics text format. Unlike the JSON format, the text format does not support OPTIONS requests.
Users that want to write tools to transform the metadata can still request the metadata via OPTIONS
request and application/json media type.
|
The above json example would look like this in OpenMetrics format
# TYPE base_fooVal_seconds gauge
# HELP base_fooVal_seconds The average duration of foo requests during last 5 minutes (1)
base_fooVal_seconds{store="webshop"} 12.345 (2)
# TYPE base_barVal_bytes gauge
base_barVal_bytes{component="backend", store="webshop"} 42000 (2)
base_barVal_bytes{component="frontend", store="webshop"} 63000 (2)
1 | The description goes into the HELP line |
2 | Metric names gets the base unit of the family appended with _ and defined labels. Values are scaled accordingly. See Handling of units. The TYPE only occurs once. |
Translation rules for metric names
OpenMetrics text format does not allow for all characters and adds the base unit of a family to the name.
Characters allowed are [a-zA-Z0-9_]
(Ascii alphabet, numbers and underscore). Exposed metric names must
follow the pattern [a-zA-Z_][a-zA-Z0-9_]*
.
-
Characters that do not fall in above category are translated to underscore (
_
). -
Scope is always specified at the start of the metric name.
-
Scope and name are separated by underscore (
_
). -
Double underscore is translated to single underscore
-
The unit is appended to the name, separated by underscore. See Handling of units
Handling of tags
Metric tags are appended to the metric name in curly braces {
and }
and are separated by comma.
Each tag is a key-value-pair in the format of <key>="<value>"
(the quotes around the value are required).
MicroProfile Metrics timers and histograms expose an OpenMetrics summary
type which requires an additional quantile
tag for certain metrics.
The quantile
tag must be included alongside the metrics tags within the curly braces {
and }
.
The tag value can be any Unicode character but the following characters must be escaped:
-
Backslash (
\
) must be escaped as\\
(as two characters:\
and\
) -
Double-quotes (
"
) must be escaped as\"
(as two characters:\
and"
) -
Line feed (
\n
) must be escaped as\n
(as two characters:\
andn
)
Handling of units
The OpenMetrics text format adheres to using "base units" when creating the HTTP response. Due to the different context of each metric type, certain metrics' values must be converted to the respective "base unit" when responding to OpenMetrics requests. For example, times in milliseconds must be divided by 1000 and displayed in the base unit (seconds).
The following sections outline how each metric type is handled:
Gauges and Histograms
The metric name and values for Gauge
and Histogram
are converted to the "base unit" in respect to the unit
value in the Metadata.
-
If the Metadata is empty,
NONE
, or null, the metric name is used as is without appending the unit name and no scaling is applied. -
If the metric’s metadata contains a known unit, as defined in the
MetricUnits
class, the OpenMetrics value should be scaled to the base unit of the respective family. The name of the base unit is appended to the metric name delimited by underscores (_
). -
If the
unit
is specified and is not defined in theMetricUnits
class, the value is not scaled but theunit
is still appended to the metric name delimited by underscores (_
).
Unit families and their base units are described under OpenMetrics metric names, Base units.
Families and OpenMetrics base units are:
Family | Base unit |
---|---|
Bits |
bytes |
Bytes |
bytes |
Time |
seconds |
Percent |
ratio (normally ratio is A_per_B, but there are exceptions like |
Counters
Counter
metrics are considered dimensionless. The implementation must not append the unit name to the metric name and must not scale the value.
Meters and Timers
Meter
and Timer
have fixed units as described below regardless of the unit
value in the Metadata.
Gauge OpenMetrics Text Format
The value of the gauge must be the value of getValue()
with appropriate naming/scaling based on Handling of units
The OpenMetrics name is composed <scope>''<metric-name>''[<unit>'_']
.
# TYPE application_cost_dollars gauge
# HELP application_cost_dollars The running cost of the server in dollars.
application_cost_dollars 80
Counter OpenMetrics Text Format
The value of the counter must be the value of getCount()
.
The exposed metric name must have a _total
suffix.
The suffix is not appended if the (translated) original metric name already ends in _total
.
Counters do not have a suffix for the unit.
The OpenMetrics name is composed <scope>_<metric-name>_<suffix>
.
# TYPE application_visitors_total counter
# HELP application_visitors_total The number of unique visitors
application_visitors_total 80
Concurrent Gauge OpenMetrics Text Format
ConcurrentGauge
is a complex metric type comprised of multiple key/values. Each key will require a suffix to be appended to the metric name. The format is specified by the table below.
Suffix{label} | TYPE | Value (Meter method) | Units |
---|---|---|---|
|
Gauge |
|
N/A |
|
Gauge |
|
N/A |
|
Gauge |
|
N/A |
Concurrent gauges do not have a suffix for the unit.
The OpenMetrics name is composed <scope>_<metric-name>_<suffix>
.
# TYPE application_method_a_invocations_current gauge
# HELP application_method_a_invocations_current The number of parallel invocations of methodA() (1)
application_method_a_invocations_current 80
# TYPE application_method_a_invocations_min gauge
application_method_a_invocations_min 20
# TYPE application_method_a_invocations_max gauge
application_method_a_invocations_max 100
1 | Note help is only emitted for the metric related to getCount() , but not for _min and _max. |
Meter OpenMetrics Text Format
Meter
is a complex metric type comprised of multiple key/values. Each key will require a suffix to be appended to the metric name. The format is specified by the table below.
The # HELP
description line is only required for the total
value as shown below.
Suffix{label} | TYPE | Value (Meter method) | Units |
---|---|---|---|
|
Counter |
|
N/A |
|
Gauge |
|
PER_SECOND |
|
Gauge |
|
PER_SECOND |
|
Gauge |
|
PER_SECOND |
|
Gauge |
|
PER_SECOND |
The OpenMetrics name is composed <scope>_<metric-name>_<suffix>
.
# TYPE application_requests_total counter
# HELP application_requests_total Tracks the number of requests to the server
application_requests_total 29382
# TYPE application_requests_rate_per_second gauge
application_requests_rate_per_second 12.223
# TYPE application_requests_one_min_rate_per_second gauge
application_requests_one_min_rate_per_second 12.563
# TYPE application_requests_five_min_rate_per_second gauge
application_requests_five_min_rate_per_second 12.364
# TYPE application_requests_fifteen_min_rate_per_second gauge
application_requests_fifteen_min_rate_per_second 12.126
Histogram OpenMetrics Text Format
Histogram
is a complex metric type comprised of multiple key/values. Each key will require a suffix to be appended to the metric name with appropriate naming/scaling based on Handling of units. The format is specified by the table below.
The # HELP
description line is only required for the summary
value as shown below.
The OpenMetrics name is composed <scope>_<metric-name>_<suffix>
where <suffix>
can consist of a fixed part and a unit or just a unit.
The quantile
OpenMetrics label is merged with the metric’s tags.
Suffix{label} | TYPE | Value (Histogram method) | Units |
---|---|---|---|
|
Gauge |
|
<units>1 |
|
Gauge |
|
<units>1 |
|
Gauge |
|
<units>1 |
|
Gauge |
|
<units>1 |
|
Summary |
|
N/A |
|
Summary |
|
<units>1 |
|
Summary |
|
<units>1 |
|
Summary |
|
<units>1 |
|
Summary |
|
<units>1 |
|
Summary |
|
<units>1 |
|
Summary |
|
<units>1 |
|
Summary |
|
<units>1 |
1 The implementation is expected to convert the result returned by the Histogram
into the base unit (if known). The <unit>
represents the base metric unit and is named according to Handling of units.
2 The summary
type is a complex metric type for OpenMetrics which consists of the count, sum and multiple quantile values.
# TYPE application_file_sizes_mean_bytes gauge
application_file_sizes_mean_bytes 4738.231
# TYPE application_file_sizes_max_bytes gauge
application_file_sizes_max_bytes 31716
# TYPE application_file_sizes_min_bytes gauge
application_file_sizes_min_bytes 180
# TYPE application_file_sizes_stddev_bytes gauge
application_file_sizes_stddev_bytes 1054.7343037063602
# TYPE application_file_sizes_bytes summary
# HELP application_file_sizes_bytes Users file size
application_file_sizes_bytes_count 2037
application_file_sizes_bytes_sum 48123
application_file_sizes_bytes{quantile="0.5"} 4201
application_file_sizes_bytes{quantile="0.75"} 6175
application_file_sizes_bytes{quantile="0.95"} 13560
application_file_sizes_bytes{quantile="0.98"} 29643
application_file_sizes_bytes{quantile="0.99"} 31716
application_file_sizes_bytes{quantile="0.999"} 31716
Timer OpenMetrics Text Format
Timer
is a complex metric type comprised of multiple key/values. Each key will require a suffix to be appended to the metric name. The format is specified by the table below.
The # HELP
description line is only required for the summary
value as shown below.
The OpenMetrics name is composed <scope>_<metric-name>_<suffix>
.
The quantile
OpenMetrics label is merged with the metric’s tags.
Suffix{label} | TYPE | Value (Timer method) | Units |
---|---|---|---|
|
Gauge |
|
PER_SECOND |
|
Gauge |
|
PER_SECOND |
|
Gauge |
|
PER_SECOND |
|
Gauge |
|
PER_SECOND |
|
Gauge |
|
SECONDS1 |
|
Gauge |
|
SECONDS1 |
|
Gauge |
|
SECONDS1 |
|
Gauge |
|
SECONDS1 |
|
Summary |
|
N/A |
|
Summary |
|
SECONDS1 |
|
Summary |
|
SECONDS1 |
|
Summary |
|
SECONDS1 |
|
Summary |
|
SECONDS1 |
|
Summary |
|
SECONDS1 |
|
Summary |
|
SECONDS1 |
|
Summary |
|
SECONDS1 |
1 The implementation is expected to convert the result returned by the Timer
into seconds
2 The summary
type is a complex metric type for OpenMetrics which consists of the count and multiple quantile values.
# TYPE application_response_time_rate_per_second gauge
application_response_time_rate_per_second 0.004292520715985437
# TYPE application_response_time_one_min_rate_per_second gauge
application_response_time_one_min_rate_per_second 2.794076465421066E-14
# TYPE application_response_time_five_min_rate_per_second gauge
application_response_time_five_min_rate_per_second 4.800392614619373E-4
# TYPE application_response_time_fifteen_min_rate_per_second gauge
application_response_time_fifteen_min_rate_per_second 0.01063191047532505
# TYPE application_response_time_mean_seconds gauge
application_response_time_mean_seconds 0.000415041
# TYPE application_response_time_max_seconds gauge
application_response_time_max_seconds 0.0005608694
# TYPE application_response_time_min_seconds gauge
application_response_time_min_seconds 0.000169916
# TYPE application_response_time_stddev_seconds gauge
application_response_time_stddev_seconds 0.000652907
# TYPE application_response_time_seconds summary
# HELP application_response_time_seconds Server response time for /index.html
application_response_time_seconds_count 80
application_response_time_seconds_sum 0.023
application_response_time_seconds{quantile="0.5"} 0.0002933240
application_response_time_seconds{quantile="0.75"} 0.000344914
application_response_time_seconds{quantile="0.95"} 0.000543647
application_response_time_seconds{quantile="0.98"} 0.002706543
application_response_time_seconds{quantile="0.99"} 0.005608694
application_response_time_seconds{quantile="0.999"} 0.005608694
Simple Timer OpenMetrics Text Format
Simple Timer
is a complex metric type comprised of multiple key/values. Each key will require a suffix to be appended to the metric name. The format is specified by the table below.
The OpenMetrics name is composed <scope>_<metric-name>_<suffix>
.
Suffix{label} |
TYPE |
Value (SimpleTimer method) |
Units |
|
Counter |
|
N/A |
|
Gauge |
|
SECONDS1 |
|
Gauge |
|
SECONDS1 |
|
Gauge |
|
SECONDS1 |
1 The implementation is expected to convert the result returned by the SimpleTimer
into seconds
# TYPE application_response_time_total counter
# HELP application_response_time_total The number of calls to this REST endpoint #(1)
application_response_time_total 12
# TYPE application_response_time_elapsedTime_seconds gauge
application_response_time_elapsedTime_seconds 12.30000000
# TYPE application_response_time_maxTimeDuration_seconds gauge
application_response_time_maxTimeDuration_seconds 3.231000000 #(2)
# TYPE application_response_time_minTimeDuration_seconds gauge
application_response_time_minTimeDuration_seconds 0.0256 #(2)
1 | Note help is only emitted for the metric related to getCount() , but not for elapsedTime. |
2 | The minTimeDuration and maxTimeDuration will display a NaN value if no values were recorded. |
Security
It must be possible to secure the endpoints via the usual means. The definition of 'usual means' is in this version of the specification implementation specific.
In case of a secured endpoint, accessing /metrics
without valid credentials must return a 401 Unauthorized
header.
A server SHOULD implement TLS encryption by default.
It is allowed to ignore security for trusted origins (e.g. localhost)
Required Metrics
Base metrics is a list of metrics that all vendors need to implement. Optional base metrics are recommended to be implemented but are not required.
These metrics are exposed under /metrics/base
.
The following is a list of required and optional base metrics. All metrics are singletons and have Multi:
set to false
unless otherwise stated.
Visit Metadata for the meaning of each key
Although vendors are required to implement these base metrics, some virtual machines can not provide them. Vendors should either use other metrics that are close enough as substitute or not fill these base metrics at all. |
General JVM Stats
UsedHeapMemory
Name |
memory.usedHeap |
DisplayName |
Used Heap Memory |
Type |
Gauge |
Unit |
Bytes |
Description |
Displays the amount of used heap memory in bytes. |
MBean |
java.lang:type=Memory/HeapMemoryUsage#used |
CommittedHeapMemory
Name |
memory.committedHeap |
DisplayName |
Committed Heap Memory |
Type |
Gauge |
Unit |
Bytes |
Description |
Displays the amount of memory in bytes that is committed for the Java virtual machine to use. This amount of memory is guaranteed for the Java virtual machine to use. |
MBean |
java.lang:type=Memory/HeapMemoryUsage#committed |
Notes |
Also from JSR 77 |
MaxHeapMemory
Name |
memory.maxHeap |
DisplayName |
Max Heap Memory |
Type |
Gauge |
Unit |
Bytes |
Description |
Displays the maximum amount of heap memory in bytes that can be used for memory management. This attribute displays -1 if the maximum heap memory size is undefined. This amount of memory is not guaranteed to be available for memory management if it is greater than the amount of committed memory. The Java virtual machine may fail to allocate memory even if the amount of used memory does not exceed this maximum size. |
MBean |
java.lang:type=Memory/HeapMemoryUsage#max |
GCCount
Name |
gc.total |
DisplayName |
Garbage Collection Count |
Type |
Counter |
Unit |
None |
Multi |
true |
Tags |
{name=%s} |
Description |
Displays the total number of collections that have occurred. This attribute lists -1 if the collection count is undefined for this collector. |
MBean |
java.lang:type=GarbageCollector,name=%s/CollectionCount |
Notes |
There can be multiple garbage collectors active that are assigned to different memory pools. The %s should be substituted with the name of the garbage collector. |
GCTime - Approximate accumulated collection elapsed time in ms
Name |
gc.time |
DisplayName |
Garbage Collection Time |
Type |
Gauge |
Unit |
Milliseconds |
Multi |
true |
Tags |
{name=%s} |
Description |
Displays the approximate accumulated collection elapsed time in milliseconds. This attribute displays -1 if the collection elapsed time is undefined for this collector. The Java virtual machine implementation may use a high resolution timer to measure the elapsed time. This attribute may display the same value even if the collection count has been incremented if the collection elapsed time is very short. |
MBean |
java.lang:type=GarbageCollector,name=%s/CollectionTime |
Notes |
There can be multiple garbage collectors active that are assigned to different memory pools. The %s should be substituted with the name of the garbage collector. |
JVM Uptime - Up time of the Java Virtual machine
Name |
jvm.uptime |
DisplayName |
JVM Uptime |
Type |
Gauge |
Unit |
Milliseconds |
Description |
Displays the time elapsed since the start of the Java virtual machine in milliseconds. |
MBean |
java.lang:type=Runtime/Uptime |
Notes |
Also from JSR 77 |
Thread JVM Stats
ThreadCount
Name |
thread.count |
DisplayName |
Thread Count |
Type |
Gauge |
Unit |
None |
Description |
Displays the current number of live threads including both daemon and non-daemon threads |
MBean |
java.lang:type=Threading/ThreadCount |
DaemonThreadCount
Name |
thread.daemon.count |
DisplayName |
Daemon Thread Count |
Type |
Gauge |
Unit |
None |
Description |
Displays the current number of live daemon threads. |
MBean |
java.lang:type=Threading/DaemonThreadCount |
PeakThreadCount
Name |
thread.max.count |
DisplayName |
Peak Thread Count |
Type |
Gauge |
Unit |
None |
Description |
Displays the peak live thread count since the Java virtual machine started or peak was reset. This includes daemon and non-daemon threads. |
MBean |
java.lang:type=Threading/PeakThreadCount |
Thread Pool Stats
(Optional) ActiveThreads
Name |
threadpool.activeThreads |
DisplayName |
Active Threads |
Type |
Gauge |
Unit |
None |
Multi |
true |
Tags |
{pool=%s} |
Description |
Number of active threads that belong to a specific thread pool. |
Notes |
The %s should be substituted with the name of the thread pool. This is a vendor specific attribute/operation that is not defined in java.lang. |
(Optional) PoolSize
Name |
threadpool.size |
DisplayName |
Thread Pool Size |
Type |
Gauge |
Unit |
None |
Multi |
true |
Tags |
{pool=%s} |
Description |
The size of a specific thread pool. |
Notes |
The %s should be substituted with the name of the thread pool. This is a vendor specific attribute/operation that is not defined in java.lang. |
ClassLoading JVM Stats
LoadedClassCount
Name |
classloader.loadedClasses.count |
DisplayName |
Current Loaded Class Count |
Type |
Gauge |
Unit |
None |
Description |
Displays the number of classes that are currently loaded in the Java virtual machine. |
MBean |
java.lang:type=ClassLoading/LoadedClassCount |
TotalLoadedClassCount
Name |
classloader.loadedClasses.total |
DisplayName |
Total Loaded Class Count |
Type |
Counter |
Unit |
None |
Description |
Displays the total number of classes that have been loaded since the Java virtual machine has started execution. |
MBean |
java.lang:type=ClassLoading/TotalLoadedClassCount |
UnloadedClassCount
Name |
classloader.unloadedClasses.total |
DisplayName |
Total Unloaded Class Count |
Type |
Counter |
Unit |
None |
Description |
Displays the total number of classes unloaded since the Java virtual machine has started execution. |
MBean |
java.lang:type=ClassLoading/UnloadedClassCount |
Operating System
AvailableProcessors
Name |
cpu.availableProcessors |
DisplayName |
Available Processors |
Type |
Gauge |
Unit |
None |
Description |
Displays the number of processors available to the Java virtual machine. This value may change during a particular invocation of the virtual machine. |
MBean |
java.lang:type=OperatingSystem/AvailableProcessors |
(Optional) SystemLoadAverage
Name |
cpu.systemLoadAverage |
DisplayName |
System Load Average |
Type |
Gauge |
Unit |
None |
Description |
Displays the system load average for the last minute. The system load average is the sum of the number of runnable entities queued to the available processors and the number of runnable entities running on the available processors averaged over a period of time. The way in which the load average is calculated is operating system specific but is typically a damped time-dependent average. If the load average is not available, a negative value is displayed. This attribute is designed to provide a hint about the system load and may be queried frequently. The load average may be unavailable on some platforms where it is expensive to implement this method. |
MBean |
java.lang:type=OperatingSystem/SystemLoadAverage |
(Optional) ProcessCpuLoad
Name |
cpu.processCpuLoad |
DisplayName |
Process CPU Load |
Type |
Gauge |
Unit |
Percent |
Description |
Displays the "recent cpu usage" for the Java Virtual Machine process |
MBean |
java.lang:type=OperatingSystem (com.sun.management.UnixOperatingSystemMXBean for Oracle Java, similar one exists for IBM Java: com.ibm.lang.management.ExtendedOperatingSystem) Note: This is a vendor specific attribute/operation that is not defined in java.lang |
(Optional) ProcessCpuTime
Name |
cpu.processCpuTime |
DisplayName |
Process CPU Time |
Type |
Gauge |
Unit |
Nanoseconds |
Description |
Displays the CPU time used by the process on which the Java virtual machine is running in nanoseconds. |
MBean |
java.lang:type=OperatingSystem (com.sun.management.UnixOperatingSystemMXBean for Oracle Java, similar one exists for IBM Java: com.ibm.lang.management.ExtendedOperatingSystem) Note: This is a vendor specific attribute/operation that is not defined in java.lang |
(Optional) REST
Metrics gathered from REST stats are optional and therefore may not be available in every implementation.
The MicroProfile Metrics runtime will track metrics from RESTful resource method calls during runtime (i.e GET, POST, PUT, DELETE, OPTIONS, PATCH, HEAD). It is up to the implementation to decide how to enable the REST metrics.
Mapped and Unmapped Exceptions
The metrics defined below will treat a REST request that ends in a mapped exception or an unmapped exception differently. For the MicroProfile Metrics runtime, mapped exceptions and succesful REST requests should be considered and handled the same way. This is because mapped exceptions are expected by the developer and may then be handled appropriately as part of the application’s expected behviour. Unmapped exceptions on the other hand are unexpected and can skew metric data if its' respective REST request is recorded. To avoid contaminating the metric values with these unsuccesful REST requests, the below metrics may omit tracking a REST request that ends with an unmapped exception. There are also metrics that purposely track REST requests that end with an unmapped exception.
(Optional) RESTRequest
Name |
REST.request |
DisplayName |
Total Requests and Response Time |
Type |
SimpleTimer |
Unit |
None |
Multi |
true |
Tags |
{class=%s1,method=%s2} |
Description |
The number of invocations and total response time of this RESTful resource method since the start of the server. The metric will not record the elapsed time nor count of a REST request if it resulted in an unmapped exception. Also tracks the highest recorded time duration within the previous completed full minute and lowest recorded time duration within the previous completed full minute. |
Notes |
With an asynchronous request the timing that is tracked by the REST metric must incorporate the time spent by the asynchronous call. The The Parameter type formatting rules: |
(Optional) RESTRequestUnmappedExceptions
Name |
REST.request.unmappedException.total |
DisplayName |
Total Unmapped Exceptions count |
Type |
Counter |
Unit |
None |
Multi |
true |
Tags |
{class=%s1,method=%s2} |
Description |
The total number of unmapped exceptions that occur from this RESTful resouce method since the start of the server. |
Notes |
The The Parameter type formatting rules: |
For example given the following RESTful resource:
package org.eclipse.microprofile.metrics.demo;
@ApplicationScoped
public class RestDemo {
@POST
public void postMethod(Object o, String... s){
...
}
}
The OpenMetrics formatted rest metrics would be:
# TYPE base_REST_request_total counter
base_REST_request_total{class="org.eclipse.microprofile.metrics.demo.RestDemo",method="postMethod_java.lang.Object_java.lang.String[]"} 1
# TYPE base_REST_request_elapsedTime_seconds gauge
base_REST_request_elapsedTime_seconds{class="org.eclipse.microprofile.metrics.demo.RestDemo",method="postMethod_java.lang.Object_java.lang.String[]"} 1.000
# TYPE base_REST_request_maxTimeDuration_seconds gauge
base_REST_request_maxTimeDuration_seconds{class="org.eclipse.microprofile.metrics.demo.RestDemo",method="postMethod_java.lang.Object_java.lang.String[]"} 1.000
# TYPE base_REST_request_minTimeDuration_seconds gauge
base_REST_request_minTimeDuration_seconds{class="org.eclipse.microprofile.metrics.demo.RestDemo",method="postMethod_java.lang.Object_java.lang.String[]"} 1.000
# TYPE base_REST_request_unmappedException_total counter
base_REST_request_unmappedException_total{class="org.eclipse.microprofile.metrics.demo.RestDemo",method="postMethod_java.lang.Object_java.lang.String[]"} 0
Application Metrics Programming Model
MicroProfile Metrics provides a way to register Application-specific metrics to allow applications to expose metrics in the application scope (see Scopes for the description of scopes).
Metrics and their metadata are added to a Metric Registry upon definition and can afterwards have their values set and retrieved via the Java-API and also be exposed via the REST-API (see Exposing metrics via REST API).
Implementors of this specification can use the Java API to also expose metrics for base and vendor scope by using the respective Metric Registry. |
There are two options for registering metrics. The easier one is using annotations - the metrics declared by annotations will be automatically added to the registry when the application starts. In some cases, however, for example when the full list of required metrics is not known in advance, or when it is too large, it might be necessary to interact with the registry programmatically and create new metrics dynamically at runtime. Both approaches can also be combined.
MetricUnits.NONE
is used, an explicit name is provided@Gauge(unit = MetricUnits.NONE, name = "queueSize")
public int getQueueSize() {
return queue.size;
}
-
NOTE: The programming API was inspired by Dropwizard Metrics 3.2.3 API, with some changes. It is expected that many existing DropWizard Metrics based applications can easily be ported over by switching the package names.
-
NOTE: There are no hard limits on the number of metrics, but it is often not a good practice to create a huge number of metrics, because the downstream time series databases that need to store the metrics may not deal well with this amount of data.
Responsibility of the MicroProfile Metrics implementation
-
The implementation must scan the application at deploy time for Annotations and register the Metrics along with their metadata in the application MetricsRegistry. This does not apply to gauges, they can be registered lazily when the declaring bean is being instantiated.
-
The implementation must watch the annotated objects and update internal data structures when the values of the annotated objects change. The value of a
Gauge
is recomputed each time a client requests the value. -
The implementation must expose the values of the objects registered in the MetricsRegistry via REST-API as described in Exposing metrics via REST API.
-
Metrics registered via non-annotations API need their values be set via updates from the application code.
-
The implementation must detect if multiple annotations declare the same gauge (with the same
MetricID
) and throw anIllegalArgumentException
if such duplicate exists-
See reusing of metrics for more details.
-
-
The implementation must flag and reject metrics upon registration if the metadata information being registered is not equivalent to the metadata information that has already been registered under the given metric name (if it already exists).
-
All metrics of a given metric name must be associated with the same metadata information
-
The implementation must throw an
IllegalArgumentException
when the metric is rejected.
-
-
The implementation must throw an
IllegalStateException
if an annotated metric is invoked, but the metric no longer exists in the MetricRegistry. This applies to the following annotations : @Timed, @SimplyTimed, @Counted, @ConcurrentGauge, @Metered -
The implementation must make sure that metric registries are thread-safe, in other words, concurrent calls to methods of
MetricRegistry
must not leave the registry in an inconsistent state.
Base Package
All Java-Classes are in the top-level package org.eclipse.microprofile.metrics
or one of its sub-packages.
The Implementors can consult this project for implementation ideas. See References for more information. |
Annotations
All Annotations are in the org.eclipse.microprofile.metrics.annotation
package
These annotations include interceptor bindings as defined by the Java Interceptors specification. CDI leverages the Java Interceptors specification to provide the ability to associate interceptors to beans via typesafe interceptor bindings, as a means to separate cross-cutting concerns, like Metrics annotations instrumentation, from the application business logic. Both the Java Interceptors and CDI specifications set restrictions about the type of bean to which an interceptor can be bound. That implies only managed beans whose bean types are proxyable can be instrumented using the Metrics annotations. |
The Implementors can consult this project for implementation ideas. See References for more information. |
The following Annotations exist, see below for common fields:
Annotation | Applies to | Description | Default Unit |
---|---|---|---|
@Counted |
M, C, T |
Denotes a counter, which counts the invocations of the annotated object. |
MetricUnits.NONE |
@ConcurrentGauge |
M, C, T |
Denotes a gauge which counts the parallel invocations of the annotated object. |
MetricUnits.NONE |
@Gauge |
M |
Denotes a gauge, which samples the value of the annotated object. |
no default, must be supplied by the user |
@Metered |
M, C, T |
Denotes a meter, which tracks the frequency of invocations of the annotated object. |
MetricUnits.PER_SECOND |
@Metric |
F, P |
An annotation that contains the metadata information when requesting a metric to be injected. |
MetricUnits.NONE |
@Timed |
M, C, T |
Denotes a timer, which tracks duration of the annotated object. |
MetricUnits.NANOSECONDS |
@SimplyTimed |
M, C, T |
Denotes a simple timer, which tracks duration and invocations of the annotated object. |
MetricUnits.NANOSECONDS |
(C=Constructor, F=Field, M=Method, P=Parameter, T=Type)
Annotation | Description | Default |
---|---|---|
@RegistryType |
Qualifies the scope of Metric Registry to inject when injecting a MetricRegistry. |
application (scope) |
Fields
All annotations (Except RegistryType
) have the following fields that correspond to the metadata fields described
in Metadata.
String name
-
Optional. Sets the name of the metric. If not explicitly given the name of the annotated object is used.
boolean absolute
-
If
true
, uses the given name as the absolute name of the metric. Iffalse
, prepends the package name and class name before the given name. Default value isfalse
. String displayName
-
Optional. A human readable display name for metadata.
String description
-
Optional. A description of the metric.
String unit
-
Unit of the metric. For
@Gauge
no default is provided. Check theMetricUnits
class for a set of pre-defined units.
Implementors are encouraged to issue warnings in the server log if metadata is missing. Implementors MAY stop the deployment of an application if Metadata is missing. |
Annotated Naming Convention
Annotated metrics are registered into the application MetricRegistry
with the name computed using the rules in the following tables.
If the metric annotation is placed on a method or field:
|
|
|
|
Value of the |
Name of the annotated element |
|
|
|
If the metric annotation is placed on a class, then for each method (including constructors), the metric name will be:
|
|
|
|
|
|
|
|
|
In case of constructors, "name of the method" is the short name of the declaring class.
package com.example;
import jakarta.inject.Inject;
import org.eclipse.microprofile.metrics.Counter;
import org.eclipse.microprofile.metrics.annotation.Metric;
public class Colours {
@Counted
public void red() {
// ...
}
@Counted(name="blueCount")
public void blue() {
// ...
}
@Counted(name="greenCount", absolute=true)
public void green() {
// ...
}
@Counted(absolute=true)
public void yellow() {
// ...
}
}
The above bean would produce the following entries in the MetricRegistry
com.example.Colours.red com.example.Colours.blueCount greenCount yellow
@Inject
is used together with @Metric
package com.example;
import jakarta.inject.Inject;
import org.eclipse.microprofile.metrics.Counter;
import org.eclipse.microprofile.metrics.annotation.Metric;
public class Colours {
@Inject
@Metric
Counter redCount;
@Inject
@Metric(name="blue")
Counter blueCount;
@Inject
@Metric(absolute=true)
Counter greenCount;
@Inject
@Metric(name="purple", absolute=true)
Counter purpleCount;
}
The above bean would produce the following entries in the MetricRegistry
com.example.Colours.redCount com.example.Colours.blue greenCount purple
@Counted
An annotation for marking a method, constructor, or type as a counter.
The implementation must support the following annotation targets:
-
CONSTRUCTOR
-
METHOD
-
TYPE
This annotation has changed in MicroProfile Metrics 2.0: Counters now always increase monotonically upon invocation.
The old behaviour pre 2.0 can now be achieved with @ConcurrentGauge .
|
If the metric no longer exists in the MetricRegistry
when the annotated element is invoked then an IllegalStateException
will be thrown.
The following lists the behavior for each annotation target.
CONSTRUCTOR
When a constructor is annotated, the implementation must register a counter for the constructor using the Annotated Naming Convention. The counter is increased by one when the constructor is invoked.
@Counted
public CounterBean() {
}
METHOD
When a non-private method is annotated, the implementation must register a counter for the method using the Annotated Naming Convention. The counter is increased by one when the method is invoked.
@Counted
public void run() {
}
TYPE
When a type/class is annotated, the implementation must register a counter for each of the constructors and non-private methods using the Annotated Naming Convention. The counters are increased by one when the corresponding constructor/method is invoked.
@Counted
public class CounterBean {
public void countMethod1() {}
public void countMethod2() {}
}
@ConcurrentGauge
An annotation for marking a method, constructor, or type as a parallel invocation counted. The semantics is such that upon entering a marked item, the parallel count is increased by one and upon exit again decreased by one. The purpose of this annotation is to gauge the number of parallel invocations of the marked methods or constructors.
The implementation must support the following annotation targets:
-
CONSTRUCTOR
-
METHOD
-
TYPE
If the metric no longer exists in the MetricRegistry
when the annotated element is invoked then an IllegalStateException
will be thrown.
The following lists the behavior for each annotation target.
CONSTRUCTOR
When a constructor is annotated, the implementation must register gauges, representing the current, previous minute maximum, and previous minute minimum values for the constructor using the Annotated Naming Convention.
@ConcurrentGauge
public CounterBean() {
}
METHOD
When a non-private method is annotated, the implementation must register gauges, representing the current, previous minute maximum, and previous minute minimum values for the method using the Annotated Naming Convention.
@ConcurrentGauge
public void run() {
}
TYPE
When a type/class is annotated, the implementation must register gauges, representing the current, previous minute maximum, and previous minute minimum values for each of the constructors and non-private methods using the Annotated Naming Convention.
@ConcurrentGauge
public class CounterBean {
public void countMethod1() {}
public void countMethod2() {}
}
@Gauge
An annotation for marking a method as a gauge. No default MetricUnit
is supplied, so the unit
must always be specified explicitly.
The implementation must support the following annotation target:
-
METHOD
The following lists the behavior for each annotation target.
METHOD
When a non-private method is annotated, the implementation must register a gauge for the method using the Annotated Naming Convention. The gauge value and type is equal to the annotated method return value and type.
@Gauge(unit = MetricUnits.NONE)
public long getValue() {
return value;
}
@Metered
An annotation for marking a constructor or method as metered. The meter counts the invocations of the constructor or method and tracks how frequently they are called.
The implementation must support the following annotation targets:
-
CONSTRUCTOR
-
METHOD
-
TYPE
If the metric no longer exists in the MetricRegistry
when the annotated element is invoked then an IllegalStateException
will be thrown.
The following lists the behavior for each annotation target.
CONSTRUCTOR
When a constructor is annotated, the implementation must register a meter for the constructor using the Annotated Naming Convention. The meter is marked each time the constructor is invoked.
@Metered
public MeteredBean() {
}
METHOD
When a non-private method is annotated, the implementation must register a meter for the method using the Annotated Naming Convention. The meter is marked each time the method is invoked.
@Metered
public void run() {
}
TYPE
When a type/class is annotated, the implementation must register a meter for each of the constructors and non-private methods using the Annotated Naming Convention. The meters are marked each time the corresponding constructor/method is invoked.
@Metered
public class MeteredBean {
public void meteredMethod1() {}
public void meteredMethod2() {}
}
@SimplyTimed
An annotation for marking a constructor or method of an annotated object as simply timed. The metric of type SimpleTimer tracks the count of invocations of the annotated object and tracks how long it took the invocations to complete.
The implementation must support the following annotation targets:
-
CONSTRUCTOR
-
METHOD
-
TYPE
If the metric no longer exists in the MetricRegistry
when the annotated element is invoked then an IllegalStateException
will be thrown.
The following lists the behavior for each annotation target.
CONSTRUCTOR
When a constructor is annotated, the implementation must register a simple timer for the constructor using the Annotated Naming Convention. Each time the constructor is invoked, the execution will be timed.
@SimplyTimed
public SimplyTimedBean() {
}
METHOD
When a non-private method is annotated, the implementation must register a simple timer for the method using the Annotated Naming Convention. Each time the method is invoked, the execution will be timed.
@SimplyTimed
public void run() {
}
TYPE
When a type/class is annotated, the implementation must register a simple timer for each of the constructors and non-private methods using the Annotated Naming Convention. Each time a constructor/method is invoked, the execution will be timed with the corresponding simple timer.
@SimplyTimed
public class SimplyTimedBean {
public void simplyTimedMethod1() {}
public void simplyTimedMethod2() {}
}
@Timed
An annotation for marking a constructor or method of an annotated object as timed. The metric of type Timer tracks how frequently the annotated object is invoked, and tracks how long it took the invocations to complete. The data is aggregated to calculate duration statistics and throughput statistics.
The implementation must support the following annotation targets:
-
CONSTRUCTOR
-
METHOD
-
TYPE
If the metric no longer exists in the MetricRegistry
when the annotated element is invoked then an IllegalStateException
will be thrown.
The following lists the behavior for each annotation target.
CONSTRUCTOR
When a constructor is annotated, the implementation must register a timer for the constructor using the Annotated Naming Convention. Each time the constructor is invoked, the execution will be timed.
@Timed
public TimedBean() {
}
METHOD
When a non-private method is annotated, the implementation must register a timer for the method using the Annotated Naming Convention. Each time the method is invoked, the execution will be timed.
@Timed
public void run() {
}
TYPE
When a type/class is annotated, the implementation must register a timer for each of the constructors and non-private methods using the Annotated Naming Convention. Each time a constructor/method is invoked, the execution will be timed with the corresponding timer.
@Timed
public class TimedBean {
public void timedMethod1() {}
public void timedMethod2() {}
}
@Metric
An annotation requesting that a metric should be injected or registered.
The implementation must support the following annotation targets:
-
FIELD
-
PARAMETER
The following lists the behavior for each annotation target.
FIELD
When a metric injected field is annotated, the implementation must provide the registered metric with the given name (using the Annotated Naming Convention) if the metric already exists. If no metric exists with the given name then the implementation must produce and register the requested metric.
Gauges are an exception to this rule, because it could happen that an annotated gauge is not registered yet when the reference to it is being injected. In that case, the implementation
must inject a proxy gauge implementation which forwards getValue()
calls to the actual gauge, if the actual gauge already exists. If getValue()
is called on the
proxy gauge and the actual gauge still does not exist in the registry, getValue()
will return null.
@Inject
@Metric(name = "applicationCount")
Counter count;
PARAMETER
When a metric parameter is annotated, the implementation must provide the registered metric with the given name (using the Annotated Naming Convention) if the metric already exist. If no metric exists with the given name then the implementation must produce and register the requested metric.
@Inject
public void init(@Metric(name="instances") Counter instances) {
instances.inc();
}
Usage of CDI stereotypes
If a metric annotation is applied to a bean through a CDI stereotype, the implementation must handle it the same way as if the metric annotation was applied on the target bean directly. Metric names are computed relative to the name and package of the bean itself, not of the stereotype.
Registering metrics dynamically
In addition to declaring metrics via annotations, it is possible to dynamically (un)register metrics by
calling methods of a MetricRegistry
object.
Registering metrics dynamically can be useful in some cases, for example, when the final list of metrics is not known in
advance (when the application is being coded), or when there are too many similar metrics and
it would be more practical to register them in a for
loop than to introduce
lots of annotations in the code. The two approaches can also be combined if necessary.
List of methods of the MetricRegistry related to registering new metrics
Method | Description |
---|---|
|
Counter with given name and no tags |
|
Counter with given name and tags |
|
Counter from given |
|
Counter from given |
|
Concurrent gauge with given name and no tags |
|
Concurrent gauge with given name and tags |
|
Concurrent gauge from given |
|
Concurrent gauge from given |
|
Histogram with given name and no tags |
|
Histogram with given name and tags |
|
Histogram from given |
|
Histogram from given |
|
Meter with given name and no tags |
|
Meter with given name and tags |
|
Meter from given |
|
Meter from given |
|
Timer with given name and no tags |
|
Timer with given name and tags |
|
Timer from given |
|
Timer from given |
|
SimpleTimer with given name and no tags |
|
SimpleTimer with given name and tags |
|
SimpleTimer from given |
|
SimpleTimer from given |
|
Registers the given metric instance under the given name |
|
Registers the given metric instance using the given metadata object |
|
Registers the given metric instance using the given metadata object and given tags |
All metrics in the table above, except the variants of register
, exhibit the get-or-create semantics,
so if a compatible metric with the same MetricID
already exists, the existing one is returned. "Compatible"
in this context means that the type and all specified metadata must be equal - else an exception is thrown.
If a metric exists under the same name but with different tags, the newly created metric must have
all of its metadata equal to the existing metric’s metadata.
The register
method variants exhibit the create semantics, that means, if a metric with the same MetricID
already exists, an exception is thrown. If a metric exists under the same name but with different tags,
the newly created metric must have all of its metadata equal to the existing metric’s metadata.
For methods that accept a Metadata
parameter and whose name implies a metric type (all except register
),
it is possible to call them with a Metadata
object which does not specify the metric type (it can be null).
In this case, the implementation must sanitize the Metadata
object in a way that if it is retrieved later
from the registry and metadata.getType()
or metadata.getTypeRaw()
is called on it,
the actual metric type will be returned. Conversely, if there is a mismatch because the type
specified in the Metadata
is different than the one implied by the method name, an exception must be thrown.
Unregistering metrics
While the general recommendation is that metrics live for the whole lifecycle of the application, it is still possible to dynamically remove metrics from metric registries at runtime.
List of methods of the MetricRegistry related to removing metrics
Method | Description |
---|---|
|
Removes all metrics with the given name |
|
Removes the metric with the given |
|
Removes all metrics that are accepted by the given |
Metric Registries
The MetricRegistry
is used to maintain a collection of metrics along with their metadata.
There is one shared singleton of the MetricRegistry
per scope (application, base, and vendor).
When metrics are registered using annotations, the metrics are registered in the application MetricRegistry
(and thus the application scope).
When injected, the @RegistryType
is used as a qualifier to selectively inject either the APPLICATION
, BASE
, or VENDOR
registry.
If no qualifier is used, the default MetricRegistry
returned is the APPLICATION
registry.
Implementations may choose to use a Factory class to produce the injectable MetricRegistry
bean via CDI. See Example Metric Registry Factory. Note: The factory would be an internal class and not exposed to the application.
@RegistryType
The @RegistryType
can be used to retrieve the MetricRegistry
for a specific scope.
The implementation must produce the corresponding MetricRegistry
specified by the RegistryType
.
The implementor can optionally provide a read_only copy of the MetricRegistry for base and vendor scopes.
|
Application Metric Registry
The implementation must produce the application MetricRegistry
when no RegistryType
is provided (@Default
) or when the RegistryType
is APPLICATION
.
@Inject
MetricRegistry metricRegistry;
@Inject
@RegistryType(type=MetricRegistry.Type.APPLICATION)
MetricRegistry metricRegistry;
Base Metric Registry
The implementation must produce the base MetricRegistry
when the RegistryType
is BASE
. The base MetricRegistry
must contain the required metrics specified in Required Metrics.
@Inject
@RegistryType(type=MetricRegistry.Type.BASE)
MetricRegistry baseRegistry;
Vendor Metric Registry
The implementation must produce the vendor MetricRegistry
when the RegistryType
is VENDOR
. The vendor MetricRegistry
must contain any vendor specific metrics.
@Inject
@RegistryType(type=MetricRegistry.Type.VENDOR)
MetricRegistry vendorRegistry;
Metadata
Metadata is used in MicroProfile-Metrics to provide immutable information about a Metric at registration time. Metadata in the architecture section describes this further.
Therefore Metadata
is an interface to construct an immutable metadata object.
The object can be built via a MetadataBuilder
with a fluent API.
Metadata
object for a Meter and registering it in Application scopeMetadata m = Metadata.builder()
.withName("myMeter")
.withDescription("Example meter")
.withType(MetricType.METER)
.build();
Meter me = new MyMeterImpl();
metricRegistry.register(m, me, new Tag("colour","blue"));
A default implementation DefaultMetadata
is provided in the API for convenience.
Appendix
Alternatives considered
Jolokia JMX-HTTP bridge. Using this for application specific metrics would require those metrics to be exposed to JMX first, which many users are not familiar with.
Example configuration format for base and vendor-specific data
The following is an example configuration in YAML format.
base:
- name: "thread-count"
mbean: "java.lang:type=Threading/ThreadCount"
description: "Number of currently deployed threads"
unit: "none"
type: "gauge"
displayName: "Current Thread count"
- name: "peak-thread-count"
mbean: "java.lang:type=Threading/PeakThreadCount"
description: "Max number of threads"
unit: "none"
type: "gauge"
- name: "total-started-thread-count"
mbean: "java.lang:type=Threading/TotalStartedThreadCount"
description: "Number of threads started for this server"
unit: "none"
type: "counter"
- name: "max-heap"
mbean: "java.lang:type=Memory/HeapMemoryUsage#max"
description: "Number of threads started for this server"
unit: "bytes"
type: "counter"
tags: "kind=memory"
vendor:
- name: "msc-loaded-modules"
mbean: "jboss.modules:type=ModuleLoader,name=BootModuleLoader-2/LoadedModuleCount"
description: "Number of loaded modules"
unit: "none"
type: "gauge"
This configuration can be backed into the runtime or be provided via an external configuration file.
Example Metric Registry Factory
@ApplicationScoped
public class MetricRegistryFactory {
@Produces
public static MetricRegistry getDefaultRegistry() {
return getApplicationRegistry();
}
@Produces
@RegistryType(type = Type.APPLICATION)
public static MetricRegistry getApplicationRegistry() {
// Returns the static instance of the Application MetricRegistry
[...]
}
@Produces
@RegistryType(type = Type.BASE)
public static MetricRegistry getBaseRegistry() {
// Returns the static instance of the Base MetricRegistry
[...]
}
@Produces
@RegistryType(type = Type.VENDOR)
public static MetricRegistry getVendorRegistry() {
// Returns the static instance of the Vendor MetricRegistry
[...]
}
}
Migration hints
To version 2.0
@Counted
The @Counted
annotation has changed. Users of the previous @Counted
annotation were surprised to learn that by default counters were not monotonic. Also, the OpenMetrics format expects all counters to be monotonic.
To migrate:
-
Replace
@Counted()
or@Counted(monotonic=false)
with@ConcurrentGauge
. A set of gauges will be created in the OpenMetrics output for each@ConcurrentGauge
. -
Replace
@Counted(monotonic=true)
with@Counted
(monotonic flag is gone)
This change has also had an impact on the Counter
interface to basically follow the above change:
-
Modify code which uses
Counter.dec()
to use aGauge
orConcurrentGauge
.
Some base metrics' types have changed from Counter
to Gauge
since counters must now count monotonically. Update code or dashboards that use the following metrics:
-
thread.count
-
thread.daemon.count
-
classloader.currentLoadedClass.count
-
thread.max.count
Some base metrics' names have changed to follow the convention of ending the name of accumulating counters with total
. Update code or dashboards that use the following metrics:
-
gc.count → gc.total
-
classloader.currentLoadedClass.count → classloader.loadedClasses.count (changed to stay consistent with other classloader metric names)
-
classloader.totalLoadedClass.count → classloader.loadedClasses.total
-
classloader.totalUnloadedClass.count → classloader.unloadedClasses.total
Release Notes
Changes in 4.0
Incompatible Changes
-
This release aligns with Jakarta EE 9.1, so it won’t work with earlier versions of Jakarta or Java EE (#639)
Changes in 3.0
Breaking changes
-
Removed everything related to reusability from the API code. All metrics are now considered reusable.
-
CDI producers annotated with
@Metric
no longer trigger metric registration. If these metrics should be registered, it must be done differently (for example using theMetricRegistry
methods) -
MetricRegistry
changed fromabstract class
tointerface
-
Changed
Timer.update(long duration, java.util.concurrent.TimeUnit)
toTimer.update(java.time.Duration duration)
-
Removed
MetadataBuilder.withOptional*
methods, the remainingwith*
methods do acceptnull
value (considered not present) exceptwithName
which does not acceptnull
or""
-
Changed
Metadata.getDescription()
andMetadata.getUnit()
to returnString
instead ofOptional<String>
and addedMetadata.description()
andMetadata.unit()
that returnOptional<String>
API/SPI Changes
-
Updated dependencies scopes and versions to align with Jakarta EE 8
-
MetricRegistry
changed fromabstract class
tointerface
-
Added the
MetricRegistry.getType()
method -
Added the
MetricRegistry.counter(MetricID)
method -
Added the
MetricRegistry.concurrentGauge(MetricID)
method -
Added the
MetricRegistry.gauge(String, Object, Function, Tag[])
method -
Added the
MetricRegistry.gauge(MetricID, Object, Function)
method -
Added the
MetricRegistry.gauge(Metadata, Object, Function, Tag[])
method -
Added the
MetricRegistry.gauge(String, Supplier, Tag[])
method -
Added the
MetricRegistry.gauge(MetricID, Supplier)
method -
Added the
MetricRegistry.gauge(Metadata), Supplier, Tag[])
method -
Added the
MetricRegistry.histogram(MetricID)
method -
Added the
MetricRegistry.meter(MetricID)
method -
Added the
MetricRegistry.timer(MetricID)
method -
Added the
MetricRegistry.simpleTimer(MetricID)
method -
Added the
MetricRegistry.getMetric(MetricID)
method -
Added the
MetricRegistry.getMetric(MetricID metricID, Class)
method -
Added the
MetricRegistry.getCounter(MetricID)
method -
Added the
MetricRegistry.getConcurrentGauge(MetricID)
method -
Added the
MetricRegistry.getGauge(MetricID)
method -
Added the
MetricRegistry.getHistogram(MetricID)
method -
Added the
MetricRegistry.getMeter(MetricID)
method -
Added the
MetricRegistry.getTimer(MetricID)
method -
Added the
MetricRegistry.getSimpleTimer(MetricID)
method -
Added the
MetricRegistry.getMetadata(String)
method -
Added the
MetricRegistry.getMetrics(MetricFilter)
method -
Added the
MetricRegistry.getMetrics(Class, MetricFilter)
method -
Added
SimpleTimer.getMinTimeDuration()
andSimpleTimer.getMaxTimeDuration()
methods which return ajava.time.Duration
object (#523) -
Timer class updated (#524)
-
Changed
Timer.update(long duration, java.util.concurrent.TimeUnit)
toTimer.update(java.time.Duration duration)
-
Added
Timer.getElapsedTime()
which returnsjava.time.Duration
-
-
Removed
MetadataBuilder.withOptional*
methods -
Global tags and
_app
tag are no longer handled automatically by theMetricID
class, the implementation is expected to add them by itself, for example during metric export -
Added the
Histogram.getSum()
which returnslong
(#597)
Functional Changes
-
Simple Timer metrics now track the highest and lowest recorded timing duration of the previous completed minute (#523)
-
Timer now exposes total elapsed time duration as a metric value. (#524)
-
Clarified that the existing REST metric
REST.request
will not monitor and track a REST request to a REST endpoint if an unmapped exception occurs. -
Introduced a new base REST metric
REST.request.unmappedException.total
that counts the occurrences of unmapped exceptions for each REST endpoint (#533) -
Histogram now exposes the total sum of recorded values as a
sum
value (#597)-
In JSON format it is exposed as a
sum
value -
In OpenMetrics format it is exposed as a
sum
value under thesummary
type
-
-
Timer now exposes the
elapsedTime
metric value assum
under thesummary
type in OpenMetrics format (#597)
Specification Changes
-
Removed the concept of reusability
-
CDI producers annotated with
@Metric
no longer trigger metric registration -
Clarified how the implementation must handle metrics applied via CDI stereotypes
-
The implementation is required to sanitize
Metadata
passed by the application in cases when it does not contain an explicit type, but the type is implied by the name of the registration method that is being called. -
Clarified that the existing REST metric
REST.request
will not monitor and track a REST request to a REST endpoint if an unmapped exception occurs -
Introduced a new base REST metric
REST.request.unmappedException.total
that counts the occurrences of unmapped exceptions for each REST endpoint (#533) -
Histogram now exposes the total sum of recorded values as a
sum
value (#597)-
In JSON format it is exposed as a
sum
value -
In OpenMetrics format it is exposed as a
sum
value under thesummary
type
-
-
Timer now exposes the
elapsedTime
metric value assum
under thesummary
type in OpenMetrics format (#597)
Changes in 2.3
A full list of changes may be found on the MicroProfile Metrics 2.3 Milestone
Functional Changes
-
Introduced a new base metric derived from RESTful stats into the base scope.
-
REST.request
: Tracks the total count of requests and total elapsed time spent at the REST endpoint
-
-
Introduced the simple timer (
@SimplyTimed
) metric. (#496) -
The API code no longer requires a correctly configured MP Config implementation to be available at runtime, so it is possible to slim down deployments if MP Config is not necessary (#466)
Changes in 2.2
A full list of changes may be found on the MicroProfile Metrics 2.2.1 Milestone
API/SPI Changes
-
Reverted a problematic change from 2.1 where Gauges were required to return subclasses of
java.lang.Number
Functional Changes
-
Reverted a problematic change from 2.1 where Gauges were required to return subclasses of
java.lang.Number
-
(2.2.1) Added ProcessCpuTime as a new optional base metric. (#480)
Specification Changes
-
(2.2.1) Added ProcessCpuTime as a new optional base metric. (#480)
Changes in 2.1
A full list of changes may be found on the MicroProfile Metrics 2.1 Milestone and MicroProfile Metrics 2.1.1 Milestone
Functional Changes
-
(2.1.1) Added ProcessCpuTime as a new optional base metric. (#454)
-
Clarified in the API code that Gauges must return values that extend
java.lang.Number
. [NOTE: this caused issues with backward compatibility and was reverted in 2.2] (#304) -
Clarified that implementations can, for JSON export of scopes containing no metrics, omit them, or that they can be present with an empty value. (#416)
-
Clarified that metrics should not be created for private methods when a class is annotated (the TCK asserted this in 2.0 anyway) (#416)
-
Added the
reusable(boolean)
method for MetadataBuilder (#407)
Specification Changes
-
(2.1.1) Added ProcessCpuTime as a new optional base metric. (#454)
-
Clarified that metric registry implementations are required to be thread-safe. (#300)
-
Clarified that implementations can, for JSON export of scopes containing no metrics, omit them, or that they can be present with an empty value. (#416)
-
Clarified that metrics should not be created for private methods when a class is annotated (the TCK asserted this in 2.0 anyway) (#416)
-
Added some text to the specification about programmatic creation of metrics (without annotations) (#399)
Miscellaneous
-
Explicitly excluded the transitive dependency on
jakarta.el-api
from the build of the specification. It wasn’t actually used anywhere in the build so there should be no impact. Implementations can still support the Expression Language if they choose to. (#417)
Changes in 2.0
A full list of changes may be found on the MicroProfile Metrics 2.0 Milestone and MicroProfile Metrics 2.0.1 Milestone and MicroProfile Metrics 2.0.2 Milestone
Changes marked with are breaking changes relative to previous versions of the spec.
API/SPI Changes
-
Refactoring of Counters, as the old
@Counted
was misleading in practice. (#290)-
Counters via
@Counted
are now always monotonic, themonotonic
attribute is gone. TheCounted
interface lost thedec()
methods. -
Former non-monotonic counters are now
@ConcurrentGauge
and also in the output reported as gauges. (#290) -
See Migration hints about migration of applications using MicroProfile Metrics. (#290)
-
-
Removed unnecessary
@InterceptorBinding
annotation fromorg.eclipse.microprofile.metrics.annotation.Metric
. (#188) -
Removed deprecated
org.eclipse.microprofile.metrics.MetricRegistry.register(String name, Metric, Metadata)
(#268) -
Metadata
is now immutable and built via aMetadataBuilder
. (#228) -
Introduced a Tag object which represents a singular tag key/value pair. (#238)
-
MetricFilter modified to filter with MetricID instead of name. (#238)
Functional Changes
-
(2.0.3) Added ProcessCpuTime as a new optional base metric. (#454)
-
Metadata
is now immutable and built via aMetadataBuilder
. (#228) -
Metrics are now uniquely identified by a MetricID (combination of the metric’s name and tags). (#238)
-
MetricFilter modified to filter with MetricID instead of name. (#238)
-
The 'Metadata' is mapped to a unique metric name in the
MetricRegistry
and this relationship is immutable. (#238) -
Tag key names for labels are restricted to match the regex
[a-zA-Z_][a-zA-Z0-9_]*
. (#238) -
Tag values defined through MP_METRICS_TAGS must escape equal signs
=
and commas,
with a backslash\
. (#238) -
JSON output format for GET requests now appends tags along with the metric in
metricName;tag=value;tag=value
format. JSON format for OPTIONS requests have been modified such that the 'tags' attribute is a list of nested lists which holds tags from different metrics that are associated with the metadata. (#381) -
OpenMetrics format - formerly called Prometheus format
-
The default value of the
reusable
attribute for metric objects created programmatically (not via annotations) is nowtrue
(#328) -
Some base metrics' names have changed to follow the convention of ending the name of accumulating counters with
total
. (#375) -
Some base metrics' types have changed from Counter to Gauge since Counters must now count monotonically. (#375)
-
Some base metrics' names have changed because they now use tags to distinguish metrics for multiple JVM objects. For example, each existing garbage collector now has its own
gc.total
metric with the name of the garbage collector being in a tag. Names of some base metrics in the OpenMetrics output are also affected by the removal of conversion from camelCase to snake_case. (#375)
Specification Changes
-
(2.0.3) Added ProcessCpuTime as a new optional base metric. (#454)
-
Refactoring of Counters, as the old
@Counted
was misleading in practice. (#290)-
Counters via
@Counted
are now always monotonic, themonotonic
attribute is gone. TheCounted
interface lost thedec()
methods. -
Former non-monotonic counters are now
@ConcurrentGauge
and also in the output reported as gauges. (#290) -
See Migration hints about migration of applications using MicroProfile Metrics. (#290)
-
-
Metrics are now uniquely identified by a MetricID (combination of the metric’s name and tags). (#238)
-
The 'Metadata' is mapped to a unique metric name in the
MetricRegistry
and this relationship is immutable. (#238) -
Tag key names for labels are restricted to match the regex
[a-zA-Z_][a-zA-Z0-9_]*
. (#238) -
Tag values defined through MP_METRICS_TAGS must escape equal signs
=
and commas,
with a backslash\
. (#238) -
OpenMetrics format - formerly called Prometheus format
-
The default value of the
reusable
attribute for metric objects created programmatically (not via annotations) is nowtrue
(#328) -
Some base metrics' names have changed to follow the convention of ending the name of accumulating counters with
total
. (#375) -
Some base metrics' types have changed from Counter to Gauge since Counters must now count monotonically. (#375)
-
Some base metrics' names have changed because they now use tags to distinguish metrics for multiple JVM objects. For example, each existing garbage collector now has its own
gc.total
metric with the name of the garbage collector being in a tag. Names of some base metrics in the OpenMetrics output are also affected by the removal of conversion from camelCase to snake_case. (#375) -
Added a set of recommendations how application servers with multiple deployed applications should behave if they support MP Metrics. (#240)
Changes in 1.1
A full list of changes may be found on the MicroProfile Metrics 1.1 Milestone
API/SPI Changes
-
org.eclipse.microprofile.metrics.MetricRegistry.register(String name, Metric, Metadata)
is deprecated. Useorg.eclipse.microprofile.metrics.MetricRegistry.register(Metadata, Metric)
instead, whereMetadata
already has a field for the name.
Functional Changes
-
org.eclipse.microprofile.metrics.MetricRegistry.register(String name, Metric, Metadata)
is deprecated. Useorg.eclipse.microprofile.metrics.MetricRegistry.register(Metadata, Metric)
instead, whereMetadata
already has a field for the name. -
Global tags are now supplied via the means of MicroProfile Config (the env variable is still valid). (#165)
Specification Changes
-
Annotations and
Metadata
can now have a flagreusable
that indicates that the metric name can be registered more than once. Default isfalse
as in Metrics 1.0. See Reusing Metrics.