DEV Community

Nitish
Nitish

Posted on

Spring Boot 3 + Micrometer + Grafana: Metrics That Survive Midnight Deployments


Okay, the user wants me to write a technical article about Spring Boot observability with Micrometer and Grafana. Let me start by understanding the structure they provided. The title needs to be specific, including the main names. The example given uses Spring Boot 3 and Micrometer, so I'll follow that format.

The opening should address a real problem developers face in production. Instead of generic statements, I need to mention specific issues like metrics not being exposed or dashboards not updating. That makes it more relatable.

Prerequisites: I need to list specific versions and tools. Java 17, Spring Boot 3.2, Micrometer, Grafana, and maybe Docker. Let me check the latest versions to ensure accuracy.

Section 1 is about core concepts and dependencies. I'll explain Micrometer's role and then provide the pom.xml with Micrometer and Actuator dependencies. Then the application.properties entries to expose metrics and enable Prometheus. Each property needs a brief explanation.

Section 2 is the implementation. I need a Java class that demonstrates custom metrics. A service with a counter and timer makes sense. The code must be complete with package, imports, annotations, and methods. After the code, mention how to test it via the Actuator endpoint.

Section 3 is infrastructure. Since Grafana and Prometheus are involved, I should include a docker-compose.yml to set them up. Also, a Prometheus config file. Then explain how to import the dashboard.

Common Mistakes: Three specific errors. Maybe missing the Prometheus dependency, incorrect meter names, and not starting the registry. Show wrong code, the fix, and explain why it's wrong.

Summary: Three actionable points, like adding dependencies, using custom metrics, and using Docker for Grafana.

I need to ensure all code blocks have the correct language tags. No placeholders, everything compilable. Use Spring Boot 3.x only. Avoid any marketing language, keep it developer-focused.

Let me start drafting each section, making sure to follow the structure exactly. Check that the Java class includes all necessary parts. Verify the dependencies are up-to-date. For the docker-compose, use the latest images. In common mistakes, pick issues that are easy to overlook but critical for functionality.

Finally, the footer with the author's link. Make sure the article is within the word count. Avoid any markdown beyond what's specified. Double-check for any typos or incorrect versions.

Spring Boot 3 + Micrometer + Grafana: Metrics That Survive Midnight Deployments

Production Spring Boot apps fail silently when JVM memory spikes go undetected, custom business metrics aren’t tracked, and Grafana dashboards show stale data from misconfigured Prometheus scrapers. These gaps turn simple outages into all-night debugging sessions.

Prerequisites

  • Java 17+ SDK
  • Spring Boot 3.2.x
  • Micrometer 1.12+ (included via Spring Boot)
  • Grafana 10.1+ (via Docker)
  • Prometheus 2.47+ (via Docker)

Section 1 — core concept and dependency setup

Micrometer acts as a metrics facade, exposing Spring Boot's internal metrics while enabling custom instrumentation. Add these dependencies:

<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>
</dependencies>
Enter fullscreen mode Exit fullscreen mode
# application.properties
management.endpoints.web.exposure.include=health,metrics,prometheus
management.metrics.export.prometheus.enabled=true
management.server.port=8081
Enter fullscreen mode Exit fullscreen mode
  • exposure.include: Enables Prometheus scraping endpoint
  • metrics.export.prometheus: Activates registry
  • server.port: Separates metrics from main app port

Section 2 — the implementation

Create a service tracking order processing metrics with custom counter and timer. The @MeterBinder annotation registers metrics at startup:

package com.example.orderservice;

import io.micrometer.core.instrument.*;
import org.springframework.stereotype.Service;
import org.springframework.context.annotation.Bean;

@Service
public class OrderMetrics {

    private final Counter ordersCreated;
    private final Timer orderProcessingTime;

    public OrderMetrics(MeterRegistry registry) {
        ordersCreated = Counter.builder("orders.created")
                .description("Total completed orders")
                .register(registry);

        orderProcessingTime = Timer.builder("orders.processing.time")
                .description("Order fulfillment latency")
                .publishPercentiles(0.95)
                .register(registry);
    }

    @Bean
    public MeterBinder cacheMetrics() {
        return registry -> Gauge.builder("orders.queue.size", this::simulateQueue)
                .register(registry);
    }

    private Double simulateQueue() {
        return Math.random() * 100;
    }

    public void recordOrder() {
        ordersCreated.increment();
        orderProcessingTime.record(() -> {
            try { Thread.sleep((long)(Math.random() * 200)); } 
            catch (InterruptedException ignored) {}
        });
    }
}
Enter fullscreen mode Exit fullscreen mode

Test by hitting /actuator/prometheus and searching for orders_created_total. Call recordOrder() from a controller to generate metrics.

Section 3 — infrastructure or integration

Create docker-compose.yml for observability stack:

version: '3'
services:
  prometheus:
    image: prom/prometheus:v2.47.0
    ports:
      - "9090:9090"
    volumes:
      - ./prometheus.yml:/etc/prometheus/prometheus.yml

  grafana:
    image: grafana/grafana:10.1.5
    ports:
      - "3000:3000"
Enter fullscreen mode Exit fullscreen mode
# prometheus.yml
global:
  scrape_interval: 5s

scrape_configs:
  - job_name: 'spring'
    metrics_path: '/actuator/prometheus'
    static_configs:
      - targets: ['host.docker.internal:8081']
Enter fullscreen mode Exit fullscreen mode

Run docker compose up, then import dashboard ID 4701 in Grafana for Spring Boot metrics.

Common Mistakes

Mistake 1: Missing Prometheus registry dependency

Wrong:

<!-- Only actuator present -->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
Enter fullscreen mode Exit fullscreen mode

Fix:

<dependency>
    <groupId>io.micrometer</groupId>
    <artifactId>micrometer-registry-prometheus</artifactId>
</dependency>
Enter fullscreen mode Exit fullscreen mode

Prometheus metrics won't appear without the registry implementation.

Mistake 2: Incorrect meter name casing

Wrong:

Counter.builder("Orders_Created") // Upper case
Enter fullscreen mode Exit fullscreen mode

Fix:

Counter.builder("orders.created") // Lowercase with dots
Enter fullscreen mode Exit fullscreen mode

Prometheus requires snake_case metrics - Micrometer auto-converts dots to underscores.

Mistake 3: Not starting meter registry

Wrong:

public OrderMetrics() { 
    // No MeterRegistry injection
}
Enter fullscreen mode Exit fullscreen mode

Fix:

public OrderMetrics(MeterRegistry registry) {
    // Use injected registry
}
Enter fullscreen mode Exit fullscreen mode

Metrics won't be registered without binding to the active MeterRegistry.

Summary

  • Expose Prometheus endpoint with actuator and registry dependency
  • Instrument custom business logic with @MeterBinder and Timer/Counter builders
  • Configure Docker-based Grafana with host.docker.internal for local dev scraping

The author publishes Spring Boot starter templates at https://gumroad.com

Top comments (0)