Monitoring Made Easy: How to Use Micrometer API

Micrometer is a metrics instrumentation library for Java applications. It provides a simple facade over the instrumentation clients for a number of popular monitoring systems. In this tutorial, we will learn how to use Micrometer API in a Jakarta EE application with WildFly or a Quarkus application.

Firstly, we will cover the Micrometer configuration using two popular options: WildFly application Server and Quarkus. Then, we will look at the Micrometer API which can be used in both Runtimes.

MicroMeter set up on WildFly

To use MicroMeter on WildFly, firstly download WildFly 28 (or newer). Next, you need to add the available extension to your WildFly Configuration and define an endpoint for your Metrics:

/extension=org.wildfly.extension.micrometer:add
/subsystem=micrometer:add(endpoint="http://localhost:4318/v1/metrics")
reload

Finally, you need to include the following dependency in your Jakarta EE project:

<dependency>
    <groupId>io.micrometer</groupId>
    <artifactId>micrometer-registry-prometheus</artifactId>
    <version>1.7.6</version>
</dependency>

MicroMeter set up on Quarkus

To run MicroMeter applications on Quarkus, simply drop the quarkus-micrometer-registry-prometheus dependency in your Quarkus project:

<dependency>
    <groupId>io.quarkus</groupId>
    <artifactId>quarkus-micrometer-registry-prometheus</artifactId>
</dependency>

Micrometer vs MicroProfile Metrics

Micrometer and MicroProfile Metrics API are both metrics instrumentation libraries for Java applications. However, there are some differences between them.

One key difference is that Micrometer provides a simple facade over the instrumentation clients for multiple monitoring systems, while MicroProfile Metrics API is designed specifically for use with MicroProfile-compatible application servers.

Another difference is that Micrometer provides more advanced features such as hierarchical meters and dimensional data models, while MicroProfile Metrics API provides a simpler API with basic metric types such as counters and gauges.

In summary, if you are using a MicroProfile-compatible application server and only need basic metrics instrumentation, you may want to use MicroProfile Metrics API. However, if you need more advanced features or want to use multiple monitoring systems, Micrometer may be a better choice.

To learn more about the Metrics API, check this article: Microprofile Metrics using WildFly application Server

How to use Micrometer API

Once you that your set up is complete, you can start using it in your application. You can use the MeterRegistry bean to create and register meters such as counters, gauges, and timers.

@Inject
private MeterRegistry registry;

The following meters are available:

  • Gauge: A Gauge measures resource usage, capacity, etc. This is the simplest kind of meter you can use in your application
  • Counter: A Counter measures a number of events or actions – a value that only increases, and never decreases.
  • Timer: A Timer measures short-duration events and their frequency such as method execution time, request duration, etc.
  • DistributionSummary: This metric captures a set of values and its output is a distribution of count,sum and max for the recorded values during the push interval.

Using a Gauge

Here is an example REST Method which uses a Gauge Meter to store an attribute, which you can later set or get:

@GET
@Path("gauge")

public int gauge() {

    AtomicInteger myGauge = registry.gauge("numberGauge", new AtomicInteger(0));

    // ... then you can update the value using the object reference
    myGauge.set(10);
    return myGauge.get();

}

Using a Counter

The following sample code creates a Counter with the name “dummy” which is incremented at every iteration of the method:

@GET
@Path("counter")
public String counter() {

    registry.counter("dummy").increment();
    return "Counter is " + registry.counter("dummy").count();
}  

Using a Timer

You can use a Timer to measure the execution time for a method. The method record of the Timer object allows to start recording the time. then, the totaltime returns the time spent in the execution:

@GET
@Path("timer")
@Timed(value = "mytimer")
public double timer() {

    Timer timer = registry.timer("demo_timer", Tags.of("ts", "" + System.currentTimeMillis()));
    //&nbsp;recording&nbsp;execution&nbsp;time&nbsp;of&nbsp;code
    timer.record(() -> {
        try {
            Thread.sleep((long) (Math.random() * 1000L));
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }

    });
    return timer.totalTime(TimeUnit.MILLISECONDS); 

}

Please notice in this example we show how to use Tags with a Meter object. In Micrometer, Tags are key-value pairs that you can associate with metrics to provide additional dimensions for aggregation and filtering. Tags allow you to add more context to your metrics and make it easier to query and analyze your data.

For example, let’s say you have a web application and you want to track the number of HTTP requests it receives. You could create a counter and increment it every time a request is received. However, this would only give you the total number of requests, without any additional information.

By using tags, you can add more context to your metrics. For example, you could add tags for the HTTP method, the URI, and the response status code. This would allow you to see not only the total number of requests but also how many requests were GET requests, how many were for a specific URI, and how many resulted in a specific status code.

Using a Distribution Summary

Finally, we show how to use a DistributionSummary. A DistributionSummary in Micrometer is a metric type that can be used to track the distribution of a large number of measurements. It is similar to a histogram, but it allows you to track the distribution of non-integer values such as response sizes or latencies.

@GET
@Path("summary")

public void summary() {

    DistributionSummary summary = DistributionSummary
    .builder("my_summary")
    .description("The summary description") // optional
    .register(registry);
    // Record values to distributionSummary
    summary.record(10);
    summary.record(20);
    summary.record(30);

}

Capturing MicroMeter Metrics

At the beginning of this article, we have configured WildFly to send our Metrics to a Collector available at this address:

/subsystem=micrometer:add(endpoint="http://localhost:4318/v1/metrics")

We will show here how to bootstrap the OpenTelemetry (OTel) connector which will collect the Metrics from WildFly application. To do that, you need to configure Docker on your machine. Then, add the following docker-compose.yml file:

version: "3"
volumes:
  shared-volume:
    # - logs:/var/log
services:
  otel-collector:
    image: otel/opentelemetry-collector
    command: [--config=/etc/otel-collector-config.yaml]
    volumes:
      - ./otel-collector-config.yaml:/etc/otel-collector-config.yaml
    ports:
      - 1888:1888 # pprof extension
      - 8888:8888 # Prometheus metrics exposed by the collector
      - 8889:8889 # Prometheus exporter metrics
      - 13133:13133 # health_check extension
      - 4317:4317 # OTLP gRPC receiver
      - 4318:4318 # OTLP http receiver
      - 55679:55679 # zpages extension
      - 1234:1234 # /metrics endpoint

This YAML file bootstraps the otel/opentelemetry-collector, opening a set of ports on your machine to expose the metrics. Finally, the –config command allows to define the OpenTelemetry (OTel) connector in the following external file otel-collector-config.yaml:

extensions:
  health_check:
  pprof:
    endpoint: 0.0.0.0:1777
  zpages:
    endpoint: 0.0.0.0:55679

receivers:
  otlp:
    protocols:
      grpc:
      http:

processors:
  batch:

exporters:
  prometheus:
    endpoint: "0.0.0.0:1234"

service:
  pipelines:
    metrics:
      receivers: [otlp]
      processors: [batch]
      exporters: [prometheus]

  extensions: [health_check, pprof, zpages]

You can start the Collector with:

docker-compose up

Verify that everything is ready to go:

micrometer applications quickstart

Then, you can start requesting the REST Endpoint on WildFly or Quarkus. For example:

curl http://localhost:8080/micrometer/demo/counter

Finally, check on the available metrics at the following endpoint:

http://localhost:1234/metrics

For example:

curl -s http://localhost:1234/metrics | grep dummy
# HELP dummy 
# TYPE dummy counter
dummy{job="wildfly"} 8

Conclusion

Micrometer is a powerful tool for collecting and monitoring performance data in Java applications, including those built with Jakarta EE. By configuring a registry and creating metrics, you can easily track important statistics like response times and error rates. While the MicroProfile Metrics API provides similar functionality, Micrometer offers a more modern and flexible solution with support for a wider range of monitoring systems.

Thanks to Jason Lee and Brian StansBerry for providing useful hints on MicroMeter configuration.

Source code: https://github.com/fmarchioni/mastertheboss/tree/master/micro-services/mp-micrometer