Spring Boot’s observability, powered by Micrometer, can feel like magic until you realize it’s just about making the invisible visible.
Let’s watch it in action.
Imagine a simple Spring Boot web application. We’ll add micrometer-tracing-bridge-brave and micrometer-registry-prometheus to our pom.xml:
<dependency>
<groupId>io.micrometer</groupId>
<artifactId>micrometer-tracing-bridge-brave</artifactId>
</dependency>
<dependency>
<groupId>io.micrometer.registry</groupId>
<artifactId>micrometer-registry-prometheus</artifactId>
</dependency>
Now, when we start this application and hit an endpoint, say /hello, Micrometer automatically instruments several things:
- HTTP Server Requests: It counts incoming requests, measures their duration, and tags them with details like the HTTP method (
http.method), URI (http.uri), and status code (http.status_code). You’ll see metrics likehttp.server.requests.countandhttp.server.requests.duration. - JVM Metrics: It tracks vital signs of the Java Virtual Machine, such as garbage collection activity (
jvm.gc.pause), memory usage (jvm.memory.used), and thread counts (jvm.threads.live). - System Metrics: Basic operating system-level information like CPU load (
system.cpu.usage) and file descriptor usage (system.file.descriptors) is also gathered.
For tracing, Brave is the default instrumentation library. When a request comes in, it generates a trace ID. If another service is called downstream, that trace ID is propagated, allowing you to follow the entire request flow across multiple services. Each step in the trace is a "span," representing a unit of work with a start time, duration, and tags.
The magic isn’t in the collection itself, but in how it’s standardized. Micrometer provides a vendor-neutral API. You can switch from Prometheus to Datadog, New Relic, or any other monitoring system simply by changing the registry dependency and a few configuration properties, without rewriting your application code.
Here’s how you might configure Prometheus exposure in application.properties:
management.endpoints.web.exposure.include=prometheus,health
management.endpoint.prometheus.enabled=true
management.metrics.export.prometheus.enabled=true
With this, a /actuator/prometheus endpoint becomes available, serving metrics in a format Prometheus can scrape.
The core problem this solves is visibility into complex, distributed systems. Without observability, debugging a slow request or an intermittent error in a microservices architecture is like finding a needle in a haystack blindfolded. Metrics tell you what is happening (e.g., high latency on /users endpoint), and tracing tells you where it’s happening (e.g., the latency is in the database call within the /users service).
The real power comes from combining these. Imagine a dashboard showing HTTP request latency (metrics) alongside traces that pinpoint a specific database query causing that latency. This is the synergy Micrometer provides.
The default configuration for tracing often includes the client-side HTTP requests. This means if your Spring Boot application makes a RestTemplate or WebClient call to another service, Micrometer will automatically create a client span for that outgoing request, linked to the incoming server span. This automatic instrumentation is incredibly powerful because it requires minimal code changes.
The most surprising thing is how deeply integrated this is, and how much you get "for free." You don’t typically need to add annotations or modify your business logic to get basic metrics and tracing for common operations like HTTP requests, database calls (via Spring Data JDBC/JPA), and message queue interactions. The auto-configuration within Spring Boot, combined with Micrometer’s built-in instrumentation for popular libraries, means you get a significant amount of observability out of the box just by adding the dependencies.
The next step is to explore custom metrics and distributed tracing with custom spans to gain even deeper insights.