I used to use dropwizard built-in metrics annotations and graphite, but now I wanted to integrate these into my javaee project exposing prometheus metrics format. The main difference between graphite and prometheus is the push or pull mentality. So instead of pushing the metrics data to the sink, they are provided via http servlet and the prometheus server is scraping them from there.
There are two registry classes which we have to bring together. One is the com.codahale.metrics.MetricRegistry which holds all codahale metrics and the other one is the io.prometheus.client.CollectorRegistry which holds all metrics being published in the prometheus. So in our case, we will receive all metrics from our Jax-RS resource classes annotated with com.codahale.metrics.annotation.Timed or com.codahale.metrics.annotation.ExceptionMetered.
The prometheus library contains some default Jvm metrics (DefaultExports), but I could not use them because of some sun jdk classes which do not exist in OpenJDK for example. But it is good to add these in addition to the metrics coming from our annotated Jax-RS resource classes. So this is the class
import io.prometheus.client.CollectorRegistry; | |
import io.prometheus.client.dropwizard.DropwizardExports; | |
import io.prometheus.client.hotspot.ClassLoadingExports; | |
import io.prometheus.client.hotspot.GarbageCollectorExports; | |
import io.prometheus.client.hotspot.MemoryPoolsExports; | |
import io.prometheus.client.hotspot.ThreadExports; | |
import io.prometheus.client.hotspot.VersionInfoExports; | |
import javax.enterprise.context.ApplicationScoped; | |
import javax.enterprise.context.Destroyed; | |
import javax.enterprise.context.Initialized; | |
import javax.enterprise.event.Observes; | |
import javax.inject.Inject; | |
import com.codahale.metrics.MetricRegistry; | |
/** | |
* A bean, which is starting the metrics reporter during startup and its is shutting down the | |
* reporter when application is destroyed. | |
* | |
* | |
*/ | |
@ApplicationScoped | |
public class MetricsBean { | |
@Inject | |
private MetricRegistry registry; | |
public void init(@Observes @Initialized(ApplicationScoped.class) final Object init) { | |
// DefaultExports.initialize(); | |
new MemoryPoolsExports().register(); | |
new GarbageCollectorExports().register(); | |
new ThreadExports().register(); | |
new ClassLoadingExports().register(); | |
new VersionInfoExports().register(); | |
CollectorRegistry.defaultRegistry.register(new DropwizardExports(registry)); | |
} | |
public void destroy(@Observes @Destroyed(ApplicationScoped.class) final Object init) { | |
CollectorRegistry.defaultRegistry.clear(); | |
} | |
} |
and these are the dependencies for the project:
<dependency> | |
<groupId>io.astefanutti.metrics.cdi</groupId> | |
<artifactId>metrics-cdi</artifactId> | |
<version>1.3.6</version> | |
</dependency> | |
<dependency> | |
<groupId>io.prometheus</groupId> | |
<artifactId>simpleclient</artifactId> | |
<version>0.0.23</version> | |
</dependency> | |
<dependency> | |
<groupId>io.prometheus</groupId> | |
<artifactId>simpleclient_servlet</artifactId> | |
<version>0.0.23</version> | |
</dependency> | |
<dependency> | |
<groupId>io.prometheus</groupId> | |
<artifactId>simpleclient_dropwizard</artifactId> | |
<version>0.0.23</version> | |
</dependency> | |
<dependency> | |
<groupId>io.prometheus</groupId> | |
<artifactId>simpleclient_hotspot</artifactId> | |
<version>0.0.23</version> | |
</dependency> |
As you can see, by the help of CDI we are able to bind everything together during the startup phase of the application inside of the JEE container.
Then we add the servlet to the web.xml like this and we are done:
<?xml version="1.0" encoding="UTF-8"?> | |
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://java.sun.com/xml/ns/javaee" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd" version="3.0"> | |
<welcome-file-list> | |
<welcome-file>/index.html</welcome-file> | |
</welcome-file-list> | |
<!-- --> | |
<servlet> | |
<servlet-name>prometheusMetrics</servlet-name> | |
<servlet-class>io.prometheus.client.exporter.MetricsServlet</servlet-class> | |
</servlet> | |
<servlet-mapping> | |
<servlet-name>prometheusMetrics</servlet-name> | |
<url-pattern>/prometheusMetrics/*</url-pattern> | |
</servlet-mapping> | |
</web-app> |
In your prometheus server create a scrape config
scrape_configs: | |
- job_name: 'jaxrs' | |
metrics_path: /prometheusMetrics/ | |
# Override the global default and scrape targets from this job every 5 seconds. | |
scrape_interval: 5s | |
static_configs: | |
- targets: ['www.example.com:80'] | |
labels: | |
group: 'jaxrs' |
and you should be able to see you metrics in prometheus expression browser or Grafana.