diff --git a/prometheus-metrics-instrumentation-dropwizard5/src/main/java/io/prometheus/metrics/instrumentation/dropwizard5/labels/GraphiteNamePattern.java b/prometheus-metrics-instrumentation-dropwizard5/src/main/java/io/prometheus/metrics/instrumentation/dropwizard5/labels/GraphiteNamePattern.java
index c13ffd91c..7ec0445d0 100644
--- a/prometheus-metrics-instrumentation-dropwizard5/src/main/java/io/prometheus/metrics/instrumentation/dropwizard5/labels/GraphiteNamePattern.java
+++ b/prometheus-metrics-instrumentation-dropwizard5/src/main/java/io/prometheus/metrics/instrumentation/dropwizard5/labels/GraphiteNamePattern.java
@@ -9,13 +9,16 @@
/**
* GraphiteNamePattern is initialised with a simplified glob pattern that only allows '*' as special
- * character. Examples of valid patterns:
+ * character. Accepts a broad range of metric name patterns including single-level names, names with
+ * underscores, hyphens, and colons. Examples of valid patterns:
*
*
* - org.test.controller.gather.status.400
*
- org.test.controller.gather.status.*
*
- org.test.controller.*.status.*
*
- *.test.controller.*.status.*
+ *
- app_metric_some_count
+ *
- io.dropwizard.jetty.MutableServletContextHandler.*-requests
*
*
* It contains logic to match a metric name and to extract named parameters from it.
@@ -32,6 +35,10 @@ class GraphiteNamePattern {
* @param pattern The glob style pattern to be used.
*/
GraphiteNamePattern(String pattern) throws IllegalArgumentException {
+ if (pattern.contains("**")) {
+ throw new IllegalArgumentException(
+ String.format("Provided pattern [%s] must not contain '**' (double-star glob)", pattern));
+ }
if (!VALIDATION_PATTERN.matcher(pattern).matches()) {
throw new IllegalArgumentException(
String.format("Provided pattern [%s] does not matches [%s]", pattern, METRIC_GLOB_REGEX));
diff --git a/prometheus-metrics-instrumentation-dropwizard5/src/main/java/io/prometheus/metrics/instrumentation/dropwizard5/labels/MapperConfig.java b/prometheus-metrics-instrumentation-dropwizard5/src/main/java/io/prometheus/metrics/instrumentation/dropwizard5/labels/MapperConfig.java
index 13d890861..935286cf1 100644
--- a/prometheus-metrics-instrumentation-dropwizard5/src/main/java/io/prometheus/metrics/instrumentation/dropwizard5/labels/MapperConfig.java
+++ b/prometheus-metrics-instrumentation-dropwizard5/src/main/java/io/prometheus/metrics/instrumentation/dropwizard5/labels/MapperConfig.java
@@ -16,11 +16,14 @@
* and new labels based on this config.
*/
public final class MapperConfig {
- // each part of the metric name between dots
- private static final String METRIC_PART_REGEX = "[a-zA-Z_0-9](-?[a-zA-Z0-9_])+";
- // Simplified GLOB: we can have "*." at the beginning and "*" only at the end
+ // Each part of the metric name between dots. Accepts letters, digits, underscores, hyphens,
+ // colons, and glob wildcards (*) to support a broad range of metric naming conventions.
+ private static final String METRIC_PART_REGEX = "[a-zA-Z_0-9*][a-zA-Z0-9_:\\-*]*";
+ // Simplified GLOB: accepts single-level names, dot-separated names, and glob patterns with '*'.
+ // The pattern requires at least one non-empty segment and does not allow empty segments (double
+ // dots) or empty/whitespace-only strings. The '**' glob is rejected separately in validateMatch.
static final String METRIC_GLOB_REGEX =
- "^(\\*\\.|" + METRIC_PART_REGEX + "\\.)+(\\*|" + METRIC_PART_REGEX + ")$";
+ "^(" + METRIC_PART_REGEX + ")(\\." + METRIC_PART_REGEX + ")*$";
// Labels validation.
private static final String LABEL_REGEX = "^[a-zA-Z_][a-zA-Z0-9_]+$";
private static final Pattern MATCH_EXPRESSION_PATTERN = Pattern.compile(METRIC_GLOB_REGEX);
@@ -109,6 +112,10 @@ public void setLabels(Map labels) {
}
private void validateMatch(String match) {
+ if (match.contains("**")) {
+ throw new IllegalArgumentException(
+ String.format("Match expression [%s] must not contain '**' (double-star glob)", match));
+ }
if (!MATCH_EXPRESSION_PATTERN.matcher(match).matches()) {
throw new IllegalArgumentException(
String.format(
diff --git a/prometheus-metrics-instrumentation-dropwizard5/src/test/java/io/prometheus/metrics/instrumentation/dropwizard5/labels/GraphiteNamePatternTest.java b/prometheus-metrics-instrumentation-dropwizard5/src/test/java/io/prometheus/metrics/instrumentation/dropwizard5/labels/GraphiteNamePatternTest.java
index 100703d94..aa3e20e6b 100644
--- a/prometheus-metrics-instrumentation-dropwizard5/src/test/java/io/prometheus/metrics/instrumentation/dropwizard5/labels/GraphiteNamePatternTest.java
+++ b/prometheus-metrics-instrumentation-dropwizard5/src/test/java/io/prometheus/metrics/instrumentation/dropwizard5/labels/GraphiteNamePatternTest.java
@@ -17,18 +17,12 @@ void createNew_WHEN_InvalidPattern_THEN_ShouldThrowException() {
List invalidPatterns =
Arrays.asList(
"",
- "a",
- "1org",
"1org.",
"org.",
"org.**",
- "org.**",
- "org.company-",
"org.company-.",
- "org.company-*",
"org.company.**",
"org.company.**-",
- "org.com*pany.*",
"org.test.contr.oller.gather.status..400",
"org.test.controller.gather.status..400");
for (String pattern : invalidPatterns) {
@@ -47,7 +41,22 @@ void createNew_WHEN_ValidPattern_THEN_ShouldCreateThePatternSuccessfully() {
"org.test.controller.*.status.*",
"*.test.controller.*.status.*",
"*.test.controller-1.*.status.*",
- "*.amazing-test.controller-1.*.status.*");
+ "*.amazing-test.controller-1.*.status.*",
+ // Single-level names (previously rejected, fixes #461)
+ "a",
+ "1org",
+ "app_metric_some_count",
+ // Names with colons (fixes #645)
+ "my:metric:name",
+ "org.company:metric.*",
+ // Names with hyphens at boundaries
+ "org.company-",
+ "org.company-*",
+ // Embedded glob in segment (fixes #518)
+ "org.com*pany.*",
+ "io.dropwizard.jetty.MutableServletContextHandler.*-requests",
+ // Standalone glob
+ "*");
for (String pattern : validPatterns) {
new GraphiteNamePattern(pattern);
}
diff --git a/prometheus-metrics-instrumentation-dropwizard5/src/test/java/io/prometheus/metrics/instrumentation/dropwizard5/labels/MapperConfigTest.java b/prometheus-metrics-instrumentation-dropwizard5/src/test/java/io/prometheus/metrics/instrumentation/dropwizard5/labels/MapperConfigTest.java
index 982e8f24e..dd36ae7db 100644
--- a/prometheus-metrics-instrumentation-dropwizard5/src/test/java/io/prometheus/metrics/instrumentation/dropwizard5/labels/MapperConfigTest.java
+++ b/prometheus-metrics-instrumentation-dropwizard5/src/test/java/io/prometheus/metrics/instrumentation/dropwizard5/labels/MapperConfigTest.java
@@ -41,6 +41,52 @@ void setLabels_WHEN_ExpressionDoesnNotMatchPattern_ThrowException() {
.isThrownBy(() -> mapperConfig.setLabels(labels));
}
+ @Test
+ void setMatch_WHEN_SingleLevelName_AllGood() {
+ final MapperConfig mapperConfig = new MapperConfig();
+ mapperConfig.setMatch("app_metric_some_count");
+ assertThat(mapperConfig.getMatch()).isEqualTo("app_metric_some_count");
+ }
+
+ @Test
+ void setMatch_WHEN_NameWithColons_AllGood() {
+ final MapperConfig mapperConfig = new MapperConfig();
+ mapperConfig.setMatch("my:metric:name");
+ assertThat(mapperConfig.getMatch()).isEqualTo("my:metric:name");
+ }
+
+ @Test
+ void setMatch_WHEN_EmbeddedGlobInSegment_AllGood() {
+ final MapperConfig mapperConfig = new MapperConfig();
+ mapperConfig.setMatch("io.dropwizard.jetty.MutableServletContextHandler.*-requests");
+ assertThat(mapperConfig.getMatch())
+ .isEqualTo("io.dropwizard.jetty.MutableServletContextHandler.*-requests");
+ }
+
+ @Test
+ void setMatch_WHEN_DoubleStarGlob_ThrowException() {
+ assertThatExceptionOfType(IllegalArgumentException.class)
+ .isThrownBy(() -> new MapperConfig().setMatch("org.**"));
+ }
+
+ @Test
+ void setMatch_WHEN_EmptyString_ThrowException() {
+ assertThatExceptionOfType(IllegalArgumentException.class)
+ .isThrownBy(() -> new MapperConfig().setMatch(""));
+ }
+
+ @Test
+ void setMatch_WHEN_TrailingDot_ThrowException() {
+ assertThatExceptionOfType(IllegalArgumentException.class)
+ .isThrownBy(() -> new MapperConfig().setMatch("org.company."));
+ }
+
+ @Test
+ void setMatch_WHEN_DoubleDot_ThrowException() {
+ assertThatExceptionOfType(IllegalArgumentException.class)
+ .isThrownBy(() -> new MapperConfig().setMatch("org..company"));
+ }
+
@Test
void toString_WHEN_EmptyConfig_AllGood() {
final MapperConfig mapperConfig = new MapperConfig();