Repository: jenkinsci/prometheus-plugin (java)

Issue:
Problem with feature to disable certain metrics
### Jenkins and plugins versions report

<details>
  <summary>Environment</summary>

  <!-- Paste your environment details below -->
  ```text
  Paste the output here
  ```

</details>


### What Operating System are you using (both controller, and any agents involved in the problem)?

Container Debian GNU/Linux 11 (bullseye)

### Reproduction steps

1. Enable feature "Disable metrics"
2. Add Fully qualified Name Entry : jenkins_job_count_history
3. Add Regex entry: vm.*
4. Save

### Expected Results

The expected result is to disable metrics jenkins_job_count_history and all metrics starts with vm_* and to not have them on metrics in the page $jenkins_url/prometheus

### Actual Results

The configuration is saved correctly, but without any results, the metrics are not disabled, and I can still see them on $jenkins_url/prometheus

### Anything else?

I can see that the parameters is taken by the conf file on the jenkins machine:
```
<disabledMetricConfig>
    <entries>
        <org.jenkinsci.plugins.prometheus.config.disabledmetrics.NamedDisabledMetric>
            <metricName>jenkins_job_count_history</metricName>
        </org.jenkinsci.plugins.prometheus.config.dísabledmetrics.NamedDisabledMetric>
        <org.jenkinsci.plugins.prometheus.config.dísabledmetrics.RegexDisabledMetric>
            <regex>vm.*</regex>
        </org.jenkinsci.plugins.prometheus.config.dísabledmetrics.RegexDisabledMetric>
    </entries>
</disabledMetricConfig>
```

Pull request:
Fixes #554. Apply Metric Filter on all external metrics
Fixes #554

### Changes proposed
Filter external metrics

### Checklist

- [x] Includes tests covering the new functionality?
- [x] Ready for review
- [x] Follows CONTRIBUTING rules


Patch:
diff --git a/src/main/java/org/jenkinsci/plugins/prometheus/config/disabledmetrics/MetricStatusChecker.java b/src/main/java/org/jenkinsci/plugins/prometheus/config/disabledmetrics/MetricStatusChecker.java
index cca188f..3f7e4cc 100644
--- a/src/main/java/org/jenkinsci/plugins/prometheus/config/disabledmetrics/MetricStatusChecker.java
+++ b/src/main/java/org/jenkinsci/plugins/prometheus/config/disabledmetrics/MetricStatusChecker.java
@@ -1,12 +1,15 @@
 package org.jenkinsci.plugins.prometheus.config.disabledmetrics;
 
+import io.prometheus.client.Collector;
+import io.prometheus.client.CollectorRegistry;
 import org.jenkinsci.plugins.prometheus.config.PrometheusConfiguration;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
-import java.util.List;
+import java.util.*;
 import java.util.regex.Matcher;
 import java.util.regex.Pattern;
+import java.util.stream.Collectors;
 
 public class MetricStatusChecker {
 
@@ -51,4 +54,11 @@ public class MetricStatusChecker {
         }
         return true;
     }
+
+    public static Set<String> filter(List<String> allMetricNames) {
+        if (allMetricNames == null) {
+            return new HashSet<>();
+        }
+        return allMetricNames.stream().filter(MetricStatusChecker::isEnabled).collect(Collectors.toSet());
+    }
 }
diff --git a/src/main/java/org/jenkinsci/plugins/prometheus/service/DefaultPrometheusMetrics.java b/src/main/java/org/jenkinsci/plugins/prometheus/service/DefaultPrometheusMetrics.java
index 99259f1..2b7935a 100644
--- a/src/main/java/org/jenkinsci/plugins/prometheus/service/DefaultPrometheusMetrics.java
+++ b/src/main/java/org/jenkinsci/plugins/prometheus/service/DefaultPrometheusMetrics.java
@@ -8,12 +8,14 @@ import io.prometheus.client.exporter.common.TextFormat;
 import io.prometheus.client.hotspot.DefaultExports;
 import jenkins.metrics.api.Metrics;
 import org.jenkinsci.plugins.prometheus.*;
+import org.jenkinsci.plugins.prometheus.config.disabledmetrics.MetricStatusChecker;
 import org.jenkinsci.plugins.prometheus.util.JenkinsNodeBuildsSampleBuilder;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
 import java.io.IOException;
 import java.io.StringWriter;
+import java.util.*;
 import java.util.concurrent.atomic.AtomicReference;
 
 public class DefaultPrometheusMetrics implements PrometheusMetrics {
@@ -49,10 +51,23 @@ public class DefaultPrometheusMetrics implements PrometheusMetrics {
     @Override
     public void collectMetrics() {
         try (StringWriter buffer = new StringWriter()) {
-            TextFormat.write004(buffer, collectorRegistry.metricFamilySamples());
+            Set<String> filteredMetrics = MetricStatusChecker.filter(getMetricNames());
+            TextFormat.write004(buffer, collectorRegistry.filteredMetricFamilySamples(filteredMetrics));
             cachedMetrics.set(buffer.toString());
         } catch (IOException e) {
             logger.debug("Unable to collect metrics");
         }
     }
+
+    private List<String> getMetricNames() {
+        Enumeration<Collector.MetricFamilySamples> metricFamilySamplesEnumeration = collectorRegistry.metricFamilySamples();
+        List<String> allMetricNames = new ArrayList<>();
+        while (metricFamilySamplesEnumeration.hasMoreElements()) {
+            Collector.MetricFamilySamples familySamples = metricFamilySamplesEnumeration.nextElement();
+            if (familySamples != null && familySamples.name != null) {
+                allMetricNames.add(familySamples.name);
+            }
+        }
+        return allMetricNames;
+    }
 }


Test patch:
diff --git a/src/test/java/org/jenkinsci/plugins/prometheus/config/disabledmetrics/MetricStatusCheckerTest.java b/src/test/java/org/jenkinsci/plugins/prometheus/config/disabledmetrics/MetricStatusCheckerTest.java
index 46ab18f..bc10896 100644
--- a/src/test/java/org/jenkinsci/plugins/prometheus/config/disabledmetrics/MetricStatusCheckerTest.java
+++ b/src/test/java/org/jenkinsci/plugins/prometheus/config/disabledmetrics/MetricStatusCheckerTest.java
@@ -9,6 +9,7 @@ import org.mockito.MockedStatic;
 
 import java.util.ArrayList;
 import java.util.List;
+import java.util.Set;
 
 import static org.mockito.Mockito.*;
 
@@ -181,6 +182,32 @@ public class MetricStatusCheckerTest {
         }
     }
 
+
+    @Test
+    void testFilterExternalMetricNames() {
+        Jenkins jenkins = mock(Jenkins.class);
+
+        PrometheusConfiguration mockedConfig = mock(PrometheusConfiguration.class);
+        List<Entry> entries = new ArrayList<>();
+        entries.add(new NamedDisabledMetric("other_metric"));
+        entries.add(new RegexDisabledMetric("j?vm.*"));
+        DisabledMetricConfig disabledMetricConfig = new DisabledMetricConfig(entries);
+
+        when(mockedConfig.getDisabledMetricConfig()).thenReturn(disabledMetricConfig);
+
+
+        try (MockedStatic<Jenkins> jenkinsStatic = mockStatic(Jenkins.class);
+             MockedStatic<PrometheusConfiguration> configStatic = mockStatic(PrometheusConfiguration.class)) {
+            jenkinsStatic.when(Jenkins::get).thenReturn(jenkins);
+            configStatic.when(PrometheusConfiguration::get).thenReturn(mockedConfig);
+
+            List<String> allMetrics = List.of("some_metric", "jvm_xxx", "vm_xxx");
+            Set<String> filteredMetrics = MetricStatusChecker.filter(allMetrics);
+            Assertions.assertEquals(1, filteredMetrics.size());
+            Assertions.assertEquals("some_metric", filteredMetrics.stream().findFirst().get());
+        }
+    }
+
     @Test
     // shouldn't take more than 3 seconds
     @Timeout(value = 3L)