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.adoc for more information regarding metrics that are required for each vendor.
See section app-programming-model.adoc for more information regarding the application metrics programming model.
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)
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 app-programming-model.adoc. 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.adoc 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 implementation must tag base metrics with scope=base
.
Required base metrics are exposed under /metrics?scope=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 at the same version.
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 app-programming-model.adoc.
The implementation must automatically tag application metrics with scope=application
, except where the metric already has a scope
tag.
Metrics with application scope are exposed under /metrics?scope=application
.
Applications may also define their own scope names by tagging metrics with another
scope
(other than base
, vendor
, application
). Metrics with custom scopes are exposed
under /metrics?scope=scope_name
where scope_name is defined by the application. Scope names must match the regex [a-zA-Z_][a-zA-Z0-9_]*
. If an illegal character is used, the implementation must
throw an IllegalArgumentException
.
Metrics with application-defined scopes are exposed under /metrics?scope=scope_name
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?scope=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.
Vendors are encouraged to use metric names consistent with the Open Telemetry Metrics Semantic Conventions where applicable.
Tags (also known as 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 tags named _scope
and _app
are reserved. If an application attempts to create a metric with either of these tags, the implementation must throw an IllegalArgumentException
.
The tag value may contain any UTF-8 encoded character.
Note
|
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.adoc for more information on the reserved characters. |
Tags can be supplied in two ways:
-
At the level of a metric as described in app-programming-model.adoc.
-
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.
Note
|
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 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.
Tip
|
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).
-
gauge: a metric that is sampled to obtain its value (e.g. cpu temperature or disk usage).
-
histogram: a metric which calculates the distribution of a value.
-
timer: a metric which aggregates timing durations and provides duration statistics.
-
-
description (optional): A human readable description of the metric.
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.
Important
|
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. |
The MetricRegistry
stores the metrics and metadata information.
There is one MetricRegistry
instance for each of the predefined 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 and be identified by the same set of tag names 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.
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.
For metrics declared using annotations, it is allowed to reference one metric by multiple annotations. The prerequisite for this is that all such annotations must carry the same metadata and tag names. If multiple annotations declare the same metric but contain different metadata or tag names, 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.
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.
The only exception from this are 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.
Data is exposed via REST over HTTP under the /metrics
base path in different data formats for GET
requests:
-
JSON format - used when the HTTP Accept header best matches
application/json
. -
OpenMetrics exposition format - used when the HTTP Accept header best matches
application/openmetrics-text; version=1.0.0
. Support for this format by implementations is optional. -
Prometheus text-based exposition format - used when the HTTP Accept header best matches
text/plain; version=0.0.4
. This format is also returned when no media type is requested (i.e. no Accept header is provided in the request)
Note
|
Implementations and/or future versions of this specification may allow for more export formats that are triggered by their specific media type. The Prometheus text-based exposition 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, Prometheus, OpenMetrics |
Returns all registered metrics |
|
GET |
JSON, Prometheus, OpenMetrics |
Returns metrics registered for the respective scope. Scopes are listed in Metrics Setup |
|
GET |
JSON, Prometheus, OpenMetrics |
Returns metrics that match 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 |
Note
|
The implementation must return a 406 response code if the request’s HTTP Accept header for an OPTIONS request
does not match application/json .
|
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 added 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.