Spring MVC integrates prometheus+grafana

Write in front

Spring MVC is an old project... It hurts
Our project uses spring mvc4 x. prometheus is used for time series database and grafana is used for dashboard.
There are a lot of materials about prometheus for spring boot on the Internet, and there are also many templates for grafana dashboard for spring boot. Unfortunately, no one cares about spring MVC... If you want to use the existing micrometer registry Prometheus to expose service indicators and directly apply the springboot mode in the template dashboard of grafana, you have to look at the source code of micrometer registry Prometheus.
Therefore, this tutorial will first explain the next part of the source code, and then tell you the configuration of Prometheus for spring MVC.

Environment of this case

micrometer-registry-prometheus:1.1.0
jdk 1.8
springmvc 4.x
tomcat 8.x

Interpretation of core package functions

Official source code GitHub: https://github.com/micrometer-metrics/micrometer
Enter the micrometer core to view it, focusing on several core classes

  1. Registrar MeterRegistry
    MeterRegistry is the base class (abstract class) of all registries, and all registrars should inherit from it
public abstract class MeterRegistry {
    protected final Clock clock;
    private final Object meterMapLock = new Object();
    private volatile MeterFilter[] filters = new MeterFilter[0];
    private final List<Consumer<Meter>> meterAddedListeners = new CopyOnWriteArrayList<>();
    private final List<Consumer<Meter>> meterRemovedListeners = new CopyOnWriteArrayList<>();
    private final Config config = new Config();
    private final More more = new More();
    //...
}

For example, the registration class of prometheus is

package io.micrometer.prometheus;
public class PrometheusMeterRegistry extends MeterRegistry {
    private final CollectorRegistry registry;
    private final ConcurrentMap<String, MicrometerCollector> collectorMap;
    private final PrometheusConfig prometheusConfig;
    //...
}
  1. Binding interface MeterBinder for internal measurement of container
public interface MeterBinder {
    void bindTo(@NonNull MeterRegistry registry);
}

MeterBinder can bind internal measurement data to a registry, such as jvm's gc indicator JvmGcMetrics

public class JvmGcMetrics implements MeterBinder {
    public void bindTo(MeterRegistry registry) {
        AtomicLong maxDataSize = new AtomicLong(0L);
        Gauge.builder("jvm.gc.max.data.size", maxDataSize, AtomicLong::get).tags(this.tags).description("Max size of old generation memory pool").baseUnit("bytes").register(registry);
        // ...
    }
}
  1. Bind the monitoring indicator MeterBinder to the registry
public void initRegistry() {
    PrometheusMeterRegistry registry = new PrometheusMeterRegistry();

    JvmGcMetrics jvmGcMetrics = new JvmGcMetrics();
    jvmGcMetrics.bindTo(registry);
}

  1. Class inheritance diagram

  2. MeterRegistry common implementation classes

  3. Output index data

String response = MeterRegistry.scrape();

Return the response to the front end, as shown below

Spring MVC integrates prometheus and uses the metrics provided by default

Such a large dashboard template can be implemented under springboot. It is introduced in both official tutorials and major forums.

However, the introduction to spring MVC is so poor that you can't even find it in the dashboard template of grafana. Therefore, this chapter will tell you how spring MVC exposes data indicators.

The principle of meterregistry has been introduced above. Please read the following code carefully

pom.xml management dependency

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

web.xml configuration indicator exposure servlet

<!-- Exposure service monitoring indicators -->
<servlet>
    <servlet-name>prometheus</servlet-name>
    <servlet-class>com.epoch.wan37.servlet.PrometheusMetricsServlet</servlet-class>
</servlet>
<servlet-mapping>
    <servlet-name>prometheus</servlet-name>
    <url-pattern>/prometheus</url-pattern>
</servlet-mapping>

Configure servlet

package com.epoch.wan37.servlet;

import com.netflix.hystrix.HystrixMetrics;
import io.micrometer.core.instrument.Clock;
import io.micrometer.core.instrument.Counter;
import io.micrometer.core.instrument.MockClock;
import io.micrometer.core.instrument.binder.MeterBinder;
import io.micrometer.core.instrument.binder.cache.*;
import io.micrometer.core.instrument.binder.db.DatabaseTableMetrics;
import io.micrometer.core.instrument.binder.db.PostgreSQLDatabaseMetrics;
import io.micrometer.core.instrument.binder.jetty.JettyServerThreadPoolMetrics;
import io.micrometer.core.instrument.binder.jetty.JettyStatisticsMetrics;
import io.micrometer.core.instrument.binder.jpa.HibernateMetrics;
import io.micrometer.core.instrument.binder.jvm.*;
import io.micrometer.core.instrument.binder.kafka.KafkaConsumerMetrics;
import io.micrometer.core.instrument.binder.logging.Log4j2Metrics;
import io.micrometer.core.instrument.binder.logging.LogbackMetrics;
import io.micrometer.core.instrument.binder.system.FileDescriptorMetrics;
import io.micrometer.core.instrument.binder.system.ProcessorMetrics;
import io.micrometer.core.instrument.binder.system.UptimeMetrics;
import io.micrometer.core.instrument.binder.tomcat.TomcatMetrics;
import io.micrometer.prometheus.PrometheusConfig;
import io.micrometer.prometheus.PrometheusMeterRegistry;
import io.micrometer.prometheus.PrometheusRenameFilter;
import io.prometheus.client.CollectorRegistry;
import io.prometheus.client.exporter.common.TextFormat;
import org.apache.catalina.Manager;
import org.apache.catalina.session.StandardManager;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Configuration;

import javax.annotation.PostConstruct;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.Collections;
import java.util.HashSet;
import java.util.Set;

/**
 * @desc describe
 * @auther winnie
 * @date 2021/8/20
 */
@Configuration
public class PrometheusMetricsServlet extends HttpServlet {
    @Value("${spring.application.name}")
    private String appName;

    private PrometheusMeterRegistry registry;
    private Set<MeterBinder> meterBinderSet = new HashSet<>();
    /**
     * Initialize indicator registrar
     */
    @PostConstruct
    public void initRegistry() {
        PrometheusConfig prometheusConfig = PrometheusConfig.DEFAULT;
        CollectorRegistry defaultRegistry = CollectorRegistry.defaultRegistry;
        Clock clock = new MockClock();
        registry = new PrometheusMeterRegistry(prometheusConfig,defaultRegistry,clock);
        // Configure app name
        registry.config().commonTags("application",appName);

        // Initialize monitoring indicators
        initMetrics();

        // Indicator binding registrar
        meterBinderSet.forEach(meterBinder -> meterBinder.bindTo(registry));
    }

    /**
     * Initialize indicators and configure them according to project needs. The following configurations are io Prometheus is provided by default
     */
    private void initMetrics() {
        // jvm metrics
        JvmGcMetrics jvmGcMetrics = new JvmGcMetrics();
        meterBinderSet.add(jvmGcMetrics);
        JvmThreadMetrics jvmThreadMetrics = new JvmThreadMetrics();
        meterBinderSet.add(jvmThreadMetrics);
        JvmMemoryMetrics jvmMemoryMetrics = new JvmMemoryMetrics();
        meterBinderSet.add(jvmMemoryMetrics);
        ClassLoaderMetrics classLoaderMetrics = new ClassLoaderMetrics();
        meterBinderSet.add(classLoaderMetrics);
//        DiskSpaceMetrics diskSpaceMetrics = new DiskSpaceMetrics();
//        ExecutorServiceMetrics executorServiceMetrics = new ExecutorServiceMetrics();
        // Cache index
//        CacheMeterBinder cacheMeterBinder = new CacheMeterBinder();
//        CaffeineCacheMetrics caffeineCacheMetrics = new CaffeineCacheMetrics();
//        GuavaCacheMetrics guavaCacheMetrics = new GuavaCacheMetrics();
//        HazelcastCacheMetrics hazelcastCacheMetrics = new HazelcastCacheMetrics();
//        JCacheMetrics jCacheMetrics = new JCacheMetrics();
        // DB index
//        DatabaseTableMetrics databaseTableMetrics = new DatabaseTableMetrics();
//        PostgreSQLDatabaseMetrics postgreSQLDatabaseMetrics = new PostgreSQLDatabaseMetrics();
        // System index
        FileDescriptorMetrics fileDescriptorMetrics = new FileDescriptorMetrics();
        meterBinderSet.add(fileDescriptorMetrics);
        ProcessorMetrics processorMetrics = new ProcessorMetrics();
        meterBinderSet.add(processorMetrics);
        UptimeMetrics uptimeMetrics = new UptimeMetrics();
        meterBinderSet.add(uptimeMetrics);
        // logging indicator
        Log4j2Metrics log4j2Metrics = new Log4j2Metrics();
        meterBinderSet.add(log4j2Metrics);
//        LogbackMetrics logbackMetrics = new LogbackMetrics();
        // jetty index
//        JettyServerThreadPoolMetrics jettyServerThreadPoolMetrics = new JettyServerThreadPoolMetrics();
//        JettyStatisticsMetrics jettyStatisticsMetrics = new JettyStatisticsMetrics();
        // tomcat index
        Manager standardManager = new StandardManager();
        TomcatMetrics tomcatMetrics = new TomcatMetrics(standardManager, Collections.EMPTY_LIST);
        meterBinderSet.add(tomcatMetrics);
        // kafka index
//        KafkaConsumerMetrics kafkaConsumerMetrics = new KafkaConsumerMetrics();
        // jpa index
//        HibernateMetrics hibernateMetrics = new HibernateMetrics();
        // hystrix index
//        HystrixMetrics hystrixMetrics = new HystrixMetrics();
    }

    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        PrintWriter writer = resp.getWriter();
        try {
            String response = registry.scrape();
            writer.write(response);
            writer.flush();
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            writer.close();
        }
    }

    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        this.doGet(req, resp);
    }
}

Where, ${spring.application.name}: is the name of your app

Start the tomcat container and test it

${TOMCAT_HOME}/bin/startup.sh

Access in browser

http://${yourHostname}:${port}/${ContextPath}/prometheus

If the following information appears, our service indicators have been exposed

The next operation is very simple, that is, we expose the index api to prometheus to listen
prometheus.yml

scrape_configs:
  - job_name: fssc
  metrics_path: '/fssc/prometheus'
  static_configs:
    - targets: ['10.16.16.202:8081']

job_name: prometheus task name
Replace the ip and port with your own services, and prometheus will visit 10.16 16.202:8081 / FSSC / prometheus obtain the index data, persist it, and present the data to you through grafana.

Restart prometheus.

Configuring the grafana instrument cluster

Official recommendation: https://grafana.com/grafana/dashboards/4701

  1. Download json file

  2. Import into grafana and upload the json file

Congratulations, you have completed (part of) the process of integrating prometheus with spring MVC

Write at the end

In this case, you will see that some indicator data are not connected, and will be supplemented as much as possible in the future.

Keywords: Prometheus

Added by rdawson on Tue, 21 Dec 2021 08:33:18 +0200