From 046e987456d4e22e691413e4221b9177a1406ba3 Mon Sep 17 00:00:00 2001 From: Tanishq Choudhary Date: Thu, 22 Oct 2026 20:57:14 +0530 Subject: [PATCH] fix(config): keep metric name case in metric-specific properties normalizePropertyKey() lowercases the whole key, so settings for a metric with uppercase letters, such as io.prometheus.metrics.myLatency.histogram_classic_upper_bounds, are stored under my_latency and never found for myLatency. Normalize only the property name in io.prometheus.metrics.. keys, so metric names keep their case as they did before 1.5.0. Signed-off-by: Tanishq Choudhary --- .../config/PrometheusPropertiesLoader.java | 8 ++++++++ .../PrometheusPropertiesLoaderTest.java | 20 +++++++++++++++++++ 2 files changed, 28 insertions(+) diff --git a/prometheus-metrics-config/src/main/java/io/prometheus/metrics/config/PrometheusPropertiesLoader.java b/prometheus-metrics-config/src/main/java/io/prometheus/metrics/config/PrometheusPropertiesLoader.java index 04bbfe4a53..26334a6ba4 100644 --- a/prometheus-metrics-config/src/main/java/io/prometheus/metrics/config/PrometheusPropertiesLoader.java +++ b/prometheus-metrics-config/src/main/java/io/prometheus/metrics/config/PrometheusPropertiesLoader.java @@ -210,10 +210,18 @@ private static Map loadPropertiesFromEnvironment() { *

For example: exemplarsEnabled → exemplars_enabled exemplars_enabled → exemplars_enabled * (unchanged) * + *

In metric-specific keys like {@code io.prometheus.metrics..} only the + * property name is normalized, because metric names are case-sensitive. + * * @param key the property key * @return the normalized property key */ static String normalizePropertyKey(String key) { + String metricsPrefix = "io.prometheus.metrics."; + int lastDot = key.lastIndexOf('.'); + if (key.startsWith(metricsPrefix) && lastDot >= metricsPrefix.length()) { + return key.substring(0, lastDot + 1) + normalizePropertyKey(key.substring(lastDot + 1)); + } // Insert underscores before uppercase letters to convert camelCase to snake_case String withUnderscores = key.replaceAll("([a-z])([A-Z])", "$1_$2"); return withUnderscores.toLowerCase(java.util.Locale.ROOT); diff --git a/prometheus-metrics-config/src/test/java/io/prometheus/metrics/config/PrometheusPropertiesLoaderTest.java b/prometheus-metrics-config/src/test/java/io/prometheus/metrics/config/PrometheusPropertiesLoaderTest.java index 8f910ccebd..f7b3f9505e 100644 --- a/prometheus-metrics-config/src/test/java/io/prometheus/metrics/config/PrometheusPropertiesLoaderTest.java +++ b/prometheus-metrics-config/src/test/java/io/prometheus/metrics/config/PrometheusPropertiesLoaderTest.java @@ -56,6 +56,22 @@ void externalPropertiesShouldOverridePropertiesFile() { .isFalse(); } + @Test + void metricSpecificPropertiesShouldKeepMetricNameCase() { + Properties properties = new Properties(); + properties.setProperty( + "io.prometheus.metrics.myLatency.histogram_classic_upper_bounds", ".005, .01, .015"); + properties.setProperty("io.prometheus.metrics.HTTP_duration.histogramNativeOnly", "true"); + + PrometheusProperties prometheusProperties = PrometheusPropertiesLoader.load(properties); + assertThat( + prometheusProperties.getMetricProperties("myLatency").getHistogramClassicUpperBounds()) + .containsExactly(0.005, 0.01, 0.015); + assertThat(prometheusProperties.getMetricProperties("HTTP_duration").getHistogramNativeOnly()) + .isTrue(); + assertThat(prometheusProperties.getMetricProperties("my_latency")).isNull(); + } + @Test void environmentVariablesShouldConfigureMetrics() { // Simulate environment variables as they would be loaded @@ -160,6 +176,10 @@ void normalizePropertyKeyShouldConvertCamelCaseToSnakeCase() { PrometheusPropertiesLoader.normalizePropertyKey( "io.prometheus.exporter.openTelemetry.serviceName")) .isEqualTo("io.prometheus.exporter.open_telemetry.service_name"); + assertThat( + PrometheusPropertiesLoader.normalizePropertyKey( + "io.prometheus.metrics.myLatency.histogramNativeOnly")) + .isEqualTo("io.prometheus.metrics.myLatency.histogram_native_only"); // Test that snake_case keys remain unchanged assertThat(