How to Monitor Spring Boot Apps with Prometheus and Grafana: Step‑by‑Step Guide

This tutorial walks through building a Spring Boot application, integrating Micrometer for metric collection, deploying Prometheus and Grafana via Docker, configuring dynamic service discovery, and creating custom request‑count metrics with AOP, providing a complete end‑to‑end monitoring solution.

Java High-Performance Architecture
Java High-Performance Architecture
Java High-Performance Architecture
How to Monitor Spring Boot Apps with Prometheus and Grafana: Step‑by‑Step Guide

Overall Architecture

Spring Boot provides actuator endpoints via actuator.

Prometheus is a monitoring system that scrapes metrics from Spring Boot and stores them as time‑series data.

Grafana is a UI dashboard that can query Prometheus and display the metrics.

Spring Boot 2 includes micrometer, which simplifies integration with monitoring systems such as Prometheus.

Architecture diagram
Architecture diagram

The overall flow is:

Spring Boot (with micrometer) generates metrics.

Prometheus scrapes the metrics, stores them, and provides a query API.

Grafana connects to Prometheus as a data source and visualizes the metrics.

Practical Steps

Create a Spring Boot application that produces metrics.

Add micrometer dependency to integrate with Prometheus.

Deploy Prometheus.

Configure Prometheus to scrape the Spring Boot application.

Deploy Grafana.

Add Prometheus as a data source in Grafana.

Import a JVM dashboard (ID 4701) to show JVM metrics.

Define custom metrics (e.g., request count) using Micrometer and AOP.

Use dynamic service discovery to avoid restarting Prometheus when targets change.

1. Create Application and Add Micrometer

Create a minimal Spring Boot project and add micrometer dependencies.

pom.xml:

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.2.4.RELEASE</version>
        <relativePath/>
    </parent>
    <groupId>com.example</groupId>
    <artifactId>springboot2demo</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <properties>
        <java.version>1.8</java.version>
    </properties>
    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-actuator</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>io.micrometer</groupId>
            <artifactId>micrometer-registry-prometheus</artifactId>
            <version>1.1.4</version>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
            <exclusions>
                <exclusion>
                    <groupId>org.junit.vintage</groupId>
                    <artifactId>junit-vintage-engine</artifactId>
                </exclusion>
            </exclusions>
        </dependency>
    </dependencies>
    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>
</project>

application.properties:

spring.application.name=springboot2demo
management.endpoints.web.exposure.include=*
management.metrics.tags.application=${spring.application.name}

Add a Bean to register JVM metrics:

package com.example.springboot2demo;

import io.micrometer.core.instrument.MeterRegistry;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.actuate.autoconfigure.metrics.MeterRegistryCustomizer;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Bean;

@SpringBootApplication
public class Springboot2demoApplication {
    public static void main(String[] args) {
        SpringApplication.run(Springboot2demoApplication.class, args);
    }

    @Bean
    MeterRegistryCustomizer<MeterRegistry> configurer(
            @Value("${spring.application.name}") String applicationName) {
        return registry -> registry.config().commonTags("application", applicationName);
    }
}

Start the service and view the actuator endpoints.

Actuator endpoint screenshot
Actuator endpoint screenshot

2. Deploy Prometheus

Run Prometheus via Docker:

$ docker run --name prometheus -d -p 127.0.0.1:9090:9090 prom/prometheus

Access http://localhost:9090/targets to see the target list and http://localhost:9090/graph for the query console.

Prometheus targets
Prometheus targets
Prometheus graph
Prometheus graph

3. Connect Prometheus to Spring Boot

Edit the Prometheus configuration ( /etc/prometheus/prometheus.yml) to add a job for the Spring Boot app:

- job_name: 'springboot_app'
  scrape_interval: 5s
  metrics_path: '/actuator/prometheus'
  static_configs:
    - targets: ['192.168.31.6:8080']
      labels:
        instance: 'springboot2-A'
        service: 'springboot2-A-service'

Restart the Prometheus container with the updated configuration file mounted:

$ docker run --name prometheus -d \
    -p 9090:9090 \
    -v [PATH]/prometheus.yml:/etc/prometheus/prometheus.yml \
    prom/prometheus

The Spring Boot metrics now appear in the Prometheus target list.

Prometheus target with Spring Boot
Prometheus target with Spring Boot

4. Deploy Grafana

Run Grafana via Docker:

$ docker run -d -p 3000:3000 --name=grafana grafana/grafana

Log in at http://localhost:3000 with default credentials admin/admin.

Grafana login
Grafana login

5. Add Prometheus Data Source

In Grafana, add a new data source of type Prometheus pointing to http://localhost:9090.

Grafana data source
Grafana data source

6. Show JVM Metrics

Import the official JVM dashboard (ID 4701) to visualize JVM statistics.

JVM dashboard
JVM dashboard

7. Custom Metrics

Add AOP‑based request counting:

package com.example.springboot2demo;

import io.micrometer.core.instrument.Counter;
import io.micrometer.core.instrument.MeterRegistry;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import javax.annotation.PostConstruct;

@Component
@Aspect
public class APICounterAop {
    @Pointcut("execution(public * com.example.springboot2demo.*.*(..))")
    public void pointCut() {}

    @Autowired
    MeterRegistry registry;
    private Counter counter;

    @PostConstruct
    private void init() {
        counter = registry.counter("requests_total", "status", "success");
    }

    @Before("pointCut()")
    public void doBefore(JoinPoint joinPoint) {
        System.out.println("do before");
        counter.increment(); // request count
    }

    @AfterReturning(pointcut = "pointCut()", returning = "returnVal")
    public void doAfterReturning(Object returnVal) {
        System.out.println("do after");
    }
}

Test controller:

package com.example.springboot2demo;

import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class TestController {
    @RequestMapping("/hello")
    public String hello() {
        return "hello";
    }
}

After invoking the endpoint several times, the custom metric appears in Prometheus and can be visualized in Grafana.

Custom metric in Prometheus
Custom metric in Prometheus
Custom metric in Grafana
Custom metric in Grafana

8. Dynamic Target Management

Instead of static targets, use Prometheus file‑based service discovery. Create a JSON file with target definitions and reference it via file_sd_configs in prometheus.yml. Mount the directory into the container so Prometheus reloads targets automatically.

[
    {
        "targets": ["192.168.31.6:8080"],
        "labels": {
            "instance": "springboot2-A",
            "service": "springboot2-A-service"
        }
    }
]
- job_name: 'springboot_app'
    scrape_interval: 5s
    metrics_path: '/actuator/prometheus'
    file_sd_configs:
      - files:
        - /home/*.json
        refresh_interval: 1m
$ docker run --name prometheus -d -p 9090:9090 \
    -v [PATH]/prometheus.yml:/etc/prometheus/prometheus.yml \
    -v [PATH]:/home \
    prom/prometheus

This enables automatic target updates without restarting Prometheus.

Original Source

Signed-in readers can open the original source through BestHub's protected redirect.

Sign in to view source
Republication Notice

This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactadmin@besthub.devand we will review it promptly.

DockerPrometheusSpring BootGrafanaMicrometer
Java High-Performance Architecture
Written by

Java High-Performance Architecture

Sharing Java development articles and resources, including SSM architecture and the Spring ecosystem (Spring Boot, Spring Cloud, MyBatis, Dubbo, Docker), Zookeeper, Redis, architecture design, microservices, message queues, Git, etc.

0 followers
Reader feedback

How this landed with the community

Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.