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:
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
.