From 6dfc8f9567d333afe9100987eba480d357dca595 Mon Sep 17 00:00:00 2001 From: Tommy Ludwig <8924140+shakuzen@users.noreply.github.com> Date: Wed, 9 Apr 2025 16:27:32 +0900 Subject: [PATCH 1/2] Support Per-meter configuration for OtlpMetricsProperties Add configuration properties support for the new `OtlpConfig` methods `histogramFlavorPerMeter` and `maxBucketsPerMeter`. The existing `histogramFlavor` and `maxBucketCount` configuration properties are used as defaults when there isn't a specific per-meter configuration set. See gh-45203 --- .../export/otlp/OtlpMetricsProperties.java | 34 +++++++++++++++++-- .../OtlpMetricsPropertiesConfigAdapter.java | 10 ++++++ ...lpMetricsPropertiesConfigAdapterTests.java | 24 +++++++++++++ .../otlp/OtlpMetricsPropertiesTests.java | 2 ++ .../spring-boot-dependencies/build.gradle | 2 +- 5 files changed, 68 insertions(+), 4 deletions(-) diff --git a/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/metrics/export/otlp/OtlpMetricsProperties.java b/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/metrics/export/otlp/OtlpMetricsProperties.java index bcda23c88c..139ff2fd35 100644 --- a/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/metrics/export/otlp/OtlpMetricsProperties.java +++ b/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/metrics/export/otlp/OtlpMetricsProperties.java @@ -16,6 +16,7 @@ package org.springframework.boot.actuate.autoconfigure.metrics.export.otlp; +import java.util.LinkedHashMap; import java.util.Map; import java.util.concurrent.TimeUnit; @@ -53,21 +54,32 @@ public class OtlpMetricsProperties extends StepRegistryProperties { private Map headers; /** - * Histogram type to be preferred when histogram publishing is enabled. + * Default histogram type when histogram publishing is enabled. */ private HistogramFlavor histogramFlavor = HistogramFlavor.EXPLICIT_BUCKET_HISTOGRAM; + /** + * Per meter histogram type to be preferred when histogram publishing is enabled. + */ + private Map histogramFlavorPerMeter = new LinkedHashMap<>(); + /** * Max scale to use for exponential histograms, if configured. */ private int maxScale = 20; /** - * Maximum number of buckets to be used for exponential histograms, if configured. - * This has no effect on explicit bucket histograms. + * Default maximum number of buckets to be used for exponential histograms, if + * configured. This has no effect on explicit bucket histograms. */ private int maxBucketCount = 160; + /** + * Per meter number of max buckets used for exponential histograms, if configured. + * This has no effect on explicit bucket histograms. + */ + private Map maxBucketsPerMeter = new LinkedHashMap<>(); + /** * Time unit for exported metrics. */ @@ -105,6 +117,14 @@ public class OtlpMetricsProperties extends StepRegistryProperties { this.histogramFlavor = histogramFlavor; } + public Map getHistogramFlavorPerMeter() { + return this.histogramFlavorPerMeter; + } + + public void setHistogramFlavorPerMeter(Map histogramFlavorPerMeter) { + this.histogramFlavorPerMeter = histogramFlavorPerMeter; + } + public int getMaxScale() { return this.maxScale; } @@ -121,6 +141,14 @@ public class OtlpMetricsProperties extends StepRegistryProperties { this.maxBucketCount = maxBucketCount; } + public Map getMaxBucketsPerMeter() { + return this.maxBucketsPerMeter; + } + + public void setMaxBucketsPerMeter(Map maxBucketsPerMeter) { + this.maxBucketsPerMeter = maxBucketsPerMeter; + } + public TimeUnit getBaseTimeUnit() { return this.baseTimeUnit; } diff --git a/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/metrics/export/otlp/OtlpMetricsPropertiesConfigAdapter.java b/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/metrics/export/otlp/OtlpMetricsPropertiesConfigAdapter.java index 8ad4085583..abb1178f6b 100644 --- a/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/metrics/export/otlp/OtlpMetricsPropertiesConfigAdapter.java +++ b/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/metrics/export/otlp/OtlpMetricsPropertiesConfigAdapter.java @@ -88,6 +88,16 @@ class OtlpMetricsPropertiesConfigAdapter extends StepRegistryPropertiesConfigAda return get(OtlpMetricsProperties::getHistogramFlavor, OtlpConfig.super::histogramFlavor); } + @Override + public Map histogramFlavorPerMeter() { + return get(OtlpMetricsProperties::getHistogramFlavorPerMeter, OtlpConfig.super::histogramFlavorPerMeter); + } + + @Override + public Map maxBucketsPerMeter() { + return get(OtlpMetricsProperties::getMaxBucketsPerMeter, OtlpConfig.super::maxBucketsPerMeter); + } + @Override public int maxScale() { return get(OtlpMetricsProperties::getMaxScale, OtlpConfig.super::maxScale); diff --git a/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/metrics/export/otlp/OtlpMetricsPropertiesConfigAdapterTests.java b/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/metrics/export/otlp/OtlpMetricsPropertiesConfigAdapterTests.java index cd067216b1..723202318a 100644 --- a/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/metrics/export/otlp/OtlpMetricsPropertiesConfigAdapterTests.java +++ b/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/metrics/export/otlp/OtlpMetricsPropertiesConfigAdapterTests.java @@ -110,6 +110,19 @@ class OtlpMetricsPropertiesConfigAdapterTests { assertThat(createAdapter().histogramFlavor()).isSameAs(HistogramFlavor.BASE2_EXPONENTIAL_BUCKET_HISTOGRAM); } + @Test + void whenPropertiesHistogramFlavorPerMeterIsNotSetAdapterHistogramFlavorReturnsEmptyMap() { + assertThat(createAdapter().histogramFlavorPerMeter()).isEmpty(); + } + + @Test + void whenPropertiesHistogramFlavorPerMeterIsSetAdapterHistogramFlavorPerMeterReturnsIt() { + this.properties + .setHistogramFlavorPerMeter(Map.of("my.histograms", HistogramFlavor.BASE2_EXPONENTIAL_BUCKET_HISTOGRAM)); + assertThat(createAdapter().histogramFlavorPerMeter()).containsEntry("my.histograms", + HistogramFlavor.BASE2_EXPONENTIAL_BUCKET_HISTOGRAM); + } + @Test void whenPropertiesMaxScaleIsNotSetAdapterMaxScaleReturns20() { assertThat(createAdapter().maxScale()).isEqualTo(20); @@ -132,6 +145,17 @@ class OtlpMetricsPropertiesConfigAdapterTests { assertThat(createAdapter().maxBucketCount()).isEqualTo(6); } + @Test + void whenPropertiesMaxBucketsPerMeterIsNotSetAdapterMaxBucketsPerMeterReturnsEmptyMap() { + assertThat(createAdapter().maxBucketsPerMeter()).isEmpty(); + } + + @Test + void whenPropertiesMaxBucketsPerMeterIsSetAdapterMaxBucketsPerMeterReturnsIt() { + this.properties.setMaxBucketsPerMeter(Map.of("my.histograms", 111)); + assertThat(createAdapter().maxBucketsPerMeter()).containsEntry("my.histograms", 111); + } + @Test void whenPropertiesBaseTimeUnitIsNotSetAdapterBaseTimeUnitReturnsMillis() { assertThat(createAdapter().baseTimeUnit()).isSameAs(TimeUnit.MILLISECONDS); diff --git a/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/metrics/export/otlp/OtlpMetricsPropertiesTests.java b/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/metrics/export/otlp/OtlpMetricsPropertiesTests.java index d4938491e7..843c5fd3aa 100644 --- a/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/metrics/export/otlp/OtlpMetricsPropertiesTests.java +++ b/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/metrics/export/otlp/OtlpMetricsPropertiesTests.java @@ -37,8 +37,10 @@ class OtlpMetricsPropertiesTests extends StepRegistryPropertiesTests { assertStepRegistryDefaultValues(properties, config); assertThat(properties.getAggregationTemporality()).isSameAs(config.aggregationTemporality()); assertThat(properties.getHistogramFlavor()).isSameAs(config.histogramFlavor()); + assertThat(properties.getHistogramFlavorPerMeter()).isEqualTo(config.histogramFlavorPerMeter()); assertThat(properties.getMaxScale()).isEqualTo(config.maxScale()); assertThat(properties.getMaxBucketCount()).isEqualTo(config.maxBucketCount()); + assertThat(properties.getMaxBucketsPerMeter()).isEqualTo(config.maxBucketsPerMeter()); assertThat(properties.getBaseTimeUnit()).isSameAs(config.baseTimeUnit()); } diff --git a/spring-boot-project/spring-boot-dependencies/build.gradle b/spring-boot-project/spring-boot-dependencies/build.gradle index 9bf051754d..3da4e7e12b 100644 --- a/spring-boot-project/spring-boot-dependencies/build.gradle +++ b/spring-boot-project/spring-boot-dependencies/build.gradle @@ -1548,7 +1548,7 @@ bom { releaseNotes("https://github.com/apache/maven-war-plugin/releases/tag/maven-war-plugin-{version}") } } - library("Micrometer", "1.15.0-SNAPSHOT") { + library("Micrometer", "1.15.0-RC1") { considerSnapshots() group("io.micrometer") { modules = [ From b60cfe9360854cdd7b781bf4ae8aea66944e556d Mon Sep 17 00:00:00 2001 From: Phillip Webb Date: Tue, 15 Apr 2025 11:35:55 -0700 Subject: [PATCH 2/2] Polish 'Support Per-meter configuration for OtlpMetricsProperties' See gh-45203 --- .../export/otlp/OtlpMetricsProperties.java | 70 ++++++++++++------- .../OtlpMetricsPropertiesConfigAdapter.java | 24 ++++++- ...lpMetricsPropertiesConfigAdapterTests.java | 10 ++- .../otlp/OtlpMetricsPropertiesTests.java | 3 +- .../spring-boot-dependencies/build.gradle | 2 +- 5 files changed, 74 insertions(+), 35 deletions(-) diff --git a/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/metrics/export/otlp/OtlpMetricsProperties.java b/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/metrics/export/otlp/OtlpMetricsProperties.java index 139ff2fd35..983eff38a6 100644 --- a/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/metrics/export/otlp/OtlpMetricsProperties.java +++ b/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/metrics/export/otlp/OtlpMetricsProperties.java @@ -58,11 +58,6 @@ public class OtlpMetricsProperties extends StepRegistryProperties { */ private HistogramFlavor histogramFlavor = HistogramFlavor.EXPLICIT_BUCKET_HISTOGRAM; - /** - * Per meter histogram type to be preferred when histogram publishing is enabled. - */ - private Map histogramFlavorPerMeter = new LinkedHashMap<>(); - /** * Max scale to use for exponential histograms, if configured. */ @@ -74,17 +69,16 @@ public class OtlpMetricsProperties extends StepRegistryProperties { */ private int maxBucketCount = 160; - /** - * Per meter number of max buckets used for exponential histograms, if configured. - * This has no effect on explicit bucket histograms. - */ - private Map maxBucketsPerMeter = new LinkedHashMap<>(); - /** * Time unit for exported metrics. */ private TimeUnit baseTimeUnit = TimeUnit.MILLISECONDS; + /** + * Per-meter properties that can be used to override defaults. + */ + private Map meter = new LinkedHashMap<>(); + public String getUrl() { return this.url; } @@ -117,14 +111,6 @@ public class OtlpMetricsProperties extends StepRegistryProperties { this.histogramFlavor = histogramFlavor; } - public Map getHistogramFlavorPerMeter() { - return this.histogramFlavorPerMeter; - } - - public void setHistogramFlavorPerMeter(Map histogramFlavorPerMeter) { - this.histogramFlavorPerMeter = histogramFlavorPerMeter; - } - public int getMaxScale() { return this.maxScale; } @@ -141,14 +127,6 @@ public class OtlpMetricsProperties extends StepRegistryProperties { this.maxBucketCount = maxBucketCount; } - public Map getMaxBucketsPerMeter() { - return this.maxBucketsPerMeter; - } - - public void setMaxBucketsPerMeter(Map maxBucketsPerMeter) { - this.maxBucketsPerMeter = maxBucketsPerMeter; - } - public TimeUnit getBaseTimeUnit() { return this.baseTimeUnit; } @@ -157,4 +135,42 @@ public class OtlpMetricsProperties extends StepRegistryProperties { this.baseTimeUnit = baseTimeUnit; } + public Map getMeter() { + return this.meter; + } + + /** + * Per-meter settings. + */ + public static class Meter { + + /** + * Maximum number of buckets to be used for exponential histograms, if configured. + * This has no effect on explicit bucket histograms. + */ + private Integer maxBucketCount; + + /** + * Histogram type when histogram publishing is enabled. + */ + private HistogramFlavor histogramFlavor; + + public Integer getMaxBucketCount() { + return this.maxBucketCount; + } + + public void setMaxBucketCount(Integer maxBucketCount) { + this.maxBucketCount = maxBucketCount; + } + + public HistogramFlavor getHistogramFlavor() { + return this.histogramFlavor; + } + + public void setHistogramFlavor(HistogramFlavor histogramFlavor) { + this.histogramFlavor = histogramFlavor; + } + + } + } diff --git a/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/metrics/export/otlp/OtlpMetricsPropertiesConfigAdapter.java b/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/metrics/export/otlp/OtlpMetricsPropertiesConfigAdapter.java index abb1178f6b..d3f9c4838b 100644 --- a/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/metrics/export/otlp/OtlpMetricsPropertiesConfigAdapter.java +++ b/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/metrics/export/otlp/OtlpMetricsPropertiesConfigAdapter.java @@ -20,15 +20,18 @@ import java.util.Collections; import java.util.LinkedHashMap; import java.util.Map; import java.util.concurrent.TimeUnit; +import java.util.function.Function; import io.micrometer.registry.otlp.AggregationTemporality; import io.micrometer.registry.otlp.HistogramFlavor; import io.micrometer.registry.otlp.OtlpConfig; +import org.springframework.boot.actuate.autoconfigure.metrics.export.otlp.OtlpMetricsProperties.Meter; import org.springframework.boot.actuate.autoconfigure.metrics.export.properties.StepRegistryPropertiesConfigAdapter; import org.springframework.boot.actuate.autoconfigure.opentelemetry.OpenTelemetryProperties; import org.springframework.boot.actuate.autoconfigure.opentelemetry.OpenTelemetryResourceAttributes; import org.springframework.core.env.Environment; +import org.springframework.util.CollectionUtils; /** * Adapter to convert {@link OtlpMetricsProperties} to an {@link OtlpConfig}. @@ -90,12 +93,12 @@ class OtlpMetricsPropertiesConfigAdapter extends StepRegistryPropertiesConfigAda @Override public Map histogramFlavorPerMeter() { - return get(OtlpMetricsProperties::getHistogramFlavorPerMeter, OtlpConfig.super::histogramFlavorPerMeter); + return get(perMeter(Meter::getHistogramFlavor), OtlpConfig.super::histogramFlavorPerMeter); } @Override public Map maxBucketsPerMeter() { - return get(OtlpMetricsProperties::getMaxBucketsPerMeter, OtlpConfig.super::maxBucketsPerMeter); + return get(perMeter(Meter::getMaxBucketCount), OtlpConfig.super::maxBucketsPerMeter); } @Override @@ -113,4 +116,21 @@ class OtlpMetricsPropertiesConfigAdapter extends StepRegistryPropertiesConfigAda return get(OtlpMetricsProperties::getBaseTimeUnit, OtlpConfig.super::baseTimeUnit); } + private Function> perMeter(Function getter) { + return (properties) -> { + if (CollectionUtils.isEmpty(properties.getMeter())) { + return null; + } + Map perMeter = new LinkedHashMap<>(); + properties.getMeter().forEach((key, meterProperties) -> { + V value = getter.apply(meterProperties); + if (value != null) { + perMeter.put(key, value); + } + }); + return (!perMeter.isEmpty()) ? perMeter : null; + }; + + } + } diff --git a/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/metrics/export/otlp/OtlpMetricsPropertiesConfigAdapterTests.java b/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/metrics/export/otlp/OtlpMetricsPropertiesConfigAdapterTests.java index 723202318a..7f050f1650 100644 --- a/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/metrics/export/otlp/OtlpMetricsPropertiesConfigAdapterTests.java +++ b/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/metrics/export/otlp/OtlpMetricsPropertiesConfigAdapterTests.java @@ -25,6 +25,7 @@ import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.springframework.boot.actuate.autoconfigure.metrics.export.otlp.OtlpMetricsExportAutoConfiguration.PropertiesOtlpMetricsConnectionDetails; +import org.springframework.boot.actuate.autoconfigure.metrics.export.otlp.OtlpMetricsProperties.Meter; import org.springframework.boot.actuate.autoconfigure.opentelemetry.OpenTelemetryProperties; import org.springframework.mock.env.MockEnvironment; @@ -117,8 +118,9 @@ class OtlpMetricsPropertiesConfigAdapterTests { @Test void whenPropertiesHistogramFlavorPerMeterIsSetAdapterHistogramFlavorPerMeterReturnsIt() { - this.properties - .setHistogramFlavorPerMeter(Map.of("my.histograms", HistogramFlavor.BASE2_EXPONENTIAL_BUCKET_HISTOGRAM)); + Meter meterProperties = new Meter(); + meterProperties.setHistogramFlavor(HistogramFlavor.BASE2_EXPONENTIAL_BUCKET_HISTOGRAM); + this.properties.getMeter().put("my.histograms", meterProperties); assertThat(createAdapter().histogramFlavorPerMeter()).containsEntry("my.histograms", HistogramFlavor.BASE2_EXPONENTIAL_BUCKET_HISTOGRAM); } @@ -152,7 +154,9 @@ class OtlpMetricsPropertiesConfigAdapterTests { @Test void whenPropertiesMaxBucketsPerMeterIsSetAdapterMaxBucketsPerMeterReturnsIt() { - this.properties.setMaxBucketsPerMeter(Map.of("my.histograms", 111)); + Meter meterProperties = new Meter(); + meterProperties.setMaxBucketCount(111); + this.properties.getMeter().put("my.histograms", meterProperties); assertThat(createAdapter().maxBucketsPerMeter()).containsEntry("my.histograms", 111); } diff --git a/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/metrics/export/otlp/OtlpMetricsPropertiesTests.java b/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/metrics/export/otlp/OtlpMetricsPropertiesTests.java index 843c5fd3aa..2809e84a73 100644 --- a/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/metrics/export/otlp/OtlpMetricsPropertiesTests.java +++ b/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/metrics/export/otlp/OtlpMetricsPropertiesTests.java @@ -37,11 +37,10 @@ class OtlpMetricsPropertiesTests extends StepRegistryPropertiesTests { assertStepRegistryDefaultValues(properties, config); assertThat(properties.getAggregationTemporality()).isSameAs(config.aggregationTemporality()); assertThat(properties.getHistogramFlavor()).isSameAs(config.histogramFlavor()); - assertThat(properties.getHistogramFlavorPerMeter()).isEqualTo(config.histogramFlavorPerMeter()); assertThat(properties.getMaxScale()).isEqualTo(config.maxScale()); assertThat(properties.getMaxBucketCount()).isEqualTo(config.maxBucketCount()); - assertThat(properties.getMaxBucketsPerMeter()).isEqualTo(config.maxBucketsPerMeter()); assertThat(properties.getBaseTimeUnit()).isSameAs(config.baseTimeUnit()); + assertThat(properties.getMeter()).isEmpty(); } } diff --git a/spring-boot-project/spring-boot-dependencies/build.gradle b/spring-boot-project/spring-boot-dependencies/build.gradle index 3da4e7e12b..9bf051754d 100644 --- a/spring-boot-project/spring-boot-dependencies/build.gradle +++ b/spring-boot-project/spring-boot-dependencies/build.gradle @@ -1548,7 +1548,7 @@ bom { releaseNotes("https://github.com/apache/maven-war-plugin/releases/tag/maven-war-plugin-{version}") } } - library("Micrometer", "1.15.0-RC1") { + library("Micrometer", "1.15.0-SNAPSHOT") { considerSnapshots() group("io.micrometer") { modules = [