From b8568675d009093caecc0a4f271bd6fc853a1dfe Mon Sep 17 00:00:00 2001 From: SavitarC <71271200+savitarc@users.noreply.github.com> Date: Wed, 22 Apr 2026 23:18:08 +0800 Subject: [PATCH] apply full metric-kind mapping for prometheus metrics IT assertions --- .../consumer/ConsumerMetricsIT.java | 145 ++++++++------ .../util/PrometheusMetricAssert.java | 151 ++++++++++++++ .../provider/ProviderMetricsIT.java | 185 ++++++++++-------- 3 files changed, 335 insertions(+), 146 deletions(-) create mode 100644 4-governance/dubbo-samples-metrics-prometheus/dubbo-samples-metrics-prometheus-interface/src/main/java/org/apache/dubbo/samples/metrics/prometheus/util/PrometheusMetricAssert.java diff --git a/4-governance/dubbo-samples-metrics-prometheus/dubbo-samples-metrics-prometheus-consumer/src/test/java/org/apache/dubbo/samples/metrics/prometheus/consumer/ConsumerMetricsIT.java b/4-governance/dubbo-samples-metrics-prometheus/dubbo-samples-metrics-prometheus-consumer/src/test/java/org/apache/dubbo/samples/metrics/prometheus/consumer/ConsumerMetricsIT.java index ec3f8637cc..6892e12915 100644 --- a/4-governance/dubbo-samples-metrics-prometheus/dubbo-samples-metrics-prometheus-consumer/src/test/java/org/apache/dubbo/samples/metrics/prometheus/consumer/ConsumerMetricsIT.java +++ b/4-governance/dubbo-samples-metrics-prometheus/dubbo-samples-metrics-prometheus-consumer/src/test/java/org/apache/dubbo/samples/metrics/prometheus/consumer/ConsumerMetricsIT.java @@ -17,6 +17,7 @@ package org.apache.dubbo.samples.metrics.prometheus.consumer; +import org.apache.dubbo.samples.metrics.prometheus.util.PrometheusMetricAssert; import org.apache.http.client.methods.CloseableHttpResponse; import org.apache.http.client.methods.HttpGet; import org.apache.http.impl.client.CloseableHttpClient; @@ -40,89 +41,96 @@ public class ConsumerMetricsIT { private static final Logger logger = LoggerFactory.getLogger(ConsumerMetricsIT.class); - private final String port = "20889"; - private final List metricKeys = new ArrayList(); + private final List metricExpectations = new ArrayList<>(); @Before public void setUp() { - add("dubbo_application_info_total"); + addCounter("dubbo.application.info.total"); //config - add("dubbo_configcenter_total"); - - add("dubbo_provider_rt_milliseconds_p99"); - add("dubbo_provider_requests_total_aggregate"); - add("dubbo_provider_rt_milliseconds_p90"); - add("dubbo_provider_rt_max_milliseconds_aggregate"); - add("dubbo_provider_requests_succeed_total"); - add("dubbo_provider_requests_business_failed_aggregate"); - add("dubbo_provider_requests_business_failed_total"); - add("dubbo_provider_requests_total"); - add("dubbo_provider_requests_failed_service_unavailable_total_aggregate"); - add("dubbo_provider_requests_limit_aggregate"); - add("dubbo_provider_qps_total"); - add("dubbo_provider_rt_milliseconds_p50"); - add("dubbo_provider_requests_processing_total"); - add("dubbo_provider_requests_failed_total"); - add("dubbo_provider_requests_succeed_aggregate"); - add("dubbo_provider_rt_milliseconds_p95"); - add("dubbo_provider_requests_failed_aggregate"); - add("dubbo_provider_rt_avg_milliseconds_aggregate"); - add("dubbo_provider_requests_failed_total_aggregate"); - add("dubbo_provider_requests_failed_network_total_aggregate"); - add("dubbo_provider_rt_min_milliseconds_aggregate"); - add("dubbo_provider_requests_failed_codec_total_aggregate"); - add("dubbo_provider_requests_timeout_failed_aggregate"); + addGauge("dubbo.configcenter.total"); + + addGauge("dubbo.provider.rt.milliseconds.p99"); + addGauge("dubbo.provider.requests.total.aggregate"); + addGauge("dubbo.provider.rt.milliseconds.p90"); + addGauge("dubbo.provider.rt.max.milliseconds.aggregate"); + addCounter("dubbo.provider.requests.succeed.total"); + addGauge("dubbo.provider.requests.business.failed.aggregate"); + addCounter("dubbo.provider.requests.business.failed.total"); + addCounter("dubbo.provider.requests.total"); + addGauge("dubbo.provider.requests.failed.service.unavailable.total.aggregate"); + addGauge("dubbo.provider.requests.limit.aggregate"); + addGauge("dubbo.provider.qps.total"); + addGauge("dubbo.provider.rt.milliseconds.p50"); + addGauge("dubbo.provider.requests.processing.total"); + addCounter("dubbo.provider.requests.failed.total"); + addGauge("dubbo.provider.requests.succeed.aggregate"); + addGauge("dubbo.provider.rt.milliseconds.p95"); + addGauge("dubbo.provider.requests.failed.aggregate"); + addGauge("dubbo.provider.rt.avg.milliseconds.aggregate"); + addGauge("dubbo.provider.requests.failed.total.aggregate"); + addGauge("dubbo.provider.requests.failed.network.total.aggregate"); + addGauge("dubbo.provider.rt.min.milliseconds.aggregate"); + addGauge("dubbo.provider.requests.failed.codec.total.aggregate"); + addGauge("dubbo.provider.requests.timeout.failed.aggregate"); //consumer - add("dubbo_consumer_requests_failed_service_unavailable_total"); + addCounter("dubbo.consumer.requests.failed.service.unavailable.total"); //register - add("dubbo_registry_subscribe_num_total"); - add("dubbo_registry_register_requests_succeed_total"); - add("dubbo_register_rt_milliseconds_last"); - add("dubbo_registry_register_requests_total"); - add("dubbo_register_rt_milliseconds_avg"); - add("dubbo_registry_subscribe_num_succeed_total"); - add("dubbo_register_rt_milliseconds_max"); - add("dubbo_register_rt_milliseconds_min"); - add("dubbo_register_rt_milliseconds_sum"); - add("dubbo_registry_notify_requests_total"); - add("dubbo_registry_register_requests_failed_total"); - add("dubbo_registry_subscribe_num_failed_total"); - add("dubbo_register_rt_milliseconds_max"); - add("dubbo_registry_register_requests_succeed_total"); + addGauge("dubbo.registry.subscribe.num.total"); + addGauge("dubbo.registry.register.requests.succeed.total"); + addGauge("dubbo.register.rt.milliseconds.last"); + addGauge("dubbo.registry.register.requests.total"); + addGauge("dubbo.register.rt.milliseconds.avg"); + addGauge("dubbo.registry.subscribe.num.succeed.total"); + addGauge("dubbo.register.rt.milliseconds.max"); + addGauge("dubbo.register.rt.milliseconds.min"); + addGauge("dubbo.register.rt.milliseconds.sum"); + addGauge("dubbo.registry.notify.requests.total"); + addGauge("dubbo.registry.register.requests.failed.total"); + addGauge("dubbo.registry.subscribe.num.failed.total"); + addGauge("dubbo.register.rt.milliseconds.max"); + addGauge("dubbo.registry.register.requests.succeed.total"); //metadata - add("dubbo_metadata_subscribe_num_failed_total"); - add("dubbo_metadata_push_num_failed_total"); - add("dubbo_metadata_subscribe_num_total"); - add("dubbo_metadata_subscribe_num_succeed_total"); - add("dubbo_metadata_push_num_succeed_total"); - add("dubbo_metadata_push_num_total"); + addGauge("dubbo.metadata.subscribe.num.failed.total"); + addGauge("dubbo.metadata.push.num.failed.total"); + addGauge("dubbo.metadata.subscribe.num.total"); + addGauge("dubbo.metadata.subscribe.num.succeed.total"); + addGauge("dubbo.metadata.push.num.succeed.total"); + addGauge("dubbo.metadata.push.num.total"); //thread - add("dubbo_thread_pool_thread_count"); - add("dubbo_thread_pool_largest_size"); - add("dubbo_thread_pool_active_size"); - add("dubbo_thread_pool_queue_size"); - add("dubbo_thread_pool_core_size"); - add("dubbo_thread_pool_max_size"); + addGauge("dubbo.thread.pool.thread.count"); + addGauge("dubbo.thread.pool.largest.size"); + addGauge("dubbo.thread.pool.active.size"); + addGauge("dubbo.thread.pool.queue.size"); + addGauge("dubbo.thread.pool.core.size"); + addGauge("dubbo.thread.pool.max.size"); + } + + // Use explicit kind only when it changes cross-registry compatible names. + // Example: `dubbo.application.info.total` resolves to `dubbo_application_total` on new clients. + private void addCounter(String rawName) { + metricExpectations.add(new MetricExpectation(rawName, PrometheusMetricAssert.MetricKind.COUNTER)); } - private void add(String dubboStoreProviderInterfaceRtMillisecondsMin) { - metricKeys.add(dubboStoreProviderInterfaceRtMillisecondsMin); + // Example: `...qps.total` is exposed without `_total` on new clients when treated as a gauge. + private void addGauge(String rawName) { + metricExpectations.add(new MetricExpectation(rawName, PrometheusMetricAssert.MetricKind.GAUGE)); } + @Test public void test() throws Exception { new EmbeddedZooKeeper(2181, false).start(); ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("dubbo-demo-consumer.xml"); - List notExistedList = new ArrayList<>(); + List notExistedList = new ArrayList<>(); HttpGet request = new HttpGet("http://localhost:" + port + "/metrics"); try (CloseableHttpClient client = HttpClients.createDefault()) { // retry 3 times as all metrics data are not collected immediately. @@ -132,9 +140,11 @@ public void test() throws Exception { InputStream inputStream = response.getEntity().getContent(); String text = new BufferedReader(new InputStreamReader(inputStream, StandardCharsets.UTF_8)).lines() .collect(Collectors.joining("\n")); - for (String metricKey : metricKeys) { - if (!text.contains(metricKey)) { - notExistedList.add(metricKey); + for (MetricExpectation expectation : metricExpectations) { + try { + PrometheusMetricAssert.assertDubboMetricExposed(text, expectation.rawName, expectation.kind); + } catch (AssertionError ignored) { + notExistedList.add(expectation); } } if (notExistedList.isEmpty()) { @@ -146,10 +156,19 @@ public void test() throws Exception { Assert.fail(e.getMessage()); } context.stop(); - for (String metricKey : notExistedList) { - logger.error("metric key:{} doesn't exist", metricKey); + for (MetricExpectation metric : notExistedList) { + logger.error("metric rawName:{} with kind:{} doesn't exist", metric.rawName, metric.kind); } Assert.assertTrue(notExistedList.isEmpty()); } + private static final class MetricExpectation { + private final String rawName; + private final PrometheusMetricAssert.MetricKind kind; + + private MetricExpectation(String rawName, PrometheusMetricAssert.MetricKind kind) { + this.rawName = rawName; + this.kind = kind; + } + } } diff --git a/4-governance/dubbo-samples-metrics-prometheus/dubbo-samples-metrics-prometheus-interface/src/main/java/org/apache/dubbo/samples/metrics/prometheus/util/PrometheusMetricAssert.java b/4-governance/dubbo-samples-metrics-prometheus/dubbo-samples-metrics-prometheus-interface/src/main/java/org/apache/dubbo/samples/metrics/prometheus/util/PrometheusMetricAssert.java new file mode 100644 index 0000000000..019edc181b --- /dev/null +++ b/4-governance/dubbo-samples-metrics-prometheus/dubbo-samples-metrics-prometheus-interface/src/main/java/org/apache/dubbo/samples/metrics/prometheus/util/PrometheusMetricAssert.java @@ -0,0 +1,151 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.dubbo.samples.metrics.prometheus.util; + +import java.util.LinkedHashSet; +import java.util.Set; +import java.util.regex.Pattern; + +public final class PrometheusMetricAssert { + + private static final String[] RESERVED_SUFFIXES = { + "_total", "_created", "_bucket", "_info", + ".total", ".created", ".bucket", ".info" + }; + + private PrometheusMetricAssert() { + } + + public enum MetricKind { + COUNTER, + GAUGE, + INFO, + HISTOGRAM, + SUMMARY, + UNKNOWN + } + + public static void assertDubboMetricExposed(String scrapeText, String dubboRawName, MetricKind kind) { + Set compatibleNames = resolveCompatibleNames(dubboRawName, kind); + boolean matched = compatibleNames.stream().anyMatch(name -> containsMetricLine(scrapeText, name)); + if (!matched) { + throw new AssertionError("Expected dubbo metric to be exposed. rawName=" + dubboRawName + + ", kind=" + kind + + ", compatibleNames=" + compatibleNames); + } + } + + public static Set resolveCompatibleNames(String dubboRawName, MetricKind kind) { + Set names = new LinkedHashSet<>(); + names.add(legacyPrometheusName(dubboRawName, kind)); + names.add(newPrometheusName(dubboRawName, kind)); + return names; + } + + private static boolean containsMetricLine(String scrapeText, String metricName) { + Pattern pattern = Pattern.compile("(?m)^" + Pattern.quote(metricName) + "(\\{|\\s).*"); + return pattern.matcher(scrapeText).find(); + } + + private static String legacyPrometheusName(String rawName, MetricKind kind) { + String conventionName = snakeCase(rawName); + if (kind == MetricKind.COUNTER && !conventionName.endsWith("_total")) { + conventionName += "_total"; + } + + String sanitized = conventionName.replaceAll("[^a-zA-Z0-9_:]", "_"); + if (sanitized.isEmpty()) { + return "m_"; + } + if (!Character.isLetter(sanitized.charAt(0))) { + return "m_" + sanitized; + } + return sanitized; + } + + private static String snakeCase(String rawName) { + StringBuilder result = new StringBuilder(); + char[] chars = rawName.toCharArray(); + for (int i = 0; i < chars.length; i++) { + char current = chars[i]; + if (Character.isUpperCase(current)) { + if (i > 0 && Character.isLowerCase(chars[i - 1])) { + result.append('_'); + } + result.append(Character.toLowerCase(current)); + } else if (Character.isLetterOrDigit(current) || current == '_') { + result.append(current); + } else { + result.append('_'); + } + } + return result.toString(); + } + + private static String newPrometheusName(String rawName, MetricKind kind) { + String baseName = stripReservedSuffixesRepeatedly(rawName); + String prometheusName = sanitizePrometheusName(baseName.replace('.', '_')); + + switch (kind) { + case COUNTER: + return appendSuffixIfAbsent(prometheusName, "_total"); + case INFO: + return appendSuffixIfAbsent(prometheusName, "_info"); + case GAUGE: + case UNKNOWN: + case HISTOGRAM: + case SUMMARY: + default: + return prometheusName; + } + } + + private static String appendSuffixIfAbsent(String metricName, String suffix) { + if (metricName.endsWith(suffix)) { + return metricName; + } + return metricName + suffix; + } + + private static String stripReservedSuffixesRepeatedly(String rawName) { + String value = rawName; + boolean stripped; + do { + stripped = false; + for (String suffix : RESERVED_SUFFIXES) { + if (value.endsWith(suffix)) { + value = value.substring(0, value.length() - suffix.length()); + stripped = true; + break; + } + } + } while (stripped); + return value; + } + + private static String sanitizePrometheusName(String value) { + String sanitized = value.replaceAll("[^a-zA-Z0-9_:]", "_"); + if (sanitized.isEmpty()) { + return "m_"; + } + if (!Character.isLetter(sanitized.charAt(0))) { + return "m_" + sanitized; + } + return sanitized; + } +} diff --git a/4-governance/dubbo-samples-metrics-prometheus/dubbo-samples-metrics-prometheus-provider/src/test/java/org/apache/dubbo/samples/metrics/prometheus/provider/ProviderMetricsIT.java b/4-governance/dubbo-samples-metrics-prometheus/dubbo-samples-metrics-prometheus-provider/src/test/java/org/apache/dubbo/samples/metrics/prometheus/provider/ProviderMetricsIT.java index 6ce19833ac..2086eb61b9 100644 --- a/4-governance/dubbo-samples-metrics-prometheus/dubbo-samples-metrics-prometheus-provider/src/test/java/org/apache/dubbo/samples/metrics/prometheus/provider/ProviderMetricsIT.java +++ b/4-governance/dubbo-samples-metrics-prometheus/dubbo-samples-metrics-prometheus-provider/src/test/java/org/apache/dubbo/samples/metrics/prometheus/provider/ProviderMetricsIT.java @@ -17,6 +17,7 @@ package org.apache.dubbo.samples.metrics.prometheus.provider; +import org.apache.dubbo.samples.metrics.prometheus.util.PrometheusMetricAssert; import org.apache.http.client.methods.CloseableHttpResponse; import org.apache.http.client.methods.HttpGet; import org.apache.http.impl.client.CloseableHttpClient; @@ -42,130 +43,148 @@ public class ProviderMetricsIT { private final String port = "20888"; - private final List metricKeys = new ArrayList(); + private final List metricExpectations = new ArrayList<>(); @Before public void setUp() { //application - add("dubbo_application_info_total"); + addCounter("dubbo.application.info.total"); //provider - add("dubbo_provider_requests_succeed_total"); - add("dubbo_provider_rt_milliseconds_p99"); - add("dubbo_provider_requests_failed_codec_total_aggregate"); - add("dubbo_provider_requests_business_failed_aggregate"); - add("dubbo_provider_requests_failed_aggregate"); - add("dubbo_provider_requests_timeout_failed_aggregate"); - add("dubbo_provider_requests_limit_aggregate"); - add("dubbo_provider_requests_failed_service_unavailable_total_aggregate"); - add("dubbo_provider_qps_total"); - add("dubbo_provider_rt_min_milliseconds_aggregate"); - add("dubbo_provider_rt_max_milliseconds_aggregate"); - add("dubbo_provider_rt_milliseconds_p90"); - add("dubbo_provider_rt_milliseconds_p50"); - add("dubbo_provider_rt_milliseconds_p95"); - add("dubbo_provider_requests_total"); - add("dubbo_provider_rt_avg_milliseconds_aggregate"); - add("dubbo_provider_requests_total_aggregate"); - add("dubbo_provider_requests_failed_total"); - add("dubbo_provider_requests_processing_total"); - add("dubbo_provider_requests_failed_total_aggregate"); - add("dubbo_provider_requests_failed_network_total_aggregate"); - add("dubbo_provider_requests_succeed_aggregate"); - add("dubbo_provider_requests_business_failed_total"); + addCounter("dubbo.provider.requests.succeed.total"); + addGauge("dubbo.provider.rt.milliseconds.p99"); + addGauge("dubbo.provider.requests.failed.codec.total.aggregate"); + addGauge("dubbo.provider.requests.business.failed.aggregate"); + addGauge("dubbo.provider.requests.failed.aggregate"); + addGauge("dubbo.provider.requests.timeout.failed.aggregate"); + addGauge("dubbo.provider.requests.limit.aggregate"); + addGauge("dubbo.provider.requests.failed.service.unavailable.total.aggregate"); + addGauge("dubbo.provider.qps.total"); + addGauge("dubbo.provider.rt.min.milliseconds.aggregate"); + addGauge("dubbo.provider.rt.max.milliseconds.aggregate"); + addGauge("dubbo.provider.rt.milliseconds.p90"); + addGauge("dubbo.provider.rt.milliseconds.p50"); + addGauge("dubbo.provider.rt.milliseconds.p95"); + addCounter("dubbo.provider.requests.total"); + addGauge("dubbo.provider.rt.avg.milliseconds.aggregate"); + addGauge("dubbo.provider.requests.total.aggregate"); + addCounter("dubbo.provider.requests.failed.total"); + addGauge("dubbo.provider.requests.processing.total"); + addGauge("dubbo.provider.requests.failed.total.aggregate"); + addGauge("dubbo.provider.requests.failed.network.total.aggregate"); + addGauge("dubbo.provider.requests.succeed.aggregate"); + addCounter("dubbo.provider.requests.business.failed.total"); //consumer - add("dubbo_consumer_requests_failed_service_unavailable_total"); + addCounter("dubbo.consumer.requests.failed.service.unavailable.total"); //config - add("dubbo_configcenter_total"); + addGauge("dubbo.configcenter.total"); //register - add("dubbo_register_service_rt_milliseconds_sum"); - add("dubbo_registry_register_service_total"); - add("dubbo_registry_register_service_succeed_total"); - add("dubbo_register_service_rt_milliseconds_min"); - add("dubbo_registry_register_requests_succeed_total"); - add("dubbo_registry_register_requests_total"); - add("dubbo_register_rt_milliseconds_last"); - add("dubbo_register_rt_milliseconds_avg"); - add("dubbo_register_rt_milliseconds_min"); - add("dubbo_register_service_rt_milliseconds_last"); - add("dubbo_register_rt_milliseconds_sum"); - add("dubbo_register_service_rt_milliseconds_max"); - add("dubbo_register_rt_milliseconds_max"); - add("dubbo_register_service_rt_milliseconds_avg"); - - add("dubbo_registry_notify_requests_total"); - add("dubbo_registry_subscribe_num_total"); - add("dubbo_registry_register_requests_failed_total"); - add("dubbo_registry_subscribe_num_succeed_total"); - add("dubbo_registry_subscribe_num_failed_total"); - - add("dubbo_push_rt_milliseconds_avg"); - add("dubbo_push_rt_milliseconds_min"); - add("dubbo_push_rt_milliseconds_last"); - add("dubbo_push_rt_milliseconds_sum"); - add("dubbo_push_rt_milliseconds_max"); - - add("dubbo_store_provider_interface_rt_milliseconds_min"); - add("dubbo_store_provider_interface_rt_milliseconds_max"); - add("dubbo_store_provider_interface_rt_milliseconds_avg"); - add("dubbo_store_provider_interface_rt_milliseconds_last"); - add("dubbo_store_provider_interface_rt_milliseconds_sum"); + addGauge("dubbo.register.service.rt.milliseconds.sum"); + addGauge("dubbo.registry.register.service.total"); + addGauge("dubbo.registry.register.service.succeed.total"); + addGauge("dubbo.register.service.rt.milliseconds.min"); + addGauge("dubbo.registry.register.requests.succeed.total"); + addGauge("dubbo.registry.register.requests.total"); + addGauge("dubbo.register.rt.milliseconds.last"); + addGauge("dubbo.register.rt.milliseconds.avg"); + addGauge("dubbo.register.rt.milliseconds.min"); + addGauge("dubbo.register.service.rt.milliseconds.last"); + addGauge("dubbo.register.rt.milliseconds.sum"); + addGauge("dubbo.register.service.rt.milliseconds.max"); + addGauge("dubbo.register.rt.milliseconds.max"); + addGauge("dubbo.register.service.rt.milliseconds.avg"); + + addGauge("dubbo.registry.notify.requests.total"); + addGauge("dubbo.registry.subscribe.num.total"); + addGauge("dubbo.registry.register.requests.failed.total"); + addGauge("dubbo.registry.subscribe.num.succeed.total"); + addGauge("dubbo.registry.subscribe.num.failed.total"); + + addGauge("dubbo.push.rt.milliseconds.avg"); + addGauge("dubbo.push.rt.milliseconds.min"); + addGauge("dubbo.push.rt.milliseconds.last"); + addGauge("dubbo.push.rt.milliseconds.sum"); + addGauge("dubbo.push.rt.milliseconds.max"); + + addGauge("dubbo.store.provider.interface.rt.milliseconds.min"); + addGauge("dubbo.store.provider.interface.rt.milliseconds.max"); + addGauge("dubbo.store.provider.interface.rt.milliseconds.avg"); + addGauge("dubbo.store.provider.interface.rt.milliseconds.last"); + addGauge("dubbo.store.provider.interface.rt.milliseconds.sum"); //metadata - add("dubbo_metadata_store_provider_succeed_total"); - add("dubbo_metadata_store_provider_total"); - add("dubbo_metadata_subscribe_num_failed_total"); - add("dubbo_metadata_push_num_total"); - add("dubbo_metadata_subscribe_num_total"); - add("dubbo_metadata_subscribe_num_succeed_total"); - add("dubbo_metadata_push_num_succeed_total"); - add("dubbo_metadata_push_num_failed_total"); + addGauge("dubbo.metadata.store.provider.succeed.total"); + addGauge("dubbo.metadata.store.provider.total"); + addGauge("dubbo.metadata.subscribe.num.failed.total"); + addGauge("dubbo.metadata.push.num.total"); + addGauge("dubbo.metadata.subscribe.num.total"); + addGauge("dubbo.metadata.subscribe.num.succeed.total"); + addGauge("dubbo.metadata.push.num.succeed.total"); + addGauge("dubbo.metadata.push.num.failed.total"); //thread - add("dubbo_thread_pool_thread_count"); - add("dubbo_thread_pool_largest_size"); - add("dubbo_thread_pool_active_size"); - add("dubbo_thread_pool_queue_size"); - add("dubbo_thread_pool_core_size"); - add("dubbo_thread_pool_max_size"); + addGauge("dubbo.thread.pool.thread.count"); + addGauge("dubbo.thread.pool.largest.size"); + addGauge("dubbo.thread.pool.active.size"); + addGauge("dubbo.thread.pool.queue.size"); + addGauge("dubbo.thread.pool.core.size"); + addGauge("dubbo.thread.pool.max.size"); // it usually does not appear in test case. - // add("dubbo_thread_pool_reject_thread_count"); + // add("dubbo.thread.pool.reject.thread.count"); } - private void add(String dubboStoreProviderInterfaceRtMillisecondsMin) { - metricKeys.add(dubboStoreProviderInterfaceRtMillisecondsMin); + // Use explicit kind only when it changes cross-registry compatible names. + // Example: `dubbo.application.info.total` resolves to `dubbo_application_total` on new clients. + private void addCounter(String rawName) { + metricExpectations.add(new MetricExpectation(rawName, PrometheusMetricAssert.MetricKind.COUNTER)); } + // Example: `...qps.total` is exposed without `_total` on new clients when treated as a gauge. + private void addGauge(String rawName) { + metricExpectations.add(new MetricExpectation(rawName, PrometheusMetricAssert.MetricKind.GAUGE)); + } + + @Test public void test() throws Exception { new EmbeddedZooKeeper(2181, false).start(); ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("dubbo-demo-provider.xml"); context.start(); - List notExistedList = new ArrayList<>(); + List notExistedList = new ArrayList<>(); try (CloseableHttpClient client = HttpClients.createDefault()) { HttpGet request = new HttpGet("http://localhost:" + port + "/metrics"); CloseableHttpResponse response = client.execute(request); InputStream inputStream = response.getEntity().getContent(); String text = new BufferedReader(new InputStreamReader(inputStream, StandardCharsets.UTF_8)) .lines().collect(Collectors.joining("\n")); - for (int i = 0; i < metricKeys.size(); i++) { - String metricKey = metricKeys.get(i); - if (!text.contains(metricKey)) { - notExistedList.add(metricKey); + for (MetricExpectation expectation : metricExpectations) { + try { + PrometheusMetricAssert.assertDubboMetricExposed(text, expectation.rawName, expectation.kind); + } catch (AssertionError ignored) { + notExistedList.add(expectation); } } } catch (Throwable e) { Assert.fail(e.getMessage()); } context.stop(); - for (String metricKey : notExistedList) { - logger.error("metric key:{} doesn't exist", metricKey); + for (MetricExpectation metric : notExistedList) { + logger.error("metric rawName:{} with kind:{} doesn't exist", metric.rawName, metric.kind); } Assert.assertTrue(notExistedList.isEmpty()); } + private static final class MetricExpectation { + private final String rawName; + private final PrometheusMetricAssert.MetricKind kind; + + private MetricExpectation(String rawName, PrometheusMetricAssert.MetricKind kind) { + this.rawName = rawName; + this.kind = kind; + } + } }