Using Prometheus with Spring Boot

For monitoring and alerting, we use Prometheus. To scrape input, Prometheus requires the different services to expose an endpoint with a specific format. This is a quick intro to getting this endpoint to work with Spring Boot.

TL; DR:

Include Prometheus dependencies using Gradle:

compile 'io.prometheus:simpleclient_spring_boot:0.0.21'
compile 'io.prometheus:simpleclient_servlet:0.0.21'
compile 'io.prometheus:simpleclient_hotspot:0.0.21'

Annotate main class with @EnablePrometheusEndpoint (and optionally @EnableSpringBootMetricsCollector).

Optionally change path using endpoints.prometheus.path="some-valid-path".

Also, there is a running code example over at GitHub.

Now for a more detailed discussion…

Getting a Prometheus endpoint in Spring Boot

The simplest way to enable this endpoint in Spring Boot is utilizing the @EnablePrometheusEndpoint-annotation. Any version >= 0.0.18 (I think) should work with this annotation.

Note: This new endpoint will bind to the management port if this is enabled, not the default application port (which is great – you typically want to hide this from your users).

Firstly, however, we have to include the required packages. For Gradle, this means:

compile 'io.prometheus:simpleclient_spring_boot:0.0.21'
compile 'io.prometheus:simpleclient_servlet:0.0.21'
compile 'io.prometheus:simpleclient_hotspot:0.0.21'

After these dependencies are available, it’s simply a matter of adding the annotation to your main application class, and the endpoint will be available at /prometheus:

package hello;

import io.prometheus.client.spring.boot.EnablePrometheusEndpoint;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
@EnablePrometheusEndpoint
public class Application {

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

When you start the application and navigate to /prometheus, this is what you get:


Yup, that’s right, nothing. Not really that exciting, but at least it’s a start (i.e., 200 OK!).

But what about the metrics?

I’m glad you asked. Prometheus supplies a few default metrics which we can just include using @EnableSpringBootMetricsCollector. The new main application class now looks like this:

package hello;

import io.prometheus.client.spring.boot.EnablePrometheusEndpoint;
import io.prometheus.client.spring.boot.EnableSpringBootMetricsCollector;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
@EnablePrometheusEndpoint
@EnableSpringBootMetricsCollector
public class Application {

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

Navigating to /prometheus now produces:

# HELP mem mem
# TYPE mem gauge
mem 427446.0
# HELP mem_free mem_free
# TYPE mem_free gauge
mem_free 159748.0
# HELP processors processors
# TYPE processors gauge
processors 8.0
# HELP instance_uptime instance_uptime
# TYPE instance_uptime gauge
instance_uptime 13715.0
# HELP uptime uptime
# TYPE uptime gauge
uptime 16324.0
# HELP systemload_average systemload_average
# TYPE systemload_average gauge
systemload_average 1.83984375
# HELP heap_committed heap_committed
# TYPE heap_committed gauge
heap_committed 374272.0
# HELP heap_init heap_init
# TYPE heap_init gauge
heap_init 262144.0
# HELP heap_used heap_used
# TYPE heap_used gauge
heap_used 214523.0
# HELP heap heap
# TYPE heap gauge
heap 3728384.0
# HELP nonheap_committed nonheap_committed
# TYPE nonheap_committed gauge
nonheap_committed 54528.0
# HELP nonheap_init nonheap_init
# TYPE nonheap_init gauge
nonheap_init 2496.0
# HELP nonheap_used nonheap_used
# TYPE nonheap_used gauge
nonheap_used 53175.0
# HELP nonheap nonheap
# TYPE nonheap gauge
nonheap 0.0
# HELP threads_peak threads_peak
# TYPE threads_peak gauge
threads_peak 37.0
# HELP threads_daemon threads_daemon
# TYPE threads_daemon gauge
threads_daemon 3.0
# HELP threads_totalStarted threads_totalStarted
# TYPE threads_totalStarted gauge
threads_totalStarted 43.0
# HELP threads threads
# TYPE threads gauge
threads 37.0
# HELP classes classes
# TYPE classes gauge
classes 6597.0
# HELP classes_loaded classes_loaded
# TYPE classes_loaded gauge
classes_loaded 6597.0
# HELP classes_unloaded classes_unloaded
# TYPE classes_unloaded gauge
classes_unloaded 0.0
# HELP gc_ps_scavenge_count gc_ps_scavenge_count
# TYPE gc_ps_scavenge_count gauge
gc_ps_scavenge_count 7.0
# HELP gc_ps_scavenge_time gc_ps_scavenge_time
# TYPE gc_ps_scavenge_time gauge
gc_ps_scavenge_time 60.0
# HELP gc_ps_marksweep_count gc_ps_marksweep_count
# TYPE gc_ps_marksweep_count gauge
gc_ps_marksweep_count 1.0
# HELP gc_ps_marksweep_time gc_ps_marksweep_time
# TYPE gc_ps_marksweep_time gauge
gc_ps_marksweep_time 35.0

Adding your own collectors

Adding collectors are straight forward. Say I want to add a Counter collector, counting the total number of different messages I receive and their processing status, I could add the following to the class handling the messages:

private static final Counter messages = Counter.build()
            .name("application_messages_received")
            .labelNames("messageType", "processingStatus")
            .help("Handling status of the different messages received").register();
...
public void handleMyMessage(MessageType message) {
    // Do logic
    String processingResult = businessLogic(message);
    messages.labels(message.getClass().getSimpleName(), processingResult).inc();
}

This will report the new Counter with type and help on /prometheus.

For more detailed (and possibly proper) use, please refer to Prometheus documentation.

Configuring the Prometheus endpoint

The enpoint exposed by Prometheus is encapsulated in a bean called prometheus. Using the ordinary Spring Boot’s configuration settings, you can override most of what you find interesting.

What we required was a way to change the path of the endpoint, and this works with the following configuration:

endpoints:
  prometheus:
    path: "prometheus-metrics"

This simply changes the endpoint url to /prometheus-metrics.

Note: It is also possible to change the path by changing endpoints.prometheus.id. However, this changes the bean ID, and does not allow for any other values than characters and underscore. Thus, it would not be possible to change the id to prometheus-metrics, as this contains a dash. Furthermore, this smells bad in my opinion.

But I want to see working code!

Great! Have a look at this git repository.

Acknowledgements

I first ran across a two part blog post on using Spring Boot and Prometheus. Thank you Raymond Lee.

Furthermore, this Stack Overflow question got me reading about @EnablePrometheusEndpoint.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google photo

You are commenting using your Google account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s