MicroProfile Metrics provides a way to register Application-specific metrics to allow applications to expose metrics in the application scope (see architecture.adoc 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 architecture.adoc).
Tip
|
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: 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.
-
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 architecture.adoc.
-
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 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 reject metrics upon registration if the set of tag names specified is not the same as the set of tag names used in prior registrations of metrics with the same metric name.
-
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, @Counted -
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.
All Java-Classes are in the top-level package org.eclipse.microprofile.metrics
or one of its sub-packages.
All Annotations are in the org.eclipse.microprofile.metrics.annotation
package
Note
|
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 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 |
@Gauge |
M |
Denotes a gauge, which samples the value of the annotated object. |
no default, must be supplied by the user |
@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.NANOSECONDS1 |
(C=Constructor, F=Field, M=Method, P=Parameter, T=Type)
1 Prometheus output will always display timer values in seconds. When using the Timer
API to retrieve the Snapshot
, the values returned from the Snapshot
will be in nanoseconds.
Annotation | Description | Default |
---|---|---|
@RegistryScope |
Indicates the scope of Metric Registry to inject when injecting a MetricRegistry. |
application (scope) |
@RegistryType |
Qualifies the scope of Metric Registry to inject when injecting a MetricRegistry. Note: This is deprecated. Please use @RegistryScope |
application (scope) |
All annotations (Except RegistryScope
and RegistryType
) have the following fields that correspond to the metadata fields described
in architecture.adoc.
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 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. String scope
-
Optional. The
MetricRegistry
scope that this metric belongs to. Default value isapplication
.
Note
|
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 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
An annotation for marking a method, constructor, or type as a counter.
The implementation must support the following annotation targets:
-
CONSTRUCTOR
-
METHOD
-
TYPE
Note
|
This annotation has changed in MicroProfile Metrics 2.0: Counters now always increase monotonically upon invocation. |
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.
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() {
}
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() {
}
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() {}
}
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.
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;
}
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.
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() {
}
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() {
}
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() {}
}
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.
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;
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();
}
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.
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.
Method | Description |
---|---|
|
Counter with given name and no tags |
|
Counter with given name and tags |
|
Counter from given |
|
Counter from given |
|
Histogram with given name and no tags |
|
Histogram with given name and tags |
|
Histogram from given |
|
Histogram from given |
|
Timer with given name and no tags |
|
Timer with given name and tags |
|
Timer from given |
|
Timer from given |
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.
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.
Method | Description |
---|---|
|
Removes all metrics with the given name |
|
Removes the metric with the given |
|
Removes all metrics that are accepted by the given |
The MetricRegistry
is used to maintain a collection of metrics along with their metadata.
There is one shared singleton of the MetricRegistry
per pre-defined scope (application, base, and vendor).
There is also one shared singleton of the MetricRegistry
per custom scope.
When metrics are registered using annotations and no scope is provided, the metrics are registered in the application MetricRegistry
(and thus the application scope).
When injected, the @RegistryScope
is used to selectively inject one of the application
, base
, vendor
or custom registries.
If no scope parameter is used, the default MetricRegistry
returned is the application
registry.
If using the deprecated @RegistryType
, it will be used as a qualifier to selectively inject one of the application
, base
or vendor
registries.
If no qualifier is used, the default MetricRegistry
returned is the application
registry. Note that @RegistryType
is now deprecated. Please use @RegistryScope
instead.
The @RegistryScope
annotation and @RegistryType
qualifer annotation should not be used together for the same MetricRegistry
injection. The application
, base
or vendor
registry produced by either injection strategies should be the same respective to the scope. That is to say, an injection of a MetricRegistry
with @RegistryScope(scope = MetricRegistry.APPLICATION_SCOPE)
will produce the same MetricRegistry
from using @RegistryType(type = MetricRegistry.Type.APPLICATION)
and vice-versa.
Implementations may choose to use a Factory class to produce the injectable MetricRegistry
bean via CDI. See appendix.adoc. Note: The factory would be an internal class and not exposed to the application.
The @RegistryScope
can be used to retrieve the MetricRegistry
for a specific scope.
The implementation must produce the corresponding MetricRegistry
specified by the RegistryScope
.
The @RegistryType
can be used to retrieve the MetricRegistry
for a specific scope.
The implementation must produce the corresponding MetricRegistry
specified by the RegistryType
.
Note
|
The implementor can optionally provide a read_only copy of the MetricRegistry for base and vendor scopes.
|
Note
|
The @RegistryType is deprecated. Please use @RegistryScope instead.
|
The implementation must produce the application MetricRegistry
when no RegistryScope
(or RegistryType
) is provided or when the RegistryScope
is application
(i.e. MetricRegistry.APPLICATION_SCOPE
) or if the deprecated RegistryType
is application
(i.e. MetricRegistry.Type.APPLICATION
). Application-defined metrics can also be registered to user-defined scopes
@Inject
MetricRegistry metricRegistry;
@RegistryScope
@Inject
@RegistryScope(scope=MetricRegistry.APPLICATION_SCOPE)
MetricRegistry metricRegistry;
@RegistryType
/*
* @RegistryType is deprecated. Please use @RegistryScope
*/
@Inject
@RegistryType(type=MetricRegistry.Type.APPLICATION)
MetricRegistry metricRegistry;
The implementation must produce the base MetricRegistry
when the RegistryScope
is base
(i.e. MetricRegistry.BASE_SCOPE
). The base MetricRegistry
contains any metrics the vendor has chosen to provide from base-metrics.adoc.
@RegistryScope
@Inject
@RegistryScope(scope=MetricRegistry.BASE_SCOPE)
MetricRegistry baseRegistry;
@RegistryType
@Inject
@RegistryType(type=MetricRegistry.Type.BASE)
MetricRegistry baseRegistry;
The implementation must produce the vendor MetricRegistry
when the RegistryScope
is vendor
(i.e. MetricRegistry.VENDOR_SCOPE
). The vendor MetricRegistry
must contain any vendor specific metrics.
@RegistryScope
@Inject
@RegistryScope(scope=MetricRegistry.VENDOR_SCOPE)
MetricRegistry vendorRegistry;
@RegistryType
@Inject
@RegistryType(type=MetricRegistry.Type.VENDOR)
MetricRegistry vendorRegistry;
The implementation must produce the MetricRegistry
corresponding to the custom-named registry when the RegistryType
is a custom value. If the custom-named MetricRegistry
does not yet exist the implementation must create a MetricRegistry
with the specified name.
@Inject
@RegistryScope(scope="motorguide")
MetricRegistry motorGuideRegistry;
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")
.build();
Meter me = new MyMeterImpl();
metricRegistry.register(m, me, new Tag("colour","blue"));
A default implementation DefaultMetadata
is provided in the API for convenience.