diff --git a/examples/example-exporter-multi-target/src/main/java/io/prometheus/metrics/examples/multitarget/SampleMultiCollector.java b/examples/example-exporter-multi-target/src/main/java/io/prometheus/metrics/examples/multitarget/SampleMultiCollector.java
index 207c024a5..ca3ca08f9 100644
--- a/examples/example-exporter-multi-target/src/main/java/io/prometheus/metrics/examples/multitarget/SampleMultiCollector.java
+++ b/examples/example-exporter-multi-target/src/main/java/io/prometheus/metrics/examples/multitarget/SampleMultiCollector.java
@@ -7,7 +7,7 @@
import io.prometheus.metrics.model.snapshots.Labels;
import io.prometheus.metrics.model.snapshots.MetricSnapshot;
import io.prometheus.metrics.model.snapshots.MetricSnapshots;
-import io.prometheus.metrics.model.snapshots.PrometheusNaming;
+import io.prometheus.metrics.model.snapshots.PrometheusNames;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
@@ -34,7 +34,7 @@ protected MetricSnapshots collectMetricSnapshots(PrometheusScrapeRequest scrapeR
gaugeBuilder.name("x_load").help("process load");
CounterSnapshot.Builder counterBuilder = CounterSnapshot.builder();
- counterBuilder.name(PrometheusNaming.sanitizeMetricName("x_calls_total")).help("invocations");
+ counterBuilder.name(PrometheusNames.sanitizeMetricName("x_calls_total")).help("invocations");
String[] targetNames = scrapeRequest.getParameterValues("target");
String targetName;
diff --git a/prometheus-metrics-core/src/main/java/io/prometheus/metrics/core/metrics/Counter.java b/prometheus-metrics-core/src/main/java/io/prometheus/metrics/core/metrics/Counter.java
index a2bac20d2..89ab7b02e 100644
--- a/prometheus-metrics-core/src/main/java/io/prometheus/metrics/core/metrics/Counter.java
+++ b/prometheus-metrics-core/src/main/java/io/prometheus/metrics/core/metrics/Counter.java
@@ -230,7 +230,7 @@ private Builder(PrometheusProperties properties) {
* Prometheus.
*
*
Throws an {@link IllegalArgumentException} if {@link
- * io.prometheus.metrics.model.snapshots.PrometheusNaming#isValidMetricName(String)
+ * io.prometheus.metrics.model.snapshots.PrometheusNames#isValidMetricName(String)
* MetricMetadata.isValidMetricName(name)} is {@code false}.
*/
@Override
diff --git a/prometheus-metrics-core/src/main/java/io/prometheus/metrics/core/metrics/CounterWithCallback.java b/prometheus-metrics-core/src/main/java/io/prometheus/metrics/core/metrics/CounterWithCallback.java
index 044644ec5..dc5fa6e9c 100644
--- a/prometheus-metrics-core/src/main/java/io/prometheus/metrics/core/metrics/CounterWithCallback.java
+++ b/prometheus-metrics-core/src/main/java/io/prometheus/metrics/core/metrics/CounterWithCallback.java
@@ -88,7 +88,7 @@ private Builder(PrometheusProperties properties) {
* Prometheus.
*
*
Throws an {@link IllegalArgumentException} if {@link
- * io.prometheus.metrics.model.snapshots.PrometheusNaming#isValidMetricName(String)
+ * io.prometheus.metrics.model.snapshots.PrometheusNames#isValidMetricName(String)
* MetricMetadata.isValidMetricName(name)} is {@code false}.
*/
@Override
diff --git a/prometheus-metrics-core/src/main/java/io/prometheus/metrics/core/metrics/Info.java b/prometheus-metrics-core/src/main/java/io/prometheus/metrics/core/metrics/Info.java
index d7aa6be70..b2138dafa 100644
--- a/prometheus-metrics-core/src/main/java/io/prometheus/metrics/core/metrics/Info.java
+++ b/prometheus-metrics-core/src/main/java/io/prometheus/metrics/core/metrics/Info.java
@@ -135,7 +135,7 @@ private Builder(PrometheusProperties config) {
* "runtime_info"} in Prometheus.
*
*
Throws an {@link IllegalArgumentException} if {@link
- * io.prometheus.metrics.model.snapshots.PrometheusNaming#isValidMetricName(String)
+ * io.prometheus.metrics.model.snapshots.PrometheusNames#isValidMetricName(String)
* MetricMetadata.isValidMetricName(name)} is {@code false}.
*/
@Override
diff --git a/prometheus-metrics-core/src/main/java/io/prometheus/metrics/core/metrics/MetricWithFixedMetadata.java b/prometheus-metrics-core/src/main/java/io/prometheus/metrics/core/metrics/MetricWithFixedMetadata.java
index 6f6afa482..003febe66 100644
--- a/prometheus-metrics-core/src/main/java/io/prometheus/metrics/core/metrics/MetricWithFixedMetadata.java
+++ b/prometheus-metrics-core/src/main/java/io/prometheus/metrics/core/metrics/MetricWithFixedMetadata.java
@@ -3,7 +3,7 @@
import io.prometheus.metrics.config.PrometheusProperties;
import io.prometheus.metrics.model.snapshots.Labels;
import io.prometheus.metrics.model.snapshots.MetricMetadata;
-import io.prometheus.metrics.model.snapshots.PrometheusNaming;
+import io.prometheus.metrics.model.snapshots.PrometheusNames;
import io.prometheus.metrics.model.snapshots.Unit;
import java.util.Arrays;
import java.util.List;
@@ -61,7 +61,7 @@ protected Builder(List illegalLabelNames, PrometheusProperties propertie
}
public B name(String name) {
- String error = PrometheusNaming.validateMetricName(name);
+ String error = PrometheusNames.validateMetricName(name);
if (error != null) {
throw new IllegalArgumentException("'" + name + "': Illegal metric name: " + error);
}
@@ -81,7 +81,7 @@ public B help(String help) {
public B labelNames(String... labelNames) {
for (String labelName : labelNames) {
- if (!PrometheusNaming.isValidLabelName(labelName)) {
+ if (!PrometheusNames.isValidLabelName(labelName)) {
throw new IllegalArgumentException(labelName + ": illegal label name");
}
if (illegalLabelNames.contains(labelName)) {
diff --git a/prometheus-metrics-core/src/main/java/io/prometheus/metrics/core/metrics/StateSet.java b/prometheus-metrics-core/src/main/java/io/prometheus/metrics/core/metrics/StateSet.java
index 4dbaf8ad5..6de7cef89 100644
--- a/prometheus-metrics-core/src/main/java/io/prometheus/metrics/core/metrics/StateSet.java
+++ b/prometheus-metrics-core/src/main/java/io/prometheus/metrics/core/metrics/StateSet.java
@@ -1,6 +1,6 @@
package io.prometheus.metrics.core.metrics;
-import static io.prometheus.metrics.model.snapshots.PrometheusNaming.prometheusName;
+import static io.prometheus.metrics.model.snapshots.PrometheusNames.prometheusName;
import io.prometheus.metrics.config.PrometheusProperties;
import io.prometheus.metrics.core.datapoints.StateSetDataPoint;
diff --git a/prometheus-metrics-exporter-pushgateway/src/main/java/io/prometheus/metrics/exporter/pushgateway/PushGateway.java b/prometheus-metrics-exporter-pushgateway/src/main/java/io/prometheus/metrics/exporter/pushgateway/PushGateway.java
index 643e0aeca..f30cab10b 100644
--- a/prometheus-metrics-exporter-pushgateway/src/main/java/io/prometheus/metrics/exporter/pushgateway/PushGateway.java
+++ b/prometheus-metrics-exporter-pushgateway/src/main/java/io/prometheus/metrics/exporter/pushgateway/PushGateway.java
@@ -1,7 +1,7 @@
package io.prometheus.metrics.exporter.pushgateway;
import static io.prometheus.metrics.exporter.pushgateway.Scheme.HTTP;
-import static io.prometheus.metrics.model.snapshots.PrometheusNaming.escapeName;
+import static io.prometheus.metrics.model.snapshots.NameEscaper.escapeName;
import static java.util.Objects.requireNonNull;
import io.prometheus.metrics.config.EscapingScheme;
diff --git a/prometheus-metrics-exposition-textformats/src/main/java/io/prometheus/metrics/expositionformats/OpenMetricsTextFormatWriter.java b/prometheus-metrics-exposition-textformats/src/main/java/io/prometheus/metrics/expositionformats/OpenMetricsTextFormatWriter.java
index 1ba1c627d..2699d133e 100644
--- a/prometheus-metrics-exposition-textformats/src/main/java/io/prometheus/metrics/expositionformats/OpenMetricsTextFormatWriter.java
+++ b/prometheus-metrics-exposition-textformats/src/main/java/io/prometheus/metrics/expositionformats/OpenMetricsTextFormatWriter.java
@@ -23,7 +23,7 @@
import io.prometheus.metrics.model.snapshots.MetricMetadata;
import io.prometheus.metrics.model.snapshots.MetricSnapshot;
import io.prometheus.metrics.model.snapshots.MetricSnapshots;
-import io.prometheus.metrics.model.snapshots.PrometheusNaming;
+import io.prometheus.metrics.model.snapshots.PrometheusNames;
import io.prometheus.metrics.model.snapshots.Quantile;
import io.prometheus.metrics.model.snapshots.SnapshotEscaper;
import io.prometheus.metrics.model.snapshots.StateSetSnapshot;
@@ -396,7 +396,7 @@ private void writeNameAndLabels(
boolean metricInsideBraces = false;
// If the name does not pass the legacy validity check, we must put the
// metric name inside the braces.
- if (!PrometheusNaming.isValidLegacyMetricName(name)) {
+ if (!PrometheusNames.isValidLegacyMetricName(name)) {
metricInsideBraces = true;
writer.write('{');
}
diff --git a/prometheus-metrics-exposition-textformats/src/main/java/io/prometheus/metrics/expositionformats/PrometheusTextFormatWriter.java b/prometheus-metrics-exposition-textformats/src/main/java/io/prometheus/metrics/expositionformats/PrometheusTextFormatWriter.java
index 5dc3f629b..4bb0e4220 100644
--- a/prometheus-metrics-exposition-textformats/src/main/java/io/prometheus/metrics/expositionformats/PrometheusTextFormatWriter.java
+++ b/prometheus-metrics-exposition-textformats/src/main/java/io/prometheus/metrics/expositionformats/PrometheusTextFormatWriter.java
@@ -21,7 +21,7 @@
import io.prometheus.metrics.model.snapshots.MetricMetadata;
import io.prometheus.metrics.model.snapshots.MetricSnapshot;
import io.prometheus.metrics.model.snapshots.MetricSnapshots;
-import io.prometheus.metrics.model.snapshots.PrometheusNaming;
+import io.prometheus.metrics.model.snapshots.PrometheusNames;
import io.prometheus.metrics.model.snapshots.Quantile;
import io.prometheus.metrics.model.snapshots.StateSetSnapshot;
import io.prometheus.metrics.model.snapshots.SummarySnapshot;
@@ -396,7 +396,7 @@ private void writeNameAndLabels(
boolean metricInsideBraces = false;
// If the name does not pass the legacy validity check, we must put the
// metric name inside the braces.
- if (!PrometheusNaming.isValidLegacyLabelName(name)) {
+ if (!PrometheusNames.isValidLegacyLabelName(name)) {
metricInsideBraces = true;
writer.write('{');
}
diff --git a/prometheus-metrics-exposition-textformats/src/main/java/io/prometheus/metrics/expositionformats/TextFormatUtil.java b/prometheus-metrics-exposition-textformats/src/main/java/io/prometheus/metrics/expositionformats/TextFormatUtil.java
index 42909ee25..1ca1402dd 100644
--- a/prometheus-metrics-exposition-textformats/src/main/java/io/prometheus/metrics/expositionformats/TextFormatUtil.java
+++ b/prometheus-metrics-exposition-textformats/src/main/java/io/prometheus/metrics/expositionformats/TextFormatUtil.java
@@ -2,7 +2,7 @@
import io.prometheus.metrics.config.EscapingScheme;
import io.prometheus.metrics.model.snapshots.Labels;
-import io.prometheus.metrics.model.snapshots.PrometheusNaming;
+import io.prometheus.metrics.model.snapshots.PrometheusNames;
import io.prometheus.metrics.model.snapshots.SnapshotEscaper;
import java.io.IOException;
import java.io.Writer;
@@ -137,13 +137,13 @@ static void writeLabels(
static void writeName(Writer writer, String name, NameType nameType) throws IOException {
switch (nameType) {
case Metric:
- if (PrometheusNaming.isValidLegacyMetricName(name)) {
+ if (PrometheusNames.isValidLegacyMetricName(name)) {
writer.write(name);
return;
}
break;
case Label:
- if (PrometheusNaming.isValidLegacyLabelName(name)) {
+ if (PrometheusNames.isValidLegacyLabelName(name)) {
writer.write(name);
return;
}
diff --git a/prometheus-metrics-exposition-textformats/src/test/java/io/prometheus/metrics/expositionformats/ExpositionFormatsTest.java b/prometheus-metrics-exposition-textformats/src/test/java/io/prometheus/metrics/expositionformats/ExpositionFormatsTest.java
index 09acff58c..c7d5d6435 100644
--- a/prometheus-metrics-exposition-textformats/src/test/java/io/prometheus/metrics/expositionformats/ExpositionFormatsTest.java
+++ b/prometheus-metrics-exposition-textformats/src/test/java/io/prometheus/metrics/expositionformats/ExpositionFormatsTest.java
@@ -17,7 +17,7 @@
import io.prometheus.metrics.model.snapshots.MetricSnapshot;
import io.prometheus.metrics.model.snapshots.MetricSnapshots;
import io.prometheus.metrics.model.snapshots.NativeHistogramBuckets;
-import io.prometheus.metrics.model.snapshots.PrometheusNaming;
+import io.prometheus.metrics.model.snapshots.PrometheusNames;
import io.prometheus.metrics.model.snapshots.Quantiles;
import io.prometheus.metrics.model.snapshots.StateSetSnapshot;
import io.prometheus.metrics.model.snapshots.SummarySnapshot;
@@ -2707,7 +2707,7 @@ public void testUnknownWithDots() throws IOException {
// @formatter:on
UnknownSnapshot unknown =
UnknownSnapshot.builder()
- .name(PrometheusNaming.sanitizeMetricName("some.unknown.metric", Unit.BYTES))
+ .name(PrometheusNames.sanitizeMetricName("some.unknown.metric", Unit.BYTES))
.help("help message")
.unit(Unit.BYTES)
.dataPoint(
diff --git a/prometheus-metrics-instrumentation-dropwizard/src/main/java/io/prometheus/metrics/instrumentation/dropwizard/DropwizardExports.java b/prometheus-metrics-instrumentation-dropwizard/src/main/java/io/prometheus/metrics/instrumentation/dropwizard/DropwizardExports.java
index 4ab03341e..3942aec90 100644
--- a/prometheus-metrics-instrumentation-dropwizard/src/main/java/io/prometheus/metrics/instrumentation/dropwizard/DropwizardExports.java
+++ b/prometheus-metrics-instrumentation-dropwizard/src/main/java/io/prometheus/metrics/instrumentation/dropwizard/DropwizardExports.java
@@ -18,7 +18,7 @@
import io.prometheus.metrics.model.snapshots.MetricMetadata;
import io.prometheus.metrics.model.snapshots.MetricSnapshot;
import io.prometheus.metrics.model.snapshots.MetricSnapshots;
-import io.prometheus.metrics.model.snapshots.PrometheusNaming;
+import io.prometheus.metrics.model.snapshots.PrometheusNames;
import io.prometheus.metrics.model.snapshots.Quantiles;
import io.prometheus.metrics.model.snapshots.SummarySnapshot;
import java.util.Collections;
@@ -101,7 +101,7 @@ private static String getHelpMessage(String metricName, Metric metric) {
private MetricMetadata getMetricMetaData(String metricName, Metric metric) {
String name = labelMapper != null ? labelMapper.getName(metricName) : metricName;
return new MetricMetadata(
- PrometheusNaming.sanitizeMetricName(name), getHelpMessage(metricName, metric));
+ PrometheusNames.sanitizeMetricName(name), getHelpMessage(metricName, metric));
}
/**
@@ -134,7 +134,7 @@ MetricSnapshot fromGauge(String dropwizardName, Gauge> gauge) {
Level.FINE,
String.format(
"Invalid type for Gauge %s: %s",
- PrometheusNaming.sanitizeMetricName(dropwizardName),
+ PrometheusNames.sanitizeMetricName(dropwizardName),
obj == null ? "null" : obj.getClass().getName()));
return null;
}
@@ -170,7 +170,7 @@ MetricSnapshot fromSnapshotAndCount(
String name = labelMapper != null ? labelMapper.getName(dropwizardName) : dropwizardName;
MetricMetadata metadata =
- new MetricMetadata(PrometheusNaming.sanitizeMetricName(name), helpMessage);
+ new MetricMetadata(PrometheusNames.sanitizeMetricName(name), helpMessage);
SummarySnapshot.SummaryDataPointSnapshot.Builder dataPointBuilder =
SummarySnapshot.SummaryDataPointSnapshot.builder().quantiles(quantiles).count(count);
if (labelMapper != null) {
diff --git a/prometheus-metrics-instrumentation-dropwizard5/src/main/java/io/prometheus/metrics/instrumentation/dropwizard5/DropwizardExports.java b/prometheus-metrics-instrumentation-dropwizard5/src/main/java/io/prometheus/metrics/instrumentation/dropwizard5/DropwizardExports.java
index c68d26f49..04b005abc 100644
--- a/prometheus-metrics-instrumentation-dropwizard5/src/main/java/io/prometheus/metrics/instrumentation/dropwizard5/DropwizardExports.java
+++ b/prometheus-metrics-instrumentation-dropwizard5/src/main/java/io/prometheus/metrics/instrumentation/dropwizard5/DropwizardExports.java
@@ -18,7 +18,7 @@
import io.prometheus.metrics.model.snapshots.MetricMetadata;
import io.prometheus.metrics.model.snapshots.MetricSnapshot;
import io.prometheus.metrics.model.snapshots.MetricSnapshots;
-import io.prometheus.metrics.model.snapshots.PrometheusNaming;
+import io.prometheus.metrics.model.snapshots.PrometheusNames;
import io.prometheus.metrics.model.snapshots.Quantiles;
import io.prometheus.metrics.model.snapshots.SummarySnapshot;
import java.util.Collections;
@@ -101,7 +101,7 @@ private static String getHelpMessage(String metricName, Metric metric) {
private MetricMetadata getMetricMetaData(String metricName, Metric metric) {
String name = labelMapper != null ? labelMapper.getName(metricName) : metricName;
return new MetricMetadata(
- PrometheusNaming.sanitizeMetricName(name), getHelpMessage(metricName, metric));
+ PrometheusNames.sanitizeMetricName(name), getHelpMessage(metricName, metric));
}
/**
@@ -134,7 +134,7 @@ MetricSnapshot fromGauge(String dropwizardName, Gauge> gauge) {
Level.FINE,
String.format(
"Invalid type for Gauge %s: %s",
- PrometheusNaming.sanitizeMetricName(dropwizardName),
+ PrometheusNames.sanitizeMetricName(dropwizardName),
obj == null ? "null" : obj.getClass().getName()));
return null;
}
@@ -170,7 +170,7 @@ MetricSnapshot fromSnapshotAndCount(
String name = labelMapper != null ? labelMapper.getName(dropwizardName) : dropwizardName;
MetricMetadata metadata =
- new MetricMetadata(PrometheusNaming.sanitizeMetricName(name), helpMessage);
+ new MetricMetadata(PrometheusNames.sanitizeMetricName(name), helpMessage);
SummarySnapshot.SummaryDataPointSnapshot.Builder dataPointBuilder =
SummarySnapshot.SummaryDataPointSnapshot.builder().quantiles(quantiles).count(count);
if (labelMapper != null) {
diff --git a/prometheus-metrics-model/src/main/java/io/prometheus/metrics/model/registry/PrometheusRegistry.java b/prometheus-metrics-model/src/main/java/io/prometheus/metrics/model/registry/PrometheusRegistry.java
index 7db568d95..4ebbef8e6 100644
--- a/prometheus-metrics-model/src/main/java/io/prometheus/metrics/model/registry/PrometheusRegistry.java
+++ b/prometheus-metrics-model/src/main/java/io/prometheus/metrics/model/registry/PrometheusRegistry.java
@@ -1,6 +1,6 @@
package io.prometheus.metrics.model.registry;
-import static io.prometheus.metrics.model.snapshots.PrometheusNaming.prometheusName;
+import static io.prometheus.metrics.model.snapshots.PrometheusNames.prometheusName;
import io.prometheus.metrics.model.snapshots.MetricSnapshot;
import io.prometheus.metrics.model.snapshots.MetricSnapshots;
diff --git a/prometheus-metrics-model/src/main/java/io/prometheus/metrics/model/snapshots/Labels.java b/prometheus-metrics-model/src/main/java/io/prometheus/metrics/model/snapshots/Labels.java
index 31c6ab485..1e762123d 100644
--- a/prometheus-metrics-model/src/main/java/io/prometheus/metrics/model/snapshots/Labels.java
+++ b/prometheus-metrics-model/src/main/java/io/prometheus/metrics/model/snapshots/Labels.java
@@ -1,7 +1,7 @@
package io.prometheus.metrics.model.snapshots;
-import static io.prometheus.metrics.model.snapshots.PrometheusNaming.isValidLabelName;
-import static io.prometheus.metrics.model.snapshots.PrometheusNaming.prometheusName;
+import static io.prometheus.metrics.model.snapshots.PrometheusNames.isValidLabelName;
+import static io.prometheus.metrics.model.snapshots.PrometheusNames.prometheusName;
import java.util.ArrayList;
import java.util.Arrays;
@@ -48,8 +48,8 @@ public boolean isEmpty() {
* Labels.of(...)} methods, or you can use the {@link Labels#builder()}.
*
* @param keyValuePairs as in {@code {name1, value1, name2, value2}}. Length must be even. {@link
- * PrometheusNaming#isValidLabelName(String)} must be true for each name. Use {@link
- * PrometheusNaming#sanitizeLabelName(String)} to convert arbitrary strings to valid label
+ * PrometheusNames#isValidLabelName(String)} must be true for each name. Use {@link
+ * PrometheusNames#sanitizeLabelName(String)} to convert arbitrary strings to valid label
* names. Label names must be unique (no duplicate label names).
*/
public static Labels of(String... keyValuePairs) {
@@ -75,8 +75,8 @@ public static Labels of(String... keyValuePairs) {
* Create a new Labels instance. You can either create Labels with one of the static {@code
* Labels.of(...)} methods, or you can use the {@link Labels#builder()}.
*
- * @param names label names. {@link PrometheusNaming#isValidLabelName(String)} must be true for
- * each name. Use {@link PrometheusNaming#sanitizeLabelName(String)} to convert arbitrary
+ * @param names label names. {@link PrometheusNames#isValidLabelName(String)} must be true for
+ * each name. Use {@link PrometheusNames#sanitizeLabelName(String)} to convert arbitrary
* strings to valid label names. Label names must be unique (no duplicate label names).
* @param values label values. {@code names.size()} must be equal to {@code values.size()}.
*/
@@ -98,8 +98,8 @@ public static Labels of(List names, List values) {
* Create a new Labels instance. You can either create Labels with one of the static {@code
* Labels.of(...)} methods, or you can use the {@link Labels#builder()}.
*
- * @param names label names. {@link PrometheusNaming#isValidLabelName(String)} must be true for
- * each name. Use {@link PrometheusNaming#sanitizeLabelName(String)} to convert arbitrary
+ * @param names label names. {@link PrometheusNames#isValidLabelName(String)} must be true for
+ * each name. Use {@link PrometheusNames#sanitizeLabelName(String)} to convert arbitrary
* strings to valid label names. Label names must be unique (no duplicate label names).
* @param values label values. {@code names.length} must be equal to {@code values.length}.
*/
@@ -121,11 +121,11 @@ static String[] makePrometheusNames(String[] names) {
String[] prometheusNames = names;
for (int i = 0; i < names.length; i++) {
String name = names[i];
- if (!PrometheusNaming.isValidLegacyLabelName(name)) {
+ if (!PrometheusNames.isValidLegacyLabelName(name)) {
if (prometheusNames == names) {
prometheusNames = Arrays.copyOf(names, names.length);
}
- prometheusNames[i] = PrometheusNaming.prometheusName(name);
+ prometheusNames[i] = PrometheusNames.prometheusName(name);
}
}
return prometheusNames;
diff --git a/prometheus-metrics-model/src/main/java/io/prometheus/metrics/model/snapshots/MetricMetadata.java b/prometheus-metrics-model/src/main/java/io/prometheus/metrics/model/snapshots/MetricMetadata.java
index 9c54f96d5..b29f36f35 100644
--- a/prometheus-metrics-model/src/main/java/io/prometheus/metrics/model/snapshots/MetricMetadata.java
+++ b/prometheus-metrics-model/src/main/java/io/prometheus/metrics/model/snapshots/MetricMetadata.java
@@ -45,9 +45,9 @@ public MetricMetadata(String name, String help) {
/**
* Constructor.
*
- * @param name must not be {@code null}. {@link PrometheusNaming#isValidMetricName(String)
+ * @param name must not be {@code null}. {@link PrometheusNames#isValidMetricName(String)
* isValidMetricName(name)} must be {@code true}. Use {@link
- * PrometheusNaming#sanitizeMetricName(String)} to convert arbitrary strings into valid names.
+ * PrometheusNames#sanitizeMetricName(String)} to convert arbitrary strings into valid names.
* @param help optional. May be {@code null}.
* @param unit optional. May be {@code null}.
*/
@@ -56,7 +56,7 @@ public MetricMetadata(String name, @Nullable String help, @Nullable Unit unit) {
this.help = help;
this.unit = unit;
validate();
- this.prometheusName = PrometheusNaming.prometheusName(name);
+ this.prometheusName = PrometheusNames.prometheusName(name);
}
/**
@@ -97,7 +97,7 @@ private void validate() {
if (name == null) {
throw new IllegalArgumentException("Missing required field: name is null");
}
- String error = PrometheusNaming.validateMetricName(name);
+ String error = PrometheusNames.validateMetricName(name);
if (error != null) {
throw new IllegalArgumentException(
"'"
@@ -105,7 +105,7 @@ private void validate() {
+ "': Illegal metric name. "
+ error
+ " Call "
- + PrometheusNaming.class.getSimpleName()
+ + PrometheusNames.class.getSimpleName()
+ ".sanitizeMetricName(name) to avoid this error.");
}
if (hasUnit()) {
@@ -118,13 +118,13 @@ private void validate() {
+ unit
+ "."
+ " Call "
- + PrometheusNaming.class.getSimpleName()
+ + PrometheusNames.class.getSimpleName()
+ ".sanitizeMetricName(name, unit) to avoid this error.");
}
}
}
MetricMetadata escape(EscapingScheme escapingScheme) {
- return new MetricMetadata(PrometheusNaming.escapeName(name, escapingScheme), help, unit);
+ return new MetricMetadata(NameEscaper.escapeName(name, escapingScheme), help, unit);
}
}
diff --git a/prometheus-metrics-model/src/main/java/io/prometheus/metrics/model/snapshots/MetricSnapshot.java b/prometheus-metrics-model/src/main/java/io/prometheus/metrics/model/snapshots/MetricSnapshot.java
index 4dac2e30e..04f435475 100644
--- a/prometheus-metrics-model/src/main/java/io/prometheus/metrics/model/snapshots/MetricSnapshot.java
+++ b/prometheus-metrics-model/src/main/java/io/prometheus/metrics/model/snapshots/MetricSnapshot.java
@@ -61,7 +61,7 @@ public abstract static class Builder> {
/**
* The name is required. If the name is missing or invalid, {@code build()} will throw an {@link
- * IllegalArgumentException}. See {@link PrometheusNaming#isValidMetricName(String)} for info on
+ * IllegalArgumentException}. See {@link PrometheusNames#isValidMetricName(String)} for info on
* valid metric names.
*/
public T name(String name) {
diff --git a/prometheus-metrics-model/src/main/java/io/prometheus/metrics/model/snapshots/MetricSnapshots.java b/prometheus-metrics-model/src/main/java/io/prometheus/metrics/model/snapshots/MetricSnapshots.java
index ecee897e4..48a9ccd6a 100644
--- a/prometheus-metrics-model/src/main/java/io/prometheus/metrics/model/snapshots/MetricSnapshots.java
+++ b/prometheus-metrics-model/src/main/java/io/prometheus/metrics/model/snapshots/MetricSnapshots.java
@@ -1,6 +1,6 @@
package io.prometheus.metrics.model.snapshots;
-import static io.prometheus.metrics.model.snapshots.PrometheusNaming.prometheusName;
+import static io.prometheus.metrics.model.snapshots.PrometheusNames.prometheusName;
import static java.util.Collections.unmodifiableList;
import static java.util.Comparator.comparing;
diff --git a/prometheus-metrics-model/src/main/java/io/prometheus/metrics/model/snapshots/NameEscaper.java b/prometheus-metrics-model/src/main/java/io/prometheus/metrics/model/snapshots/NameEscaper.java
new file mode 100644
index 000000000..715f88951
--- /dev/null
+++ b/prometheus-metrics-model/src/main/java/io/prometheus/metrics/model/snapshots/NameEscaper.java
@@ -0,0 +1,74 @@
+package io.prometheus.metrics.model.snapshots;
+
+import io.prometheus.metrics.config.EscapingScheme;
+
+public class NameEscaper {
+ /**
+ * Escapes the incoming name according to the provided escaping scheme. Depending on the rules of
+ * escaping, this may cause no change in the string that is returned (especially NO_ESCAPING,
+ * which by definition is a noop). This method does not do any validation of the name.
+ */
+ public static String escapeName(String name, EscapingScheme scheme) {
+ if (name.isEmpty() || !needsEscaping(name, scheme)) {
+ return name;
+ }
+
+ StringBuilder escaped = new StringBuilder();
+ switch (scheme) {
+ case ALLOW_UTF8:
+ return name;
+ case UNDERSCORE_ESCAPING:
+ for (int i = 0; i < name.length(); ) {
+ int c = name.codePointAt(i);
+ if (PrometheusNames.isValidLegacyChar(c, i)) {
+ escaped.appendCodePoint(c);
+ } else {
+ escaped.append('_');
+ }
+ i += Character.charCount(c);
+ }
+ return escaped.toString();
+ case DOTS_ESCAPING:
+ // Do not early return for legacy valid names, we still escape underscores.
+ for (int i = 0; i < name.length(); ) {
+ int c = name.codePointAt(i);
+ if (c == '_') {
+ escaped.append("__");
+ } else if (c == '.') {
+ escaped.append("_dot_");
+ } else if (PrometheusNames.isValidLegacyChar(c, i)) {
+ escaped.appendCodePoint(c);
+ } else {
+ escaped.append("__");
+ }
+ i += Character.charCount(c);
+ }
+ return escaped.toString();
+ case VALUE_ENCODING_ESCAPING:
+ escaped.append("U__");
+ for (int i = 0; i < name.length(); ) {
+ int c = name.codePointAt(i);
+ if (c == '_') {
+ escaped.append("__");
+ } else if (PrometheusNames.isValidLegacyChar(c, i)) {
+ escaped.appendCodePoint(c);
+ } else if (!PrometheusNames.isValidUtf8Char(c)) {
+ escaped.append("_FFFD_");
+ } else {
+ escaped.append('_');
+ escaped.append(Integer.toHexString(c));
+ escaped.append('_');
+ }
+ i += Character.charCount(c);
+ }
+ return escaped.toString();
+ default:
+ throw new IllegalArgumentException("Invalid escaping scheme " + scheme);
+ }
+ }
+
+ static boolean needsEscaping(String name, EscapingScheme scheme) {
+ return !PrometheusNames.isValidLegacyMetricName(name)
+ || (scheme == EscapingScheme.DOTS_ESCAPING && (name.contains(".") || name.contains("_")));
+ }
+}
diff --git a/prometheus-metrics-model/src/main/java/io/prometheus/metrics/model/snapshots/PrometheusNames.java b/prometheus-metrics-model/src/main/java/io/prometheus/metrics/model/snapshots/PrometheusNames.java
new file mode 100644
index 000000000..1cb74f735
--- /dev/null
+++ b/prometheus-metrics-model/src/main/java/io/prometheus/metrics/model/snapshots/PrometheusNames.java
@@ -0,0 +1,287 @@
+package io.prometheus.metrics.model.snapshots;
+
+import static java.lang.Character.MAX_CODE_POINT;
+import static java.lang.Character.MAX_LOW_SURROGATE;
+import static java.lang.Character.MIN_HIGH_SURROGATE;
+
+import io.prometheus.metrics.config.EscapingScheme;
+import java.nio.charset.StandardCharsets;
+import java.util.regex.Pattern;
+import javax.annotation.Nullable;
+
+/**
+ * Utility for Prometheus Metric and Label naming.
+ *
+ * Note that this library allows dots in metric and label names. Dots will automatically be
+ * replaced with underscores in Prometheus exposition formats. However, if metrics are exposed in
+ * OpenTelemetry format the dots are retained.
+ */
+public class PrometheusNames {
+
+ static final Pattern METRIC_NAME_PATTERN = Pattern.compile("^[a-zA-Z_:][a-zA-Z0-9_:]*$");
+
+ /** Legal characters for label names. */
+ static final Pattern LEGACY_LABEL_NAME_PATTERN = Pattern.compile("^[a-zA-Z_][a-zA-Z0-9_]*$");
+
+ /** Legal characters for unit names, including dot. */
+ private static final Pattern UNIT_NAME_PATTERN = Pattern.compile("^[a-zA-Z0-9_.:]+$");
+
+ /**
+ * According to OpenMetrics {@code _count} and {@code _sum} (and {@code _gcount}, {@code _gsum})
+ * should also be reserved metric name suffixes. However, popular instrumentation libraries have
+ * Gauges with names ending in {@code _count}. Examples:
+ *
+ *
+ * - Micrometer: {@code jvm_buffer_count}
+ *
- OpenTelemetry: {@code process_runtime_jvm_buffer_count}
+ *
+ *
+ * We do not treat {@code _count} and {@code _sum} as reserved suffixes here for compatibility
+ * with these libraries. However, there is a risk of name conflict if someone creates a gauge
+ * named {@code my_data_count} and a histogram or summary named {@code my_data}, because the
+ * histogram or summary will implicitly have a sample named {@code my_data_count}.
+ */
+ static final String[] RESERVED_METRIC_NAME_SUFFIXES = {
+ "_total", "_created", "_bucket", "_info",
+ ".total", ".created", ".bucket", ".info"
+ };
+
+ /**
+ * Test if a metric name is valid. Rules:
+ *
+ *
+ * - The name must match {@link #METRIC_NAME_PATTERN}.
+ *
- The name MUST NOT end with one of the {@link #RESERVED_METRIC_NAME_SUFFIXES}.
+ *
+ *
+ * If a metric has a {@link Unit}, the metric name SHOULD end with the unit as a suffix. Note that
+ * OpenMetrics requires metric names to have their unit as
+ * suffix, and we implement this in {@code prometheus-metrics-core}. However, {@code
+ * prometheus-metrics-model} does not enforce Unit suffixes.
+ *
+ * Example: If you create a Counter for a processing time with Unit {@link Unit#SECONDS
+ * SECONDS}, the name should be {@code processing_time_seconds}. When exposed in OpenMetrics Text
+ * format, this will be represented as two values: {@code processing_time_seconds_total} for the
+ * counter value, and the optional {@code processing_time_seconds_created} timestamp.
+ *
+ *
Use {@link #sanitizeMetricName(String)} to convert arbitrary Strings to valid metric names.
+ */
+ public static boolean isValidMetricName(String name) {
+ return validateMetricName(name) == null;
+ }
+
+ /**
+ * Same as {@link #isValidMetricName(String)}, but produces an error message.
+ *
+ *
The name is valid if the error message is {@code null}.
+ */
+ @Nullable
+ public static String validateMetricName(String name) {
+ String reservedSuffix = findReservedSuffix(name);
+ if (reservedSuffix != null) {
+ return reservedSuffix;
+ }
+ if (isValidUtf8(name)) {
+ return null;
+ }
+ return "The metric name contains unsupported characters";
+ }
+
+ @Nullable
+ static String findReservedSuffix(String name) {
+ for (String reservedSuffix : RESERVED_METRIC_NAME_SUFFIXES) {
+ if (name.endsWith(reservedSuffix)) {
+ return "The metric name must not include the '" + reservedSuffix + "' suffix.";
+ }
+ }
+ return null;
+ }
+
+ public static boolean isValidLegacyMetricName(String name) {
+ return METRIC_NAME_PATTERN.matcher(name).matches();
+ }
+
+ public static boolean isValidLabelName(String name) {
+ return isValidUtf8(name) && !hasInvalidLabelPrefix(name);
+ }
+
+ static boolean hasInvalidLabelPrefix(String name) {
+ return name.startsWith("__")
+ || name.startsWith("._")
+ || name.startsWith("..")
+ || name.startsWith("_.");
+ }
+
+ private static boolean isValidUtf8(String name) {
+ return !name.isEmpty() && StandardCharsets.UTF_8.newEncoder().canEncode(name);
+ }
+
+ public static boolean isValidLegacyLabelName(String name) {
+ return LEGACY_LABEL_NAME_PATTERN.matcher(name).matches();
+ }
+
+ /**
+ * Units may not have illegal characters, and they may not end with a reserved suffix like
+ * 'total'.
+ */
+ public static boolean isValidUnitName(String name) {
+ return validateUnitName(name) == null;
+ }
+
+ /** Same as {@link #isValidUnitName(String)} but returns an error message. */
+ @Nullable
+ public static String validateUnitName(String name) {
+ if (name.isEmpty()) {
+ return "The unit name must not be empty.";
+ }
+ for (String reservedSuffix : RESERVED_METRIC_NAME_SUFFIXES) {
+ String suffixName = reservedSuffix.substring(1);
+ if (name.endsWith(suffixName)) {
+ return suffixName + " is a reserved suffix in Prometheus";
+ }
+ }
+ if (!UNIT_NAME_PATTERN.matcher(name).matches()) {
+ return "The unit name contains unsupported characters";
+ }
+ return null;
+ }
+
+ /**
+ * Get the metric or label name that is used in Prometheus exposition format.
+ *
+ * @param name must be a valid metric or label name, i.e. {@link #isValidMetricName(String)
+ * isValidMetricName(name)} or {@link #isValidLabelName(String) isValidLabelName(name)} must
+ * be true.
+ * @return the name with dots replaced by underscores.
+ */
+ public static String prometheusName(String name) {
+ return NameEscaper.escapeName(name, EscapingScheme.UNDERSCORE_ESCAPING);
+ }
+
+ /**
+ * Convert an arbitrary string to a name where {@link #isValidMetricName(String)
+ * isValidMetricName(name)} is true.
+ */
+ public static String sanitizeMetricName(String metricName) {
+ if (metricName.isEmpty()) {
+ throw new IllegalArgumentException("Cannot convert an empty string to a valid metric name.");
+ }
+ String sanitizedName = metricName;
+ boolean modified = true;
+ while (modified) {
+ modified = false;
+ for (String reservedSuffix : RESERVED_METRIC_NAME_SUFFIXES) {
+ if (sanitizedName.equals(reservedSuffix)) {
+ // This is for the corner case when you call sanitizeMetricName("_total").
+ // In that case the result will be "total".
+ return reservedSuffix.substring(1);
+ }
+ if (sanitizedName.endsWith(reservedSuffix)) {
+ sanitizedName =
+ sanitizedName.substring(0, sanitizedName.length() - reservedSuffix.length());
+ modified = true;
+ }
+ }
+ }
+ return sanitizedName;
+ }
+
+ /**
+ * Like {@link #sanitizeMetricName(String)}, but also makes sure that the unit is appended as a
+ * suffix if the unit is not {@code null}.
+ */
+ public static String sanitizeMetricName(String metricName, Unit unit) {
+ String result = sanitizeMetricName(metricName);
+ if (unit != null) {
+ if (!result.endsWith("_" + unit) && !result.endsWith("." + unit)) {
+ result += "_" + unit;
+ }
+ }
+ return result;
+ }
+
+ /**
+ * Convert an arbitrary string to a name where {@link #isValidLabelName(String)
+ * isValidLabelName(name)} is true.
+ */
+ public static String sanitizeLabelName(String labelName) {
+ if (labelName.isEmpty()) {
+ throw new IllegalArgumentException("Cannot convert an empty string to a valid label name.");
+ }
+ String sanitizedName = labelName;
+ while (hasInvalidLabelPrefix(sanitizedName)) {
+ sanitizedName = sanitizedName.substring(1);
+ }
+ return sanitizedName;
+ }
+
+ /**
+ * Convert an arbitrary string to a name where {@link #validateUnitName(String)} is {@code null}
+ * (i.e. the name is valid).
+ *
+ * @throws IllegalArgumentException if the {@code unitName} cannot be converted, for example if
+ * you call {@code sanitizeUnitName("total")} or {@code sanitizeUnitName("")}.
+ * @throws NullPointerException if {@code unitName} is null.
+ */
+ public static String sanitizeUnitName(String unitName) {
+ if (unitName.isEmpty()) {
+ throw new IllegalArgumentException("Cannot convert an empty string to a valid unit name.");
+ }
+ String sanitizedName = replaceIllegalCharsInUnitName(unitName);
+ boolean modified = true;
+ while (modified) {
+ modified = false;
+ while (sanitizedName.startsWith("_") || sanitizedName.startsWith(".")) {
+ sanitizedName = sanitizedName.substring(1);
+ modified = true;
+ }
+ while (sanitizedName.endsWith(".") || sanitizedName.endsWith("_")) {
+ sanitizedName = sanitizedName.substring(0, sanitizedName.length() - 1);
+ modified = true;
+ }
+ for (String reservedSuffix : RESERVED_METRIC_NAME_SUFFIXES) {
+ String suffixName = reservedSuffix.substring(1);
+ if (sanitizedName.endsWith(suffixName)) {
+ sanitizedName = sanitizedName.substring(0, sanitizedName.length() - suffixName.length());
+ modified = true;
+ }
+ }
+ }
+ if (sanitizedName.isEmpty()) {
+ throw new IllegalArgumentException(
+ "Cannot convert '" + unitName + "' into a valid unit name.");
+ }
+ return sanitizedName;
+ }
+
+ /** Returns a string that matches {@link #UNIT_NAME_PATTERN}. */
+ private static String replaceIllegalCharsInUnitName(String name) {
+ int length = name.length();
+ char[] sanitized = new char[length];
+ for (int i = 0; i < length; i++) {
+ char ch = name.charAt(i);
+ if (ch == ':'
+ || ch == '.'
+ || (ch >= 'a' && ch <= 'z')
+ || (ch >= 'A' && ch <= 'Z')
+ || (ch >= '0' && ch <= '9')) {
+ sanitized[i] = ch;
+ } else {
+ sanitized[i] = '_';
+ }
+ }
+ return new String(sanitized);
+ }
+
+ static boolean isValidLegacyChar(int c, int i) {
+ return (c >= 'a' && c <= 'z')
+ || (c >= 'A' && c <= 'Z')
+ || c == '_'
+ || c == ':'
+ || (c >= '0' && c <= '9' && i > 0);
+ }
+
+ static boolean isValidUtf8Char(int c) {
+ return (0 <= c && c < MIN_HIGH_SURROGATE) || (MAX_LOW_SURROGATE < c && c <= MAX_CODE_POINT);
+ }
+}
diff --git a/prometheus-metrics-model/src/main/java/io/prometheus/metrics/model/snapshots/PrometheusNaming.java b/prometheus-metrics-model/src/main/java/io/prometheus/metrics/model/snapshots/PrometheusNaming.java
index e79b0d4d8..e399adc55 100644
--- a/prometheus-metrics-model/src/main/java/io/prometheus/metrics/model/snapshots/PrometheusNaming.java
+++ b/prometheus-metrics-model/src/main/java/io/prometheus/metrics/model/snapshots/PrometheusNaming.java
@@ -1,12 +1,5 @@
package io.prometheus.metrics.model.snapshots;
-import static java.lang.Character.MAX_CODE_POINT;
-import static java.lang.Character.MAX_LOW_SURROGATE;
-import static java.lang.Character.MIN_HIGH_SURROGATE;
-
-import io.prometheus.metrics.config.EscapingScheme;
-import java.nio.charset.StandardCharsets;
-import java.util.regex.Pattern;
import javax.annotation.Nullable;
/**
@@ -15,44 +8,20 @@
*
Note that this library allows dots in metric and label names. Dots will automatically be
* replaced with underscores in Prometheus exposition formats. However, if metrics are exposed in
* OpenTelemetry format the dots are retained.
+ *
+ * @deprecated use {@link PrometheusNames} instead.
*/
+@Deprecated
+@SuppressWarnings("InlineMeSuggester")
public class PrometheusNaming {
- private static final Pattern METRIC_NAME_PATTERN = Pattern.compile("^[a-zA-Z_:][a-zA-Z0-9_:]*$");
-
- /** Legal characters for label names. */
- private static final Pattern LEGACY_LABEL_NAME_PATTERN =
- Pattern.compile("^[a-zA-Z_][a-zA-Z0-9_]*$");
-
- /** Legal characters for unit names, including dot. */
- private static final Pattern UNIT_NAME_PATTERN = Pattern.compile("^[a-zA-Z0-9_.:]+$");
-
- /**
- * According to OpenMetrics {@code _count} and {@code _sum} (and {@code _gcount}, {@code _gsum})
- * should also be reserved metric name suffixes. However, popular instrumentation libraries have
- * Gauges with names ending in {@code _count}. Examples:
- *
- *
- * - Micrometer: {@code jvm_buffer_count}
- *
- OpenTelemetry: {@code process_runtime_jvm_buffer_count}
- *
- *
- * We do not treat {@code _count} and {@code _sum} as reserved suffixes here for compatibility
- * with these libraries. However, there is a risk of name conflict if someone creates a gauge
- * named {@code my_data_count} and a histogram or summary named {@code my_data}, because the
- * histogram or summary will implicitly have a sample named {@code my_data_count}.
- */
- private static final String[] RESERVED_METRIC_NAME_SUFFIXES = {
- "_total", "_created", "_bucket", "_info",
- ".total", ".created", ".bucket", ".info"
- };
-
/**
* Test if a metric name is valid. Rules:
*
*
- * - The name must match {@link #METRIC_NAME_PATTERN}.
- *
- The name MUST NOT end with one of the {@link #RESERVED_METRIC_NAME_SUFFIXES}.
+ *
- The name must match {@link PrometheusNames#METRIC_NAME_PATTERN}.
+ *
- The name MUST NOT end with one of the {@link
+ * PrometheusNames#RESERVED_METRIC_NAME_SUFFIXES}.
*
*
* If a metric has a {@link Unit}, the metric name SHOULD end with the unit as a suffix. Note that
@@ -66,7 +35,10 @@ public class PrometheusNaming {
* counter value, and the optional {@code processing_time_seconds_created} timestamp.
*
* Use {@link #sanitizeMetricName(String)} to convert arbitrary Strings to valid metric names.
+ *
+ * @deprecated use {@link PrometheusNames#isValidMetricName(String)} instead.
*/
+ @Deprecated
public static boolean isValidMetricName(String name) {
return validateMetricName(name) == null;
}
@@ -75,64 +47,60 @@ public static boolean isValidMetricName(String name) {
* Same as {@link #isValidMetricName(String)}, but produces an error message.
*
*
The name is valid if the error message is {@code null}.
+ *
+ * @deprecated use {@link PrometheusNames#validateMetricName(String)} instead.
*/
+ @Deprecated
@Nullable
public static String validateMetricName(String name) {
- for (String reservedSuffix : RESERVED_METRIC_NAME_SUFFIXES) {
- if (name.endsWith(reservedSuffix)) {
- return "The metric name must not include the '" + reservedSuffix + "' suffix.";
- }
+ String reservedSuffix = PrometheusNames.findReservedSuffix(name);
+ if (reservedSuffix != null) {
+ return reservedSuffix;
}
- if (isValidUtf8(name)) {
- return null;
+ if (!PrometheusNames.isValidLegacyMetricName(name)) {
+ return "The metric name contains unsupported characters";
}
- return "The metric name contains unsupported characters";
- }
-
- public static boolean isValidLegacyMetricName(String name) {
- return METRIC_NAME_PATTERN.matcher(name).matches();
+ return null;
}
+ /**
+ * Test if a label name is valid. Rules:
+ *
+ *
+ * - The name must match {@link PrometheusNames#LEGACY_LABEL_NAME_PATTERN _PATTERN}.
+ *
- The name MUST NOT start with {@code __}, {@code ._}, or {@code _.} or {@code ..}
+ *
+ *
+ * @deprecated use {@link PrometheusNames#isValidLabelName(String)} instead.
+ */
+ @Deprecated
public static boolean isValidLabelName(String name) {
- return isValidUtf8(name)
- && !(name.startsWith("__")
- || name.startsWith("._")
- || name.startsWith("..")
- || name.startsWith("_."));
- }
-
- private static boolean isValidUtf8(String name) {
- return !name.isEmpty() && StandardCharsets.UTF_8.newEncoder().canEncode(name);
- }
-
- public static boolean isValidLegacyLabelName(String name) {
- return LEGACY_LABEL_NAME_PATTERN.matcher(name).matches();
+ return PrometheusNames.isValidLegacyLabelName(name)
+ && !PrometheusNames.hasInvalidLabelPrefix(name);
}
/**
* Units may not have illegal characters, and they may not end with a reserved suffix like
* 'total'.
+ *
+ * @deprecated use {@link PrometheusNames#isValidUnitName(String)} instead.
*/
+ @Deprecated
public static boolean isValidUnitName(String name) {
- return validateUnitName(name) == null;
+ // no Unicode support for unit names
+ return PrometheusNames.isValidUnitName(name);
}
- /** Same as {@link #isValidUnitName(String)} but returns an error message. */
+ /**
+ * Same as {@link #isValidUnitName(String)} but returns an error message.
+ *
+ * @deprecated use {@link PrometheusNames#validateUnitName(String)} instead.
+ */
+ @Deprecated
@Nullable
public static String validateUnitName(String name) {
- if (name.isEmpty()) {
- return "The unit name must not be empty.";
- }
- for (String reservedSuffix : RESERVED_METRIC_NAME_SUFFIXES) {
- String suffixName = reservedSuffix.substring(1);
- if (name.endsWith(suffixName)) {
- return suffixName + " is a reserved suffix in Prometheus";
- }
- }
- if (!UNIT_NAME_PATTERN.matcher(name).matches()) {
- return "The unit name contains unsupported characters";
- }
- return null;
+ // no Unicode support for unit names
+ return PrometheusNames.validateUnitName(name);
}
/**
@@ -142,121 +110,77 @@ public static String validateUnitName(String name) {
* isValidMetricName(name)} or {@link #isValidLabelName(String) isValidLabelName(name)} must
* be true.
* @return the name with dots replaced by underscores.
+ * @deprecated use {@link PrometheusNames#prometheusName(String)} instead.
*/
+ @Deprecated
public static String prometheusName(String name) {
- return escapeName(name, EscapingScheme.UNDERSCORE_ESCAPING);
+ return name.replace(".", "_");
}
/**
* Convert an arbitrary string to a name where {@link #isValidMetricName(String)
* isValidMetricName(name)} is true.
+ *
+ * @deprecated use {@link PrometheusNames#sanitizeMetricName(String)} instead.
*/
+ @Deprecated
public static String sanitizeMetricName(String metricName) {
if (metricName.isEmpty()) {
throw new IllegalArgumentException("Cannot convert an empty string to a valid metric name.");
}
- String sanitizedName = metricName;
- boolean modified = true;
- while (modified) {
- modified = false;
- for (String reservedSuffix : RESERVED_METRIC_NAME_SUFFIXES) {
- if (sanitizedName.equals(reservedSuffix)) {
- // This is for the corner case when you call sanitizeMetricName("_total").
- // In that case the result will be "total".
- return reservedSuffix.substring(1);
- }
- if (sanitizedName.endsWith(reservedSuffix)) {
- sanitizedName =
- sanitizedName.substring(0, sanitizedName.length() - reservedSuffix.length());
- modified = true;
- }
- }
- }
- return sanitizedName;
+ return PrometheusNames.sanitizeMetricName(replaceIllegalCharsInMetricName(metricName));
}
/**
* Like {@link #sanitizeMetricName(String)}, but also makes sure that the unit is appended as a
* suffix if the unit is not {@code null}.
+ *
+ * @deprecated use {@link PrometheusNames#sanitizeMetricName(String, Unit)} instead.
*/
+ @Deprecated
public static String sanitizeMetricName(String metricName, Unit unit) {
- String result = sanitizeMetricName(metricName);
- if (unit != null) {
- if (!result.endsWith("_" + unit) && !result.endsWith("." + unit)) {
- result += "_" + unit;
- }
- }
- return result;
+ return PrometheusNames.sanitizeMetricName(replaceIllegalCharsInMetricName(metricName), unit);
}
/**
* Convert an arbitrary string to a name where {@link #isValidLabelName(String)
* isValidLabelName(name)} is true.
+ *
+ * @deprecated use {@link PrometheusNames#sanitizeLabelName(String)} instead.
*/
+ @Deprecated
public static String sanitizeLabelName(String labelName) {
if (labelName.isEmpty()) {
throw new IllegalArgumentException("Cannot convert an empty string to a valid label name.");
}
- String sanitizedName = labelName;
- while (sanitizedName.startsWith("__")
- || sanitizedName.startsWith("_.")
- || sanitizedName.startsWith("._")
- || sanitizedName.startsWith("..")) {
- sanitizedName = sanitizedName.substring(1);
- }
- return sanitizedName;
+ return PrometheusNames.sanitizeLabelName(replaceIllegalCharsInLabelName(labelName));
}
/**
- * Convert an arbitrary string to a name where {@link #validateUnitName(String)} is {@code null}
- * (i.e. the name is valid).
+ * Convert an arbitrary string to a name where {@link #isValidUnitName(String)
+ * isValidUnitName(name)} is true.
*
* @throws IllegalArgumentException if the {@code unitName} cannot be converted, for example if
* you call {@code sanitizeUnitName("total")} or {@code sanitizeUnitName("")}.
* @throws NullPointerException if {@code unitName} is null.
+ * @deprecated use {@link PrometheusNames#sanitizeUnitName(String)} instead.
*/
+ @Deprecated
public static String sanitizeUnitName(String unitName) {
- if (unitName.isEmpty()) {
- throw new IllegalArgumentException("Cannot convert an empty string to a valid unit name.");
- }
- String sanitizedName = replaceIllegalCharsInUnitName(unitName);
- boolean modified = true;
- while (modified) {
- modified = false;
- while (sanitizedName.startsWith("_") || sanitizedName.startsWith(".")) {
- sanitizedName = sanitizedName.substring(1);
- modified = true;
- }
- while (sanitizedName.endsWith(".") || sanitizedName.endsWith("_")) {
- sanitizedName = sanitizedName.substring(0, sanitizedName.length() - 1);
- modified = true;
- }
- for (String reservedSuffix : RESERVED_METRIC_NAME_SUFFIXES) {
- String suffixName = reservedSuffix.substring(1);
- if (sanitizedName.endsWith(suffixName)) {
- sanitizedName = sanitizedName.substring(0, sanitizedName.length() - suffixName.length());
- modified = true;
- }
- }
- }
- if (sanitizedName.isEmpty()) {
- throw new IllegalArgumentException(
- "Cannot convert '" + unitName + "' into a valid unit name.");
- }
- return sanitizedName;
+ // no Unicode support for unit names
+ return PrometheusNames.sanitizeUnitName(unitName);
}
- /** Returns a string that matches {@link #UNIT_NAME_PATTERN}. */
- private static String replaceIllegalCharsInUnitName(String name) {
+ /** Returns a string that matches {@link PrometheusNames#METRIC_NAME_PATTERN}. */
+ private static String replaceIllegalCharsInMetricName(String name) {
int length = name.length();
char[] sanitized = new char[length];
for (int i = 0; i < length; i++) {
char ch = name.charAt(i);
- if (ch == ':'
- || ch == '.'
+ if (ch == '.'
|| (ch >= 'a' && ch <= 'z')
|| (ch >= 'A' && ch <= 'Z')
- || (ch >= '0' && ch <= '9')) {
+ || (i > 0 && ch >= '0' && ch <= '9')) {
sanitized[i] = ch;
} else {
sanitized[i] = '_';
@@ -265,84 +189,21 @@ private static String replaceIllegalCharsInUnitName(String name) {
return new String(sanitized);
}
- /**
- * Escapes the incoming name according to the provided escaping scheme. Depending on the rules of
- * escaping, this may cause no change in the string that is returned (especially NO_ESCAPING,
- * which by definition is a noop). This method does not do any validation of the name.
- */
- public static String escapeName(String name, EscapingScheme scheme) {
- if (name.isEmpty() || !needsEscaping(name, scheme)) {
- return name;
- }
-
- StringBuilder escaped = new StringBuilder();
- switch (scheme) {
- case ALLOW_UTF8:
- return name;
- case UNDERSCORE_ESCAPING:
- for (int i = 0; i < name.length(); ) {
- int c = name.codePointAt(i);
- if (isValidLegacyChar(c, i)) {
- escaped.appendCodePoint(c);
- } else {
- escaped.append('_');
- }
- i += Character.charCount(c);
- }
- return escaped.toString();
- case DOTS_ESCAPING:
- // Do not early return for legacy valid names, we still escape underscores.
- for (int i = 0; i < name.length(); ) {
- int c = name.codePointAt(i);
- if (c == '_') {
- escaped.append("__");
- } else if (c == '.') {
- escaped.append("_dot_");
- } else if (isValidLegacyChar(c, i)) {
- escaped.appendCodePoint(c);
- } else {
- escaped.append("__");
- }
- i += Character.charCount(c);
- }
- return escaped.toString();
- case VALUE_ENCODING_ESCAPING:
- escaped.append("U__");
- for (int i = 0; i < name.length(); ) {
- int c = name.codePointAt(i);
- if (c == '_') {
- escaped.append("__");
- } else if (isValidLegacyChar(c, i)) {
- escaped.appendCodePoint(c);
- } else if (!isValidUtf8Char(c)) {
- escaped.append("_FFFD_");
- } else {
- escaped.append('_');
- escaped.append(Integer.toHexString(c));
- escaped.append('_');
- }
- i += Character.charCount(c);
- }
- return escaped.toString();
- default:
- throw new IllegalArgumentException("Invalid escaping scheme " + scheme);
+ /** Returns a string that matches {@link PrometheusNames#LEGACY_LABEL_NAME_PATTERN}. */
+ private static String replaceIllegalCharsInLabelName(String name) {
+ int length = name.length();
+ char[] sanitized = new char[length];
+ for (int i = 0; i < length; i++) {
+ char ch = name.charAt(i);
+ if (ch == '.'
+ || (ch >= 'a' && ch <= 'z')
+ || (ch >= 'A' && ch <= 'Z')
+ || (i > 0 && ch >= '0' && ch <= '9')) {
+ sanitized[i] = ch;
+ } else {
+ sanitized[i] = '_';
+ }
}
- }
-
- public static boolean needsEscaping(String name, EscapingScheme scheme) {
- return !isValidLegacyMetricName(name)
- || (scheme == EscapingScheme.DOTS_ESCAPING && (name.contains(".") || name.contains("_")));
- }
-
- static boolean isValidLegacyChar(int c, int i) {
- return (c >= 'a' && c <= 'z')
- || (c >= 'A' && c <= 'Z')
- || c == '_'
- || c == ':'
- || (c >= '0' && c <= '9' && i > 0);
- }
-
- private static boolean isValidUtf8Char(int c) {
- return (0 <= c && c < MIN_HIGH_SURROGATE) || (MAX_LOW_SURROGATE < c && c <= MAX_CODE_POINT);
+ return new String(sanitized);
}
}
diff --git a/prometheus-metrics-model/src/main/java/io/prometheus/metrics/model/snapshots/SnapshotEscaper.java b/prometheus-metrics-model/src/main/java/io/prometheus/metrics/model/snapshots/SnapshotEscaper.java
index 422b36ee0..df8c2e862 100644
--- a/prometheus-metrics-model/src/main/java/io/prometheus/metrics/model/snapshots/SnapshotEscaper.java
+++ b/prometheus-metrics-model/src/main/java/io/prometheus/metrics/model/snapshots/SnapshotEscaper.java
@@ -60,7 +60,7 @@ static boolean snapshotNeedsEscaping(DataPointSnapshot d, EscapingScheme scheme)
private static boolean labelsNeedsEscaping(Labels labels, EscapingScheme scheme) {
for (Label l : labels) {
- if (PrometheusNaming.needsEscaping(l.getName(), scheme)) {
+ if (NameEscaper.needsEscaping(l.getName(), scheme)) {
return true;
}
}
@@ -100,7 +100,7 @@ public static Labels escapeLabels(Labels labels, EscapingScheme scheme) {
Labels.Builder outLabelsBuilder = Labels.builder();
for (Label l : labels) {
- outLabelsBuilder.label(PrometheusNaming.escapeName(l.getName(), scheme), l.getValue());
+ outLabelsBuilder.label(NameEscaper.escapeName(l.getName(), scheme), l.getValue());
}
return outLabelsBuilder.build();
diff --git a/prometheus-metrics-model/src/main/java/io/prometheus/metrics/model/snapshots/Unit.java b/prometheus-metrics-model/src/main/java/io/prometheus/metrics/model/snapshots/Unit.java
index 31a9524e7..5fca57dc1 100644
--- a/prometheus-metrics-model/src/main/java/io/prometheus/metrics/model/snapshots/Unit.java
+++ b/prometheus-metrics-model/src/main/java/io/prometheus/metrics/model/snapshots/Unit.java
@@ -31,7 +31,7 @@ public Unit(String name) {
throw new NullPointerException("Unit name cannot be null.");
}
name = name.trim();
- String error = PrometheusNaming.validateUnitName(name);
+ String error = PrometheusNames.validateUnitName(name);
if (error != null) {
throw new IllegalArgumentException(name + ": Illegal unit name: " + error);
}
diff --git a/prometheus-metrics-model/src/test/java/io/prometheus/metrics/model/snapshots/MetricMetadataTest.java b/prometheus-metrics-model/src/test/java/io/prometheus/metrics/model/snapshots/MetricMetadataTest.java
index f2d6a6ba4..9607a8adb 100644
--- a/prometheus-metrics-model/src/test/java/io/prometheus/metrics/model/snapshots/MetricMetadataTest.java
+++ b/prometheus-metrics-model/src/test/java/io/prometheus/metrics/model/snapshots/MetricMetadataTest.java
@@ -1,6 +1,6 @@
package io.prometheus.metrics.model.snapshots;
-import static io.prometheus.metrics.model.snapshots.PrometheusNaming.sanitizeMetricName;
+import static io.prometheus.metrics.model.snapshots.PrometheusNames.sanitizeMetricName;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
diff --git a/prometheus-metrics-model/src/test/java/io/prometheus/metrics/model/snapshots/NameEscaperTest.java b/prometheus-metrics-model/src/test/java/io/prometheus/metrics/model/snapshots/NameEscaperTest.java
new file mode 100644
index 000000000..95199c6be
--- /dev/null
+++ b/prometheus-metrics-model/src/test/java/io/prometheus/metrics/model/snapshots/NameEscaperTest.java
@@ -0,0 +1,77 @@
+package io.prometheus.metrics.model.snapshots;
+
+import static io.prometheus.metrics.model.snapshots.NameEscaper.escapeName;
+import static org.assertj.core.api.Assertions.assertThat;
+
+import io.prometheus.metrics.config.EscapingScheme;
+import java.util.stream.Stream;
+import org.junit.jupiter.params.ParameterizedTest;
+import org.junit.jupiter.params.provider.Arguments;
+import org.junit.jupiter.params.provider.MethodSource;
+
+class NameEscaperTest {
+ @ParameterizedTest
+ @MethodSource("escapeNameLegacyTestCases")
+ public void testEscapeName(String input, EscapingScheme escapingScheme, String expected) {
+ assertThat(escapeName(input, escapingScheme)).isEqualTo(expected);
+ }
+
+ static Stream escapeNameLegacyTestCases() {
+ return Stream.of(
+ Arguments.of("", EscapingScheme.UNDERSCORE_ESCAPING, ""),
+ Arguments.of("", EscapingScheme.DOTS_ESCAPING, ""),
+ Arguments.of("", EscapingScheme.VALUE_ENCODING_ESCAPING, ""),
+ Arguments.of(
+ "no:escaping_required", EscapingScheme.UNDERSCORE_ESCAPING, "no:escaping_required"),
+ // Dots escaping will escape underscores even though it's not strictly
+ // necessary for compatibility.
+ Arguments.of("no:escaping_required", EscapingScheme.DOTS_ESCAPING, "no:escaping__required"),
+ Arguments.of(
+ "no:escaping_required", EscapingScheme.VALUE_ENCODING_ESCAPING, "no:escaping_required"),
+ Arguments.of(
+ "no:escaping_required", EscapingScheme.UNDERSCORE_ESCAPING, "no:escaping_required"),
+ Arguments.of(
+ "mysystem.prod.west.cpu.load",
+ EscapingScheme.DOTS_ESCAPING,
+ "mysystem_dot_prod_dot_west_dot_cpu_dot_load"),
+ Arguments.of(
+ "mysystem.prod.west.cpu.load_total",
+ EscapingScheme.DOTS_ESCAPING,
+ "mysystem_dot_prod_dot_west_dot_cpu_dot_load__total"),
+ Arguments.of("http.status:sum", EscapingScheme.DOTS_ESCAPING, "http_dot_status:sum"),
+ Arguments.of("label with 😱", EscapingScheme.UNDERSCORE_ESCAPING, "label_with__"),
+ Arguments.of("label with 😱", EscapingScheme.DOTS_ESCAPING, "label__with____"),
+ Arguments.of(
+ "label with 😱", EscapingScheme.VALUE_ENCODING_ESCAPING, "U__label_20_with_20__1f631_"),
+ // name with unicode characters > 0x100
+ Arguments.of("花火", EscapingScheme.UNDERSCORE_ESCAPING, "__"),
+ // Dots-replacement does not know the difference between two replaced
+ Arguments.of("花火", EscapingScheme.DOTS_ESCAPING, "____"),
+ Arguments.of("花火", EscapingScheme.VALUE_ENCODING_ESCAPING, "U___82b1__706b_"),
+ // name with spaces and edge-case value
+ Arguments.of("label with Ā", EscapingScheme.UNDERSCORE_ESCAPING, "label_with__"),
+ Arguments.of("label with Ā", EscapingScheme.DOTS_ESCAPING, "label__with____"),
+ Arguments.of(
+ "label with Ā", EscapingScheme.VALUE_ENCODING_ESCAPING, "U__label_20_with_20__100_"),
+ // name with dots - needs UTF-8 validation for escaping to occur
+ Arguments.of(
+ "mysystem.prod.west.cpu.load",
+ EscapingScheme.UNDERSCORE_ESCAPING,
+ "mysystem_prod_west_cpu_load"),
+ Arguments.of(
+ "mysystem.prod.west.cpu.load",
+ EscapingScheme.VALUE_ENCODING_ESCAPING,
+ "U__mysystem_2e_prod_2e_west_2e_cpu_2e_load"),
+ Arguments.of(
+ "mysystem.prod.west.cpu.load_total",
+ EscapingScheme.UNDERSCORE_ESCAPING,
+ "mysystem_prod_west_cpu_load_total"),
+ Arguments.of(
+ "mysystem.prod.west.cpu.load_total",
+ EscapingScheme.VALUE_ENCODING_ESCAPING,
+ "U__mysystem_2e_prod_2e_west_2e_cpu_2e_load__total"),
+ Arguments.of("http.status:sum", EscapingScheme.UNDERSCORE_ESCAPING, "http_status:sum"),
+ Arguments.of(
+ "http.status:sum", EscapingScheme.VALUE_ENCODING_ESCAPING, "U__http_2e_status:sum"));
+ }
+}
diff --git a/prometheus-metrics-model/src/test/java/io/prometheus/metrics/model/snapshots/PrometheusNamesTest.java b/prometheus-metrics-model/src/test/java/io/prometheus/metrics/model/snapshots/PrometheusNamesTest.java
new file mode 100644
index 000000000..38fbc1d5c
--- /dev/null
+++ b/prometheus-metrics-model/src/test/java/io/prometheus/metrics/model/snapshots/PrometheusNamesTest.java
@@ -0,0 +1,154 @@
+package io.prometheus.metrics.model.snapshots;
+
+import static io.prometheus.metrics.model.snapshots.PrometheusNames.isValidLabelName;
+import static io.prometheus.metrics.model.snapshots.PrometheusNames.prometheusName;
+import static io.prometheus.metrics.model.snapshots.PrometheusNames.sanitizeLabelName;
+import static io.prometheus.metrics.model.snapshots.PrometheusNames.sanitizeMetricName;
+import static io.prometheus.metrics.model.snapshots.PrometheusNames.sanitizeUnitName;
+import static io.prometheus.metrics.model.snapshots.PrometheusNames.validateMetricName;
+import static io.prometheus.metrics.model.snapshots.PrometheusNames.validateUnitName;
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
+
+import java.util.stream.Stream;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.params.ParameterizedTest;
+import org.junit.jupiter.params.provider.Arguments;
+import org.junit.jupiter.params.provider.MethodSource;
+
+class PrometheusNamesTest {
+
+ @Test
+ void testSanitizeMetricName() {
+ assertThat(sanitizeMetricName("my_counter_total")).isEqualTo("my_counter");
+ assertThat(sanitizeMetricName("jvm.info")).isEqualTo("jvm");
+ assertThat(sanitizeMetricName("jvm_info")).isEqualTo("jvm");
+ assertThat(sanitizeMetricName("jvm.info")).isEqualTo("jvm");
+ assertThat(sanitizeMetricName("a.b")).isEqualTo("a.b");
+ assertThat(sanitizeMetricName("_total")).isEqualTo("total");
+ assertThat(sanitizeMetricName("total")).isEqualTo("total");
+ }
+
+ @Test
+ public void testSanitizeMetricNameWithUnit() {
+ assertThat(prometheusName(sanitizeMetricName("def", Unit.RATIO)))
+ .isEqualTo("def_" + Unit.RATIO);
+ assertThat(prometheusName(sanitizeMetricName("my_counter_total", Unit.RATIO)))
+ .isEqualTo("my_counter_" + Unit.RATIO);
+ assertThat(sanitizeMetricName("jvm.info", Unit.RATIO)).isEqualTo("jvm_" + Unit.RATIO);
+ assertThat(sanitizeMetricName("_total", Unit.RATIO)).isEqualTo("total_" + Unit.RATIO);
+ assertThat(sanitizeMetricName("total", Unit.RATIO)).isEqualTo("total_" + Unit.RATIO);
+ }
+
+ @Test
+ public void testSanitizeLabelName() {
+ assertThat(prometheusName(sanitizeLabelName("0abc.def"))).isEqualTo("_abc_def");
+ assertThat(prometheusName(sanitizeLabelName("_abc"))).isEqualTo("_abc");
+ assertThat(prometheusName(sanitizeLabelName("__abc"))).isEqualTo("_abc");
+ assertThat(prometheusName(sanitizeLabelName("___abc"))).isEqualTo("_abc");
+ assertThat(prometheusName(sanitizeLabelName("_.abc"))).isEqualTo("_abc");
+ assertThat(sanitizeLabelName("abc.def")).isEqualTo("abc.def");
+ assertThat(sanitizeLabelName("abc.def2")).isEqualTo("abc.def2");
+ }
+
+ @Test
+ public void testValidateUnitName() {
+ assertThat(validateUnitName("secondstotal")).isNotNull();
+ assertThat(validateUnitName("total")).isNotNull();
+ assertThat(validateUnitName("seconds_total")).isNotNull();
+ assertThat(validateUnitName("_total")).isNotNull();
+ assertThat(validateUnitName("")).isNotNull();
+
+ assertThat(validateUnitName("seconds")).isNull();
+ assertThat(validateUnitName("2")).isNull();
+ }
+
+ @Test
+ public void testSanitizeUnitName() {
+ assertThat(sanitizeUnitName("seconds")).isEqualTo("seconds");
+ assertThat(sanitizeUnitName("seconds_total")).isEqualTo("seconds");
+ assertThat(sanitizeUnitName("seconds_total_total")).isEqualTo("seconds");
+ assertThat(sanitizeUnitName("m/s")).isEqualTo("m_s");
+ assertThat(sanitizeUnitName("secondstotal")).isEqualTo("seconds");
+ assertThat(sanitizeUnitName("2")).isEqualTo("2");
+ }
+
+ @Test
+ public void testInvalidUnitName1() {
+ assertThatExceptionOfType(IllegalArgumentException.class)
+ .isThrownBy(() -> sanitizeUnitName("total"));
+ }
+
+ @Test
+ public void testInvalidUnitName2() {
+ assertThatExceptionOfType(IllegalArgumentException.class)
+ .isThrownBy(() -> sanitizeUnitName("_total"));
+ }
+
+ @Test
+ public void testInvalidUnitName3() {
+ assertThatExceptionOfType(IllegalArgumentException.class)
+ .isThrownBy(() -> sanitizeUnitName("%"));
+ }
+
+ @Test
+ public void testEmptyUnitName() {
+ assertThatExceptionOfType(IllegalArgumentException.class)
+ .isThrownBy(() -> sanitizeUnitName(""));
+ }
+
+ @ParameterizedTest
+ @MethodSource("nameIsValid")
+ public void testLabelNameIsValidUtf8(String labelName, boolean utf8Valid) {
+ assertMetricName(labelName, utf8Valid);
+ assertLabelName(labelName, utf8Valid);
+ }
+
+ private static void assertLabelName(String labelName, boolean legacyValid) {
+ assertThat(isValidLabelName(labelName))
+ .describedAs("isValidLabelName(%s)", labelName)
+ .isEqualTo(legacyValid);
+ }
+
+ private static void assertMetricName(String labelName, boolean valid) {
+ assertThat(validateMetricName(labelName))
+ .describedAs("validateMetricName(%s)", labelName)
+ .isEqualTo(valid ? null : "The metric name contains unsupported characters");
+ }
+
+ static Stream nameIsValid() {
+ return Stream.of(
+ Arguments.of("", false),
+ Arguments.of("Avalid_23name", true),
+ Arguments.of("_Avalid_23name", true),
+ Arguments.of("1valid_23name", true),
+ Arguments.of("avalid_23name", true),
+ Arguments.of("Ava:lid_23name", true),
+ Arguments.of("a lid_23name", true),
+ Arguments.of(":leading_colon", true),
+ Arguments.of("colon:in:the:middle", true),
+ Arguments.of("aΩz", true),
+ Arguments.of("a\ud800z", false));
+ }
+
+ @Test
+ void testValidMetricName() {
+ assertThat(PrometheusNames.isValidMetricName("valid_metric_name")).isTrue();
+ assertThat(PrometheusNames.isValidMetricName("invalid_metric_name_total")).isFalse();
+ assertThat(PrometheusNames.isValidMetricName("0abc.def")).isTrue();
+ }
+
+ @Test
+ void testValidLabelName() {
+ assertThat(PrometheusNames.isValidLabelName("valid_label_name")).isTrue();
+ assertThat(PrometheusNames.isValidLabelName("0invalid_label_name")).isTrue();
+ assertThat(PrometheusNames.isValidLabelName("invalid-label-name")).isTrue();
+ }
+
+ @Test
+ void testValidUnitName() {
+ assertThat(PrometheusNames.isValidUnitName("seconds")).isTrue();
+ assertThat(PrometheusNames.isValidUnitName("seconds_total")).isFalse();
+ assertThat(PrometheusNames.isValidUnitName("2")).isTrue();
+ }
+}
diff --git a/prometheus-metrics-model/src/test/java/io/prometheus/metrics/model/snapshots/PrometheusNamingTest.java b/prometheus-metrics-model/src/test/java/io/prometheus/metrics/model/snapshots/PrometheusNamingTest.java
index 40c1f1bde..3c60ece29 100644
--- a/prometheus-metrics-model/src/test/java/io/prometheus/metrics/model/snapshots/PrometheusNamingTest.java
+++ b/prometheus-metrics-model/src/test/java/io/prometheus/metrics/model/snapshots/PrometheusNamingTest.java
@@ -1,200 +1,143 @@
package io.prometheus.metrics.model.snapshots;
-import static io.prometheus.metrics.model.snapshots.PrometheusNaming.escapeName;
-import static io.prometheus.metrics.model.snapshots.PrometheusNaming.isValidLabelName;
-import static io.prometheus.metrics.model.snapshots.PrometheusNaming.prometheusName;
-import static io.prometheus.metrics.model.snapshots.PrometheusNaming.sanitizeLabelName;
-import static io.prometheus.metrics.model.snapshots.PrometheusNaming.sanitizeMetricName;
-import static io.prometheus.metrics.model.snapshots.PrometheusNaming.sanitizeUnitName;
-import static io.prometheus.metrics.model.snapshots.PrometheusNaming.validateMetricName;
-import static io.prometheus.metrics.model.snapshots.PrometheusNaming.validateUnitName;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
-import io.prometheus.metrics.config.EscapingScheme;
-import java.util.stream.Stream;
import org.junit.jupiter.api.Test;
-import org.junit.jupiter.params.ParameterizedTest;
-import org.junit.jupiter.params.provider.Arguments;
-import org.junit.jupiter.params.provider.MethodSource;
+@SuppressWarnings("deprecation")
class PrometheusNamingTest {
@Test
public void testSanitizeMetricName() {
- assertThat(sanitizeMetricName("my_counter_total")).isEqualTo("my_counter");
- assertThat(sanitizeMetricName("jvm.info")).isEqualTo("jvm");
- assertThat(sanitizeMetricName("jvm_info")).isEqualTo("jvm");
- assertThat(sanitizeMetricName("jvm.info")).isEqualTo("jvm");
- assertThat(sanitizeMetricName("a.b")).isEqualTo("a.b");
- assertThat(sanitizeMetricName("_total")).isEqualTo("total");
- assertThat(sanitizeMetricName("total")).isEqualTo("total");
+ assertThat(PrometheusNaming.prometheusName(PrometheusNaming.sanitizeMetricName("0abc.def")))
+ .isEqualTo("_abc_def");
+ assertThat(PrometheusNaming.prometheusName(PrometheusNaming.sanitizeMetricName("___ab.:c0")))
+ .isEqualTo("___ab__c0");
+ assertThat(PrometheusNaming.sanitizeMetricName("my_prefix/my_metric"))
+ .isEqualTo("my_prefix_my_metric");
+ assertThat(
+ PrometheusNaming.prometheusName(
+ PrometheusNaming.sanitizeMetricName("my_counter_total")))
+ .isEqualTo("my_counter");
+ assertThat(PrometheusNaming.sanitizeMetricName("jvm.info")).isEqualTo("jvm");
+ assertThat(PrometheusNaming.sanitizeMetricName("jvm_info")).isEqualTo("jvm");
+ assertThat(PrometheusNaming.sanitizeMetricName("jvm.info")).isEqualTo("jvm");
+ assertThat(PrometheusNaming.sanitizeMetricName("a.b")).isEqualTo("a.b");
+ assertThat(PrometheusNaming.sanitizeMetricName("_total")).isEqualTo("total");
+ assertThat(PrometheusNaming.sanitizeMetricName("total")).isEqualTo("total");
}
@Test
public void testSanitizeMetricNameWithUnit() {
- assertThat(prometheusName(sanitizeMetricName("def", Unit.RATIO)))
- .isEqualTo("def_" + Unit.RATIO);
- assertThat(prometheusName(sanitizeMetricName("my_counter_total", Unit.RATIO)))
+ assertThat(
+ PrometheusNaming.prometheusName(
+ PrometheusNaming.sanitizeMetricName("0abc.def", Unit.RATIO)))
+ .isEqualTo("_abc_def_" + Unit.RATIO);
+ assertThat(
+ PrometheusNaming.prometheusName(
+ PrometheusNaming.sanitizeMetricName("___ab.:c0", Unit.RATIO)))
+ .isEqualTo("___ab__c0_" + Unit.RATIO);
+ assertThat(PrometheusNaming.sanitizeMetricName("my_prefix/my_metric", Unit.RATIO))
+ .isEqualTo("my_prefix_my_metric_" + Unit.RATIO);
+ assertThat(
+ PrometheusNaming.prometheusName(
+ PrometheusNaming.sanitizeMetricName("my_counter_total", Unit.RATIO)))
.isEqualTo("my_counter_" + Unit.RATIO);
- assertThat(sanitizeMetricName("jvm.info", Unit.RATIO)).isEqualTo("jvm_" + Unit.RATIO);
- assertThat(sanitizeMetricName("_total", Unit.RATIO)).isEqualTo("total_" + Unit.RATIO);
- assertThat(sanitizeMetricName("total", Unit.RATIO)).isEqualTo("total_" + Unit.RATIO);
+ assertThat(PrometheusNaming.sanitizeMetricName("jvm.info", Unit.RATIO))
+ .isEqualTo("jvm_" + Unit.RATIO);
+ assertThat(PrometheusNaming.sanitizeMetricName("jvm_info", Unit.RATIO))
+ .isEqualTo("jvm_" + Unit.RATIO);
+ assertThat(PrometheusNaming.sanitizeMetricName("jvm.info", Unit.RATIO))
+ .isEqualTo("jvm_" + Unit.RATIO);
+ assertThat(PrometheusNaming.sanitizeMetricName("a.b", Unit.RATIO))
+ .isEqualTo("a.b_" + Unit.RATIO);
+ assertThat(PrometheusNaming.sanitizeMetricName("_total", Unit.RATIO))
+ .isEqualTo("total_" + Unit.RATIO);
+ assertThat(PrometheusNaming.sanitizeMetricName("total", Unit.RATIO))
+ .isEqualTo("total_" + Unit.RATIO);
}
@Test
public void testSanitizeLabelName() {
- assertThat(prometheusName(sanitizeLabelName("0abc.def"))).isEqualTo("_abc_def");
- assertThat(prometheusName(sanitizeLabelName("_abc"))).isEqualTo("_abc");
- assertThat(prometheusName(sanitizeLabelName("__abc"))).isEqualTo("_abc");
- assertThat(prometheusName(sanitizeLabelName("___abc"))).isEqualTo("_abc");
- assertThat(prometheusName(sanitizeLabelName("_.abc"))).isEqualTo("_abc");
- assertThat(sanitizeLabelName("abc.def")).isEqualTo("abc.def");
- assertThat(sanitizeLabelName("abc.def2")).isEqualTo("abc.def2");
+ assertThat(PrometheusNaming.prometheusName(PrometheusNaming.sanitizeLabelName("0abc.def")))
+ .isEqualTo("_abc_def");
+ assertThat(PrometheusNaming.prometheusName(PrometheusNaming.sanitizeLabelName("_abc")))
+ .isEqualTo("_abc");
+ assertThat(PrometheusNaming.prometheusName(PrometheusNaming.sanitizeLabelName("__abc")))
+ .isEqualTo("_abc");
+ assertThat(PrometheusNaming.prometheusName(PrometheusNaming.sanitizeLabelName("___abc")))
+ .isEqualTo("_abc");
+ assertThat(PrometheusNaming.prometheusName(PrometheusNaming.sanitizeLabelName("_.abc")))
+ .isEqualTo("_abc");
+ assertThat(PrometheusNaming.sanitizeLabelName("abc.def")).isEqualTo("abc.def");
+ assertThat(PrometheusNaming.sanitizeLabelName("abc.def2")).isEqualTo("abc.def2");
}
@Test
public void testValidateUnitName() {
- assertThat(validateUnitName("secondstotal")).isNotNull();
- assertThat(validateUnitName("total")).isNotNull();
- assertThat(validateUnitName("seconds_total")).isNotNull();
- assertThat(validateUnitName("_total")).isNotNull();
- assertThat(validateUnitName("")).isNotNull();
-
- assertThat(validateUnitName("seconds")).isNull();
- assertThat(validateUnitName("2")).isNull();
+ assertThat(PrometheusNaming.validateUnitName("secondstotal")).isNotNull();
+ assertThat(PrometheusNaming.validateUnitName("total")).isNotNull();
+ assertThat(PrometheusNaming.validateUnitName("seconds_total")).isNotNull();
+ assertThat(PrometheusNaming.validateUnitName("_total")).isNotNull();
+ assertThat(PrometheusNaming.validateUnitName("")).isNotNull();
+
+ assertThat(PrometheusNaming.validateUnitName("seconds")).isNull();
+ assertThat(PrometheusNaming.validateUnitName("2")).isNull();
}
@Test
public void testSanitizeUnitName() {
- assertThat(sanitizeUnitName("seconds")).isEqualTo("seconds");
- assertThat(sanitizeUnitName("seconds_total")).isEqualTo("seconds");
- assertThat(sanitizeUnitName("seconds_total_total")).isEqualTo("seconds");
- assertThat(sanitizeUnitName("m/s")).isEqualTo("m_s");
- assertThat(sanitizeUnitName("secondstotal")).isEqualTo("seconds");
- assertThat(sanitizeUnitName("2")).isEqualTo("2");
+ assertThat(PrometheusNaming.sanitizeUnitName("seconds")).isEqualTo("seconds");
+ assertThat(PrometheusNaming.sanitizeUnitName("seconds_total")).isEqualTo("seconds");
+ assertThat(PrometheusNaming.sanitizeUnitName("seconds_total_total")).isEqualTo("seconds");
+ assertThat(PrometheusNaming.sanitizeUnitName("m/s")).isEqualTo("m_s");
+ assertThat(PrometheusNaming.sanitizeUnitName("secondstotal")).isEqualTo("seconds");
+ assertThat(PrometheusNaming.sanitizeUnitName("2")).isEqualTo("2");
}
@Test
public void testInvalidUnitName1() {
assertThatExceptionOfType(IllegalArgumentException.class)
- .isThrownBy(() -> sanitizeUnitName("total"));
+ .isThrownBy(() -> PrometheusNaming.sanitizeUnitName("total"));
}
@Test
public void testInvalidUnitName2() {
assertThatExceptionOfType(IllegalArgumentException.class)
- .isThrownBy(() -> sanitizeUnitName("_total"));
+ .isThrownBy(() -> PrometheusNaming.sanitizeUnitName("_total"));
}
@Test
public void testInvalidUnitName3() {
assertThatExceptionOfType(IllegalArgumentException.class)
- .isThrownBy(() -> sanitizeUnitName("%"));
+ .isThrownBy(() -> PrometheusNaming.sanitizeUnitName("%"));
}
@Test
public void testEmptyUnitName() {
assertThatExceptionOfType(IllegalArgumentException.class)
- .isThrownBy(() -> sanitizeUnitName(""));
+ .isThrownBy(() -> PrometheusNaming.sanitizeUnitName(""));
}
- @ParameterizedTest
- @MethodSource("nameIsValid")
- public void testLabelNameIsValidUtf8(String labelName, boolean utf8Valid) {
- assertMetricName(labelName, utf8Valid);
- assertLabelName(labelName, utf8Valid);
- }
-
- private static void assertLabelName(String labelName, boolean legacyValid) {
- assertThat(isValidLabelName(labelName))
- .describedAs("isValidLabelName(%s)", labelName)
- .isEqualTo(legacyValid);
- }
-
- private static void assertMetricName(String labelName, boolean valid) {
- assertThat(validateMetricName(labelName))
- .describedAs("validateMetricName(%s)", labelName)
- .isEqualTo(valid ? null : "The metric name contains unsupported characters");
- }
-
- static Stream nameIsValid() {
- return Stream.of(
- Arguments.of("", false),
- Arguments.of("Avalid_23name", true),
- Arguments.of("_Avalid_23name", true),
- Arguments.of("1valid_23name", true),
- Arguments.of("avalid_23name", true),
- Arguments.of("Ava:lid_23name", true),
- Arguments.of("a lid_23name", true),
- Arguments.of(":leading_colon", true),
- Arguments.of("colon:in:the:middle", true),
- Arguments.of("aΩz", true),
- Arguments.of("a\ud800z", false));
+ @Test
+ void testValidMetricName() {
+ assertThat(PrometheusNaming.isValidMetricName("valid_metric_name")).isTrue();
+ assertThat(PrometheusNaming.isValidMetricName("invalid_metric_name_total")).isFalse();
+ assertThat(PrometheusNaming.isValidMetricName("0abc.def")).isFalse();
}
- @ParameterizedTest
- @MethodSource("escapeNameLegacyTestCases")
- public void testEscapeName(String input, EscapingScheme escapingScheme, String expected) {
- assertThat(escapeName(input, escapingScheme)).isEqualTo(expected);
+ @Test
+ void testValidLabelName() {
+ assertThat(PrometheusNaming.isValidLabelName("valid_label_name")).isTrue();
+ assertThat(PrometheusNaming.isValidLabelName("0invalid_label_name")).isFalse();
+ assertThat(PrometheusNaming.isValidLabelName("invalid-label-name")).isFalse();
}
- static Stream escapeNameLegacyTestCases() {
- return Stream.of(
- Arguments.of("", EscapingScheme.UNDERSCORE_ESCAPING, ""),
- Arguments.of("", EscapingScheme.DOTS_ESCAPING, ""),
- Arguments.of("", EscapingScheme.VALUE_ENCODING_ESCAPING, ""),
- Arguments.of(
- "no:escaping_required", EscapingScheme.UNDERSCORE_ESCAPING, "no:escaping_required"),
- // Dots escaping will escape underscores even though it's not strictly
- // necessary for compatibility.
- Arguments.of("no:escaping_required", EscapingScheme.DOTS_ESCAPING, "no:escaping__required"),
- Arguments.of(
- "no:escaping_required", EscapingScheme.VALUE_ENCODING_ESCAPING, "no:escaping_required"),
- Arguments.of(
- "no:escaping_required", EscapingScheme.UNDERSCORE_ESCAPING, "no:escaping_required"),
- Arguments.of(
- "mysystem.prod.west.cpu.load",
- EscapingScheme.DOTS_ESCAPING,
- "mysystem_dot_prod_dot_west_dot_cpu_dot_load"),
- Arguments.of(
- "mysystem.prod.west.cpu.load_total",
- EscapingScheme.DOTS_ESCAPING,
- "mysystem_dot_prod_dot_west_dot_cpu_dot_load__total"),
- Arguments.of("http.status:sum", EscapingScheme.DOTS_ESCAPING, "http_dot_status:sum"),
- Arguments.of("label with 😱", EscapingScheme.UNDERSCORE_ESCAPING, "label_with__"),
- Arguments.of("label with 😱", EscapingScheme.DOTS_ESCAPING, "label__with____"),
- Arguments.of(
- "label with 😱", EscapingScheme.VALUE_ENCODING_ESCAPING, "U__label_20_with_20__1f631_"),
- // name with unicode characters > 0x100
- Arguments.of("花火", EscapingScheme.UNDERSCORE_ESCAPING, "__"),
- // Dots-replacement does not know the difference between two replaced
- Arguments.of("花火", EscapingScheme.DOTS_ESCAPING, "____"),
- Arguments.of("花火", EscapingScheme.VALUE_ENCODING_ESCAPING, "U___82b1__706b_"),
- // name with spaces and edge-case value
- Arguments.of("label with Ā", EscapingScheme.UNDERSCORE_ESCAPING, "label_with__"),
- Arguments.of("label with Ā", EscapingScheme.DOTS_ESCAPING, "label__with____"),
- Arguments.of(
- "label with Ā", EscapingScheme.VALUE_ENCODING_ESCAPING, "U__label_20_with_20__100_"),
- // name with dots - needs UTF-8 validation for escaping to occur
- Arguments.of(
- "mysystem.prod.west.cpu.load",
- EscapingScheme.UNDERSCORE_ESCAPING,
- "mysystem_prod_west_cpu_load"),
- Arguments.of(
- "mysystem.prod.west.cpu.load",
- EscapingScheme.VALUE_ENCODING_ESCAPING,
- "U__mysystem_2e_prod_2e_west_2e_cpu_2e_load"),
- Arguments.of(
- "mysystem.prod.west.cpu.load_total",
- EscapingScheme.UNDERSCORE_ESCAPING,
- "mysystem_prod_west_cpu_load_total"),
- Arguments.of(
- "mysystem.prod.west.cpu.load_total",
- EscapingScheme.VALUE_ENCODING_ESCAPING,
- "U__mysystem_2e_prod_2e_west_2e_cpu_2e_load__total"),
- Arguments.of("http.status:sum", EscapingScheme.UNDERSCORE_ESCAPING, "http_status:sum"),
- Arguments.of(
- "http.status:sum", EscapingScheme.VALUE_ENCODING_ESCAPING, "U__http_2e_status:sum"));
+ @Test
+ void testValidUnitName() {
+ assertThat(PrometheusNaming.isValidUnitName("seconds")).isTrue();
+ assertThat(PrometheusNaming.isValidUnitName("seconds_total")).isFalse();
+ assertThat(PrometheusNaming.isValidUnitName("2")).isTrue();
}
}
diff --git a/prometheus-metrics-simpleclient-bridge/src/main/java/io/prometheus/metrics/simpleclient/bridge/SimpleclientCollector.java b/prometheus-metrics-simpleclient-bridge/src/main/java/io/prometheus/metrics/simpleclient/bridge/SimpleclientCollector.java
index 3a96453e7..61f13d602 100644
--- a/prometheus-metrics-simpleclient-bridge/src/main/java/io/prometheus/metrics/simpleclient/bridge/SimpleclientCollector.java
+++ b/prometheus-metrics-simpleclient-bridge/src/main/java/io/prometheus/metrics/simpleclient/bridge/SimpleclientCollector.java
@@ -1,6 +1,6 @@
package io.prometheus.metrics.simpleclient.bridge;
-import static io.prometheus.metrics.model.snapshots.PrometheusNaming.sanitizeMetricName;
+import static io.prometheus.metrics.model.snapshots.PrometheusNames.sanitizeMetricName;
import static java.util.Objects.requireNonNull;
import io.prometheus.client.Collector;