2020import java .util .Objects ;
2121import java .util .Optional ;
2222import java .util .Set ;
23- import java .util .function . Predicate ;
23+ import java .util .regex . Pattern ;
2424
2525import io .helidon .common .media .type .MediaType ;
2626import io .helidon .common .media .type .MediaTypes ;
@@ -49,16 +49,34 @@ public class MicrometerPrometheusFormatter implements MeterRegistryFormatter {
4949 public static final Map <MediaType , String > MEDIA_TYPE_TO_FORMAT = Map .of (
5050 MediaTypes .TEXT_PLAIN , TextFormat .CONTENT_TYPE_004 ,
5151 MediaTypes .APPLICATION_OPENMETRICS_TEXT , TextFormat .CONTENT_TYPE_OPENMETRICS_100 );
52+
53+ private static final Pattern SPECIAL_CHARACTERS_MAPPED_TO_UNDERSCORE_PATTERN = Pattern .compile ("[-+.!?@#$%^&*`'\\ s]+" );
54+ private static final Pattern NON_DIGIT_OR_UNDERSCORE_PREFIX_PATTERN = Pattern .compile ("^[0-9_]+.*" );
55+ private static final Pattern NON_IDENTIFIER_PATTERN = Pattern .compile ("[^A-Za-z0-9_:]" );
56+
5257 private final String scopeTagName ;
53- private final Iterable <String > scopeSelection ;
54- private final Iterable <String > meterNameSelection ;
58+ private final Set <String > scopes ;
59+ private final Set <String > meterNames ;
5560 private final MediaType resultMediaType ;
5661 private final MeterRegistry meterRegistry ;
5762
5863 private MicrometerPrometheusFormatter (Builder builder ) {
5964 scopeTagName = builder .scopeTagName ;
60- scopeSelection = builder .scopeSelection ;
61- meterNameSelection = builder .meterNameSelection ;
65+ meterNames = (builder .meterNameSelection instanceof Set <String > namesSet )
66+ ? namesSet
67+ : new HashSet <>() {
68+ {
69+ builder .meterNameSelection .forEach (this ::add );
70+ }
71+ };
72+
73+ scopes = (builder .scopeSelection instanceof Set <String > scopesSet )
74+ ? scopesSet
75+ : new HashSet <>() {
76+ {
77+ builder .scopeSelection .forEach (this ::add );
78+ }
79+ };
6280 resultMediaType = builder .resultMediaType ;
6381 meterRegistry = Objects .requireNonNullElseGet (builder .meterRegistry ,
6482 io .helidon .metrics .api .Metrics ::globalRegistry );
@@ -84,15 +102,15 @@ public static String normalizeNameToPrometheus(String name) {
84102 String result = name ;
85103
86104 // Convert special characters to underscores.
87- result = result .replaceAll ("[-+.!?@#$%^&*`' \\ s]+" , "_" );
105+ result = SPECIAL_CHARACTERS_MAPPED_TO_UNDERSCORE_PATTERN . matcher ( result ) .replaceAll ("_" );
88106
89107 // Prometheus simple client adds the prefix "m_" if a meter name starts with a digit or an underscore.
90- if (result .matches ("^[0-9_]+.*" )) {
108+ if (NON_DIGIT_OR_UNDERSCORE_PREFIX_PATTERN . matcher ( result ) .matches ()) {
91109 result = "m_" + result ;
92110 }
93111
94112 // Replace non-identifier characters.
95- result = result .replaceAll ("[^A-Za-z0-9_:]" , "_" );
113+ result = NON_IDENTIFIER_PATTERN . matcher ( result ) .replaceAll ("_" );
96114
97115 return result ;
98116 }
@@ -123,17 +141,24 @@ public Optional<Object> format() {
123141 Optional <PrometheusMeterRegistry > prometheusMeterRegistry = prometheusMeterRegistry (meterRegistry );
124142 if (prometheusMeterRegistry .isPresent ()) {
125143
126- // Scraping the Prometheus registry lets us limit the output to include only specified names.
127- Set <String > meterNamesOfInterest = meterNamesOfInterest (prometheusMeterRegistry .get (),
128- scopeSelection ,
129- meterNameSelection );
130- if (meterNamesOfInterest .isEmpty ()) {
131- return Optional .empty ();
144+ /*
145+ Optimize for the no-selection case (neither scope nor name selections were requested).
146+ */
147+ Set <String > meterNamesOfInterest ;
148+
149+ if (meterNames .isEmpty () && scopes .isEmpty ()) {
150+ meterNamesOfInterest = null ; // The Prometheus registry's scrape method treats null as "match all names."
151+ } else {
152+ meterNamesOfInterest = meterNamesOfInterest (prometheusMeterRegistry .get (),
153+ scopes ,
154+ meterNames );
155+ if (meterNamesOfInterest .isEmpty ()) {
156+ return Optional .empty ();
157+ }
132158 }
133159
134160 String prometheusOutput = prometheusMeterRegistry .get ()
135- .scrape (MicrometerPrometheusFormatter .MEDIA_TYPE_TO_FORMAT .get (
136- resultMediaType ),
161+ .scrape (MicrometerPrometheusFormatter .MEDIA_TYPE_TO_FORMAT .get (resultMediaType ),
137162 meterNamesOfInterest );
138163
139164 return prometheusOutput .isBlank () ? Optional .empty () : Optional .of (prometheusOutput );
@@ -166,31 +191,23 @@ public Optional<Object> formatMetadata() {
166191 * </p>
167192 *
168193 * @param prometheusMeterRegistry Prometheus meter registry to query
169- * @param scopeSelection scope names to select
170- * @param meterNameSelection meter names to select
194+ * @param scopes scope names to select
195+ * @param names meter names to select
171196 * @return set of matching meter names (with units and suffixes as needed) to match the names as stored in the meter registry
172197 */
173198 Set <String > meterNamesOfInterest (PrometheusMeterRegistry prometheusMeterRegistry ,
174- Iterable <String > scopeSelection ,
175- Iterable <String > meterNameSelection ) {
199+ Set <String > scopes ,
200+ Set <String > names ) {
176201
177202 Set <String > result = new HashSet <>();
178203
179- var scopes = new HashSet <>();
180- scopeSelection .forEach (scopes ::add );
181-
182- var names = new HashSet <>();
183- meterNameSelection .forEach (names ::add );
184-
185- Predicate <Meter > scopePredicate = scopes .isEmpty () || scopeTagName == null || scopeTagName .isBlank ()
186- ? m -> true
187- : m -> scopes .contains (m .getId ().getTag (scopeTagName ));
188-
189- Predicate <String > namePredicate = names .isEmpty () ? n -> true : names ::contains ;
190-
191204 for (Meter meter : prometheusMeterRegistry .getMeters ()) {
192205 String meterName = meter .getId ().getName ();
193- if (!namePredicate .test (meterName ) || !scopePredicate .test (meter )) {
206+ if ((!names .isEmpty () && !names .contains (meterName ))
207+ || (!scopes .isEmpty ()
208+ && scopeTagName != null
209+ && !scopeTagName .isBlank ()
210+ && !scopes .contains (meter .getId ().getTag (scopeTagName )))) {
194211 continue ;
195212 }
196213 Set <String > allUnitsForMeterName = new HashSet <>();
0 commit comments