diff --git a/benchmarks/benchmarks-core/src/jmh/java/io/micrometer/benchmark/core/CounterBenchmark.java b/benchmarks/benchmarks-core/src/jmh/java/io/micrometer/benchmark/core/CounterBenchmark.java
index 7b12a40aa3..11c995b173 100644
--- a/benchmarks/benchmarks-core/src/jmh/java/io/micrometer/benchmark/core/CounterBenchmark.java
+++ b/benchmarks/benchmarks-core/src/jmh/java/io/micrometer/benchmark/core/CounterBenchmark.java
@@ -52,6 +52,16 @@ public void setup() {
counter = registry.counter("counter");
}
+ @Benchmark
+ public void baseline() {
+ // this method was intentionally left blank.
+ }
+
+ @Benchmark
+ public Counter retrieve() {
+ return registry.counter("counter");
+ }
+
@Benchmark
public double countSum() {
counter.increment();
diff --git a/benchmarks/benchmarks-core/src/jmh/java/io/micrometer/benchmark/core/GaugeBenchmark.java b/benchmarks/benchmarks-core/src/jmh/java/io/micrometer/benchmark/core/GaugeBenchmark.java
new file mode 100644
index 0000000000..6e794e4b3d
--- /dev/null
+++ b/benchmarks/benchmarks-core/src/jmh/java/io/micrometer/benchmark/core/GaugeBenchmark.java
@@ -0,0 +1,72 @@
+/*
+ * Copyright 2024 VMware, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package io.micrometer.benchmark.core;
+
+import io.micrometer.core.instrument.Gauge;
+import io.micrometer.core.instrument.MeterRegistry;
+import io.micrometer.prometheusmetrics.PrometheusConfig;
+import io.micrometer.prometheusmetrics.PrometheusMeterRegistry;
+import org.openjdk.jmh.annotations.*;
+import org.openjdk.jmh.runner.Runner;
+import org.openjdk.jmh.runner.RunnerException;
+import org.openjdk.jmh.runner.options.OptionsBuilder;
+
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicInteger;
+
+@Fork(1)
+@State(Scope.Benchmark)
+@BenchmarkMode(Mode.SampleTime)
+@OutputTimeUnit(TimeUnit.MICROSECONDS)
+public class GaugeBenchmark {
+
+ private MeterRegistry registry;
+
+ private AtomicInteger stateObject;
+
+ @Setup
+ public void setup() {
+ registry = new PrometheusMeterRegistry(PrometheusConfig.DEFAULT);
+ stateObject = registry.gauge("test.gauge", new AtomicInteger());
+ // emits warn because of double registration
+ stateObject = registry.gauge("test.gauge", new AtomicInteger());
+ // emits debug because of double registration and keeps emitting debug from now on
+ stateObject = registry.gauge("test.gauge", new AtomicInteger());
+ }
+
+ @Benchmark
+ public AtomicInteger baseline() {
+ stateObject = new AtomicInteger();
+ return stateObject;
+ }
+
+ @Benchmark
+ public AtomicInteger gaugeReRegistrationWithoutBuilder() {
+ stateObject = registry.gauge("test.gauge", new AtomicInteger());
+ return stateObject;
+ }
+
+ @Benchmark
+ public Gauge gaugeReRegistrationWithBuilder() {
+ stateObject = new AtomicInteger();
+ return Gauge.builder("test.gauge", stateObject, AtomicInteger::doubleValue).register(registry);
+ }
+
+ public static void main(String[] args) throws RunnerException {
+ new Runner(new OptionsBuilder().include(GaugeBenchmark.class.getSimpleName()).build()).run();
+ }
+
+}
diff --git a/benchmarks/benchmarks-core/src/jmh/resources/logback.xml b/benchmarks/benchmarks-core/src/jmh/resources/logback.xml
new file mode 100644
index 0000000000..f2b1939e72
--- /dev/null
+++ b/benchmarks/benchmarks-core/src/jmh/resources/logback.xml
@@ -0,0 +1,26 @@
+
+
+
+
+ %d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n
+
+
+
+
+
+
+
diff --git a/micrometer-core/src/main/java/io/micrometer/core/instrument/MeterRegistry.java b/micrometer-core/src/main/java/io/micrometer/core/instrument/MeterRegistry.java
index 718ef8f4eb..863c0631e4 100644
--- a/micrometer-core/src/main/java/io/micrometer/core/instrument/MeterRegistry.java
+++ b/micrometer-core/src/main/java/io/micrometer/core/instrument/MeterRegistry.java
@@ -18,6 +18,7 @@
import io.micrometer.common.lang.Nullable;
import io.micrometer.common.util.internal.logging.InternalLogger;
import io.micrometer.common.util.internal.logging.InternalLoggerFactory;
+import io.micrometer.common.util.internal.logging.WarnThenDebugLogger;
import io.micrometer.core.annotation.Incubating;
import io.micrometer.core.instrument.Meter.Id;
import io.micrometer.core.instrument.config.MeterFilter;
@@ -66,6 +67,9 @@
*/
public abstract class MeterRegistry {
+ private static final WarnThenDebugLogger gaugeDoubleRegistrationLogger = new WarnThenDebugLogger(
+ MeterRegistry.class);
+
// @formatter:off
private static final EnumMap BASE_TIME_UNIT_STRING_CACHE = Arrays.stream(TimeUnit.values())
.collect(
@@ -630,6 +634,7 @@ private Meter getOrCreateMeter(@Nullable DistributionStatisticConfig config,
Meter m = preFilterIdToMeterMap.get(originalId);
if (m != null && !isStaleId(originalId)) {
+ checkAndWarnAboutGaugeDoubleRegistration(m);
return m;
}
@@ -642,6 +647,7 @@ private Meter getOrCreateMeter(@Nullable DistributionStatisticConfig config,
if (isStaleId(originalId)) {
unmarkStaleId(originalId);
}
+ checkAndWarnAboutGaugeDoubleRegistration(m);
}
else {
if (isClosed()) {
@@ -699,6 +705,14 @@ private boolean unmarkStaleId(Id originalId) {
return !stalePreFilterIds.isEmpty() && stalePreFilterIds.remove(originalId);
}
+ private void checkAndWarnAboutGaugeDoubleRegistration(Meter meter) {
+ if (meter instanceof Gauge) {
+ gaugeDoubleRegistrationLogger.log(() -> String.format(
+ "This Gauge has been already registered (%s), the Gauge registration will be ignored.",
+ meter.getId()));
+ }
+ }
+
private boolean accept(Meter.Id id) {
for (MeterFilter filter : filters) {
MeterFilterReply reply = filter.accept(id);