diff --git a/docs/content/getting-started/multi-target.md b/docs/content/getting-started/multi-target.md new file mode 100644 index 000000000..6a1c0412c --- /dev/null +++ b/docs/content/getting-started/multi-target.md @@ -0,0 +1,114 @@ +--- +title: Multi-Target Pattern +weight: 7 +--- + +{{< hint type=note >}} +This is for the upcoming release 1.1.0. +{{< /hint >}} + +To support multi-target pattern you can create a custom collector overriding the purposed internal method in ExtendedMultiCollector +see SampleExtendedMultiCollector in io.prometheus.metrics.examples.httpserver + +```java +public class SampleExtendedMultiCollector extends ExtendedMultiCollector { + + public SampleExtendedMultiCollector() { + super(); + } + + @Override + protected MetricSnapshots collectMetricSnapshots(PrometheusScrapeRequest scrapeRequest) { + + GaugeSnapshot.Builder gaugeBuilder = GaugeSnapshot.builder(); + gaugeBuilder.name("x_load").help("process load"); + + CounterSnapshot.Builder counterBuilder = CounterSnapshot.builder(); + counterBuilder.name(PrometheusNaming.sanitizeMetricName("x_calls_total")).help("invocations"); + + String[] targetNames = scrapeRequest.getParameterValues("target"); + String targetName; + String[] procs = scrapeRequest.getParameterValues("proc"); + if (targetNames == null || targetNames.length == 0) { + targetName = "defaultTarget"; + procs = null; //ignore procs param + } else { + targetName = targetNames[0]; + } + Builder counterDataPointBuilder = CounterSnapshot.CounterDataPointSnapshot.builder(); + io.prometheus.metrics.model.snapshots.GaugeSnapshot.GaugeDataPointSnapshot.Builder gaugeDataPointBuilder = GaugeSnapshot.GaugeDataPointSnapshot.builder(); + Labels lbls = Labels.of("target", targetName); + + if (procs == null || procs.length == 0) { + counterDataPointBuilder.labels(lbls.merge(Labels.of("proc", "defaultProc"))); + gaugeDataPointBuilder.labels(lbls.merge(Labels.of("proc", "defaultProc"))); + counterDataPointBuilder.value(70); + gaugeDataPointBuilder.value(Math.random()); + + counterBuilder.dataPoint(counterDataPointBuilder.build()); + gaugeBuilder.dataPoint(gaugeDataPointBuilder.build()); + + } else { + for (int i = 0; i < procs.length; i++) { + counterDataPointBuilder.labels(lbls.merge(Labels.of("proc", procs[i]))); + gaugeDataPointBuilder.labels(lbls.merge(Labels.of("proc", procs[i]))); + counterDataPointBuilder.value(Math.random()); + gaugeDataPointBuilder.value(Math.random()); + + counterBuilder.dataPoint(counterDataPointBuilder.build()); + gaugeBuilder.dataPoint(gaugeDataPointBuilder.build()); + } + } + Collection snaps = new ArrayList(); + snaps.add(counterBuilder.build()); + snaps.add(gaugeBuilder.build()); + MetricSnapshots msnaps = new MetricSnapshots(snaps); + return msnaps; + } + + public List getPrometheusNames() { + List names = new ArrayList(); + names.add("x_calls_total"); + names.add("x_load"); + return names; + } + +} + +``` +`PrometheusScrapeRequest` provides methods to access http-related infos from the request originally received by the endpoint + +```java +public interface PrometheusScrapeRequest { + String getRequestURI(); + + String[] getParameterValues(String name); +} + +``` + + +Sample Prometheus scrape_config + +``` + - job_name: "multi-target" + + # metrics_path defaults to '/metrics' + # scheme defaults to 'http'. + params: + proc: [proc1, proc2] + relabel_configs: + - source_labels: [__address__] + target_label: __param_target + - source_labels: [__param_target] + target_label: instance + - target_label: __address__ + replacement: localhost:9401 + static_configs: + - targets: ["target1", "target2"] +``` +It's up to the specific MultiCollector implementation how to interpret the _target_ parameter. +It might be an explicit real target (i.e. via host name/ip address) or as an alias in some internal configuration. +The latter is more suitable when the MultiCollector implementation is a proxy (see https://github.com/prometheus/snmp_exporter) +In this case, invoking real target might require extra parameters (e.g. credentials) that might be complex to manage in Prometheus configuration +(not considering the case where the proxy might become an "open relay") \ No newline at end of file diff --git a/examples/example-exporter-multi-target/README.md b/examples/example-exporter-multi-target/README.md new file mode 100644 index 000000000..b21f9ce9e --- /dev/null +++ b/examples/example-exporter-multi-target/README.md @@ -0,0 +1,33 @@ +# Multi-Target pattern example + +## Build + +This example is built as part of the `client_java` project. + +``` +./mvnw package +``` + +## Run + +The build creates a JAR file with the example application in `./examples/example-exporter-multi-target/target/`. + +``` +java -jar ./examples/example-exporter-multi-target/target/example-exporter-multi-target.jar +``` + +## Manually testing the Metrics Endpoint + +Accessing [http://localhost:9400/metrics](http://localhost:9400/metrics) with a Web browser should yield an example of a counter metric. + +``` +# HELP uptime_seconds_total total number of seconds since this application was started +# TYPE uptime_seconds_total counter +uptime_seconds_total 301.0 +``` + +The exporter supports a `debug` URL parameter to quickly view other formats in your Web browser: + +* [http://localhost:9400/metrics?debug=text](http://localhost:9400/metrics?debug=text): Prometheus text format, same as without the `debug` option. +* [http://localhost:9400/metrics?debug=openmetrics](http://localhost:9400/metrics?debug=openmetrics): OpenMetrics text format. +* [http://localhost:9400/metrics?debug=prometheus-protobuf](http://localhost:9400/metrics?debug=prometheus-protobuf): Text representation of the Prometheus protobuf format. diff --git a/examples/example-exporter-multi-target/pom.xml b/examples/example-exporter-multi-target/pom.xml new file mode 100644 index 000000000..fa9d1a94a --- /dev/null +++ b/examples/example-exporter-multi-target/pom.xml @@ -0,0 +1,76 @@ + + + 4.0.0 + + + io.prometheus + examples + 1.1.0-SNAPSHOT + + + example-exporter-multi-target + + Example - HTTPServer Exporter Multi Target + + Prometheus Metrics Example for multi-target pattern implementation + + + + + The Apache Software License, Version 2.0 + http://www.apache.org/licenses/LICENSE-2.0.txt + repo + + + + + + fstab + Guido Anzuoni + ganzuoni@gmail.com + + + + + + io.prometheus + prometheus-metrics-core + ${project.version} + + + io.prometheus + prometheus-metrics-instrumentation-jvm + ${project.version} + + + io.prometheus + prometheus-metrics-exporter-httpserver + ${project.version} + + + + + ${project.artifactId} + + + org.apache.maven.plugins + maven-shade-plugin + + + package + + shade + + + + + io.prometheus.metrics.examples.multitarget.Main + + + + + + + + + diff --git a/examples/example-exporter-multi-target/src/main/java/io/prometheus/metrics/examples/multitarget/Main.java b/examples/example-exporter-multi-target/src/main/java/io/prometheus/metrics/examples/multitarget/Main.java new file mode 100644 index 000000000..da36346b9 --- /dev/null +++ b/examples/example-exporter-multi-target/src/main/java/io/prometheus/metrics/examples/multitarget/Main.java @@ -0,0 +1,23 @@ +package io.prometheus.metrics.examples.multitarget; + +import java.io.IOException; + +import io.prometheus.metrics.exporter.httpserver.HTTPServer; +import io.prometheus.metrics.model.registry.PrometheusRegistry; + +/** + * Simple example of an application exposing metrics via Prometheus' built-in HTTPServer. + */ +public class Main { + + public static void main(String[] args) throws IOException, InterruptedException { + + SampleMultiCollector xmc = new SampleMultiCollector(); + PrometheusRegistry.defaultRegistry.register(xmc); + HTTPServer server = HTTPServer.builder() + .port(9401) + .buildAndStart(); + + System.out.println("HTTPServer listening on port http://localhost:" + server.getPort() + "/metrics"); + } +} diff --git a/examples/example-exporter-multi-target/src/main/java/io/prometheus/metrics/examples/multitarget/SampleMultiCollector.java b/examples/example-exporter-multi-target/src/main/java/io/prometheus/metrics/examples/multitarget/SampleMultiCollector.java new file mode 100644 index 000000000..819bb3028 --- /dev/null +++ b/examples/example-exporter-multi-target/src/main/java/io/prometheus/metrics/examples/multitarget/SampleMultiCollector.java @@ -0,0 +1,88 @@ +package io.prometheus.metrics.examples.multitarget; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; + +import io.prometheus.metrics.model.registry.MultiCollector; +import io.prometheus.metrics.model.registry.PrometheusScrapeRequest; +import io.prometheus.metrics.model.snapshots.CounterSnapshot; +import io.prometheus.metrics.model.snapshots.CounterSnapshot.CounterDataPointSnapshot.Builder; +import io.prometheus.metrics.model.snapshots.GaugeSnapshot; +import io.prometheus.metrics.model.snapshots.Labels; +import io.prometheus.metrics.model.snapshots.MetricSnapshot; +import io.prometheus.metrics.model.snapshots.MetricSnapshots; +import io.prometheus.metrics.model.snapshots.PrometheusNaming; + +public class SampleMultiCollector implements MultiCollector { + + public SampleMultiCollector() { + super(); + } + + @Override + public MetricSnapshots collect() { + return new MetricSnapshots(); + } + + @Override + public MetricSnapshots collect(PrometheusScrapeRequest scrapeRequest) { + return collectMetricSnapshots(scrapeRequest); + } + + protected MetricSnapshots collectMetricSnapshots(PrometheusScrapeRequest scrapeRequest) { + + GaugeSnapshot.Builder gaugeBuilder = GaugeSnapshot.builder(); + gaugeBuilder.name("x_load").help("process load"); + + CounterSnapshot.Builder counterBuilder = CounterSnapshot.builder(); + counterBuilder.name(PrometheusNaming.sanitizeMetricName("x_calls_total")).help("invocations"); + + String[] targetNames = scrapeRequest.getParameterValues("target"); + String targetName; + String[] procs = scrapeRequest.getParameterValues("proc"); + if (targetNames == null || targetNames.length == 0) { + targetName = "defaultTarget"; + procs = null; //ignore procs param + } else { + targetName = targetNames[0]; + } + Builder counterDataPointBuilder = CounterSnapshot.CounterDataPointSnapshot.builder(); + io.prometheus.metrics.model.snapshots.GaugeSnapshot.GaugeDataPointSnapshot.Builder gaugeDataPointBuilder = GaugeSnapshot.GaugeDataPointSnapshot.builder(); + Labels lbls = Labels.of("target", targetName); + + if (procs == null || procs.length == 0) { + counterDataPointBuilder.labels(lbls.merge(Labels.of("proc", "defaultProc"))); + gaugeDataPointBuilder.labels(lbls.merge(Labels.of("proc", "defaultProc"))); + counterDataPointBuilder.value(70); + gaugeDataPointBuilder.value(Math.random()); + + counterBuilder.dataPoint(counterDataPointBuilder.build()); + gaugeBuilder.dataPoint(gaugeDataPointBuilder.build()); + + } else { + for (int i = 0; i < procs.length; i++) { + counterDataPointBuilder.labels(lbls.merge(Labels.of("proc", procs[i]))); + gaugeDataPointBuilder.labels(lbls.merge(Labels.of("proc", procs[i]))); + counterDataPointBuilder.value(Math.random()); + gaugeDataPointBuilder.value(Math.random()); + + counterBuilder.dataPoint(counterDataPointBuilder.build()); + gaugeBuilder.dataPoint(gaugeDataPointBuilder.build()); + } + } + Collection snaps = new ArrayList(); + snaps.add(counterBuilder.build()); + snaps.add(gaugeBuilder.build()); + MetricSnapshots msnaps = new MetricSnapshots(snaps); + return msnaps; + } + + public List getPrometheusNames() { + List names = new ArrayList(); + names.add("x_calls_total"); + names.add("x_load"); + return names; + } + +} diff --git a/examples/pom.xml b/examples/pom.xml index 0b293cedc..61db27a97 100644 --- a/examples/pom.xml +++ b/examples/pom.xml @@ -36,6 +36,7 @@ example-exemplars-tail-sampling example-exporter-servlet-tomcat example-exporter-httpserver + example-exporter-multi-target example-exporter-opentelemetry example-simpleclient-bridge example-native-histogram diff --git a/prometheus-metrics-exporter-common/src/main/java/io/prometheus/metrics/exporter/common/PrometheusHttpRequest.java b/prometheus-metrics-exporter-common/src/main/java/io/prometheus/metrics/exporter/common/PrometheusHttpRequest.java index b855ce0dc..f7b5346a5 100644 --- a/prometheus-metrics-exporter-common/src/main/java/io/prometheus/metrics/exporter/common/PrometheusHttpRequest.java +++ b/prometheus-metrics-exporter-common/src/main/java/io/prometheus/metrics/exporter/common/PrometheusHttpRequest.java @@ -5,7 +5,9 @@ import java.util.ArrayList; import java.util.Enumeration; -public interface PrometheusHttpRequest { +import io.prometheus.metrics.model.registry.PrometheusScrapeRequest; + +public interface PrometheusHttpRequest extends PrometheusScrapeRequest { /** * See {@code jakarta.servlet.http.HttpServletRequest.getQueryString()} diff --git a/prometheus-metrics-exporter-common/src/main/java/io/prometheus/metrics/exporter/common/PrometheusScrapeHandler.java b/prometheus-metrics-exporter-common/src/main/java/io/prometheus/metrics/exporter/common/PrometheusScrapeHandler.java index 6fae1218e..5155457df 100644 --- a/prometheus-metrics-exporter-common/src/main/java/io/prometheus/metrics/exporter/common/PrometheusScrapeHandler.java +++ b/prometheus-metrics-exporter-common/src/main/java/io/prometheus/metrics/exporter/common/PrometheusScrapeHandler.java @@ -104,11 +104,12 @@ private Predicate makeNameFilter(ExporterFilterProperties props) { } private MetricSnapshots scrape(PrometheusHttpRequest request) { + Predicate filter = makeNameFilter(request.getParameterValues("name[]")); if (filter != null) { - return registry.scrape(filter); + return registry.scrape(filter, request); } else { - return registry.scrape(); + return registry.scrape(request); } } diff --git a/prometheus-metrics-exporter-httpserver/src/main/java/io/prometheus/metrics/exporter/httpserver/HttpExchangeAdapter.java b/prometheus-metrics-exporter-httpserver/src/main/java/io/prometheus/metrics/exporter/httpserver/HttpExchangeAdapter.java index 4f1b4f63a..daaeb2dbd 100644 --- a/prometheus-metrics-exporter-httpserver/src/main/java/io/prometheus/metrics/exporter/httpserver/HttpExchangeAdapter.java +++ b/prometheus-metrics-exporter-httpserver/src/main/java/io/prometheus/metrics/exporter/httpserver/HttpExchangeAdapter.java @@ -9,6 +9,7 @@ import java.io.OutputStream; import java.io.PrintWriter; import java.io.StringWriter; +import java.net.URI; import java.nio.charset.StandardCharsets; import java.util.Collections; import java.util.Enumeration; @@ -29,7 +30,7 @@ public HttpExchangeAdapter(HttpExchange httpExchange) { public class HttpRequest implements PrometheusHttpRequest { - @Override + @Override public String getQueryString() { return httpExchange.getRequestURI().getRawQuery(); } diff --git a/prometheus-metrics-exporter-servlet-jakarta/src/main/java/io/prometheus/metrics/exporter/servlet/jakarta/HttpExchangeAdapter.java b/prometheus-metrics-exporter-servlet-jakarta/src/main/java/io/prometheus/metrics/exporter/servlet/jakarta/HttpExchangeAdapter.java index 656d661eb..c55eb7967 100644 --- a/prometheus-metrics-exporter-servlet-jakarta/src/main/java/io/prometheus/metrics/exporter/servlet/jakarta/HttpExchangeAdapter.java +++ b/prometheus-metrics-exporter-servlet-jakarta/src/main/java/io/prometheus/metrics/exporter/servlet/jakarta/HttpExchangeAdapter.java @@ -53,7 +53,7 @@ public Request(HttpServletRequest request) { this.request = request; } - @Override + @Override public String getQueryString() { return request.getQueryString(); } diff --git a/prometheus-metrics-model/src/main/java/io/prometheus/metrics/model/registry/Collector.java b/prometheus-metrics-model/src/main/java/io/prometheus/metrics/model/registry/Collector.java index d180eda63..0c69a89a9 100644 --- a/prometheus-metrics-model/src/main/java/io/prometheus/metrics/model/registry/Collector.java +++ b/prometheus-metrics-model/src/main/java/io/prometheus/metrics/model/registry/Collector.java @@ -19,6 +19,14 @@ public interface Collector { */ MetricSnapshot collect(); + /** + * Provides Collector with the details of the request issued by Prometheus to allow multi-target pattern implementation + * Override to implement request dependent logic to provide MetricSnapshot + */ + default MetricSnapshot collect(PrometheusScrapeRequest scrapeRequest) { + return collect(); + } + /** * Like {@link #collect()}, but returns {@code null} if {@code includedNames.test(name)} is {@code false}. *

@@ -32,6 +40,21 @@ default MetricSnapshot collect(Predicate includedNames) { return null; } } + + /** + * Like {@link #collect(Predicate)}, but with support for multi-target pattern. + *

+ * Override this if there is a more efficient way than first collecting the snapshot and then discarding it. + */ + default MetricSnapshot collect(Predicate includedNames, PrometheusScrapeRequest scrapeRequest) { + MetricSnapshot result = collect(scrapeRequest); + if (includedNames.test(result.getMetadata().getPrometheusName())) { + return result; + } else { + return null; + } + } + /** * This is called in two places: diff --git a/prometheus-metrics-model/src/main/java/io/prometheus/metrics/model/registry/MultiCollector.java b/prometheus-metrics-model/src/main/java/io/prometheus/metrics/model/registry/MultiCollector.java index 1edb5cfc6..5434c0ec0 100644 --- a/prometheus-metrics-model/src/main/java/io/prometheus/metrics/model/registry/MultiCollector.java +++ b/prometheus-metrics-model/src/main/java/io/prometheus/metrics/model/registry/MultiCollector.java @@ -18,13 +18,31 @@ public interface MultiCollector { */ MetricSnapshots collect(); + /** + * Provides Collector with the details of the request issued by Prometheus to allow multi-target pattern implementation + * Override to implement request dependent logic to provide MetricSnapshot + */ + default MetricSnapshots collect(PrometheusScrapeRequest scrapeRequest) { + return collect(); + } + + /** * Like {@link #collect()}, but returns only the snapshots where {@code includedNames.test(name)} is {@code true}. *

* Override this if there is a more efficient way than first collecting all snapshot and then discarding the excluded ones. */ default MetricSnapshots collect(Predicate includedNames) { - MetricSnapshots allSnapshots = collect(); + return collect(includedNames, null); + } + + /** + * Like {@link #collect(Predicate)}, but with support for multi-target pattern. + *

+ * Override this if there is a more efficient way than first collecting the snapshot and then discarding it. + */ + default MetricSnapshots collect(Predicate includedNames, PrometheusScrapeRequest scrapeRequest) { + MetricSnapshots allSnapshots = scrapeRequest == null ? collect(): collect(scrapeRequest); MetricSnapshots.Builder result = MetricSnapshots.builder(); for (MetricSnapshot snapshot : allSnapshots) { if (includedNames.test(snapshot.getMetadata().getPrometheusName())) { @@ -34,6 +52,7 @@ default MetricSnapshots collect(Predicate includedNames) { return result.build(); } + /** * This is called in two places: *

    diff --git a/prometheus-metrics-model/src/main/java/io/prometheus/metrics/model/registry/PrometheusRegistry.java b/prometheus-metrics-model/src/main/java/io/prometheus/metrics/model/registry/PrometheusRegistry.java index 0fe565947..a8ab5a762 100644 --- a/prometheus-metrics-model/src/main/java/io/prometheus/metrics/model/registry/PrometheusRegistry.java +++ b/prometheus-metrics-model/src/main/java/io/prometheus/metrics/model/registry/PrometheusRegistry.java @@ -1,7 +1,6 @@ package io.prometheus.metrics.model.registry; -import io.prometheus.metrics.model.snapshots.MetricSnapshot; -import io.prometheus.metrics.model.snapshots.MetricSnapshots; +import static io.prometheus.metrics.model.snapshots.PrometheusNaming.prometheusName; import java.util.List; import java.util.Set; @@ -9,104 +8,119 @@ import java.util.concurrent.CopyOnWriteArrayList; import java.util.function.Predicate; -import static io.prometheus.metrics.model.snapshots.PrometheusNaming.prometheusName; +import io.prometheus.metrics.model.snapshots.MetricSnapshot; +import io.prometheus.metrics.model.snapshots.MetricSnapshots; public class PrometheusRegistry { - public static final PrometheusRegistry defaultRegistry = new PrometheusRegistry(); + public static final PrometheusRegistry defaultRegistry = new PrometheusRegistry(); + + private final Set prometheusNames = ConcurrentHashMap.newKeySet(); + private final List collectors = new CopyOnWriteArrayList<>(); + private final List multiCollectors = new CopyOnWriteArrayList<>(); + + public void register(Collector collector) { + String prometheusName = collector.getPrometheusName(); + if (prometheusName != null) { + if (!prometheusNames.add(prometheusName)) { + throw new IllegalStateException("Can't register " + prometheusName + " because a metric with that name is already registered."); + } + } + collectors.add(collector); + } - private final Set prometheusNames = ConcurrentHashMap.newKeySet(); - private final List collectors = new CopyOnWriteArrayList<>(); - private final List multiCollectors = new CopyOnWriteArrayList<>(); + public void register(MultiCollector collector) { + for (String prometheusName : collector.getPrometheusNames()) { + if (!prometheusNames.add(prometheusName)) { + throw new IllegalStateException("Can't register " + prometheusName + " because that name is already registered."); + } + } + multiCollectors.add(collector); + } - public void register(Collector collector) { - String prometheusName = collector.getPrometheusName(); - if (prometheusName != null) { - if (!prometheusNames.add(prometheusName)) { - throw new IllegalStateException("Can't register " + prometheusName + " because a metric with that name is already registered."); - } - } - collectors.add(collector); - } + public void unregister(Collector collector) { + collectors.remove(collector); + String prometheusName = collector.getPrometheusName(); + if (prometheusName != null) { + prometheusNames.remove(collector.getPrometheusName()); + } + } - public void register(MultiCollector collector) { - for (String prometheusName : collector.getPrometheusNames()) { - if (!prometheusNames.add(prometheusName)) { - throw new IllegalStateException("Can't register " + prometheusName + " because that name is already registered."); - } - } - multiCollectors.add(collector); - } + public void unregister(MultiCollector collector) { + multiCollectors.remove(collector); + for (String prometheusName : collector.getPrometheusNames()) { + prometheusNames.remove(prometheusName(prometheusName)); + } + } - public void unregister(Collector collector) { - collectors.remove(collector); - String prometheusName = collector.getPrometheusName(); - if (prometheusName != null) { - prometheusNames.remove(collector.getPrometheusName()); - } - } + public MetricSnapshots scrape() { + return scrape((PrometheusScrapeRequest) null); + } - public void unregister(MultiCollector collector) { - multiCollectors.remove(collector); - for (String prometheusName : collector.getPrometheusNames()) { - prometheusNames.remove(prometheusName(prometheusName)); - } - } + public MetricSnapshots scrape(PrometheusScrapeRequest scrapeRequest) { + MetricSnapshots.Builder result = MetricSnapshots.builder(); + for (Collector collector : collectors) { + MetricSnapshot snapshot = scrapeRequest == null ? collector.collect() : collector.collect(scrapeRequest); + if (snapshot != null) { + if (result.containsMetricName(snapshot.getMetadata().getName())) { + throw new IllegalStateException(snapshot.getMetadata().getPrometheusName() + ": duplicate metric name."); + } + result.metricSnapshot(snapshot); + } + } + for (MultiCollector collector : multiCollectors) { + MetricSnapshots snaphots = scrapeRequest == null ? collector.collect() : collector.collect(scrapeRequest); + for (MetricSnapshot snapshot : snaphots) { + if (result.containsMetricName(snapshot.getMetadata().getName())) { + throw new IllegalStateException(snapshot.getMetadata().getPrometheusName() + ": duplicate metric name."); + } + result.metricSnapshot(snapshot); + } + } + return result.build(); + } - public MetricSnapshots scrape() { - MetricSnapshots.Builder result = MetricSnapshots.builder(); - for (Collector collector : collectors) { - MetricSnapshot snapshot = collector.collect(); - if (snapshot != null) { - if (result.containsMetricName(snapshot.getMetadata().getName())) { - throw new IllegalStateException(snapshot.getMetadata().getPrometheusName() + ": duplicate metric name."); - } - result.metricSnapshot(snapshot); - } - } - for (MultiCollector collector : multiCollectors) { - for (MetricSnapshot snapshot : collector.collect()) { - if (result.containsMetricName(snapshot.getMetadata().getName())) { - throw new IllegalStateException(snapshot.getMetadata().getPrometheusName() + ": duplicate metric name."); - } - result.metricSnapshot(snapshot); - } - } - return result.build(); - } + public MetricSnapshots scrape(Predicate includedNames) { + if (includedNames == null) { + return scrape(); + } + return scrape(includedNames, null); + } - public MetricSnapshots scrape(Predicate includedNames) { - if (includedNames == null) { - return scrape(); - } - MetricSnapshots.Builder result = MetricSnapshots.builder(); - for (Collector collector : collectors) { - String prometheusName = collector.getPrometheusName(); - if (prometheusName == null || includedNames.test(prometheusName)) { - MetricSnapshot snapshot = collector.collect(includedNames); - if (snapshot != null) { - result.metricSnapshot(snapshot); - } - } - } - for (MultiCollector collector : multiCollectors) { - List prometheusNames = collector.getPrometheusNames(); - boolean excluded = prometheusNames.size() > 0; // the multi-collector is excluded unless at least one name matches - for (String prometheusName : prometheusNames) { - if (includedNames.test(prometheusName)) { - excluded = false; - break; - } - } - if (!excluded) { - for (MetricSnapshot snapshot : collector.collect(includedNames)) { - if (snapshot != null) { - result.metricSnapshot(snapshot); - } - } - } - } - return result.build(); - } + public MetricSnapshots scrape(Predicate includedNames, PrometheusScrapeRequest scrapeRequest) { + if (includedNames == null) { + return scrape(scrapeRequest); + } + MetricSnapshots.Builder result = MetricSnapshots.builder(); + for (Collector collector : collectors) { + String prometheusName = collector.getPrometheusName(); + if (prometheusName == null || includedNames.test(prometheusName)) { + MetricSnapshot snapshot = scrapeRequest == null ? collector.collect(includedNames) : collector.collect(includedNames, scrapeRequest); + if (snapshot != null) { + result.metricSnapshot(snapshot); + } + } + } + for (MultiCollector collector : multiCollectors) { + List prometheusNames = collector.getPrometheusNames(); + boolean excluded = true; // the multi-collector is excluded unless + // at least one name matches + for (String prometheusName : prometheusNames) { + if (includedNames.test(prometheusName)) { + excluded = false; + break; + } + } + if (!excluded) { + MetricSnapshots snaphots = scrapeRequest == null ? collector.collect(includedNames) : collector.collect(includedNames, scrapeRequest); + for (MetricSnapshot snapshot : snaphots) { + if (snapshot != null) { + result.metricSnapshot(snapshot); + } + } + } + } + return result.build(); + } } diff --git a/prometheus-metrics-model/src/main/java/io/prometheus/metrics/model/registry/PrometheusScrapeRequest.java b/prometheus-metrics-model/src/main/java/io/prometheus/metrics/model/registry/PrometheusScrapeRequest.java new file mode 100644 index 000000000..268a326e1 --- /dev/null +++ b/prometheus-metrics-model/src/main/java/io/prometheus/metrics/model/registry/PrometheusScrapeRequest.java @@ -0,0 +1,11 @@ +package io.prometheus.metrics.model.registry; + +/** + * Infos extracted from the request received by the endpoint + * + */ +public interface PrometheusScrapeRequest { + + String[] getParameterValues(String name); + +}