Metrics Collection in Spring Boot With Micrometer and Prometheus

...

Table of Content

Introduction

In this post, you’ll learn how to integrate your spring boot application with monitoring platform called Prometheus. We will collect application metrics using Micrometer and use Prometheus to store and visualize this data.

What is Prometheus ?

Prometheus is an open-source monitoring system that was originally built by SoundCloud. It consists of the following core components:

  • A data scraper that pulls metrics data over HTTP periodically at a configured interval.
  • A time-series database to store all the metrics data.
  • A simple user interface where you can visualize, query and monitor all the metrics.

We will be using all of the above Prometheus components in this post.

Pre-requisite

  • Java 1.8 or newer
  • IntelliJ or Eclipse IDE
  • Docker

Bootstrapping application using Spring Initializer

Let’s use Spring Initializr to bootstrap our application. Fill the form as shown below and click on Generate Project to download the project. Here I have included Spring MVC and Spring Actuator as dependencies.

spring-io-bootstrap

Project Structure

After your download is complete, unzip and open the project in your favorite IDE. I am using IntelliJ in this post.

project-structure

Adding Micrometer Prometheus Registry

Spring Boot uses Micrometer, an application metric facade to integrate actuator metrics with external monitoring systems.

architecture

Micrometer provides registry for all the monitoring systems it supports. To integrate actuator with Prometheus, you will need to add the micrometer-registry-prometheus as runtime dependency.

dependencies {
  implementation 'org.springframework.boot:spring-boot-starter-actuator'
  runtime 'io.micrometer:micrometer-registry-prometheus'
  implementation 'org.springframework.boot:spring-boot-starter-web'
}

Once you add the above dependency, Spring Boot will automatically configure PrometheusMeterRegistry and a CollectorRegistry to collect and export metrics data in a format that can be scrapped by a Prometheus server.

Enabling Prometheus Endpoints

Prometheus is a polling monitoring system. It requires an endpoint from which it can scrape the metrics data at a configured interval. By default in Spring Boot Actuator only info and health endpoints are enabled. To enable prometheus endpoint add the following to your application.properties file.

management.endpoints.web.exposure.include=health,info,prometheus

You can verify the list of enabled metrics by visiting http://localhost:8080/actuator endpoint. After including the above, application metrics will be made available at http://localhost:8080/actuator/prometheus. In the next section you will learn how to configure Prometheus to scrape data from this endpoint.

prometheus-metrics

Setup Prometheus using Docker

Downloading Prometheus

You can download the Prometheus docker image using docker pull command like shown below.

$ docker pull prom/prometheus

Once the image is downloaded, you can verify using docker image ls | grep prometheus

$ docker image ls | grep prometheus
prom/prometheus         latest              690f4cf8dee2        2 weeks ago         101MB

Prometheus Configuration

Now, we need to configure Prometheus to scrape metrics from our application’s prometheus endpoint.

For this create a new file called prometheus.yml with the following configurations:

# my global config
global:
  scrape_interval:     15s # Set the scrape interval to every 15 seconds. Default is every 1 minute.
  evaluation_interval: 15s # Evaluate rules every 15 seconds. The default is every 1 minute.
  # scrape_timeout is set to the global default (10s).

# Alertmanager configuration
alerting:
  alertmanagers:
  - static_configs:
    - targets:
      # - alertmanager:9093

# Load rules once and periodically evaluate them according to the global 'evaluation_interval'.
rule_files:
  # - "first_rules.yml"
  # - "second_rules.yml"

# 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'

    # metrics_path defaults to '/metrics'
    # scheme defaults to 'http'.

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

  - job_name: 'spring-actuator'
    metrics_path: '/actuator/prometheus'
    scrape_interval: 5s
    static_configs:
    - targets: ['HOST_IP:8080']

The most important thing to note in the above configuration is the spring-actuator job inside scrape_configs section.

This is where we define our application’s prometheus endpoint. The targets section contains the HOST and PORT of your Spring Boot application. Make sure to replace HOST_IP with your IP address of the machine. Note that, localhost won’t work here because your spring application is running outside the docker container. To obtain IP address on mac you can use the below command.

$ ifconfig| grep inet

Running Prometheus using Docker

Finally, let us start a Prometheus server using Docker. You can use the following command to start a Prometheus server in the background with our custom configuration.

$ docker run -d --name=prometheus -p 9090:32775 -v <PATH_TO_prometheus.yml_FILE>:/etc/prometheus/prometheus.yml prom/prometheus --config.file=/etc/prometheus/prometheus.yml

After running the above command, docker will start the Prometheus server inside a container.

$ docker container ls
CONTAINER ID        IMAGE                    COMMAND                  CREATED             STATUS              PORTS                     NAMES
040d1b7ace05        prom/prometheus:latest   "/bin/prometheus --c…"   8 hours ago         Up 5 hours

Metrics Visualization in Prometheus Dashboard

That’s it! You can now navigate to http://localhost:32775 to explore the Prometheus dashboard.

Following is a Prometheus graph for CPU Usage metrics collected by Micrometer in our application.

prometheus-metrics-system-usage

You can check out the official Prometheus documentation to learn more about Prometheus Query Expression.

Micrometer Registry Customization

In this section we will go a step forward and customize the default Micrometer’s MeterRegistry to suit our needs.

Let say you have N number of instances in your production distributed across different regional cluster. Now, requirement is to track application metrics by instance and region. The default setup doesn’t allow to differentiate between instances or hosts. However, Micrometer provides MeterRegistryCustomizer which can be used to customize the MeterRegistry and inject some common tags to help fulfill this requirement.

@Configuration
public class MetricsConfig {

  @Value("${host}")
  private String host;

  @Value("${service}")
  private String service;

  @Value("${region}")
  private String region;

  @Bean
  MeterRegistryCustomizer<MeterRegistry> meterRegistryCustomizer() {
    return registry -> registry.config()
        .commonTags("host", host,
            "service", service,
            "region", region)
        .meterFilter(MeterFilter.deny(id -> {
          String uri = id.getTag("uri");
          return uri != null && uri.startsWith("/actuator");
        }))
        .meterFilter(MeterFilter.deny(id -> {
          String uri = id.getTag("uri");
          return uri != null && uri.contains("favicon");
        }));
  }
}

The value of host, service and region can be overridden in the application.properties during deployment using a start script. So that all nodes can have unique values for these fields.

Now, we can run below query in Prometheus dashboard to look at the CPU usage of all nodes in a particular region.

system_cpu_usage{region="us-west-1"}

prometheus-metrics-system-usage-by-region

Custom Metrics

By default, Micrometer in Spring Boot provides below metrics via Actuator.

  • JVM metrics: reports utilization of memory, garbage collection and thread
  • CPU metrics
  • File descriptor metrics
  • Logback metrics: records the number of events logged to Logback at each log level
  • Uptime metrics
  • Tomcat metrics
  • Spring integration metrics
  • Spring MVC metrics
  • Spring Webflux metrics
  • RestTemplate metrics

However, in most of the production grade application, there is always a need to add custom metrics. Below code registers a Counter, that maintains the count of differnt type of messages received by the application.

@RestController
public class MyController {

  @Autowired
  MeterRegistry registry;

  @RequestMapping(value = "/message/{message}", method = RequestMethod.GET)
  public String getMessage(@PathVariable("message") String message) {

    // counter to count different types of messages received
    registry.counter("custom.metrics.message", "value", message).increment();

    return message;
  }

Source Code on Github

comments powered by Disqus