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 e4b124d332..e88b89f154 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..858eacf10a
--- /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 java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicInteger;
+
+import io.micrometer.core.instrument.Gauge;
+import io.micrometer.core.instrument.MeterRegistry;
+import io.micrometer.prometheus.PrometheusConfig;
+import io.micrometer.prometheus.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;
+
+@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 bacdd0dd56..b41e3b80dc 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
@@ -16,6 +16,7 @@
package io.micrometer.core.instrument;
import io.micrometer.common.lang.Nullable;
+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;
@@ -64,6 +65,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(
@@ -612,6 +616,7 @@ private Meter getOrCreateMeter(@Nullable DistributionStatisticConfig config,
BiFunction builder, Id originalId,
Id mappedId, Function noopBuilder) {
Meter m = meterMap.get(mappedId);
+ checkAndWarnAboutGaugeDoubleRegistration(m);
if (m == null) {
if (isClosed()) {
@@ -620,6 +625,7 @@ private Meter getOrCreateMeter(@Nullable DistributionStatisticConfig config,
synchronized (meterMapLock) {
m = meterMap.get(mappedId);
+ checkAndWarnAboutGaugeDoubleRegistration(m);
if (m == null) {
if (!accept(mappedId)) {
@@ -654,6 +660,14 @@ private Meter getOrCreateMeter(@Nullable DistributionStatisticConfig config,
return m;
}
+ 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);