Contents
Overview
Helidon SE metrics is a neutral metrics API which provides
a unified way for Helidon servers to export monitoring data—telemetry—to management agents, and
a unified Java API which all application programmers can use to register and update meters to expose telemetry data from their services.
Metrics is one of the Helidon observability features.
Recommended Configuration Setting
Beginning with Helidon 4.1, strongly consider assigning the config setting
metrics.gc-time-type = gaugeSee the longer discussion below in the Configuration section.
A Word about Terminology
Helidon SE uses the term "metrics" to refer to the subsystem in Helidon which manages the registration of, updates to, and reporting of aggregate statistical measurements about the service. The term "meter" refers to an entity which collects these measurements, such as a counter or a timer.
Maven Coordinates
To enable metrics, add the following dependency to your project’s pom.xml (see Managing Dependencies).
<dependency>
<groupId>io.helidon.metrics</groupId>
<artifactId>helidon-metrics-api</artifactId>
</dependency>This dependency adds the metrics API and a no-op implementation of that API to your project. The no-op implementation:
does not register meters in a registry
does not update meter values
does not expose the metrics endpoint for reporting meter values.
To include the full-featured metrics implementation and support for the metrics endpoint, add the following dependency to your project:
<dependency>
<groupId>io.helidon.webserver.observe</groupId>
<artifactId>helidon-webserver-observe-metrics</artifactId>
</dependency>Adding this dependency packages the full-featured metrics implementation and support for the metrics endpoint with your service.
You might notice the transitive dependency io.helidon.metrics.providers:helidon-metrics-providers-micrometer in your project. This component contains an implementation of the Helidon metrics API that uses Micrometer as the underlying metrics technology.
Helidon provides several built-in meters in a separate artifact. To include the build-in meters, add the following dependency to your project:
<dependency>
<groupId>io.helidon.metrics</groupId>
<artifactId>helidon-metrics-system-meters</artifactId>
<scope>runtime</scope>
</dependency>Usage
Instrumenting Your Service
You add meters to your service by writing code which explicitly invokes the metrics API to register meters, retrieve previously-registered meters, and update meter values.
Later sections of this document describe how to do this.
Meter Types
Helidon supports meters inspired by Micrometer and summarized in the following table:
| Meter Type | Description | Micrometer reference |
Counter | Monotonically-increasing long value. | Counters |
DistributionSummary | Summary of samples each with a long value. Reports aggregate information over all samples (count, total, mean, max) as well as the distribution of sample values using percentiles and bucket counts. | Distribution summaries |
Timer | Accumulation of short-duration (typically under a minute) intervals. Typically updated using a Java Duration or by recording the time taken by a method invocation or lambda. Reports the count, total time, max, and mean; provides a distribution summary of the samples. | Timers |
Gauge<? extends Number> | View of a value that is assignment-compatible with a subtype of Java Number. The underlying value is updated by code elsewhere in the system, not by invoking methods on the gauge itself. | Gauges |
Categorizing Types of Meters
Helidon distinguishes among scopes, or categories, of meters.
Helidon includes meters in the built-in scopes described below. Applications often register their own meters in the application scope but can create their own scopes and register meters within them.
| Built-in Scope | Typical Usage |
|---|---|
base | OS or Java runtime measurements (available heap, disk space, etc.). |
vendor | Implemented by vendors, including the REST.request metrics and other key performance indicator measurements (described in later sections). |
application | Declared via annotations or programmatically registered by your service code. |
When an application creates a new meter it can specify which scope the meter belongs to. If the application does not specify a scope for a new meter, the default scope is application.
Meter Registry
Helidon stores all meters in a meter registry. Typically, applications use the global meter registry which is the registry where Helidon stores built-in meters. Application code refers to the global registry using Metrics.globalRegistry().
Publishing Metrics for External Access
Helidon’s Micrometer-based metrics implementation includes these ways of publishing metrics data to external systems:
Prometheus/OpenMetrics
OTLP (OpenTelemetry Protocol)
Configuring Publishers
The configuration of metrics publishers as described below is a preview feature which Helidon intends to keep, but its external interface or behavior might evolve between dot releases.
You can configure publishers in the publishers configuration section under the top level metrics node or under server.features.observe.observers.metrics. If you do not set up publishers explicitly, Helidon uses an inferred Prometheus publisher for backward compatibility. See this later section for details.
Publishers in Helidon’s Micrometer-based metrics implementation use Micrometer MeterRegistry implementations. For each enabled publisher, Helidon adds the corresponding meter registry to Micrometer’s global registry. This has these important effects:
Meters which Helidon or your code registers using the Helidon metrics API are registered in all active Micrometer meter registries.
Each Helidon meter registered has an implementation in every active Micrometer meter registry.
When Helidon or your code updates a Helidon meter, Micrometer applies the change to every corresponding meter from each active meter registry.
As a result, configuring more than one active meter registry can affect performance.
Make sure at least one of the configured publishers is enabled. If not, Micrometer does not have any active meter registry implementations and the registered metrics are no-ops. Helidon logs a warning in this case during the metrics observer initialization.
Configuring an OTLP Publisher
If you configure an OTLP publisher, Helidon exports metrics data periodically to a backend system you configure.
Configuration options
| Key | Kind | Type | Default Value | Description |
|---|---|---|---|---|
aggregation-temporality | VALUE | i.m.r.o.AggregationTemporality | CUMULATIVE | Algorithm to use for adjusting values before transmission |
base-time-unit | VALUE | TimeUnit | java.util.concurrent.TimeUnit.MILLISECONDS | Base time unit for timers |
batch-size | VALUE | Integer | 10000 | Number of measurements to send in a single request to the backend |
enabled | VALUE | Boolean | true | Whether the configured publisher is enabled |
headers | MAP | String | Headers to add to each transmission message | |
interval | VALUE | Duration | PT60s | Interval between successive transmissions of metrics data |
max-bucket-count | VALUE | Integer | 160 | Maximum bucket count to apply to statistical histogram |
max-buckets-per-meter | MAP | Integer | Maximum number of buckets to use for specific meters | |
max-scale | VALUE | Integer | 20 | Maximum scale value to apply to statistical histogram |
name | VALUE | String | N/A | |
prefix | VALUE | String | otlp | The prefix for settings |
properties | MAP | String | Property values to be returned by the OTLP meter registry configuration | |
resource-attributes | MAP | String | Attribute name/value pairs to be associated with all metrics transmissions | |
url | VALUE | String | http://localhost:4318/v1/metrics | URL to which to send metrics telemetry |
The configuration directly mirrors the Micrometer OtlpMeterRegistry settings so you can control all behavior which Micrometer exposes for the meter registry.
The following example sets up an OTLP publisher to transmit metrics data every 30 seconds.
metrics:
publishers:
otlp:
interval: PT30S
url: 'http://somehost.com:4318/v1/metrics'- Introduces the configured publishers.
- Configures an OTLP publisher to transmit every 30 seconds to the given endpoint.
Configuring a Prometheus Publisher
If you configure a Prometheus publisher or rely on the inferred one, Helidon can make the metrics data available in the Prometheus/OpenMetrics format. (To serve the data at the metrics endpoint in your service, your project must also depend on the Helidon metrics observer component.)
Configuration options
Understanding the Inferred Prometheus Publisher
As described earlier, Helidon prepares an inferred Prometheus publisher if you do not set up any publishers.
Note that Helidon uses the inferred publisher only if you add no publishers explicitly, either in the configuration or programmatically. If you specify any publishers explicitly, Helidon uses only the ones you set up.
In particular, Helidon does not use the inferred Prometheus publisher if you create a metrics.publishers section containing only an OTLP publisher.
You can configure other publishers and still have Helidon use the default one by simply adding the prometheus publisher entry. You do not need to specify further settings for it.
metrics:
publishers:
prometheus:
otlp:
interval: PT20SWriting Additional Publishers
You can write other publishers by following these steps:
- Choose one of the Micrometer
MeterRegistryimplementations for the type of publishing you want to support. (for exampleDatadogMeterRegistry) - Create a config blueprint which exposes the meter registry’s settable properties from
DatadogConfig. - Write a
DatadogPublisherclass which implements Helidon’sMetricsPublisherfor Datadog. - Write a
DatadogPublisherProviderclass which implements Helidon’sMetricsPublisherProviderfor your publisher. - Advertise your provider so Java service loading can find it, creating a
META-INF/services/io.helidon.metrics.spi.PublisherProviderfile listing your implementation class.
Look at Helidon’s OTLP publisher blueprint and the related types as an example.
Refer to your publisher in configuration using the config key you set up in the publisher provider.
metrics:
publishers:
micrometer-datadog:
interval: PT15SUsing and Controlling the Metrics Endpoint
When you add the helidon-webserver-observe-metrics dependency to your project, and if you explicitly set up a Prometheus publisher or use the default one, Helidon provides a built-in REST endpoint /observe/metrics which responds with a report of the registered meters and their values.
Clients can request a particular output format from the endpoint.
/observe/metrics output| Format | Requested by |
|---|---|
| OpenMetrics (Prometheus) | default (text/plain) |
| JSON | Header Accept: application/json |
Clients can also limit the report by specifying the scope as a query parameter in the request URL:
/observe/metrics?scope=base/observe/metrics?scope=vendor/observe/metrics?scope=application
Further, clients can narrow down to a specific metric name by adding the name as another query parameter, such as /observe/metrics?scope=application&name=myCount.
curl -s -H 'Accept: text/plain' -X GET http://localhost:8080/observe/metrics# HELP classloader_loadedClasses_count Displays the number of classes that are currently loaded in the Java virtual machine.
# TYPE classloader_loadedClasses_count gauge
classloader_loadedClasses_count{scope="base",} 5297.0See the summary of the OpenMetrics and Prometheus Format for more information.
curl -s -H 'Accept: application/json' -X GET http://localhost:8080/observe/metrics{
"base" : {
"memory.maxHeap" : 3817865216,
"memory.committedHeap" : 335544320
}
}In addition to your application meters, the reports contain other meters of interest such as system and VM information.
OpenMetrics and Prometheus Format
The OpenMetrics format and the Prometheus exposition format are very similar in most important respects but are not identical. This brief summary treats them as the same.
The OpenMetrics/Prometheus format represents each meter using three lines of output as summarized in the following table.
| Line prefix | Purpose | Format |
|---|---|---|
# TYPE | Displays the scope, name, and type of the meter | TYPE <scope>:<output-name> <meter-type> |
# HELP | Displays the scope, name, and description of the meter | HELP <scope>:<output-name> <registered description> |
| (none) | Displays the scope, meter ID, and current value of the meter | <scope>:<output-name> <current value> |
The OpenMetrics/Prometheus output converts meter IDs in these ways:
Names in camel case are converted to "snake case" and dots are converted to underscores.
Names include any units specified for the meter.
For percentiles, the ID includes a tag identifying which percentile the line of output describes.
As the earlier example output showed, for a meter with multiple values, such as a timer or a distribution summary, (with, among others, max, mean, and count), the OpenMetrics/Prometheus output reports a "metric family" which includes a separate family member meter for each of the multiple values. The name for each member in the family is derived from the registered name for the meter plus a suffix indicating which one of the meter’s multiple values the line refers to.
The following table summarizes the naming for each meter type.
| Meter Type | Example registered name | Meter family member | Name Suffix | Example displayed name |
|---|---|---|---|---|
Counter | requests.count | count | _total | requests_count_total |
DistributionSummary | nameLengths | count | _count | nameLengths_count |
| sum | _sum | nameLengths_sum | ||
| max | _max | nameLengths_max | ||
| percentile | none | nameLengths{scope="base",quantile="0.5",} | ||
Gauge | classloader.loadedClasses.count | value | none | classloader_loadedClasses_count |
Timer 1 | vthreads.recentPinned | count | _count | vthreads_recentPinned_seconds_count |
| sum | _sum | vthreads_recentPinned_seconds_sum | ||
| max | _max | vthreads_recentPinned_seconds_max | ||
| percentile | none | vthreads_recentPinned_seconds{scope="base",quantile="0.5",} |
1 The OpenMetrics/Prometheus output format reports a timer as a summary with units of seconds.
JSON Format
Unlike OpenMetrics/Prometheus output, which combines the data and the metadata in a single response, you use an HTTP GET request to retrieve metrics JSON data and an OPTIONS request to retrieve metadata in JSON format.
Helidon groups meters in the same scope together in JSON output as shown in the following example.
{
"application": {
"getTimer": {
"type": "timer",
"unit": "seconds",
"description": "Timer for getting the default greeting"
}
},
"vendor": {
"requests.count": {
"type": "counter",
"description": "Each request (regardless of HTTP method) will increase this counter"
}
},
"base": {
"cpu.systemLoadAverage": {
"type": "gauge",
"description": "Displays the system load average for the last minute."
},
"classloader.loadedClasses.count": {
"type": "gauge",
"description": "Displays the number of classes that are currently loaded in the Java virtual machine."
}
}
}- Note the
application,vendor, andbasesections.
If an HTTP request selects by scope, the output omits the extra level of structure that identifies the scope as shown in the following example.
base scope (partial){
"cpu.systemLoadAverage": {
"type": "gauge",
"description": "Displays the system load average for the last minute."
},
"classloader.loadedClasses.count": {
"type": "gauge",
"description": "Displays the number of classes that are currently loaded in the Java virtual machine."
}
}Understanding the JSON Metrics Data Format
The Helidon JSON format expresses each meter as either a single value (for example, a counter) or a structure with multiple values (for example, a timer).
Counter)"requests.count": 5Timer)"getTimer": {
"count": 3,
"max": 0.0030455,
"mean": 0.0011060836666666666,
"elapsedTime": 0.003318251,
"p0.5": 0.000151552,
"p0.75": 0.003141632,
"p0.95": 0.003141632,
"p0.98": 0.003141632,
"p0.99": 0.003141632,
"p0.999": 0.003141632
}By default, Helidon formats time values contained in JSON output as seconds. You can change this behavior as described below.
Understanding the JSON Metrics Metadata Format
Access the metrics endpoint with an HTTP OPTIONS request and the Accept: application/json header to retrieve metadata in JSON format.
Counter metadata"requests.count": {
"type": "counter",
"description": "Each request (regardless of HTTP method) will increase this counter"
}Timer metadata"getTimer": {
"type": "timer",
"unit": "seconds",
"description": "Timer for getting the default greeting"
}Generally, the output for a given meter reflects only the metadata that the application or Helidon code explicitly set on that meter.
One exception is that metadata for a timer always includes the unit field. By default, Helidon formats timer data in JSON output as seconds, regardless of any explicit baseUnit setting applied to the timers. But as described below you can change this behavior which can lead to different timers being formatted using different units. Checking the metadata is the only way to know for sure what units Helidon used to express a given timer, so Helidon always includes unit in timer metadata.
Controlling JSON Timer Output
By default, Helidon expresses timer data as seconds.
You can change this using configuration:
application.yamlmetrics:
timers:
json-units-default: units - For units specify any valid name for a
TimeUnitvalue (SECONDS,MILLISECONDS, etc.)
If you have configured json-units-default, Helidon formats each timer’s data as follows:
- If code set
baseUniton the timer, Helidon uses those units for that timer. - Otherwise, Helidon uses the default units you configured.
To enable the JSON output behavior from Helidon 3, specify json-units-default as NANOSECONDS.
Enabling the Metrics REST Service
If you add the dependencies described above, your service automatically supports the metrics REST endpoint as long as the WebServer is configured to discover features automatically.
If you disable auto-discovery, you can add the metrics observer explicitly.
- Create an instance of
MetricsObserver, either directly as shown below or using its builder. - Include the
MetricsObserverinstance in your application’sObserveFeature. - Register your
ObserveFeaturewith yourWebServer.
ObserveFeature observe = ObserveFeature.builder()
.config(config.get("server.features.observe"))
.addObserver(MetricsObserver.create())
.build();
WebServer server = WebServer.builder()
.config(Config.global().get("server"))
.featuresDiscoverServices(false)
.addFeature(observe)
.routing(Main::routing)
.build()
.start();API
To work with Helidon Metrics in your code, follow these steps:
- Use the static
globalRegistrymethod on theMetricsinterface to get a reference to the globalMeterRegistryinstance. - Use the
MeterRegistryinstance to register new meters and look up previously-registered meters. - Use the meter reference returned from the
MeterRegistryto update the meter or get its value.
You can also use the MeterRegistry to remove an existing meter.
Helidon Metrics API
The Helidon Metrics API defines the classes and interfaces for meter types and other related items.
The following table summarizes the meter types.
| Meter Type | Usage |
|---|---|
Counter | Monotonically increasing count of events. |
Gauge | Access to a value managed by other code in the service. |
DistributionSummary | Calculates the distribution of a value. |
Timer | Frequency of invocations and the distribution of how long the invocations take. |
Each meter type has its own set of methods for updating and retrieving the value.
The MeterRegistry API
To register or look up meters programmatically, your service code uses the global MeterRegistry. Simply invoke Metrics.globalRegistry() to get a reference to the global meter registry.
To locate an existing meter or register a new one, your code:
- Creates a builder of the appropriate type of meter, setting the name and possibly other characteristics of the meter.
- Invokes the
MeterRegistry.getOrCreatemethod, passing the builder.
The meter registry returns a reference to a previously-registered meter with the specified name and tags or, if none exists, a newly-registered meter. Your code can then operate on the returned meter as needed to record new measurements or retrieve existing data.
The example code in the section below illustrates how to register, retrieve, and update meters.
Understanding Timers, Units, and Output
Your application can assign the meter builder’s Meter.Builder baseUnit setting for any meter your application creates. In particular, the Timer.Builder baseUnit method allows code to assign a baseUnit for a timer, passing a Java TimeUnit value. The timer builder also has the String variant of the baseUnit method and enforces that the value corresponds (case-insensitively) to one of the TimeUnit enum values.
Note that, regardless of the baseUnit setting for a Timer, by convention and specification Prometheus output expresses time values in seconds.
By default, the same is true of Helidon’s JSON format: timer values are displayed in seconds regardless of any timer’s baseUnit setting. You can override this as described in the Controlling Timer Output section, in which case the JSON output for each timer reflects its baseUnit setting.
Accessing the Underlying Implementation: unwrap
The neutral Helidon metrics API is an abstraction of common metrics behavior independent from any given implementation. As such, we intentionally excluded some implementation-specific behavior from the API.
Sometimes you might want access to methods that are present in a particular metrics implementation but not in the Helidon API. Helidon allows that via the unwrap method on the meter types and on their builders. Each full implementation of the Helidon meter types and their builders refers to a delegate meter or delegate builder internally. The unwrap method lets you obtain the delegate, cast to the type you want.
Of course, using this technique binds your code to a particular metrics implementation.
The Wrapper interface declares the unwrap method which accepts a class parameter to which the delegate is cast. You can then invoke any method declared on the implementation-specific type.
Configuration
To control how the Helidon metrics subsystem behaves, add a metrics section to your configuration file, such as application.yaml.
Certain default configuration values depend on the fact that you are using Helidon SE as described in the second table below.
Configuration options
| Key | Kind | Type | Default Value | Description |
|---|---|---|---|---|
app-name | VALUE | String | Value for the application tag to be added to each meter ID | |
app-tag-name | VALUE | String | Name for the application tag to be added to each meter ID | |
built-in-meter-name-format | VALUE | i.h.m.a.BuiltInMeterNameFormat | CAMEL | Output format for built-in meter names |
enabled | VALUE | Boolean | true | Whether metrics functionality is enabled |
key-performance-indicators | VALUE | i.h.m.a.KeyPerformanceIndicatorMetricsConfig | Key performance indicator metrics settings | |
permit-all | VALUE | Boolean | true | Whether to allow anybody to access the endpoint |
publishers | LIST | i.h.m.a.MetricsPublisher | Metrics publishers which make the metrics data available to external systems | |
publishers-discover-services | VALUE | Boolean | false | Whether to enable automatic service discovery for publishers |
rest-request.enabled | VALUE | Boolean | false | Whether automatic REST request metrics should be measured |
roles | LIST | String | observe | Hints for role names the user is expected to be in |
scoping | VALUE | i.h.m.a.ScopingConfig | Settings related to scoping management | |
tags | LIST | i.h.m.a.MetricsConfigSupport | Global tags | |
timers.json-units-default | VALUE | TimeUnit | Default units for timer output in JSON if not specified on a given timer | |
virtual-threads.enabled | VALUE | Boolean | false | Whether Helidon should expose meters related to virtual threads |
virtual-threads.pinned.threshold | VALUE | Duration | PT0.020S | Threshold for sampling pinned virtual threads to include in the pinned threads meter |
warn-on-multiple-registries | VALUE | Boolean | true | Whether to log warnings when multiple registries are created |
Deprecated Options
| Key | Kind | Type | Default Value | Description |
|---|---|---|---|---|
gc-time-type | VALUE | i.h.m.a.GcTimeType | COUNTER | Whether the gc.time meter should be registered as a gauge (vs |
rest-request-enabled | VALUE | Boolean | Whether automatic REST request metrics should be measured (as indicated by the deprecated config key rest-request-enabled, the config key using a hyphen instead of a dot separator) |
| Key | Default Value |
|---|---|
app-tag-name |
|
scoping.tag-name |
|
scoping.default |
|
Controlling the Meter Type for gc.time
To date Helidon 4 releases have implemented the system-provided meter gc.time as a counter. In fact, a gauge is more suitable for the approximate time the JVM has spent doing garbage collection.
Helidon 4.4.1 continues to use a counter by default to preserve backward compatibility, but you can choose to use a gauge by setting the configuration property metrics.gc-time-type to gauge. You can also set the config property to counter which is the default.
Why should you care? In fact, this distinction might not make a difference for many users. But for others the differences between the programmatic APIs for Counter and Gauge would affect application code that works directly with the gc-time meter. Further, the difference in output—particularly in the OpenMetrics/Prometheus format—might affect their application or downstream monitoring tools.
The ability to choose the meter type for gc.time is deprecated and is planned for removal in a future major release of Helidon at which time Helidon will always use a gauge.
Controlling the Metrics Observer
Helidon can make the registered meters and their current values available externally at an endpoint (/observe/metrics by default). You can control aspects of how Helidon furnishes this information under the server.features.observe.observers.metrics configuration section.
| key | type | default value | description |
|---|---|---|---|
auto | Automatic metrics collection settings. | ||
enabled | boolean | true | Whether this observer is enabled. |
endpoint | string | /observe/metrics | Path at which clients can retrieve metrics information. |
See the Helidon OpenTelemetry documentation for more information.
Selecting REST Endpoints for Automatic Measurement
You can choose which endpoints to include in Helidon’s automatic measurements using the auto-http-metrics config section.
Configuration options
| Key | Kind | Type | Default Value | Description |
|---|---|---|---|---|
enabled | VALUE | Boolean | true | Whether automatic metrics collection as a whole is enabled |
opt-in | LIST | String | Elective attribute for which to opt in | |
paths | LIST | i.h.w.o.m.AutoHttpMetricsPathConfig | Automatic metrics collection settings | |
sockets | LIST | String | Socket names for sockets to be instrumented with automatic metrics |
The paths section contains zero or more entries, each entry having the following settings:
path entry settings| Key | Required | Default Value | Usage |
|---|---|---|---|
path | yes | Path-matching expression:
| |
methods | all HTTP method types | Which HTTP methods match this entry | |
enabled | true | Whether requests that match this entry should be measured |
Helidon decides whether to measure incoming requests as follows:
If you omit the
auto-http-metricsconfiguration, Helidon measures all endpoints.If you specify the
auto-http-metricsconfiguration, by default Helidon does not measure built-in endpoints such as metrics, health, and openapi. You can add items underauto-http-metrics.pathsto control more exactly which endpoints to measure.If you include the
pathssection, Helidon checks a request against the path entries in order. A given request matches an entry if its path matches the path pattern and its HTTP method is in themethodslist. If there is nomethodslist for an entry, all HTTP methods match the entry.If a request matches an entry, the entry’s
enabledsetting determines if the request should be measured.If a request matches multiple entries, the first match wins.
If a request matches no entry, it is measured.
The auto-http-metrics.sockets setting controls which sockets are included in the measurements; if not set, Helidon measures requests on all sockets.
server:
features:
observe:
observers:
metrics:
auto-http-metrics:
paths:
- path: "/greet"
methods: ["GET","HEAD"]
- path: "/greet/{name}"
enabled: false
sockets: ["@default","private"] - Measure
/greetfor onlyGETandHEADrequests. - Do not measure the personalized greeting requests.
- Measure only endpoints on the default socket and the socket named
private. Endpoints on other sockets (such as if you had anadminsocket) are not measured.
The AutoHttpMetricsConfig documentation describes the configuration more fully.
Examples
Helidon SE includes several pre-written example applications illustrating aspects of metrics:
Enabling/disabling meters using
MetricsObserverandMetricsConfigControlling key performance indicator metrics using configuration and
KeyPerformanceIndicatorMetricsSettings.
The rest of this section shows how to add a custom meter to your code and how to configure the Helidon metrics subsystem.
Example Application Code
The following example, based on the Helidon SE QuickStart application, shows how to register and update a new Counter in application code. The counter tracks the number of times any of the service endpoints is accessed.
Counterpublic class GreetService implements HttpService {
private final Counter accessCtr = Metrics.globalRegistry()
.getOrCreate(Counter.builder("accessctr"));
@Override
public void routing(HttpRules rules) {
rules
.any(this::countAccess)
.get("/", this::getDefaultMessageHandler)
.get("/{name}", this::getMessageHandler)
.put("/greeting", this::updateGreetingHandler);
}
void countAccess(ServerRequest request,
ServerResponse response) {
accessCtr.increment();
response.next();
}
void getDefaultMessageHandler(ServerRequest request,
ServerResponse response) {
// ...
}
void getMessageHandler(ServerRequest request,
ServerResponse response) {
// ...
}
void updateGreetingHandler(ServerRequest request,
ServerResponse response) {
// ...
}
}- Get the global meter registry.
- Create (or find) a counter named "accessctr" in the global registry.
- Route every request to the
countAccessmethod. - Increment the access counter for every request.
Perform the following steps to see the new counter in action.
mvn package
java -jar target/helidon-quickstart-se.jarapplication metricscurl 'http://localhost:8080/observe/metrics?scope=application' # HELP accessctr_total
# TYPE accessctr_total counter
accessctr_total{scope="application",} 0.0 - Access the metrics endpoint, selecting only application meters.
- Note the counter is zero; we have not accessed a service endpoint yet.
curl http://localhost:8080/greet{"message":"Hello World"}application metrics againcurl 'http://localhost:8080/observe/metrics?scope=application'# HELP accessctr_total
# TYPE accessctr_total counter
accessctr_total{scope="application",} 1.0 - The counter now reports 1, reflecting our earlier access to the
/greetendpoint.
Example Configuration
Metrics configuration is quite extensive and powerful and, therefore, a bit complicated. The rest of this section illustrates some of the most common scenarios:
Disable Metrics Subsystem
server:
features:
observe:
observers:
metrics:
enabled: falseHelidon does not update metrics, and the /observe/metrics endpoints respond with 404.
Configuring Virtual Threads Meters
Enabling Virtual Threads Meters
Gathering data to compute the meters for virtual threads is designed to be as efficient as possible, but doing so still imposes a load on the server and by default Helidon does not report meters related to virtual threads.
To enable the meters describing virtual threads include a config setting as shown in the following example.
metrics:
virtual-threads:
enabled: trueControlling Measurements of Pinned Virtual Threads
Helidon measures pinned virtual threads only when the thread is pinned for a length of time at or above a threshold. Control the threshold as shown in the example below.
metrics:
virtual-threads:
pinned:
threshold: PT0.100SThe threshold value is a Duration string, such as PT0.100S for 100 milliseconds.
Collecting Basic and Extended Key Performance Indicator (KPI) Meters
Any time you include the Helidon metrics module in your application, Helidon tracks a basic performance indicator meter: a Counter of all requests received (requests.count)
Helidon SE also includes additional, extended KPI meters which are disabled by default:
current number of requests in-flight - a
Gauge(requests.inFlight) of requests currently being processedlong-running requests - a
Counter(requests.longRunning) measuring the total number of requests which take at least a given amount of time to complete; configurable, defaults to 10000 milliseconds (10 seconds)load - a
Counter(requests.load) measuring the number of requests worked on (as opposed to received)deferred - a
Gauge(requests.deferred) measuring delayed request processing (work on a request was delayed after Helidon received the request)
You can enable and control these meters using configuration:
server:
features:
observe:
observers:
metrics:
key-performance-indicators:
extended: true
long-running:
threshold-ms: 2000Additional Information
References
Support for the Prometheus Metrics API
Helidon provides optional support for the Prometheus metrics API.
To use it, your service registers Prometheus support with your routing set-up. You can customize its configuration. For information about using Prometheus, see the Prometheus documentation: https://prometheus.io/docs/introduction/overview/.
Helidon’s fully-functional, built-in metrics implementation supports Prometheus (OpenMetrics) output. Use the optional support described in this section only if you want to use the Prometheus API from your application code.
Maven Coordinates
<dependency>
<groupId>io.helidon.metrics</groupId>
<artifactId>helidon-metrics-prometheus</artifactId>
</dependency>Usage
Your application code uses the Prometheus API to manage metrics. To expose those metrics to clients via a REST endpoint, your code uses the PrometheusSupport interface which Helidon provides.
API
Your code creates a PrometheusSupport object either using a static factory method (shown in the following example) or by using its Builder.
routing
.addFeature(PrometheusSupport.create())
.register("/myapp", new MyService());This example uses the default Prometheus CollectorRegistry. By default, the PrometheusSupport and exposes its REST endpoint at the path /metrics. Use the builder obtained by PrometheusSupport.builder() to configure a different CollectorRegistry or a different path.