You need to enable JavaScript to run this app. 如何为Prometheus Java客户端默认JMX指标注入自定义标签区分微服务
优惠活动
大模型
产品
解决方案
定价
更多

如何为Prometheus Java客户端默认JMX指标注入自定义标签区分微服务

Prometheus Java客户端给默认指标追加自定义服务标签的实现方案

方案一:客户端侧统一追加全局标签(推荐)

不需要修改默认导出的指标逻辑,通过包装CollectorRegistry的方式,给所有输出的指标自动追加固定标签,最终暴露的指标本身就会携带service标签。

1. 自定义可追加全局标签的Registry代理

这个代理类会在收集所有指标样本时,自动合并你指定的全局标签到每个样本中:

import io.prometheus.client.Collector;
import io.prometheus.client.CollectorRegistry;
import io.prometheus.client.exporter.MetricsServlet;

import java.util.ArrayList;
import java.util.List;
import java.util.Map;

public class GlobalLabelRegistry extends CollectorRegistry {
    private final CollectorRegistry delegate;
    private final List<String> globalLabelNames;
    private final List<String> globalLabelValues;

    /**
     * @param delegate 原始的指标Registry
     * @param globalLabels 需要全局追加的固定标签键值对,比如 {"service": "foo"}
     */
    public GlobalLabelRegistry(CollectorRegistry delegate, Map<String, String> globalLabels) {
        this.delegate = delegate;
        this.globalLabelNames = new ArrayList<>(globalLabels.keySet());
        this.globalLabelValues = new ArrayList<>(globalLabels.values());
    }

    @Override
    public List<MetricFamilySamples> collect() {
        List<MetricFamilySamples> originalSamples = delegate.collect();
        List<MetricFamilySamples> wrappedSamples = new ArrayList<>();

        for (MetricFamilySamples mfs : originalSamples) {
            List<MetricFamilySamples.Sample> wrappedSampleList = new ArrayList<>();
            for (MetricFamilySamples.Sample sample : mfs.samples) {
                // 合并原有标签和全局标签
                List<String> allLabelNames = new ArrayList<>(sample.labelNames);
                allLabelNames.addAll(globalLabelNames);
                List<String> allLabelValues = new ArrayList<>(sample.labelValues);
                allLabelValues.addAll(globalLabelValues);

                wrappedSampleList.add(new MetricFamilySamples.Sample(
                        sample.name, allLabelNames, allLabelValues, sample.value
                ));
            }
            wrappedSamples.add(new MetricFamilySamples(mfs.name, mfs.type, mfs.help, wrappedSampleList));
        }
        return wrappedSamples;
    }

    // 其余registry方法直接委托给原始实现即可
    @Override
    public void register(Collector collector) {
        delegate.register(collector);
    }

    @Override
    public void unregister(Collector collector) {
        delegate.unregister(collector);
    }
}

2. 修改原有监控服务初始化逻辑

不需要改动原来的Undertow启动逻辑,只需要调整指标初始化和Servlet注入的部分:

// 新建独立的指标Registry,避免污染全局默认Registry
CollectorRegistry baseRegistry = new CollectorRegistry();
// 把默认JVM、系统指标注册到这个独立Registry
DefaultExports.initialize(baseRegistry);

// 定义需要追加的全局标签,service的值可以从配置、启动参数中读取
Map<String, String> globalLabels = Map.of("service", "foo");
// 包装Registry,绑定全局标签
GlobalLabelRegistry wrappedRegistry = new GlobalLabelRegistry(baseRegistry, globalLabels);

// 初始化监控Servlet,传入包装后的Registry
DeploymentInfo monitoringServletInfo = Servlets.deployment()
        .setClassLoader(MonitoringServer.class.getClassLoader())
        .setContextPath("/")
        .setDeploymentName("monitoring")
        // 核心改动:传入带全局标签的Registry实例
        .addServlet(Servlets.servlet(new MetricsServlet(wrappedRegistry)).addMapping("/*"));

// 后续Undertow的启动逻辑和原代码完全一致,无需修改
DeploymentManager monitoringDeployment = Servlets.defaultContainer().addDeployment(monitoringServletInfo);
monitoringDeployment.deploy();
HttpHandler monitoringHandler = monitoringDeployment.start();

server = Undertow.builder()
        .addHttpListener(port, "0.0.0.0")
        .setHandler(
                Handlers.path().addExactPath(HEALTH_PATH, new HealthHandler())
                        .addExactPath(MONITORING_PATH, monitoringHandler))
        .build();

server.start();

实现后暴露的指标效果完全符合预期:

jvm_buffer_pool_used_bytes{pool="direct",service="foo"} 116776.0

注意:全局追加的标签名不要和现有指标已有的标签名重复,否则会出现标签冲突。

方案二:Prometheus抓取侧统一加标签(无需修改Java代码)

如果Prometheus服务是统一运维的,不需要每个微服务单独改造代码,也可以在Prometheus的抓取配置中给每个任务统一加标签:

scrape_configs:
  # 微服务foo的抓取配置
  - job_name: 'service-foo'
    static_configs:
      - targets: ['foo服务的IP:端口']
        # 直接给该任务抓取的所有指标绑定service标签
        labels:
          service: 'foo'
  # 微服务bar的抓取配置
  - job_name: 'service-bar'
    static_configs:
      - targets: ['bar服务的IP:端口']
        labels:
          service: 'bar'

这种方式的优势是所有标签逻辑统一在Prometheus侧维护,不需要业务侧改动代码;缺点是指标本身暴露时不带service标签,仅在Prometheus存储、Grafana查询时可识别。

内容的提问来源于stack exchange,提问作者Denis Voloshin

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.10.25 12:06:04