Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,310 @@
package io.prometheus.metrics.expositionformats.internal;

import com.google.protobuf.ByteString;
import com.google.protobuf.TextFormat;
import com.google.protobuf.Timestamp;
import io.prometheus.metrics.expositionformats.generated.Metrics;

@SuppressWarnings("NonCanonicalType")
class PrometheusProtobufDebugFormat {

private static final String INDENT = " ";

static String toDebugString(Metrics.MetricFamily metricFamily) {
StringBuilder result = new StringBuilder();
appendMetricFamily(result, "", metricFamily);
return result.toString();
}

private static void appendMetricFamily(
StringBuilder result, String indent, Metrics.MetricFamily metricFamily) {
appendString(result, indent, "name", metricFamily.hasName(), metricFamily.getName());
appendString(result, indent, "help", metricFamily.hasHelp(), metricFamily.getHelp());
if (metricFamily.hasType()) {
appendScalar(result, indent, "type", metricFamily.getType().name());
}
for (Metrics.Metric metric : metricFamily.getMetricList()) {
appendMessage(result, indent, "metric", () -> appendMetric(result, indent + INDENT, metric));
}
appendString(result, indent, "unit", metricFamily.hasUnit(), metricFamily.getUnit());
}

private static void appendMetric(StringBuilder result, String indent, Metrics.Metric metric) {
for (Metrics.LabelPair label : metric.getLabelList()) {
appendMessage(result, indent, "label", () -> appendLabel(result, indent + INDENT, label));
}
if (metric.hasGauge()) {
appendMessage(
result, indent, "gauge", () -> appendGauge(result, indent + INDENT, metric.getGauge()));
}
if (metric.hasCounter()) {
appendMessage(
result,
indent,
"counter",
() -> appendCounter(result, indent + INDENT, metric.getCounter()));
}
if (metric.hasSummary()) {
appendMessage(
result,
indent,
"summary",
() -> appendSummary(result, indent + INDENT, metric.getSummary()));
}
if (metric.hasUntyped()) {
appendMessage(
result,
indent,
"untyped",
() -> appendUntyped(result, indent + INDENT, metric.getUntyped()));
}
if (metric.hasHistogram()) {
appendMessage(
result,
indent,
"histogram",
() -> appendHistogram(result, indent + INDENT, metric.getHistogram()));
}
appendScalar(result, indent, "timestamp_ms", metric.hasTimestampMs(), metric.getTimestampMs());
}

private static void appendLabel(StringBuilder result, String indent, Metrics.LabelPair label) {
appendString(result, indent, "name", label.hasName(), label.getName());
appendString(result, indent, "value", label.hasValue(), label.getValue());
}

private static void appendGauge(StringBuilder result, String indent, Metrics.Gauge gauge) {
appendScalar(result, indent, "value", gauge.hasValue(), gauge.getValue());
}

private static void appendCounter(StringBuilder result, String indent, Metrics.Counter counter) {
appendScalar(result, indent, "value", counter.hasValue(), counter.getValue());
if (counter.hasExemplar()) {
appendMessage(
result,
indent,
"exemplar",
() -> appendExemplar(result, indent + INDENT, counter.getExemplar()));
}
appendTimestamp(
result,
indent,
"created_timestamp",
counter.hasCreatedTimestamp(),
counter.getCreatedTimestamp());
}

private static void appendSummary(StringBuilder result, String indent, Metrics.Summary summary) {
appendScalar(
result, indent, "sample_count", summary.hasSampleCount(), summary.getSampleCount());
appendScalar(result, indent, "sample_sum", summary.hasSampleSum(), summary.getSampleSum());
for (Metrics.Quantile quantile : summary.getQuantileList()) {
appendMessage(
result, indent, "quantile", () -> appendQuantile(result, indent + INDENT, quantile));
}
appendTimestamp(
result,
indent,
"created_timestamp",
summary.hasCreatedTimestamp(),
summary.getCreatedTimestamp());
}

private static void appendQuantile(
StringBuilder result, String indent, Metrics.Quantile quantile) {
appendScalar(result, indent, "quantile", quantile.hasQuantile(), quantile.getQuantile());
appendScalar(result, indent, "value", quantile.hasValue(), quantile.getValue());
}

private static void appendUntyped(StringBuilder result, String indent, Metrics.Untyped untyped) {
appendScalar(result, indent, "value", untyped.hasValue(), untyped.getValue());
}

private static void appendHistogram(
StringBuilder result, String indent, Metrics.Histogram histogram) {
appendScalar(
result, indent, "sample_count", histogram.hasSampleCount(), histogram.getSampleCount());
appendScalar(
result,
indent,
"sample_count_float",
histogram.hasSampleCountFloat(),
histogram.getSampleCountFloat());
appendScalar(result, indent, "sample_sum", histogram.hasSampleSum(), histogram.getSampleSum());
for (Metrics.Bucket bucket : histogram.getBucketList()) {
appendMessage(result, indent, "bucket", () -> appendBucket(result, indent + INDENT, bucket));
}
appendTimestamp(
result,
indent,
"created_timestamp",
histogram.hasCreatedTimestamp(),
histogram.getCreatedTimestamp());
appendScalar(result, indent, "schema", histogram.hasSchema(), histogram.getSchema());
appendScalar(
result,
indent,
"zero_threshold",
histogram.hasZeroThreshold(),
histogram.getZeroThreshold());
appendScalar(result, indent, "zero_count", histogram.hasZeroCount(), histogram.getZeroCount());
appendScalar(
result,
indent,
"zero_count_float",
histogram.hasZeroCountFloat(),
histogram.getZeroCountFloat());
for (Metrics.BucketSpan span : histogram.getNegativeSpanList()) {
appendMessage(
result,
indent,
"negative_span",
() -> appendBucketSpan(result, indent + INDENT, span));
}
for (int i = 0; i < histogram.getNegativeDeltaCount(); i++) {
appendScalar(result, indent, "negative_delta", histogram.getNegativeDelta(i));
}
for (int i = 0; i < histogram.getNegativeCountCount(); i++) {
appendScalar(result, indent, "negative_count", histogram.getNegativeCount(i));
}
for (Metrics.BucketSpan span : histogram.getPositiveSpanList()) {
appendMessage(
result,
indent,
"positive_span",
() -> appendBucketSpan(result, indent + INDENT, span));
}
for (int i = 0; i < histogram.getPositiveDeltaCount(); i++) {
appendScalar(result, indent, "positive_delta", histogram.getPositiveDelta(i));
}
for (int i = 0; i < histogram.getPositiveCountCount(); i++) {
appendScalar(result, indent, "positive_count", histogram.getPositiveCount(i));
}
for (Metrics.Exemplar exemplar : histogram.getExemplarsList()) {
appendMessage(
result,
indent,
"exemplars",
() -> appendExemplar(result, indent + INDENT, exemplar));
}
}

private static void appendBucket(StringBuilder result, String indent, Metrics.Bucket bucket) {
appendScalar(
result,
indent,
"cumulative_count",
bucket.hasCumulativeCount(),
bucket.getCumulativeCount());
appendScalar(
result,
indent,
"cumulative_count_float",
bucket.hasCumulativeCountFloat(),
bucket.getCumulativeCountFloat());
appendScalar(result, indent, "upper_bound", bucket.hasUpperBound(), bucket.getUpperBound());
if (bucket.hasExemplar()) {
appendMessage(
result,
indent,
"exemplar",
() -> appendExemplar(result, indent + INDENT, bucket.getExemplar()));
}
}

private static void appendBucketSpan(
StringBuilder result, String indent, Metrics.BucketSpan span) {
appendScalar(result, indent, "offset", span.hasOffset(), span.getOffset());
appendScalar(result, indent, "length", span.hasLength(), span.getLength());
}

private static void appendExemplar(
StringBuilder result, String indent, Metrics.Exemplar exemplar) {
for (Metrics.LabelPair label : exemplar.getLabelList()) {
appendMessage(result, indent, "label", () -> appendLabel(result, indent + INDENT, label));
}
appendScalar(result, indent, "value", exemplar.hasValue(), exemplar.getValue());
appendTimestamp(result, indent, "timestamp", exemplar.hasTimestamp(), exemplar.getTimestamp());
}

private static void appendTimestamp(
StringBuilder result, String indent, String fieldName, boolean present, Timestamp timestamp) {
if (present) {
appendMessage(
result,
indent,
fieldName,
() -> {
appendScalar(result, indent + INDENT, "seconds", timestamp.getSeconds());
appendScalar(result, indent + INDENT, "nanos", timestamp.getNanos());
});
}
}

private static void appendString(
StringBuilder result, String indent, String fieldName, boolean present, String value) {
if (present) {
appendScalar(result, indent, fieldName, quote(value));
}
}

private static void appendScalar(
StringBuilder result, String indent, String fieldName, boolean present, double value) {
if (present) {
appendScalar(result, indent, fieldName, formatDouble(value));
}
}

private static void appendScalar(
StringBuilder result, String indent, String fieldName, boolean present, long value) {
if (present) {
appendScalar(result, indent, fieldName, Long.toString(value));
}
}

private static void appendScalar(
StringBuilder result, String indent, String fieldName, boolean present, int value) {
if (present) {
appendScalar(result, indent, fieldName, Integer.toString(value));
}
}

private static void appendScalar(
StringBuilder result, String indent, String fieldName, double value) {
appendScalar(result, indent, fieldName, formatDouble(value));
}

private static void appendScalar(
StringBuilder result, String indent, String fieldName, long value) {
appendScalar(result, indent, fieldName, Long.toString(value));
}

private static void appendScalar(
StringBuilder result, String indent, String fieldName, String value) {
result.append(indent).append(fieldName).append(": ").append(value).append('\n');
}

private static void appendMessage(
StringBuilder result, String indent, String fieldName, Runnable appendBody) {
result.append(indent).append(fieldName).append(" {\n");
appendBody.run();
result.append(indent).append("}\n");
}

private static String quote(String value) {
return "\"" + TextFormat.escapeBytes(ByteString.copyFromUtf8(value)) + "\"";
}

private static String formatDouble(double value) {
if (Double.isNaN(value)) {
return "nan";
}
if (value == Double.POSITIVE_INFINITY) {
return "inf";
}
if (value == Double.NEGATIVE_INFINITY) {
return "-inf";
}
return Double.toString(value);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@
import static io.prometheus.metrics.expositionformats.internal.ProtobufUtil.timestampFromMillis;
import static io.prometheus.metrics.model.snapshots.SnapshotEscaper.getSnapshotLabelName;

import com.google.protobuf.TextFormat;
import io.prometheus.metrics.config.EscapingScheme;
import io.prometheus.metrics.expositionformats.ExpositionFormatWriter;
import io.prometheus.metrics.expositionformats.TextFormatUtil;
Expand Down Expand Up @@ -51,9 +50,8 @@ public String toDebugString(MetricSnapshots metricSnapshots, EscapingScheme esca
MetricSnapshot snapshot = SnapshotEscaper.escapeMetricSnapshot(s, escapingScheme);
if (!snapshot.getDataPoints().isEmpty()) {
stringBuilder.append(
TextFormat.printer()
.printToString(
convert(snapshot, s.getMetadata().getOriginalName(), escapingScheme)));
PrometheusProtobufDebugFormat.toDebugString(
convert(snapshot, s.getMetadata().getOriginalName(), escapingScheme)));
}
}
return stringBuilder.toString();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,11 @@
import io.prometheus.metrics.expositionformats.generated.Metrics;
import io.prometheus.metrics.expositionformats.internal.PrometheusProtobufWriterImpl;
import io.prometheus.metrics.expositionformats.internal.ProtobufUtil;
import io.prometheus.metrics.model.snapshots.HistogramSnapshot;
import io.prometheus.metrics.model.snapshots.MetricSnapshot;
import io.prometheus.metrics.model.snapshots.MetricSnapshots;
import io.prometheus.metrics.model.snapshots.NativeHistogramBuckets;
import org.junit.jupiter.api.Test;

@SuppressWarnings("NonCanonicalType")
class ProtobufExpositionFormatsTest extends ExpositionFormatsTest {
Expand All @@ -19,4 +23,45 @@ protected void assertPrometheusProtobuf(String expected, MetricSnapshot snapshot
String actual = ProtobufUtil.shortDebugString(protobufData);
assertThat(actual).isEqualTo(expected);
}

@Test
void testNativeHistogramDebugString() {
HistogramSnapshot histogram =
HistogramSnapshot.builder()
.name("request_latency_seconds")
.help("request latency")
.dataPoint(
HistogramSnapshot.HistogramDataPointSnapshot.builder()
.sum(0.123)
.nativeSchema(5)
.nativeZeroThreshold(2.938735877055719E-39)
.nativeZeroCount(0)
.nativeBucketsForPositiveValues(
NativeHistogramBuckets.builder().bucket(-96, 1).build())
.build())
.build();

PrometheusProtobufWriterImpl writer = new PrometheusProtobufWriterImpl();

assertThat(
writer.toDebugString(MetricSnapshots.of(histogram), EscapingScheme.UNDERSCORE_ESCAPING))
.isEqualTo(
"name: \"request_latency_seconds\"\n"
+ "help: \"request latency\"\n"
+ "type: HISTOGRAM\n"
+ "metric {\n"
+ " histogram {\n"
+ " sample_count: 1\n"
+ " sample_sum: 0.123\n"
+ " schema: 5\n"
+ " zero_threshold: 2.938735877055719E-39\n"
+ " zero_count: 0\n"
+ " positive_span {\n"
+ " offset: -96\n"
+ " length: 1\n"
+ " }\n"
+ " positive_delta: 1\n"
+ " }\n"
+ "}\n");
}
}