diff --git a/prometheus-metrics-exposition-textformats/src/main/java/io/prometheus/metrics/expositionformats/OpenMetrics2TextFormatWriter.java b/prometheus-metrics-exposition-textformats/src/main/java/io/prometheus/metrics/expositionformats/OpenMetrics2TextFormatWriter.java index f400ecce0..396bb0025 100644 --- a/prometheus-metrics-exposition-textformats/src/main/java/io/prometheus/metrics/expositionformats/OpenMetrics2TextFormatWriter.java +++ b/prometheus-metrics-exposition-textformats/src/main/java/io/prometheus/metrics/expositionformats/OpenMetrics2TextFormatWriter.java @@ -176,9 +176,10 @@ private void writeCounter(Writer writer, CounterSnapshot snapshot, EscapingSchem MetricMetadata metadata = snapshot.getMetadata(); // OM2: use the original name, no _total or unit suffix appending. String counterName = getOriginalMetadataName(metadata, scheme); + boolean nameValidLegacy = PrometheusNaming.isValidLegacyMetricName(counterName); writeMetadataWithName(writer, counterName, "counter", metadata); for (CounterSnapshot.CounterDataPointSnapshot data : snapshot.getDataPoints()) { - writeNameAndLabels(writer, counterName, null, data.getLabels(), scheme); + writeNameAndLabels(writer, counterName, null, data.getLabels(), scheme, nameValidLegacy); writeDouble(writer, data.getValue()); if (data.hasScrapeTimestamp()) { writer.write(' '); @@ -197,9 +198,10 @@ private void writeGauge(Writer writer, GaugeSnapshot snapshot, EscapingScheme sc throws IOException { MetricMetadata metadata = snapshot.getMetadata(); String name = getOriginalMetadataName(metadata, scheme); + boolean nameValidLegacy = PrometheusNaming.isValidLegacyMetricName(name); writeMetadataWithName(writer, name, "gauge", metadata); for (GaugeSnapshot.GaugeDataPointSnapshot data : snapshot.getDataPoints()) { - writeNameAndLabels(writer, name, null, data.getLabels(), scheme); + writeNameAndLabels(writer, name, null, data.getLabels(), scheme, nameValidLegacy); writeDouble(writer, data.getValue()); if (exemplarsOnAllMetricTypesEnabled) { writeScrapeTimestampAndExemplar(writer, data, data.getExemplar(), scheme); @@ -215,40 +217,51 @@ private void writeHistogram(Writer writer, HistogramSnapshot snapshot, EscapingS openMetrics2Properties.getCompositeValues() || openMetrics2Properties.getNativeHistograms(); MetricMetadata metadata = snapshot.getMetadata(); String name = getOriginalMetadataName(metadata, scheme); + boolean nameValidLegacy = PrometheusNaming.isValidLegacyMetricName(name); if (!compositeHistogram && !openMetrics2Properties.getExemplarCompliance()) { - writeClassicHistogram(writer, name, snapshot, scheme); + writeClassicHistogram(writer, name, snapshot, scheme, nameValidLegacy); return; } if (snapshot.isGaugeHistogram()) { writeMetadataWithName(writer, name, "gaugehistogram", metadata); for (HistogramSnapshot.HistogramDataPointSnapshot data : snapshot.getDataPoints()) { if (openMetrics2Properties.getNativeHistograms() && data.hasNativeHistogramData()) { - writeNativeHistogramDataPoint(writer, name, "gcount", "gsum", data, scheme, false); + writeNativeHistogramDataPoint( + writer, name, "gcount", "gsum", data, scheme, false, nameValidLegacy); } else { - writeCompositeHistogramDataPoint(writer, name, "gcount", "gsum", data, scheme, false); + writeCompositeHistogramDataPoint( + writer, name, "gcount", "gsum", data, scheme, false, nameValidLegacy); } } } else { writeMetadataWithName(writer, name, "histogram", metadata); for (HistogramSnapshot.HistogramDataPointSnapshot data : snapshot.getDataPoints()) { if (openMetrics2Properties.getNativeHistograms() && data.hasNativeHistogramData()) { - writeNativeHistogramDataPoint(writer, name, "count", "sum", data, scheme, true); + writeNativeHistogramDataPoint( + writer, name, "count", "sum", data, scheme, true, nameValidLegacy); } else { - writeCompositeHistogramDataPoint(writer, name, "count", "sum", data, scheme, true); + writeCompositeHistogramDataPoint( + writer, name, "count", "sum", data, scheme, true, nameValidLegacy); } } } } private void writeClassicHistogram( - Writer writer, String name, HistogramSnapshot snapshot, EscapingScheme scheme) + Writer writer, + String name, + HistogramSnapshot snapshot, + EscapingScheme scheme, + boolean nameValidLegacy) throws IOException { if (snapshot.isGaugeHistogram()) { writeMetadataWithName(writer, name, "gaugehistogram", snapshot.getMetadata()); - writeClassicHistogramDataPoints(writer, name, "_gcount", "_gsum", snapshot, scheme); + writeClassicHistogramDataPoints( + writer, name, "_gcount", "_gsum", snapshot, scheme, nameValidLegacy); } else { writeMetadataWithName(writer, name, "histogram", snapshot.getMetadata()); - writeClassicHistogramDataPoints(writer, name, "_count", "_sum", snapshot, scheme); + writeClassicHistogramDataPoints( + writer, name, "_count", "_sum", snapshot, scheme, nameValidLegacy); } } @@ -258,7 +271,8 @@ private void writeClassicHistogramDataPoints( String countSuffix, String sumSuffix, HistogramSnapshot snapshot, - EscapingScheme scheme) + EscapingScheme scheme, + boolean nameValidLegacy) throws IOException { String bucketName = name + "_bucket"; for (HistogramSnapshot.HistogramDataPointSnapshot data : snapshot.getDataPoints()) { @@ -268,7 +282,14 @@ private void writeClassicHistogramDataPoints( for (int i = 0; i < buckets.size(); i++) { cumulativeCount += buckets.getCount(i); writeNameAndLabels( - writer, bucketName, null, data.getLabels(), scheme, "le", buckets.getUpperBound(i)); + writer, + bucketName, + null, + data.getLabels(), + scheme, + "le", + buckets.getUpperBound(i), + nameValidLegacy); writeLong(writer, cumulativeCount); Exemplar exemplar; if (i == 0) { @@ -279,9 +300,10 @@ private void writeClassicHistogramDataPoints( writeScrapeTimestampAndExemplar(writer, data, exemplar, scheme); } if (data.hasCount() && data.hasSum()) { - writeClassicCountAndSum(writer, name, data, countSuffix, sumSuffix, exemplars, scheme); + writeClassicCountAndSum( + writer, name, data, countSuffix, sumSuffix, exemplars, scheme, nameValidLegacy); } - writeClassicCreated(writer, name, data, scheme); + writeClassicCreated(writer, name, data, scheme, nameValidLegacy); } } @@ -292,16 +314,17 @@ private void writeClassicCountAndSum( String countSuffix, String sumSuffix, Exemplars exemplars, - EscapingScheme scheme) + EscapingScheme scheme, + boolean nameValidLegacy) throws IOException { - writeNameAndLabels(writer, name, countSuffix, data.getLabels(), scheme); + writeNameAndLabels(writer, name, countSuffix, data.getLabels(), scheme, nameValidLegacy); writeLong(writer, data.getCount()); if (exemplarsOnAllMetricTypesEnabled) { writeScrapeTimestampAndExemplar(writer, data, exemplars.getLatest(), scheme); } else { writeScrapeTimestampAndExemplar(writer, data, null, scheme); } - writeNameAndLabels(writer, name, sumSuffix, data.getLabels(), scheme); + writeNameAndLabels(writer, name, sumSuffix, data.getLabels(), scheme, nameValidLegacy); writeDouble(writer, data.getSum()); writeScrapeTimestampAndExemplar(writer, data, null, scheme); } @@ -310,10 +333,11 @@ private void writeClassicCreated( Writer writer, String name, HistogramSnapshot.HistogramDataPointSnapshot data, - EscapingScheme scheme) + EscapingScheme scheme, + boolean nameValidLegacy) throws IOException { if (createdTimestampsEnabled && data.hasCreatedTimestamp()) { - writeNameAndLabels(writer, name, "_created", data.getLabels(), scheme); + writeNameAndLabels(writer, name, "_created", data.getLabels(), scheme, nameValidLegacy); writeOpenMetricsTimestamp(writer, data.getCreatedTimestampMillis()); if (data.hasScrapeTimestamp()) { writer.write(' '); @@ -330,9 +354,10 @@ private void writeCompositeHistogramDataPoint( String sumKey, HistogramSnapshot.HistogramDataPointSnapshot data, EscapingScheme scheme, - boolean includeStartTimestamp) + boolean includeStartTimestamp, + boolean nameValidLegacy) throws IOException { - writeNameAndLabels(writer, name, null, data.getLabels(), scheme); + writeNameAndLabels(writer, name, null, data.getLabels(), scheme, nameValidLegacy); writer.write('{'); writer.write(countKey); writer.write(':'); @@ -362,9 +387,10 @@ private void writeNativeHistogramDataPoint( String sumKey, HistogramSnapshot.HistogramDataPointSnapshot data, EscapingScheme scheme, - boolean includeStartTimestamp) + boolean includeStartTimestamp, + boolean nameValidLegacy) throws IOException { - writeNameAndLabels(writer, name, null, data.getLabels(), scheme); + writeNameAndLabels(writer, name, null, data.getLabels(), scheme, nameValidLegacy); writer.write('{'); writer.write(countKey); writer.write(':'); @@ -486,6 +512,7 @@ private void writeSummary(Writer writer, SummarySnapshot snapshot, EscapingSchem boolean metadataWritten = false; MetricMetadata metadata = snapshot.getMetadata(); String name = getOriginalMetadataName(metadata, scheme); + boolean nameValidLegacy = PrometheusNaming.isValidLegacyMetricName(name); for (SummarySnapshot.SummaryDataPointSnapshot data : snapshot.getDataPoints()) { if (data.getQuantiles().size() == 0 && !data.hasCount() && !data.hasSum()) { continue; @@ -494,7 +521,7 @@ private void writeSummary(Writer writer, SummarySnapshot snapshot, EscapingSchem writeMetadataWithName(writer, name, "summary", metadata); metadataWritten = true; } - writeCompositeSummaryDataPoint(writer, name, data, scheme); + writeCompositeSummaryDataPoint(writer, name, data, scheme, nameValidLegacy); } } @@ -502,9 +529,10 @@ private void writeCompositeSummaryDataPoint( Writer writer, String name, SummarySnapshot.SummaryDataPointSnapshot data, - EscapingScheme scheme) + EscapingScheme scheme, + boolean nameValidLegacy) throws IOException { - writeNameAndLabels(writer, name, null, data.getLabels(), scheme); + writeNameAndLabels(writer, name, null, data.getLabels(), scheme, nameValidLegacy); writer.write('{'); boolean first = true; if (data.hasCount()) { @@ -553,9 +581,10 @@ private void writeInfo(Writer writer, InfoSnapshot snapshot, EscapingScheme sche // OM2 spec: Info MetricFamily name MUST end in _info. // In OM2, TYPE/HELP use the same name as the data lines. String infoName = ensureSuffix(getOriginalMetadataName(metadata, scheme), "_info"); + boolean nameValidLegacy = PrometheusNaming.isValidLegacyMetricName(infoName); writeMetadataWithName(writer, infoName, "info", metadata); for (InfoSnapshot.InfoDataPointSnapshot data : snapshot.getDataPoints()) { - writeNameAndLabels(writer, infoName, null, data.getLabels(), scheme); + writeNameAndLabels(writer, infoName, null, data.getLabels(), scheme, nameValidLegacy); writer.write("1"); writeScrapeTimestampAndExemplar(writer, data, null, scheme); } @@ -601,9 +630,10 @@ private void writeUnknown(Writer writer, UnknownSnapshot snapshot, EscapingSchem throws IOException { MetricMetadata metadata = snapshot.getMetadata(); String name = getOriginalMetadataName(metadata, scheme); + boolean nameValidLegacy = PrometheusNaming.isValidLegacyMetricName(name); writeMetadataWithName(writer, name, "unknown", metadata); for (UnknownSnapshot.UnknownDataPointSnapshot data : snapshot.getDataPoints()) { - writeNameAndLabels(writer, name, null, data.getLabels(), scheme); + writeNameAndLabels(writer, name, null, data.getLabels(), scheme, nameValidLegacy); writeDouble(writer, data.getValue()); if (exemplarsOnAllMetricTypesEnabled) { writeScrapeTimestampAndExemplar(writer, data, data.getExemplar(), scheme); @@ -618,9 +648,10 @@ private void writeNameAndLabels( String name, @Nullable String suffix, Labels labels, - EscapingScheme escapingScheme) + EscapingScheme escapingScheme, + boolean nameValidLegacy) throws IOException { - writeNameAndLabels(writer, name, suffix, labels, escapingScheme, null, 0.0); + writeNameAndLabels(writer, name, suffix, labels, escapingScheme, null, 0.0, nameValidLegacy); } private void writeNameAndLabels( @@ -630,16 +661,19 @@ private void writeNameAndLabels( Labels labels, EscapingScheme escapingScheme, @Nullable String additionalLabelName, - double additionalLabelValue) + double additionalLabelValue, + boolean nameValidLegacy) throws IOException { 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 the name does not pass the legacy validity check, we must put the metric name inside the + // braces. Validity is scanned once per family by the caller (the metric name is constant across + // all data points), not re-scanned for every series. A suffix built from legacy characters + // (_total, _bucket, ...) does not change legacy validity, so the base-name check applies here. + if (!nameValidLegacy) { metricInsideBraces = true; writer.write('{'); } - writeName(writer, suffix != null ? name + suffix : name, NameType.Metric); + writeName(writer, suffix != null ? name + suffix : name, NameType.Metric, nameValidLegacy); if (!labels.isEmpty() || additionalLabelName != null) { writeLabels( writer, 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 02614a561..c553634f0 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 @@ -146,12 +146,13 @@ private void writeCounter(Writer writer, CounterSnapshot snapshot, EscapingSchem MetricMetadata metadata = snapshot.getMetadata(); String counterName = resolveExpositionName(metadata, "_total", scheme); String baseName = resolveBaseName(counterName, "_total"); + boolean nameValidLegacy = PrometheusNaming.isValidLegacyMetricName(counterName); writeMetadataWithName(writer, baseName, "counter", metadata); for (CounterSnapshot.CounterDataPointSnapshot data : snapshot.getDataPoints()) { - writeNameAndLabels(writer, counterName, null, data.getLabels(), scheme); + writeNameAndLabels(writer, counterName, null, data.getLabels(), scheme, nameValidLegacy); writeDouble(writer, data.getValue()); writeScrapeTimestampAndExemplar(writer, data, data.getExemplar(), scheme); - writeCreated(writer, baseName, data, scheme); + writeCreated(writer, baseName, data, scheme, nameValidLegacy); } } @@ -160,8 +161,9 @@ private void writeGauge(Writer writer, GaugeSnapshot snapshot, EscapingScheme sc MetricMetadata metadata = snapshot.getMetadata(); writeMetadata(writer, "gauge", metadata, scheme); String name = getMetadataName(metadata, scheme); + boolean nameValidLegacy = PrometheusNaming.isValidLegacyMetricName(name); for (GaugeSnapshot.GaugeDataPointSnapshot data : snapshot.getDataPoints()) { - writeNameAndLabels(writer, name, null, data.getLabels(), scheme); + writeNameAndLabels(writer, name, null, data.getLabels(), scheme, nameValidLegacy); writeDouble(writer, data.getValue()); if (exemplarsOnAllMetricTypesEnabled) { writeScrapeTimestampAndExemplar(writer, data, data.getExemplar(), scheme); @@ -197,6 +199,7 @@ private void writeClassicHistogramBuckets( String bucketName = name + "_bucket"; String countName = name + countSuffix; String sumName = name + sumSuffix; + boolean nameValidLegacy = PrometheusNaming.isValidLegacyMetricName(name); for (HistogramSnapshot.HistogramDataPointSnapshot data : dataList) { ClassicHistogramBuckets buckets = getClassicBuckets(data); Exemplars exemplars = data.getExemplars(); @@ -204,7 +207,14 @@ private void writeClassicHistogramBuckets( for (int i = 0; i < buckets.size(); i++) { cumulativeCount += buckets.getCount(i); writeNameAndLabels( - writer, bucketName, null, data.getLabels(), scheme, "le", buckets.getUpperBound(i)); + writer, + bucketName, + null, + data.getLabels(), + scheme, + "le", + buckets.getUpperBound(i), + nameValidLegacy); writeLong(writer, cumulativeCount); Exemplar exemplar; if (i == 0) { @@ -216,9 +226,9 @@ private void writeClassicHistogramBuckets( } // In OpenMetrics format, histogram _count and _sum are either both present or both absent. if (data.hasCount() && data.hasSum()) { - writeCountAndSum(writer, countName, sumName, data, exemplars, scheme); + writeCountAndSum(writer, countName, sumName, data, exemplars, scheme, nameValidLegacy); } - writeCreated(writer, name, data, scheme); + writeCreated(writer, name, data, scheme, nameValidLegacy); } } @@ -239,6 +249,7 @@ void writeSummary(Writer writer, SummarySnapshot snapshot, EscapingScheme scheme String name = getMetadataName(metadata, scheme); String countName = name + "_count"; String sumName = name + "_sum"; + boolean nameValidLegacy = PrometheusNaming.isValidLegacyMetricName(name); for (SummarySnapshot.SummaryDataPointSnapshot data : snapshot.getDataPoints()) { if (data.getQuantiles().size() == 0 && !data.hasCount() && !data.hasSum()) { continue; @@ -256,7 +267,14 @@ void writeSummary(Writer writer, SummarySnapshot snapshot, EscapingScheme scheme int exemplarIndex = 1; for (Quantile quantile : data.getQuantiles()) { writeNameAndLabels( - writer, name, null, data.getLabels(), scheme, "quantile", quantile.getQuantile()); + writer, + name, + null, + data.getLabels(), + scheme, + "quantile", + quantile.getQuantile(), + nameValidLegacy); writeDouble(writer, quantile.getValue()); if (exemplars.size() > 0 && exemplarsOnAllMetricTypesEnabled) { exemplarIndex = (exemplarIndex + 1) % exemplars.size(); @@ -266,8 +284,8 @@ void writeSummary(Writer writer, SummarySnapshot snapshot, EscapingScheme scheme } } // Unlike histograms, summaries can have only a count or only a sum according to OpenMetrics. - writeCountAndSum(writer, countName, sumName, data, exemplars, scheme); - writeCreated(writer, name, data, scheme); + writeCountAndSum(writer, countName, sumName, data, exemplars, scheme, nameValidLegacy); + writeCreated(writer, name, data, scheme, nameValidLegacy); } } @@ -276,9 +294,10 @@ private void writeInfo(Writer writer, InfoSnapshot snapshot, EscapingScheme sche MetricMetadata metadata = snapshot.getMetadata(); String infoName = resolveExpositionName(metadata, "_info", scheme); String baseName = resolveBaseName(infoName, "_info"); + boolean nameValidLegacy = PrometheusNaming.isValidLegacyMetricName(infoName); writeMetadataWithName(writer, baseName, "info", metadata); for (InfoSnapshot.InfoDataPointSnapshot data : snapshot.getDataPoints()) { - writeNameAndLabels(writer, infoName, null, data.getLabels(), scheme); + writeNameAndLabels(writer, infoName, null, data.getLabels(), scheme, nameValidLegacy); writer.write("1"); writeScrapeTimestampAndExemplar(writer, data, null, scheme); } @@ -325,8 +344,9 @@ private void writeUnknown(Writer writer, UnknownSnapshot snapshot, EscapingSchem MetricMetadata metadata = snapshot.getMetadata(); writeMetadata(writer, "unknown", metadata, scheme); String name = getMetadataName(metadata, scheme); + boolean nameValidLegacy = PrometheusNaming.isValidLegacyMetricName(name); for (UnknownSnapshot.UnknownDataPointSnapshot data : snapshot.getDataPoints()) { - writeNameAndLabels(writer, name, null, data.getLabels(), scheme); + writeNameAndLabels(writer, name, null, data.getLabels(), scheme, nameValidLegacy); writeDouble(writer, data.getValue()); if (exemplarsOnAllMetricTypesEnabled) { writeScrapeTimestampAndExemplar(writer, data, data.getExemplar(), scheme); @@ -342,10 +362,11 @@ private void writeCountAndSum( String sumName, DistributionDataPointSnapshot data, Exemplars exemplars, - EscapingScheme scheme) + EscapingScheme scheme, + boolean nameValidLegacy) throws IOException { if (data.hasCount()) { - writeNameAndLabels(writer, countName, null, data.getLabels(), scheme); + writeNameAndLabels(writer, countName, null, data.getLabels(), scheme, nameValidLegacy); writeLong(writer, data.getCount()); if (exemplarsOnAllMetricTypesEnabled) { writeScrapeTimestampAndExemplar(writer, data, exemplars.getLatest(), scheme); @@ -354,17 +375,21 @@ private void writeCountAndSum( } } if (data.hasSum()) { - writeNameAndLabels(writer, sumName, null, data.getLabels(), scheme); + writeNameAndLabels(writer, sumName, null, data.getLabels(), scheme, nameValidLegacy); writeDouble(writer, data.getSum()); writeScrapeTimestampAndExemplar(writer, data, null, scheme); } } private void writeCreated( - Writer writer, String baseName, DataPointSnapshot data, EscapingScheme scheme) + Writer writer, + String baseName, + DataPointSnapshot data, + EscapingScheme scheme, + boolean nameValidLegacy) throws IOException { if (createdTimestampsEnabled && data.hasCreatedTimestamp()) { - writeNameAndLabels(writer, baseName, "_created", data.getLabels(), scheme); + writeNameAndLabels(writer, baseName, "_created", data.getLabels(), scheme, nameValidLegacy); writeOpenMetricsTimestamp(writer, data.getCreatedTimestampMillis()); if (data.hasScrapeTimestamp()) { writer.write(' '); @@ -379,9 +404,10 @@ private void writeNameAndLabels( String name, @Nullable String suffix, Labels labels, - EscapingScheme escapingScheme) + EscapingScheme escapingScheme, + boolean nameValidLegacy) throws IOException { - writeNameAndLabels(writer, name, suffix, labels, escapingScheme, null, 0.0); + writeNameAndLabels(writer, name, suffix, labels, escapingScheme, null, 0.0, nameValidLegacy); } private void writeNameAndLabels( @@ -391,16 +417,19 @@ private void writeNameAndLabels( Labels labels, EscapingScheme escapingScheme, @Nullable String additionalLabelName, - double additionalLabelValue) + double additionalLabelValue, + boolean nameValidLegacy) throws IOException { 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 the name does not pass the legacy validity check, we must put the metric name inside the + // braces. Validity is scanned once per family by the caller (the metric name is constant across + // all data points), not re-scanned for every series. A suffix built from legacy characters + // (_total, _bucket, ...) does not change legacy validity, so the base-name check applies here. + if (!nameValidLegacy) { metricInsideBraces = true; writer.write('{'); } - writeName(writer, suffix != null ? name + suffix : name, NameType.Metric); + writeName(writer, suffix != null ? name + suffix : name, NameType.Metric, nameValidLegacy); if (!labels.isEmpty() || additionalLabelName != null) { writeLabels( writer, 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 f94d710ba..ab5ed4c14 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 @@ -168,13 +168,14 @@ public void writeCreated(Writer writer, MetricSnapshot snapshot, EscapingScheme if (snapshot instanceof CounterSnapshot) { baseName = resolveBaseName(resolveExpositionName(metadata, "_total", scheme), "_total"); } + boolean nameValidLegacy = PrometheusNaming.isValidLegacyMetricName(baseName); for (DataPointSnapshot data : snapshot.getDataPoints()) { if (data.hasCreatedTimestamp()) { if (!metadataWritten) { writeMetadataWithFullName(writer, baseName + "_created", "gauge", metadata); metadataWritten = true; } - writeNameAndLabels(writer, baseName, "_created", data.getLabels(), scheme); + writeNameAndLabels(writer, baseName, "_created", data.getLabels(), scheme, nameValidLegacy); writePrometheusTimestamp(writer, data.getCreatedTimestampMillis(), timestampsInMs); writeScrapeTimestampAndNewline(writer, data); } @@ -186,9 +187,10 @@ private void writeCounter(Writer writer, CounterSnapshot snapshot, EscapingSchem if (!snapshot.getDataPoints().isEmpty()) { MetricMetadata metadata = snapshot.getMetadata(); String counterName = resolveExpositionName(metadata, "_total", scheme); + boolean nameValidLegacy = PrometheusNaming.isValidLegacyMetricName(counterName); writeMetadataWithFullName(writer, counterName, "counter", metadata); for (CounterSnapshot.CounterDataPointSnapshot data : snapshot.getDataPoints()) { - writeNameAndLabels(writer, counterName, null, data.getLabels(), scheme); + writeNameAndLabels(writer, counterName, null, data.getLabels(), scheme, nameValidLegacy); writeDouble(writer, data.getValue()); writeScrapeTimestampAndNewline(writer, data); } @@ -200,9 +202,10 @@ private void writeGauge( throws IOException { MetricMetadata metadata = snapshot.getMetadata(); String gaugeName = getLegacyGaugeName(metadata, rawOriginalName, scheme); + boolean nameValidLegacy = PrometheusNaming.isValidLegacyMetricName(gaugeName); writeMetadataWithFullName(writer, gaugeName, "gauge", metadata); for (GaugeSnapshot.GaugeDataPointSnapshot data : snapshot.getDataPoints()) { - writeNameAndLabels(writer, gaugeName, null, data.getLabels(), scheme); + writeNameAndLabels(writer, gaugeName, null, data.getLabels(), scheme, nameValidLegacy); writeDouble(writer, data.getValue()); writeScrapeTimestampAndNewline(writer, data); } @@ -216,24 +219,32 @@ private void writeHistogram(Writer writer, HistogramSnapshot snapshot, EscapingS String bucketName = name + "_bucket"; String countName = name + "_count"; String sumName = name + "_sum"; + boolean nameValidLegacy = PrometheusNaming.isValidLegacyMetricName(name); for (HistogramSnapshot.HistogramDataPointSnapshot data : snapshot.getDataPoints()) { ClassicHistogramBuckets buckets = getClassicBuckets(data); long cumulativeCount = 0; for (int i = 0; i < buckets.size(); i++) { cumulativeCount += buckets.getCount(i); writeNameAndLabels( - writer, bucketName, null, data.getLabels(), scheme, "le", buckets.getUpperBound(i)); + writer, + bucketName, + null, + data.getLabels(), + scheme, + "le", + buckets.getUpperBound(i), + nameValidLegacy); writeLong(writer, cumulativeCount); writeScrapeTimestampAndNewline(writer, data); } if (!snapshot.isGaugeHistogram()) { if (data.hasCount()) { - writeNameAndLabels(writer, countName, null, data.getLabels(), scheme); + writeNameAndLabels(writer, countName, null, data.getLabels(), scheme, nameValidLegacy); writeLong(writer, data.getCount()); writeScrapeTimestampAndNewline(writer, data); } if (data.hasSum()) { - writeNameAndLabels(writer, sumName, null, data.getLabels(), scheme); + writeNameAndLabels(writer, sumName, null, data.getLabels(), scheme, nameValidLegacy); writeDouble(writer, data.getSum()); writeScrapeTimestampAndNewline(writer, data); } @@ -262,6 +273,7 @@ private void writeGaugeCountSum( String baseName = getMetadataName(metadata, scheme); String gaugeCountName = baseName + "_gcount"; String gaugeSumName = baseName + "_gsum"; + boolean nameValidLegacy = PrometheusNaming.isValidLegacyMetricName(baseName); boolean metadataWritten = false; for (HistogramSnapshot.HistogramDataPointSnapshot data : snapshot.getDataPoints()) { if (data.hasCount()) { @@ -269,7 +281,7 @@ private void writeGaugeCountSum( writeMetadata(writer, "_gcount", "gauge", metadata, scheme); metadataWritten = true; } - writeNameAndLabels(writer, gaugeCountName, null, data.getLabels(), scheme); + writeNameAndLabels(writer, gaugeCountName, null, data.getLabels(), scheme, nameValidLegacy); writeLong(writer, data.getCount()); writeScrapeTimestampAndNewline(writer, data); } @@ -281,7 +293,7 @@ private void writeGaugeCountSum( writeMetadata(writer, "_gsum", "gauge", metadata, scheme); metadataWritten = true; } - writeNameAndLabels(writer, gaugeSumName, null, data.getLabels(), scheme); + writeNameAndLabels(writer, gaugeSumName, null, data.getLabels(), scheme, nameValidLegacy); writeDouble(writer, data.getSum()); writeScrapeTimestampAndNewline(writer, data); } @@ -295,6 +307,7 @@ private void writeSummary(Writer writer, SummarySnapshot snapshot, EscapingSchem String name = getMetadataName(metadata, scheme); String countName = name + "_count"; String sumName = name + "_sum"; + boolean nameValidLegacy = PrometheusNaming.isValidLegacyMetricName(name); for (SummarySnapshot.SummaryDataPointSnapshot data : snapshot.getDataPoints()) { if (data.getQuantiles().size() == 0 && !data.hasCount() && !data.hasSum()) { continue; @@ -305,17 +318,24 @@ private void writeSummary(Writer writer, SummarySnapshot snapshot, EscapingSchem } for (Quantile quantile : data.getQuantiles()) { writeNameAndLabels( - writer, name, null, data.getLabels(), scheme, "quantile", quantile.getQuantile()); + writer, + name, + null, + data.getLabels(), + scheme, + "quantile", + quantile.getQuantile(), + nameValidLegacy); writeDouble(writer, quantile.getValue()); writeScrapeTimestampAndNewline(writer, data); } if (data.hasCount()) { - writeNameAndLabels(writer, countName, null, data.getLabels(), scheme); + writeNameAndLabels(writer, countName, null, data.getLabels(), scheme, nameValidLegacy); writeLong(writer, data.getCount()); writeScrapeTimestampAndNewline(writer, data); } if (data.hasSum()) { - writeNameAndLabels(writer, sumName, null, data.getLabels(), scheme); + writeNameAndLabels(writer, sumName, null, data.getLabels(), scheme, nameValidLegacy); writeDouble(writer, data.getSum()); writeScrapeTimestampAndNewline(writer, data); } @@ -326,9 +346,10 @@ private void writeInfo(Writer writer, InfoSnapshot snapshot, EscapingScheme sche throws IOException { MetricMetadata metadata = snapshot.getMetadata(); String infoName = resolveExpositionName(metadata, "_info", scheme); + boolean nameValidLegacy = PrometheusNaming.isValidLegacyMetricName(infoName); writeMetadataWithFullName(writer, infoName, "gauge", metadata); for (InfoSnapshot.InfoDataPointSnapshot data : snapshot.getDataPoints()) { - writeNameAndLabels(writer, infoName, null, data.getLabels(), scheme); + writeNameAndLabels(writer, infoName, null, data.getLabels(), scheme, nameValidLegacy); writer.write("1"); writeScrapeTimestampAndNewline(writer, data); } @@ -374,8 +395,9 @@ private void writeUnknown(Writer writer, UnknownSnapshot snapshot, EscapingSchem MetricMetadata metadata = snapshot.getMetadata(); writeMetadata(writer, null, "untyped", metadata, scheme); String name = getMetadataName(metadata, scheme); + boolean nameValidLegacy = PrometheusNaming.isValidLegacyMetricName(name); for (UnknownSnapshot.UnknownDataPointSnapshot data : snapshot.getDataPoints()) { - writeNameAndLabels(writer, name, null, data.getLabels(), scheme); + writeNameAndLabels(writer, name, null, data.getLabels(), scheme, nameValidLegacy); writeDouble(writer, data.getValue()); writeScrapeTimestampAndNewline(writer, data); } @@ -386,9 +408,10 @@ private void writeNameAndLabels( String name, @Nullable String suffix, Labels labels, - EscapingScheme escapingScheme) + EscapingScheme escapingScheme, + boolean nameValidLegacy) throws IOException { - writeNameAndLabels(writer, name, suffix, labels, escapingScheme, null, 0.0); + writeNameAndLabels(writer, name, suffix, labels, escapingScheme, null, 0.0, nameValidLegacy); } private void writeNameAndLabels( @@ -398,16 +421,20 @@ private void writeNameAndLabels( Labels labels, EscapingScheme scheme, @Nullable String additionalLabelName, - double additionalLabelValue) + double additionalLabelValue, + boolean nameValidLegacy) throws IOException { 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)) { + // metric name inside the braces. Validity is scanned once per family by the caller (the metric + // name is constant across all data points), not re-scanned for every series here. Appending a + // suffix built from legacy characters (_total, _bucket, ...) does not change legacy validity, + // so the caller's check of the base name applies to the suffixed name too. + if (!nameValidLegacy) { metricInsideBraces = true; writer.write('{'); } - writeName(writer, suffix != null ? name + suffix : name, NameType.Metric); + writeName(writer, suffix != null ? name + suffix : name, NameType.Metric, nameValidLegacy); if (!labels.isEmpty() || additionalLabelName != null) { writeLabels( writer, labels, additionalLabelName, additionalLabelValue, metricInsideBraces, scheme); 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 8cfed2b29..4a4168fed 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 @@ -205,25 +205,39 @@ static void writeLabels( } static void writeName(Writer writer, String name, NameType nameType) throws IOException { + writeName(writer, name, nameType, isValidLegacyName(name, nameType)); + } + + /** + * Variant of {@link #writeName(Writer, String, NameType)} for callers that already know whether + * {@code name} is a valid legacy name. The metric name is constant across all data points of a + * metric family, so a caller can scan it once per family (via {@link #isValidLegacyName}) and + * hoist the check out of the per-data-point loop instead of re-scanning the same string for every + * series. + */ + static void writeName(Writer writer, String name, NameType nameType, boolean validLegacyName) + throws IOException { + if (validLegacyName) { + writer.write(name); + return; + } + if (nameType != NameType.Metric && nameType != NameType.Label) { + throw new RuntimeException("Invalid name type requested: " + nameType); + } + writer.write('"'); + writeEscapedString(writer, name); + writer.write('"'); + } + + static boolean isValidLegacyName(String name, NameType nameType) { switch (nameType) { case Metric: - if (PrometheusNaming.isValidLegacyMetricName(name)) { - writer.write(name); - return; - } - break; + return PrometheusNaming.isValidLegacyMetricName(name); case Label: - if (PrometheusNaming.isValidLegacyLabelName(name)) { - writer.write(name); - return; - } - break; + return PrometheusNaming.isValidLegacyLabelName(name); default: throw new RuntimeException("Invalid name type requested: " + nameType); } - writer.write('"'); - writeEscapedString(writer, name); - writer.write('"'); } /**