Monitor Spring Boot Custom Metrics with Micrometer and Prometheus using Docker

Mehmet Ozkaya
6 min readFeb 14, 2023

--

In this article, we will explore the concepts around Spring Actuator, Micrometer and Prometheus to Monitor Spring Boot applications.

Today’s complex, modern applications usually consist of multiple, smaller services that known as microservices. To ensure these microservices run smoothly, it’s important to monitor various aspects such as their overall health, performance metrics, and logging data. Additionally, custom metrics can also be created to better manage the application in a production environment.

I have just published a new course — Design Microservices Architecture with Patterns & Principles.

Background

In this tutorial series, we will learn how horizontally auto-scale spring boot microservice applications with using Prometheus custom metrics and KEDA — Kubernetes Event-driven Auto-scaler. Here you can find the 3 main article that we are going to follow:

  1. Monitor Spring Boot Custom Metrics with Micrometer and Prometheus using Docker (this article)
  2. Monitor Custom Metrics with deploying Kubernetes using Prometheus
  3. Auto-scaling Kubernetes apps with Prometheus and KEDA

Prerequisites

As you can understand from the first image, we have some prerequisite for monitoring Spring Boot application. Those are;

  • Spring Boot — Java applications
  • Actuator, Micrometer libraries
  • Prometheus and Grafana for monitoring tools
  • Docker

Now we will start to learn all these concepts and develop our Spring Boot Application in order to monitor custom metrics with using all these components.

Sprint Boot Actuator

Spring Boot Actuator is a sub-project of the Spring Boot Framework. It includes a number of additional features that help us to monitor and manage the Spring Boot application. It contains the actuator endpoints. There are 3 main features of Spring Boot Actuator: Endpoints, Metrics, Audit

  • Maven artifact: spring-boot-starter-actuator
  • Predefined endpoints:
  • /health summarizes the health status of our application. Can connect K8s pod health's.
  • /metrics details metrics of our application. This might include generic metrics as well as custom ones.
  • /prometheus returns metrics like the previous one, but formatted to work with a Prometheus server.
  • Metrics: Spring Boot Actuator provides dimensional metrics by integrating with the micrometer.
  • We can add custom metrics into actuator and deliver o Prometheus with using micrometer.

Micrometer

Provides a simple façade over the instrumentation clients for the most popular monitoring systems.

  • Vendor-neutral application metrics façade
  • Application metrics recorded by Micrometer are intended to be used to observe, alert, and react to the current/recent operational state of your environment.
  • Metrics Façade for Amazon Cloud Watch, Elastic, Prometheus
  • Custom Meter Registry
  • https://micrometer.io/docs/guide/customMeterRegistry
  • Micrometer for Prometheus Maven artifact: micrometer-registryprometheus

Create Maven-based Spring Boot Application and Set Dependencies

To add the actuator to a Maven-based project, add the following ‘Starter’ dependency to pom.xml.

<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<dependency>
<groupId>io.micrometer</groupId>
<artifactId>micrometer-registry-prometheus</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>

Actuator endpoints let us monitor and interact with our application. Spring Boot includes a number of built-in endpoints. Also we use micrometer-registry-prometheus in order to convert our metrics to micrometers standarts and able to monitor from Promethues.

Configure Application Properties

In order to enable these endpoints, we should configure application.properties file. By default, all endpoints except for shutdown are enabled.

management.endpoint.info.enabled=true
management.endpoints.web.exposure.include=health,metrics,prometheus,loggers

Micrometer Concepts

Let us review key concepts around Micrometer.

  • Meter Meter is the interface for collecting a set of measurements (which we individually call metrics) about the application.
  • MeterRegistry Meters in Micrometer are created from and held in a MeterRegistry. Each supported monitoring system has an implementation of MeterRegistry.
  • SimpleMeterRegistry Micrometer includes a SimpleMeterRegistry that holds the latest value of each meter in memory and does not export the data anywhere. A SimpleMeterRegistry is autowired in a Spring Boot application.
  • @Timed annotation The micrometer-core module contains a @Timed annotation that frameworks can use to add timing support to either specific types of methods such as those serving web request endpoints or, more generally, to all methods. In the example below, we have added @Timed annotation to the API method which exposes timing metrics on the endpoint.

Developing Spring Boot Application

We will define the application and rest controller. We are exposing 2 APIs to order books and movies. Pls note the usage of @Timed annotation which we enabled to query the API timing metrics.

By default Spring Boot applications are Autowired with SimpleMeterRegistry. In this example, we define the configuration to create a CompositeMeterRegistry: PocAppConfig.java

package com.prom.poc;import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import io.micrometer.core.instrument.MeterRegistry;
import io.micrometer.core.instrument.composite.CompositeMeterRegistry;

@Configuration
public class PocAppConfig {
@Bean
public MeterRegistry getMeterRegistry() {
CompositeMeterRegistry meterRegistry = new CompositeMeterRegistry();
return meterRegistry;
}
}

With using MeterRegistry, we will create ItemService that creates custom metrics: ItemService.java

package com.prom.poc;

import io.micrometer.core.instrument.Gauge;
import org.springframework.stereotype.Component;
import io.micrometer.core.instrument.Counter;
import io.micrometer.core.instrument.composite.CompositeMeterRegistry;
import java.util.Random;
import java.util.concurrent.atomic.AtomicInteger;

@Component
public class ItemService {
private static int bookOrderId = 0;
private static int movieOrderId = 0;
private Counter bookCounter = null;
private Counter movieCounter = null;
private AtomicInteger activeUsers = null;

public ItemService(CompositeMeterRegistry meterRegistry) {
bookCounter = meterRegistry.counter("order.books");
movieCounter = meterRegistry.counter("order.movies");
activeUsers = meterRegistry.gauge("number.of.active.users",new AtomicInteger(0));
Random random = new Random();
activeUsers.set(random.nextInt());
}

public Number fetchActiveUsers(){
return 5;
}

public String orderBook() {
bookOrderId += 1;
bookCounter.increment();
return new String("Ordered Book with id = " + bookOrderId);
}

public String orderMovie() {
movieOrderId += 1;
movieCounter.increment();
return new String("Ordered Movie with id = " + movieOrderId);
}
}

Next we will define the application and rest controller. We are exposing 2 APIs to order books and movies.

package com.prom.poc;

import io.micrometer.core.annotation.Timed;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
@Timed
public class HelloController {

@Autowired
ItemService itemService;

@GetMapping("/")
@Timed("api")
public String index() {
return "Greetings from Spring Boot!";
}

@PostMapping("/books")
@Timed("books.api")
public String orderBook() {
return itemService.orderBook();
}

@PostMapping("/movies")
@Timed("movies.api")
public String orderMovie() {
return itemService.orderMovie();
}
}

Please note the usage of @Timed annotation which we enabled to query the API timing metrics.

Create Docker container for the Spring Boot app

Create a docker image for the demo application. Use a simple Dockerfile which package and run the application JAR: Dockerfile

FROM openjdk:17-alpine
ARG JAR_FILE=target/*.jar
COPY ${JAR_FILE} app.jar
ENTRYPOINT ["java","-jar","/app.jar"]

Run the demoapp and Promethues as Docker containers

We will run demoapp and prometheus as docker containers. Create prometheus.yml file to scrape the demoapp metrics.

global:
scrape_interval: 15s

scrape_configs:
- job_name: "demoapp_metrics"
metrics_path: "/actuator/prometheus"
static_configs:
- targets: ["demoapp:8080"]

Create a docker-compose.yml and include the demoapp and prometheus as services. We also mount the prometheus.yml to the prometheus container. docker-compose.yaml :

services:
demoapp:
image: demoapp:1
ports:
- "8080:8080" prometheus:
image: prom/prometheus
volumes:
- ./prometheus.yml:/etc/prometheus/prometheus.yml
ports:
- "9090:9090"

Now if we run command below:

docker-compose up

We can query metrics from the link below:

http://localhost:8080/actuator/prometheus

# HELP order_books_total  
# TYPE order_books_total counter
order_books_total 1.0
# HELP books_api_seconds_max
# TYPE books_api_seconds_max gauge
books_api_seconds_max{exception="None",method="POST",outcome="SUCCESS",status="200",uri="/books",} 0.0
# HELP books_api_seconds
# TYPE books_api_seconds summary
books_api_seconds_count{exception="None",method="POST",outcome="SUCCESS",status="200",uri="/books",} 1.0
books_api_seconds_sum{exception="None",method="POST",outcome="SUCCESS",status="200",uri="/books",} 0.05435327

Send HTTP POST Request to http://localhost:8080/books spring boot controller that will increment to counter.

POST
http://localhost:8080/books

Also we can use the Prometheus Dashboard at “http://localhost:9090/" to query and visualize the metrics.

So far we have finished the first part of tutorial which is “1- Monitor Spring Boot Custom Metrics with Micrometer and Prometheus using Docker”. Now we can go forward to second part.

Source Code

Get the Source Code from Github — Clone or fork this repository, if you like don’t forget the star. If you find or ask anything you can directly open issue on repository.

Afterwards

In this tutorial series, we will learn how horizontally auto-scale spring boot microservice applications with using Prometheus custom metrics and KEDA — Kubernetes Event-driven Auto-scaler. Here you can find the 3 main article that we are going to follow:

  1. Monitor Spring Boot Custom Metrics with Micrometer and Prometheus using Docker (this article)
  2. Monitor Custom Metrics with deploying Kubernetes using Prometheus
  3. Auto-scaling Kubernetes apps with Prometheus and KEDA

Step by Step Design Architectures w/ Course

I have just published a new course — Design Microservices Architecture with Patterns & Principles.

In this course, we’re going to learn how to Design Microservices Architecture with using Design Patterns, Principles and the Best Practices. We will start with designing Monolithic to Event-Driven Microservices step by step and together using the right architecture design patterns and techniques.

References

https://www.stackstalk.com/2022/03/monitor-spring-boot-app.html

https://tanzu.vmware.com/developer/guides/spring-prometheus/

https://tanzu.vmware.com/developer/guides/observability-prometheus-grafana-p1/

https://itnext.io/tutorial-auto-scale-your-kubernetes-apps-with-prometheus-and-keda-c6ea460e4642

https://djamaile.dev/blog/using-keda-and-prometheus/

--

--

Mehmet Ozkaya

Software Architect | Udemy Instructor | AWS Community Builder | Cloud-Native and Serverless Event-driven Microservices https://github.com/mehmetozkaya