Micrometer metrics with spring java (NO spring boot)
Asked Answered
M

3

10

I'm fresh new to Micrometer/Prometheus world.

I need to use it to get some metrics for existing spring application. No spring boot just plain spring java.

And I'm having a lot of trouble registering e.g. MeterRegistry in order to implement Counters or Timers on some endpoints that I want to have metrics for as well as exposing it to prometheus for scraping.

Can you give me some advice on this topic?

Thank you.

I've tried to register a bean of MeterRegistry but I can't seem to do that for PrometheusMeterRegestry nor PrometheuConfig.

Also just exposing localhost:8080/metrics endpoint from spring doesn't seem to provide anything to Prometheus server.

I would like to be able to scrape some simple metrics from my application, like how many times are some endpoints called and how much time does it take for some endpoints to finish the job or some data processing.

Malefaction answered 28/3, 2019 at 10:52 Comment(0)
S
18

Add below dependency in your pom.xml:

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

Here is a simple test class :

import io.micrometer.core.instrument.Counter;
import io.micrometer.core.instrument.DistributionSummary;
import io.micrometer.core.instrument.Meter;
import io.micrometer.core.instrument.Tags;
import io.micrometer.prometheus.PrometheusConfig;
import io.micrometer.prometheus.PrometheusMeterRegistry;
import org.junit.jupiter.api.Test;

public class MicrometerTest {

    private PrometheusMeterRegistry registry = new PrometheusMeterRegistry(PrometheusConfig.DEFAULT);

    @Test
    public void test() {}

//    @Test
    public void test_metrics() {
        Counter.builder("http_requests_total").description("Http Request Total").tags("method", "GET", "handler",
                "/employee", "status", "200").register(registry).increment();
        Counter.builder("http_requests_total").description("Http Request Total").tags("method", "GET", "handler",
                "/employee", "status", "200").register(registry).increment();

        DistributionSummary.builder("http_response_time_milliseconds").description("Request completed time in milliseconds")
                .tags("method", "GET", "handler", "/employee", "status", "200")
                .publishPercentiles(.5,.95,.99)
                .register(registry).record(40d);
        DistributionSummary.builder("http_response_time_milliseconds").description("Request completed time in milliseconds")
                .tags("method", "GET", "handler", "/employee", "status", "200")
                .publishPercentiles(.5,.95,.99)
                .register(registry).record(50d);

        registry.counter("http_requests_total2", "method", "GET", "status", "200").increment();
        registry.counter("http_requests_total2", "method", "Post", "status", "200").increment();
        registry.counter("http_requests_total2", "method", "GET", "status", "200").increment();

        registry.newCounter(new Meter.Id("query time", Tags.of("select query", "country"), null, "query desc", Meter.Type.COUNTER));
        System.out.println(registry.scrape());
    }
}

If you want to expose it via servlet, add another dependency :

<dependency>
   <groupId>javax.servlet</groupId>
   <artifactId>javax.servlet-api</artifactId>
   <version>4.0.1</version>
</dependency>

Sample servlet to expose metrics:

import com.aeris.amp.metrics.util.MeterRegistry;
import io.micrometer.core.instrument.binder.jvm.DiskSpaceMetrics;
import io.micrometer.core.instrument.binder.jvm.JvmGcMetrics;
import io.micrometer.core.instrument.binder.jvm.JvmMemoryMetrics;
import io.micrometer.core.instrument.binder.jvm.JvmThreadMetrics;
import io.micrometer.core.instrument.binder.system.ProcessorMetrics;
import io.micrometer.core.instrument.binder.system.UptimeMetrics;
import io.micrometer.prometheus.PrometheusConfig;
import io.micrometer.prometheus.PrometheusMeterRegistry;
import io.prometheus.client.exporter.common.TextFormat;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.File;
import java.io.IOException;
import java.io.Writer;

@WebServlet("/metrics")
public class MetricsServlet extends HttpServlet {

    public static PrometheusMeterRegistry registry = new PrometheusMeterRegistry(PrometheusConfig.DEFAULT);

    public void init() {
        new JvmThreadMetrics().bindTo(registry);
        new JvmGcMetrics().bindTo(registry);
        new JvmMemoryMetrics().bindTo(registry);
        new DiskSpaceMetrics(new File("/")).bindTo(registry);
        new ProcessorMetrics().bindTo(registry); // metrics related to the CPU stats
        new UptimeMetrics().bindTo(registry);
    }

    @Override
    protected void doGet(final HttpServletRequest req, final HttpServletResponse resp)
            throws IOException {

        resp.setStatus(HttpServletResponse.SC_OK);
        resp.setContentType(TextFormat.CONTENT_TYPE_004);

        Writer writer = resp.getWriter();
        try {
            registry.scrape(writer);
            writer.flush();
        } finally {
            writer.close();
        }

    }

    public static String getMetricsString() {
        if (registry == null)
            registry = MeterRegistry.registry;
        return registry.scrape();
    }
}
Systematism answered 1/11, 2019 at 10:17 Comment(1)
Excellent, elegant and simple solution Mr. Spock.Outflank
D
0

Without Spring Boot you would register a PrometheusMeterRegistry and then just use the underlying Prometheus client to create your own controller endpoint named /metrics and write out to the HttpServletResponse using the TextFormat livrary. Here is a usage example

You mentioned having trouble creating the PrometheusMeterRegistry. You should include those details, since that will be needed.

Deficiency answered 28/3, 2019 at 17:7 Comment(1)
Thanks man. I had actually a problem with maven. all is good now and I was able to scrape some example metrics. Thanks for the tip. However I would like to be able to have every request measured and be able to see path, status, time elapsed like in default spring boot but I'm still unable to do that... Any advice on that?Malefaction
O
0

I got this going using the following spring bean definition

<bean id="prometheusMeterRegistry" class="io.micrometer.prometheus.PrometheusMeterRegistry">
    <constructor-arg>
       <util:constant static-field="io.micrometer.prometheus.PrometheusConfig.DEFAULT" />
    </constructor-arg>
</bean>

And then created another bean to use it as a REST resource that exposes the metrics for prometheus on /metrics endpoint.

<bean id="metricResource" class="com.example.service.metrics.MetricsResource" init-method="init" />

The Rest class is

package com.example.service.metrics;

import java.io.File;

import javax.inject.Inject;
import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.core.Response;

import io.micrometer.core.instrument.binder.jvm.JvmMemoryMetrics;
import io.micrometer.core.instrument.binder.jvm.JvmThreadMetrics;
import io.micrometer.core.instrument.binder.system.ProcessorMetrics;
import io.micrometer.core.instrument.binder.system.UptimeMetrics;
import io.micrometer.prometheus.PrometheusMeterRegistry;


@Path("/")
public class MetricsResource {

  @Inject
  private PrometheusMeterRegistry _registry;

  @GET
  public Response getMetrics() {
    return Response.ok(_registry.scrape()).build();
  }

  public void init() {
    new JvmThreadMetrics().bindTo(_registry);
    new JvmMemoryMetrics().bindTo(_registry);
    new io.micrometer.core.instrument.binder.system.DiskSpaceMetrics(new File("/")).bindTo(_registry);
    new ProcessorMetrics().bindTo(_registry); // metrics related to the CPU stats
    new UptimeMetrics().bindTo(_registry);
  }

}

And then in the web.xml I added the following:

<servlet>
    <servlet-name>MetricsServices</servlet-name>
    <servlet-class>org.glassfish.jersey.servlet.ServletContainer</servlet-class>
    <init-param>
      <param-name>jersey.config.server.provider.packages</param-name>
      <param-value>com.example.service.metrics</param-value>
    </init-param>
  </servlet>
  <servlet-mapping>
    <servlet-name>MetricsServices</servlet-name>
    <url-pattern>/metrics</url-pattern>
  </servlet-mapping>

I hope this helps you get the metrics when you access https://localhost:<your_port>//metrics

Overpower answered 16/4 at 18:53 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.