springboot actuator自定义prometheus监控指标

Posted 今夜月色很美

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了springboot actuator自定义prometheus监控指标相关的知识,希望对你有一定的参考价值。

pom.xml

		<dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-actuator</artifactId>
        </dependency>
        <dependency>
            <groupId>io.micrometer</groupId>
            <artifactId>micrometer-registry-prometheus</artifactId>
            <version>1.3.2</version>
        </dependency>

工具类HandleUtils

import io.micrometer.core.instrument.ImmutableTag;
import io.micrometer.core.instrument.Tag;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.util.CollectionUtils;

import java.util.List;

/**
 * 操作工具类
 */
public class HandleUtils {
    private static final Logger logger = LoggerFactory.getLogger(HandleUtils.class);

    /**
     * 组装tags
     *
     * @param tags 扩展tags
     * @return tags数组
     */
    public static String[] handelTag(List<Tag> tags) {
        if(CollectionUtils.isEmpty(tags)){
            return new String[0];
        }
        int size = tags.size() * 2 + 2 ;
        String[] tagArray = new String[size];
        int i = 0;
        for (Tag tag : tags) {
            tagArray[++i] = tag.getKey();
            tagArray[++i] = tag.getValue();
        }
        return tagArray;
    }

}

counter计数器

import com.iflytek.lingxi.actuator.client.micrometer.constant.MetricsConstants;
import com.iflytek.lingxi.actuator.client.micrometer.utils.HandleUtils;
import com.iflytek.lingxi.actuator.client.micrometer.utils.RegexUtils;
import io.micrometer.core.instrument.Counter;
import io.micrometer.core.instrument.Metrics;
import io.micrometer.core.instrument.Tag;

import java.util.List;

/**
 * 介绍:
 * Counter(计数器) 一个增量为正数的单值计数器
 * <p>
 * 使用场景:
 * Counter的作用是记录XXX的总量或者计数值,适用于一些增长类型的统计,
 * 例如下单、支付次数、 Http请求总量记录等等,通过Tag可以区分不同的场景,
 * 对于下单,可以使用不同的Tag标记不同的业务来源或者是按日期划分,
 * 对于Http请求总量记录,可以使用Tag区分不同的URL
 *
 */
public class MetricsCounterService {

    /**
     * 计数器
     *
     * @param name 指标名称 (数字字母下划线组合)
     */
    public static Counter counter(String name) {
        return MetricsCounterService.counter(name, null);
    }

    /**
     * 计数器(可扩展tag)
     *
     * @param name 指标名称 (数字字母下划线组合)
     * @param tags 自定义tag
     */
    public static Counter counter(String name, List<Tag> tags) {
        // 参数校验
        RegexUtils.checkNameAndTag(name);
        // 组装tags
        String[] tagArray = HandleUtils.handelTag(tags);
        return Metrics.counter(MetricsConstants.NAME_PREFIX + name, tagArray);
    }
}

gauge仪表盘

import com.iflytek.lingxi.actuator.client.micrometer.constant.MetricsConstants;
import com.iflytek.lingxi.actuator.client.micrometer.utils.RegexUtils;
import io.micrometer.core.instrument.*;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.util.Base64Utils;
import org.springframework.util.CollectionUtils;

import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.ConcurrentHashMap;

/**
 * @author lyz
 * @Title: CustomMetricsGaugeService
 * @Description: 继承MetricsGaugeService添加removeGauge方法
 * @date 2021/8/23 9:53
 */
@Slf4j
public class CustomMetricsGaugeService{

    private static ConcurrentHashMap<String, Number> cacheMap = new ConcurrentHashMap<String, Number>();

    /**
     * 仪表
     *
     * @param name 指标名称 (数字字母下划线组合)
     */
    public static <T extends Number> T gauge(String name, T number) {
        // 参数校验
        return gauge(name, new ArrayList<Tag>(), number);
    }

    /**
     * 非master移除所有监控指标
     */
    public static void removeAllMetrics(){
        cacheMap.clear();
        Metrics.globalRegistry.forEachMeter(item -> {
            Metrics.globalRegistry.remove(item);
        });
    }

    /**
     * gauge监控指标移除
     * @param appid
     * @param api
     */
    public static void removeGauge(String appid, String api){
        Metrics.globalRegistry.forEachMeter(item -> {
            String apiTag = item.getId().getTag("api");
            String appidTag = item.getId().getTag("appid");
            if (StringUtils.isNotBlank(apiTag) && StringUtils.isNotBlank(appidTag) && apiTag.equals(api) && appidTag.equals(appid)){
                log.info("删除计量监控指标,api:{},appid:{}", api, appid);
                Metrics.globalRegistry.remove(item);
	            String key = item.getId().getName();
	            for (Tag tag : item.getId().getTags()) {
	                key += tag.getKey() + tag.getValue();
	            }
	            String baseKey = Base64Utils.encodeToString(key.getBytes());
	            cacheMap.remove(baseKey);
            }
        });
    }

    /**
     * 仪表(可扩展tag)
     *
     * @param name 指标名称 (数字字母下划线组合)
     * @param tags 自定义tag
     */
    public static <T extends Number> T gauge(String name, List<Tag> tags, T number) {
        // 参数校验
        RegexUtils.checkNameAndTag(name);
        String key = MetricsConstants.NAME_PREFIX + name;
        if (!CollectionUtils.isEmpty(tags)) {
            for (Tag tag : tags) {
                key += tag.getKey() + tag.getValue();
            }
        }
        String baseKey = Base64Utils.encodeToString(key.getBytes());
        Number t = cacheMap.get(baseKey);
        if (t == null) {
            t = (T) Metrics.gauge(MetricsConstants.NAME_PREFIX + name, tags, number);
            cacheMap.put(baseKey, t);
        } else {
            t = ifNaNReSet(name, tags, number, baseKey, t);
        }
        return (T) t;
    }

    private static <T extends Number> Number ifNaNReSet(String name, List<Tag> tags, T number, String baseKey, Number t) {
        try{
            Gauge gauge = Metrics.globalRegistry.get(MetricsConstants.NAME_PREFIX + name).tags(tags).gauge();
            boolean isNaN = Double.isNaN(gauge.value());
            if (isNaN){
                Metrics.globalRegistry.remove(gauge);
                t = (T) Metrics.gauge(MetricsConstants.NAME_PREFIX + name, tags, number);
                cacheMap.put(baseKey, t);
            }
        } catch (Exception e){
            t = (T) Metrics.gauge(MetricsConstants.NAME_PREFIX + name, tags, number);
        }
        return t;
    }
}

Summary摘要

import com.iflytek.lingxi.actuator.client.micrometer.constant.MetricsConstants;
import com.iflytek.lingxi.actuator.client.micrometer.utils.HandleUtils;
import com.iflytek.lingxi.actuator.client.micrometer.utils.RegexUtils;
import io.micrometer.core.instrument.DistributionSummary;
import io.micrometer.core.instrument.Metrics;
import io.micrometer.core.instrument.Tag;

import java.util.List;

/**
 * 介绍:
 * Summary(摘要)用于跟踪事件的分布。
 * 它类似于一个计时器,但更一般的情况是,它的大小并不一定是一段时间的测量值。
 * 在micrometer中,对应的类是DistributionSummary,它的用法有点像Timer,
 * 但是记录的值是需要直接指定,而不是通过测量一个任务的执行时间。
 * <p>
 * 使用场景:
 * 1、记录指定方法的执行时间用于展示。
 * 2、记录一些任务的执行时间,从而确定某些数据来源的速率,例如消息队列消息的消费速率等。
 *
 */
public class MetricsSummaryService {
    /**
     * 计时器
     *
     * @param name 指标名称 (数字字母下划线组合)
     * @return Summary对象
     */
    public static DistributionSummary summary(String name) {
        return MetricsSummaryService.summary(name, null);
    }

    /**
     * 计时器 (可扩展tag)
     *
     * @param name 指标名称 (数字字母下划线组合)
     * @param tags 自定义tag
     * @return Summary对象
     */
    public static DistributionSummary summary(String name, List<Tag> tags) {
        // 参数校验
        RegexUtils.checkNameAndTag(name);
        // 组装tags
        String[] tagArray = HandleUtils.handelTag(tags);
        return Metrics.summary(MetricsConstants.NAME_PREFIX + name, tagArray);
    }
}

Timer计时器

import com.iflytek.lingxi.actuator.client.micrometer.constant.MetricsConstants;
import com.iflytek.lingxi.actuator.client.micrometer.utils.HandleUtils;
import com.iflytek.lingxi.actuator.client.micrometer.utils.RegexUtils;
import io.micrometer.core.instrument.Metrics;
import io.micrometer.core.instrument.Tag;
import io.micrometer.core.instrument.Timer;

import java.util.List;

/**
 * 介绍:
 * Timer(计时器)同时测量一个特定的代码逻辑块的调用(执行)速度和它的时间分布。
 * 简单来说,就是在调用结束的时间点记录整个调用块执行的总时间,适用于测量短时间执行的事件的耗时分布,例如消息队列消息的消费速率。
 * <p>
 * 使用场景:
 * 1、记录指定方法的执行时间用于展示。
 * 2、记录一些任务的执行时间,从而确定某些数据来源的速率,例如消息队列消息的消费速率等。
 *
 */
public class MetricsTimerService {
    /**
     * 计时器
     *
     * @param name 指标名称 (数字字母下划线组合)
     * @return Timer对象
     */
    public static Timer timer(String name) {
        return MetricsTimerService.timer(name, null);
    }

    /**
     * 计时器 (可扩展tag)
     *
     * @param name 指标名称 (数字字母下划线组合)
     * @param tags 自定义tag
     * @return Timer对象
     */
    public static Timer timer(String name, List<Tag> tags) {
        // 参数校验
        RegexUtils.checkNameAndTag(name);
        // 组装tags
        String[] tagArray = HandleUtils.handelTag(tags);
        return Metrics.timer(MetricsConstants.NAME_PREFIX + name, tagArray);
    }
}

自定义操作

自动注入类PrometheusMetricsExportAutoConfiguration

根据该类源码可以看到,项目启动时已注入CollectorRegistry

@Autowired
    private CollectorRegistry collectorRegistry;

    public void test(){
        Enumeration<Collector.MetricFamilySamples> metricFamilySamplesEnumeration = this.collectorRegistry.metricFamilySamples();
        while (metricFamilySamplesEnumeration.hasMoreElements()){
            Collector.MetricFamilySamples metricFamilySamples = metricFamilySamplesEnumeration.nextElement();
            List<Collector.MetricFamilySamples.Sample> sampleList = metricFamilySamples.samples;
            for (Collector.MetricFamilySamples.Sample sample : sampleList) {
                if ("xxx".equals(sample.name)){
                    System.out.println(sample);

                }
            }
        }
    }

获取registry

Set<MeterRegistry> registrySet = Metrics.globalRegistry.getRegistries();
        for (MeterRegistry meterRegistry : registrySet) {
            if (meterRegistry instanceof PrometheusMeterRegistry){
                ((PrometheusMeterRegistry) meterRegistry).getPrometheusRegistry().unregister(meterRegistry.);
            }
        }

暴漏监控端点类

PrometheusScrapeEndpoint

以上是关于springboot actuator自定义prometheus监控指标的主要内容,如果未能解决你的问题,请参考以下文章

SpringBoot2——指标监控actuator多环境切换Profile和自定义starter

SpringBoot - 构建监控体系02_定义度量指标和 Actuator 端点

SpringBoot2.x系列教程(七十)Spring Boot Actuator集成及自定义Endpoint详解

SpringBoot集成Actuator端点配置

Spring Boot Actuator Endpoints 安全性不适用于自定义 Spring Security 配置

SpringBoot掌握的差不多了,就剩下一个Actuator没搞定了,本文详细来介绍!!!