Monitoring with Prometheus, Simple Counter Example with Quarkus

Prometheus ↱ is a monitoring and alerting toolkit. The post Monitoring with Prometheus gives a brief introduction to the Prometheus ecosystem.

The Quarkus Micrometer Metrics Extension ↱ is a Quarkus extension (Quarkus Extension Concepts ↱). which implements a core library providing a registration mechanism for core metric types (counters, gauges, timers, distributions and summaries) for different backend monitoring systems.

Docker Setup

For this example the Prometheus application runs in a Docker container. The example service is also running in a Docker container. Both containers are connected through a virtual network. The prometheus.yml configuration file is mounted into the Prometheus container.

Diagram Docker Virtual Network

Create the Docker Network

Create a Docker network for the two containers.

$ docker network create 00001-monitoring

An Example Service

Create a new Quarkus application with the maven build tool and the quarkus-maven-plugin. Add the Quarkus extensions ‘resteasy-reactive’ and ‘micrometer-registry-prometheus’.

mvn io.quarkus.platform:quarkus-maven-plugin:2.16.4.Final:create \
  -DprojectGroupId=io.aperios \
  -DprojectArtifactId=monitoring-simple-counter \
  -Dextensions='resteasy-reactive,micrometer-registry-prometheus' \
  -DnoCode

The class SimpleCounter.java

Count the requests to the endpoint hello/ and the endpoint hello/{name} with its parameter.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
package io.aperios;

import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
import javax.ws.rs.Produces;

import io.micrometer.core.instrument.MeterRegistry;
import io.micrometer.core.instrument.Tags;

@Path("/")
@Produces("text/plain")
public class SimpleCounter {

    private static final String COUNTER_NAME = "basic.monitoring.http.requests_total";

    private final MeterRegistry registry;

    SimpleCounter(MeterRegistry registry) {
        this.registry = registry;
    }

    @GET
    @Path("hello/")
    public String helloAll() {
        registry.counter(COUNTER_NAME, "method", "GET", "status", "200", "path", "hello/").increment();
        return "Hello world!";
    }

    @GET
    @Path("hello/{name}")
    public String helloName(@PathParam(value = "name") String name) {
        registry.counter(COUNTER_NAME, "method", "GET", "status", "200", "path", "hello/{name}").increment();
        return String.format("Hello, %s!", name);
    }
}

Lines 8 - 9
Import the MeterRegistry and Tags from the io.micrometer quarkus extension. The MeterRegistry includes by default some JVM metrics and some HttpServer engine metrics.

Line 15
Define a name for the metric the service collects.

Line 17
A reference to the MeterRegistry which stores and manages the metrics.

Lines 19 - 21
Add a constructor which initializes the reference to the MeterRegistry.

Line 26
Increment the counter on a GET request, record the status result and the request path.

Line 33
Increment the counter on a GET request, record the status result and the request path with its parameter.

application.properties

In the src/main/ressources/prometheus.yml the path to the metrics url is rewritten to the default Prometheus path.

1
quarkus.micrometer.export.prometheus.path=/metrics

Build the application and the Docker container

$ ./mvnw clean package
$ ./mvnw quarkus:build
$ docker build -f src/main/docker/Dockerfile.jvm -t quarkus/monitoring-simple-counter-jvm .

Run the container

Start a new container and attach it to the 00001-monitoring network.

$ docker run -d -p 8080:8080 --network 00001-monitoring --name monitoring-simple-counter quarkus/monitoring-simple-counter-jvm

Check the logs if the startup was successful.

$ docker logs -f monitoring-simple-counter

Make some test requests to the running container

$ curl http://localhost:8080/hello/
$ curl http://localhost:8080/hello/Nagos

Check if the sample service returns the metrics data http://localhost:8080/metrics/

Prometheus Configuration

Add a new Job to the ‘prometheus.yml’

The sample prometheus.yml monitors the Prometheus application in its own Docker container and the sample service in the docker container monitoring-simple-counter.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
global:
  scrape_interval:     15s # By default, scrape targets every 15 seconds.

  # Attach these labels to any time series or alerts when communicating with
  # external systems (federation, remote storage, Alertmanager).
  external_labels:
    monitor: 'codelab-monitor'

# A scrape configuration containing exactly one endpoint to scrape:
# Here it's Prometheus itself.
scrape_configs:
  # The job name is added as a label `job=<job_name>` to any timeseries scraped from this config.
  - job_name: 'prometheus'

    # Override the global default and scrape targets from this job every 5 seconds.
    scrape_interval: 5s

    static_configs:
      - targets: ['localhost:9090']

  # The job name is added as a label `job=<job_name>` to any timeseries scraped from this config.
  - job_name: 'basic_monitoring'

    # Override the global default and scrape targets from this job every 5 seconds.
    scrape_interval: 5s

    static_configs:
      - targets: [ 'monitoring-simple-counter:8080' ]

Line 1 - 7
The global part defines defaults and other setting. In this example we define a default scrape_interval of 15 seconds for all jobs.

Line 11
In the scrape_configs jobs can be defined.

Line 13 - 19
The first job monitors Prometheus itself every 5 Seconds.

Line 22 - 29
The lines 22 to 29 define the job to pull the metrics from the sample webservice. Here we name the job basic_monitoring, line 22. The Prometheus server should scrape / pull (scrape_interval, line 25) every 5 seconds for monitoring metrics data. The node static_configs contains a array targets with hostnames/ ip addresses and port of the example service. Prometheus pulls then from ‘http://<hostnames:port>/metrics’ the metrics data. The service to monitor runs in a Docker container named ‘monitoring-simple-counter’.

Microservices in a productive environment

This is an example only. In many productive environments the monitoring information are served to another TCP/IP port for security reasons.

Start a Prometheus Container

Start a Prometheus container and mount the prometheus.yml configuration file into the container. Set the container name to ‘prometheus’ and add the container to the already created Docker network ‘00001-monitoring’.

$ cd examples/Quarkus/release/00001-monitoring/monitoring-simple-counter
$ docker run -d -p 9090:9090 \
    -v $PWD/prometheus.yml:/etc/prometheus/prometheus.yml \
    --name prometheus \
    --network 00001-monitoring \
    prom/prometheus

Check the logs if the startup was successful.

$ docker logs -f prometheus

Access the Prometheus UI.

The Prometheus container should now up and running. To access the Prometheus UI open the URL in a browser: http://localhost:9090.

Check the collected metrics in the Prometheus UI

Open http://localhost:9090 and in the metrics explorer tab (main “Prometheus’ tab), search for basic_monitoring_http_requests_total. The ‘Table’ tab displays the collected metrics. The ‘Graph’ tab show a simple line graph for the collected metrics.

Stop and Remove all Container and remove the Network Configuration

Stop the sample service.

$ docker stop monitoring-simple-counter
$ docker rm monitoring-simple-counter

Stop the Prometheus container.

$ docker stop prometheus
$ docker rm prometheus

Remove the Docker network.

$ docker network rm 00001-monitoring

Requires

Tool Version See
java, OpenJDK > 11.0 Tools
mvn >= 3.5.4, < 3.9.0 Tools
docker >= 19.03.0 Tools
Quarkus >= 2.16.4.Final Quarkus Quick Links
Prometheus, (docker image) >= 2.15.1, (prom/prometheus:v2.15.0) Prometheus, Docker Image ↱

See also