Skip to content
Closed
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
package io.prometheus.metrics.config;

import java.util.Map;

public class NamingProperties {

private static final String VALIDATION_SCHEME = "validationScheme";
private final String validationScheme;

private NamingProperties(String validation) {
this.validationScheme = validation;
}

public String getValidationScheme() {
return validationScheme;
}

static NamingProperties load(String prefix, Map<Object, Object> properties) throws PrometheusPropertiesException {
String validationScheme = Util.loadString(prefix + "." + VALIDATION_SCHEME, properties);
return new NamingProperties(validationScheme);
}

public static Builder builder() {
return new Builder();
}

public static class Builder {

private String validationScheme;

private Builder() {}

public Builder validation(String validationScheme) {
this.validationScheme = validationScheme;
return this;
}

public NamingProperties build() {
return new NamingProperties(validationScheme);
}
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ public class PrometheusProperties {
private final ExporterFilterProperties exporterFilterProperties;
private final ExporterHttpServerProperties exporterHttpServerProperties;
private final ExporterOpenTelemetryProperties exporterOpenTelemetryProperties;
private final NamingProperties namingProperties;

/**
* Get the properties instance. When called for the first time, {@code get()} loads the properties from the following locations:
Expand All @@ -39,14 +40,16 @@ public PrometheusProperties(
ExporterProperties exporterProperties,
ExporterFilterProperties exporterFilterProperties,
ExporterHttpServerProperties httpServerConfig,
ExporterOpenTelemetryProperties otelConfig) {
ExporterOpenTelemetryProperties otelConfig,
NamingProperties namingProperties) {
this.defaultMetricsProperties = defaultMetricsProperties;
this.metricProperties.putAll(metricProperties);
this.exemplarProperties = exemplarProperties;
this.exporterProperties = exporterProperties;
this.exporterFilterProperties = exporterFilterProperties;
this.exporterHttpServerProperties = httpServerConfig;
this.exporterOpenTelemetryProperties = otelConfig;
this.namingProperties = namingProperties;
}

/**
Expand Down Expand Up @@ -83,4 +86,8 @@ public ExporterHttpServerProperties getExporterHttpServerProperties() {
public ExporterOpenTelemetryProperties getExporterOpenTelemetryProperties() {
return exporterOpenTelemetryProperties;
}

public NamingProperties getNamingProperties() {
return namingProperties;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -33,8 +33,9 @@ public static PrometheusProperties load() throws PrometheusPropertiesException {
ExporterFilterProperties exporterFilterProperties = ExporterFilterProperties.load("io.prometheus.exporter.filter", properties);
ExporterHttpServerProperties exporterHttpServerProperties = ExporterHttpServerProperties.load("io.prometheus.exporter.httpServer", properties);
ExporterOpenTelemetryProperties exporterOpenTelemetryProperties = ExporterOpenTelemetryProperties.load("io.prometheus.exporter.opentelemetry", properties);
NamingProperties namingProperties = NamingProperties.load("io.prometheus.naming", properties);
validateAllPropertiesProcessed(properties);
return new PrometheusProperties(defaultMetricsProperties, metricsConfigs, exemplarConfig, exporterProperties, exporterFilterProperties, exporterHttpServerProperties, exporterOpenTelemetryProperties);
return new PrometheusProperties(defaultMetricsProperties, metricsConfigs, exemplarConfig, exporterProperties, exporterFilterProperties, exporterHttpServerProperties, exporterOpenTelemetryProperties, namingProperties);
}

// This will remove entries from properties when they are processed.
Expand Down
Original file line number Diff line number Diff line change
@@ -1,17 +1,12 @@
package io.prometheus.metrics.core.metrics;

import io.prometheus.metrics.model.snapshots.*;
import io.prometheus.metrics.shaded.com_google_protobuf_3_21_7.TextFormat;
import io.prometheus.metrics.core.datapoints.DistributionDataPoint;
import io.prometheus.metrics.core.exemplars.ExemplarSamplerConfigTestUtil;
import io.prometheus.metrics.expositionformats.OpenMetricsTextFormatWriter;
import io.prometheus.metrics.expositionformats.PrometheusProtobufWriter;
import io.prometheus.metrics.expositionformats.generated.com_google_protobuf_3_21_7.Metrics;
import io.prometheus.metrics.model.snapshots.ClassicHistogramBucket;
import io.prometheus.metrics.model.snapshots.Exemplar;
import io.prometheus.metrics.model.snapshots.Exemplars;
import io.prometheus.metrics.model.snapshots.HistogramSnapshot;
import io.prometheus.metrics.model.snapshots.Labels;
import io.prometheus.metrics.model.snapshots.MetricSnapshots;
import io.prometheus.metrics.tracer.common.SpanContext;
import io.prometheus.metrics.tracer.initializer.SpanContextSupplier;
import org.junit.After;
Expand Down Expand Up @@ -723,7 +718,7 @@ public void testDefaults() throws IOException {
// text
ByteArrayOutputStream out = new ByteArrayOutputStream();
OpenMetricsTextFormatWriter writer = new OpenMetricsTextFormatWriter(false, true);
writer.write(out, MetricSnapshots.of(snapshot));
writer.write(out, MetricSnapshots.of(snapshot), EscapingScheme.NO_ESCAPING);
Assert.assertEquals(expectedTextFormat, out.toString());
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package io.prometheus.metrics.core.metrics;

import io.prometheus.metrics.expositionformats.OpenMetricsTextFormatWriter;
import io.prometheus.metrics.model.snapshots.EscapingScheme;
import io.prometheus.metrics.model.snapshots.Labels;
import io.prometheus.metrics.model.snapshots.MetricSnapshots;
import io.prometheus.metrics.shaded.com_google_protobuf_3_21_7.TextFormat;
Expand Down Expand Up @@ -98,7 +99,7 @@ public void testConstLabelsDuplicate2() {
private void assertTextFormat(String expected, Info info) throws IOException {
OpenMetricsTextFormatWriter writer = new OpenMetricsTextFormatWriter(true, true);
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
writer.write(outputStream, MetricSnapshots.of(info.collect()));
writer.write(outputStream, MetricSnapshots.of(info.collect()), EscapingScheme.NO_ESCAPING);
String result = outputStream.toString(StandardCharsets.UTF_8.name());
if (!result.contains(expected)) {
throw new AssertionError(expected + " is not contained in the following output:\n" + result);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
import io.prometheus.metrics.expositionformats.ExpositionFormats;
import io.prometheus.metrics.model.registry.MetricNameFilter;
import io.prometheus.metrics.model.registry.PrometheusRegistry;
import io.prometheus.metrics.model.snapshots.EscapingScheme;
import io.prometheus.metrics.model.snapshots.MetricSnapshots;

import java.io.ByteArrayOutputStream;
Expand Down Expand Up @@ -51,15 +52,16 @@ public void handleRequest(PrometheusHttpExchange exchange) throws IOException {
PrometheusHttpRequest request = exchange.getRequest();
PrometheusHttpResponse response = exchange.getResponse();
MetricSnapshots snapshots = scrape(request);
if (writeDebugResponse(snapshots, exchange)) {
String acceptHeader = request.getHeader("Accept");
EscapingScheme escapingScheme = EscapingScheme.fromAcceptHeader(acceptHeader);
if (writeDebugResponse(snapshots, exchange, escapingScheme)) {
return;
}
ByteArrayOutputStream responseBuffer = new ByteArrayOutputStream(lastResponseSize.get() + 1024);
String acceptHeader = request.getHeader("Accept");
ExpositionFormatWriter writer = expositionFormats.findWriter(acceptHeader);
writer.write(responseBuffer, snapshots);
writer.write(responseBuffer, snapshots, escapingScheme);
lastResponseSize.set(responseBuffer.size());
response.setHeader("Content-Type", writer.getContentType());
response.setHeader("Content-Type", writer.getContentType() + escapingScheme.toHeaderFormat());

if (shouldUseCompression(request)) {
response.setHeader("Content-Encoding", "gzip");
Expand Down Expand Up @@ -126,7 +128,7 @@ private Predicate<String> makeNameFilter(String[] includedNames) {
return result;
}

private boolean writeDebugResponse(MetricSnapshots snapshots, PrometheusHttpExchange exchange) throws IOException {
private boolean writeDebugResponse(MetricSnapshots snapshots, PrometheusHttpExchange exchange, EscapingScheme escapingScheme) throws IOException {
String debugParam = exchange.getRequest().getParameter("debug");
PrometheusHttpResponse response = exchange.getResponse();
if (debugParam == null) {
Expand All @@ -138,10 +140,10 @@ private boolean writeDebugResponse(MetricSnapshots snapshots, PrometheusHttpExch
OutputStream body = response.sendHeadersAndGetBody(responseStatus, 0);
switch (debugParam) {
case "openmetrics":
expositionFormats.getOpenMetricsTextFormatWriter().write(body, snapshots);
expositionFormats.getOpenMetricsTextFormatWriter().write(body, snapshots, escapingScheme);
break;
case "text":
expositionFormats.getPrometheusTextFormatWriter().write(body, snapshots);
expositionFormats.getPrometheusTextFormatWriter().write(body, snapshots, escapingScheme);
break;
case "prometheus-protobuf":
String debugString = expositionFormats.getPrometheusProtobufWriter().toDebugString(snapshots);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package io.prometheus.metrics.expositionformats;

import io.prometheus.metrics.model.snapshots.EscapingScheme;
import io.prometheus.metrics.model.snapshots.MetricSnapshots;

import java.io.IOException;
Expand All @@ -11,6 +12,6 @@ public interface ExpositionFormatWriter {
/**
* Text formats use UTF-8 encoding.
*/
void write(OutputStream out, MetricSnapshots metricSnapshots) throws IOException;
void write(OutputStream out, MetricSnapshots metricSnapshots, EscapingScheme escapingScheme) throws IOException;
String getContentType();
}
Original file line number Diff line number Diff line change
@@ -1,34 +1,14 @@
package io.prometheus.metrics.expositionformats;

import io.prometheus.metrics.model.snapshots.ClassicHistogramBuckets;
import io.prometheus.metrics.model.snapshots.CounterSnapshot;
import io.prometheus.metrics.model.snapshots.DataPointSnapshot;
import io.prometheus.metrics.model.snapshots.DistributionDataPointSnapshot;
import io.prometheus.metrics.model.snapshots.Exemplar;
import io.prometheus.metrics.model.snapshots.Exemplars;
import io.prometheus.metrics.model.snapshots.GaugeSnapshot;
import io.prometheus.metrics.model.snapshots.HistogramSnapshot;
import io.prometheus.metrics.model.snapshots.InfoSnapshot;
import io.prometheus.metrics.model.snapshots.Labels;
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.Quantile;
import io.prometheus.metrics.model.snapshots.StateSetSnapshot;
import io.prometheus.metrics.model.snapshots.SummarySnapshot;
import io.prometheus.metrics.model.snapshots.UnknownSnapshot;
import io.prometheus.metrics.model.snapshots.*;

import java.io.IOException;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.nio.charset.StandardCharsets;
import java.util.List;

import static io.prometheus.metrics.expositionformats.TextFormatUtil.writeDouble;
import static io.prometheus.metrics.expositionformats.TextFormatUtil.writeEscapedLabelValue;
import static io.prometheus.metrics.expositionformats.TextFormatUtil.writeLabels;
import static io.prometheus.metrics.expositionformats.TextFormatUtil.writeLong;
import static io.prometheus.metrics.expositionformats.TextFormatUtil.writeTimestamp;
import static io.prometheus.metrics.expositionformats.TextFormatUtil.*;

/**
* Write the OpenMetrics text format as defined on <a href="https://openmetrics.io/">https://openmetrics.io</a>.
Expand Down Expand Up @@ -60,9 +40,11 @@ public String getContentType() {
return CONTENT_TYPE;
}

public void write(OutputStream out, MetricSnapshots metricSnapshots) throws IOException {
public void write(OutputStream out, MetricSnapshots metricSnapshots, EscapingScheme escapingScheme) throws IOException {
OutputStreamWriter writer = new OutputStreamWriter(out, StandardCharsets.UTF_8);
for (MetricSnapshot snapshot : metricSnapshots) {
for (MetricSnapshot s : metricSnapshots) {
MetricSnapshot snapshot = PrometheusNaming.escapeMetricSnapshot(s, escapingScheme);

if (snapshot.getDataPoints().size() > 0) {
if (snapshot instanceof CounterSnapshot) {
writeCounter(writer, (CounterSnapshot) snapshot);
Expand Down Expand Up @@ -214,15 +196,15 @@ private void writeStateSet(OutputStreamWriter writer, StateSetSnapshot snapshot)
}
writer.write(data.getLabels().getPrometheusName(j));
writer.write("=\"");
writeEscapedLabelValue(writer, data.getLabels().getValue(j));
writeEscapedString(writer, data.getLabels().getValue(j));
writer.write("\"");
}
if (!data.getLabels().isEmpty()) {
writer.write(",");
}
writer.write(metadata.getPrometheusName());
writer.write("=\"");
writeEscapedLabelValue(writer, data.getName(i));
writeEscapedString(writer, data.getName(i));
writer.write("\"} ");
if (data.isTrue(i)) {
writer.write("1");
Expand Down Expand Up @@ -289,13 +271,21 @@ private void writeNameAndLabels(OutputStreamWriter writer, String name, String s

private void writeNameAndLabels(OutputStreamWriter writer, String name, String suffix, Labels labels,
String additionalLabelName, double additionalLabelValue) throws IOException {
writer.write(name);
if (suffix != null) {
writer.write(suffix);
boolean metricInsideBraces = false;
// If the name does not pass the legacy validity check, we must put the
// metric name inside the braces.
if (PrometheusNaming.validateLegacyMetricName(name) != null) {
metricInsideBraces = true;
writer.write('{');
}
writeName(writer, name + (suffix != null ? suffix : ""), NameType.Metric);

if (!labels.isEmpty() || additionalLabelName != null) {
writeLabels(writer, labels, additionalLabelName, additionalLabelValue);
writeLabels(writer, labels, additionalLabelName, additionalLabelValue, metricInsideBraces);
} else if (metricInsideBraces) {
writer.write('}');
}

writer.write(' ');
}

Expand All @@ -306,7 +296,7 @@ private void writeScrapeTimestampAndExemplar(OutputStreamWriter writer, DataPoin
}
if (exemplar != null) {
writer.write(" # ");
writeLabels(writer, exemplar.getLabels(), null, 0);
writeLabels(writer, exemplar.getLabels(), null, 0, false);
writer.write(' ');
writeDouble(writer, exemplar.getValue());
if (exemplar.hasTimestamp()) {
Expand All @@ -319,22 +309,22 @@ private void writeScrapeTimestampAndExemplar(OutputStreamWriter writer, DataPoin

private void writeMetadata(OutputStreamWriter writer, String typeName, MetricMetadata metadata) throws IOException {
writer.write("# TYPE ");
writer.write(metadata.getPrometheusName());
writeName(writer, metadata.getPrometheusName(), NameType.Metric);
writer.write(' ');
writer.write(typeName);
writer.write('\n');
if (metadata.getUnit() != null) {
writer.write("# UNIT ");
writer.write(metadata.getPrometheusName());
writeName(writer, metadata.getPrometheusName(), NameType.Metric);
writer.write(' ');
writeEscapedLabelValue(writer, metadata.getUnit().toString());
writeEscapedString(writer, metadata.getUnit().toString());
writer.write('\n');
}
if (metadata.getHelp() != null && !metadata.getHelp().isEmpty()) {
writer.write("# HELP ");
writer.write(metadata.getPrometheusName());
writeName(writer, metadata.getPrometheusName(), NameType.Metric);
writer.write(' ');
writeEscapedLabelValue(writer, metadata.getHelp());
writeEscapedString(writer, metadata.getHelp());
writer.write('\n');
}
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,24 +1,9 @@
package io.prometheus.metrics.expositionformats;

import io.prometheus.metrics.model.snapshots.*;
import io.prometheus.metrics.shaded.com_google_protobuf_3_21_7.TextFormat;
import io.prometheus.metrics.expositionformats.generated.com_google_protobuf_3_21_7.Metrics;
import io.prometheus.metrics.model.snapshots.ClassicHistogramBuckets;
import io.prometheus.metrics.model.snapshots.CounterSnapshot;
import io.prometheus.metrics.model.snapshots.CounterSnapshot.CounterDataPointSnapshot;
import io.prometheus.metrics.model.snapshots.Exemplar;
import io.prometheus.metrics.model.snapshots.GaugeSnapshot;
import io.prometheus.metrics.model.snapshots.HistogramSnapshot;
import io.prometheus.metrics.model.snapshots.InfoSnapshot;
import io.prometheus.metrics.model.snapshots.Labels;
import io.prometheus.metrics.model.snapshots.DataPointSnapshot;
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.NativeHistogramBuckets;
import io.prometheus.metrics.model.snapshots.Quantiles;
import io.prometheus.metrics.model.snapshots.StateSetSnapshot;
import io.prometheus.metrics.model.snapshots.SummarySnapshot;
import io.prometheus.metrics.model.snapshots.UnknownSnapshot;

import java.io.IOException;
import java.io.OutputStream;
Expand Down Expand Up @@ -61,7 +46,7 @@ public String toDebugString(MetricSnapshots metricSnapshots) {
}

@Override
public void write(OutputStream out, MetricSnapshots metricSnapshots) throws IOException {
public void write(OutputStream out, MetricSnapshots metricSnapshots, EscapingScheme escapingScheme) throws IOException {
for (MetricSnapshot snapshot : metricSnapshots) {
if (snapshot.getDataPoints().size() > 0) {
convert(snapshot).writeDelimitedTo(out);
Expand Down
Loading