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.

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).

Packaging the metrics API
<dependency>
    <groupId>io.helidon.metrics</groupId>
    <artifactId>helidon-metrics-api</artifactId>
</dependency>
Copied

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, add the following dependency to your project:

Packaging a full-featured metrics implementation
<dependency>
    <groupId>io.helidon.webserver.observe</groupId>
    <artifactId>helidon-webserver-observe-metrics</artifactId>
</dependency>
Copied

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:

Packaging the built-in meters
<dependency>
    <groupId>io.helidon.metrics</groupId>
    <artifactId>helidon-metrics-system-meters</artifactId>
    <scope>runtime</scope>
</dependency>
Copied

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.

Categorizing Types of Meters

Helidon distinguishes among scopes, or types, 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 meter scopes
Built-in ScopeTypical Usage
baseOS or Java runtime measurements (available heap, disk space, etc.).
vendorImplemented by vendors, including the REST.request metrics and other key performance indicator measurements (described in later sections).
applicationDeclared 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().

Retrieving Metrics Reports from your Service

When you add the helidon-webserver-observe-metrics dependency to your project, Helidon automatically 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.

Formats for /observe/metrics output
FormatRequested by
OpenMetrics (Prometheus)default (text/plain)
JSONHeader 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.

Example Reporting: Prometheus format
curl -s -H 'Accept: text/plain' -X GET http://localhost:8080/observe/metrics
Copied
# TYPE base:classloader_total_loaded_class_count counter
# HELP base:classloader_total_loaded_class_count Displays the total number of classes that have been loaded since the Java virtual machine has started execution.
base:classloader_total_loaded_class_count 3157
Copied
Example Reporting: JSON format
curl -s -H 'Accept: application/json' -X GET http://localhost:8080/observe/metrics
Copied
JSON response:
{
   "base" : {
      "memory.maxHeap" : 3817865216,
      "memory.committedHeap" : 335544320
    }
}
Copied

In addition to your application meters, the reports contain other meters of interest such as system and VM information.

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.

  1. Create an instance of MetricsObserver, either directly as shown below or using its builder.
  2. Include the MetricsObserver instance in your application’s ObserveFeature.
  3. Register your ObserveFeature with your WebServer.
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();
Copied

API

To work with Helidon Metrics in your code, follow these steps:

  1. Use the static globalRegistry method on the Metrics interface to get a reference to the global MeterRegistry instance.
  2. Use the MeterRegistry instance to register new meters and look up previously-registered meters.
  3. Use the meter reference returned from the MeterRegistry to 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 Types
Meter TypeUsage
CounterMonotonically increasing count of events.
GaugeAccess to a value managed by other code in the service.
DistributionSummaryCalculates the distribution of a value.
TimerFrequency 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:

  1. Creates a builder of the appropriate type of meter, setting the name and possibly other characteristics of the meter.
  2. Invokes the MeterRegistry.getOrCreate method, 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.

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.

Type: io.helidon.webserver.observe.metrics.MetricsObserver

This is a standalone configuration type, prefix from configuration root: metrics

This type provides the following service implementations:

  • io.helidon.webserver.observe.spi.ObserveProvider

Configuration options

Optional configuration options
keytypedefault valuedescription
app-name

string

 

Value for the application tag to be added to each meter ID.

@return application tag value
app-tag-name

string

 

Name for the application tag to be added to each meter ID.

@return application tag name
enabled

boolean

true

Whether metrics functionality is enabled.

@return if metrics are configured to be enabled
endpoint

string

metrics
key-performance-indicators 

Key performance indicator metrics settings.

@return key performance indicator metrics settings
permit-all

boolean

 

Whether to allow anybody to access the endpoint.

@return whether to permit access to metrics endpoint to anybody, defaults to `true`
@see #roles()
rest-request-enabled

boolean

 

Whether automatic REST request metrics should be measured.

@return true/false
roles

string[]

 

Hints for role names the user is expected to be in.

@return list of hints
scoping 

Settings related to scoping management.

@return scoping settings
tags 

Global tags.

@return name/value pairs for global tags

Examples

Helidon SE includes several pre-written example applications illustrating aspects of metrics:

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.

Define and use a Counter
public 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) {
        // ...
    }
}
Copied
  • Get the global meter registry.
  • Create (or find) a counter named "accessctr" in the global registry.
  • Route every request to the countAccess method.
  • Increment the access counter for every request.

Perform the following steps to see the new counter in action.

Build and run the application
mvn package
java -jar target/helidon-quickstart-se.jar
Copied
Retrieve application metrics
curl 'http://localhost:8080/observe/metrics?scope=application' 
Copied
Response
# HELP accessctr_total
# TYPE accessctr_total counter
accessctr_total{scope="application",} 0.0 
Copied
  • Access the metrics endpoint, selecting only application meters.
  • Note the counter is zero; we have not accessed a service endpoint yet.
Access a service endpoint to retrieve a greeting
curl http://localhost:8080/greet
Copied
JSON response:
{"message":"Hello World"}
Copied
Retrieve application metrics again
curl 'http://localhost:8080/observe/metrics?scope=application'
Copied
Response
# HELP accessctr_total
# TYPE accessctr_total counter
accessctr_total{scope="application",} 1.0 
Copied
  • The counter now reports 1, reflecting our earlier access to the /greet endpoint.

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

Disabling metrics entirely
server:
  features:
    observe:
      observers:
        metrics:
          enabled: false
Copied

Helidon does not update metrics, and the /observe/metrics endpoints respond with 404..

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 processed

  • long-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:

Controlling extended KPI meters
server:
  features:
    observe:
      observers:
        metrics:
          key-performance-indicators:
            extended: true
            long-running:
              threshold-ms: 2000
Copied

Additional Information

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 for Helidon Prometheus API support
<dependency>
    <groupId>io.helidon.metrics</groupId>
    <artifactId>helidon-metrics-prometheus</artifactId>
</dependency>
Copied

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());
Copied

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.