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);