11package io .prometheus .metrics .model .registry ;
22
3+ import static java .util .Collections .emptySet ;
4+
35import io .prometheus .metrics .model .snapshots .MetricMetadata ;
46import io .prometheus .metrics .model .snapshots .MetricSnapshot ;
57import io .prometheus .metrics .model .snapshots .MetricSnapshots ;
@@ -53,8 +55,7 @@ private static class MultiCollectorRegistration {
5355
5456 /**
5557 * Tracks registration information for each metric name to enable validation of type consistency,
56- * label schema uniqueness, and help/unit consistency. Stores metadata to enable O(1)
57- * unregistration without iterating through all collectors.
58+ * label schema uniqueness, and help/unit consistency.
5859 */
5960 private static class RegistrationInfo {
6061 private final MetricType type ;
@@ -80,31 +81,25 @@ static RegistrationInfo of(
8081 @ Nullable Unit unit ) {
8182 Set <Set <String >> labelSchemas = ConcurrentHashMap .newKeySet ();
8283 Set <String > normalized =
83- (labelNames == null || labelNames .isEmpty ()) ? Collections . emptySet () : labelNames ;
84+ (labelNames == null || labelNames .isEmpty ()) ? emptySet () : labelNames ;
8485 labelSchemas .add (normalized );
8586 return new RegistrationInfo (type , labelSchemas , help , unit );
8687 }
8788
8889 /**
89- * Validates that the given help and unit are consistent with this registration. Throws if
90- * non-null values conflict. When stored help/unit is null and the new value is non-null,
91- * captures the first non-null so subsequent registrations are validated consistently .
90+ * Validates that the given help and unit are exactly equal to this registration. Throws if
91+ * values differ, including when one is null and the other is non-null. This ensures consistent
92+ * metadata across all collectors sharing the same metric name .
9293 */
9394 void validateMetadata (@ Nullable String newHelp , @ Nullable Unit newUnit ) {
94- if (help != null && newHelp != null && !Objects .equals (help , newHelp )) {
95+ if (!Objects .equals (help , newHelp )) {
9596 throw new IllegalArgumentException (
9697 "Conflicting help strings. Existing: \" " + help + "\" , new: \" " + newHelp + "\" " );
9798 }
98- if (unit != null && newUnit != null && !Objects .equals (unit , newUnit )) {
99+ if (!Objects .equals (unit , newUnit )) {
99100 throw new IllegalArgumentException (
100101 "Conflicting unit. Existing: " + unit + ", new: " + newUnit );
101102 }
102- if (help == null && newHelp != null ) {
103- this .help = newHelp ;
104- }
105- if (unit == null && newUnit != null ) {
106- this .unit = newUnit ;
107- }
108103 }
109104
110105 /**
@@ -115,7 +110,7 @@ void validateMetadata(@Nullable String newHelp, @Nullable Unit newUnit) {
115110 */
116111 boolean addLabelSet (@ Nullable Set <String > labelNames ) {
117112 Set <String > normalized =
118- (labelNames == null || labelNames .isEmpty ()) ? Collections . emptySet () : labelNames ;
113+ (labelNames == null || labelNames .isEmpty ()) ? emptySet () : labelNames ;
119114 return labelSchemas .add (normalized );
120115 }
121116
@@ -126,7 +121,7 @@ boolean addLabelSet(@Nullable Set<String> labelNames) {
126121 */
127122 void removeLabelSet (@ Nullable Set <String > labelNames ) {
128123 Set <String > normalized =
129- (labelNames == null || labelNames .isEmpty ()) ? Collections . emptySet () : labelNames ;
124+ (labelNames == null || labelNames .isEmpty ()) ? emptySet () : labelNames ;
130125 labelSchemas .remove (normalized );
131126 }
132127
@@ -147,11 +142,56 @@ MetricType getType() {
147142 */
148143 private static Set <String > immutableLabelNames (@ Nullable Set <String > labelNames ) {
149144 if (labelNames == null || labelNames .isEmpty ()) {
150- return Collections . emptySet ();
145+ return emptySet ();
151146 }
152147 return Collections .unmodifiableSet (new HashSet <>(labelNames ));
153148 }
154149
150+ /**
151+ * Validates the registration of a metric with the given parameters. Ensures type consistency,
152+ * label schema uniqueness, and help/unit consistency.
153+ */
154+ private void validateRegistration (
155+ String prometheusName ,
156+ MetricType metricType ,
157+ Set <String > normalizedLabels ,
158+ @ Nullable String help ,
159+ @ Nullable Unit unit ) {
160+ final MetricType type = metricType ;
161+ final Set <String > names = normalizedLabels ;
162+ final String helpForValidation = help ;
163+ final Unit unitForValidation = unit ;
164+ registered .compute (
165+ prometheusName ,
166+ (n , existingInfo ) -> {
167+ if (existingInfo == null ) {
168+ return RegistrationInfo .of (type , names , helpForValidation , unitForValidation );
169+ } else {
170+ if (existingInfo .getType () != type ) {
171+ throw new IllegalArgumentException (
172+ prometheusName
173+ + ": Conflicting metric types. Existing: "
174+ + existingInfo .getType ()
175+ + ", new: "
176+ + type );
177+ }
178+ // Check label set first; only mutate help/unit after validation passes.
179+ if (!existingInfo .addLabelSet (names )) {
180+ throw new IllegalArgumentException (
181+ prometheusName + ": duplicate metric name with identical label schema " + names );
182+ }
183+ // Roll back label schema if metadata validation fails
184+ try {
185+ existingInfo .validateMetadata (helpForValidation , unitForValidation );
186+ } catch (IllegalArgumentException e ) {
187+ existingInfo .removeLabelSet (names );
188+ throw e ;
189+ }
190+ return existingInfo ;
191+ }
192+ });
193+ }
194+
155195 public void register (Collector collector ) {
156196 if (!collectors .add (collector )) {
157197 throw new IllegalArgumentException ("Collector instance is already registered" );
@@ -167,35 +207,7 @@ public void register(Collector collector) {
167207 // Only perform validation if collector provides sufficient metadata.
168208 // Collectors that don't implement getPrometheusName()/getMetricType() will skip validation.
169209 if (prometheusName != null && metricType != null ) {
170- final String name = prometheusName ;
171- final MetricType type = metricType ;
172- final Set <String > names = normalizedLabels ;
173- final String helpForValidation = help ;
174- final Unit unitForValidation = unit ;
175- registered .compute (
176- prometheusName ,
177- (n , existingInfo ) -> {
178- if (existingInfo == null ) {
179- return RegistrationInfo .of (type , names , helpForValidation , unitForValidation );
180- } else {
181- if (existingInfo .getType () != type ) {
182- throw new IllegalArgumentException (
183- name
184- + ": Conflicting metric types. Existing: "
185- + existingInfo .getType ()
186- + ", new: "
187- + type );
188- }
189- // Check label set first; only mutate help/unit after validation passes.
190- if (!existingInfo .addLabelSet (names )) {
191- throw new IllegalArgumentException (
192- name + ": duplicate metric name with identical label schema " + names );
193- }
194- existingInfo .validateMetadata (helpForValidation , unitForValidation );
195- return existingInfo ;
196- }
197- });
198-
210+ validateRegistration (prometheusName , metricType , normalizedLabels , help , unit );
199211 collectorMetadata .put (
200212 collector , new CollectorRegistration (prometheusName , normalizedLabels ));
201213 }
@@ -225,37 +237,7 @@ public void register(MultiCollector collector) {
225237 Unit unit = metadata != null ? metadata .getUnit () : null ;
226238
227239 if (metricType != null ) {
228- final MetricType type = metricType ;
229- final Set <String > labelNamesForValidation = normalizedLabels ;
230- final String helpForValidation = help ;
231- final Unit unitForValidation = unit ;
232- registered .compute (
233- prometheusName ,
234- (name , existingInfo ) -> {
235- if (existingInfo == null ) {
236- return RegistrationInfo .of (
237- type , labelNamesForValidation , helpForValidation , unitForValidation );
238- } else {
239- if (existingInfo .getType () != type ) {
240- throw new IllegalArgumentException (
241- prometheusName
242- + ": Conflicting metric types. Existing: "
243- + existingInfo .getType ()
244- + ", new: "
245- + type );
246- }
247- // Check label set first; only mutate help/unit after validation passes.
248- if (!existingInfo .addLabelSet (labelNamesForValidation )) {
249- throw new IllegalArgumentException (
250- prometheusName
251- + ": duplicate metric name with identical label schema "
252- + labelNamesForValidation );
253- }
254- existingInfo .validateMetadata (helpForValidation , unitForValidation );
255- return existingInfo ;
256- }
257- });
258-
240+ validateRegistration (prometheusName , metricType , normalizedLabels , help , unit );
259241 registrations .add (new MultiCollectorRegistration (prometheusName , normalizedLabels ));
260242 }
261243 }
@@ -300,7 +282,7 @@ private void unregisterLabelSchema(String prometheusName, Set<String> labelNames
300282 (name , info ) -> {
301283 info .removeLabelSet (labelNames );
302284 if (info .isEmpty ()) {
303- return null ; // remove from registered map
285+ return null ;
304286 }
305287 return info ;
306288 });
0 commit comments