44import io .prometheus .client .exemplars .Exemplar ;
55
66import java .util .ArrayList ;
7+ import java .util .Collections ;
78import java .util .List ;
89import java .util .regex .Pattern ;
910
1718 * @see <a href="http://prometheus.io/docs/instrumenting/exposition_formats/">Exposition formats</a>.
1819 */
1920public abstract class Collector {
21+
2022 /**
21- * Return all of the metrics of this Collector.
23+ * Return all metrics of this Collector.
2224 */
2325 public abstract List <MetricFamilySamples > collect ();
26+
27+ /**
28+ * Like {@link #collect()}, but the result may exclude {@code MetricFamilySamples} where
29+ * {@code metricNameFilter.test(name)} is {@code false} for all Sample names.
30+ * <p>
31+ * The default implementation first collects all {@code MetricFamilySamples} and then discards the ones
32+ * where {@code metricNameFilter.test(name)} returns {@code false} for all names in
33+ * {@link MetricFamilySamples#getNames()}.
34+ * To improve performance, collector implementations should override this method to prevent
35+ * {@code MetricFamilySamples} from being collected if they will be discarded anyways.
36+ * See {@code ThreadExports} for an example.
37+ * <p>
38+ * Note that the resulting List may contain "false positives", i.e. {@code MetricFamilySamples} where it turns
39+ * out that none of the Sample names returns {@code true} for {@code metricNameFilter.test(name)},
40+ * or "partial matches", i.e. {@code MetricFamilySamples} where some Sample names return {@code true} for
41+ * {@code metricNameFilter.test(name)} but some Sample names return {@code false}.
42+ * This is ok, because before we produce the output format we will call
43+ * {@link MetricFamilySamples#filter(Predicate)} to strip all Samples where {@code metricNameFilter.test(name)}
44+ * returns {@code false}.
45+ *
46+ * @param metricNameFilter may be {@code null}, indicating that all metrics should be collected.
47+ */
48+ public List <MetricFamilySamples > collect (Predicate <String > metricNameFilter ) {
49+ List <MetricFamilySamples > all = collect ();
50+ if (metricNameFilter == null ) {
51+ return all ;
52+ }
53+ List <MetricFamilySamples > remaining = new ArrayList <MetricFamilySamples >(all .size ());
54+ for (MetricFamilySamples mfs : all ) {
55+ for (String name : mfs .getNames ()) {
56+ if (metricNameFilter .test (name )) {
57+ remaining .add (mfs );
58+ break ;
59+ }
60+ }
61+ }
62+ return remaining ;
63+ }
64+
2465 public enum Type {
2566 UNKNOWN , // This is untyped in Prometheus text format.
2667 COUNTER ,
@@ -40,7 +81,11 @@ static public class MetricFamilySamples {
4081 public final String unit ;
4182 public final Type type ;
4283 public final String help ;
43- public final List <Sample > samples ;
84+ public final List <Sample > samples ; // this list can be modified as samples are added/removed.
85+
86+ public MetricFamilySamples (String name , Type type , String help , List <Sample > samples ) {
87+ this (name , "" , type , help , samples );
88+ }
4489
4590 public MetricFamilySamples (String name , String unit , Type type , String help , List <Sample > samples ) {
4691 if (!unit .isEmpty () && !name .endsWith ("_" + unit )) {
@@ -72,10 +117,84 @@ public MetricFamilySamples(String name, String unit, Type type, String help, Lis
72117 this .samples = mungedSamples ;
73118 }
74119
75- public MetricFamilySamples (String name , Type type , String help , List <Sample > samples ) {
76- this (name , "" , type , help , samples );
120+ /**
121+ *
122+ * @param metricNameFilter may be {@link null} indicating that the result contains the complete list of samples.
123+ * @return A new MetricFamilySamples containing only the Samples matching the metricNameFilter,
124+ * or {@code null} if no Sample matches.
125+ */
126+ public MetricFamilySamples filter (Predicate <String > metricNameFilter ) {
127+ if (metricNameFilter == null ) {
128+ return this ;
129+ }
130+ List <Sample > remainingSamples = new ArrayList <Sample >(samples .size ());
131+ for (Sample sample : samples ) {
132+ if (metricNameFilter .test (sample .name )) {
133+ remainingSamples .add (sample );
134+ }
135+ }
136+ if (remainingSamples .isEmpty ()) {
137+ return null ;
138+ }
139+ return new MetricFamilySamples (name , unit , type , help , remainingSamples );
77140 }
78141
142+ /**
143+ * List of names that are reserved for Samples in these MetricsFamilySamples.
144+ * <p>
145+ * This is used in two places:
146+ * <ol>
147+ * <li>To check potential name collisions in {@link CollectorRegistry#register(Collector)}.
148+ * <li>To check if a collector may contain metrics matching the metric name filter
149+ * in {@link Collector#collect(Predicate)}.
150+ * </ol>
151+ * Note that {@code getNames()} always includes the name without suffix, even though some
152+ * metrics types (like Counter) will not have a Sample with that name.
153+ * The reason is that the name without suffix is used in the metadata comments ({@code # TYPE}, {@code # UNIT},
154+ * {@code # HELP}), and as this name <a href="https://github.com/prometheus/common/issues/319">must be unique</a>
155+ * we include the name without suffix here as well.
156+ */
157+ public String [] getNames () {
158+ switch (type ) {
159+ case COUNTER :
160+ return new String []{
161+ name + "_total" ,
162+ name + "_created" ,
163+ name
164+ };
165+ case SUMMARY :
166+ return new String []{
167+ name + "_count" ,
168+ name + "_sum" ,
169+ name + "_created" ,
170+ name
171+ };
172+ case HISTOGRAM :
173+ return new String []{
174+ name + "_count" ,
175+ name + "_sum" ,
176+ name + "_bucket" ,
177+ name + "_created" ,
178+ name
179+ };
180+ case GAUGE_HISTOGRAM :
181+ return new String []{
182+ name + "_gcount" ,
183+ name + "_gsum" ,
184+ name + "_bucket" ,
185+ name
186+ };
187+ case INFO :
188+ return new String []{
189+ name + "_info" ,
190+ name
191+ };
192+ default :
193+ return new String []{name };
194+ }
195+ }
196+
197+
79198 @ Override
80199 public boolean equals (Object obj ) {
81200 if (!(obj instanceof MetricFamilySamples )) {
@@ -204,7 +323,7 @@ public interface Describable {
204323 * Usually custom collectors do not have to implement Describable. If
205324 * Describable is not implemented and the CollectorRegistry was created
206325 * with auto describe enabled (which is the case for the default registry)
207- * then {@link collect} will be called at registration time instead of
326+ * then {@link # collect} will be called at registration time instead of
208327 * describe. If this could cause problems, either implement a proper
209328 * describe, or if that's not practical have describe return an empty
210329 * list.
0 commit comments