We addressed some significant questions while creating MicroProfile Metrics v5.0.
API or no API
In light of the increasing prevalence of developer use of APIs from Micrometer and OpenTelemetry, we considered whether we should continue to have a distinct API for MicroProfile Metrics. We decided to continue our path of providing an API for the following reasons:
-
provides an easy-to-use metrics API for application developers
-
provides continuity for the existing MicroProfile Metrics user community
-
provides a MicroProfile-style API (for example, CDI-based annotations), and configurability (MicroProfile Config), for ease of adoption by MicroProfile users
-
ensures compatibility across APIs within the same MicroProfile release
We also considered feedback from an informal poll in which a majority of respondents said they would use a MicroProfile Metrics API, given the other options.
Fixed implementation or vendor-chosen implementation
We considered whether MicroProfile Metrics should require vendors to use a particular metrics library in their implementations. The benefit of requiring a particular metrics library would be the potential for improved consistency across vendors. The benefits of not requiring a particular metrics library would be avoiding MicroProfile potentially overreaching by telling vendors which libraries to use, and leaving flexibility for vendors to change their implementation in the future if needed. Ultimately, we decided to not require a specific metrics library to be used in the implementation. Vendors may choose to implement using Micrometer libraries, OpenTelemetry libraries, Dropwizard libraries, custom code, or anything else they choose.
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"
- 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.
@ApplicationScoped
public class MetricRegistryFactory {
@Produces
@Default
public MetricRegistry getMetricRegistry(InjectionPoint ip) {
RegistryScope registryTypeAnnotation = ip.getAnnotated().getAnnotation(RegistryScope.class);
if (registryTypeAnnotation == null) {
return getOrCreate(MetricRegistry.APPLICATION_SCOPE);
} else {
String annoScope = registryTypeAnnotation.scope();
return getOrCreate(annoScope);
}
}
@Produces
@RegistryType(type = MetricRegistry.Type.APPLICATION)
public MetricRegistry getApplicationRegistry() {
return getOrCreate(MetricRegistry.Type.APPLICATION);
}
@Produces
@RegistryType(type = MetricRegistry.Type.BASE)
public MetricRegistry getBaseRegistry() {
return getOrCreate(MetricRegistry.Type.BASE);
}
@Produces
@RegistryType(type = MetricRegistry.Type.VENDOR)
public MetricRegistry getVendorRegistry() {
return getOrCreate(MetricRegistry.Type.VENDOR);
}
}
The SimpleTimer
class and @SimplyTimed
annotation have been removed. This change was made to make it possible to implement the spec using commonly used metrics libraries that lack a similar metric type.
Use Timer
class or @Timed
annotation instead. Alternatively, you can create your own Gauge
to track the total time and your own Counter
to track the total number of hits of something you want to time.
The ConcurrentGauge
class and @ConcurrentGauge
annotation have been removed. This change was made to make it possible to implement the spec using commonly used metrics libraries that lack a similar metric type.
Use Gauge
class or @Gauge
annotation instead. A Gauge
allows you to track a value that may go up or down over time. If you need to track the recent maximum or minimum with precision (as was handled by a ConcurrentGauge
), create a separate Gauge
for each of those statistics, in addition to the Gauge
to track the current value of what you are observing.
The Meter
class and @Metered
annotation have been removed. This change was made to make it possible to implement the spec using commonly used metrics libraries that lack a similar metric type.
Use Counter
class or @Counted
annotation instead. Tools, such as Prometheus, are able to compute the rate of increase of an observed metric over a specified period of time.
The Snapshot
class has been modified to avoid restricting the list of percentiles to a fixed set of percentile values. This change was made in anticipation of making the list of percentiles be configurable in the future. As in prior releases, the Timer
and Histogram
classes still track the 50th, 75th, 95th, 98th, 99th, and 99.9th percentiles in the corresponding Snapshot
.
Use snapshot.percentileValues()
method, then iterate over the returned array of PercentileValue
objects to find the value at the specific percentile you’re interested in.
The base_
, vendor_
and application_
prefixes for metric names that were used in prior releases have been replaced by a tag named mp_scope
with value base
, vendor
, or application
(you can also register metrics with custom scopes).
When using the Prometheus format output from the /metrics
endpoint, use metric_name{mp_scope="scopeValue",…}
instead of scopeValue_metric_name{…}
where metric_name
is the Prometheus-formatted name of your metric and scopeValue
is one of base
, vendor
, application
or a custom value.