Contents

Overview

Helidon MP metrics implements the MicroProfile Metrics specification, providing:

  • a unified way for MicroProfile servers to export monitoring data—​telemetry—​to management agents, and

  • a unified Java API which all application programmers can use to register and update metrics to expose telemetry data from their services.

  • support for metrics-related annotations.

Learn more about the MicroProfile Metrics specification.

Maven Coordinates

To enable metrics add the following dependency to your project’s pom.xml (see Managing Dependencies).

<dependency>
    <groupId>io.helidon.microprofile.metrics</groupId>
    <artifactId>helidon-microprofile-metrics</artifactId>
</dependency>
Copied

Adding this dependency packages the full-featured metrics implementation with your service.

Other Packaging Options

Helidon gives you flexibility in how you make metrics available to your service. This document explains your options.

Usage

Instrumenting Your Service

You add metrics to your service in these ways:

  • Annotate bean methods—​typically your REST resource endpoint methods (the Java code that receives incoming REST requests); Helidon automatically registers these metrics and updates them when the annotated methods are invoked via CDI.

  • Write code which explicitly invokes the metrics API to register metrics, retrieve previously-registered metrics, and update metric values.

  • Configure some simple REST.request metrics which Helidon automatically registers and updates for all REST resource endpoints.

Later sections of this document describe how to do each of these.

Categorizing the Types of Metrics

Helidon distinguishes among three general types, or scopes, of metrics, as described in the MP metrics specification.

Types (scopes) of metrics
Type/scopeTypical Usage
baseMandated by the MP metrics specification, such as OS 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 you add metrics annotations to your service code, Helidon registers the resulting metrics as type application.

Metric Registries

A metric registry collects registered metrics of a given type. Helidon supports three registries, one for each of the three metrics types.

When you add code to your service to create a metric programmatically, the code first locates the appropriate registry and then registers the metric with that registry.

Retrieving Metrics Reports From Your Service

When you add the metrics dependency to your project, Helidon automatically provides a built-in REST endpoint /metrics which responds with a report of the registered metrics and their values.

Clients can request a particular output format.

Formats for /metrics output
FormatRequested by
OpenMetrics (Prometheus)default (text/plain)
JSONHeader Accept: application/json

Clients can also limit the report by appending the metric type to the path:

  • /metrics/base

  • /metrics/vendor

  • /metrics/application

Further, clients can narrow down to a specific metric name by adding the name as a subpath such as /metrics/application/myCount.

Example Reporting: Prometheus format
curl -s -H 'Accept: text/plain' -X GET http://localhost:8080/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
Example Reporting: JSON format
curl -s -H 'Accept: application/json' -X GET http://localhost:8080/metrics/
Copied
{
   "base" : {
      "memory.maxHeap" : 3817865216,
      "memory.committedHeap" : 335544320,
    }
}

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

API

The MicroProfile Metrics API prescribes all the standard interfaces related to metrics. This section summarizes a few key points about using that API and explains some Helidon-specific interfaces.

Metrics Annotations

You can very easily instrument your service and refer to registered metrics by annotating methods to be measured and injecting metrics which your code needs to observe.

Metric-Defining Annotations

The MicroProfile Metrics specification describes several metric types you can create using annotations, summarized in the following table:

Metrics Annotations
AnnotationUsage
@CountedMonotonically increasing count of events.
@ConcurrentGaugeIncreasing and decreasing measurement of currently-executing blocks of code.
@GaugeAccess to a value managed by other code in the service.
@MeteredCount of invocations and how frequently invocations have occurred.
@SimplyTimedCount of invocations and the total duration consumed by those invocations.
@TimedFrequency of invocations and the distribution of how long the invocations take.

Place annotations on constructors or methods to measure those specific executables. If you annotate the class instead, Helidon applies that annotation to all constructors and methods which the class declares.

Metric-Referencing Annotations

To get a reference to a specific metric, use a metric-referencing annotation in any bean, including your REST resource classes.

You can @Inject a field of the correct type. Helidon uses the MicroProfile Metrics naming conventions to select which specific metric to inject. Use the @Metric annotation to control that selection.

You can also add @Metric on a constructor or method parameter to trigger injection there.

Helidon automatically looks up the metric referenced from any injection site and provides a reference to the metric. Your code then simply invokes methods on the injected metric.

The MetricRegistry API

To register or look up metrics programmatically, your service code uses one of the three MetricRegistry instances (base, vendor, and application) which Helidon furnishes automatically.

To get a MetricRegistry reference

  • @Inject the metric registry you want, perhaps also using the @RegistryType annotation to select the registry type, or

  • Get a Helidon RegistryFactory; either

    • @Inject RegistryFactory or

    • Invoke one of the static getInstance methods on RegistryFactory

    Then invoke getRegistry on the RegistryFactory instance.

The MetricRegistry allows your code to register new metrics, look up previously-registered metrics, and remove metrics.

Working with Metrics in CDI Extensions

You can work with metrics inside your own CDI extensions, but be careful to do so at the correct point in the CDI lifecycle.

Configuration can influence how the metrics system behaves, as the configuration section below explains. Your code should work with metrics only after the Helidon metrics system has initialized itself using configuration. One way to accomplish this is to deal with metrics in a method that observes the Helidon RuntimeStart CDI event, which the extension example below illustrates.

Configuration

To control how the Helidon metrics subsystem behaves, add a metrics section to your META-INF/microprofile-config.properties file.

Type: io.helidon.metrics.serviceapi.MetricsSupport

Configuration Options

Optional configuration options
keytypedefault valuedescription
appName

string

 

Sets the value for the _app tag to be applied to all metrics.

base 

Set the base metrics settings.

cors 

Sets the cross-origin config builder for use in establishing CORS support for the service endpoints.

enabled

boolean

 

Sets whether metrics should be enabled.

key-performance-indicators 

Set the KPI metrics settings.

registries 

Sets the registry settings for the specified registry type.

routing

string

 

Sets the routing name to use for setting up the service’s endpoint.

tags

Map<string, string>

 

Sets the global tags to be applied to all metrics.

web-context

string

 

Sets the web context to use for the service’s endpoint.

Examples

Helidon MP includes a prewritten example application illustrating enabling/disabling metrics using configuration.

The rest of this section contains other examples of working with metrics:

Example Application Code

Adding Method-Level Annotations

The following example adds a new resource class, GreetingCards, to the Helidon MP QuickStart example. It shows how to use the @Counted annotation to track the number of times the /cards endpoint is called.

Create a new class GreetingCards with the following code:
package io.helidon.examples.quickstart.mp;

import java.util.Collections;
import jakarta.enterprise.context.RequestScoped;
import jakarta.json.Json;
import jakarta.json.JsonBuilderFactory;
import jakarta.json.JsonObject;
import jakarta.ws.rs.GET;
import jakarta.ws.rs.Path;
import jakarta.ws.rs.Produces;
import jakarta.ws.rs.core.MediaType;
import org.eclipse.microprofile.metrics.annotation.Counted;

@Path("/cards") 
@RequestScoped 
public class GreetingCards {

  private static final JsonBuilderFactory JSON = Json.createBuilderFactory(Collections.emptyMap());

  @GET
  @Produces(MediaType.APPLICATION_JSON)
  @Counted(name = "any-card")  
  public JsonObject anyCard() throws InterruptedException {
    return createResponse("Here are some random cards ...");
  }

  private JsonObject createResponse(String msg) {
    return JSON.createObjectBuilder().add("message", msg).build();
  }
}
Copied
  • This class is annotated with Path which sets the path for this resource as /cards.
  • The @RequestScoped annotation defines that this bean is request scoped. The request scope is active only for the duration of one web service invocation and it is destroyed at the end of that invocation.
  • The annotation @Counted will register a Counter metric for this method, creating it if needed. The counter is incremented each time the anyCards method is called. The name attribute is optional.
Build and run the application
mvn package
java -jar target/helidon-quickstart-mp.jar
Copied
Access the application endpoints
curl http://localhost:8080/cards
curl http://localhost:8080/cards
curl -H "Accept: application/json"  http://localhost:8080/metrics/application
Copied
JSON response:
{
  "io.helidon.examples.quickstart.mp.GreetingCards.any-card":2 
}
Copied
  • The any-card count is two, since you invoked the endpoint twice.

Notice the counter name is fully qualified with the class and method names. You can remove the prefix by using the absolute=true field in the @Counted annotation. You must use absolute=false (the default) for class-level annotations.

Additional Method-Level Metrics

The @ConcurrentGauge, @Timed`, @Metered, and @SimplyTimed annotations can also be used with a method. For the following example. you can just annotate the same method with @Metered and @Timed. These metrics collect significant information about the measured methods, but at a cost of some overhead and more complicated output. Use @SimplyTimed in cases where capturing the invocation count and the total elapsed time spent in a block of code is sufficient.

Note that when using multiple annotations on a method, you must give the metrics different names as shown below (although they do not have to be absolute).

Update the GreetingCards class with the following code:
package io.helidon.examples.quickstart.mp;

import java.util.Collections;
import jakarta.enterprise.context.RequestScoped;
import jakarta.json.Json;
import jakarta.json.JsonBuilderFactory;
import jakarta.json.JsonObject;
import jakarta.ws.rs.GET;
import jakarta.ws.rs.Path;
import jakarta.ws.rs.Produces;
import jakarta.ws.rs.core.MediaType;
import org.eclipse.microprofile.metrics.MetricUnits;
import org.eclipse.microprofile.metrics.annotation.Counted;
import org.eclipse.microprofile.metrics.annotation.Metered;
import org.eclipse.microprofile.metrics.annotation.Timed;

@Path("/cards")
@RequestScoped
public class GreetingCards {

  private static final JsonBuilderFactory JSON = Json.createBuilderFactory(Collections.emptyMap());

  @GET
  @Produces(MediaType.APPLICATION_JSON)
  @Counted(name = "cardCount", absolute = true) 
  @Metered(name = "cardMeter", absolute = true, unit = MetricUnits.MILLISECONDS) 
  @Timed(name = "cardTimer", absolute = true, unit = MetricUnits.MILLISECONDS) 
  public JsonObject anyCard() {
    return createResponse("Here are some random cards ...");
  }

  private JsonObject createResponse(String msg) {
    return JSON.createObjectBuilder().add("message", msg).build();
  }
}
Copied
  • Specify a custom name for the Counter metric and set absolute=true to remove the path prefix from the name.
  • Add the @Metered annotation to get a Meter metric.
  • Add the @Timed annotation to get a Timer metric.
Build and run the application
mvn package
java -jar target/helidon-quickstart-mp.jar
Copied
Access the application endpoints
curl http://localhost:8080/cards
curl http://localhost:8080/cards
curl -H "Accept: application/json"  http://localhost:8080/metrics/application
Copied
JSON response:
{
  "cardCount": 2,
  "cardMeter": { 
    "count": 2,
    "meanRate": 0.15653506570241812,
    "oneMinRate": 0,
    "fiveMinRate": 0,
    "fifteenMinRate": 0
  },
  "cardTimer": { 
    "count": 2,
    "elapsedTime": 2,
    "meanRate": 0.15651866263362785,
    "oneMinRate": 0,
    "fiveMinRate": 0,
    "fifteenMinRate": 0,
    "min": 0,
    "max": 2,
    "mean": 1.0506565,
    "stddev": 1.0405735,
    "p50": 2.09123,
    "p75": 2.09123,
    "p95": 2.09123,
    "p98": 2.09123,
    "p99": 2.09123,
    "p999": 2.09123
  }
}
Copied
  • The Meter metric includes the count field (it is a superset of Counter).
  • The Timer metric includes the Meter fields (it is a superset of Meter).

Class-Level Metrics

You can collect metrics at the class-level to aggregate data from all methods in that class using the same metric. The following example introduces a metric to count all card queries. In the following example, the method-level metrics are not needed to aggregate the counts, but they are left in the example to demonstrate the combined output of all three metrics.

Update the GreetingCards class with the following code:
package io.helidon.examples.quickstart.mp;

import java.util.Collections;
import jakarta.enterprise.context.RequestScoped;
import jakarta.json.Json;
import jakarta.json.JsonBuilderFactory;
import jakarta.json.JsonObject;
import jakarta.ws.rs.GET;
import jakarta.ws.rs.Path;
import jakarta.ws.rs.Produces;
import jakarta.ws.rs.core.MediaType;
import org.eclipse.microprofile.metrics.annotation.Counted;

@Path("/cards")
@RequestScoped
@Counted(name = "totalCards") 
public class GreetingCards {

  private static final JsonBuilderFactory JSON = Json.createBuilderFactory(Collections.emptyMap());

  @GET
  @Produces(MediaType.APPLICATION_JSON)
  @Counted(absolute = true) 
  public JsonObject anyCard() throws InterruptedException {
    return createResponse("Here are some random cards ...");
  }

  @Path("/birthday")
  @GET
  @Produces(MediaType.APPLICATION_JSON)
  @Counted(absolute = true) 
  public JsonObject birthdayCard() throws InterruptedException {
    return createResponse("Here are some birthday cards ...");
  }

  private JsonObject createResponse(String msg) {
    return JSON.createObjectBuilder().add("message", msg).build();
  }
}
Copied
  • This class is annotated with @Counted, which aggregates count data from all the method that have a Count annotation.
  • Use absolute=true to remove path prefix for method-level annotations.
  • Add a method with a Counter metric to get birthday cards.
Build and run the application
mvn package
java -jar target/helidon-quickstart-mp.jar
Copied
Access the application endpoints
curl http://localhost:8080/cards
curl http://localhost:8080/cards/birthday
curl -H "Accept: application/json"  http://localhost:8080/metrics/application
Copied
JSON response from /metrics/application:
{
  "anyCard": 1,
  "birthdayCard": 1,
  "io.helidon.examples.quickstart.mp.totalCards.GreetingCards": 2  
}
Copied
  • The totalCards count is a total of all the method-level Counter metrics. Class level metric names are always fully qualified.

Field-Level Metrics

Field level metrics can be injected into managed objects, but they need to be updated by the application code. This annotation can be used on fields of type Meter, Timer, Counter, and Histogram.

The following example shows how to use a field-level Counter metric to track cache hits.

Update the GreetingCards class with the following code:
package io.helidon.examples.quickstart.mp;

import java.util.Collections;
import java.util.Random;
import jakarta.enterprise.context.RequestScoped;
import jakarta.inject.Inject;
import jakarta.json.Json;
import jakarta.json.JsonBuilderFactory;
import jakarta.json.JsonObject;
import jakarta.ws.rs.GET;
import jakarta.ws.rs.Path;
import jakarta.ws.rs.Produces;
import jakarta.ws.rs.core.MediaType;
import org.eclipse.microprofile.metrics.Counter;
import org.eclipse.microprofile.metrics.annotation.Counted;
import org.eclipse.microprofile.metrics.annotation.Metric;

@Path("/cards")
@RequestScoped
@Counted(name = "totalCards")
public class GreetingCards {

  private static final JsonBuilderFactory JSON = Json.createBuilderFactory(Collections.emptyMap());

  @Inject
  @Metric(name = "cacheHits", absolute = true) 
  private Counter cacheHits;

  @GET
  @Produces(MediaType.APPLICATION_JSON)
  @Counted(absolute = true)
  public JsonObject anyCard() throws InterruptedException {
    updateStats(); 
    return createResponse("Here are some random cards ...");
  }

  @Path("/birthday")
  @GET
  @Produces(MediaType.APPLICATION_JSON)
  @Counted(absolute = true)
  public JsonObject birthdayCard() throws InterruptedException {
    updateStats();  
    return createResponse("Here are some birthday cards ...");
  }

  private JsonObject createResponse(String msg) {
    return JSON.createObjectBuilder().add("message", msg).build();
  }

  private void updateStats() {
    if (new Random().nextInt(3) == 1) {
      cacheHits.inc(); 
    }
  }
}
Copied
  • A Counter metric field, cacheHits, is automatically injected by Helidon.
  • Call updateStats() to update the cache hits.
  • Call updateStats() to update the cache hits.
  • Randomly increment the cacheHits counter.
Build and run the application, then invoke the following endpoints:
curl http://localhost:8080/cards
curl http://localhost:8080/cards
curl http://localhost:8080/cards/birthday
curl http://localhost:8080/cards/birthday
curl http://localhost:8080/cards/birthday
curl -H "Accept: application/json"  http://localhost:8080/metrics/application
Copied
JSON response from /metrics/application:
{
  "anyCard": 2,
  "birthdayCard": 3,
  "cacheHits": 2, 
  "io.helidon.examples.quickstart.mp.totalCards.GreetingCards": 5
}
Copied
  • The cache was hit two times out of five queries.

Gauge Metric

The metrics you have tested so far are updated in response to an application REST request, i.e GET /cards. These metrics can be declared in a request scoped class and Helidon will store the metric in the MetricRegistry, so the value persists across requests. When GET /metrics/application is invoked, Helidon will return the current value of the metric stored in the MetricRegistry. The Gauge metric is different from all the other metrics. The application must provide a getter to return the gauge value in an application scoped class. When GET /metrics/application is invoked, Helidon will call the Gauge getter, store that value in the MetricsRegistry, and return it as part of the metrics response payload. So, the Gauge metric value is updated real-time, in response to the get metrics request.

The following example demonstrates how to use a Gauge to track application up-time.

Create a new GreetingCardsAppMetrics class with the following code:
package io.helidon.examples.quickstart.mp;

import java.time.Duration;
import java.util.concurrent.atomic.AtomicLong;
import jakarta.enterprise.context.ApplicationScoped;
import jakarta.enterprise.context.Initialized;
import jakarta.enterprise.event.Observes;
import org.eclipse.microprofile.metrics.annotation.Gauge;

@ApplicationScoped 
public class GreetingCardsAppMetrics {

  private AtomicLong startTime = new AtomicLong(0); 

  public void onStartUp(@Observes @Initialized(ApplicationScoped.class) Object init) {
    startTime = new AtomicLong(System.currentTimeMillis()); 
  }

  @Gauge(unit = "TimeSeconds")
  public long appUpTimeSeconds() {
    return Duration.ofMillis(System.currentTimeMillis() - startTime.get()).getSeconds();  
  }
}
Copied
  • This managed object must be application scoped to properly register and use the Gauge metric.
  • Declare an AtomicLong field to hold the start time of the application.
  • Initialize the application start time.
  • Return the application appUpTimeSeconds metric, which will be included in the application metrics.
Update the GreetingCards class with the following code to simplify the metrics output:
package io.helidon.examples.quickstart.mp;

import java.util.Collections;
import jakarta.enterprise.context.RequestScoped;
import jakarta.json.Json;
import jakarta.json.JsonBuilderFactory;
import jakarta.json.JsonObject;
import jakarta.ws.rs.GET;
import jakarta.ws.rs.Path;
import jakarta.ws.rs.Produces;
import jakarta.ws.rs.core.MediaType;
import org.eclipse.microprofile.metrics.annotation.Counted;

@Path("/cards")
@RequestScoped
public class GreetingCards {

  private static final JsonBuilderFactory JSON = Json.createBuilderFactory(Collections.emptyMap());

  @GET
  @Produces(MediaType.APPLICATION_JSON)
  @Counted(name = "cardCount", absolute = true)
  public JsonObject anyCard() throws InterruptedException {
    return createResponse("Here are some random cards ...");
  }

  private JsonObject createResponse(String msg) {
    return JSON.createObjectBuilder().add("message", msg).build();
  }
}
Copied
Build and run the application, then invoke the application metrics endpoint:
curl -H "Accept: application/json"  http://localhost:8080/metrics/application
Copied
JSON response from /metrics/application:
{
  "cardCount": 0,
  "io.helidon.examples.quickstart.mp.GreetingCardsAppMetrics.appUpTimeSeconds": 6 
}
Copied
  • The application has been running for 6 seconds.

Working with Metrics in CDI Extensions

You can work with metrics from your own CDI extension by observing the RuntimeStart event.

CDI Extension that works correctly with metrics
import io.helidon.microprofile.cdi.RuntimeStart;

import jakarta.enterprise.event.Observes;
import jakarta.enterprise.inject.spi.Extension;
import org.eclipse.microprofile.metrics.Counter;
import org.eclipse.microprofile.metrics.MetricRegistry;

public class MyExtension implements Extension {

    void startup(@Observes @RuntimeStart Object event,  
                 MetricRegistry metricRegistry) {       
        metricRegistry.counter("myCounter");         
        }
}
Copied
  • Declares that your observer method responds to the RuntimeStart event. By this time, Helidon has initialized the metrics system.
  • Injects a MetricRegistry (the application registry by default).
  • Uses the injected registry to register a metric (a counter in this case).

Note

Helidon does not prevent you from working with metrics earlier than the RuntimeStart event, but, if you do so, then Helidon might ignore certain configuration settings that would otherwise control how metrics behaves.

Your extension might use earlier lifecycle events (such as ProcessAnnotatedType) to gather and store information about metrics that you want to register. Then your RuntimeStart observer method would use that stored information to register the metrics you need.

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
metrics.enabled=false
Copied

Helidon does not update metrics, and the /metrics endpoints respond with 404 plus a message that the metrics subsystem is disabled.

Disable Selected Metrics

You can be even more selective. Within a registry type you can configure up to two regular expression patterns:

  • one matching metric names to exclude, and

  • one matching metric names to include.

Helidon updates and reports a metric only if two conditions hold:

  • the metric name does not match the exclude regex pattern (if you define one), and

  • either

    • there is no include regex pattern, or

    • the metric name matches the include pattern.

Note

Make sure any include regex pattern you specify matches all the metric names you want to capture.

Suppose your application creates and updates a group of metrics with names such as myapp.xxx.queries, myapp.xxx.creates, myapp.xxx.updates, and myapp.xxx.deletes where xxx can be either supplier or customer.

The following example gathers all metrics except those from your application regarding suppliers although supplier updates are included:

Disabling and enabling metrics by name
metrics.registries.0.type=application
metrics.registries.0.application.filter.exclude=myapp\.supplier\..*
metrics.registries.0.application.filter.include=myapp\.supplier\.updates
Copied

This setting excludes metrics with names starting with myapp.supplier except for the metric myapp.supplier.updates. The exclude and include values are regular expressions.

Collecting Basic and Extended Key Performance Indicator (KPI) Metrics

Any time you include the Helidon metrics module in your application, Helidon tracks two basic performance indicator metrics:

  • a Counter of all requests received (requests.count), and

  • a Meter of all requests received (requests.meter).

Helidon MP also includes additional, extended KPI metrics which are disabled by default:

  • current number of requests in-flight - a ConcurrentGauge (requests.inFlight) of requests currently being processed

  • long-running requests - a Meter (requests.longRunning) measuring the rate at which Helidon processes requests which take at least a given amount of time to complete; configurable, defaults to 10000 milliseconds (10 seconds)

  • load - a Meter (requests.load) measuring the rate at which requests are worked on (as opposed to received)

  • deferred - a Meter (requests.deferred) measuring the rate at which a request’s processing is delayed after Helidon receives the request

You can enable and control these metrics using configuration:

Controlling extended KPI metrics
metrics.key-performance-indicators.extended = true
metrics.key-performance-indicators.long-running.threshold-ms = 2000
Copied

Enable REST.request Metrics

Controlling REST request metrics
metrics.rest-request.enabled=true
Copied

Helidon automatically registers and updates SimpleTimer metrics for every REST endpoint in your service.

Additional Information

Integration with Kubernetes and Prometheus

Kubernetes Integration

The following example shows how to integrate the Helidon MP application with Kubernetes.

Stop the application and build the docker image:
docker build -t helidon-metrics-mp .
Copied
Create the Kubernetes YAML specification, named metrics.yaml, with the following content:
kind: Service
apiVersion: v1
metadata:
  name: helidon-metrics 
  labels:
    app: helidon-metrics
  annotations:
    prometheus.io/scrape: true 
spec:
  type: NodePort
  selector:
    app: helidon-metrics
  ports:
    - port: 8080
      targetPort: 8080
      name: http
---
kind: Deployment
apiVersion: apps/v1
metadata:
  name: helidon-metrics
spec:
  replicas: 1 
  selector:
    matchLabels:
      app: helidon-metrics
  template:
    metadata:
      labels:
        app: helidon-metrics
        version: v1
    spec:
      containers:
        - name: helidon-metrics
          image: helidon-metrics-mp
          imagePullPolicy: IfNotPresent
          ports:
            - containerPort: 8080
Copied
  • A service of type NodePort that serves the default routes on port 8080.
  • An annotation that will allow Prometheus to discover and scrape the application pod.
  • A deployment with one replica of a pod.
Create and deploy the application into Kubernetes:
kubectl apply -f ./metrics.yaml
Copied
Get the service information:
kubectl get service/helidon-metrics
Copied
NAME             TYPE       CLUSTER-IP      EXTERNAL-IP   PORT(S)          AGE
helidon-metrics   NodePort   10.99.159.2   <none>        8080:31143/TCP   8s 
Copied
  • A service of type NodePort that serves the default routes on port 31143.
Verify the metrics endpoint using port 30116, your port will likely be different:
curl http://localhost:31143/metrics
Copied

Leave the application running in Kubernetes since it will be used for Prometheus integration.

Prometheus Integration

The metrics service that you just deployed into Kubernetes is already annotated with prometheus.io/scrape:. This will allow Prometheus to discover the service and scrape the metrics. This example shows how to install Prometheus into Kubernetes, then verify that it discovered the Helidon metrics in your application.

Install Prometheus and wait until the pod is ready:
helm install stable/prometheus --name metrics
export POD_NAME=$(kubectl get pods --namespace default -l "app=prometheus,component=server" -o jsonpath="{.items[0].metadata.name}")
kubectl get pod $POD_NAME
Copied

You will see output similar to the following. Repeat the kubectl get pod command until you see 2/2 and Running. This may take up to one minute.

metrics-prometheus-server-5fc5dc86cb-79lk4   2/2     Running   0          46s
Copied
Create a port-forward so you can access the server URL:
kubectl --namespace default port-forward $POD_NAME 7090:9090
Copied

Now open your browser and navigate to http://localhost:7090/targets. Search for helidon on the page and you will see your Helidon application as one of the Prometheus targets.

Final Cleanup

You can now delete the Kubernetes resources that were just created during this example.

Delete the Prometheus Kubernetes resources:
helm delete --purge metrics
Copied
Delete the application Kubernetes resources:
kubectl delete -f ./metrics.yaml
Copied

References