Contents

Overview

This document explains Helidon MP metrics-capable components and applications and describes how to create and control them.

Think of Helidon metrics in several related but different parts:

APIs

  • The Helidon metrics API

    This API allows your code to register, look-up, remove, and update metrics using the RegistryFactory, MetricRegistry, and individual metrics interfaces.

  • The Helidon metrics REST service API

    This API allows your code to set up and respond to the /metrics endpoint so clients can retreive metrics information.

Implementations of the APIs

  • Implementations of the Helidon metrics API.

    Helidon provides two—​minimal and full-featured—​and selects which one to use at runtime, based on what components are present on the runtime path and whether metrics is configured to be enabled or disabled.

    By default, Helidon MP services use the full-featured implementation.

  • Implementations of the Helidon metrics REST service API.

    Helidon provides two—​minimal and full-featured—​and selects which one to use at runtime.

    By default, Helidon MP apps which use metrics use the full-featured metrics REST service by default.

As you plan and write Helidon components and applications, you make some choices about exactly how your code will use metrics. This document gives some background information, describes each option and its effect, and provides some code examples.

Usage

This section helps you decide how incorporate metrics into your software by describing the categories of metrics usage, explaining generally how Helidon implements metrics, and illustrating how to write the metrics-related code accordingly.

Categorizing Metrics Usage

We can place each Helidon component and Helidon application into one of three categories based on how it relies on metrics. The type of module dictates the compile-time dependency you declare in the project pom.xml.

Types of Metrics Usage
Registers, updates, removes metrics?Refers to metrics values?Category
metrics-independent
metrics-capable
metrics-dependent

Whenever possible, if your component or application uses metrics, then write it as metrics-capable code.

Understanding the Two Metrics Implementations

Helidon provides two metrics implementations:

  • Full-featured metrics allows registering, removing, and updating metrics and observing metrics' changing values. The helidon-metrics component contains full-featured metrics.

  • Minimal metrics supports registering, removing, and updating metrics. The metrics objects provided by the minimal implementation are no-ops: their values never change. The minimal implementation is part of the helidon-metrics-api component.

Any code compiled with helidon-metrics-api can assume that the runtime path will include the minimal implementation.

Both implementations support all the operations of the RegistryFactory and the MetricRegistry. The full implementation provides fully-functional metrics instances (counters, timers, etc.). In the minimal implementations, metrics do not update their values.

For Helidon to use the full implementation, two conditions must hold:

  • The helidon-metrics component must be on the runtime path.

  • Metrics must be enabled, using either a builder or configuration. (Enabled is the default.)

Otherwise, provided that the runtime path includes helidon-metrics-api, Helidon activates the minimal implementation.

Understanding the Two Metrics Service Implementations

Helidon includes two implementations of support for the metrics web service endpoint /metrics (or whatever context value is configured).

The full-service implementation sends responses which describe the metadata and current values for the metrics registered in metric registries. The helidon-metrics component contains this implementation.

The helidon-metrics-service-api component contains the API for the metrics web service support (the MetricsSupport interface) and also a minimal implementation. This implementation simply responds with 404 and an explanatory message that metrics are disabled.

Any code compiled with helidon-metrics-service-api can assume that the runtime path will contain the minimal implementation.

Helidon activates the full implementation if the runtime path includes the full implementation and metrics is configured as enabled; Helidon uses the minimal implementation otherwise.

Enabling and Disabling Metrics

Using configuration, your component can let end users control at runtime whether Helidon should use full-featured metrics. If an end user sets metrics.enabled to false, then Helidon activates the minimal metrics and metrics service implementations provided they are in the runtime path.

Further, users can set component-name.metrics.enabled to false which disables metrics for just that component so long as the component was written to check that setting and act on it accordingly.

Designing and Writing Metrics-Capable Applications and Components

Whoever packages and deploys your application or component can control what code will be on the runtime path and whether metrics is enabled or not. As a result, wherever possible, construct your modules which use metrics so that they do not make decisions based on the values of metrics; that is, design them to be metrics-capable, not metrics-dependent. Doing so allows your code to operate regardless of whether the full-featured metrics implementation is active at runtime.

Declaring Dependencies

  1. Include this dependency:
    Dependency for Helidon metrics API
    <dependency>
        <groupId>io.helidon.metrics</groupId>
        <artifactId>helidon-metrics-api</artifactId>
    </dependency>
    Copied

    This module defines the metrics API: RegistryFactory, MetricRegistry, and the various metrics themselves.

  2. Declare an explicit runtime dependency on the full-featured metrics implementation:
    Dependency for full metrics and metrics service implementations
    <dependency>
        <groupId>io.helidon.metrics</groupId>
        <artifactId>helidon-metrics</artifactId>
        <scope>runtime</scope>
    </dependency>
    Copied

Writing Metrics-Capable Code

The way you write a metrics-capable module depends on whether it is a component (that is, not an application) or an application.

Writing a Helidon MP Application

When your MP application code uses @Inject for either a RegistryFactory or a MetricRegistry, Helidon injects either the full-featured instance or the minimal instance according to whether the runtime path includes the full implementation and, if so, whether metrics is enabled.

By choosing and injecting the appropriate implementation, Helidon allows you to write your code without concern for which implementation is available at runtime.

Packaging a Metrics-Capable Helidon MP Application

The Helidon MP metrics implementation depends on the metrics and metrics service APIs as well as helidon-metrics which contains the full implementation of each. Therefore, by default, Helidon MP applications have full-featured metrics and endpoint support.

Application code can @Inject the RegistryFactory and MetricRegistry instances. Helidon MP itself uses metrics settings in the configuration to make the correct RegistryFactory and MetricRegistry instances available at injection sites.

Helidon’s MicroProfile metrics component helidon-microprofile-metrics has its own runtime dependency on the minimal implementation, so that implementation, at least, is available at runtime.

By default, Helidon MP applications use the full implementation, because Helidon’s MP metrics depends also on the full metrics implementation. That said, a developer of a Helidon MP app can explicitly exclude the dependency on the full implementation:

Explicit exclusion of helidon-metrics
<dependency>
    <groupId>io.helidon.microprofile.bundles</groupId>
    <artifactId>helidon-microprofile</artifactId>
    <exclusions>
        <exclusion>
            <groupId>io.helidon.metrics</groupId>
            <artifactId>helidon-metrics</artifactId>
        </exclusion>
    </exclusions>
</dependency>
Copied

In the resulting Helidon MP application, Helidon will use the minimal metrics and metrics support implementations.

Writing a Non-Application Component

Write your non-application component to accept component-specific configuration that includes an optional metrics section which can include an optional enabled setting. Helidon defaults the value to true. The following example shows one way to accomplish this:

Example code to support disabling metrics usage in a component
import io.helidon.config.Config;
import io.helidon.metrics.api.ComponentMetricsSettings;
import io.helidon.metrics.api.MetricsSettings;
import io.helidon.metrics.api.RegistryFactory;

import org.eclipse.microprofile.metrics.MetricRegistry;

public class UtilComponent {

    private final MetricRegistry metricRegistry; 

    public static class Builder implements io.helidon.common.Builder<UtilComponent> { 
        private ComponentMetricsSettings.Builder componentMetricsSettingsBuilder = ComponentMetricsSettings.builder();

        public Builder componentMetricsSettings(ComponentMetricsSettings.Builder componentMetricsSettingsBuilder) { 
            this.componentMetricsSettingsBuilder = componentMetricsSettingsBuilder;
            return this;
        }

        public Builder config(Config componentConfig) { 
            componentConfig
                .get(ComponentMetricsSettings.Builder.METRICS_CONFIG_KEY)
                .as(ComponentMetricsSettings::create)
                .ifPresent(this::componentMetricsSettings);
            return this;
        }

        public UtilComponent build() {
            return new UtilComponent(this);
        }
    }

    private UtilComponent(Builder builder) {
        metricRegistry = RegistryFactory
                .getInstance(builder.componentMetricsSettingsBuilder.build())
                .getRegistry(MetricRegistry.Type.VENDOR); 
    }

    MetricRegistry metricRegistry() { 
        return metricRegistry;
    }
}
Copied
  • Other code in the component uses this metric registry for registering, looking up, and removing metrics.
  • Applications which use instances of MyComponent use this Builder to set up and create those instances.
  • Applications which layer on your component invoke this method to set up the component-level metrics behavior they want your component to use.
  • If an application supports configuration, it passes the util config to this method.
  • The constructor for your component obtains the MetricRegistry which the rest of your component will use.
  • Provides easy access to the MetricRegistry which the component’s metrics code should use.

Helidon returns either a full-featured RegistryFactory or a minimal one, depending on:

  • whether the full-featured metrics implementation is on the runtime path,

  • whether metrics overall is enabled or disabled, and

  • whether the component metrics settings requests enabled or disabled metrics.

Examples

The following example shows how useful metrics-capable code can be in the context of building Docker images.

You (or others) could assemble a Docker image with your metrics-capable app as its top layer or your metrics-capable component in a middle layer, built on a lower layer containing several Helidon modules including the full metrics implementation. When that Docker image runs, your app will run with full-featured metrics support.

Separately, someone could build a similar Docker image which does not include the Helidon metrics implementation. In this Docker image, your app or component will run successfully but will not incur the overhead of actually updating the metrics it uses.

Users can create different Docker images, some with full metrics support and some without, which all use a single version of your metrics-capable app or component which runs properly in either environment without change.

Additional Information

Advantages of Writing Metrics-Capable Modules

By writing a metrics-capable app or component, you give packagers and deployers of your code the flexibility to include or exclude the full metrics implementation at runtime as they see fit.

Because your one module works correctly in either environment:

  • The consumers of your app benefit by not needing to understand and choose between two different implementations of your module, or having to add both your main module and an optional add-on which adds metrics support to your module.

  • You benefit by writing and maintaining a single module, not two: one that is metrics-independent and one that is metrics-dependent.