Dirac implements MicroProfile Metrics 5.1.1, whose vocabulary nails down four primitives — Counter, Gauge, Histogram, Timer — their scopes (APPLICATION, BASE, VENDOR), and their exposition in two formats (OpenMetrics text and JSON). This page sweeps through these choices and states the invariants of the Vidocq implementation.
The four metric types
MP Metrics 5.0 simplified the surface inherited from the 1.x and 2.x lines: Meter, ConcurrentGauge, and SimpleTimer were dropped. Four primitives remain:
| Type | Annotation | Description |
|---|---|---|
|
|
Monotonic incremental counter. Implementation: |
|
|
Instantaneous value returned by a bean method. Resolved once at startup via |
|
(not annotable in the 5.1.1 API JAR used here) |
Value distribution with configurable percentiles (p50–p999 by default) and optional buckets. |
|
|
Invocation duration measured in |
|
The |
Three mandatory scopes
Spec §1.3 distinguishes three registries, populated and read separately:
| Scope | Role |
|---|---|
|
Application metrics. Injectable with no qualifier (default registry). |
|
Spec-mandated JVM metrics: |
|
Runtime-specific metrics — initially empty on Dirac’s side, extensible. |
On the injection side:
@Inject
MetricRegistry application; // APPLICATION by default
@Inject @RegistryScope(scope = MetricRegistry.BASE_SCOPE)
MetricRegistry base;
@Inject @RegistryScope(scope = MetricRegistry.VENDOR_SCOPE)
MetricRegistry vendor;
Metric identity: MetricID
The pair (name, tags) is the unique identity. Two counters with the same name but different tags are distinct metrics; two counters with the same name and the same tags are the same metric.
counter("http_requests_total", new Tag("method", "GET"));
counter("http_requests_total", new Tag("method", "POST"));
// → two distinct Counter instances, aggregated under the same OpenMetrics family
MetricID is an immutable record. The registry is a ConcurrentHashMap<MetricID, Metric> per scope.
Tags
Tags are arbitrary (key, value) pairs. Three sources combine:
-
Annotation tags — fixed at declaration:
@Counted(tags = "endpoint=orders"). -
Global application tags — key
mp.metrics.tags, e.g.mp.metrics.tags=app=orders,env=prod. Added to every metric of the application. -
mp_scopetag — automatically injected by the OpenMetrics formatter with the scope value (application,base,vendor).
A metric’s uniqueness is computed on the union of those tags after spec §3 normalisation.
Exposition formats
Two formats are produced by dirac-core, both hand-written with StringBuilder:
| Format | Detail |
|---|---|
OpenMetrics / Prometheus text |
Content-Type |
JSON |
Content-Type |
Content negotiation is handled by MetricsEndpoint (Reference).
Distribution configuration
Histograms and timers read three mp.metrics.distribution.* keys through MicroProfile Config:
| Key | Effect |
|---|---|
|
List of percentiles ( |
|
Fixed buckets for |
|
Fixed buckets for |
An empty value (mp.metrics.distribution.percentiles=) disables percentiles entirely. Implementation lives in DistributionConfig inside dirac-core.
REST endpoint GET /metrics
When dirac-rest and Cassini are on the classpath, three paths are exposed:
| Route | Effect |
|---|---|
|
Every scope, format negotiated on |
|
A single scope ( |
|
A single metric family. 404 if not found. |
The endpoint is optional: without dirac-rest, metrics are still reachable programmatically via MetricRegistry.
Further reading
-
Usage patterns — annotations, tags, percentiles.
-
Internals — lock-free registry, Vauban BCE.
-
Reference — exhaustive
mp.metrics.*keys.