Learnitweb

Expose Custom Metrics

To expose custom metrics in Spring Boot, you can use Micrometer, which is the metrics collection facade integrated into Spring Boot Actuator. Spring Boot Actuator provides production-ready endpoints like /actuator/metrics, and you can extend it with custom metrics to track application-specific behavior.

Step-by-Step Guide to Expose Custom Metrics

1. Add Required Dependencies

If you’re using Spring Boot Starter Actuator (most common case):

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-actuator</artifactId>
</dependency>

If you want to export metrics to Prometheus, Datadog, etc., add the corresponding registry (e.g., Prometheus):

<dependency>
    <groupId>io.micrometer</groupId>
    <artifactId>micrometer-registry-prometheus</artifactId>
</dependency>

2. Enable Metrics Endpoint in application.properties

management.endpoints.web.exposure.include=metrics,prometheus

This exposes:

  • /actuator/metrics – for all metrics
  • /actuator/metrics/<metric-name> – for individual metrics
  • /actuator/prometheus – for Prometheus scraping (if you added the Prometheus registry)

3. Inject MeterRegistry to Register Custom Metrics

You can use MeterRegistry to create custom counters, timers, gauges, etc.

Custom Counter Example:

@Component
public class CustomMetricsService {

    private final Counter userCreatedCounter;

    public CustomMetricsService(MeterRegistry registry) {
        this.userCreatedCounter = Counter.builder("app.user.created.count")
                .description("Number of users created")
                .register(registry);
    }

    public void userCreated() {
        // business logic to create a user
        userCreatedCounter.increment(); // increment the custom metric
    }
}

Now you can see this metric at:

/actuator/metrics/app.user.created.count

4. Expose Custom Timers (Duration Tracking)

@Component
public class CustomTimerService {

    private final Timer requestTimer;

    public CustomTimerService(MeterRegistry registry) {
        this.requestTimer = Timer.builder("app.request.duration")
                .description("Request processing time")
                .register(registry);
    }

    public void process() {
        requestTimer.record(() -> {
            // code whose duration you want to measure
            try {
                Thread.sleep(200); // simulate delay
            } catch (InterruptedException e) {
                Thread.currentThread().interrupt();
            }
        });
    }
}

Check metrics via:

/actuator/metrics/app.request.duration

5. Expose Gauges (Real-time Value Tracking)

For values that fluctuate, like queue size or memory usage.

@Component
public class QueueMonitor {

    private final Queue<String> queue = new ConcurrentLinkedQueue<>();

    public QueueMonitor(MeterRegistry registry) {
        Gauge.builder("app.queue.size", queue, Queue::size)
             .description("Size of in-memory queue")
             .register(registry);
    }

    public void addToQueue(String item) {
        queue.add(item);
    }

    public String removeFromQueue() {
        return queue.poll();
    }
}

Access via:

/actuator/metrics/app.queue.size

Types of Custom Metrics

TypeDescriptionMethod
CounterIncrements when an event happensCounter.builder(...).increment()
GaugeReports a changing value (e.g., queue size)Gauge.builder(...).register()
TimerRecords duration of operationsTimer.record(Runnable)
HistogramShows distribution of values (via Timer)Handled automatically in Timer