From 6dbaa9dc7d6050024965805065488e770c53e358 Mon Sep 17 00:00:00 2001 From: Tadaya Tsuyukubo Date: Thu, 13 Jun 2024 01:13:42 -0700 Subject: [PATCH 01/74] Fix wrong reference in the deprecation javadoc (#5197) Signed-off-by: Tadaya Tsuyukubo --- .../src/main/java/io/micrometer/core/lang/NonNullFields.java | 2 +- .../src/main/java/io/micrometer/core/lang/Nullable.java | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/micrometer-core/src/main/java/io/micrometer/core/lang/NonNullFields.java b/micrometer-core/src/main/java/io/micrometer/core/lang/NonNullFields.java index bdb5a92680..4d472361c2 100644 --- a/micrometer-core/src/main/java/io/micrometer/core/lang/NonNullFields.java +++ b/micrometer-core/src/main/java/io/micrometer/core/lang/NonNullFields.java @@ -36,7 +36,7 @@ * @see io.micrometer.common.lang.NonNullFields * @see io.micrometer.common.lang.Nullable * @see io.micrometer.common.lang.NonNull - * @deprecated Please use {@link io.micrometer.common.lang.NonNullApi} instead. + * @deprecated Please use {@link io.micrometer.common.lang.NonNullFields} instead. */ @Target({ ElementType.PACKAGE, ElementType.TYPE }) @Retention(RetentionPolicy.RUNTIME) diff --git a/micrometer-core/src/main/java/io/micrometer/core/lang/Nullable.java b/micrometer-core/src/main/java/io/micrometer/core/lang/Nullable.java index eabe46f113..6fd0eafd5a 100644 --- a/micrometer-core/src/main/java/io/micrometer/core/lang/Nullable.java +++ b/micrometer-core/src/main/java/io/micrometer/core/lang/Nullable.java @@ -39,7 +39,7 @@ * @see io.micrometer.common.lang.NonNullApi * @see io.micrometer.common.lang.NonNullFields * @see io.micrometer.common.lang.NonNull - * @deprecated Please use {@link io.micrometer.common.lang.NonNullApi} instead. + * @deprecated Please use {@link io.micrometer.common.lang.Nullable} instead. */ @Target({ ElementType.METHOD, ElementType.PARAMETER, ElementType.FIELD }) @Retention(RetentionPolicy.RUNTIME) From 796c1e5f39b1d44da94aec1ee0760b73214f28b2 Mon Sep 17 00:00:00 2001 From: Jonatan Ivanov Date: Thu, 13 Jun 2024 15:42:52 -0700 Subject: [PATCH 02/74] Fix histogram consistency in PrometheusMeterRegistry The Histogram implementation of PrometheusMeterRegistry lets inconsistencies to happen in certain concurrent scenarios. For example, if a Timer is scraped while a data point is recorded, the count field of the Timer can be less than the value of the last bucket in its histogram snapshot (the bucket is already incremented while the counter is not). With the new Prometheus Client (1.x), this can cause exceptions since: 1. The Prometheus Client's ClassicHistogramBuckets is not cumulative but the histograms we use are so we need to convert it to non-cumulative (delta) by doing a little math. 2. If missing, we calculate the value of the +Inf bucket using the count and the last bucket of the histogram, the calculation looks like this: +Inf bucket = count - last bucket (delta). 3. In this situation, if the value of the count is less than the value of the last bucket in the histogram snapshot (concurrency issue), the value of the +Inf bucket will be negative which is validated by the Prometheus Client and an exception is thrown: java.lang.IllegalArgumentException: Counts in ClassicHistogramBuckets cannot be negative. In case of Prometheus Client 0.x, there is no such exception since we don't need to calculate the value of the +Inf bucket but we reuse the cumulative value of count. Closes gh-5193 --- concurrency-tests/build.gradle | 2 + ...rometheusMeterRegistryConcurrencyTest.java | 129 ++++++++++++++++++ .../PrometheusMeterRegistry.java | 6 +- 3 files changed, 135 insertions(+), 2 deletions(-) create mode 100644 concurrency-tests/src/jcstress/java/io/micrometer/concurrencytests/PrometheusMeterRegistryConcurrencyTest.java diff --git a/concurrency-tests/build.gradle b/concurrency-tests/build.gradle index e05ed81110..6870dd985e 100644 --- a/concurrency-tests/build.gradle +++ b/concurrency-tests/build.gradle @@ -5,6 +5,8 @@ plugins { dependencies { implementation project(":micrometer-core") // implementation("io.micrometer:micrometer-core:1.12.4") + implementation project(":micrometer-test") + implementation project(":micrometer-registry-prometheus") runtimeOnly(libs.logbackLatest) } diff --git a/concurrency-tests/src/jcstress/java/io/micrometer/concurrencytests/PrometheusMeterRegistryConcurrencyTest.java b/concurrency-tests/src/jcstress/java/io/micrometer/concurrencytests/PrometheusMeterRegistryConcurrencyTest.java new file mode 100644 index 0000000000..bfe4c5d255 --- /dev/null +++ b/concurrency-tests/src/jcstress/java/io/micrometer/concurrencytests/PrometheusMeterRegistryConcurrencyTest.java @@ -0,0 +1,129 @@ +/* + * 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.concurrencytests; + +import io.micrometer.core.Issue; +import io.micrometer.core.instrument.DistributionSummary; +import io.micrometer.core.instrument.LongTaskTimer; +import io.micrometer.core.instrument.LongTaskTimer.Sample; +import io.micrometer.core.instrument.Timer; +import io.micrometer.prometheusmetrics.PrometheusConfig; +import io.micrometer.prometheusmetrics.PrometheusMeterRegistry; +import org.openjdk.jcstress.annotations.Actor; +import org.openjdk.jcstress.annotations.JCStressTest; +import org.openjdk.jcstress.annotations.Outcome; +import org.openjdk.jcstress.annotations.State; +import org.openjdk.jcstress.infra.results.Z_Result; + +import java.time.Duration; + +import static org.openjdk.jcstress.annotations.Expect.ACCEPTABLE; +import static org.openjdk.jcstress.annotations.Expect.FORBIDDEN; + +/** + * Concurrency tests for histogram using {@link PrometheusMeterRegistry}. + */ +public class PrometheusMeterRegistryConcurrencyTest { + + @Issue("#5193") + @JCStressTest + @State + @Outcome(id = "true", expect = ACCEPTABLE, desc = "Successful scrape") + @Outcome(expect = FORBIDDEN, desc = "Failed scrape") + public static class ConsistentTimerHistogram { + + PrometheusMeterRegistry registry = new PrometheusMeterRegistry(PrometheusConfig.DEFAULT); + + Timer timer = Timer.builder("test").publishPercentileHistogram().register(registry); + + @Actor + public void record() { + timer.record(Duration.ofMillis(100)); + } + + @Actor + public void scrape(Z_Result r) { + try { + registry.scrape(); + r.r1 = true; + } + catch (Exception e) { + r.r1 = false; + } + } + + } + + @Issue("#5193") + @JCStressTest + @State + @Outcome(id = "true", expect = ACCEPTABLE, desc = "Successful scrape") + @Outcome(expect = FORBIDDEN, desc = "Failed scrape") + public static class ConsistentDistributionSummaryHistogram { + + PrometheusMeterRegistry registry = new PrometheusMeterRegistry(PrometheusConfig.DEFAULT); + + DistributionSummary ds = DistributionSummary.builder("test").publishPercentileHistogram().register(registry); + + @Actor + public void record() { + ds.record(100); + } + + @Actor + public void scrape(Z_Result r) { + try { + registry.scrape(); + r.r1 = true; + } + catch (Exception e) { + r.r1 = false; + } + } + + } + + @Issue("#5193") + @JCStressTest + @State + @Outcome(id = "true", expect = ACCEPTABLE, desc = "Successful scrape") + @Outcome(expect = FORBIDDEN, desc = "Failed scrape") + public static class ConsistentLongTaskTimerHistogram { + + PrometheusMeterRegistry registry = new PrometheusMeterRegistry(PrometheusConfig.DEFAULT); + + LongTaskTimer ltt = LongTaskTimer.builder("test").publishPercentileHistogram().register(registry); + + @Actor + public void record() { + Sample sample = ltt.start(); + sample.stop(); + } + + @Actor + public void scrape(Z_Result r) { + try { + registry.scrape(); + r.r1 = true; + } + catch (Exception e) { + r.r1 = false; + } + } + + } + +} diff --git a/implementations/micrometer-registry-prometheus/src/main/java/io/micrometer/prometheusmetrics/PrometheusMeterRegistry.java b/implementations/micrometer-registry-prometheus/src/main/java/io/micrometer/prometheusmetrics/PrometheusMeterRegistry.java index 2a5864e7c1..ecb957bc52 100644 --- a/implementations/micrometer-registry-prometheus/src/main/java/io/micrometer/prometheusmetrics/PrometheusMeterRegistry.java +++ b/implementations/micrometer-registry-prometheus/src/main/java/io/micrometer/prometheusmetrics/PrometheusMeterRegistry.java @@ -271,7 +271,8 @@ public DistributionSummary newDistributionSummary(Meter.Id id, if (Double.isFinite(histogramCounts[histogramCounts.length - 1].bucket())) { // ClassicHistogramBuckets is not cumulative buckets.add(Double.POSITIVE_INFINITY); - counts.add(count - histogramCounts[histogramCounts.length - 1].count()); + double infCount = count - histogramCounts[histogramCounts.length - 1].count(); + counts.add(infCount >= 0 ? infCount : 0); } Exemplars exemplars = summary.exemplars(); @@ -494,7 +495,8 @@ private void addDistributionStatisticSamples(Meter.Id id, MicrometerCollector co if (Double.isFinite(histogramCounts[histogramCounts.length - 1].bucket())) { // ClassicHistogramBuckets is not cumulative buckets.add(Double.POSITIVE_INFINITY); - counts.add(count - histogramCounts[histogramCounts.length - 1].count()); + double infCount = count - histogramCounts[histogramCounts.length - 1].count(); + counts.add(infCount >= 0 ? infCount : 0); } Exemplars exemplars = createExemplarsWithScaledValues(exemplarsSupplier.get()); From a353d9414fff877f0bf4a295eb19004adaffe209 Mon Sep 17 00:00:00 2001 From: Johnny Lim Date: Mon, 17 Jun 2024 17:04:32 +0900 Subject: [PATCH 03/74] Polish gh-4867 (#5224) --- .../DefaultExemplarSamplerFactory.java | 8 +++----- .../ExemplarSamplerFactory.java | 3 +-- .../PrometheusMeterRegistryTest.java | 20 +++++++++++-------- .../samples/PrometheusExemplarsSample.java | 12 +++++------ 4 files changed, 22 insertions(+), 21 deletions(-) diff --git a/implementations/micrometer-registry-prometheus/src/main/java/io/micrometer/prometheusmetrics/DefaultExemplarSamplerFactory.java b/implementations/micrometer-registry-prometheus/src/main/java/io/micrometer/prometheusmetrics/DefaultExemplarSamplerFactory.java index 295c5d822f..e4be6236fa 100644 --- a/implementations/micrometer-registry-prometheus/src/main/java/io/micrometer/prometheusmetrics/DefaultExemplarSamplerFactory.java +++ b/implementations/micrometer-registry-prometheus/src/main/java/io/micrometer/prometheusmetrics/DefaultExemplarSamplerFactory.java @@ -27,7 +27,6 @@ * Default implementation of {@link ExemplarSamplerFactory}. * * @author Jonatan Ivanov - * @since 1.13.0 */ class DefaultExemplarSamplerFactory implements ExemplarSamplerFactory { @@ -39,7 +38,7 @@ class DefaultExemplarSamplerFactory implements ExemplarSamplerFactory { private final SpanContext spanContext; - public DefaultExemplarSamplerFactory(SpanContext spanContext, ExemplarsProperties exemplarsProperties) { + DefaultExemplarSamplerFactory(SpanContext spanContext, ExemplarsProperties exemplarsProperties) { this.spanContext = spanContext; this.exemplarsProperties = exemplarsProperties; } @@ -52,10 +51,9 @@ public ExemplarSampler createExemplarSampler(int numberOfExemplars) { } @Override - public ExemplarSampler createExemplarSampler(double[] histogramClassicUpperBounds) { + public ExemplarSampler createExemplarSampler(double[] histogramUpperBounds) { ExemplarSamplerConfig config = exemplarSamplerConfigsByHistogramUpperBounds.computeIfAbsent( - histogramClassicUpperBounds, - key -> new ExemplarSamplerConfig(exemplarsProperties, histogramClassicUpperBounds)); + histogramUpperBounds, key -> new ExemplarSamplerConfig(exemplarsProperties, histogramUpperBounds)); return new ExemplarSampler(config, spanContext); } diff --git a/implementations/micrometer-registry-prometheus/src/main/java/io/micrometer/prometheusmetrics/ExemplarSamplerFactory.java b/implementations/micrometer-registry-prometheus/src/main/java/io/micrometer/prometheusmetrics/ExemplarSamplerFactory.java index 61663b2d6f..5d6441bb0d 100644 --- a/implementations/micrometer-registry-prometheus/src/main/java/io/micrometer/prometheusmetrics/ExemplarSamplerFactory.java +++ b/implementations/micrometer-registry-prometheus/src/main/java/io/micrometer/prometheusmetrics/ExemplarSamplerFactory.java @@ -21,7 +21,6 @@ * A factory that creates {@link ExemplarSampler} instances with the desired properties. * * @author Jonatan Ivanov - * @since 1.13.0 */ interface ExemplarSamplerFactory { @@ -34,7 +33,7 @@ interface ExemplarSamplerFactory { /** * Creates an {@link ExemplarSampler} that stores exemplars for the defined histogram - * buckets. This means as many exemplars as many buckets are defined. + * buckets. This means as many exemplars as buckets are defined. * @param histogramUpperBounds histogram buckets to store exemplars for. * @return a new {@link ExemplarSampler} instance. */ diff --git a/implementations/micrometer-registry-prometheus/src/test/java/io/micrometer/prometheusmetrics/PrometheusMeterRegistryTest.java b/implementations/micrometer-registry-prometheus/src/test/java/io/micrometer/prometheusmetrics/PrometheusMeterRegistryTest.java index 5499651b5e..d2475e39e0 100644 --- a/implementations/micrometer-registry-prometheus/src/test/java/io/micrometer/prometheusmetrics/PrometheusMeterRegistryTest.java +++ b/implementations/micrometer-registry-prometheus/src/test/java/io/micrometer/prometheusmetrics/PrometheusMeterRegistryTest.java @@ -871,18 +871,18 @@ void openMetricsScrapeWithExemplars() throws InterruptedException { Timer timerWithHistogram = Timer.builder("timer.withHistogram").publishPercentileHistogram().register(registry); timerWithHistogram.record(15, TimeUnit.MILLISECONDS); - Thread.sleep(5); // sleeping 5ms since the sample interval limit is 1ms + sleepToAvoidRateLimiting(); timerWithHistogram.record(150, TimeUnit.MILLISECONDS); - Thread.sleep(5); // sleeping 5ms since the sample interval limit is 1ms + sleepToAvoidRateLimiting(); timerWithHistogram.record(60, TimeUnit.SECONDS); Timer timerWithSlos = Timer.builder("timer.withSlos") .serviceLevelObjectives(Duration.ofMillis(100), Duration.ofMillis(200), Duration.ofMillis(300)) .register(registry); timerWithSlos.record(Duration.ofMillis(15)); - Thread.sleep(5); // sleeping 5ms since the sample interval limit is 1ms + sleepToAvoidRateLimiting(); timerWithSlos.record(Duration.ofMillis(1_500)); - Thread.sleep(5); // sleeping 5ms since the sample interval limit is 1ms + sleepToAvoidRateLimiting(); timerWithSlos.record(Duration.ofMillis(150)); DistributionSummary summary = DistributionSummary.builder("summary.noHistogram").register(registry); @@ -894,18 +894,18 @@ void openMetricsScrapeWithExemplars() throws InterruptedException { .publishPercentileHistogram() .register(registry); summaryWithHistogram.record(0.15); - Thread.sleep(5); // sleeping 5ms since the sample interval limit is 1ms + sleepToAvoidRateLimiting(); summaryWithHistogram.record(5E18); - Thread.sleep(5); // sleeping 5ms since the sample interval limit is 1ms + sleepToAvoidRateLimiting(); summaryWithHistogram.record(15); DistributionSummary slos = DistributionSummary.builder("summary.withSlos") .serviceLevelObjectives(100, 200, 300) .register(registry); slos.record(10); - Thread.sleep(5); // sleeping 5ms since the sample interval limit is 1ms + sleepToAvoidRateLimiting(); slos.record(1_000); - Thread.sleep(5); // sleeping 5ms since the sample interval limit is 1ms + sleepToAvoidRateLimiting(); slos.record(250); String scraped = registry.scrape("application/openmetrics-text"); @@ -944,6 +944,10 @@ void openMetricsScrapeWithExemplars() throws InterruptedException { assertThat(scraped).endsWith("# EOF\n"); } + private static void sleepToAvoidRateLimiting() throws InterruptedException { + Thread.sleep(5); // sleeping 5ms since the sample interval limit is 1ms + } + @Test void noExemplarsIfNoSampler() { PrometheusMeterRegistry registry = new PrometheusMeterRegistry(PrometheusConfig.DEFAULT, prometheusRegistry, diff --git a/samples/micrometer-samples-core/src/main/java/io/micrometer/core/samples/PrometheusExemplarsSample.java b/samples/micrometer-samples-core/src/main/java/io/micrometer/core/samples/PrometheusExemplarsSample.java index 8bf569f898..ca5b38ded1 100644 --- a/samples/micrometer-samples-core/src/main/java/io/micrometer/core/samples/PrometheusExemplarsSample.java +++ b/samples/micrometer-samples-core/src/main/java/io/micrometer/core/samples/PrometheusExemplarsSample.java @@ -40,26 +40,26 @@ public static void main(String[] args) throws InterruptedException { Timer timer = Timer.builder("test.timer").publishPercentileHistogram().register(registry); timer.record(Duration.ofNanos(1_000 * 100)); - sleep(); // sleeping to avoid rate-limiting + sleepToAvoidRateLimiting(); timer.record(Duration.ofMillis(2)); - sleep(); // sleeping to avoid rate-limiting + sleepToAvoidRateLimiting(); timer.record(Duration.ofMillis(100)); - sleep(); // sleeping to avoid rate-limiting + sleepToAvoidRateLimiting(); timer.record(Duration.ofSeconds(60)); DistributionSummary distributionSummary = DistributionSummary.builder("test.distribution") .publishPercentileHistogram() .register(registry); distributionSummary.record(0.15); - sleep(); // sleeping to avoid rate-limiting + sleepToAvoidRateLimiting(); distributionSummary.record(15); - sleep(); // sleeping to avoid rate-limiting + sleepToAvoidRateLimiting(); distributionSummary.record(5E18); System.out.println(registry.scrape(CONTENT_TYPE_OPENMETRICS_100)); } - static void sleep() { + static void sleepToAvoidRateLimiting() { try { // sleeping 100ms since the sample interval limit is 90ms Thread.sleep(100); From c875d15853f7741fdf1ee625b6a55260d948b788 Mon Sep 17 00:00:00 2001 From: Johnny Lim Date: Tue, 18 Jun 2024 07:24:11 +0900 Subject: [PATCH 04/74] Polish gh-4846 (#5146) --- .../build.gradle | 2 +- .../MicrometerCollector.java | 6 +-- .../prometheusmetrics/PrometheusConfig.java | 1 + .../prometheusmetrics/PrometheusCounter.java | 1 + .../PrometheusDistributionSummary.java | 1 + .../PrometheusDurationNamingConvention.java | 6 +++ .../PrometheusMeterRegistry.java | 19 ++++----- .../PrometheusNamingConvention.java | 1 + .../PrometheusRenameFilter.java | 1 + .../prometheusmetrics/PrometheusTimer.java | 1 + .../PrometheusMeterRegistryTest.java | 39 +++++++++---------- 11 files changed, 40 insertions(+), 38 deletions(-) diff --git a/implementations/micrometer-registry-prometheus/build.gradle b/implementations/micrometer-registry-prometheus/build.gradle index 4cc1dbee5c..16ce4fa98c 100644 --- a/implementations/micrometer-registry-prometheus/build.gradle +++ b/implementations/micrometer-registry-prometheus/build.gradle @@ -1,4 +1,4 @@ -description = 'MeterRegistry implementation for Prometheus using io.prometheus:prometheus-metrics-core. If you have compatibility issues with this module, you can go back to io.micrometer:micrometer-registry-prometheus-simpleclient that uses io.prometheus:simpleclient_common' +description = 'MeterRegistry implementation for Prometheus using io.prometheus:prometheus-metrics-core. If you have compatibility issues with this module, you can go back to io.micrometer:micrometer-registry-prometheus-simpleclient that uses io.prometheus:simpleclient_common.' dependencies { api project(':micrometer-core') diff --git a/implementations/micrometer-registry-prometheus/src/main/java/io/micrometer/prometheusmetrics/MicrometerCollector.java b/implementations/micrometer-registry-prometheus/src/main/java/io/micrometer/prometheusmetrics/MicrometerCollector.java index eb6fa60370..21817fb3a7 100644 --- a/implementations/micrometer-registry-prometheus/src/main/java/io/micrometer/prometheusmetrics/MicrometerCollector.java +++ b/implementations/micrometer-registry-prometheus/src/main/java/io/micrometer/prometheusmetrics/MicrometerCollector.java @@ -116,10 +116,8 @@ String getConventionName() { return conventionName; } - Family addSamples(Collection dataPointSnapshots) { - for (DataPointSnapshot dataPointSnapshot : dataPointSnapshots) { - this.dataPointSnapshots.add((T) dataPointSnapshot); - } + Family addSamples(Collection dataPointSnapshots) { + this.dataPointSnapshots.addAll(dataPointSnapshots); return this; } diff --git a/implementations/micrometer-registry-prometheus/src/main/java/io/micrometer/prometheusmetrics/PrometheusConfig.java b/implementations/micrometer-registry-prometheus/src/main/java/io/micrometer/prometheusmetrics/PrometheusConfig.java index d0466cc00c..aade7c1a7a 100644 --- a/implementations/micrometer-registry-prometheus/src/main/java/io/micrometer/prometheusmetrics/PrometheusConfig.java +++ b/implementations/micrometer-registry-prometheus/src/main/java/io/micrometer/prometheusmetrics/PrometheusConfig.java @@ -31,6 +31,7 @@ * * @author Jon Schneider * @author Jonatan Ivanov + * @since 1.13.0 */ public interface PrometheusConfig extends MeterRegistryConfig { diff --git a/implementations/micrometer-registry-prometheus/src/main/java/io/micrometer/prometheusmetrics/PrometheusCounter.java b/implementations/micrometer-registry-prometheus/src/main/java/io/micrometer/prometheusmetrics/PrometheusCounter.java index d355d6985d..9ea93aeb96 100644 --- a/implementations/micrometer-registry-prometheus/src/main/java/io/micrometer/prometheusmetrics/PrometheusCounter.java +++ b/implementations/micrometer-registry-prometheus/src/main/java/io/micrometer/prometheusmetrics/PrometheusCounter.java @@ -29,6 +29,7 @@ * * @author Jon Schneider * @author Jonatan Ivanov + * @since 1.13.0 */ public class PrometheusCounter extends AbstractMeter implements Counter { diff --git a/implementations/micrometer-registry-prometheus/src/main/java/io/micrometer/prometheusmetrics/PrometheusDistributionSummary.java b/implementations/micrometer-registry-prometheus/src/main/java/io/micrometer/prometheusmetrics/PrometheusDistributionSummary.java index bd7e3d9401..af33818213 100644 --- a/implementations/micrometer-registry-prometheus/src/main/java/io/micrometer/prometheusmetrics/PrometheusDistributionSummary.java +++ b/implementations/micrometer-registry-prometheus/src/main/java/io/micrometer/prometheusmetrics/PrometheusDistributionSummary.java @@ -31,6 +31,7 @@ * * @author Jon Schneider * @author Jonatan Ivanov + * @since 1.13.0 */ public class PrometheusDistributionSummary extends AbstractDistributionSummary { diff --git a/implementations/micrometer-registry-prometheus/src/main/java/io/micrometer/prometheusmetrics/PrometheusDurationNamingConvention.java b/implementations/micrometer-registry-prometheus/src/main/java/io/micrometer/prometheusmetrics/PrometheusDurationNamingConvention.java index 9112031fa8..0d144f222e 100644 --- a/implementations/micrometer-registry-prometheus/src/main/java/io/micrometer/prometheusmetrics/PrometheusDurationNamingConvention.java +++ b/implementations/micrometer-registry-prometheus/src/main/java/io/micrometer/prometheusmetrics/PrometheusDurationNamingConvention.java @@ -15,6 +15,12 @@ */ package io.micrometer.prometheusmetrics; +/** + * {@link PrometheusNamingConvention} with {@code _duration} suffix for timers. + * + * @author Clint Checketts + * @since 1.13.0 + */ public class PrometheusDurationNamingConvention extends PrometheusNamingConvention { public PrometheusDurationNamingConvention() { diff --git a/implementations/micrometer-registry-prometheus/src/main/java/io/micrometer/prometheusmetrics/PrometheusMeterRegistry.java b/implementations/micrometer-registry-prometheus/src/main/java/io/micrometer/prometheusmetrics/PrometheusMeterRegistry.java index ecb957bc52..641acb4fb4 100644 --- a/implementations/micrometer-registry-prometheus/src/main/java/io/micrometer/prometheusmetrics/PrometheusMeterRegistry.java +++ b/implementations/micrometer-registry-prometheus/src/main/java/io/micrometer/prometheusmetrics/PrometheusMeterRegistry.java @@ -64,6 +64,7 @@ * @author Jon Schneider * @author Johnny Lim * @author Jonatan Ivanov + * @since 1.13.0 */ public class PrometheusMeterRegistry extends MeterRegistry { @@ -128,7 +129,6 @@ public String scrape() { * @param contentType the scrape Content-Type * @return the scrape body * @see ExpositionFormats - * @since 1.7.0 */ public String scrape(String contentType) { ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); @@ -175,7 +175,6 @@ private void scrape(OutputStream outputStream, String contentType, MetricSnapsho * @return Content that should be included in the response body for an endpoint * designated for Prometheus to scrape from. * @see ExpositionFormats - * @since 1.7.0 */ public String scrape(String contentType, @Nullable Set includedNames) { ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); @@ -197,7 +196,6 @@ public String scrape(String contentType, @Nullable Set includedNames) { * {@code null}. * @throws IOException if writing fails * @see ExpositionFormats - * @since 1.7.0 */ public void scrape(OutputStream outputStream, String contentType, @Nullable Set includedNames) throws IOException { @@ -258,7 +256,7 @@ public DistributionSummary newDistributionSummary(Meter.Id id, // TODO: remove this cumulative -> non cumulative conversion // ClassicHistogramBuckets is not cumulative but the // histograms we use are cumulative - // so we converting it to non-cumulative just for the + // so we convert it to non-cumulative just for the // Prometheus client library // can convert it back to cumulative. buckets.add(histogramCounts[0].bucket()); @@ -287,7 +285,7 @@ public DistributionSummary newDistributionSummary(Meter.Id id, // a bunch of Collector.MetricFamilySamples.Sample // that has an le label for Prometheus and a vmrange label for // Victoria. - // That control is gone now, we don’t have control over the + // That control is gone now, so we don’t have control over the // output and when HistogramDataPointSnapshot is written, the // bucket name is hardcoded to le. } @@ -482,7 +480,7 @@ private void addDistributionStatisticSamples(Meter.Id id, MicrometerCollector co // TODO: remove this cumulative -> non cumulative conversion // ClassicHistogramBuckets is not cumulative but the histograms we // use are cumulative - // so we converting it to non-cumulative just for the Prometheus + // so we convert it to non-cumulative just for the Prometheus // client library // can convert it back to cumulative. buckets.add(histogramCounts[0].bucket(getBaseTimeUnit())); @@ -511,7 +509,7 @@ private void addDistributionStatisticSamples(Meter.Id id, MicrometerCollector co // a bunch of Collector.MetricFamilySamples.Sample // that has an le label for Prometheus and a vmrange label for // Victoria. - // That control is gone now, we don’t have control over the + // That control is gone now, so we don’t have control over the // output and when HistogramDataPointSnapshot is written, the // bucket name is hardcoded to le. } @@ -604,7 +602,6 @@ protected DistributionStatisticConfig defaultHistogramConfig() { * MeterRegistry.Config#onMeterRegistrationFailed(BiConsumer)} when you want meters * with the same name but different tags to cause an unchecked exception. * @return This registry - * @since 1.6.0 */ public PrometheusMeterRegistry throwExceptionOnRegistrationFailure() { config().onMeterRegistrationFailed((id, reason) -> { @@ -616,9 +613,7 @@ public PrometheusMeterRegistry throwExceptionOnRegistrationFailure() { private enum Format { - TEXT_004("text/plain; version=0.0.4; charset=utf-8"), - OPENMETRICS_100("application/openmetrics-text; version=1.0.0; charset=utf-8"), - PROTOBUF("application/vnd.google.protobuf; proto=io.prometheus.client.MetricFamily; encoding=delimited"); + TEXT_004("text/plain; version=0.0.4; charset=utf-8"); private final String contentType; @@ -626,7 +621,7 @@ private enum Format { this.contentType = contentType; } - public String getContentType() { + String getContentType() { return contentType; } diff --git a/implementations/micrometer-registry-prometheus/src/main/java/io/micrometer/prometheusmetrics/PrometheusNamingConvention.java b/implementations/micrometer-registry-prometheus/src/main/java/io/micrometer/prometheusmetrics/PrometheusNamingConvention.java index cd1fbaef71..dfb7bbbba3 100644 --- a/implementations/micrometer-registry-prometheus/src/main/java/io/micrometer/prometheusmetrics/PrometheusNamingConvention.java +++ b/implementations/micrometer-registry-prometheus/src/main/java/io/micrometer/prometheusmetrics/PrometheusNamingConvention.java @@ -26,6 +26,7 @@ * docs for a specification of the constraints on metric names and labels * * @author Jon Schneider + * @since 1.13.0 */ public class PrometheusNamingConvention implements NamingConvention { diff --git a/implementations/micrometer-registry-prometheus/src/main/java/io/micrometer/prometheusmetrics/PrometheusRenameFilter.java b/implementations/micrometer-registry-prometheus/src/main/java/io/micrometer/prometheusmetrics/PrometheusRenameFilter.java index 1bef1b2fb6..8f791fa882 100644 --- a/implementations/micrometer-registry-prometheus/src/main/java/io/micrometer/prometheusmetrics/PrometheusRenameFilter.java +++ b/implementations/micrometer-registry-prometheus/src/main/java/io/micrometer/prometheusmetrics/PrometheusRenameFilter.java @@ -26,6 +26,7 @@ * name. * * @author Tommy Ludwig + * @since 1.13.0 */ public class PrometheusRenameFilter implements MeterFilter { diff --git a/implementations/micrometer-registry-prometheus/src/main/java/io/micrometer/prometheusmetrics/PrometheusTimer.java b/implementations/micrometer-registry-prometheus/src/main/java/io/micrometer/prometheusmetrics/PrometheusTimer.java index 3719dd2d2c..dc8585f9f5 100644 --- a/implementations/micrometer-registry-prometheus/src/main/java/io/micrometer/prometheusmetrics/PrometheusTimer.java +++ b/implementations/micrometer-registry-prometheus/src/main/java/io/micrometer/prometheusmetrics/PrometheusTimer.java @@ -33,6 +33,7 @@ * * @author Jon Schneider * @author Jonatan Ivanov + * @since 1.13.0 */ public class PrometheusTimer extends AbstractTimer { diff --git a/implementations/micrometer-registry-prometheus/src/test/java/io/micrometer/prometheusmetrics/PrometheusMeterRegistryTest.java b/implementations/micrometer-registry-prometheus/src/test/java/io/micrometer/prometheusmetrics/PrometheusMeterRegistryTest.java index d2475e39e0..40e0d1be33 100644 --- a/implementations/micrometer-registry-prometheus/src/test/java/io/micrometer/prometheusmetrics/PrometheusMeterRegistryTest.java +++ b/implementations/micrometer-registry-prometheus/src/test/java/io/micrometer/prometheusmetrics/PrometheusMeterRegistryTest.java @@ -36,7 +36,6 @@ import java.util.concurrent.*; import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.atomic.AtomicLong; -import java.util.stream.Collectors; import static io.micrometer.core.instrument.MockClock.clock; import static java.util.Collections.emptyList; @@ -161,12 +160,10 @@ void typedCustomMeters() { MetricSnapshot snapshot = registry.getPrometheusRegistry().scrape().get(0); assertThat(snapshot).describedAs("custom counter with a type of COUNTER").isInstanceOf(CounterSnapshot.class); - assertThat( - snapshot.getDataPoints().get(0).getLabels().stream().map(Label::getName).collect(Collectors.toList())) - .containsExactly("statistic"); - assertThat( - snapshot.getDataPoints().get(0).getLabels().stream().map(Label::getValue).collect(Collectors.toList())) - .containsExactly("COUNT"); + assertThat(snapshot.getDataPoints().get(0).getLabels().stream().map(Label::getName)).singleElement() + .isEqualTo("statistic"); + assertThat(snapshot.getDataPoints().get(0).getLabels().stream().map(Label::getValue)).singleElement() + .isEqualTo("COUNT"); } @DisplayName("attempts to register different meter types with the same name fail somewhat gracefully") @@ -379,7 +376,7 @@ void functionCounterNamingConvention() { assertThat(registry.scrape()).contains("api_requests_total 1.0"); } - private Condition> withNameAndQuantile(String name) { + private Condition> withNameAndQuantile(String name) { return new Condition<>( metricSnapshots -> ((MetricSnapshots) metricSnapshots).stream() .filter(snapshot -> snapshot.getMetadata().getPrometheusName().equals(name)) @@ -511,8 +508,8 @@ void filteredMetricFamilySamplesWithCounter() { } private void assertFilteredMetricSnapshots(String[] includedNames, String[] expectedNames) { - MetricSnapshots snapshots = registry.getPrometheusRegistry() - .scrape(name -> new HashSet<>(Arrays.asList(includedNames)).contains(name)); + Set includeNameSet = new HashSet<>(Arrays.asList(includedNames)); + MetricSnapshots snapshots = registry.getPrometheusRegistry().scrape(name -> includeNameSet.contains(name)); String[] names = snapshots.stream() .map(snapshot -> snapshot.getMetadata().getPrometheusName()) .toArray(String[]::new); @@ -542,7 +539,7 @@ void filteredMetricFamilySamplesWithTimer() { @Test void filteredMetricFamilySamplesWithLongTaskTimer() { String[] includedNames = { "my_long_task_timer_seconds", "my_long_task_timer_seconds_max" }; - String[] expectedNames = { "my_long_task_timer_seconds_max", "my_long_task_timer_seconds" }; + String[] expectedNames = { "my_long_task_timer_seconds", "my_long_task_timer_seconds_max" }; LongTaskTimer.builder("my.long.task.timer").register(registry); assertFilteredMetricSnapshots(includedNames, expectedNames); @@ -585,7 +582,7 @@ void timerSumAndMaxHaveCorrectBaseUnit_whenPercentileHistogramEnabled() { @Test @Issue("#4988") - void longTaskTimerRecordingsSchouldBeCorrect() { + void longTaskTimerRecordingsShouldBeCorrect() { LongTaskTimer ltt = LongTaskTimer.builder("test.ltt").publishPercentileHistogram().register(registry); String result = registry.scrape(); @@ -870,11 +867,11 @@ void openMetricsScrapeWithExemplars() throws InterruptedException { timer.record(Duration.ofMillis(150)); Timer timerWithHistogram = Timer.builder("timer.withHistogram").publishPercentileHistogram().register(registry); - timerWithHistogram.record(15, TimeUnit.MILLISECONDS); + timerWithHistogram.record(Duration.ofMillis(15)); sleepToAvoidRateLimiting(); - timerWithHistogram.record(150, TimeUnit.MILLISECONDS); + timerWithHistogram.record(Duration.ofMillis(150)); sleepToAvoidRateLimiting(); - timerWithHistogram.record(60, TimeUnit.SECONDS); + timerWithHistogram.record(Duration.ofSeconds(60)); Timer timerWithSlos = Timer.builder("timer.withSlos") .serviceLevelObjectives(Duration.ofMillis(100), Duration.ofMillis(200), Duration.ofMillis(300)) @@ -899,14 +896,14 @@ void openMetricsScrapeWithExemplars() throws InterruptedException { sleepToAvoidRateLimiting(); summaryWithHistogram.record(15); - DistributionSummary slos = DistributionSummary.builder("summary.withSlos") + DistributionSummary summaryWithSlos = DistributionSummary.builder("summary.withSlos") .serviceLevelObjectives(100, 200, 300) .register(registry); - slos.record(10); + summaryWithSlos.record(10); sleepToAvoidRateLimiting(); - slos.record(1_000); + summaryWithSlos.record(1_000); sleepToAvoidRateLimiting(); - slos.record(250); + summaryWithSlos.record(250); String scraped = registry.scrape("application/openmetrics-text"); assertThat(scraped).contains("my_counter_total 1.0 # {span_id=\"1\",trace_id=\"2\"} 1.0 "); @@ -1010,10 +1007,10 @@ public Properties prometheusProperties() { } }; - return new PrometheusMeterRegistry(prometheusConfig, prometheusRegistry, clock, new TestSpanContex()); + return new PrometheusMeterRegistry(prometheusConfig, prometheusRegistry, clock, new TestSpanContext()); } - static class TestSpanContex implements SpanContext { + static class TestSpanContext implements SpanContext { private final AtomicLong count = new AtomicLong(); From 98ada249275a943bdda333331a6c1e36b29d54b6 Mon Sep 17 00:00:00 2001 From: Johnny Lim Date: Tue, 18 Jun 2024 07:39:43 +0900 Subject: [PATCH 05/74] Add Javadoc since for PushMeterRegistry.startMessage() (#5142) See gh-4848 --- .../io/micrometer/core/instrument/push/PushMeterRegistry.java | 1 + 1 file changed, 1 insertion(+) diff --git a/micrometer-core/src/main/java/io/micrometer/core/instrument/push/PushMeterRegistry.java b/micrometer-core/src/main/java/io/micrometer/core/instrument/push/PushMeterRegistry.java index 843b8e0782..598aabcb69 100644 --- a/micrometer-core/src/main/java/io/micrometer/core/instrument/push/PushMeterRegistry.java +++ b/micrometer-core/src/main/java/io/micrometer/core/instrument/push/PushMeterRegistry.java @@ -123,6 +123,7 @@ public void start(ThreadFactory threadFactory) { * registry implementation that may be helpful in troubleshooting. By default, the * registry class name and step interval are included. * @return message to log on registry start + * @since 1.13.0 */ protected String startMessage() { return "publishing metrics for " + getClass().getSimpleName() + " every " + TimeUtils.format(config.step()); From 4fb7f686915e673028b154561d87626e554512dc Mon Sep 17 00:00:00 2001 From: Johnny Lim Date: Tue, 18 Jun 2024 07:41:24 +0900 Subject: [PATCH 06/74] Fix log to include stack trace in OtlpMeterRegistry.publish() (#5141) See gh-4829 --- .../java/io/micrometer/registry/otlp/OtlpMeterRegistry.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/implementations/micrometer-registry-otlp/src/main/java/io/micrometer/registry/otlp/OtlpMeterRegistry.java b/implementations/micrometer-registry-otlp/src/main/java/io/micrometer/registry/otlp/OtlpMeterRegistry.java index 1c0a26fbe4..9895de732b 100644 --- a/implementations/micrometer-registry-otlp/src/main/java/io/micrometer/registry/otlp/OtlpMeterRegistry.java +++ b/implementations/micrometer-registry-otlp/src/main/java/io/micrometer/registry/otlp/OtlpMeterRegistry.java @@ -172,7 +172,8 @@ protected void publish() { } } catch (Throwable e) { - logger.warn("Failed to publish metrics to OTLP receiver (context: {})", getConfigurationContext(), e); + logger.warn(String.format("Failed to publish metrics to OTLP receiver (context: %s)", + getConfigurationContext()), e); } } } From f2fba9cf508bef456bd8bc0f9cb7ab4ddbf74a26 Mon Sep 17 00:00:00 2001 From: Johnny Lim Date: Fri, 21 Jun 2024 07:28:31 +0900 Subject: [PATCH 07/74] Polish gh-4780 (#5107) --- .../types/DynatraceLongTaskTimer.java | 2 +- .../types/DynatraceSummarySnapshot.java | 4 ++++ .../types/DynatraceLongTaskTimerTest.java | 22 ++++++------------- 3 files changed, 12 insertions(+), 16 deletions(-) diff --git a/implementations/micrometer-registry-dynatrace/src/main/java/io/micrometer/dynatrace/types/DynatraceLongTaskTimer.java b/implementations/micrometer-registry-dynatrace/src/main/java/io/micrometer/dynatrace/types/DynatraceLongTaskTimer.java index 758662fc9a..ed89209e56 100644 --- a/implementations/micrometer-registry-dynatrace/src/main/java/io/micrometer/dynatrace/types/DynatraceLongTaskTimer.java +++ b/implementations/micrometer-registry-dynatrace/src/main/java/io/micrometer/dynatrace/types/DynatraceLongTaskTimer.java @@ -59,7 +59,7 @@ public DynatraceSummarySnapshot takeSummarySnapshot(TimeUnit unit) { // sample.duration(...) will return -1 if the task is already finished // (only currently active tasks are measured). // -1 will be ignored in recordNonNegative. - super.forEachActive(sample -> summary.recordNonNegative(sample.duration(unit))); + forEachActive(sample -> summary.recordNonNegative(sample.duration(unit))); return summary.takeSummarySnapshot(); } diff --git a/implementations/micrometer-registry-dynatrace/src/main/java/io/micrometer/dynatrace/types/DynatraceSummarySnapshot.java b/implementations/micrometer-registry-dynatrace/src/main/java/io/micrometer/dynatrace/types/DynatraceSummarySnapshot.java index d5e6da44ac..a2f5b200a3 100644 --- a/implementations/micrometer-registry-dynatrace/src/main/java/io/micrometer/dynatrace/types/DynatraceSummarySnapshot.java +++ b/implementations/micrometer-registry-dynatrace/src/main/java/io/micrometer/dynatrace/types/DynatraceSummarySnapshot.java @@ -26,6 +26,10 @@ @Immutable public final class DynatraceSummarySnapshot { + /** + * For empty value. + * @since 1.9.18 + */ public static final DynatraceSummarySnapshot EMPTY = new DynatraceSummarySnapshot(0, 0, 0, 0); private final double min; diff --git a/implementations/micrometer-registry-dynatrace/src/test/java/io/micrometer/dynatrace/types/DynatraceLongTaskTimerTest.java b/implementations/micrometer-registry-dynatrace/src/test/java/io/micrometer/dynatrace/types/DynatraceLongTaskTimerTest.java index e2c3c39acb..383d39ca25 100644 --- a/implementations/micrometer-registry-dynatrace/src/test/java/io/micrometer/dynatrace/types/DynatraceLongTaskTimerTest.java +++ b/implementations/micrometer-registry-dynatrace/src/test/java/io/micrometer/dynatrace/types/DynatraceLongTaskTimerTest.java @@ -19,7 +19,6 @@ import io.micrometer.core.instrument.MockClock; import io.micrometer.core.instrument.Tags; import io.micrometer.core.instrument.distribution.DistributionStatisticConfig; -import org.assertj.core.data.Offset; import org.junit.jupiter.api.Test; import java.time.Duration; @@ -35,8 +34,6 @@ */ class DynatraceLongTaskTimerTest { - private static final Offset OFFSET = Offset.offset(0.0001); - private static final Meter.Id ID = new Meter.Id("test.id", Tags.empty(), "1", "desc", Meter.Type.DISTRIBUTION_SUMMARY); @@ -44,8 +41,6 @@ class DynatraceLongTaskTimerTest { private static final MockClock CLOCK = new MockClock(); - private static final Offset TOLERANCE = Offset.offset(0.000001); - @Test void singleTaskValuesAreRecorded() throws InterruptedException { DynatraceLongTaskTimer ltt = new DynatraceLongTaskTimer(ID, CLOCK, TimeUnit.MILLISECONDS, @@ -74,13 +69,11 @@ void singleTaskValuesAreRecorded() throws InterruptedException { // can release the background task stopLatch.countDown(); - assertThat(snapshot.getMin()).isCloseTo(100, TOLERANCE); - assertThat(snapshot.getMax()).isCloseTo(100, TOLERANCE); + assertThat(snapshot.getMin()).isEqualTo(100); + assertThat(snapshot.getMax()).isEqualTo(100); assertThat(snapshot.getCount()).isEqualTo(1); // in the case of count == 1, the total has to be equal to min and max - assertThat(snapshot.getTotal()).isGreaterThan(0) - .isCloseTo(snapshot.getMin(), TOLERANCE) - .isCloseTo(snapshot.getMax(), TOLERANCE); + assertThat(snapshot.getTotal()).isEqualTo(snapshot.getMin()).isEqualTo(snapshot.getMax()); } @Test @@ -152,20 +145,19 @@ void parallelTasksValuesAreRecorded() throws InterruptedException { // Task 1 has been "running" for 70ms at the time of recording and will // supply the max - assertThat(snapshot.getMax()).isCloseTo(70, OFFSET); + assertThat(snapshot.getMax()).isEqualTo(70); // Task 2 has been "running" for only 30ms at the time of recording and // will supply the min - assertThat(snapshot.getMin()).isCloseTo(30, OFFSET); + assertThat(snapshot.getMin()).isEqualTo(30); // Both tasks have been running in parallel. // After the second CLOCK.add(Duration) is called, the first task has been running // for 70ms, and the second task has been running for 30ms // together, they have been running for 100ms in total. - assertThat(snapshot.getTotal()).isCloseTo(100, OFFSET); + assertThat(snapshot.getTotal()).isEqualTo(100); // Two tasks were running in parallel. assertThat(snapshot.getCount()).isEqualTo(2); // On the clock, 70ms have passed. MockClock starts at 1, that's why the result - // here - // is 71 instead of 70. + // here is 71 instead of 70. assertThat(CLOCK.wallTime()).isEqualTo(71); } From 93e1117e880a922f3d07e477f916c6d793a30bd4 Mon Sep 17 00:00:00 2001 From: Johnny Lim Date: Fri, 21 Jun 2024 07:34:45 +0900 Subject: [PATCH 08/74] Apply NamingConvention to tags for OtlpMeterRegistry (#5114) See https://github.com/micrometer-metrics/micrometer/issues/4053#issuecomment-1702959975 --- .../registry/otlp/OtlpMeterRegistry.java | 2 +- .../registry/otlp/OtlpMeterRegistryTest.java | 19 +++++++++++++++++++ 2 files changed, 20 insertions(+), 1 deletion(-) diff --git a/implementations/micrometer-registry-otlp/src/main/java/io/micrometer/registry/otlp/OtlpMeterRegistry.java b/implementations/micrometer-registry-otlp/src/main/java/io/micrometer/registry/otlp/OtlpMeterRegistry.java index 1fb0e2bc8a..66bfd7c3f2 100644 --- a/implementations/micrometer-registry-otlp/src/main/java/io/micrometer/registry/otlp/OtlpMeterRegistry.java +++ b/implementations/micrometer-registry-otlp/src/main/java/io/micrometer/registry/otlp/OtlpMeterRegistry.java @@ -298,7 +298,7 @@ private Metric.Builder getMetricBuilder(Meter.Id id) { } private Iterable getTagsForId(Meter.Id id) { - return id.getTags() + return id.getConventionTags(config().namingConvention()) .stream() .map(tag -> createKeyValue(tag.getKey(), tag.getValue())) .collect(Collectors.toList()); diff --git a/implementations/micrometer-registry-otlp/src/test/java/io/micrometer/registry/otlp/OtlpMeterRegistryTest.java b/implementations/micrometer-registry-otlp/src/test/java/io/micrometer/registry/otlp/OtlpMeterRegistryTest.java index 15196abff1..27c7bde880 100644 --- a/implementations/micrometer-registry-otlp/src/test/java/io/micrometer/registry/otlp/OtlpMeterRegistryTest.java +++ b/implementations/micrometer-registry-otlp/src/test/java/io/micrometer/registry/otlp/OtlpMeterRegistryTest.java @@ -16,6 +16,8 @@ package io.micrometer.registry.otlp; import io.micrometer.core.instrument.*; +import io.micrometer.core.instrument.config.NamingConvention; +import io.opentelemetry.proto.metrics.v1.Metric; import org.junit.jupiter.api.Test; import java.io.IOException; @@ -553,4 +555,21 @@ void setResourceAttributesFromEnvironmentVariables() throws Exception { }); } + @Test + void applyCustomNamingConvention() { + registry.config().namingConvention(NamingConvention.snakeCase); + + Gauge gauge = Gauge.builder("test.meter", () -> 1) + .tags("test.tag", "1") + .description("description") + .register(registry); + + Metric metric = registry.writeGauge(gauge); + + assertThat(metric.getName()).isEqualTo("test_meter"); + assertThat(metric.getGauge().getDataPointsList()).singleElement() + .satisfies(dataPoint -> assertThat(dataPoint.getAttributesList()).singleElement() + .satisfies(attribute -> assertThat(attribute.getKey()).isEqualTo("test_tag"))); + } + } From bf1409dbda7c53d171383fbc24071fb70ad97b91 Mon Sep 17 00:00:00 2001 From: Jonatan Ivanov Date: Thu, 20 Jun 2024 17:45:46 -0700 Subject: [PATCH 09/74] Fix quantile value for DistributionSummary for OtlpMeterRegistry Closes gh-5115 Co-authored-by: Johnny Lim --- .../registry/otlp/OtlpMeterRegistry.java | 3 ++- .../registry/otlp/OtlpMeterRegistryTest.java | 21 +++++++++++++++++++ 2 files changed, 23 insertions(+), 1 deletion(-) diff --git a/implementations/micrometer-registry-otlp/src/main/java/io/micrometer/registry/otlp/OtlpMeterRegistry.java b/implementations/micrometer-registry-otlp/src/main/java/io/micrometer/registry/otlp/OtlpMeterRegistry.java index 66bfd7c3f2..46310ac276 100644 --- a/implementations/micrometer-registry-otlp/src/main/java/io/micrometer/registry/otlp/OtlpMeterRegistry.java +++ b/implementations/micrometer-registry-otlp/src/main/java/io/micrometer/registry/otlp/OtlpMeterRegistry.java @@ -238,9 +238,10 @@ Metric writeHistogramSupport(HistogramSupport histogramSupport) { .setSum(total) .setCount(count); for (ValueAtPercentile percentile : histogramSnapshot.percentileValues()) { + double value = percentile.value(); summaryData.addQuantileValues(SummaryDataPoint.ValueAtQuantile.newBuilder() .setQuantile(percentile.percentile()) - .setValue(TimeUtils.convert(percentile.value(), TimeUnit.NANOSECONDS, getBaseTimeUnit()))); + .setValue(isTimeBased ? TimeUtils.convert(value, TimeUnit.NANOSECONDS, getBaseTimeUnit()) : value)); } metricBuilder.setSummary(Summary.newBuilder().addDataPoints(summaryData)); return metricBuilder.build(); diff --git a/implementations/micrometer-registry-otlp/src/test/java/io/micrometer/registry/otlp/OtlpMeterRegistryTest.java b/implementations/micrometer-registry-otlp/src/test/java/io/micrometer/registry/otlp/OtlpMeterRegistryTest.java index 27c7bde880..045a47b490 100644 --- a/implementations/micrometer-registry-otlp/src/test/java/io/micrometer/registry/otlp/OtlpMeterRegistryTest.java +++ b/implementations/micrometer-registry-otlp/src/test/java/io/micrometer/registry/otlp/OtlpMeterRegistryTest.java @@ -482,6 +482,27 @@ void distributionSummaryWithHistogramBuckets() { } } + @Test + void distributionSummaryWithWithPercentiles() { + DistributionSummary size = DistributionSummary.builder("http.response.size") + .baseUnit("bytes") + .publishPercentiles(0.5, 0.9, 0.99) + .register(registry); + size.record(100); + size.record(15); + size.record(2233); + clock.add(OtlpConfig.DEFAULT.step()); + size.record(204); + + assertThat(registry.writeHistogramSupport(size).toString()) + .isEqualTo("name: \"http.response.size\"\n" + "unit: \"bytes\"\n" + "summary {\n" + " data_points {\n" + + " start_time_unix_nano: 1000000\n" + " time_unix_nano: 60001000000\n" + " count: 4\n" + + " sum: 2552.0\n" + " quantile_values {\n" + " quantile: 0.5\n" + " value: 200.0\n" + + " }\n" + " quantile_values {\n" + " quantile: 0.9\n" + " value: 200.0\n" + + " }\n" + " quantile_values {\n" + " quantile: 0.99\n" + " value: 200.0\n" + + " }\n" + " }\n" + "}\n"); + } + private double extractValue(String line) { return Double.parseDouble(line.substring(line.lastIndexOf(' '))); } From 4b2147eb465f328e2149bb87ba9db45c457d2657 Mon Sep 17 00:00:00 2001 From: Johnny Lim Date: Sat, 22 Jun 2024 01:47:59 +0900 Subject: [PATCH 10/74] Add Javadoc since for PrometheusConfig.prometheusProperties() (#5227) See gh-4875 --- .../java/io/micrometer/prometheusmetrics/PrometheusConfig.java | 1 + 1 file changed, 1 insertion(+) diff --git a/implementations/micrometer-registry-prometheus/src/main/java/io/micrometer/prometheusmetrics/PrometheusConfig.java b/implementations/micrometer-registry-prometheus/src/main/java/io/micrometer/prometheusmetrics/PrometheusConfig.java index aade7c1a7a..67b1d2f771 100644 --- a/implementations/micrometer-registry-prometheus/src/main/java/io/micrometer/prometheusmetrics/PrometheusConfig.java +++ b/implementations/micrometer-registry-prometheus/src/main/java/io/micrometer/prometheusmetrics/PrometheusConfig.java @@ -68,6 +68,7 @@ default Duration step() { * {@code io.prometheus.exporter.exemplarsOnAllMetricTypes=true}. * @see Prometheus * docs + * @since 1.13.0 */ @Nullable default Properties prometheusProperties() { From 446a2509446acf4e8919131f0aa6dab322c1131a Mon Sep 17 00:00:00 2001 From: "shalk(xiao kun)" Date: Sat, 22 Jun 2024 02:11:16 +0800 Subject: [PATCH 11/74] Update prometheus.adoc (#5139) --- docs/modules/ROOT/pages/implementations/prometheus.adoc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/modules/ROOT/pages/implementations/prometheus.adoc b/docs/modules/ROOT/pages/implementations/prometheus.adoc index 3014c4bba3..4534e5846c 100644 --- a/docs/modules/ROOT/pages/implementations/prometheus.adoc +++ b/docs/modules/ROOT/pages/implementations/prometheus.adoc @@ -43,7 +43,7 @@ catch (IOException e) { ---- <1> The `PrometheusMeterRegistry` has a `scrape()` function that knows how to supply the String data necessary for the scrape. All you have to do is wire it to an endpoint. -If you use the "new" client (`micrometer-registry-prometheus`), you can alternatively use `io.prometheus.metrics.exporter.httpserver.HTTPServer`, which you can find in `io.prometheus:prometheus-metrics-exporter-httpserver`: +If you use the "new" client (`micrometer-registry-prometheus`), you can alternatively use `io.prometheus.metrics.exporter.httpserver.HTTPServer`, which you can find in `io.prometheus:prometheus-metrics-exporter-httpserver` (you need to add it as a dependency if you want to use it): [source,java] ---- From 56eb24ba956675912ae2f4679664ab2e1c5d5a2f Mon Sep 17 00:00:00 2001 From: Johnny Lim Date: Tue, 25 Jun 2024 17:07:54 +0900 Subject: [PATCH 12/74] Upgrade CI machine image to ubuntu-2204:2024.05.1 (#5226) --- .circleci/config.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index 76b11fc510..50468c2fa7 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -25,7 +25,7 @@ executors: machine-executor: working_directory: ~/micrometer machine: - image: ubuntu-2204:2024.01.2 + image: ubuntu-2204:2024.05.1 commands: gradlew-build: From 1e9a05c786acf8aa34dddbd8e4f10dcee7c5b3c2 Mon Sep 17 00:00:00 2001 From: Johnny Lim Date: Tue, 25 Jun 2024 17:08:45 +0900 Subject: [PATCH 13/74] Polish gh-4917 (#5230) --- gradle/libs.versions.toml | 2 +- micrometer-core/build.gradle | 2 +- .../core/instrument/MeterRegistryLoggingTest.java | 7 ++++--- 3 files changed, 6 insertions(+), 5 deletions(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index ce72477dc5..66b1faaeb1 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -163,7 +163,7 @@ jsr107 = { module = "org.jsr107.ri:cache-ri-impl", version.ref = "jsr107" } jsr305 = { module = "com.google.code.findbugs:jsr305", version.ref = "jsr305" } junitBom = { module = "org.junit:junit-bom", version.ref = "junit" } junitJupiter = { module = "org.junit.jupiter:junit-jupiter", version.ref = "junit" } -junitLoggingExtention = "com.innoq:junit5-logging-extension:0.2.0" +junitLoggingExtension = "com.innoq:junit5-logging-extension:0.2.0" junitPlatformLauncher = { module = "org.junit.platform:junit-platform-launcher", version.ref = "junit-platform" } kafkaClients = { module = "org.apache.kafka:kafka-clients", version.ref = "kafka" } kafkaStreams = { module = "org.apache.kafka:kafka-streams", version.ref = "kafka" } diff --git a/micrometer-core/build.gradle b/micrometer-core/build.gradle index 71472d0bb2..dc883a4c22 100644 --- a/micrometer-core/build.gradle +++ b/micrometer-core/build.gradle @@ -156,7 +156,7 @@ dependencies { } // Needed for LogbackMetrics tests testImplementation libs.slf4jApi - testImplementation(libs.junitLoggingExtention) + testImplementation(libs.junitLoggingExtension) testImplementation libs.mockitoCore5 diff --git a/micrometer-core/src/test/java/io/micrometer/core/instrument/MeterRegistryLoggingTest.java b/micrometer-core/src/test/java/io/micrometer/core/instrument/MeterRegistryLoggingTest.java index f48475828a..204f844f70 100644 --- a/micrometer-core/src/test/java/io/micrometer/core/instrument/MeterRegistryLoggingTest.java +++ b/micrometer-core/src/test/java/io/micrometer/core/instrument/MeterRegistryLoggingTest.java @@ -50,8 +50,9 @@ void meterRegistrationBeforeMeterFilterConfig(LoggingEvents logEvents) { @Test void meterRegistrationBeforeMeterFilterConfigWithDebugLogging(LoggingEvents logEvents) { - Level priorLevel = ((Logger) LoggerFactory.getLogger(SimpleMeterRegistry.class)).getLevel(); - ((Logger) LoggerFactory.getLogger(SimpleMeterRegistry.class)).setLevel(Level.DEBUG); + Logger logger = (Logger) LoggerFactory.getLogger(SimpleMeterRegistry.class); + Level priorLevel = logger.getLevel(); + logger.setLevel(Level.DEBUG); try { registerMetricsAndConfigure(); @@ -65,7 +66,7 @@ void meterRegistrationBeforeMeterFilterConfigWithDebugLogging(LoggingEvents logE + "\tat io.micrometer.core.instrument.MeterRegistryLoggingTest.meterRegistrationBeforeMeterFilterConfigWithDebugLogging\\(MeterRegistryLoggingTest.java:\\d+\\)"); } finally { - ((Logger) LoggerFactory.getLogger(SimpleMeterRegistry.class)).setLevel(priorLevel); + logger.setLevel(priorLevel); } } From 07cb4e6b986c4f57f0972f05f9067715d6923fe0 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 3 Jul 2024 12:43:44 -0700 Subject: [PATCH 14/74] Bump com.amazonaws:aws-java-sdk-cloudwatch from 1.12.739 to 1.12.755 (#5273) Bumps [com.amazonaws:aws-java-sdk-cloudwatch](https://github.com/aws/aws-sdk-java) from 1.12.739 to 1.12.755. - [Changelog](https://github.com/aws/aws-sdk-java/blob/master/CHANGELOG.md) - [Commits](https://github.com/aws/aws-sdk-java/compare/1.12.739...1.12.755) --- updated-dependencies: - dependency-name: com.amazonaws:aws-java-sdk-cloudwatch dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- gradle/libs.versions.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 96fe76ab48..1dc3fd21f7 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -6,7 +6,7 @@ aspectjweaver = "1.8.14" assertj = "3.24.2" awaitility = "4.2.1" # legacy SDK -aws-cloudwatch = "1.12.739" +aws-cloudwatch = "1.12.755" caffeine = "2.9.3" cloudwatch2 = "2.18.41" colt = "1.2.0" From de36f6332789baf327bcea9b09f9917f163e2207 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 3 Jul 2024 12:43:53 -0700 Subject: [PATCH 15/74] Bump io.netty:netty-bom from 4.1.110.Final to 4.1.111.Final (#5242) Bumps [io.netty:netty-bom](https://github.com/netty/netty) from 4.1.110.Final to 4.1.111.Final. - [Commits](https://github.com/netty/netty/compare/netty-4.1.110.Final...netty-4.1.111.Final) --- updated-dependencies: - dependency-name: io.netty:netty-bom dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- gradle/libs.versions.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 1dc3fd21f7..8b97736ee1 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -56,7 +56,7 @@ maven-resolver = "1.8.2" # 5.x requires JDK 11. mockito = "4.11.0" mongo = "4.6.1" -netty = "4.1.110.Final" +netty = "4.1.111.Final" newrelic-api = "5.14.0" okhttp = "4.11.0" postgre = "42.3.10" From 95b5adb82644abf6e9c1900fa9ee0a70c30fabbc Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 3 Jul 2024 12:43:59 -0700 Subject: [PATCH 16/74] Bump com.signalfx.public:signalfx-java from 1.0.42 to 1.0.43 (#5243) Bumps [com.signalfx.public:signalfx-java](https://github.com/signalfx/signalfx-java) from 1.0.42 to 1.0.43. - [Release notes](https://github.com/signalfx/signalfx-java/releases) - [Commits](https://github.com/signalfx/signalfx-java/compare/v1.0.42...v1.0.43) --- updated-dependencies: - dependency-name: com.signalfx.public:signalfx-java dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- gradle/libs.versions.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 8b97736ee1..6b30a3efc3 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -63,7 +63,7 @@ postgre = "42.3.10" prometheus = "0.15.0" reactor = "2020.0.44" rest-assured = "5.3.2" -signalfx = "1.0.42" +signalfx = "1.0.43" slf4j = "1.7.36" spectator-atlas = "1.3.10" spring = "5.3.36" From dd991c2ed59787a2698ae273457a0d9124a3f214 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 3 Jul 2024 12:44:06 -0700 Subject: [PATCH 17/74] Bump spring from 5.3.36 to 5.3.37 (#5240) Bumps `spring` from 5.3.36 to 5.3.37. Updates `org.springframework:spring-context` from 5.3.36 to 5.3.37 - [Release notes](https://github.com/spring-projects/spring-framework/releases) - [Commits](https://github.com/spring-projects/spring-framework/compare/v5.3.36...v5.3.37) Updates `org.springframework:spring-core` from 5.3.36 to 5.3.37 - [Release notes](https://github.com/spring-projects/spring-framework/releases) - [Commits](https://github.com/spring-projects/spring-framework/compare/v5.3.36...v5.3.37) --- updated-dependencies: - dependency-name: org.springframework:spring-context dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: org.springframework:spring-core dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- gradle/libs.versions.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 6b30a3efc3..d87cf7a268 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -66,7 +66,7 @@ rest-assured = "5.3.2" signalfx = "1.0.43" slf4j = "1.7.36" spectator-atlas = "1.3.10" -spring = "5.3.36" +spring = "5.3.37" spring-javaformat = "0.0.42" testcontainers = "1.19.8" tomcat = "8.5.100" From 5c20ace6a9c1c7948caf0ab84e41def6bc0e5695 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 3 Jul 2024 12:44:14 -0700 Subject: [PATCH 18/74] Bump com.gradle.develocity from 3.17.4 to 3.17.5 (#5241) Bumps com.gradle.develocity from 3.17.4 to 3.17.5. --- updated-dependencies: - dependency-name: com.gradle.develocity dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- settings.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/settings.gradle b/settings.gradle index f11e0ebd27..28fb608f9c 100644 --- a/settings.gradle +++ b/settings.gradle @@ -5,7 +5,7 @@ pluginManagement { } plugins { - id 'com.gradle.develocity' version '3.17.4' + id 'com.gradle.develocity' version '3.17.5' id 'io.spring.ge.conventions' version '0.0.17' id 'org.gradle.toolchains.foojay-resolver-convention' version '0.8.0' } From d0fafccb6fcb2f52e469661c99da89b4f2385465 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 3 Jul 2024 12:44:30 -0700 Subject: [PATCH 19/74] Bump io.projectreactor:reactor-bom from 2020.0.44 to 2020.0.45 (#5245) Bumps [io.projectreactor:reactor-bom](https://github.com/reactor/reactor) from 2020.0.44 to 2020.0.45. - [Release notes](https://github.com/reactor/reactor/releases) - [Commits](https://github.com/reactor/reactor/compare/2020.0.44...2020.0.45) --- updated-dependencies: - dependency-name: io.projectreactor:reactor-bom dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- gradle/libs.versions.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index d87cf7a268..92da5224c1 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -61,7 +61,7 @@ newrelic-api = "5.14.0" okhttp = "4.11.0" postgre = "42.3.10" prometheus = "0.15.0" -reactor = "2020.0.44" +reactor = "2020.0.45" rest-assured = "5.3.2" signalfx = "1.0.43" slf4j = "1.7.36" From b9974f098e66deea80791896c7095101d6c68213 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 3 Jul 2024 13:10:57 -0700 Subject: [PATCH 20/74] Bump org.apache.maven:maven-resolver-provider from 3.9.7 to 3.9.8 (#5255) Bumps [org.apache.maven:maven-resolver-provider](https://github.com/apache/maven) from 3.9.7 to 3.9.8. - [Release notes](https://github.com/apache/maven/releases) - [Commits](https://github.com/apache/maven/compare/maven-3.9.7...maven-3.9.8) --- updated-dependencies: - dependency-name: org.apache.maven:maven-resolver-provider dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- gradle/libs.versions.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index f6d536d37e..8546de6d85 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -169,7 +169,7 @@ logback14 = {module = "ch.qos.logback:logback-classic", version = "1.4.14" } log4j = { module = "org.apache.logging.log4j:log4j-core", version.ref = "log4j" } mavenResolverConnectorBasic = { module = "org.apache.maven.resolver:maven-resolver-connector-basic", version.ref = "maven-resolver" } mavenResolverTransportHttp = { module = "org.apache.maven.resolver:maven-resolver-transport-http", version.ref = "maven-resolver" } -mavenResolverProvider = { module = "org.apache.maven:maven-resolver-provider", version = "3.9.7" } +mavenResolverProvider = { module = "org.apache.maven:maven-resolver-provider", version = "3.9.8" } mockitoCore4 = { module = "org.mockito:mockito-core", version.ref = "mockito4" } mockitoCore5 = { module = "org.mockito:mockito-core", version.ref = "mockito5" } mongoSync = { module = "org.mongodb:mongodb-driver-sync", version.ref = "mongo" } From 4acef7a393b37666a04e8c95cdbb52148d19cdc1 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 3 Jul 2024 13:11:06 -0700 Subject: [PATCH 21/74] Bump io.projectreactor:reactor-bom from 2022.0.19 to 2022.0.20 (#5253) Bumps [io.projectreactor:reactor-bom](https://github.com/reactor/reactor) from 2022.0.19 to 2022.0.20. - [Release notes](https://github.com/reactor/reactor/releases) - [Commits](https://github.com/reactor/reactor/compare/2022.0.19...2022.0.20) --- updated-dependencies: - dependency-name: io.projectreactor:reactor-bom dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- gradle/libs.versions.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 8546de6d85..3137da245d 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -65,7 +65,7 @@ newrelic-api = "5.14.0" okhttp = "4.11.0" postgre = "42.6.2" prometheus = "0.16.0" -reactor = "2022.0.19" +reactor = "2022.0.20" rest-assured = "5.3.2" signalfx = "1.0.43" slf4j = "1.7.36" From 614598f7b6cd2b72a8fafd3933e92847df83ee7b Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 3 Jul 2024 13:11:13 -0700 Subject: [PATCH 22/74] Bump com.netflix.spectator:spectator-reg-atlas from 1.7.13 to 1.7.14 (#5250) Bumps [com.netflix.spectator:spectator-reg-atlas](https://github.com/Netflix/spectator) from 1.7.13 to 1.7.14. - [Release notes](https://github.com/Netflix/spectator/releases) - [Changelog](https://github.com/Netflix/spectator/blob/main/CHANGELOG.md) - [Commits](https://github.com/Netflix/spectator/compare/v1.7.13...v1.7.14) --- updated-dependencies: - dependency-name: com.netflix.spectator:spectator-reg-atlas dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- gradle/libs.versions.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 3137da245d..116be0c3b4 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -69,7 +69,7 @@ reactor = "2022.0.20" rest-assured = "5.3.2" signalfx = "1.0.43" slf4j = "1.7.36" -spectator-atlas = "1.7.13" +spectator-atlas = "1.7.14" spring = "5.3.37" spring-javaformat = "0.0.42" testcontainers = "1.19.8" From 1a9fb35661441f331a70dd51b1f3a9f2105ab075 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 3 Jul 2024 13:11:20 -0700 Subject: [PATCH 23/74] Bump junit from 5.10.2 to 5.10.3 (#5251) Bumps `junit` from 5.10.2 to 5.10.3. Updates `org.junit:junit-bom` from 5.10.2 to 5.10.3 - [Release notes](https://github.com/junit-team/junit5/releases) - [Commits](https://github.com/junit-team/junit5/compare/r5.10.2...r5.10.3) Updates `org.junit.jupiter:junit-jupiter` from 5.10.2 to 5.10.3 - [Release notes](https://github.com/junit-team/junit5/releases) - [Commits](https://github.com/junit-team/junit5/compare/r5.10.2...r5.10.3) --- updated-dependencies: - dependency-name: org.junit:junit-bom dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: org.junit.jupiter:junit-jupiter dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- gradle/libs.versions.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 116be0c3b4..62e273dcea 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -49,7 +49,7 @@ jmh = "1.37" jooq = "3.14.16" jsr107 = "1.1.1" jsr305 = "3.0.2" -junit = "5.10.2" +junit = "5.10.3" junit-platform = "1.10.2" kafka = "2.8.2" kafka-junit = "4.2.10" From 24bb9c20a4b627faa98bd89a41d39b5ce2d92f39 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 3 Jul 2024 13:12:11 -0700 Subject: [PATCH 24/74] Bump org.junit.platform:junit-platform-launcher from 1.10.2 to 1.10.3 (#5248) Bumps [org.junit.platform:junit-platform-launcher](https://github.com/junit-team/junit5) from 1.10.2 to 1.10.3. - [Release notes](https://github.com/junit-team/junit5/releases) - [Commits](https://github.com/junit-team/junit5/commits) --- updated-dependencies: - dependency-name: org.junit.platform:junit-platform-launcher dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- gradle/libs.versions.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 62e273dcea..8c542e0228 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -50,7 +50,7 @@ jooq = "3.14.16" jsr107 = "1.1.1" jsr305 = "3.0.2" junit = "5.10.3" -junit-platform = "1.10.2" +junit-platform = "1.10.3" kafka = "2.8.2" kafka-junit = "4.2.10" latency-utils = "2.0.3" From 7794bead664f22e528f3db11b32df3f2e19dbbf8 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 3 Jul 2024 13:23:35 -0700 Subject: [PATCH 25/74] Bump software.amazon.awssdk:cloudwatch from 2.25.69 to 2.25.70 (#5277) Bumps software.amazon.awssdk:cloudwatch from 2.25.69 to 2.25.70. --- updated-dependencies: - dependency-name: software.amazon.awssdk:cloudwatch dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- gradle/libs.versions.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index fd60206819..a5a633c8f8 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -7,7 +7,7 @@ aspectjweaver = "1.9.22.1" assertj = "3.25.3" awaitility = "4.2.1" caffeine = "2.9.3" -cloudwatch2 = "2.25.69" +cloudwatch2 = "2.25.70" colt = "1.2.0" dagger = "2.51.1" dropwizard-metrics = "4.2.26" From b1dd400e8fca52488e5943b287cba3777dde5455 Mon Sep 17 00:00:00 2001 From: Robert Mihaly Date: Thu, 9 May 2024 14:06:08 +0200 Subject: [PATCH 26/74] Backport fix AspectJ pointcut syntax Backport of: "Fix AspectJ pointcut syntax (#5058)" AspectJ pointcuts use the `&&` and `!` operators and the alternative word form syntax with `and` and `not` operators is only allowed for load-time weaving with `aop.xml` which cannot use the symbols in the XML. This fixes compile-time weaving support which failed on the `and not` syntax. Closes gh-5285 Backport-Issue: gh-5058 Co-authored-by: Jonatan Ivanov --- .../src/main/java/io/micrometer/core/aop/CountedAspect.java | 2 +- .../src/main/java/io/micrometer/core/aop/TimedAspect.java | 2 +- .../main/java/io/micrometer/observation/aop/ObservedAspect.java | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/micrometer-core/src/main/java/io/micrometer/core/aop/CountedAspect.java b/micrometer-core/src/main/java/io/micrometer/core/aop/CountedAspect.java index d0682e98eb..92785e85d4 100644 --- a/micrometer-core/src/main/java/io/micrometer/core/aop/CountedAspect.java +++ b/micrometer-core/src/main/java/io/micrometer/core/aop/CountedAspect.java @@ -167,7 +167,7 @@ public CountedAspect(MeterRegistry registry, Function Date: Mon, 8 Jul 2024 17:31:41 -0700 Subject: [PATCH 27/74] Revert "Backport fix AspectJ pointcut syntax" This reverts commit b1dd400e8fca52488e5943b287cba3777dde5455. --- .../src/main/java/io/micrometer/core/aop/CountedAspect.java | 2 +- .../src/main/java/io/micrometer/core/aop/TimedAspect.java | 2 +- .../main/java/io/micrometer/observation/aop/ObservedAspect.java | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/micrometer-core/src/main/java/io/micrometer/core/aop/CountedAspect.java b/micrometer-core/src/main/java/io/micrometer/core/aop/CountedAspect.java index 92785e85d4..d0682e98eb 100644 --- a/micrometer-core/src/main/java/io/micrometer/core/aop/CountedAspect.java +++ b/micrometer-core/src/main/java/io/micrometer/core/aop/CountedAspect.java @@ -167,7 +167,7 @@ public CountedAspect(MeterRegistry registry, Function Date: Tue, 9 Jul 2024 11:40:33 +0900 Subject: [PATCH 28/74] Avoid calling naming convention on scrape (#5288) The naming convention was being called on each scrape for each metric to create the MetricMetadata. This is unnecessary because we already have the computed convention name specifically to avoid needing to call the convention again. See gh-5229 --- .../PrometheusMeterRegistryTest.java | 45 ++++++++++ .../PrometheusMeterRegistry.java | 82 ++++++++++--------- .../PrometheusMeterRegistryTest.java | 45 ++++++++++ 3 files changed, 133 insertions(+), 39 deletions(-) diff --git a/implementations/micrometer-registry-prometheus-simpleclient/src/test/java/io/micrometer/prometheus/PrometheusMeterRegistryTest.java b/implementations/micrometer-registry-prometheus-simpleclient/src/test/java/io/micrometer/prometheus/PrometheusMeterRegistryTest.java index 0bc53ffcec..cf4c0da227 100644 --- a/implementations/micrometer-registry-prometheus-simpleclient/src/test/java/io/micrometer/prometheus/PrometheusMeterRegistryTest.java +++ b/implementations/micrometer-registry-prometheus-simpleclient/src/test/java/io/micrometer/prometheus/PrometheusMeterRegistryTest.java @@ -15,6 +15,7 @@ */ package io.micrometer.prometheus; +import io.micrometer.common.lang.Nullable; import io.micrometer.core.Issue; import io.micrometer.core.instrument.Timer; import io.micrometer.core.instrument.*; @@ -717,6 +718,50 @@ void noExemplarsIfNoSampler() { assertThat(scraped).endsWith("# EOF\n"); } + @Test + @Issue("#5229") + void doesNotCallConventionOnScrape() { + CountingPrometheusNamingConvention convention = new CountingPrometheusNamingConvention(); + registry.config().namingConvention(convention); + + Timer.builder("timer").tag("k1", "v1").description("my timer").register(registry); + Counter.builder("counter").tag("k1", "v1").description("my counter").register(registry); + DistributionSummary.builder("summary").tag("k1", "v1").description("my summary").register(registry); + Gauge.builder("gauge", new AtomicInteger(), AtomicInteger::doubleValue) + .tag("k1", "v1") + .description("my gauge") + .register(registry); + LongTaskTimer.builder("long.task.timer").tag("k1", "v1").description("my long task timer").register(registry); + + int expectedNameCount = convention.nameCount.get(); + int expectedTagKeyCount = convention.tagKeyCount.get(); + + registry.scrape(); + + assertThat(convention.nameCount.get()).isEqualTo(expectedNameCount); + assertThat(convention.tagKeyCount.get()).isEqualTo(expectedTagKeyCount); + } + + private static class CountingPrometheusNamingConvention extends PrometheusNamingConvention { + + AtomicInteger nameCount = new AtomicInteger(); + + AtomicInteger tagKeyCount = new AtomicInteger(); + + @Override + public String name(String name, Meter.Type type, @Nullable String baseUnit) { + nameCount.incrementAndGet(); + return super.name(name, type, baseUnit); + } + + @Override + public String tagKey(String key) { + tagKeyCount.incrementAndGet(); + return super.tagKey(key); + } + + } + static class TestSpanContextSupplier implements SpanContextSupplier { private final AtomicLong count = new AtomicLong(); diff --git a/implementations/micrometer-registry-prometheus/src/main/java/io/micrometer/prometheusmetrics/PrometheusMeterRegistry.java b/implementations/micrometer-registry-prometheus/src/main/java/io/micrometer/prometheusmetrics/PrometheusMeterRegistry.java index 641acb4fb4..748efa48df 100644 --- a/implementations/micrometer-registry-prometheus/src/main/java/io/micrometer/prometheusmetrics/PrometheusMeterRegistry.java +++ b/implementations/micrometer-registry-prometheus/src/main/java/io/micrometer/prometheusmetrics/PrometheusMeterRegistry.java @@ -213,8 +213,8 @@ public Counter newCounter(Meter.Id id) { (conventionName, tagKeys) -> Stream.of(new MicrometerCollector.Family<>(conventionName, family -> new CounterSnapshot(family.metadata, family.dataPointSnapshots), - getMetadata(id), new CounterDataPointSnapshot(counter.count(), - Labels.of(tagKeys, tagValues), counter.exemplar(), 0)))); + getMetadata(conventionName, id.getDescription()), new CounterDataPointSnapshot( + counter.count(), Labels.of(tagKeys, tagValues), counter.exemplar(), 0)))); }); return counter; } @@ -246,9 +246,9 @@ public DistributionSummary newDistributionSummary(Meter.Id id, Exemplars exemplars = summary.exemplars(); families.add(new MicrometerCollector.Family<>(conventionName, - family -> new SummarySnapshot(family.metadata, family.dataPointSnapshots), getMetadata(id), - new SummaryDataPointSnapshot(count, sum, quantiles, Labels.of(tagKeys, tagValues), - exemplars, 0))); + family -> new SummarySnapshot(family.metadata, family.dataPointSnapshots), + getMetadata(conventionName, id.getDescription()), new SummaryDataPointSnapshot(count, sum, + quantiles, Labels.of(tagKeys, tagValues), exemplars, 0))); } else { List buckets = new ArrayList<>(); @@ -277,8 +277,9 @@ public DistributionSummary newDistributionSummary(Meter.Id id, families.add(new MicrometerCollector.Family<>(conventionName, family -> new io.prometheus.metrics.model.snapshots.HistogramSnapshot(family.metadata, family.dataPointSnapshots), - getMetadata(id), new HistogramDataPointSnapshot(ClassicHistogramBuckets.of(buckets, counts), - sum, Labels.of(tagKeys, tagValues), exemplars, 0))); + getMetadata(conventionName, id.getDescription()), + new HistogramDataPointSnapshot(ClassicHistogramBuckets.of(buckets, counts), sum, + Labels.of(tagKeys, tagValues), exemplars, 0))); // TODO: Add support back for VictoriaMetrics // Previously we had low-level control so a histogram was just @@ -292,7 +293,7 @@ public DistributionSummary newDistributionSummary(Meter.Id id, families.add(new MicrometerCollector.Family<>(conventionName + "_max", family -> new GaugeSnapshot(family.metadata, family.dataPointSnapshots), - getMetadata(id, "_max"), + getMetadata(conventionName + "_max", id.getDescription()), new GaugeDataPointSnapshot(summary.max(), Labels.of(tagKeys, tagValues), null))); return families.build(); @@ -319,17 +320,18 @@ protected io.micrometer.core.instrument.Gauge newGauge(Meter.Id id, @Nullabl applyToCollector(id, (collector) -> { List tagValues = tagValues(id); if (id.getName().endsWith(".info")) { - collector - .add(tagValues, - (conventionName, tagKeys) -> Stream.of(new MicrometerCollector.Family<>(conventionName, - family -> new InfoSnapshot(family.metadata, family.dataPointSnapshots), - getMetadata(id), new InfoDataPointSnapshot(Labels.of(tagKeys, tagValues))))); + collector.add(tagValues, + (conventionName, + tagKeys) -> Stream.of(new MicrometerCollector.Family<>(conventionName, + family -> new InfoSnapshot(family.metadata, family.dataPointSnapshots), + getMetadata(conventionName, id.getDescription()), + new InfoDataPointSnapshot(Labels.of(tagKeys, tagValues))))); } else { collector.add(tagValues, (conventionName, tagKeys) -> Stream.of(new MicrometerCollector.Family<>(conventionName, family -> new GaugeSnapshot(family.metadata, family.dataPointSnapshots), - getMetadata(id), + getMetadata(conventionName, id.getDescription()), new GaugeDataPointSnapshot(gauge.value(), Labels.of(tagKeys, tagValues), null)))); } }); @@ -352,10 +354,12 @@ protected FunctionTimer newFunctionTimer(Meter.Id id, T obj, ToLongFunction< applyToCollector(id, (collector) -> { List tagValues = tagValues(id); collector.add(tagValues, - (conventionName, tagKeys) -> Stream.of(new MicrometerCollector.Family<>(conventionName, - family -> new SummarySnapshot(family.metadata, family.dataPointSnapshots), getMetadata(id), - new SummaryDataPointSnapshot((long) ft.count(), ft.totalTime(getBaseTimeUnit()), - Quantiles.EMPTY, Labels.of(tagKeys, tagValues), null, 0)))); + (conventionName, + tagKeys) -> Stream.of(new MicrometerCollector.Family<>(conventionName, + family -> new SummarySnapshot(family.metadata, family.dataPointSnapshots), + getMetadata(conventionName, id.getDescription()), + new SummaryDataPointSnapshot((long) ft.count(), ft.totalTime(getBaseTimeUnit()), + Quantiles.EMPTY, Labels.of(tagKeys, tagValues), null, 0)))); }); return ft; } @@ -365,10 +369,12 @@ protected FunctionCounter newFunctionCounter(Meter.Id id, T obj, ToDoubleFun FunctionCounter fc = new CumulativeFunctionCounter<>(id, obj, countFunction); applyToCollector(id, (collector) -> { List tagValues = tagValues(id); - collector.add(tagValues, - (conventionName, tagKeys) -> Stream.of(new MicrometerCollector.Family<>(conventionName, - family -> new CounterSnapshot(family.metadata, family.dataPointSnapshots), getMetadata(id), - new CounterDataPointSnapshot(fc.count(), Labels.of(tagKeys, tagValues), null, 0)))); + collector + .add(tagValues, + (conventionName, tagKeys) -> Stream.of(new MicrometerCollector.Family<>(conventionName, + family -> new CounterSnapshot(family.metadata, family.dataPointSnapshots), + getMetadata(conventionName, id.getDescription()), + new CounterDataPointSnapshot(fc.count(), Labels.of(tagKeys, tagValues), null, 0)))); }); return fc; } @@ -423,14 +429,16 @@ protected Meter newMeter(Meter.Id id, Meter.Type type, Iterable mea private MicrometerCollector.Family customCounterFamily(Meter.Id id, String conventionName, String suffix, Labels labels, double value) { return new MicrometerCollector.Family<>(conventionName + suffix, - family -> new CounterSnapshot(family.metadata, family.dataPointSnapshots), getMetadata(id, suffix), + family -> new CounterSnapshot(family.metadata, family.dataPointSnapshots), + getMetadata(conventionName + suffix, id.getDescription()), new CounterDataPointSnapshot(value, labels, null, 0)); } private MicrometerCollector.Family customGaugeFamily(Meter.Id id, String conventionName, String suffix, Labels labels, double value) { return new MicrometerCollector.Family<>(conventionName + suffix, - family -> new GaugeSnapshot(family.metadata, family.dataPointSnapshots), getMetadata(id, suffix), + family -> new GaugeSnapshot(family.metadata, family.dataPointSnapshots), + getMetadata(conventionName + suffix, id.getDescription()), new GaugeDataPointSnapshot(value, labels, null)); } @@ -470,9 +478,9 @@ private void addDistributionStatisticSamples(Meter.Id id, MicrometerCollector co Exemplars exemplars = createExemplarsWithScaledValues(exemplarsSupplier.get()); families.add(new MicrometerCollector.Family<>(conventionName, - family -> new SummarySnapshot(family.metadata, family.dataPointSnapshots), getMetadata(id), - new SummaryDataPointSnapshot(count, sum, quantiles, Labels.of(tagKeys, tagValues), exemplars, - 0))); + family -> new SummarySnapshot(family.metadata, family.dataPointSnapshots), + getMetadata(conventionName, id.getDescription()), new SummaryDataPointSnapshot(count, sum, + quantiles, Labels.of(tagKeys, tagValues), exemplars, 0))); } else { List buckets = new ArrayList<>(); @@ -501,8 +509,9 @@ private void addDistributionStatisticSamples(Meter.Id id, MicrometerCollector co families.add(new MicrometerCollector.Family<>(conventionName, family -> new io.prometheus.metrics.model.snapshots.HistogramSnapshot(forLongTaskTimer, family.metadata, family.dataPointSnapshots), - getMetadata(id), new HistogramDataPointSnapshot(ClassicHistogramBuckets.of(buckets, counts), - sum, Labels.of(tagKeys, tagValues), exemplars, 0))); + getMetadata(conventionName, id.getDescription()), + new HistogramDataPointSnapshot(ClassicHistogramBuckets.of(buckets, counts), sum, + Labels.of(tagKeys, tagValues), exemplars, 0))); // TODO: Add support back for VictoriaMetrics // Previously we had low-level control so a histogram was just @@ -515,9 +524,9 @@ private void addDistributionStatisticSamples(Meter.Id id, MicrometerCollector co } families.add(new MicrometerCollector.Family<>(conventionName + "_max", - family -> new GaugeSnapshot(family.metadata, family.dataPointSnapshots), getMetadata(id, "_max"), - new GaugeDataPointSnapshot(histogramSnapshot.max(getBaseTimeUnit()), Labels.of(tagKeys, tagValues), - null))); + family -> new GaugeSnapshot(family.metadata, family.dataPointSnapshots), + getMetadata(conventionName + "_max", id.getDescription()), new GaugeDataPointSnapshot( + histogramSnapshot.max(getBaseTimeUnit()), Labels.of(tagKeys, tagValues), null))); return families.build(); }); @@ -549,13 +558,8 @@ private void onMeterRemoved(Meter meter) { } } - private MetricMetadata getMetadata(Meter.Id id) { - return getMetadata(id, ""); - } - - private MetricMetadata getMetadata(Meter.Id id, String suffix) { - String name = config().namingConvention().name(id.getName(), id.getType(), id.getBaseUnit()) + suffix; - String help = prometheusConfig.descriptions() ? Optional.ofNullable(id.getDescription()).orElse(" ") : " "; + private MetricMetadata getMetadata(String name, @Nullable String description) { + String help = prometheusConfig.descriptions() ? Optional.ofNullable(description).orElse(" ") : " "; // Unit is intentionally not set, see: // https://github.com/OpenObservability/OpenMetrics/blob/1386544931307dff279688f332890c31b6c5de36/specification/OpenMetrics.md#unit return new MetricMetadata(name, help, null); diff --git a/implementations/micrometer-registry-prometheus/src/test/java/io/micrometer/prometheusmetrics/PrometheusMeterRegistryTest.java b/implementations/micrometer-registry-prometheus/src/test/java/io/micrometer/prometheusmetrics/PrometheusMeterRegistryTest.java index 40e0d1be33..5d41d34302 100644 --- a/implementations/micrometer-registry-prometheus/src/test/java/io/micrometer/prometheusmetrics/PrometheusMeterRegistryTest.java +++ b/implementations/micrometer-registry-prometheus/src/test/java/io/micrometer/prometheusmetrics/PrometheusMeterRegistryTest.java @@ -15,6 +15,7 @@ */ package io.micrometer.prometheusmetrics; +import io.micrometer.common.lang.Nullable; import io.micrometer.core.Issue; import io.micrometer.core.instrument.LongTaskTimer.Sample; import io.micrometer.core.instrument.Timer; @@ -991,6 +992,50 @@ void noExemplarsIfNoSampler() { assertThat(scraped).endsWith("# EOF\n"); } + @Test + @Issue("#5229") + void doesNotCallConventionOnScrape() { + CountingPrometheusNamingConvention convention = new CountingPrometheusNamingConvention(); + registry.config().namingConvention(convention); + + Timer.builder("timer").tag("k1", "v1").description("my timer").register(registry); + Counter.builder("counter").tag("k1", "v1").description("my counter").register(registry); + DistributionSummary.builder("summary").tag("k1", "v1").description("my summary").register(registry); + Gauge.builder("gauge", new AtomicInteger(), AtomicInteger::doubleValue) + .tag("k1", "v1") + .description("my gauge") + .register(registry); + LongTaskTimer.builder("long.task.timer").tag("k1", "v1").description("my long task timer").register(registry); + + int expectedNameCount = convention.nameCount.get(); + int expectedTagKeyCount = convention.tagKeyCount.get(); + + registry.scrape(); + + assertThat(convention.nameCount.get()).isEqualTo(expectedNameCount); + assertThat(convention.tagKeyCount.get()).isEqualTo(expectedTagKeyCount); + } + + private static class CountingPrometheusNamingConvention extends PrometheusNamingConvention { + + AtomicInteger nameCount = new AtomicInteger(); + + AtomicInteger tagKeyCount = new AtomicInteger(); + + @Override + public String name(String name, Meter.Type type, @Nullable String baseUnit) { + nameCount.incrementAndGet(); + return super.name(name, type, baseUnit); + } + + @Override + public String tagKey(String key) { + tagKeyCount.incrementAndGet(); + return super.tagKey(key); + } + + } + private PrometheusMeterRegistry createPrometheusMeterRegistryWithProperties(Properties properties) { PrometheusConfig prometheusConfig = new PrometheusConfig() { @Override From d9d6599ba742df8e4864731f6c187f1bb792153c Mon Sep 17 00:00:00 2001 From: Jonatan Ivanov Date: Tue, 9 Jul 2024 15:47:05 -0700 Subject: [PATCH 29/74] Disable the kotlinSourcesJar task The kotlinSourcesJar task conflicts with the sourcesJar task. Closes gh-5151 See https://youtrack.jetbrains.com/issue/KT-54207/Kotlin-has-two-sources-tasks-kotlinSourcesJar-and-sourcesJar-that-archives-sources-to-the-same-artifact --- build.gradle | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/build.gradle b/build.gradle index da422742f4..d50648e574 100644 --- a/build.gradle +++ b/build.gradle @@ -276,6 +276,13 @@ subprojects { } } + plugins.withId('org.jetbrains.kotlin.jvm') { + // We disble the kotlinSourcesJar task since it conflicts with the sourcesJar task of the Java plugin + // See: https://github.com/micrometer-metrics/micrometer/issues/5151 + // See: https://youtrack.jetbrains.com/issue/KT-54207/Kotlin-has-two-sources-tasks-kotlinSourcesJar-and-sourcesJar-that-archives-sources-to-the-same-artifact + kotlinSourcesJar.enabled = false + } + tasks.register('downloadDependencies') { outputs.upToDateWhen { false } doLast { From a7bdc559cf451a69d909bb8a9877ca7f4997cb81 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 9 Jul 2024 16:43:14 -0700 Subject: [PATCH 30/74] Bump org.jetbrains.kotlin.jvm from 1.9.23 to 1.9.24 (#5069) Bumps org.jetbrains.kotlin.jvm from 1.9.23 to 1.9.24. --- updated-dependencies: - dependency-name: org.jetbrains.kotlin.jvm dependency-type: direct:production update-type: version-update:semver-minor ... Co-authored-by: Jonatan Ivanov Co-authored-by: dependabot[bot] --- gradle/libs.versions.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 8c542e0228..8c2b0e10f7 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -219,5 +219,5 @@ plugin-spotless = { module = "com.diffplug.spotless:spotless-plugin-gradle", ver plugin-bnd = "biz.aQute.bnd:biz.aQute.bnd.gradle:6.4.0" [plugins] -kotlin19 = { id = "org.jetbrains.kotlin.jvm", version = "1.9.23" } +kotlin19 = { id = "org.jetbrains.kotlin.jvm", version = "1.9.24" } kotlin17 = { id = "org.jetbrains.kotlin.jvm", version = "1.7.22" } From aeadeab483ac74a89efda18db8f08bae5f71e528 Mon Sep 17 00:00:00 2001 From: Jonatan Ivanov Date: Fri, 12 Jul 2024 12:17:16 -0700 Subject: [PATCH 31/74] Make PrometheusMeterRegistryTest less flaky Tests that also include verification for Exemplars in PrometheusMeterRegistryTest can be flaky since the Exemplars sampler has a rate limiter that does not let sampling happening too frequently. Since our tests want to verify (sampled) exemplars, we set the rate-limit interval to it's minimum (io.prometheus.exemplars.sampleIntervalMilliseconds=1(ms)) and sleep a little between recordings to not hit the rate-limit. It seems the amount of sleep is not always enough and we need to sleep more. Closes gh-5302 --- .../prometheusmetrics/PrometheusMeterRegistryTest.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/implementations/micrometer-registry-prometheus/src/test/java/io/micrometer/prometheusmetrics/PrometheusMeterRegistryTest.java b/implementations/micrometer-registry-prometheus/src/test/java/io/micrometer/prometheusmetrics/PrometheusMeterRegistryTest.java index 5d41d34302..ebb6bd26cf 100644 --- a/implementations/micrometer-registry-prometheus/src/test/java/io/micrometer/prometheusmetrics/PrometheusMeterRegistryTest.java +++ b/implementations/micrometer-registry-prometheus/src/test/java/io/micrometer/prometheusmetrics/PrometheusMeterRegistryTest.java @@ -943,7 +943,7 @@ void openMetricsScrapeWithExemplars() throws InterruptedException { } private static void sleepToAvoidRateLimiting() throws InterruptedException { - Thread.sleep(5); // sleeping 5ms since the sample interval limit is 1ms + Thread.sleep(10); // sleeping since the sample interval limit is 1ms } @Test From eb52665a3f6374fc4021184ce876e5f80a11e768 Mon Sep 17 00:00:00 2001 From: Jonatan Ivanov Date: Fri, 12 Jul 2024 14:05:54 -0700 Subject: [PATCH 32/74] Apply test-retry settings to all tests Closes gh-5303 --- build.gradle | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/build.gradle b/build.gradle index 9d6cba2802..2018927643 100644 --- a/build.gradle +++ b/build.gradle @@ -146,11 +146,6 @@ subprojects { useJUnitPlatform { excludeTags 'docker' } - - develocity.testRetry { - maxFailures = 5 - maxRetries = 3 - } } task dockerTest(type: Test) { @@ -162,8 +157,12 @@ subprojects { } } - project.tasks.withType(Test) { Test testTask -> - testTask.testLogging.exceptionFormat = 'full' + tasks.withType(Test).configureEach { + testLogging.exceptionFormat = 'full' + develocity.testRetry { + maxFailures = 5 + maxRetries = 3 + } } license { From c2d95a64f0129712917dfbc0f9f426f7b4b00479 Mon Sep 17 00:00:00 2001 From: Jonatan Ivanov Date: Fri, 12 Jul 2024 17:32:09 -0700 Subject: [PATCH 33/74] Upgrade opentelemetry-collector-contrib to 0.104.0 Closes gh-5305 --- .../micrometer-registry-otlp/build.gradle | 2 +- .../otlp/OTelCollectorIntegrationTest.java | 30 ++++++++++++------- .../src/test/resources/collector-config.yml | 2 ++ 3 files changed, 22 insertions(+), 12 deletions(-) diff --git a/implementations/micrometer-registry-otlp/build.gradle b/implementations/micrometer-registry-otlp/build.gradle index c559ab3e56..6fdae51ac5 100644 --- a/implementations/micrometer-registry-otlp/build.gradle +++ b/implementations/micrometer-registry-otlp/build.gradle @@ -14,7 +14,7 @@ dependencies { } dockerTest { - systemProperty 'otel-collector-image.version', '0.79.0' + systemProperty 'otel-collector-image.version', '0.104.0' } // new module in 1.9. This should be removed in later branches. diff --git a/implementations/micrometer-registry-otlp/src/test/java/io/micrometer/registry/otlp/OTelCollectorIntegrationTest.java b/implementations/micrometer-registry-otlp/src/test/java/io/micrometer/registry/otlp/OTelCollectorIntegrationTest.java index 0a8fb4d912..9653f4dce0 100644 --- a/implementations/micrometer-registry-otlp/src/test/java/io/micrometer/registry/otlp/OTelCollectorIntegrationTest.java +++ b/implementations/micrometer-registry-otlp/src/test/java/io/micrometer/registry/otlp/OTelCollectorIntegrationTest.java @@ -42,9 +42,7 @@ @Tag("docker") class OTelCollectorIntegrationTest { - // TODO: The OTel Prometheus exporter does not support openmetrics-text 1.0.0 yet - // see: https://github.com/open-telemetry/opentelemetry-collector-contrib/issues/18913 - private static final String OPENMETRICS_001 = "application/openmetrics-text; version=0.0.1; charset=utf-8"; + private static final String OPENMETRICS_TEXT = "application/openmetrics-text; version=1.0.0; charset=utf-8"; private static final String CONFIG_FILE_NAME = "collector-config.yml"; @@ -55,8 +53,9 @@ class OTelCollectorIntegrationTest { private final GenericContainer container = new GenericContainer(COLLECTOR_IMAGE) .withCommand("--config=/etc/" + CONFIG_FILE_NAME) .withClasspathResourceMapping(CONFIG_FILE_NAME, "/etc/" + CONFIG_FILE_NAME, READ_ONLY) + .withExposedPorts(4318, 9090) // HTTP receiver, Prometheus exporter .waitingFor(Wait.forLogMessage(".*Everything is ready.*", 1)) - .withExposedPorts(4318, 9090); // HTTP receiver, Prometheus exporter + .waitingFor(Wait.forListeningPorts(4318)); private static String getCollectorImageVersion() { String version = System.getProperty("otel-collector-image.version"); @@ -73,7 +72,7 @@ void collectorShouldExportMetrics() throws Exception { Counter.builder("test.counter").register(registry).increment(42); Gauge.builder("test.gauge", () -> 12).register(registry); Timer.builder("test.timer").register(registry).record(Duration.ofMillis(123)); - DistributionSummary.builder("test.distributionsummary").register(registry).record(24); + DistributionSummary.builder("test.ds").register(registry).record(24); // @formatter:off await().atMost(Duration.ofSeconds(5)) @@ -81,7 +80,7 @@ void collectorShouldExportMetrics() throws Exception { .pollInterval(Duration.ofMillis(100)) .untilAsserted(() -> whenPrometheusScraped().then() .statusCode(200) - .contentType(OPENMETRICS_001) + .contentType(OPENMETRICS_TEXT) .body(endsWith("# EOF\n"), not(startsWith("# EOF\n"))) ); @@ -91,20 +90,29 @@ void collectorShouldExportMetrics() throws Exception { whenPrometheusScraped().then().body( containsString("{job=\"test\",service_name=\"test\",telemetry_sdk_language=\"java\",telemetry_sdk_name=\"io.micrometer\""), + containsString("# HELP test_counter \n"), + containsString("# TYPE test_counter counter\n"), matchesPattern("(?s)^.*test_counter_total\\{.+} 42\\.0\\n.*$"), + + containsString("# HELP test_gauge \n"), + containsString("# TYPE test_gauge gauge\n"), matchesPattern("(?s)^.*test_gauge\\{.+} 12\\.0\\n.*$"), + containsString("# HELP test_timer_milliseconds \n"), + containsString("# TYPE test_timer_milliseconds histogram\n"), matchesPattern("(?s)^.*test_timer_milliseconds_count\\{.+} 1\\n.*$"), - // TODO: Earlier this was 123s (123), should have been 123ms (0.123) + // Earlier this was 123s (123), should have been 123ms (0.123) // see: https://github.com/open-telemetry/opentelemetry-collector-contrib/issues/18903 // it seems units are still not converted but at least the unit is in the name now (breaking change) // see: https://github.com/open-telemetry/opentelemetry-collector-contrib/pull/20519 matchesPattern("(?s)^.*test_timer_milliseconds_sum\\{.+} 123\\.0\\n.*$"), matchesPattern("(?s)^.*test_timer_milliseconds_bucket\\{.+,le=\"\\+Inf\"} 1\\n.*$"), - matchesPattern("(?s)^.*test_distributionsummary_count\\{.+} 1\\n.*$"), - matchesPattern("(?s)^.*test_distributionsummary_sum\\{.+} 24\\.0\\n.*$"), - matchesPattern("(?s)^.*test_distributionsummary_bucket\\{.+,le=\"\\+Inf\"} 1\\n.*$") + containsString("# HELP test_ds \n"), + containsString("# TYPE test_ds histogram\n"), + matchesPattern("(?s)^.*test_ds_count\\{.+} 1\\n.*$"), + matchesPattern("(?s)^.*test_ds_sum\\{.+} 24\\.0\\n.*$"), + matchesPattern("(?s)^.*test_ds_bucket\\{.+,le=\"\\+Inf\"} 1\\n.*$") ); // @formatter:on } @@ -137,7 +145,7 @@ private Response whenPrometheusScraped() { // @formatter:off return given() .port(container.getMappedPort(9090)) - .accept(OPENMETRICS_001) + .accept(OPENMETRICS_TEXT) .when() .get("/metrics"); // @formatter:on diff --git a/implementations/micrometer-registry-otlp/src/test/resources/collector-config.yml b/implementations/micrometer-registry-otlp/src/test/resources/collector-config.yml index 41c2189d45..3bdf5e2001 100644 --- a/implementations/micrometer-registry-otlp/src/test/resources/collector-config.yml +++ b/implementations/micrometer-registry-otlp/src/test/resources/collector-config.yml @@ -17,7 +17,9 @@ receivers: otlp: protocols: grpc: + endpoint: 0.0.0.0:4317 http: + endpoint: 0.0.0.0:4318 exporters: # https://github.com/open-telemetry/opentelemetry-collector-contrib/tree/main/exporter/prometheusexporter From 32fabba3c067ac77855fe9363c3570c68d51c7cc Mon Sep 17 00:00:00 2001 From: Jonatan Ivanov Date: Mon, 22 Jul 2024 15:43:54 -0700 Subject: [PATCH 34/74] Migrate to io.spring.develocity.conventions:0.0.19 Closes gh-5312 --- settings.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/settings.gradle b/settings.gradle index 28fb608f9c..fbf84367f3 100644 --- a/settings.gradle +++ b/settings.gradle @@ -6,7 +6,7 @@ pluginManagement { plugins { id 'com.gradle.develocity' version '3.17.5' - id 'io.spring.ge.conventions' version '0.0.17' + id 'io.spring.develocity.conventions' version '0.0.19' id 'org.gradle.toolchains.foojay-resolver-convention' version '0.8.0' } From 84a662f337a74de79c21594c196dfe02aad82d83 Mon Sep 17 00:00:00 2001 From: BJMg Date: Wed, 24 Jul 2024 11:00:57 +0200 Subject: [PATCH 35/74] Flush channel on close so buffered metrics are not lost (#5195) We thought disposing the connection was enough to flush any buffered metrics, but this was not flushing the channel. This was missed by our IT tests (StatsdMeterRegistryPublishTest) because we did not use buffered config. This makes sure to flush the channel before disposing the connection so any buffered statsd lines are sent on close. Resolves gh-2141 --- .../statsd/StatsdMeterRegistry.java | 9 +++ .../StatsdMeterRegistryPublishTest.java | 61 ++++++++++++++----- 2 files changed, 56 insertions(+), 14 deletions(-) diff --git a/implementations/micrometer-registry-statsd/src/main/java/io/micrometer/statsd/StatsdMeterRegistry.java b/implementations/micrometer-registry-statsd/src/main/java/io/micrometer/statsd/StatsdMeterRegistry.java index 793f3d1440..cea5907cca 100644 --- a/implementations/micrometer-registry-statsd/src/main/java/io/micrometer/statsd/StatsdMeterRegistry.java +++ b/implementations/micrometer-registry-statsd/src/main/java/io/micrometer/statsd/StatsdMeterRegistry.java @@ -26,6 +26,7 @@ import io.micrometer.core.lang.Nullable; import io.micrometer.core.util.internal.logging.WarnThenDebugLogger; import io.micrometer.statsd.internal.*; +import io.netty.channel.Channel; import io.netty.channel.unix.DomainSocketAddress; import io.netty.util.AttributeKey; import org.reactivestreams.Publisher; @@ -94,6 +95,9 @@ public class StatsdMeterRegistry extends MeterRegistry { Disposable.Swap statsdConnection = Disposables.swap(); + @Nullable + private Channel flushableChannel; + private Disposable.Swap meterPoller = Disposables.swap(); @Nullable @@ -295,6 +299,7 @@ private void connectAndSubscribe(UdpClient udpClient) { private void retryReplaceClient(Mono connectMono) { connectMono.retryWhen(Retry.backoff(Long.MAX_VALUE, Duration.ofSeconds(1)).maxBackoff(Duration.ofMinutes(1))) .subscribe(connection -> { + this.flushableChannel = connection.channel(); this.statsdConnection.replace(connection); // now that we're connected, start polling gauges and other pollable @@ -309,6 +314,9 @@ private void startPolling() { public void stop() { if (started.compareAndSet(true, false)) { + if (this.flushableChannel != null) { + this.flushableChannel.flush(); + } if (statsdConnection.get() != null) { statsdConnection.get().dispose(); } @@ -321,6 +329,7 @@ public void stop() { @Override public void close() { poll(); + this.sink.complete(); stop(); super.close(); } diff --git a/implementations/micrometer-registry-statsd/src/test/java/io/micrometer/statsd/StatsdMeterRegistryPublishTest.java b/implementations/micrometer-registry-statsd/src/test/java/io/micrometer/statsd/StatsdMeterRegistryPublishTest.java index 054d802430..2414fec1b9 100644 --- a/implementations/micrometer-registry-statsd/src/test/java/io/micrometer/statsd/StatsdMeterRegistryPublishTest.java +++ b/implementations/micrometer-registry-statsd/src/test/java/io/micrometer/statsd/StatsdMeterRegistryPublishTest.java @@ -79,6 +79,25 @@ void cleanUp() { } } + @ParameterizedTest + @EnumSource(StatsdProtocol.class) + void receiveAllBufferedMetricsAfterCloseSuccessfully(StatsdProtocol protocol) throws InterruptedException { + skipUdsTestOnWindows(protocol); + serverLatch = new CountDownLatch(3); + server = startServer(protocol, 0); + + final int port = getPort(protocol); + meterRegistry = new StatsdMeterRegistry(getBufferedConfig(protocol, port), Clock.SYSTEM); + startRegistryAndWaitForClient(); + Thread.sleep(1000); + Counter counter = Counter.builder("my.counter").register(meterRegistry); + counter.increment(); + counter.increment(); + counter.increment(); + meterRegistry.close(); + assertThat(serverLatch.await(5, TimeUnit.SECONDS)).isTrue(); + } + @ParameterizedTest @EnumSource(StatsdProtocol.class) void receiveMetricsSuccessfully(StatsdProtocol protocol) throws InterruptedException { @@ -336,11 +355,14 @@ private DisposableChannel startServer(StatsdProtocol protocol, int port) { return UdpServer.create() .bindAddress(() -> protocol == StatsdProtocol.UDP ? InetSocketAddress.createUnresolved("localhost", port) : newDomainSocketAddress()) - .handle((in, out) -> in.receive().asString().flatMap(packet -> { - serverLatch.countDown(); - serverMetricReadCount.getAndIncrement(); - return Flux.never(); - })) + .handle((in, out) -> in.receive() + .asString() + .flatMap(packet -> Flux.just(packet.split("\n"))) + .flatMap(packetLine -> { + serverLatch.countDown(); + serverMetricReadCount.getAndIncrement(); + return Flux.never(); + })) .doOnBound((server) -> bound = true) .doOnUnbound((server) -> bound = false) .wiretap("udpserver", LogLevel.INFO) @@ -351,14 +373,17 @@ else if (protocol == StatsdProtocol.TCP) { return TcpServer.create() .host("localhost") .port(port) - .handle((in, out) -> in.receive().asString().flatMap(packet -> { - IntStream.range(0, packet.split("my.counter").length - 1).forEach(i -> { - serverLatch.countDown(); - serverMetricReadCount.getAndIncrement(); - }); - in.withConnection(channel::set); - return Flux.never(); - })) + .handle((in, out) -> in.receive() + .asString() + .flatMap(packet -> Flux.just(packet.split("\n"))) + .flatMap(packetLine -> { + IntStream.range(0, packetLine.split("my.counter").length - 1).forEach(i -> { + serverLatch.countDown(); + serverMetricReadCount.getAndIncrement(); + }); + in.withConnection(channel::set); + return Flux.never(); + })) .doOnBound((server) -> bound = true) .doOnUnbound((server) -> { bound = false; @@ -388,6 +413,14 @@ private static DomainSocketAddress newDomainSocketAddress() { } private StatsdConfig getUnbufferedConfig(StatsdProtocol protocol, int port) { + return getConfig(protocol, port, false); + } + + private StatsdConfig getBufferedConfig(StatsdProtocol protocol, int port) { + return getConfig(protocol, port, true); + } + + private StatsdConfig getConfig(StatsdProtocol protocol, int port, boolean buffered) { return new StatsdConfig() { @Override public String get(String key) { @@ -411,7 +444,7 @@ public StatsdProtocol protocol() { @Override public boolean buffered() { - return false; + return buffered; } }; } From a13915d973c9583d93670f6559e89c7f1fbe2651 Mon Sep 17 00:00:00 2001 From: HYEONSEOK1 <37043329+HYEONSEOK1@users.noreply.github.com> Date: Thu, 1 Aug 2024 07:22:15 +0900 Subject: [PATCH 36/74] Add default factoryType tag in CommonsObjectPool2Metrics (#5316) There are cases where the factoryType tag is missing which leads to issues in certain registries (i.e.: Prometheus). This change adds a default "none" value to the factoryType tag so that it will be always present. Closes gh-5316 --------- Co-authored-by: Jonatan Ivanov --- .../CommonsObjectPool2Metrics.java | 65 ++++++++++--------- .../CommonsObjectPool2MetricsTest.java | 50 +++++++------- 2 files changed, 61 insertions(+), 54 deletions(-) diff --git a/micrometer-core/src/main/java/io/micrometer/core/instrument/binder/commonspool2/CommonsObjectPool2Metrics.java b/micrometer-core/src/main/java/io/micrometer/core/instrument/binder/commonspool2/CommonsObjectPool2Metrics.java index 0eb5a16b17..acc67005af 100644 --- a/micrometer-core/src/main/java/io/micrometer/core/instrument/binder/commonspool2/CommonsObjectPool2Metrics.java +++ b/micrometer-core/src/main/java/io/micrometer/core/instrument/binder/commonspool2/CommonsObjectPool2Metrics.java @@ -129,13 +129,18 @@ public void bindTo(@NonNull MeterRegistry registry) { private Iterable nameTag(ObjectName name, String type) throws AttributeNotFoundException, MBeanException, ReflectionException, InstanceNotFoundException { - Tags tags = Tags.of("name", name.getKeyProperty("name"), "type", type); + return Tags.of("name", name.getKeyProperty("name"), "type", type, "factoryType", getFactoryType(name, type)); + } + + private String getFactoryType(ObjectName name, String type) + throws ReflectionException, AttributeNotFoundException, InstanceNotFoundException, MBeanException { if (Objects.equals(type, "GenericObjectPool")) { // for GenericObjectPool, we want to include the name and factoryType as tags - String factoryType = mBeanServer.getAttribute(name, "FactoryType").toString(); - tags = Tags.concat(tags, "factoryType", factoryType); + return mBeanServer.getAttribute(name, "FactoryType").toString(); + } + else { + return "none"; } - return tags; } private void registerMetricsEventually(String type, BiConsumer perObject) { @@ -170,36 +175,34 @@ private void registerNotificationListener(String type, BiConsumer { - executor.execute(() -> { - MBeanServerNotification mbs = (MBeanServerNotification) notification; - ObjectName o = mbs.getMBeanName(); - Iterable nameTags = emptyList(); - int maxTries = 3; - for (int i = 0; i < maxTries; i++) { - try { - Thread.sleep(1000); - } - catch (InterruptedException e) { - Thread.currentThread().interrupt(); - throw new RuntimeException(e); - } - try { - nameTags = nameTag(o, type); - break; - } - catch (AttributeNotFoundException | MBeanException | ReflectionException - | InstanceNotFoundException e) { - if (i == maxTries - 1) { - log.error("can not set name tag", e); - } + (notification, handback) -> executor.execute(() -> { + MBeanServerNotification mbs = (MBeanServerNotification) notification; + ObjectName o = mbs.getMBeanName(); + Iterable nameTags = emptyList(); + int maxTries = 3; + for (int i = 0; i < maxTries; i++) { + try { + Thread.sleep(1000); + } + catch (InterruptedException e) { + Thread.currentThread().interrupt(); + throw new RuntimeException(e); + } + try { + nameTags = nameTag(o, type); + break; + } + catch (AttributeNotFoundException | MBeanException | ReflectionException + | InstanceNotFoundException e) { + if (i == maxTries - 1) { + log.error("can not set name tag", e); } } - perObject.accept(o, Tags.concat(tags, nameTags)); - }); - }; + } + perObject.accept(o, Tags.concat(tags, nameTags)); + }); - NotificationFilter filter = (NotificationFilter) notification -> { + NotificationFilter filter = notification -> { if (!MBeanServerNotification.REGISTRATION_NOTIFICATION.equals(notification.getType())) return false; ObjectName obj = ((MBeanServerNotification) notification).getMBeanName(); diff --git a/micrometer-core/src/test/java/io/micrometer/core/instrument/binder/commonspool2/CommonsObjectPool2MetricsTest.java b/micrometer-core/src/test/java/io/micrometer/core/instrument/binder/commonspool2/CommonsObjectPool2MetricsTest.java index 5f8bb62284..e975cb508e 100644 --- a/micrometer-core/src/test/java/io/micrometer/core/instrument/binder/commonspool2/CommonsObjectPool2MetricsTest.java +++ b/micrometer-core/src/test/java/io/micrometer/core/instrument/binder/commonspool2/CommonsObjectPool2MetricsTest.java @@ -26,6 +26,7 @@ import org.apache.commons.pool2.impl.GenericObjectPool; import org.apache.commons.pool2.impl.GenericObjectPoolConfig; import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import java.util.Arrays; @@ -41,44 +42,50 @@ class CommonsObjectPool2MetricsTest { private int genericObjectPoolCount = 0; - private Tags tags = Tags.of("app", "myapp", "version", "1"); + private final Tags tags = Tags.of("app", "myapp", "version", "1"); - private final MeterRegistry registry = new SimpleMeterRegistry(); + private MeterRegistry registry; private final CommonsObjectPool2Metrics commonsObjectPool2Metrics = new CommonsObjectPool2Metrics(tags); + @BeforeEach + void setUp() { + registry = new SimpleMeterRegistry(); + } + @AfterEach - void afterEach() { + void tearDown() { commonsObjectPool2Metrics.close(); } @Test - void verifyMetricsWithExpectedTags() { - try (GenericObjectPool p = createGenericObjectPool()) { - MeterRegistry registry = new SimpleMeterRegistry(); + void verifyGenericObjectPoolMetricsWithExpectedTags() { + try (GenericObjectPool ignored = createGenericObjectPool()) { commonsObjectPool2Metrics.bindTo(registry); + Tags tagsToMatch = tags.and("name", "pool", "type", "GenericObjectPool", "factoryType", + "io.micrometer.core.instrument.binder.commonspool2.CommonsObjectPool2MetricsTest$1"); - registry.get("commons.pool2.num.idle").tags(tags).gauge(); - registry.get("commons.pool2.num.waiters").tags(tags).gauge(); + registry.get("commons.pool2.num.idle").tags(tagsToMatch).gauge(); + registry.get("commons.pool2.num.waiters").tags(tagsToMatch).gauge(); Arrays .asList("commons.pool2.created", "commons.pool2.borrowed", "commons.pool2.returned", "commons.pool2.destroyed", "commons.pool2.destroyed.by.evictor", "commons.pool2.destroyed.by.borrow.validation") - .forEach(name -> registry.get(name).tags(tags).functionCounter()); + .forEach(name -> registry.get(name).tags(tagsToMatch).functionCounter()); Arrays .asList("commons.pool2.max.borrow.wait", "commons.pool2.mean.active", "commons.pool2.mean.idle", "commons.pool2.mean.borrow.wait") - .forEach(name -> registry.get(name).tags(tags).timeGauge()); + .forEach(name -> registry.get(name).tags(tagsToMatch).timeGauge()); } } @Test void verifyGenericKeyedObjectPoolMetricsWithExpectedTags() { - try (GenericKeyedObjectPool p = createGenericKeyedObjectPool()) { - Tags tagsToMatch = tags.and("type", "GenericKeyedObjectPool"); + try (GenericKeyedObjectPool ignored = createGenericKeyedObjectPool()) { commonsObjectPool2Metrics.bindTo(registry); + Tags tagsToMatch = tags.and("name", "pool", "type", "GenericKeyedObjectPool", "factoryType", "none"); Arrays.asList("commons.pool2.num.idle", "commons.pool2.num.waiters") .forEach(name -> registry.get(name).tags(tagsToMatch).gauge()); @@ -106,13 +113,13 @@ void verifyIdleAndActiveInstancesAreReported() throws Exception { }); try (GenericObjectPool genericObjectPool = createGenericObjectPool()) { - latch.await(10, TimeUnit.SECONDS); - final Object o = genericObjectPool.borrowObject(10_000L); + assertThat(latch.await(10, TimeUnit.SECONDS)).isTrue(); + Object object = genericObjectPool.borrowObject(10_000L); assertThat(registry.get("commons.pool2.num.active").gauge().value()).isEqualTo(1.0); assertThat(registry.get("commons.pool2.num.idle").gauge().value()).isEqualTo(0.0); - genericObjectPool.returnObject(o); + genericObjectPool.returnObject(object); assertThat(registry.get("commons.pool2.num.active").gauge().value()).isEqualTo(0.0); assertThat(registry.get("commons.pool2.num.idle").gauge().value()).isEqualTo(1.0); @@ -121,11 +128,9 @@ void verifyIdleAndActiveInstancesAreReported() throws Exception { @Test void metricsReportedPerMultiplePools() { - try (final GenericObjectPool p1 = createGenericObjectPool(); - final GenericObjectPool p2 = createGenericObjectPool(); - final GenericObjectPool p3 = createGenericObjectPool()) { - - MeterRegistry registry = new SimpleMeterRegistry(); + try (GenericObjectPool ignored1 = createGenericObjectPool(); + GenericObjectPool ignored2 = createGenericObjectPool(); + GenericObjectPool ignored3 = createGenericObjectPool()) { commonsObjectPool2Metrics.bindTo(registry); registry.get("commons.pool2.num.waiters").tag("name", "pool" + genericObjectPoolCount).gauge(); @@ -135,7 +140,6 @@ void metricsReportedPerMultiplePools() { @Test void newPoolsAreDiscoveredByListener() throws InterruptedException { - MeterRegistry registry = new SimpleMeterRegistry(); commonsObjectPool2Metrics.bindTo(registry); CountDownLatch latch = new CountDownLatch(1); @@ -144,8 +148,8 @@ void newPoolsAreDiscoveredByListener() throws InterruptedException { latch.countDown(); }); - try (GenericObjectPool p = createGenericObjectPool()) { - latch.await(10, TimeUnit.SECONDS); + try (GenericObjectPool ignored = createGenericObjectPool()) { + assertThat(latch.await(10, TimeUnit.SECONDS)).isTrue(); } } From 43a4b973249e91f694eab9b4014393b799bbad0b Mon Sep 17 00:00:00 2001 From: Johnny Lim Date: Tue, 6 Aug 2024 15:34:50 +0900 Subject: [PATCH 37/74] Polish gh-4857 (#5368) --- concurrency-tests/build.gradle | 2 +- .../core/instrument/MeterRegistry.java | 21 ++++++++++--------- .../core/instrument/MeterRegistryTest.java | 5 +++-- 3 files changed, 15 insertions(+), 13 deletions(-) diff --git a/concurrency-tests/build.gradle b/concurrency-tests/build.gradle index 6870dd985e..7d0c6620ef 100644 --- a/concurrency-tests/build.gradle +++ b/concurrency-tests/build.gradle @@ -11,7 +11,7 @@ dependencies { } jcstress { - jcstressDependency 'org.openjdk.jcstress:jcstress-core:0.16' + libs.jcstressCore verbose = true } 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 f618e0e7e4..340e393cfa 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 @@ -103,8 +103,8 @@ public abstract class MeterRegistry { /** * write/remove guarded by meterMapLock, read in - * {@link this#getOrCreateMeter(DistributionStatisticConfig, BiFunction, Id, - * Function)} is unguarded + * {@link #getOrCreateMeter(DistributionStatisticConfig, BiFunction, Id, Function)} is + * unguarded */ private final Map preFilterIdToMeterMap = new HashMap<>(); @@ -755,15 +755,16 @@ public Meter remove(Meter.Id mappedId) { if (meterMap.containsKey(mappedId)) { synchronized (meterMapLock) { final Meter removedMeter = meterMap.remove(mappedId); - Iterator> iterator = preFilterIdToMeterMap.entrySet().iterator(); - while (iterator.hasNext()) { - Map.Entry nextEntry = iterator.next(); - if (nextEntry.getValue().equals(removedMeter)) { - stalePreFilterIds.remove(nextEntry.getKey()); - iterator.remove(); - } - } if (removedMeter != null) { + Iterator> iterator = preFilterIdToMeterMap.entrySet().iterator(); + while (iterator.hasNext()) { + Map.Entry nextEntry = iterator.next(); + if (nextEntry.getValue().equals(removedMeter)) { + stalePreFilterIds.remove(nextEntry.getKey()); + iterator.remove(); + } + } + Set synthetics = syntheticAssociations.remove(mappedId); if (synthetics != null) { for (Id synthetic : synthetics) { diff --git a/micrometer-core/src/test/java/io/micrometer/core/instrument/MeterRegistryTest.java b/micrometer-core/src/test/java/io/micrometer/core/instrument/MeterRegistryTest.java index e687c276e4..25246d662b 100644 --- a/micrometer-core/src/test/java/io/micrometer/core/instrument/MeterRegistryTest.java +++ b/micrometer-core/src/test/java/io/micrometer/core/instrument/MeterRegistryTest.java @@ -272,7 +272,6 @@ void differentPreFilterIdsMapToSameIdWithStaleId() { assertThat(c1).isSameAs(c2); assertThat(registry.remove(c1)).isSameAs(c2); - assertThat(registry.remove(c2)).isNull(); assertThat(registry.getMeters()).isEmpty(); } @@ -314,6 +313,7 @@ void removingStaleMeterRemovesItFromAllInternalState() { Counter c1 = registry.counter("counter"); // make c1 marked as stale registry.config().commonTags("common", "tag"); + assertThat(registry._getStalePreFilterIds()).hasSize(1); registry.remove(c1.getId()); assertThat(registry.getMeters()).isEmpty(); @@ -338,6 +338,7 @@ void unchangedStaleMeterShouldBeUnmarked() { Counter c1 = registry.counter("counter"); // make c1 stale registry.config().meterFilter(MeterFilter.ignoreTags("abc")); + assertThat(registry._getStalePreFilterIds()).hasSize(1); // this should cause c1 (== c2) to be unmarked as stale Counter c2 = registry.counter("counter"); @@ -347,7 +348,7 @@ void unchangedStaleMeterShouldBeUnmarked() { assertThat(registry._getPreFilterIdToMeterMap()).hasSize(1); assertThat(registry._getStalePreFilterIds()) .describedAs("If the meter-filter doesn't alter the meter creation, meters are never unmarked " - + "from staleness and we end-up paying the additional cost everytime") + + "from staleness and we end up paying the additional cost every time") .isEmpty(); } From a299d10c0d9e78a8aa0d89aa3a519ebd445edb90 Mon Sep 17 00:00:00 2001 From: Carsten Brachem Date: Wed, 7 Aug 2024 04:04:41 +0200 Subject: [PATCH 38/74] Make more explicit that recording a metric is safe to call (#5365) Adds a TIP block to the docs to make it more explicit what happens when operations on Meters like increment are called, which will make it clear that such operations are safe to call from regular code. --- docs/modules/ROOT/nav.adoc | 2 +- docs/modules/ROOT/pages/concepts/meters.adoc | 5 +++++ docs/modules/ROOT/pages/concepts/registry.adoc | 2 +- 3 files changed, 7 insertions(+), 2 deletions(-) diff --git a/docs/modules/ROOT/nav.adoc b/docs/modules/ROOT/nav.adoc index 27d5824623..32dc4e381f 100644 --- a/docs/modules/ROOT/nav.adoc +++ b/docs/modules/ROOT/nav.adoc @@ -2,8 +2,8 @@ * xref:installing.adoc[Installing] * xref:concepts.adoc[Concepts] ** xref:concepts/implementations.adoc[Supported Monitoring Systems] -** xref:concepts/registry.adoc[Registry] ** xref:concepts/meters.adoc[Meters] +** xref:concepts/registry.adoc[Registry] ** xref:concepts/naming.adoc[Naming Meters] ** xref:concepts/meter-filters.adoc[Meter Filters] ** xref:concepts/rate-aggregation.adoc[Rate Aggregation] diff --git a/docs/modules/ROOT/pages/concepts/meters.adoc b/docs/modules/ROOT/pages/concepts/meters.adoc index 911a5685a1..a29770e1ad 100644 --- a/docs/modules/ROOT/pages/concepts/meters.adoc +++ b/docs/modules/ROOT/pages/concepts/meters.adoc @@ -1,6 +1,11 @@ [[meters]] = Meters +A `Meter` is the interface for collecting a set of measurements (which we individually call metrics) about your application. + Micrometer supports a set of `Meter` primitives, including `Timer`, `Counter`, `Gauge`, `DistributionSummary`, `LongTaskTimer`, `FunctionCounter`, `FunctionTimer`, and `TimeGauge`. Different meter types result in a different number of time series metrics. For example, while there is a single metric that represents a `Gauge`, a `Timer` measures both the count of timed events and the total time of all timed events. +TIP: Recording a measurement for a `Meter` is expected to be a relatively cheap operation and should not throw any exception. +If the xref:./registry.adoc[registry] supports publishing metrics to a monitoring system, this is done in a separate thread snd should not affect recording metrics. + A meter is uniquely identified by its name and dimensions. We use the terms, "`dimensions`" and "`tags,`" interchangeably, and the Micrometer interface is `Tag` simply because it is shorter. As a general rule, it should be possible to use the name as a pivot. Dimensions let a particular named metric be sliced to drill down and reason about the data. This means that, if only the name is selected, you can drill down by using other dimensions and reason about the value being shown. diff --git a/docs/modules/ROOT/pages/concepts/registry.adoc b/docs/modules/ROOT/pages/concepts/registry.adoc index eae8732d6d..476f2d9e72 100644 --- a/docs/modules/ROOT/pages/concepts/registry.adoc +++ b/docs/modules/ROOT/pages/concepts/registry.adoc @@ -1,7 +1,7 @@ [[registry]] = Registry -A `Meter` is the interface for collecting a set of measurements (which we individually call metrics) about your application. Meters in Micrometer are created from and held in a `MeterRegistry`. Each supported monitoring system has an implementation of `MeterRegistry`. How a registry is created varies for each implementation. +Meters in Micrometer are created from and held in a `MeterRegistry`. Each supported monitoring system has an implementation of `MeterRegistry`. How a registry is created varies for each implementation. Micrometer includes a `SimpleMeterRegistry` that holds the latest value of each meter in memory and does not export the data anywhere. If you do not yet have a preferred monitoring system, you can get started playing with metrics by using the simple registry: From a483101fadae5bf4ef49d4871e8d7cf370689b23 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 9 Aug 2024 17:21:57 +0900 Subject: [PATCH 39/74] Bump com.signalfx.public:signalfx-java from 1.0.43 to 1.0.44 (#5355) Bumps [com.signalfx.public:signalfx-java](https://github.com/signalfx/signalfx-java) from 1.0.43 to 1.0.44. - [Release notes](https://github.com/signalfx/signalfx-java/releases) - [Commits](https://github.com/signalfx/signalfx-java/compare/v1.0.43...v1.0.44) --- updated-dependencies: - dependency-name: com.signalfx.public:signalfx-java dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- gradle/libs.versions.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 92da5224c1..1945523ced 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -63,7 +63,7 @@ postgre = "42.3.10" prometheus = "0.15.0" reactor = "2020.0.45" rest-assured = "5.3.2" -signalfx = "1.0.43" +signalfx = "1.0.44" slf4j = "1.7.36" spectator-atlas = "1.3.10" spring = "5.3.37" From aaf90aa59d65165553f361de3325a7650d9509d1 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 9 Aug 2024 17:22:41 +0900 Subject: [PATCH 40/74] Bump com.gradle.develocity from 3.17.5 to 3.17.6 (#5354) Bumps com.gradle.develocity from 3.17.5 to 3.17.6. --- updated-dependencies: - dependency-name: com.gradle.develocity dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- settings.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/settings.gradle b/settings.gradle index fbf84367f3..3db53fe3ba 100644 --- a/settings.gradle +++ b/settings.gradle @@ -5,7 +5,7 @@ pluginManagement { } plugins { - id 'com.gradle.develocity' version '3.17.5' + id 'com.gradle.develocity' version '3.17.6' id 'io.spring.develocity.conventions' version '0.0.19' id 'org.gradle.toolchains.foojay-resolver-convention' version '0.8.0' } From 6ccf22eae36089679bdaa8f47deeda1b37048f26 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 9 Aug 2024 17:23:20 +0900 Subject: [PATCH 41/74] Bump io.projectreactor:reactor-bom from 2020.0.45 to 2020.0.46 (#5351) Bumps [io.projectreactor:reactor-bom](https://github.com/reactor/reactor) from 2020.0.45 to 2020.0.46. - [Release notes](https://github.com/reactor/reactor/releases) - [Commits](https://github.com/reactor/reactor/compare/2020.0.45...2020.0.46) --- updated-dependencies: - dependency-name: io.projectreactor:reactor-bom dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- gradle/libs.versions.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 1945523ced..c1691c650a 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -61,7 +61,7 @@ newrelic-api = "5.14.0" okhttp = "4.11.0" postgre = "42.3.10" prometheus = "0.15.0" -reactor = "2020.0.45" +reactor = "2020.0.46" rest-assured = "5.3.2" signalfx = "1.0.44" slf4j = "1.7.36" From bc2e85aac725532ba782b80b25f9a435b68593a6 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 9 Aug 2024 17:23:46 +0900 Subject: [PATCH 42/74] Bump com.amazonaws:aws-java-sdk-cloudwatch from 1.12.755 to 1.12.767 (#5353) Bumps [com.amazonaws:aws-java-sdk-cloudwatch](https://github.com/aws/aws-sdk-java) from 1.12.755 to 1.12.767. - [Changelog](https://github.com/aws/aws-sdk-java/blob/master/CHANGELOG.md) - [Commits](https://github.com/aws/aws-sdk-java/compare/1.12.755...1.12.767) --- updated-dependencies: - dependency-name: com.amazonaws:aws-java-sdk-cloudwatch dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- gradle/libs.versions.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index c1691c650a..c5ea24d273 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -6,7 +6,7 @@ aspectjweaver = "1.8.14" assertj = "3.24.2" awaitility = "4.2.1" # legacy SDK -aws-cloudwatch = "1.12.755" +aws-cloudwatch = "1.12.767" caffeine = "2.9.3" cloudwatch2 = "2.18.41" colt = "1.2.0" From ffa0e24477857fefd540fa2f80dc3552725e13a7 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 9 Aug 2024 17:24:10 +0900 Subject: [PATCH 43/74] Bump io.netty:netty-bom from 4.1.111.Final to 4.1.112.Final (#5352) Bumps [io.netty:netty-bom](https://github.com/netty/netty) from 4.1.111.Final to 4.1.112.Final. - [Commits](https://github.com/netty/netty/compare/netty-4.1.111.Final...netty-4.1.112.Final) --- updated-dependencies: - dependency-name: io.netty:netty-bom dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- gradle/libs.versions.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index c5ea24d273..21a178895a 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -56,7 +56,7 @@ maven-resolver = "1.8.2" # 5.x requires JDK 11. mockito = "4.11.0" mongo = "4.6.1" -netty = "4.1.111.Final" +netty = "4.1.112.Final" newrelic-api = "5.14.0" okhttp = "4.11.0" postgre = "42.3.10" From ab352c69dc6dde9d739199deed425ac55852f449 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 9 Aug 2024 17:24:34 +0900 Subject: [PATCH 44/74] Bump jetty from 9.4.54.v20240208 to 9.4.55.v20240627 (#5349) Bumps `jetty` from 9.4.54.v20240208 to 9.4.55.v20240627. Updates `org.eclipse.jetty:jetty-client` from 9.4.54.v20240208 to 9.4.55.v20240627 Updates `org.eclipse.jetty:jetty-server` from 9.4.54.v20240208 to 9.4.55.v20240627 Updates `org.eclipse.jetty:jetty-servlet` from 9.4.54.v20240208 to 9.4.55.v20240627 --- updated-dependencies: - dependency-name: org.eclipse.jetty:jetty-client dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: org.eclipse.jetty:jetty-server dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: org.eclipse.jetty:jetty-servlet dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- gradle/libs.versions.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 21a178895a..37e3121a8b 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -38,7 +38,7 @@ jackson-databind = "2.13.5" javax-cache = "1.1.1" javax-inject = "1" jaxb = "2.3.1" -jetty = "9.4.54.v20240208" +jetty = "9.4.55.v20240627" jersey2 = "2.37" jersey3 = "3.0.4" jmh = "1.37" From 806dde3c81c81f4d930eb7ce5320f9fcad6a3231 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 9 Aug 2024 17:56:41 +0900 Subject: [PATCH 45/74] Bump org.awaitility:awaitility from 4.2.1 to 4.2.2 (#5371) Bumps [org.awaitility:awaitility](https://github.com/awaitility/awaitility) from 4.2.1 to 4.2.2. - [Changelog](https://github.com/awaitility/awaitility/blob/master/changelog.txt) - [Commits](https://github.com/awaitility/awaitility/compare/awaitility-4.2.1...awaitility-4.2.2) --- updated-dependencies: - dependency-name: org.awaitility:awaitility dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- gradle/libs.versions.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 37e3121a8b..30c25858b7 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -4,7 +4,7 @@ archunit = "1.1.1" asmForPlugins = "7.3.1" aspectjweaver = "1.8.14" assertj = "3.24.2" -awaitility = "4.2.1" +awaitility = "4.2.2" # legacy SDK aws-cloudwatch = "1.12.767" caffeine = "2.9.3" From 214a682db167a27834f21dfe26c22e60aa29aed6 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 9 Aug 2024 18:05:23 +0900 Subject: [PATCH 46/74] Bump maven-resolver from 1.9.20 to 1.9.22 (#5374) Bumps `maven-resolver` from 1.9.20 to 1.9.22. Updates `org.apache.maven.resolver:maven-resolver-connector-basic` from 1.9.20 to 1.9.22 - [Release notes](https://github.com/apache/maven-resolver/releases) - [Commits](https://github.com/apache/maven-resolver/compare/maven-resolver-1.9.20...maven-resolver-1.9.22) Updates `org.apache.maven.resolver:maven-resolver-transport-http` from 1.9.20 to 1.9.22 --- updated-dependencies: - dependency-name: org.apache.maven.resolver:maven-resolver-connector-basic dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: org.apache.maven.resolver:maven-resolver-transport-http dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- gradle/libs.versions.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 3ffb413dc0..6b0e792096 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -56,7 +56,7 @@ kafka-junit = "4.2.10" latency-utils = "2.0.3" logback12 = "1.2.13" log4j = "2.21.1" -maven-resolver = "1.9.20" +maven-resolver = "1.9.22" mockito4 = "4.11.0" mockito5 = "5.7.0" mongo = "4.11.2" From f1f513ee9f1fcf199c5814decc5c067f51a11af1 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 9 Aug 2024 18:05:43 +0900 Subject: [PATCH 47/74] Bump org.mongodb:mongodb-driver-sync from 4.11.2 to 4.11.3 (#5375) Bumps [org.mongodb:mongodb-driver-sync](https://github.com/mongodb/mongo-java-driver) from 4.11.2 to 4.11.3. - [Release notes](https://github.com/mongodb/mongo-java-driver/releases) - [Commits](https://github.com/mongodb/mongo-java-driver/compare/r4.11.2...r4.11.3) --- updated-dependencies: - dependency-name: org.mongodb:mongodb-driver-sync dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- gradle/libs.versions.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 6b0e792096..a3d0bc9a46 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -59,7 +59,7 @@ log4j = "2.21.1" maven-resolver = "1.9.22" mockito4 = "4.11.0" mockito5 = "5.7.0" -mongo = "4.11.2" +mongo = "4.11.3" netty = "4.1.112.Final" newrelic-api = "5.14.0" okhttp = "4.11.0" From 6032f65c10201fd24f7ab846801a40c48585da7b Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 9 Aug 2024 18:06:05 +0900 Subject: [PATCH 48/74] Bump com.netflix.spectator:spectator-reg-atlas from 1.7.14 to 1.7.18 (#5373) Bumps [com.netflix.spectator:spectator-reg-atlas](https://github.com/Netflix/spectator) from 1.7.14 to 1.7.18. - [Release notes](https://github.com/Netflix/spectator/releases) - [Changelog](https://github.com/Netflix/spectator/blob/main/CHANGELOG.md) - [Commits](https://github.com/Netflix/spectator/compare/v1.7.14...v1.7.18) --- updated-dependencies: - dependency-name: com.netflix.spectator:spectator-reg-atlas dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- gradle/libs.versions.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index a3d0bc9a46..10a1abe241 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -69,7 +69,7 @@ reactor = "2022.0.20" rest-assured = "5.3.2" signalfx = "1.0.44" slf4j = "1.7.36" -spectator-atlas = "1.7.14" +spectator-atlas = "1.7.18" spring = "5.3.37" spring-javaformat = "0.0.42" testcontainers = "1.19.8" From 0f86200c85c984d2fd8d9c36afb5c256c32520ab Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 9 Aug 2024 18:06:26 +0900 Subject: [PATCH 49/74] Bump org.awaitility:awaitility from 4.2.1 to 4.2.2 (#5372) Bumps [org.awaitility:awaitility](https://github.com/awaitility/awaitility) from 4.2.1 to 4.2.2. - [Changelog](https://github.com/awaitility/awaitility/blob/master/changelog.txt) - [Commits](https://github.com/awaitility/awaitility/compare/awaitility-4.2.1...awaitility-4.2.2) --- updated-dependencies: - dependency-name: org.awaitility:awaitility dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> From 1781a321222eb17cf98749a08831d95aee921a4d Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 9 Aug 2024 18:10:18 +0900 Subject: [PATCH 50/74] Bump io.projectreactor:reactor-bom from 2022.0.20 to 2022.0.21 (#5340) Bumps [io.projectreactor:reactor-bom](https://github.com/reactor/reactor) from 2022.0.20 to 2022.0.21. - [Release notes](https://github.com/reactor/reactor/releases) - [Commits](https://github.com/reactor/reactor/compare/2022.0.20...2022.0.21) --- updated-dependencies: - dependency-name: io.projectreactor:reactor-bom dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- gradle/libs.versions.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 10a1abe241..2687120a5a 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -65,7 +65,7 @@ newrelic-api = "5.14.0" okhttp = "4.11.0" postgre = "42.6.2" prometheus = "0.16.0" -reactor = "2022.0.20" +reactor = "2022.0.21" rest-assured = "5.3.2" signalfx = "1.0.44" slf4j = "1.7.36" From 34f06a38ac016b5c66e04c81437229054cfc7195 Mon Sep 17 00:00:00 2001 From: Jonatan Ivanov Date: Fri, 9 Aug 2024 11:44:41 -0700 Subject: [PATCH 51/74] Bump jersey3 from 3.0.4 to 3.0.12 Closes gh-5376 --- gradle/libs.versions.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 30c25858b7..e05c19517f 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -40,7 +40,7 @@ javax-inject = "1" jaxb = "2.3.1" jetty = "9.4.55.v20240627" jersey2 = "2.37" -jersey3 = "3.0.4" +jersey3 = "3.0.12" jmh = "1.37" jooq = "3.14.16" jsr107 = "1.0.0" From 98c104261a6fdba3411e2897d8dff3ab628ab6a8 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 9 Aug 2024 12:21:54 -0700 Subject: [PATCH 52/74] Bump com.fasterxml.jackson.core:jackson-databind from 2.17.1 to 2.17.2 (#5364) Bumps [com.fasterxml.jackson.core:jackson-databind](https://github.com/FasterXML/jackson) from 2.17.1 to 2.17.2. - [Commits](https://github.com/FasterXML/jackson/commits) --- updated-dependencies: - dependency-name: com.fasterxml.jackson.core:jackson-databind dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- gradle/libs.versions.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 3a3eec7d35..a1b8ba2556 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -35,7 +35,7 @@ httpcomponents-client5 = "5.3.1" # in hystrix 1.5.12, but Netflix re-released 1.5.11 as 1.5.18 late in 2018. # <=1.5.11 or 1.5.18 doesn't break with Micrometer, but open metrics won't be correct necessarily. hystrix = "1.5.12" -jackson-databind = "2.17.1" +jackson-databind = "2.17.2" javax-cache = "1.1.1" javax-inject = "1" jaxb = "2.3.1" From 90b6d09b3db46e55d237e51490122c2b167487c2 Mon Sep 17 00:00:00 2001 From: Tommy Ludwig <8924140+shakuzen@users.noreply.github.com> Date: Mon, 19 Aug 2024 16:14:38 +0900 Subject: [PATCH 53/74] Do not include concurrency-tests in BOM This is an internal test module that is not published, so it should not be included in the BOM dependency management. Resolves gh-5395 --- micrometer-bom/build.gradle | 1 + 1 file changed, 1 insertion(+) diff --git a/micrometer-bom/build.gradle b/micrometer-bom/build.gradle index 3ed07b70be..017d785a78 100644 --- a/micrometer-bom/build.gradle +++ b/micrometer-bom/build.gradle @@ -9,6 +9,7 @@ dependencies { rootProject.subprojects.findAll { !it.name.contains('sample') && !it.name.contains('benchmark') && + !it.name.contains('concurrency-tests') && !it.name.contains('micrometer-bom') && !it.name.contains('micrometer-osgi-test') && it.name != 'docs' From b2990fb61842b8d69c072e50afc67ee8cca530b6 Mon Sep 17 00:00:00 2001 From: Siarhei Date: Tue, 27 Aug 2024 09:40:29 +0200 Subject: [PATCH 54/74] Fix a typo in meters.adoc (#5399) --- docs/modules/ROOT/pages/concepts/meters.adoc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/modules/ROOT/pages/concepts/meters.adoc b/docs/modules/ROOT/pages/concepts/meters.adoc index a29770e1ad..35ddc77127 100644 --- a/docs/modules/ROOT/pages/concepts/meters.adoc +++ b/docs/modules/ROOT/pages/concepts/meters.adoc @@ -6,6 +6,6 @@ A `Meter` is the interface for collecting a set of measurements (which we indivi Micrometer supports a set of `Meter` primitives, including `Timer`, `Counter`, `Gauge`, `DistributionSummary`, `LongTaskTimer`, `FunctionCounter`, `FunctionTimer`, and `TimeGauge`. Different meter types result in a different number of time series metrics. For example, while there is a single metric that represents a `Gauge`, a `Timer` measures both the count of timed events and the total time of all timed events. TIP: Recording a measurement for a `Meter` is expected to be a relatively cheap operation and should not throw any exception. -If the xref:./registry.adoc[registry] supports publishing metrics to a monitoring system, this is done in a separate thread snd should not affect recording metrics. +If the xref:./registry.adoc[registry] supports publishing metrics to a monitoring system, this is done in a separate thread and should not affect recording metrics. A meter is uniquely identified by its name and dimensions. We use the terms, "`dimensions`" and "`tags,`" interchangeably, and the Micrometer interface is `Tag` simply because it is shorter. As a general rule, it should be possible to use the name as a pivot. Dimensions let a particular named metric be sliced to drill down and reason about the data. This means that, if only the name is selected, you can drill down by using other dimensions and reason about the value being shown. From d1429b5ed81377a3a12fe1550836c5b4e86ede11 Mon Sep 17 00:00:00 2001 From: Johnny Lim Date: Thu, 29 Aug 2024 11:33:13 +0900 Subject: [PATCH 55/74] Fix GuavaCacheMetricsTest and CaffeineCacheMetricsTest (#5405) --- .../cache/CaffeineCacheMetricsTest.java | 32 ++++++++++-- .../binder/cache/GuavaCacheMetricsTest.java | 52 +++++++++++++++---- 2 files changed, 69 insertions(+), 15 deletions(-) diff --git a/micrometer-core/src/test/java/io/micrometer/core/instrument/binder/cache/CaffeineCacheMetricsTest.java b/micrometer-core/src/test/java/io/micrometer/core/instrument/binder/cache/CaffeineCacheMetricsTest.java index 515a8097ef..6c7c37d967 100644 --- a/micrometer-core/src/test/java/io/micrometer/core/instrument/binder/cache/CaffeineCacheMetricsTest.java +++ b/micrometer-core/src/test/java/io/micrometer/core/instrument/binder/cache/CaffeineCacheMetricsTest.java @@ -26,17 +26,20 @@ import io.micrometer.core.instrument.simple.SimpleMeterRegistry; import org.junit.jupiter.api.Test; +import java.util.concurrent.TimeUnit; + import static org.assertj.core.api.Assertions.assertThat; /** * Tests for {@link CaffeineCacheMetrics}. * * @author Oleksii Bondar + * @author Johnny Lim */ class CaffeineCacheMetricsTest extends AbstractCacheMetricsTest { // tag::setup[] - LoadingCache cache = Caffeine.newBuilder().build(key -> ""); + LoadingCache cache = Caffeine.newBuilder().recordStats().build(key -> ""); CaffeineCacheMetrics> metrics = new CaffeineCacheMetrics<>(cache, "testCache", expectedTag); @@ -45,6 +48,11 @@ class CaffeineCacheMetricsTest extends AbstractCacheMetricsTest { @Test void reportExpectedGeneralMetrics() { + cache.put("a", "1"); + cache.get("a"); + cache.get("a"); + cache.get("b"); + // tag::register[] MeterRegistry registry = new SimpleMeterRegistry(); metrics.bindTo(registry); @@ -58,7 +66,7 @@ void reportExpectedGeneralMetrics() { // specific to LoadingCache instance TimeGauge loadDuration = fetch(registry, "cache.load.duration").timeGauge(); - assertThat(loadDuration.value()).isEqualTo(stats.totalLoadTime()); + assertThat(loadDuration.value(TimeUnit.NANOSECONDS)).isEqualTo(stats.totalLoadTime()); FunctionCounter successfulLoad = fetch(registry, "cache.load", Tags.of("result", "success")).functionCounter(); assertThat(successfulLoad.count()).isEqualTo(stats.loadSuccessCount()); @@ -95,12 +103,28 @@ void returnCacheSize() { @Test void returnHitCount() { - assertThat(metrics.hitCount()).isEqualTo(cache.stats().hitCount()); + cache.put("a", "1"); + cache.get("a"); + cache.get("a"); + + assertThat(metrics.hitCount()).isEqualTo(cache.stats().hitCount()).isEqualTo(2); + } + + @Test + void returnHitCountWithoutRecordStats() { + LoadingCache cache = Caffeine.newBuilder().build(key -> ""); + cache.put("a", "1"); + cache.get("a"); + cache.get("a"); + + assertThat(metrics.hitCount()).isEqualTo(cache.stats().hitCount()).isEqualTo(0); } @Test void returnMissCount() { - assertThat(metrics.missCount()).isEqualTo(cache.stats().missCount()); + cache.get("b"); + + assertThat(metrics.missCount()).isEqualTo(cache.stats().missCount()).isEqualTo(1); } @Test diff --git a/micrometer-core/src/test/java/io/micrometer/core/instrument/binder/cache/GuavaCacheMetricsTest.java b/micrometer-core/src/test/java/io/micrometer/core/instrument/binder/cache/GuavaCacheMetricsTest.java index 56f07b271f..0b13335b5e 100644 --- a/micrometer-core/src/test/java/io/micrometer/core/instrument/binder/cache/GuavaCacheMetricsTest.java +++ b/micrometer-core/src/test/java/io/micrometer/core/instrument/binder/cache/GuavaCacheMetricsTest.java @@ -20,18 +20,22 @@ import io.micrometer.core.instrument.simple.SimpleMeterRegistry; import org.junit.jupiter.api.Test; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.TimeUnit; + import static org.assertj.core.api.Assertions.assertThat; /** * Tests for {@link GuavaCacheMetrics}. * * @author Oleksii Bondar + * @author Johnny Lim */ class GuavaCacheMetricsTest extends AbstractCacheMetricsTest { // tag::setup[] - LoadingCache cache = CacheBuilder.newBuilder().build(new CacheLoader() { - public String load(String key) throws Exception { + LoadingCache cache = CacheBuilder.newBuilder().recordStats().build(new CacheLoader<>() { + public String load(String key) { return ""; } }); @@ -42,7 +46,12 @@ public String load(String key) throws Exception { // end::setup[] @Test - void reportExpectedMetrics() { + void reportExpectedMetrics() throws ExecutionException { + cache.put("a", "1"); + cache.get("a"); + cache.get("a"); + cache.get("b"); + // tag::register[] MeterRegistry registry = new SimpleMeterRegistry(); metrics.bindTo(registry); @@ -52,13 +61,13 @@ void reportExpectedMetrics() { // common metrics Gauge cacheSize = fetch(registry, "cache.size").gauge(); - assertThat(cacheSize.value()).isEqualTo(cache.size()); + assertThat(cacheSize.value()).isEqualTo(cache.size()).isEqualTo(2); FunctionCounter hitCount = fetch(registry, "cache.gets", Tags.of("result", "hit")).functionCounter(); - assertThat(hitCount.count()).isEqualTo(metrics.hitCount()); + assertThat(hitCount.count()).isEqualTo(metrics.hitCount()).isEqualTo(2); FunctionCounter missCount = fetch(registry, "cache.gets", Tags.of("result", "miss")).functionCounter(); - assertThat(missCount.count()).isEqualTo(metrics.missCount().doubleValue()); + assertThat(missCount.count()).isEqualTo(metrics.missCount().doubleValue()).isEqualTo(1); FunctionCounter cachePuts = fetch(registry, "cache.puts").functionCounter(); assertThat(cachePuts.count()).isEqualTo(metrics.putCount()); @@ -68,7 +77,7 @@ void reportExpectedMetrics() { CacheStats stats = cache.stats(); TimeGauge loadDuration = fetch(registry, "cache.load.duration").timeGauge(); - assertThat(loadDuration.value()).isEqualTo(stats.totalLoadTime()); + assertThat(loadDuration.value(TimeUnit.NANOSECONDS)).isEqualTo(stats.totalLoadTime()); FunctionCounter successfulLoad = fetch(registry, "cache.load", Tags.of("result", "success")).functionCounter(); assertThat(successfulLoad.count()).isEqualTo(stats.loadSuccessCount()); @@ -93,13 +102,34 @@ void returnCacheSize() { } @Test - void returnHitCount() { - assertThat(metrics.hitCount()).isEqualTo(cache.stats().hitCount()); + void returnHitCount() throws ExecutionException { + cache.put("a", "1"); + cache.get("a"); + cache.get("a"); + + assertThat(metrics.hitCount()).isEqualTo(cache.stats().hitCount()).isEqualTo(2); } @Test - void returnMissCount() { - assertThat(metrics.missCount()).isEqualTo(cache.stats().missCount()); + void returnHitCountWithoutRecordStats() throws ExecutionException { + LoadingCache cache = CacheBuilder.newBuilder().build(new CacheLoader<>() { + public String load(String key) { + return ""; + } + }); + + cache.put("a", "1"); + cache.get("a"); + cache.get("a"); + + assertThat(metrics.hitCount()).isEqualTo(cache.stats().hitCount()).isEqualTo(0); + } + + @Test + void returnMissCount() throws ExecutionException { + cache.get("b"); + + assertThat(metrics.missCount()).isEqualTo(cache.stats().missCount()).isEqualTo(1); } @Test From b8b4b0de338ac4d035313884ce024bbdce06e969 Mon Sep 17 00:00:00 2001 From: Georg Pirklbauer Date: Mon, 2 Sep 2024 11:05:21 +0200 Subject: [PATCH 56/74] [Dynatrace] Add artificial zero percentile in Meter creation rather than a MeterFilter (#4782) This ensures the artificial percentile is consistently added regardless of configured MeterFilters on the registry, which is not knowable at construction time. --- .../dynatrace/DynatraceMeterRegistry.java | 148 +++++++-------- .../dynatrace/DynatraceMeterRegistryTest.java | 173 +++++++++++++++++- .../tck/MeterRegistryCompatibilityKit.java | 48 +++-- 3 files changed, 276 insertions(+), 93 deletions(-) diff --git a/implementations/micrometer-registry-dynatrace/src/main/java/io/micrometer/dynatrace/DynatraceMeterRegistry.java b/implementations/micrometer-registry-dynatrace/src/main/java/io/micrometer/dynatrace/DynatraceMeterRegistry.java index effab52804..89413f0689 100644 --- a/implementations/micrometer-registry-dynatrace/src/main/java/io/micrometer/dynatrace/DynatraceMeterRegistry.java +++ b/implementations/micrometer-registry-dynatrace/src/main/java/io/micrometer/dynatrace/DynatraceMeterRegistry.java @@ -60,6 +60,10 @@ public class DynatraceMeterRegistry extends StepMeterRegistry { private final boolean useDynatraceSummaryInstruments; + private final boolean shouldAddZeroPercentile; + + private final DenyZeroPercentileMeterFilter zeroPercentileMeterFilter = new DenyZeroPercentileMeterFilter(); + private final AbstractDynatraceExporter exporter; @SuppressWarnings("deprecation") @@ -73,19 +77,24 @@ private DynatraceMeterRegistry(DynatraceConfig config, Clock clock, ThreadFactor super(config, clock); useDynatraceSummaryInstruments = config.useDynatraceSummaryInstruments(); + // zero percentile is needed only when using the V2 exporter and not using + // Dynatrace summary instruments. + shouldAddZeroPercentile = config.apiVersion() == DynatraceApiVersion.V2 && !useDynatraceSummaryInstruments; if (config.apiVersion() == DynatraceApiVersion.V2) { logger.info("Exporting to Dynatrace metrics API v2"); this.exporter = new DynatraceExporterV2(config, clock, httpClient); - // Not used for Timer and DistributionSummary in V2 anymore, but still used - // for the other timer types. - registerMinPercentile(); } else { logger.info("Exporting to Dynatrace metrics API v1"); this.exporter = new DynatraceExporterV1(config, clock, httpClient); } + if (shouldAddZeroPercentile) { + // zero percentiles automatically added should not be exported. + this.config().meterFilter(zeroPercentileMeterFilter); + } + start(threadFactory); } @@ -109,7 +118,12 @@ protected DistributionSummary newDistributionSummary(Meter.Id id, if (useDynatraceSummaryInstruments) { return new DynatraceDistributionSummary(id, clock, distributionStatisticConfig, scale); } - return super.newDistributionSummary(id, distributionStatisticConfig, scale); + + DistributionStatisticConfig config = distributionStatisticConfig; + if (shouldAddZeroPercentile) { + config = addZeroPercentileIfMissing(id, distributionStatisticConfig); + } + return super.newDistributionSummary(id, config, scale); } @Override @@ -119,7 +133,12 @@ protected Timer newTimer(Meter.Id id, DistributionStatisticConfig distributionSt return new DynatraceTimer(id, clock, distributionStatisticConfig, pauseDetector, exporter.getBaseTimeUnit()); } - return super.newTimer(id, distributionStatisticConfig, pauseDetector); + + DistributionStatisticConfig config = distributionStatisticConfig; + if (shouldAddZeroPercentile) { + config = addZeroPercentileIfMissing(id, distributionStatisticConfig); + } + return super.newTimer(id, config, pauseDetector); } @Override @@ -128,78 +147,63 @@ protected LongTaskTimer newLongTaskTimer(Meter.Id id, DistributionStatisticConfi return new DynatraceLongTaskTimer(id, clock, exporter.getBaseTimeUnit(), distributionStatisticConfig, false); } - return super.newLongTaskTimer(id, distributionStatisticConfig); + + DistributionStatisticConfig config = distributionStatisticConfig; + if (shouldAddZeroPercentile) { + config = addZeroPercentileIfMissing(id, distributionStatisticConfig); + } + return super.newLongTaskTimer(id, config); } - /** - * As the micrometer summary statistics (DistributionSummary, and a number of timer - * meter types) do not provide the minimum values that are required by Dynatrace to - * ingest summary metrics, we add the 0th percentile to each summary statistic and use - * that as the minimum value. - */ - private void registerMinPercentile() { - config().meterFilter(new MeterFilter() { - private final Set metersWithArtificialZeroPercentile = ConcurrentHashMap.newKeySet(); - - /** - * Adds 0th percentile if the user hasn't already added and tracks those meter - * names where the 0th percentile was artificially added. - */ - @Override - public DistributionStatisticConfig configure(Meter.Id id, DistributionStatisticConfig config) { - if (useDynatraceSummaryInstruments && dynatraceInstrumentTypeExists(id)) { - // do not add artificial 0 percentile when using Dynatrace instruments - return config; - } - - double[] percentiles; - - double[] configPercentiles = config.getPercentiles(); - if (configPercentiles == null) { - percentiles = new double[] { 0 }; - metersWithArtificialZeroPercentile.add(id.getName() + ".percentile"); - } - else if (!containsZeroPercentile(config)) { - percentiles = new double[configPercentiles.length + 1]; - System.arraycopy(configPercentiles, 0, percentiles, 0, configPercentiles.length); - percentiles[configPercentiles.length] = 0; // theoretically this is - // already zero - metersWithArtificialZeroPercentile.add(id.getName() + ".percentile"); - } - else { - percentiles = configPercentiles; - } - - return DistributionStatisticConfig.builder().percentiles(percentiles).build().merge(config); - } - - /** - * Denies artificially added 0th percentile meters. - */ - @Override - public MeterFilterReply accept(Meter.Id id) { - return hasArtificialZerothPercentile(id) ? DENY : NEUTRAL; - } - - private boolean containsZeroPercentile(DistributionStatisticConfig config) { - return Arrays.stream(config.getPercentiles()).anyMatch(percentile -> percentile == 0); - } - - private boolean hasArtificialZerothPercentile(Meter.Id id) { - return metersWithArtificialZeroPercentile.contains(id.getName()) && "0".equals(id.getTag("phi")); - } - }); + private static class DenyZeroPercentileMeterFilter implements MeterFilter { + + private final Set metersWithArtificialZeroPercentile = ConcurrentHashMap.newKeySet(); + + private boolean hasArtificialZeroPercentile(Meter.Id id) { + return metersWithArtificialZeroPercentile.contains(id.getName()) && "0".equals(id.getTag("phi")); + } + + @Override + public MeterFilterReply accept(Meter.Id id) { + return hasArtificialZeroPercentile(id) ? DENY : NEUTRAL; + } + + public void addMeterId(Meter.Id id) { + metersWithArtificialZeroPercentile.add(id.getName() + ".percentile"); + } + + } + + private boolean containsZero(double[] percentiles) { + return Arrays.stream(percentiles).anyMatch(percentile -> percentile == 0); } - private boolean dynatraceInstrumentTypeExists(Meter.Id id) { - switch (id.getType()) { - case DISTRIBUTION_SUMMARY: - case TIMER: - case LONG_TASK_TIMER: - return true; - default: - return false; + private DistributionStatisticConfig addZeroPercentileIfMissing(Meter.Id id, + DistributionStatisticConfig distributionStatisticConfig) { + double[] percentiles; + + double[] configPercentiles = distributionStatisticConfig.getPercentiles(); + if (configPercentiles == null) { + percentiles = new double[] { 0. }; + zeroPercentileMeterFilter.addMeterId(id); + } + else if (!containsZero(configPercentiles)) { + percentiles = new double[configPercentiles.length + 1]; + System.arraycopy(configPercentiles, 0, percentiles, 0, configPercentiles.length); + // theoretically this is already zero + percentiles[configPercentiles.length] = 0; + zeroPercentileMeterFilter.addMeterId(id); } + else { + // Zero percentile is explicitly added to the config, no need to add it to + // drop list. + return distributionStatisticConfig; + } + + return DistributionStatisticConfig.builder() + .percentiles(percentiles) + .build() + .merge(distributionStatisticConfig); } public static class Builder { diff --git a/implementations/micrometer-registry-dynatrace/src/test/java/io/micrometer/dynatrace/DynatraceMeterRegistryTest.java b/implementations/micrometer-registry-dynatrace/src/test/java/io/micrometer/dynatrace/DynatraceMeterRegistryTest.java index adcf64db1d..3837885cf9 100644 --- a/implementations/micrometer-registry-dynatrace/src/test/java/io/micrometer/dynatrace/DynatraceMeterRegistryTest.java +++ b/implementations/micrometer-registry-dynatrace/src/test/java/io/micrometer/dynatrace/DynatraceMeterRegistryTest.java @@ -15,15 +15,17 @@ */ package io.micrometer.dynatrace; -import io.micrometer.core.instrument.Counter; -import io.micrometer.core.instrument.MockClock; -import io.micrometer.core.instrument.Timer; +import io.micrometer.core.instrument.*; import io.micrometer.core.ipc.http.HttpSender; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.mockito.ArgumentCaptor; import java.nio.charset.StandardCharsets; +import java.time.Duration; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; import static java.util.concurrent.TimeUnit.MILLISECONDS; import static org.assertj.core.api.Assertions.assertThat; @@ -151,6 +153,142 @@ void shouldNotTrackPercentilesWithDynatraceSummary() throws Throwable { "#my.timer gauge dt.meta.unit=milliseconds")))); } + @Test + void shouldTrackPercentilesWhenDynatraceSummaryInstrumentsNotUsed() throws Throwable { + DynatraceConfig dynatraceConfig = getNonSummaryInstrumentsConfig(); + + DynatraceMeterRegistry registry = DynatraceMeterRegistry.builder(dynatraceConfig) + .httpClient(httpClient) + .clock(clock) + .build(); + + HttpSender.Request.Builder builder = HttpSender.Request.build(config.uri(), httpClient); + when(httpClient.post(config.uri())).thenReturn(builder); + + double[] trackedPercentiles = new double[] { 0.5, 0.7, 0.99 }; + + Timer timer = Timer.builder("my.timer").publishPercentiles(trackedPercentiles).register(registry); + DistributionSummary distributionSummary = DistributionSummary.builder("my.ds") + .publishPercentiles(trackedPercentiles) + .register(registry); + CountDownLatch lttCountDownLatch = new CountDownLatch(1); + LongTaskTimer longTaskTimer = LongTaskTimer.builder("my.ltt") + .publishPercentiles(trackedPercentiles) + .register(registry); + + timer.record(Duration.ofMillis(100)); + distributionSummary.record(100); + + ExecutorService executorService = Executors.newSingleThreadExecutor(); + executorService.submit(() -> longTaskTimer.record(() -> { + clock.add(Duration.ofMillis(100)); + + try { + assertThat(lttCountDownLatch.await(300, MILLISECONDS)).isTrue(); + } + catch (InterruptedException e) { + throw new RuntimeException(e); + } + })); + + clock.add(dynatraceConfig.step()); + + registry.publish(); + // release long task timer + lttCountDownLatch.countDown(); + + verify(httpClient).send( + assertArg((request -> assertThat(request.getEntity()).asString() + .hasLineCount(16) + .contains( + // Timer lines + "my.timer,dt.metrics.source=micrometer gauge,min=100,max=100,sum=100,count=1 " + + clock.wallTime(), + "#my.timer gauge dt.meta.unit=milliseconds", + // Timer percentile lines. Percentiles are 0 because the step + // rolled over. + "my.timer.percentile,dt.metrics.source=micrometer,phi=0.5 gauge,0 " + clock.wallTime(), + "my.timer.percentile,dt.metrics.source=micrometer,phi=0.7 gauge,0 " + clock.wallTime(), + "my.timer.percentile,dt.metrics.source=micrometer,phi=0.99 gauge,0 " + clock.wallTime(), + "#my.timer.percentile gauge dt.meta.unit=milliseconds", + + // DistributionSummary lines + "my.ds,dt.metrics.source=micrometer gauge,min=100,max=100,sum=100,count=1 " + + clock.wallTime(), + // DistributionSummary percentile lines. Percentiles are 0 + // because the step rolled over. + "my.ds.percentile,dt.metrics.source=micrometer,phi=0.5 gauge,0 " + clock.wallTime(), + "my.ds.percentile,dt.metrics.source=micrometer,phi=0.7 gauge,0 " + clock.wallTime(), + "my.ds.percentile,dt.metrics.source=micrometer,phi=0.99 gauge,0 " + clock.wallTime(), + + // LongTaskTimer lines + "my.ltt,dt.metrics.source=micrometer gauge,min=100,max=100,sum=100,count=1 " + + clock.wallTime(), + "#my.ltt gauge dt.meta.unit=milliseconds", + // LongTaskTimer percentile lines + // 0th percentile is missing because it doesn't clear the + // "interpolatable line" threshold defined in + // DefaultLongTaskTimer#takeSnapshot(). + "my.ltt.percentile,dt.metrics.source=micrometer,phi=0.5 gauge,100 " + clock.wallTime(), + "my.ltt.percentile,dt.metrics.source=micrometer,phi=0.7 gauge,100 " + clock.wallTime(), + "my.ltt.percentile,dt.metrics.source=micrometer,phi=0.99 gauge,100 " + clock.wallTime(), + "#my.ltt.percentile gauge dt.meta.unit=milliseconds")))); + } + + @Test + void shouldTrackPercentilesWhenDynatraceSummaryInstrumentsNotUsed_shouldExport0PercentileWhenSpecified() + throws Throwable { + DynatraceConfig dynatraceConfig = getNonSummaryInstrumentsConfig(); + + DynatraceMeterRegistry registry = DynatraceMeterRegistry.builder(dynatraceConfig) + .httpClient(httpClient) + .clock(clock) + .build(); + + HttpSender.Request.Builder builder = HttpSender.Request.build(config.uri(), httpClient); + when(httpClient.post(config.uri())).thenReturn(builder); + + // create instruments with an explicit 0 percentile. This should be exported. + Timer timer = Timer.builder("my.timer").publishPercentiles(0, 0.5, 0.99).register(registry); + DistributionSummary distributionSummary = DistributionSummary.builder("my.ds") + .publishPercentiles(0, 0.5, 0.99) + .register(registry); + // For LongTaskTimer, the 0 percentile is not tracked as it doesn't clear the + // "interpolatable line" threshold defined in DefaultLongTaskTimer#takeSnapshot(). + // see shouldTrackPercentilesWhenDynatraceSummaryInstrumentsNotUsed for a test + // that exports LongTaskTimer percentiles + + timer.record(Duration.ofMillis(100)); + distributionSummary.record(100); + + clock.add(dynatraceConfig.step()); + + registry.publish(); + + verify(httpClient) + .send(assertArg((request -> assertThat(request.getEntity()).asString() + .hasLineCount(10) + .contains( + // Timer lines + "my.timer,dt.metrics.source=micrometer gauge,min=100,max=100,sum=100,count=1 " + + clock.wallTime(), + "#my.timer gauge dt.meta.unit=milliseconds", + // Timer percentile lines. Percentiles are 0 because the step + // rolled over. + "my.timer.percentile,dt.metrics.source=micrometer,phi=0 gauge,0 " + clock.wallTime(), + "my.timer.percentile,dt.metrics.source=micrometer,phi=0.5 gauge,0 " + clock.wallTime(), + "my.timer.percentile,dt.metrics.source=micrometer,phi=0.99 gauge,0 " + clock.wallTime(), + "#my.timer.percentile gauge dt.meta.unit=milliseconds", + + // DistributionSummary lines + "my.ds,dt.metrics.source=micrometer gauge,min=100,max=100,sum=100,count=1 " + clock.wallTime(), + // DistributionSummary percentile lines. Percentiles are 0 because + // the step rolled over. + "my.ds.percentile,dt.metrics.source=micrometer,phi=0 gauge,0 " + clock.wallTime(), + "my.ds.percentile,dt.metrics.source=micrometer,phi=0.5 gauge,0 " + clock.wallTime(), + "my.ds.percentile,dt.metrics.source=micrometer,phi=0.99 gauge,0 " + clock.wallTime())))); + } + @Test void shouldNotExportLinesWithZeroCount() throws Throwable { HttpSender.Request.Builder builder = HttpSender.Request.build(config.uri(), httpClient); @@ -217,6 +355,35 @@ public DynatraceApiVersion apiVersion() { }; } + private static DynatraceConfig getNonSummaryInstrumentsConfig() { + return new DynatraceConfig() { + @Override + public String get(String key) { + return null; + } + + @Override + public String uri() { + return "http://localhost"; + } + + @Override + public String apiToken() { + return "apiToken"; + } + + @Override + public DynatraceApiVersion apiVersion() { + return DynatraceApiVersion.V2; + } + + @Override + public boolean useDynatraceSummaryInstruments() { + return false; + } + }; + } + private String formatDouble(double value) { if (value == (long) value) { return Long.toString((long) value); diff --git a/micrometer-test/src/main/java/io/micrometer/core/tck/MeterRegistryCompatibilityKit.java b/micrometer-test/src/main/java/io/micrometer/core/tck/MeterRegistryCompatibilityKit.java index 5d5420d3a0..51ab9b4e5f 100644 --- a/micrometer-test/src/main/java/io/micrometer/core/tck/MeterRegistryCompatibilityKit.java +++ b/micrometer-test/src/main/java/io/micrometer/core/tck/MeterRegistryCompatibilityKit.java @@ -487,9 +487,8 @@ void record() { @Test @DisplayName("supports sending the Nth percentile active task duration") void percentiles() { - LongTaskTimer t = LongTaskTimer.builder("my.timer") - .publishPercentiles(0.5, 0.7, 0.91, 0.999, 1) - .register(registry); + double[] rawPercentiles = new double[] { 0.5, 0.7, 0.91, 0.999, 1 }; + LongTaskTimer t = LongTaskTimer.builder("my.timer").publishPercentiles(rawPercentiles).register(registry); // Using the example of percentile interpolation from // https://statisticsbyjim.com/basics/percentiles/ @@ -506,22 +505,35 @@ void percentiles() { ValueAtPercentile[] percentiles = t.takeSnapshot().percentileValues(); - assertThat(percentiles[0].percentile()).isEqualTo(0.5); - assertThat(percentiles[0].value(TimeUnit.SECONDS)).isEqualTo(16); - - assertThat(percentiles[1].percentile()).isEqualTo(0.7); - assertThat(percentiles[1].value(TimeUnit.SECONDS)).isEqualTo(37, within(0.001)); - - // a value close-to the highest value that is available for interpolation (11 - // / 12) - assertThat(percentiles[2].percentile()).isEqualTo(0.91); - assertThat(percentiles[2].value(TimeUnit.SECONDS)).isEqualTo(47.5, within(0.1)); - - assertThat(percentiles[3].percentile()).isEqualTo(0.999); - assertThat(percentiles[3].value(TimeUnit.SECONDS)).isEqualTo(48, within(0.1)); + int percentilesChecked = 0; + for (ValueAtPercentile percentile : percentiles) { + if (percentile.percentile() == 0.5) { + assertThat(percentile.value(TimeUnit.SECONDS)).isEqualTo(16); + percentilesChecked++; + } + else if (percentile.percentile() == 0.7) { + assertThat(percentile.value(TimeUnit.SECONDS)).isEqualTo(37, within(0.001)); + percentilesChecked++; + } + else if (percentile.percentile() == 0.91) { + // a value close-to the highest value that is available for + // interpolation (11 + // / 12) + assertThat(percentile.value(TimeUnit.SECONDS)).isEqualTo(47.5, within(0.1)); + percentilesChecked++; + } + else if (percentile.percentile() == 0.999) { + assertThat(percentile.value(TimeUnit.SECONDS)).isEqualTo(48, within(0.1)); + percentilesChecked++; + } + else if (percentile.percentile() == 1) { + assertThat(percentile.value(TimeUnit.SECONDS)).isEqualTo(48); + percentilesChecked++; + } + } - assertThat(percentiles[4].percentile()).isEqualTo(1); - assertThat(percentiles[4].value(TimeUnit.SECONDS)).isEqualTo(48); + // ensure all percentiles specified have been checked. + assertThat(percentilesChecked).isEqualTo(rawPercentiles.length); } @Test From 56d276d91a34bf142db7bf5e9e84226882dcd25e Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 2 Sep 2024 18:26:09 +0900 Subject: [PATCH 57/74] Bump io.spring.develocity.conventions from 0.0.19 to 0.0.20 (#5458) Bumps [io.spring.develocity.conventions](https://github.com/spring-io/develocity-conventions) from 0.0.19 to 0.0.20. - [Release notes](https://github.com/spring-io/develocity-conventions/releases) - [Commits](https://github.com/spring-io/develocity-conventions/compare/v0.0.19...v0.0.20) --- updated-dependencies: - dependency-name: io.spring.develocity.conventions dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- settings.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/settings.gradle b/settings.gradle index 67d213081f..1106b5ea45 100644 --- a/settings.gradle +++ b/settings.gradle @@ -6,7 +6,7 @@ pluginManagement { plugins { id 'com.gradle.develocity' version '3.17.6' - id 'io.spring.develocity.conventions' version '0.0.19' + id 'io.spring.develocity.conventions' version '0.0.20' id 'org.gradle.toolchains.foojay-resolver-convention' version '0.8.0' } From 556aa736408496996ddd04144fe248880f1dfea1 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 2 Sep 2024 18:27:04 +0900 Subject: [PATCH 58/74] Bump io.projectreactor:reactor-bom from 2022.0.21 to 2022.0.22 (#5456) Bumps [io.projectreactor:reactor-bom](https://github.com/reactor/reactor) from 2022.0.21 to 2022.0.22. - [Release notes](https://github.com/reactor/reactor/releases) - [Commits](https://github.com/reactor/reactor/compare/2022.0.21...2022.0.22) --- updated-dependencies: - dependency-name: io.projectreactor:reactor-bom dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- gradle/libs.versions.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 92a8191e56..b6e6cda254 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -65,7 +65,7 @@ newrelic-api = "5.14.0" okhttp = "4.11.0" postgre = "42.6.2" prometheus = "0.16.0" -reactor = "2022.0.21" +reactor = "2022.0.22" rest-assured = "5.3.2" signalfx = "1.0.44" slf4j = "1.7.36" From 74a49cc6a67ff2ebfb2f385e1ef20c6551141390 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 2 Sep 2024 18:27:43 +0900 Subject: [PATCH 59/74] Bump com.netflix.spectator:spectator-reg-atlas from 1.7.18 to 1.7.19 (#5453) Bumps [com.netflix.spectator:spectator-reg-atlas](https://github.com/Netflix/spectator) from 1.7.18 to 1.7.19. - [Release notes](https://github.com/Netflix/spectator/releases) - [Changelog](https://github.com/Netflix/spectator/blob/main/CHANGELOG.md) - [Commits](https://github.com/Netflix/spectator/compare/v1.7.18...v1.7.19) --- updated-dependencies: - dependency-name: com.netflix.spectator:spectator-reg-atlas dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- gradle/libs.versions.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index b6e6cda254..56478eee77 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -69,7 +69,7 @@ reactor = "2022.0.22" rest-assured = "5.3.2" signalfx = "1.0.44" slf4j = "1.7.36" -spectator-atlas = "1.7.18" +spectator-atlas = "1.7.19" spring = "5.3.37" spring-javaformat = "0.0.42" testcontainers = "1.19.8" From e2a77964ff727f0bc7c7f88406a1e37d8a5cd9d0 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 2 Sep 2024 18:55:04 +0900 Subject: [PATCH 60/74] Bump spring-javaformat from 0.0.42 to 0.0.43 (#5452) * Bump spring-javaformat from 0.0.42 to 0.0.43 Bumps `spring-javaformat` from 0.0.42 to 0.0.43. Updates `io.spring.javaformat:spring-javaformat-checkstyle` from 0.0.42 to 0.0.43 - [Release notes](https://github.com/spring-io/spring-javaformat/releases) - [Commits](https://github.com/spring-io/spring-javaformat/compare/v0.0.42...v0.0.43) Updates `io.spring.javaformat:spring-javaformat-gradle-plugin` from 0.0.42 to 0.0.43 - [Release notes](https://github.com/spring-io/spring-javaformat/releases) - [Commits](https://github.com/spring-io/spring-javaformat/compare/v0.0.42...v0.0.43) --- updated-dependencies: - dependency-name: io.spring.javaformat:spring-javaformat-checkstyle dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: io.spring.javaformat:spring-javaformat-gradle-plugin dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] * Fix checkstyle violations --------- Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Tommy Ludwig <8924140+shakuzen@users.noreply.github.com> --- .../test/java/io/micrometer/docs/netty/NettyMetricsTests.java | 2 +- gradle/libs.versions.toml | 2 +- .../java/io/micrometer/core/tck/DefaultLongTaskTimerTest.java | 4 ++++ 3 files changed, 6 insertions(+), 2 deletions(-) diff --git a/docs/src/test/java/io/micrometer/docs/netty/NettyMetricsTests.java b/docs/src/test/java/io/micrometer/docs/netty/NettyMetricsTests.java index ffab371ca5..31a541e5a3 100644 --- a/docs/src/test/java/io/micrometer/docs/netty/NettyMetricsTests.java +++ b/docs/src/test/java/io/micrometer/docs/netty/NettyMetricsTests.java @@ -45,7 +45,7 @@ /** * Sources for netty/index.adoc */ -public class NettyMetricsTests { +class NettyMetricsTests { private SimpleMeterRegistry registry = new SimpleMeterRegistry(SimpleConfig.DEFAULT, new MockClock()); diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 56478eee77..82c2de64ca 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -71,7 +71,7 @@ signalfx = "1.0.44" slf4j = "1.7.36" spectator-atlas = "1.7.19" spring = "5.3.37" -spring-javaformat = "0.0.42" +spring-javaformat = "0.0.43" testcontainers = "1.19.8" tomcat = "8.5.100" wavefront = "3.4.3" diff --git a/micrometer-test/src/main/java/io/micrometer/core/tck/DefaultLongTaskTimerTest.java b/micrometer-test/src/main/java/io/micrometer/core/tck/DefaultLongTaskTimerTest.java index d92e6830ba..15c536320a 100644 --- a/micrometer-test/src/main/java/io/micrometer/core/tck/DefaultLongTaskTimerTest.java +++ b/micrometer-test/src/main/java/io/micrometer/core/tck/DefaultLongTaskTimerTest.java @@ -35,8 +35,12 @@ import static io.micrometer.core.instrument.MockClock.clock; import static org.assertj.core.api.Assertions.assertThat; +// Test class is public and in main code... by mistake? +//CHECKSTYLE:OFF public class DefaultLongTaskTimerTest { + // CHECKSTYLE:ON + @Test @DisplayName("supports sending histograms of active task duration") void histogram() { From 938aa3867ba91dfb4cac384f5399ea94d33a0e41 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 2 Sep 2024 18:55:39 +0900 Subject: [PATCH 61/74] Bump org.apache.maven:maven-resolver-provider from 3.9.8 to 3.9.9 (#5451) Bumps [org.apache.maven:maven-resolver-provider](https://github.com/apache/maven) from 3.9.8 to 3.9.9. - [Release notes](https://github.com/apache/maven/releases) - [Commits](https://github.com/apache/maven/compare/maven-3.9.8...maven-3.9.9) --- updated-dependencies: - dependency-name: org.apache.maven:maven-resolver-provider dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- gradle/libs.versions.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 82c2de64ca..51b83e422d 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -169,7 +169,7 @@ logback14 = {module = "ch.qos.logback:logback-classic", version = "1.4.14" } log4j = { module = "org.apache.logging.log4j:log4j-core", version.ref = "log4j" } mavenResolverConnectorBasic = { module = "org.apache.maven.resolver:maven-resolver-connector-basic", version.ref = "maven-resolver" } mavenResolverTransportHttp = { module = "org.apache.maven.resolver:maven-resolver-transport-http", version.ref = "maven-resolver" } -mavenResolverProvider = { module = "org.apache.maven:maven-resolver-provider", version = "3.9.8" } +mavenResolverProvider = { module = "org.apache.maven:maven-resolver-provider", version = "3.9.9" } mockitoCore4 = { module = "org.mockito:mockito-core", version.ref = "mockito4" } mockitoCore5 = { module = "org.mockito:mockito-core", version.ref = "mockito5" } mongoSync = { module = "org.mongodb:mongodb-driver-sync", version.ref = "mongo" } From 2fe16d777f8cfad1e1b5980d2c473b3f999af194 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 2 Sep 2024 18:59:19 +0900 Subject: [PATCH 62/74] Bump com.signalfx.public:signalfx-java from 1.0.44 to 1.0.45 (#5454) Bumps [com.signalfx.public:signalfx-java](https://github.com/signalfx/signalfx-java) from 1.0.44 to 1.0.45. - [Release notes](https://github.com/signalfx/signalfx-java/releases) - [Commits](https://github.com/signalfx/signalfx-java/compare/v1.0.44...v1.0.45) --- updated-dependencies: - dependency-name: com.signalfx.public:signalfx-java dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- gradle/libs.versions.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 51b83e422d..904bf6be26 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -67,7 +67,7 @@ postgre = "42.6.2" prometheus = "0.16.0" reactor = "2022.0.22" rest-assured = "5.3.2" -signalfx = "1.0.44" +signalfx = "1.0.45" slf4j = "1.7.36" spectator-atlas = "1.7.19" spring = "5.3.37" From 4ac97499238161467992c70c399fbbca7813913f Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 2 Sep 2024 18:59:50 +0900 Subject: [PATCH 63/74] Bump dropwizard-metrics from 4.2.26 to 4.2.27 (#5450) Bumps `dropwizard-metrics` from 4.2.26 to 4.2.27. Updates `io.dropwizard.metrics:metrics-core` from 4.2.26 to 4.2.27 - [Release notes](https://github.com/dropwizard/metrics/releases) - [Commits](https://github.com/dropwizard/metrics/compare/v4.2.26...v4.2.27) Updates `io.dropwizard.metrics:metrics-graphite` from 4.2.26 to 4.2.27 - [Release notes](https://github.com/dropwizard/metrics/releases) - [Commits](https://github.com/dropwizard/metrics/compare/v4.2.26...v4.2.27) Updates `io.dropwizard.metrics:metrics-jmx` from 4.2.26 to 4.2.27 - [Release notes](https://github.com/dropwizard/metrics/releases) - [Commits](https://github.com/dropwizard/metrics/compare/v4.2.26...v4.2.27) --- updated-dependencies: - dependency-name: io.dropwizard.metrics:metrics-core dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: io.dropwizard.metrics:metrics-graphite dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: io.dropwizard.metrics:metrics-jmx dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- gradle/libs.versions.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 904bf6be26..0b25b5b346 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -12,7 +12,7 @@ caffeine = "2.9.3" cloudwatch2 = "2.21.46" colt = "1.2.0" dagger = "2.48.1" -dropwizard-metrics = "4.2.26" +dropwizard-metrics = "4.2.27" dropwizard-metrics5 = "5.0.0" dynatrace-utils = "2.2.1" ehcache2 = "2.10.9.2" From 59ba980c9a0c9c83aecb24aabdc0791bda070da4 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 2 Sep 2024 19:00:25 +0900 Subject: [PATCH 64/74] Bump com.amazonaws:aws-java-sdk-cloudwatch from 1.12.767 to 1.12.770 (#5457) Bumps [com.amazonaws:aws-java-sdk-cloudwatch](https://github.com/aws/aws-sdk-java) from 1.12.767 to 1.12.770. - [Changelog](https://github.com/aws/aws-sdk-java/blob/master/CHANGELOG.md) - [Commits](https://github.com/aws/aws-sdk-java/compare/1.12.767...1.12.770) --- updated-dependencies: - dependency-name: com.amazonaws:aws-java-sdk-cloudwatch dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- gradle/libs.versions.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 0b25b5b346..8125863dfb 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -7,7 +7,7 @@ aspectjweaver = "1.9.22.1" assertj = "3.24.2" awaitility = "4.2.2" # legacy SDK -aws-cloudwatch = "1.12.767" +aws-cloudwatch = "1.12.770" caffeine = "2.9.3" cloudwatch2 = "2.21.46" colt = "1.2.0" From 802f8661426bb95fb34b8cff110a55f666ae2023 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 2 Sep 2024 19:29:06 +0900 Subject: [PATCH 65/74] Bump org.postgresql:postgresql from 42.7.3 to 42.7.4 (#5418) Bumps [org.postgresql:postgresql](https://github.com/pgjdbc/pgjdbc) from 42.7.3 to 42.7.4. - [Release notes](https://github.com/pgjdbc/pgjdbc/releases) - [Changelog](https://github.com/pgjdbc/pgjdbc/blob/master/CHANGELOG.md) - [Commits](https://github.com/pgjdbc/pgjdbc/compare/REL42.7.3...REL42.7.4) --- updated-dependencies: - dependency-name: org.postgresql:postgresql dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- gradle/libs.versions.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index f622316319..595ee4c299 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -67,7 +67,7 @@ netty = "4.1.112.Final" newrelic-api = "5.14.0" # Kotlin 1.7 sample will fail from OkHttp 4.12.0 due to okio dependency being a Kotlin 1.9 module okhttp = "4.11.0" -postgre = "42.7.3" +postgre = "42.7.4" prometheus = "1.2.1" prometheusSimpleClient = "0.16.0" reactor = "2022.0.22" From dddd53f16210cea2317df2b86b546d4ffa885dd7 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 3 Sep 2024 12:25:51 +0900 Subject: [PATCH 66/74] Bump dropwizard-metrics from 4.2.26 to 4.2.27 (#5411) Bumps `dropwizard-metrics` from 4.2.26 to 4.2.27. Updates `io.dropwizard.metrics:metrics-core` from 4.2.26 to 4.2.27 - [Release notes](https://github.com/dropwizard/metrics/releases) - [Commits](https://github.com/dropwizard/metrics/compare/v4.2.26...v4.2.27) Updates `io.dropwizard.metrics:metrics-graphite` from 4.2.26 to 4.2.27 - [Release notes](https://github.com/dropwizard/metrics/releases) - [Commits](https://github.com/dropwizard/metrics/compare/v4.2.26...v4.2.27) Updates `io.dropwizard.metrics:metrics-jmx` from 4.2.26 to 4.2.27 - [Release notes](https://github.com/dropwizard/metrics/releases) - [Commits](https://github.com/dropwizard/metrics/compare/v4.2.26...v4.2.27) --- updated-dependencies: - dependency-name: io.dropwizard.metrics:metrics-core dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: io.dropwizard.metrics:metrics-graphite dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: io.dropwizard.metrics:metrics-jmx dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- gradle/libs.versions.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index e05c19517f..8f68132845 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -11,7 +11,7 @@ caffeine = "2.9.3" cloudwatch2 = "2.18.41" colt = "1.2.0" dagger = "2.11" -dropwizard-metrics = "4.2.26" +dropwizard-metrics = "4.2.27" dropwizard-metrics5 = "5.0.0" dynatrace-utils = "1.5.0" ehcache2 = "2.10.9.2" From e5a5d13ad93af82b27f8340ad773b65a1df90d33 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 3 Sep 2024 12:26:25 +0900 Subject: [PATCH 67/74] Bump spring from 5.3.37 to 5.3.39 (#5417) Bumps `spring` from 5.3.37 to 5.3.39. Updates `org.springframework:spring-context` from 5.3.37 to 5.3.39 - [Release notes](https://github.com/spring-projects/spring-framework/releases) - [Commits](https://github.com/spring-projects/spring-framework/compare/v5.3.37...v5.3.39) Updates `org.springframework:spring-core` from 5.3.37 to 5.3.39 - [Release notes](https://github.com/spring-projects/spring-framework/releases) - [Commits](https://github.com/spring-projects/spring-framework/compare/v5.3.37...v5.3.39) --- updated-dependencies: - dependency-name: org.springframework:spring-context dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: org.springframework:spring-core dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- gradle/libs.versions.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 8f68132845..1388ae1781 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -66,7 +66,7 @@ rest-assured = "5.3.2" signalfx = "1.0.44" slf4j = "1.7.36" spectator-atlas = "1.3.10" -spring = "5.3.37" +spring = "5.3.39" spring-javaformat = "0.0.42" testcontainers = "1.19.8" tomcat = "8.5.100" From a6ae355b81edc38f929429c96578812b13058891 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 3 Sep 2024 12:26:44 +0900 Subject: [PATCH 68/74] Bump io.projectreactor:reactor-bom from 2020.0.46 to 2020.0.47 (#5416) Bumps [io.projectreactor:reactor-bom](https://github.com/reactor/reactor) from 2020.0.46 to 2020.0.47. - [Release notes](https://github.com/reactor/reactor/releases) - [Commits](https://github.com/reactor/reactor/compare/2020.0.46...2020.0.47) --- updated-dependencies: - dependency-name: io.projectreactor:reactor-bom dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- gradle/libs.versions.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 1388ae1781..0b23f76d86 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -61,7 +61,7 @@ newrelic-api = "5.14.0" okhttp = "4.11.0" postgre = "42.3.10" prometheus = "0.15.0" -reactor = "2020.0.46" +reactor = "2020.0.47" rest-assured = "5.3.2" signalfx = "1.0.44" slf4j = "1.7.36" From 43066e02eb5094aca80539376d9d2770d19f7e44 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 3 Sep 2024 12:27:05 +0900 Subject: [PATCH 69/74] Bump io.spring.develocity.conventions from 0.0.19 to 0.0.20 (#5413) Bumps [io.spring.develocity.conventions](https://github.com/spring-io/develocity-conventions) from 0.0.19 to 0.0.20. - [Release notes](https://github.com/spring-io/develocity-conventions/releases) - [Commits](https://github.com/spring-io/develocity-conventions/compare/v0.0.19...v0.0.20) --- updated-dependencies: - dependency-name: io.spring.develocity.conventions dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- settings.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/settings.gradle b/settings.gradle index 3db53fe3ba..a5e169f7ca 100644 --- a/settings.gradle +++ b/settings.gradle @@ -6,7 +6,7 @@ pluginManagement { plugins { id 'com.gradle.develocity' version '3.17.6' - id 'io.spring.develocity.conventions' version '0.0.19' + id 'io.spring.develocity.conventions' version '0.0.20' id 'org.gradle.toolchains.foojay-resolver-convention' version '0.8.0' } From 2a69ebdd5eb892e2be2502a6da5124be4e920149 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 3 Sep 2024 12:27:21 +0900 Subject: [PATCH 70/74] Bump com.signalfx.public:signalfx-java from 1.0.44 to 1.0.45 (#5412) Bumps [com.signalfx.public:signalfx-java](https://github.com/signalfx/signalfx-java) from 1.0.44 to 1.0.45. - [Release notes](https://github.com/signalfx/signalfx-java/releases) - [Commits](https://github.com/signalfx/signalfx-java/compare/v1.0.44...v1.0.45) --- updated-dependencies: - dependency-name: com.signalfx.public:signalfx-java dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- gradle/libs.versions.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 0b23f76d86..0772423028 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -63,7 +63,7 @@ postgre = "42.3.10" prometheus = "0.15.0" reactor = "2020.0.47" rest-assured = "5.3.2" -signalfx = "1.0.44" +signalfx = "1.0.45" slf4j = "1.7.36" spectator-atlas = "1.3.10" spring = "5.3.39" From bbb2b7d4d8563bd59dbaf40d41d85b33b5e4ae7f Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 3 Sep 2024 12:27:40 +0900 Subject: [PATCH 71/74] Bump com.amazonaws:aws-java-sdk-cloudwatch from 1.12.767 to 1.12.770 (#5415) Bumps [com.amazonaws:aws-java-sdk-cloudwatch](https://github.com/aws/aws-sdk-java) from 1.12.767 to 1.12.770. - [Changelog](https://github.com/aws/aws-sdk-java/blob/master/CHANGELOG.md) - [Commits](https://github.com/aws/aws-sdk-java/compare/1.12.767...1.12.770) --- updated-dependencies: - dependency-name: com.amazonaws:aws-java-sdk-cloudwatch dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- gradle/libs.versions.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 0772423028..ec104b1003 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -6,7 +6,7 @@ aspectjweaver = "1.8.14" assertj = "3.24.2" awaitility = "4.2.2" # legacy SDK -aws-cloudwatch = "1.12.767" +aws-cloudwatch = "1.12.770" caffeine = "2.9.3" cloudwatch2 = "2.18.41" colt = "1.2.0" From 936c7168eb377b4aadc81ca6742ee5baa9725471 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 2 Sep 2024 18:55:04 +0900 Subject: [PATCH 72/74] Bump spring-javaformat from 0.0.42 to 0.0.43 (#5414) * Bump spring-javaformat from 0.0.42 to 0.0.43 Bumps `spring-javaformat` from 0.0.42 to 0.0.43. Updates `io.spring.javaformat:spring-javaformat-checkstyle` from 0.0.42 to 0.0.43 - [Release notes](https://github.com/spring-io/spring-javaformat/releases) - [Commits](https://github.com/spring-io/spring-javaformat/compare/v0.0.42...v0.0.43) Updates `io.spring.javaformat:spring-javaformat-gradle-plugin` from 0.0.42 to 0.0.43 - [Release notes](https://github.com/spring-io/spring-javaformat/releases) - [Commits](https://github.com/spring-io/spring-javaformat/compare/v0.0.42...v0.0.43) --- updated-dependencies: - dependency-name: io.spring.javaformat:spring-javaformat-checkstyle dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: io.spring.javaformat:spring-javaformat-gradle-plugin dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] * Fix checkstyle violations --------- Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Tommy Ludwig <8924140+shakuzen@users.noreply.github.com> (cherry picked from commit e2a77964ff727f0bc7c7f88406a1e37d8a5cd9d0) Closes gh-5414 --- gradle/libs.versions.toml | 2 +- .../core/instrument/binder/jetty/JettyClientMetricsTest.java | 2 +- .../java/io/micrometer/core/tck/DefaultLongTaskTimerTest.java | 4 ++++ 3 files changed, 6 insertions(+), 2 deletions(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index ec104b1003..f6d5275776 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -67,7 +67,7 @@ signalfx = "1.0.45" slf4j = "1.7.36" spectator-atlas = "1.3.10" spring = "5.3.39" -spring-javaformat = "0.0.42" +spring-javaformat = "0.0.43" testcontainers = "1.19.8" tomcat = "8.5.100" wavefront = "3.0.4" diff --git a/micrometer-core/src/test/java/io/micrometer/core/instrument/binder/jetty/JettyClientMetricsTest.java b/micrometer-core/src/test/java/io/micrometer/core/instrument/binder/jetty/JettyClientMetricsTest.java index 4d0404cd21..51db51e50d 100644 --- a/micrometer-core/src/test/java/io/micrometer/core/instrument/binder/jetty/JettyClientMetricsTest.java +++ b/micrometer-core/src/test/java/io/micrometer/core/instrument/binder/jetty/JettyClientMetricsTest.java @@ -38,7 +38,7 @@ import static java.util.concurrent.TimeUnit.SECONDS; import static org.assertj.core.api.Assertions.assertThat; -public class JettyClientMetricsTest { +class JettyClientMetricsTest { private SimpleMeterRegistry registry = new SimpleMeterRegistry(SimpleConfig.DEFAULT, new MockClock()); diff --git a/micrometer-test/src/main/java/io/micrometer/core/tck/DefaultLongTaskTimerTest.java b/micrometer-test/src/main/java/io/micrometer/core/tck/DefaultLongTaskTimerTest.java index d92e6830ba..15c536320a 100644 --- a/micrometer-test/src/main/java/io/micrometer/core/tck/DefaultLongTaskTimerTest.java +++ b/micrometer-test/src/main/java/io/micrometer/core/tck/DefaultLongTaskTimerTest.java @@ -35,8 +35,12 @@ import static io.micrometer.core.instrument.MockClock.clock; import static org.assertj.core.api.Assertions.assertThat; +// Test class is public and in main code... by mistake? +//CHECKSTYLE:OFF public class DefaultLongTaskTimerTest { + // CHECKSTYLE:ON + @Test @DisplayName("supports sending histograms of active task duration") void histogram() { From 3a960914c15398f80d7274ee43481f7c3822e23f Mon Sep 17 00:00:00 2001 From: Jonatan Ivanov Date: Thu, 5 Sep 2024 15:26:28 -0700 Subject: [PATCH 73/74] Upgrade Gradle to 8.10 Closes gh-5465 --- build.gradle | 2 +- gradle/wrapper/gradle-wrapper.jar | Bin 43462 -> 43583 bytes gradle/wrapper/gradle-wrapper.properties | 2 +- gradlew | 7 +++++-- gradlew.bat | 2 ++ 5 files changed, 9 insertions(+), 4 deletions(-) diff --git a/build.gradle b/build.gradle index 233ec20ac7..a01f0cf53a 100644 --- a/build.gradle +++ b/build.gradle @@ -426,7 +426,7 @@ nexusPublishing { } wrapper { - gradleVersion = '8.6' + gradleVersion = '8.10' } defaultTasks 'build' diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar index d64cd4917707c1f8861d8cb53dd15194d4248596..a4b76b9530d66f5e68d973ea569d8e19de379189 100644 GIT binary patch delta 34592 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 delta 34555 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 diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index a80b22ce5c..9355b41557 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-8.6-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-8.10-bin.zip networkTimeout=10000 validateDistributionUrl=true zipStoreBase=GRADLE_USER_HOME diff --git a/gradlew b/gradlew index 1aa94a4269..f5feea6d6b 100755 --- a/gradlew +++ b/gradlew @@ -15,6 +15,8 @@ # See the License for the specific language governing permissions and # limitations under the License. # +# SPDX-License-Identifier: Apache-2.0 +# ############################################################################## # @@ -55,7 +57,7 @@ # Darwin, MinGW, and NonStop. # # (3) This script is generated from the Groovy template -# https://github.com/gradle/gradle/blob/HEAD/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt +# https://github.com/gradle/gradle/blob/HEAD/platforms/jvm/plugins-application/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt # within the Gradle project. # # You can find Gradle at https://github.com/gradle/gradle/. @@ -84,7 +86,8 @@ done # shellcheck disable=SC2034 APP_BASE_NAME=${0##*/} # Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036) -APP_HOME=$( cd "${APP_HOME:-./}" > /dev/null && pwd -P ) || exit +APP_HOME=$( cd -P "${APP_HOME:-./}" > /dev/null && printf '%s +' "$PWD" ) || exit # Use the maximum available, or set MAX_FD != -1 to use that value. MAX_FD=maximum diff --git a/gradlew.bat b/gradlew.bat index 7101f8e467..9b42019c79 100644 --- a/gradlew.bat +++ b/gradlew.bat @@ -13,6 +13,8 @@ @rem See the License for the specific language governing permissions and @rem limitations under the License. @rem +@rem SPDX-License-Identifier: Apache-2.0 +@rem @if "%DEBUG%"=="" @echo off @rem ########################################################################## From f0aa18d07fa529032593d8bacc346273e507be2a Mon Sep 17 00:00:00 2001 From: Jonatan Ivanov Date: Thu, 5 Sep 2024 15:26:28 -0700 Subject: [PATCH 74/74] Upgrade Gradle to 8.10 Closes gh-5465 --- build.gradle | 2 +- gradle/wrapper/gradle-wrapper.jar | Bin 43462 -> 43583 bytes gradle/wrapper/gradle-wrapper.properties | 2 +- gradlew | 7 +++++-- gradlew.bat | 2 ++ 5 files changed, 9 insertions(+), 4 deletions(-) diff --git a/build.gradle b/build.gradle index 2018927643..fcfeb4dbcd 100644 --- a/build.gradle +++ b/build.gradle @@ -363,7 +363,7 @@ nexusPublishing { } wrapper { - gradleVersion = '8.6' + gradleVersion = '8.10' } defaultTasks 'build' diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar index d64cd4917707c1f8861d8cb53dd15194d4248596..a4b76b9530d66f5e68d973ea569d8e19de379189 100644 GIT binary patch delta 34592 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 delta 34555 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 diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index a80b22ce5c..9355b41557 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-8.6-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-8.10-bin.zip networkTimeout=10000 validateDistributionUrl=true zipStoreBase=GRADLE_USER_HOME diff --git a/gradlew b/gradlew index 1aa94a4269..f5feea6d6b 100755 --- a/gradlew +++ b/gradlew @@ -15,6 +15,8 @@ # See the License for the specific language governing permissions and # limitations under the License. # +# SPDX-License-Identifier: Apache-2.0 +# ############################################################################## # @@ -55,7 +57,7 @@ # Darwin, MinGW, and NonStop. # # (3) This script is generated from the Groovy template -# https://github.com/gradle/gradle/blob/HEAD/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt +# https://github.com/gradle/gradle/blob/HEAD/platforms/jvm/plugins-application/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt # within the Gradle project. # # You can find Gradle at https://github.com/gradle/gradle/. @@ -84,7 +86,8 @@ done # shellcheck disable=SC2034 APP_BASE_NAME=${0##*/} # Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036) -APP_HOME=$( cd "${APP_HOME:-./}" > /dev/null && pwd -P ) || exit +APP_HOME=$( cd -P "${APP_HOME:-./}" > /dev/null && printf '%s +' "$PWD" ) || exit # Use the maximum available, or set MAX_FD != -1 to use that value. MAX_FD=maximum diff --git a/gradlew.bat b/gradlew.bat index 7101f8e467..9b42019c79 100644 --- a/gradlew.bat +++ b/gradlew.bat @@ -13,6 +13,8 @@ @rem See the License for the specific language governing permissions and @rem limitations under the License. @rem +@rem SPDX-License-Identifier: Apache-2.0 +@rem @if "%DEBUG%"=="" @echo off @rem ##########################################################################