3131import io .micrometer .core .instrument .search .RequiredSearch ;
3232import io .micrometer .core .instrument .search .Search ;
3333import io .micrometer .core .instrument .util .TimeUtils ;
34+ import org .jspecify .annotations .NonNull ;
35+ import org .jspecify .annotations .NullUnmarked ;
3436import org .jspecify .annotations .Nullable ;
3537
3638import java .time .Duration ;
@@ -106,7 +108,7 @@ public abstract class MeterRegistry {
106108
107109 /**
108110 * write/remove guarded by meterMapLock, read in
109- * {@link #getOrCreateMeter(DistributionStatisticConfig, BiFunction , Meter.Id, Function)}
111+ * {@link #getOrCreateMeter(DistributionStatisticConfig, PauseDetector, NewMeterSupplier , Meter.Id, Function)}
110112 * is unguarded
111113 */
112114 private final Map <Meter .Id , Meter > preFilterIdToMeterMap = new HashMap <>();
@@ -135,7 +137,7 @@ public abstract class MeterRegistry {
135137
136138 private final AtomicBoolean closed = new AtomicBoolean ();
137139
138- private PauseDetector pauseDetector = new NoPauseDetector () ;
140+ private PauseDetector pauseDetector = NoPauseDetector . INSTANCE ;
139141
140142 private @ Nullable HighCardinalityTagsDetector highCardinalityTagsDetector ;
141143
@@ -338,9 +340,12 @@ private String getBaseTimeUnitStr() {
338340 * Only used by {@link Counter#builder(String)}.
339341 * @param id The identifier for this counter.
340342 * @return A new or existing counter.
343+ * @implNote Avoids allocation in the case the meter is already registered,
344+ * particularly capturing lambdas.
341345 */
342346 Counter counter (Meter .Id id ) {
343- return registerMeterIfNecessary (Counter .class , id , this ::newCounter , NoopCounter ::new );
347+ return registerMeterIfNecessary (Counter .class , id , null , null ,
348+ (registry , mappedId , mappedConfig , pd ) -> registry .newCounter (mappedId ), NoopCounter ::new );
344349 }
345350
346351 /**
@@ -352,22 +357,27 @@ Counter counter(Meter.Id id) {
352357 * @return A new or existing gauge.
353358 */
354359 <T > Gauge gauge (Meter .Id id , @ Nullable T obj , ToDoubleFunction <T > valueFunction ) {
355- return registerMeterIfNecessary (Gauge .class , id , id2 -> newGauge (id2 , obj , valueFunction ), NoopGauge ::new );
360+ return registerMeterIfNecessary (Gauge .class , id ,
361+ (registry , mappedId ) -> registry .newGauge (mappedId , obj , valueFunction ), NoopGauge ::new );
356362 }
357363
358364 /**
359- * Only used by {@link Timer#builder(String)} .
365+ * Only used internally .
360366 * @param id The identifier for this timer.
361367 * @param distributionStatisticConfig Configuration that governs how distribution
362368 * statistics are computed.
369+ * @param specificPauseDetector explicit pause detector to use that may be different
370+ * from the registry-configured one
363371 * @return A new or existing timer.
372+ * @implNote Avoids allocation in the case the meter is already registered,
373+ * particularly capturing lambdas.
364374 */
365375 Timer timer (Meter .Id id , DistributionStatisticConfig distributionStatisticConfig ,
366- PauseDetector pauseDetectorOverride ) {
367- return registerMeterIfNecessary (Timer .class , id , distributionStatisticConfig , ( id2 , filteredConfig ) -> {
368- Meter . Id withUnit = id2 . withBaseUnit ( getBaseTimeUnitStr ());
369- return newTimer (withUnit , filteredConfig . merge ( defaultHistogramConfig ()), pauseDetectorOverride );
370- }, NoopTimer ::new );
376+ PauseDetector specificPauseDetector ) {
377+ return registerMeterIfNecessary (Timer .class , id , distributionStatisticConfig , specificPauseDetector ,
378+ ( registry , mappedId , mappedConfig , pd ) -> registry
379+ . newTimer (mappedId . withBaseUnit ( registry . getBaseTimeUnitStr ()), mappedConfig , pd ),
380+ NoopTimer ::new );
371381 }
372382
373383 /**
@@ -378,8 +388,9 @@ Timer timer(Meter.Id id, DistributionStatisticConfig distributionStatisticConfig
378388 * @return A new or existing distribution summary.
379389 */
380390 DistributionSummary summary (Meter .Id id , DistributionStatisticConfig distributionStatisticConfig , double scale ) {
381- return registerMeterIfNecessary (DistributionSummary .class , id , distributionStatisticConfig , (id2 ,
382- filteredConfig ) -> newDistributionSummary (id2 , filteredConfig .merge (defaultHistogramConfig ()), scale ),
391+ return registerMeterIfNecessary (
392+ DistributionSummary .class , id , distributionStatisticConfig , null , (registry , mappedId , filteredConfig ,
393+ pd ) -> registry .newDistributionSummary (mappedId , filteredConfig , scale ),
383394 NoopDistributionSummary ::new );
384395 }
385396
@@ -392,7 +403,8 @@ DistributionSummary summary(Meter.Id id, DistributionStatisticConfig distributio
392403 * @return The meter.
393404 */
394405 Meter register (Meter .Id id , Meter .Type type , Iterable <Measurement > measurements ) {
395- return registerMeterIfNecessary (Meter .class , id , id2 -> newMeter (id2 , type , measurements ), NoopMeter ::new );
406+ return registerMeterIfNecessary (Meter .class , id ,
407+ (registry , mappedId ) -> registry .newMeter (mappedId , type , measurements ), NoopMeter ::new );
396408 }
397409
398410 /**
@@ -607,14 +619,15 @@ public <T extends Collection<?>> T gaugeCollectionSize(String name, Iterable<Tag
607619 }
608620
609621 private <M extends Meter > M registerMeterIfNecessary (Class <M > meterClass , Meter .Id id ,
610- Function <Meter .Id , M > builder , Function <Meter .Id , M > noopBuilder ) {
611- return registerMeterIfNecessary (meterClass , id , null , (id2 , conf ) -> builder .apply (id2 ), noopBuilder );
622+ BiFunction <MeterRegistry , Meter .Id , M > meterSupplier , Function <Meter .Id , M > noopBuilder ) {
623+ return registerMeterIfNecessary (meterClass , id , null , null ,
624+ (registry , mappedId , conf , pd ) -> meterSupplier .apply (registry , mappedId ), noopBuilder );
612625 }
613626
614627 private <M extends Meter > M registerMeterIfNecessary (Class <M > meterClass , Meter .Id id ,
615- @ Nullable DistributionStatisticConfig config , BiFunction < Meter . Id , DistributionStatisticConfig , M > builder ,
616- Function <Meter .Id , M > noopBuilder ) {
617- Meter m = getOrCreateMeter (config , builder , id , noopBuilder );
628+ @ Nullable DistributionStatisticConfig config , @ Nullable PauseDetector specificPauseDetector ,
629+ NewMeterSupplier < M > meterSupplier , Function <Meter .Id , M > noopBuilder ) {
630+ Meter m = getOrCreateMeter (config , specificPauseDetector , meterSupplier , id , noopBuilder );
618631
619632 if (!meterClass .isInstance (m )) {
620633 throw new IllegalArgumentException (
@@ -643,8 +656,29 @@ private Meter.Id mapId(Meter.Id id) {
643656 return mappedId ;
644657 }
645658
659+ @ FunctionalInterface
660+ // Brittle, but this is internal. We pass nulls for some meter types as explained in
661+ // JavaDoc.
662+ @ NullUnmarked
663+ private interface NewMeterSupplier <M extends Meter > {
664+
665+ /**
666+ * Create a new meter with the given parameters. The DistributionStatisticConfig
667+ * and PauseDetector will be null for meter types that do not take them.
668+ * @param registry the registry from which to make the new meter
669+ * @param id the ID of the meter to create
670+ * @param distributionStatisticConfig optional distribution config for types that
671+ * take it
672+ * @param pauseDetector optional pause detector for types that take it
673+ * @return a new meter
674+ */
675+ @ NonNull M create (MeterRegistry registry , Meter .Id id , DistributionStatisticConfig distributionStatisticConfig ,
676+ PauseDetector pauseDetector );
677+
678+ }
679+
646680 private Meter getOrCreateMeter (@ Nullable DistributionStatisticConfig config ,
647- BiFunction < Meter . Id , /* Nullable Generic */ DistributionStatisticConfig , ? extends Meter > builder ,
681+ @ Nullable PauseDetector specificPauseDetector , NewMeterSupplier < ? extends Meter > meterSupplier ,
648682 Meter .Id originalId , Function <Meter .Id , ? extends Meter > noopBuilder ) {
649683
650684 Meter m = preFilterIdToMeterMap .get (originalId );
@@ -684,9 +718,10 @@ private Meter getOrCreateMeter(@Nullable DistributionStatisticConfig config,
684718 config = filteredConfig ;
685719 }
686720 }
721+ config = config .merge (defaultHistogramConfig ());
687722 }
688723
689- m = builder . apply ( mappedId , config );
724+ m = meterSupplier . create ( this , mappedId , config , specificPauseDetector );
690725
691726 Meter .Id synAssoc = mappedId .syntheticAssociation ();
692727 if (synAssoc != null ) {
@@ -1066,13 +1101,14 @@ public LongTaskTimer longTaskTimer(String name, Iterable<Tag> tags) {
10661101 * Only used by {@link LongTaskTimer#builder(String)}.
10671102 * @param id The identifier for this long task timer.
10681103 * @return A new or existing long task timer.
1104+ * @implNote Avoids allocation in the case the meter is already registered,
1105+ * particularly capturing lambdas.
10691106 */
10701107 LongTaskTimer longTaskTimer (Meter .Id id , DistributionStatisticConfig distributionStatisticConfig ) {
1071- return registerMeterIfNecessary (LongTaskTimer .class , id , distributionStatisticConfig ,
1072- (id2 , filteredConfig ) -> {
1073- Meter .Id withUnit = id2 .withBaseUnit (getBaseTimeUnitStr ());
1074- return newLongTaskTimer (withUnit , filteredConfig .merge (defaultHistogramConfig ()));
1075- }, NoopLongTaskTimer ::new );
1108+ return registerMeterIfNecessary (LongTaskTimer .class , id , distributionStatisticConfig , null ,
1109+ (registry , mappedId , mappedConfig , pd ) -> registry
1110+ .newLongTaskTimer (mappedId .withBaseUnit (registry .getBaseTimeUnitStr ()), mappedConfig ),
1111+ NoopLongTaskTimer ::new );
10761112 }
10771113
10781114 /**
@@ -1116,7 +1152,8 @@ public <T extends Number> FunctionCounter counter(String name, Iterable<Tag> tag
11161152 */
11171153 <T > FunctionCounter counter (Meter .Id id , T obj , ToDoubleFunction <T > countFunction ) {
11181154 return registerMeterIfNecessary (FunctionCounter .class , id ,
1119- id2 -> newFunctionCounter (id2 , obj , countFunction ), NoopFunctionCounter ::new );
1155+ (registry , mappedId ) -> registry .newFunctionCounter (mappedId , obj , countFunction ),
1156+ NoopFunctionCounter ::new );
11201157 }
11211158
11221159 /**
@@ -1157,10 +1194,10 @@ public <T> FunctionTimer timer(String name, Iterable<Tag> tags, T obj, ToLongFun
11571194 */
11581195 <T > FunctionTimer timer (Meter .Id id , T obj , ToLongFunction <T > countFunction ,
11591196 ToDoubleFunction <T > totalTimeFunction , TimeUnit totalTimeFunctionUnit ) {
1160- return registerMeterIfNecessary (FunctionTimer .class , id , id2 -> {
1161- Meter . Id withUnit = id2 . withBaseUnit (getBaseTimeUnitStr ());
1162- return newFunctionTimer ( withUnit , obj , countFunction , totalTimeFunction , totalTimeFunctionUnit );
1163- }, NoopFunctionTimer ::new );
1197+ return registerMeterIfNecessary (FunctionTimer .class , id ,
1198+ ( registry , mappedId ) -> registry . newFunctionTimer ( mappedId . withBaseUnit (getBaseTimeUnitStr ()), obj ,
1199+ countFunction , totalTimeFunction , totalTimeFunctionUnit ),
1200+ NoopFunctionTimer ::new );
11641201 }
11651202
11661203 /**
@@ -1198,7 +1235,8 @@ public <T> TimeGauge timeGauge(String name, Iterable<Tag> tags, T obj, TimeUnit
11981235 <T > TimeGauge timeGauge (Meter .Id id , @ Nullable T obj , TimeUnit timeFunctionUnit ,
11991236 ToDoubleFunction <T > timeFunction ) {
12001237 return registerMeterIfNecessary (TimeGauge .class , id ,
1201- id2 -> newTimeGauge (id2 , obj , timeFunctionUnit , timeFunction ), NoopTimeGauge ::new );
1238+ (registry , mappedId ) -> registry .newTimeGauge (mappedId , obj , timeFunctionUnit , timeFunction ),
1239+ NoopTimeGauge ::new );
12021240 }
12031241
12041242 }
0 commit comments