diff --git a/simpleclient/src/main/java/io/prometheus/client/SampleNameFilter.java b/simpleclient/src/main/java/io/prometheus/client/SampleNameFilter.java index 180dd56c4..1992073fa 100644 --- a/simpleclient/src/main/java/io/prometheus/client/SampleNameFilter.java +++ b/simpleclient/src/main/java/io/prometheus/client/SampleNameFilter.java @@ -22,13 +22,17 @@ public class SampleNameFilter implements Predicate { private final Collection nameIsNotEqualTo; private final Collection nameStartsWith; private final Collection nameDoesNotStartWith; + private final Collection nameEndsWith; + private final Collection nameDoesNotEndWith; @Override public boolean test(String sampleName) { return matchesNameEqualTo(sampleName) - && !matchesNameNotEqualTo(sampleName) + && matchesNameNotEqualTo(sampleName) && matchesNameStartsWith(sampleName) - && !matchesNameDoesNotStartWith(sampleName); + && matchesNameDoesNotStartWith(sampleName) + && matchesNameEndsWith(sampleName) + && matchesNameDoesNotEndWith(sampleName); } /** @@ -55,9 +59,9 @@ private boolean matchesNameEqualTo(String metricName) { private boolean matchesNameNotEqualTo(String metricName) { if (nameIsNotEqualTo.isEmpty()) { - return false; + return true; } - return nameIsNotEqualTo.contains(metricName); + return !nameIsNotEqualTo.contains(metricName); } private boolean matchesNameStartsWith(String metricName) { @@ -74,22 +78,48 @@ private boolean matchesNameStartsWith(String metricName) { private boolean matchesNameDoesNotStartWith(String metricName) { if (nameDoesNotStartWith.isEmpty()) { - return false; + return true; } for (String prefix : nameDoesNotStartWith) { if (metricName.startsWith(prefix)) { + return false; + } + } + return true; + } + + private boolean matchesNameEndsWith(String metricName) { + if (nameEndsWith.isEmpty()) { + return true; + } + for (String suffix : nameEndsWith) { + if (metricName.endsWith(suffix)) { return true; } } return false; } + private boolean matchesNameDoesNotEndWith(String metricName) { + if (nameDoesNotEndWith.isEmpty()) { + return true; + } + for (String suffix : nameDoesNotEndWith) { + if (metricName.endsWith(suffix)) { + return false; + } + } + return true; + } + public static class Builder { private final Collection nameEqualTo = new ArrayList(); private final Collection nameNotEqualTo = new ArrayList(); private final Collection nameStartsWith = new ArrayList(); private final Collection nameDoesNotStartWith = new ArrayList(); + private final Collection nameEndsWith = new ArrayList(); + private final Collection nameDoesNotEndWith = new ArrayList(); /** * @see #nameMustBeEqualTo(Collection) @@ -167,16 +197,54 @@ public Builder nameMustNotStartWith(Collection prefixes) { return this; } + /** + * @see #nameMustEndWith(Collection) + */ + public Builder nameMustEndWith(String... prefixes) { + return nameMustEndWith(Arrays.asList(prefixes)); + } + + /** + * Only samples whose name ends with one of the {@code prefixes} will be included. + * @param prefixes empty means no restriction. + */ + public Builder nameMustEndWith(Collection prefixes) { + nameEndsWith.addAll(prefixes); + return this; + } + + /** + * @see #nameMustNotEndWith(Collection) + */ + public Builder nameMustNotEndWith(String... prefixes) { + return nameMustNotEndWith(Arrays.asList(prefixes)); + } + + /** + * Samples with names ending with one of the {@code prefixes} will be excluded. + * For example, this can be used to exclude {@code _created} metrics. + * @param prefixes empty means no time series will be excluded. + */ + public Builder nameMustNotEndWith(Collection prefixes) { + nameDoesNotEndWith.addAll(prefixes); + return this; + } + public SampleNameFilter build() { - return new SampleNameFilter(nameEqualTo, nameNotEqualTo, nameStartsWith, nameDoesNotStartWith); + return new SampleNameFilter(nameEqualTo, nameNotEqualTo, nameStartsWith, nameDoesNotStartWith, + nameEndsWith, nameDoesNotEndWith); } } - private SampleNameFilter(Collection nameIsEqualTo, Collection nameIsNotEqualTo, Collection nameStartsWith, Collection nameDoesNotStartWith) { + private SampleNameFilter(Collection nameIsEqualTo, Collection nameIsNotEqualTo, + Collection nameStartsWith, Collection nameDoesNotStartWith, + Collection nameEndsWith, Collection nameDoesNotEndWith) { this.nameIsEqualTo = unmodifiableCollection(nameIsEqualTo); this.nameIsNotEqualTo = unmodifiableCollection(nameIsNotEqualTo); this.nameStartsWith = unmodifiableCollection(nameStartsWith); this.nameDoesNotStartWith = unmodifiableCollection(nameDoesNotStartWith); + this.nameEndsWith = unmodifiableCollection(nameEndsWith); + this.nameDoesNotEndWith = unmodifiableCollection(nameDoesNotEndWith); } private static class AllowAll implements Predicate { diff --git a/simpleclient_httpserver/src/test/java/io/prometheus/client/exporter/TestHTTPServer.java b/simpleclient_httpserver/src/test/java/io/prometheus/client/exporter/TestHTTPServer.java index 2b18f4e82..1d3142b97 100644 --- a/simpleclient_httpserver/src/test/java/io/prometheus/client/exporter/TestHTTPServer.java +++ b/simpleclient_httpserver/src/test/java/io/prometheus/client/exporter/TestHTTPServer.java @@ -6,6 +6,7 @@ import com.sun.net.httpserver.HttpsConfigurator; import com.sun.net.httpserver.HttpsParameters; import io.prometheus.client.CollectorRegistry; +import io.prometheus.client.Counter; import io.prometheus.client.Gauge; import io.prometheus.client.SampleNameFilter; import org.junit.Assert; @@ -191,6 +192,45 @@ public void testSampleNameFilter() throws IOException { } } + @Test + public void testCreatedMetricPresent() throws IOException { + Counter counter = Counter.build("my_counter", "my counter help").register(registry); + HTTPServer httpServer = new HTTPServer.Builder() + .withRegistry(registry) + .build(); + try { + String body = createHttpRequestBuilder(httpServer, "/metrics") + .withHeader("Accept", "application/openmetrics-text; version=0.0.1,text/plain;version=0.0.4;q=0.5,*/*;q=0.1") + .build().execute().getBody(); + assertThat(body).contains("my_counter_total 0.0"); + assertThat(body).contains("my_counter_created "); + } finally { + registry.unregister(counter); + httpServer.close(); + } + } + + @Test + public void testCreatedMetricRemoved() throws IOException { + Counter counter = Counter.build("my_counter", "my counter help").register(registry); + HTTPServer httpServer = new HTTPServer.Builder() + .withRegistry(registry) + .withSampleNameFilter(new SampleNameFilter.Builder() + .nameMustNotEndWith("_created") + .build()) + .build(); + try { + String body = createHttpRequestBuilder(httpServer, "/metrics") + .withHeader("Accept", "application/openmetrics-text; version=0.0.1,text/plain;version=0.0.4;q=0.5,*/*;q=0.1") + .build().execute().getBody(); + assertThat(body).contains("my_counter_total 0.0"); + assertThat(body).doesNotContain("my_counter_created "); + } finally { + registry.unregister(counter); + httpServer.close(); + } + } + @Test public void testSampleNameFilterEmptyBody() throws IOException { HTTPServer httpServer = new HTTPServer.Builder() diff --git a/simpleclient_servlet_common/src/main/java/io/prometheus/client/servlet/common/exporter/Exporter.java b/simpleclient_servlet_common/src/main/java/io/prometheus/client/servlet/common/exporter/Exporter.java index 8a76dd218..e482d527f 100644 --- a/simpleclient_servlet_common/src/main/java/io/prometheus/client/servlet/common/exporter/Exporter.java +++ b/simpleclient_servlet_common/src/main/java/io/prometheus/client/servlet/common/exporter/Exporter.java @@ -26,6 +26,8 @@ public class Exporter { public static final String NAME_MUST_NOT_BE_EQUAL_TO = "name-must-not-be-equal-to"; public static final String NAME_MUST_START_WITH = "name-must-start-with"; public static final String NAME_MUST_NOT_START_WITH = "name-must-not-start-with"; + public static final String NAME_MUST_END_WITH = "name-must-end-with"; + public static final String NAME_MUST_NOT_END_WITH = "name-must-not-end-with"; private CollectorRegistry registry; private Predicate sampleNameFilter; @@ -48,12 +50,16 @@ public void init(ServletConfigAdapter servletConfig) throws ServletConfiguration List excludedNames = SampleNameFilter.stringToList(servletConfig.getInitParameter(NAME_MUST_NOT_BE_EQUAL_TO)); List allowedPrefixes = SampleNameFilter.stringToList(servletConfig.getInitParameter(NAME_MUST_START_WITH)); List excludedPrefixes = SampleNameFilter.stringToList(servletConfig.getInitParameter(NAME_MUST_NOT_START_WITH)); + List allowedSuffixes = SampleNameFilter.stringToList(servletConfig.getInitParameter(NAME_MUST_END_WITH)); + List excludedSuffixes = SampleNameFilter.stringToList(servletConfig.getInitParameter(NAME_MUST_NOT_END_WITH)); if (!allowedPrefixes.isEmpty() || !excludedPrefixes.isEmpty() || !allowedNames.isEmpty() || !excludedNames.isEmpty()) { SampleNameFilter filter = new SampleNameFilter.Builder() .nameMustBeEqualTo(allowedNames) .nameMustNotBeEqualTo(excludedNames) .nameMustStartWith(allowedPrefixes) .nameMustNotStartWith(excludedPrefixes) + .nameMustEndWith(allowedSuffixes) + .nameMustNotEndWith(excludedSuffixes) .build(); if (this.sampleNameFilter != null) { this.sampleNameFilter = filter.and(this.sampleNameFilter);