All Downloads are FREE. Search and download functionalities are using the official Maven repository.

io.prometheus.metrics.expositionformats.PrometheusTextFormatWriter Maven / Gradle / Ivy

There is a newer version: 1.3.1
Show newest version
package io.prometheus.metrics.expositionformats;

import io.prometheus.metrics.model.snapshots.CounterSnapshot;
import io.prometheus.metrics.model.snapshots.ClassicHistogramBuckets;
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.Quantile;
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;
import java.io.OutputStreamWriter;
import java.io.Writer;
import java.nio.charset.StandardCharsets;

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.model.snapshots.PrometheusNaming.prometheusName;

/**
 * Write the Prometheus text format. This is the default if you view a Prometheus endpoint with your Web browser.
 */
public class PrometheusTextFormatWriter implements ExpositionFormatWriter {

    public static final String CONTENT_TYPE = "text/plain; version=0.0.4; charset=utf-8";

    private final boolean writeCreatedTimestamps;

    public PrometheusTextFormatWriter(boolean writeCreatedTimestamps) {
        this.writeCreatedTimestamps = writeCreatedTimestamps;
    }

    @Override
    public boolean accepts(String acceptHeader) {
        if (acceptHeader == null) {
            return false;
        } else {
        return acceptHeader.contains("text/plain");
        }
    }

    @Override
    public String getContentType() {
        return CONTENT_TYPE;
    }

    public void write(OutputStream out, MetricSnapshots metricSnapshots) throws IOException {
        // See https://prometheus.io/docs/instrumenting/exposition_formats/
        // "unknown", "gauge", "counter", "stateset", "info", "histogram", "gaugehistogram", and "summary".
        OutputStreamWriter writer = new OutputStreamWriter(out, StandardCharsets.UTF_8);
        for (MetricSnapshot snapshot : metricSnapshots) {
            if (snapshot.getData().size() > 0) {
                if (snapshot instanceof CounterSnapshot) {
                    writeCounter(writer, (CounterSnapshot) snapshot);
                } else if (snapshot instanceof GaugeSnapshot) {
                    writeGauge(writer, (GaugeSnapshot) snapshot);
                } else if (snapshot instanceof HistogramSnapshot) {
                    writeHistogram(writer, (HistogramSnapshot) snapshot);
                } else if (snapshot instanceof SummarySnapshot) {
                    writeSummary(writer, (SummarySnapshot) snapshot);
                } else if (snapshot instanceof InfoSnapshot) {
                    writeInfo(writer, (InfoSnapshot) snapshot);
                } else if (snapshot instanceof StateSetSnapshot) {
                    writeStateSet(writer, (StateSetSnapshot) snapshot);
                } else if (snapshot instanceof UnknownSnapshot) {
                    writeUnknown(writer, (UnknownSnapshot) snapshot);
                }
            }
        }
        if (writeCreatedTimestamps) {
            for (MetricSnapshot snapshot : metricSnapshots) {
                if (snapshot.getData().size() > 0) {
                    if (snapshot instanceof CounterSnapshot) {
                        writeCreated(writer, snapshot);
                    } else if (snapshot instanceof HistogramSnapshot) {
                        writeCreated(writer, snapshot);
                    } else if (snapshot instanceof SummarySnapshot) {
                        writeCreated(writer, snapshot);
                    }
                }
            }
        }
        writer.flush();
    }

    public void writeCreated(OutputStreamWriter writer, MetricSnapshot snapshot) throws IOException {
            boolean metadataWritten = false;
            MetricMetadata metadata = snapshot.getMetadata();
            for (DataPointSnapshot data : snapshot.getData()) {
                if (data.hasCreatedTimestamp()) {
                    if (!metadataWritten) {
                        writeMetadata(writer, "_created", "gauge", metadata);
                        metadataWritten = true;
                    }
                    writeNameAndLabels(writer, metadata.getPrometheusName(), "_created", data.getLabels());
                    writeTimestamp(writer, data.getCreatedTimestampMillis());
                    writeScrapeTimestampAndNewline(writer, data);
                }
            }

    }

    private void writeCounter(OutputStreamWriter writer, CounterSnapshot snapshot) throws IOException {
        if (snapshot.getData().size() > 0) {
            MetricMetadata metadata = snapshot.getMetadata();
            writeMetadata(writer, "_total", "counter", metadata);
            for (CounterSnapshot.CounterDataPointSnapshot data : snapshot.getData()) {
                writeNameAndLabels(writer, metadata.getPrometheusName(), "_total", data.getLabels());
                writeDouble(writer, data.getValue());
                writeScrapeTimestampAndNewline(writer, data);
            }
        }
    }

    private void writeGauge(OutputStreamWriter writer, GaugeSnapshot snapshot) throws IOException {
        MetricMetadata metadata = snapshot.getMetadata();
        writeMetadata(writer, "", "gauge", metadata);
        for (GaugeSnapshot.GaugeDataPointSnapshot data : snapshot.getData()) {
            writeNameAndLabels(writer, metadata.getPrometheusName(), null, data.getLabels());
            writeDouble(writer, data.getValue());
            writeScrapeTimestampAndNewline(writer, data);
        }
    }

    private void writeHistogram(OutputStreamWriter writer, HistogramSnapshot snapshot) throws IOException {
        MetricMetadata metadata = snapshot.getMetadata();
        writeMetadata(writer, "", "histogram", metadata);
        for (HistogramSnapshot.HistogramDataPointSnapshot data : snapshot.getData()) {
            ClassicHistogramBuckets buckets = getClassicBuckets(data);
            long cumulativeCount = 0;
            for (int i = 0; i < buckets.size(); i++) {
                cumulativeCount += buckets.getCount(i);
                writeNameAndLabels(writer, metadata.getPrometheusName(), "_bucket", data.getLabels(), "le", buckets.getUpperBound(i));
                writeLong(writer, cumulativeCount);
                writeScrapeTimestampAndNewline(writer, data);
            }
            if (!snapshot.isGaugeHistogram()) {
                if (data.hasCount()) {
                    writeNameAndLabels(writer, metadata.getPrometheusName(), "_count", data.getLabels());
                    writeLong(writer, data.getCount());
                    writeScrapeTimestampAndNewline(writer, data);
                }
                if (data.hasSum()) {
                    writeNameAndLabels(writer, metadata.getPrometheusName(), "_sum", data.getLabels());
                    writeDouble(writer, data.getSum());
                    writeScrapeTimestampAndNewline(writer, data);
                }
            }
        }
        if (snapshot.isGaugeHistogram()) {
            writeGaugeCountSum(writer, snapshot, metadata);
        }
    }

    private ClassicHistogramBuckets getClassicBuckets(HistogramSnapshot.HistogramDataPointSnapshot data) {
        if (data.getClassicBuckets().isEmpty()) {
            return ClassicHistogramBuckets.of(
                    new double[]{Double.POSITIVE_INFINITY},
                    new long[]{data.getCount()}
            );
        } else {
            return data.getClassicBuckets();
        }
    }

    private void writeGaugeCountSum(OutputStreamWriter writer, HistogramSnapshot snapshot, MetricMetadata metadata) throws IOException {
        // Prometheus text format does not support gaugehistogram's _gcount and _gsum.
        // So we append _gcount and _gsum as gauge metrics.
        boolean metadataWritten = false;
        for (HistogramSnapshot.HistogramDataPointSnapshot data : snapshot.getData()) {
            if (data.hasCount()) {
                if (!metadataWritten) {
                    writeMetadata(writer, "_gcount", "gauge", metadata);
                    metadataWritten = true;
                }
                writeNameAndLabels(writer, metadata.getPrometheusName(), "_gcount", data.getLabels());
                writeLong(writer, data.getCount());
                writeScrapeTimestampAndNewline(writer, data);
            }
        }
        metadataWritten = false;
        for (HistogramSnapshot.HistogramDataPointSnapshot data : snapshot.getData()) {
            if (data.hasSum()) {
                if (!metadataWritten) {
                    writeMetadata(writer, "_gsum", "gauge", metadata);
                    metadataWritten = true;
                }
                writeNameAndLabels(writer, metadata.getPrometheusName(), "_gsum", data.getLabels());
                writeDouble(writer, data.getSum());
                writeScrapeTimestampAndNewline(writer, data);
            }
        }
    }

    private void writeSummary(OutputStreamWriter writer, SummarySnapshot snapshot) throws IOException {
        boolean metadataWritten = false;
        MetricMetadata metadata = snapshot.getMetadata();
        for (SummarySnapshot.SummaryDataPointSnapshot data : snapshot.getData()) {
            if (data.getQuantiles().size() == 0 && !data.hasCount() && !data.hasSum()) {
                continue;
            }
            if (!metadataWritten) {
                writeMetadata(writer, "", "summary", metadata);
                metadataWritten = true;
            }
            for (Quantile quantile : data.getQuantiles()) {
                writeNameAndLabels(writer, metadata.getPrometheusName(), null, data.getLabels(), "quantile", quantile.getQuantile());
                writeDouble(writer, quantile.getValue());
                writeScrapeTimestampAndNewline(writer, data);
            }
            if (data.hasCount()) {
                writeNameAndLabels(writer, metadata.getPrometheusName(), "_count", data.getLabels());
                writeLong(writer, data.getCount());
                writeScrapeTimestampAndNewline(writer, data);
            }
            if (data.hasSum()) {
                writeNameAndLabels(writer, metadata.getPrometheusName(), "_sum", data.getLabels());
                writeDouble(writer, data.getSum());
                writeScrapeTimestampAndNewline(writer, data);
            }
        }
    }

    private void writeInfo(OutputStreamWriter writer, InfoSnapshot snapshot) throws IOException {
        MetricMetadata metadata = snapshot.getMetadata();
        writeMetadata(writer, "_info", "gauge", metadata);
        for (InfoSnapshot.InfoDataPointSnapshot data : snapshot.getData()) {
            writeNameAndLabels(writer, metadata.getPrometheusName(), "_info", data.getLabels());
            writer.write("1");
            writeScrapeTimestampAndNewline(writer, data);
        }
    }

    private void writeStateSet(OutputStreamWriter writer, StateSetSnapshot snapshot) throws IOException {
        MetricMetadata metadata = snapshot.getMetadata();
        writeMetadata(writer, "", "gauge", metadata);
        for (StateSetSnapshot.StateSetDataPointSnapshot data : snapshot.getData()) {
            for (int i = 0; i < data.size(); i++) {
                writer.write(metadata.getPrometheusName());
                writer.write('{');
                for (int j = 0; j < data.getLabels().size(); j++) {
                    if (j > 0) {
                        writer.write(",");
                    }
                    writer.write(data.getLabels().getPrometheusName(j));
                    writer.write("=\"");
                    writeEscapedLabelValue(writer, data.getLabels().getValue(j));
                    writer.write("\"");
                }
                if (!data.getLabels().isEmpty()) {
                    writer.write(",");
                }
                writer.write(metadata.getPrometheusName());
                writer.write("=\"");
                writeEscapedLabelValue(writer, data.getName(i));
                writer.write("\"} ");
                if (data.isTrue(i)) {
                    writer.write("1");
                } else {
                    writer.write("0");
                }
                writeScrapeTimestampAndNewline(writer, data);
            }
        }
    }

    private void writeUnknown(OutputStreamWriter writer, UnknownSnapshot snapshot) throws IOException {
        MetricMetadata metadata = snapshot.getMetadata();
        writeMetadata(writer, "", "untyped", metadata);
        for (UnknownSnapshot.UnknownDataPointSnapshot data : snapshot.getData()) {
            writeNameAndLabels(writer, metadata.getPrometheusName(), null, data.getLabels());
            writeDouble(writer, data.getValue());
            writeScrapeTimestampAndNewline(writer, data);
        }
    }

    private void writeNameAndLabels(OutputStreamWriter writer, String name, String suffix, Labels labels) throws IOException {
        writeNameAndLabels(writer, name, suffix, labels, null, 0.0);
    }

    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);
        }
        if (!labels.isEmpty() || additionalLabelName != null) {
            writeLabels(writer, labels, additionalLabelName, additionalLabelValue);
        }
        writer.write(' ');
    }

    private void writeMetadata(OutputStreamWriter writer, String suffix, String typeString, MetricMetadata metadata) throws IOException {
        if (metadata.getHelp() != null && !metadata.getHelp().isEmpty()) {
            writer.write("# HELP ");
            writer.write(metadata.getPrometheusName());
            if (suffix != null) {
                writer.write(suffix);
            }
            writer.write(' ');
            writeEscapedHelp(writer, metadata.getHelp());
            writer.write('\n');
        }
        writer.write("# TYPE ");
        writer.write(metadata.getPrometheusName());
        if (suffix != null) {
            writer.write(suffix);
        }
        writer.write(' ');
        writer.write(typeString);
        writer.write('\n');
    }

    private void writeEscapedHelp(Writer writer, String s) throws IOException {
        for (int i = 0; i < s.length(); i++) {
            char c = s.charAt(i);
            switch (c) {
                case '\\':
                    writer.append("\\\\");
                    break;
                case '\n':
                    writer.append("\\n");
                    break;
                default:
                    writer.append(c);
            }
        }
    }

    private void writeScrapeTimestampAndNewline(OutputStreamWriter writer, DataPointSnapshot data) throws IOException {
        if (data.hasScrapeTimestamp()) {
            writer.write(' ');
            writeTimestamp(writer, data.getScrapeTimestampMillis());
        }
        writer.write('\n');
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy