diff --git a/benchmarks/src/main/java/io/prometheus/client/benchmark/SanitizeMetricNameBenchmark.java b/benchmarks/src/main/java/io/prometheus/client/benchmark/SanitizeMetricNameBenchmark.java new file mode 100644 index 000000000..d89e676e5 --- /dev/null +++ b/benchmarks/src/main/java/io/prometheus/client/benchmark/SanitizeMetricNameBenchmark.java @@ -0,0 +1,53 @@ +package io.prometheus.client.benchmark; + +import com.codahale.metrics.MetricRegistry; +import org.openjdk.jmh.annotations.Benchmark; +import org.openjdk.jmh.annotations.BenchmarkMode; +import org.openjdk.jmh.annotations.Mode; +import org.openjdk.jmh.annotations.OutputTimeUnit; +import org.openjdk.jmh.annotations.Scope; +import org.openjdk.jmh.annotations.Setup; +import org.openjdk.jmh.annotations.State; +import org.openjdk.jmh.runner.Runner; +import org.openjdk.jmh.runner.RunnerException; +import org.openjdk.jmh.runner.options.Options; +import org.openjdk.jmh.runner.options.OptionsBuilder; +import org.openjdk.jmh.runner.options.TimeValue; + +import io.prometheus.client.Collector; + +import java.util.Random; +import java.util.concurrent.TimeUnit; + +@State(Scope.Benchmark) +public class SanitizeMetricNameBenchmark { + + @Benchmark + @BenchmarkMode({ Mode.AverageTime }) + @OutputTimeUnit(TimeUnit.NANOSECONDS) + public void sanitizeSanitizedName() { + Collector.sanitizeMetricName("good_name"); + } + + @Benchmark + @BenchmarkMode({ Mode.AverageTime }) + @OutputTimeUnit(TimeUnit.NANOSECONDS) + public void sanitizeNonSanitizedName() { + Collector.sanitizeMetricName("9not_good_name!"); + } + + public static void main(String[] args) throws RunnerException { + + Options opt = new OptionsBuilder() + .include(SanitizeMetricNameBenchmark.class.getSimpleName()) + .warmupIterations(5) + .measurementIterations(4) + .measurementTime(TimeValue.seconds(1)) + .warmupTime(TimeValue.seconds(1)) + .threads(4) + .forks(1) + .build(); + + new Runner(opt).run(); + } +} diff --git a/simpleclient/src/main/java/io/prometheus/client/Collector.java b/simpleclient/src/main/java/io/prometheus/client/Collector.java index 3a69500dc..6f84e1840 100644 --- a/simpleclient/src/main/java/io/prometheus/client/Collector.java +++ b/simpleclient/src/main/java/io/prometheus/client/Collector.java @@ -352,16 +352,24 @@ protected static void checkMetricName(String name) { } } - private static final Pattern SANITIZE_PREFIX_PATTERN = Pattern.compile("^[^a-zA-Z_:]"); - private static final Pattern SANITIZE_BODY_PATTERN = Pattern.compile("[^a-zA-Z0-9_:]"); - /** * Sanitize metric name */ public static String sanitizeMetricName(String metricName) { - return SANITIZE_BODY_PATTERN.matcher( - SANITIZE_PREFIX_PATTERN.matcher(metricName).replaceFirst("_") - ).replaceAll("_"); + int length = metricName.length(); + char[] sanitized = new char[length]; + for(int i = 0; i < length; i++) { + char ch = metricName.charAt(i); + if(ch == ':' || + (ch >= 'a' && ch <= 'z') || + (ch >= 'A' && ch <= 'Z') || + (i > 0 && ch >= '0' && ch <= '9')) { + sanitized[i] = ch; + } else { + sanitized[i] = '_'; + } + } + return new String(sanitized); } /** diff --git a/simpleclient/src/test/java/io/prometheus/client/CollectorTest.java b/simpleclient/src/test/java/io/prometheus/client/CollectorTest.java index dbf611322..73603a37c 100644 --- a/simpleclient/src/test/java/io/prometheus/client/CollectorTest.java +++ b/simpleclient/src/test/java/io/prometheus/client/CollectorTest.java @@ -9,6 +9,32 @@ import static org.junit.Assert.*; public class CollectorTest { + + @Test + public void sanitizeMetricPrefix() throws Exception { + assertEquals("afoo", Collector.sanitizeMetricName("afoo")); + assertEquals("zfoo", Collector.sanitizeMetricName("zfoo")); + assertEquals("Afoo", Collector.sanitizeMetricName("Afoo")); + assertEquals("Zfoo", Collector.sanitizeMetricName("Zfoo")); + assertEquals(":foo", Collector.sanitizeMetricName(":foo")); + + assertEquals("_foo", Collector.sanitizeMetricName("0foo")); + assertEquals("_foo", Collector.sanitizeMetricName("5foo")); + assertEquals("_foo", Collector.sanitizeMetricName("9foo")); + assertEquals("_foo", Collector.sanitizeMetricName("/foo")); + assertEquals("_foo", Collector.sanitizeMetricName("*foo")); + } + + @Test + public void sanitizeMetricBody() throws Exception { + assertEquals("aamzAMZ059", Collector.sanitizeMetricName("aamzAMZ059")); + assertEquals("aaMzAmZ009", Collector.sanitizeMetricName("aaMzAmZ009")); + assertEquals("aZmA950aMz", Collector.sanitizeMetricName("aZmA950aMz")); + assertEquals("aZ9mA0a5Mz", Collector.sanitizeMetricName("aZ9mA0a5Mz")); + assertEquals("aZ9mA_0a5Mz", Collector.sanitizeMetricName("aZ9mA*0a5Mz")); + assertEquals("aZ9mA_0a5Mz", Collector.sanitizeMetricName("aZ9mA&0a5Mz")); + } + @Test public void sanitizeMetricName() throws Exception { assertEquals("_hoge", Collector.sanitizeMetricName("0hoge"));