├── .gitignore ├── LICENSE ├── README.md ├── pom.xml └── src ├── main └── java │ └── prometheus │ ├── PrometheusDataFormat.java │ ├── PrometheusMetricDataParser.java │ ├── PrometheusMetricsProcessor.java │ ├── PrometheusScraper.java │ ├── PrometheusScraperCli.java │ ├── Util.java │ ├── binary │ ├── BinaryPrometheusMetricDataParser.java │ └── BinaryPrometheusMetricsProcessor.java │ ├── text │ ├── TextPrometheusMetricDataParser.java │ ├── TextPrometheusMetricsProcessor.java │ └── TextSample.java │ ├── types │ ├── Counter.java │ ├── Gauge.java │ ├── Histogram.java │ ├── Metric.java │ ├── MetricFamily.java │ ├── MetricType.java │ └── Summary.java │ └── walkers │ ├── CollectorPrometheusMetricsWalker.java │ ├── JSONPrometheusMetricsWalker.java │ ├── LoggingPrometheusMetricsWalker.java │ ├── PrometheusMetricsWalker.java │ ├── SimplePrometheusMetricsWalker.java │ └── XMLPrometheusMetricsWalker.java └── test ├── java └── org │ └── hawkular │ └── agent │ └── prometheus │ ├── BinaryPrometheusParserTest.java │ ├── CounterTest.java │ ├── GaugeTest.java │ ├── HistogramTest.java │ ├── MetricFamilyTest.java │ ├── SummaryTest.java │ ├── TextPrometheusParserTest.java │ └── UtilTest.java └── resources ├── prometheus-counter.txt ├── prometheus-gauge.txt ├── prometheus-histogram.txt ├── prometheus-summary.txt ├── prometheus-three-counters.txt ├── prometheus.data └── prometheus.txt /.gitignore: -------------------------------------------------------------------------------- 1 | target/ 2 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2022 John Mazzitelli 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Prometheus Metrics Scraper 2 | 3 | This provides an API and a command line utility to scrape metrics from a Prometheus protocol endpoint. 4 | It supports both binary and text Prometheus data formats. 5 | 6 | ## Command Line Utility 7 | 8 | The command line utility is run via: 9 | 10 | ```` 11 | java -jar prometheus-scraper*-cli.jar [--simple | --xml | --json] 12 | ```` 13 | 14 | where `` is the Prometheus protocol endpoint (typically something like `http://localhost:9090/metrics`). 15 | Content negotiation will be used to establish the actual format to use. 16 | 17 | ## Java Scraper API 18 | 19 | You can programmatically scrape a URL via the Java class `prometheus.PrometheusScraper`. 20 | The `scrape()` method is usually what you want to use. 21 | If you want to process a stream of data from the URL endpoint, you can write your own `prometheus.walkers.PrometheusMetricsWalker` implementation and use the `scrape(walker)` method. 22 | 23 | ### Maven Dependency 24 | 25 | To obtain this Prometheus scraper, use the following Maven dependency: 26 | 27 | ````xml 28 | 29 | org.github.jmazzitelli 30 | prometheus-scraper 31 | #.#.# 32 | 33 | ```` 34 | ## Extending 35 | 36 | The current Prometheus Metrics Scraper supports the two main data formats - protocol buffer binary data and text data. Endpoints are allowed to support additional data formats (typically human-readable formats for debugging). You can extend this Prometheus Metrics Scraper to support those additional data formats. 37 | 38 | First, subclass `prometheus.PrometheusMetricDataParser` where T is the custom data representation of the metric family data with its metrics. You can optionally choose to use the common MetricFamily API (`prometheus.types.MetricFamily`) for this type. You must implement the `parse()` method which must read one metric family from the input stream per invocation. 39 | 40 | Second, subclass `prometheus.PrometheusMetricsProcessor` (where T is again the custom data representation of the metric family data or use the common MetricFamily class itself). Implement the `convert(T)` method to convert between the custom data representation of the metric family to the common API (or just return the object if you opted to use the common MetricFamily API). You must also implement `createPrometheusMetricDataParser()` to return an instance of the custom `PrometheusMetricDataParser` class (see above). 41 | 42 | To use your extension, create an input stream to your endpoint that contains the custom-formatted metric data, create a walker instance to walk your data (say, use the `prometheus.walkers.JSONPrometheusMetricsWalker` to generate a JSON document of your metric data or `prometheus.walkers.CollectorPrometheusMetricsWalker` to simply obtain a list of all metric families) and pass the stream and walker to your extension processor's constructor then call the `walk()` method. 43 | -------------------------------------------------------------------------------- /pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4.0.0 4 | 5 | org.github.jmazzitelli 6 | prometheus-scraper 7 | 1.0.0.Final 8 | 9 | Prometheus Scraper 10 | Java API that scrapes metrics from a Prometheus protocol endpoint. 11 | 12 | 13 | 14 | 15 | io.prometheus.client 16 | model 17 | 0.0.2 18 | 19 | 20 | 21 | junit 22 | junit 23 | test 24 | 4.12 25 | 26 | 27 | 28 | org.jboss.logging 29 | jboss-logging 30 | 3.3.0.Final 31 | 32 | 33 | 34 | 35 | 36 | 37 | 1.8 38 | 1.8 39 | 40 | 41 | 42 | 43 | 44 | src/main/resources 45 | false 46 | 47 | 48 | 49 | 50 | 51 | org.apache.maven.plugins 52 | maven-shade-plugin 53 | 2.4.3 54 | 55 | 56 | package 57 | 58 | shade 59 | 60 | 61 | true 62 | cli 63 | false 64 | true 65 | 66 | 67 | prometheus.PrometheusScraperCli 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | -------------------------------------------------------------------------------- /src/main/java/prometheus/PrometheusDataFormat.java: -------------------------------------------------------------------------------- 1 | package prometheus; 2 | 3 | /** 4 | * The supported Prometheus data formats. 5 | */ 6 | public enum PrometheusDataFormat { 7 | TEXT("plain/text"), // 8 | BINARY("application/vnd.google.protobuf; proto=io.prometheus.client.MetricFamily; encoding=delimited"); 9 | 10 | private final String contentType; 11 | 12 | private PrometheusDataFormat(String contentType) { 13 | this.contentType = contentType; 14 | } 15 | 16 | public String getContentType() { 17 | return contentType; 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /src/main/java/prometheus/PrometheusMetricDataParser.java: -------------------------------------------------------------------------------- 1 | package prometheus; 2 | 3 | import java.io.IOException; 4 | import java.io.InputStream; 5 | 6 | /** 7 | * An object that can parse Prometheus found in a specific data format in an input stream. 8 | * The type is the metric family object for the specific data format. 9 | * It is the job of the associated {@link PrometheusMetricsProcessor} to process 10 | * the parsed data. 11 | * 12 | * @param the metric family object type that the parser produces 13 | */ 14 | public abstract class PrometheusMetricDataParser { 15 | private InputStream inputStream; 16 | 17 | /** 18 | * Provides the input stream where the parser will look for metric data. 19 | * NOTE: this object will not own this stream - it should never attempt to close it. 20 | * 21 | * @param inputStream the stream where the metric data can be found 22 | */ 23 | public PrometheusMetricDataParser(InputStream inputStream) { 24 | if (inputStream == null) { 25 | throw new IllegalArgumentException("Stream must not be null"); 26 | } 27 | this.inputStream = inputStream; 28 | } 29 | 30 | protected InputStream getInputStream() { 31 | return this.inputStream; 32 | } 33 | 34 | /** 35 | * Reads a single metric family from the Prometheus metric data stream and returns it. 36 | * Returns null when no more data is in the stream. 37 | * 38 | * This method is designed to be called several times, each time it returns the next metric family 39 | * found in the input stream. 40 | * 41 | * @return the metric family data found in the stream, or null 42 | * @throws IOException if failed to read the data from the stream 43 | */ 44 | public abstract T parse() throws IOException; 45 | } 46 | -------------------------------------------------------------------------------- /src/main/java/prometheus/PrometheusMetricsProcessor.java: -------------------------------------------------------------------------------- 1 | package prometheus; 2 | 3 | import java.io.InputStream; 4 | 5 | import org.jboss.logging.Logger; 6 | import prometheus.types.Counter; 7 | import prometheus.types.Gauge; 8 | import prometheus.types.MetricFamily; 9 | import prometheus.walkers.PrometheusMetricsWalker; 10 | 11 | /** 12 | * A processor is responsible for iterating over a collection of metric families found in a specific 13 | * data format and invoking a walker during the iteration so the metric families can be processed. 14 | */ 15 | public abstract class PrometheusMetricsProcessor { 16 | private static final Logger log = Logger.getLogger(PrometheusMetricsProcessor.class); 17 | 18 | private final InputStream inputStream; 19 | private final PrometheusMetricsWalker walker; 20 | 21 | /** 22 | * @param inputStream where the Prometheus metrics are that the walker will traverse. 23 | * @param theWalker the actual object that will be notified about the metrics as they are encountered 24 | */ 25 | public PrometheusMetricsProcessor(InputStream inputStream, PrometheusMetricsWalker theWalker) { 26 | if (inputStream == null) { 27 | throw new IllegalArgumentException("Stream must not be null"); 28 | } 29 | this.inputStream = inputStream; 30 | 31 | if (theWalker == null) { 32 | throw new IllegalArgumentException("Walker must not be null"); 33 | } 34 | this.walker = theWalker; 35 | } 36 | 37 | /** 38 | * This will iterate over a set of metrics that are produced by the 39 | * {@link #createPrometheusMetricDataParser() parser} and will notify the {@link #getWalker() walker} 40 | * of each metric found. 41 | */ 42 | public void walk() { 43 | // tell the walker we are starting 44 | walker.walkStart(); 45 | 46 | int totalMetrics = 0; 47 | int familyIndex = 0; 48 | 49 | try { 50 | PrometheusMetricDataParser parser = createPrometheusMetricDataParser(); 51 | T metricFamily = parser.parse(); // prime the pump 52 | 53 | while (metricFamily != null) { 54 | prometheus.types.MetricFamily convertedMetricFamily = convert(metricFamily); 55 | 56 | // let the walker know we are traversing a new family of metrics 57 | walker.walkMetricFamily(convertedMetricFamily, familyIndex++); 58 | 59 | // walk through each metric in the family 60 | int metricIndex = 0; 61 | 62 | for (prometheus.types.Metric metric : convertedMetricFamily.getMetrics()) { 63 | switch (convertedMetricFamily.getType()) { 64 | case COUNTER: 65 | walker.walkCounterMetric(convertedMetricFamily, (Counter) metric, metricIndex); 66 | break; 67 | 68 | case GAUGE: 69 | walker.walkGaugeMetric(convertedMetricFamily, (Gauge) metric, metricIndex); 70 | break; 71 | 72 | case SUMMARY: 73 | walker.walkSummaryMetric(convertedMetricFamily, 74 | ((prometheus.types.Summary) metric), metricIndex); 75 | break; 76 | 77 | case HISTOGRAM: 78 | walker.walkHistogramMetric(convertedMetricFamily, 79 | ((prometheus.types.Histogram) metric), metricIndex); 80 | break; 81 | } 82 | 83 | metricIndex++; 84 | } 85 | 86 | // finished processing the metrics for the current family 87 | totalMetrics += convertedMetricFamily.getMetrics().size(); 88 | 89 | // go to the next metric family 90 | metricFamily = parser.parse(); 91 | } 92 | } catch (Exception e) { 93 | log.debugf(e, "Error while processing binary data"); 94 | } 95 | 96 | // tell the walker we have finished 97 | walker.walkFinish(familyIndex, totalMetrics); 98 | } 99 | 100 | /** 101 | * @return the input stream where the metric family data in a specific data format is found 102 | */ 103 | protected InputStream getInputStream() { 104 | return inputStream; 105 | } 106 | 107 | /** 108 | * @return the object that will iterate over the found metric data 109 | */ 110 | protected PrometheusMetricsWalker getWalker() { 111 | return walker; 112 | } 113 | 114 | /** 115 | * @return a new parser instance that can be used to parse the formatted data 116 | * found in the {@link #getInputStream() input stream}. 117 | */ 118 | protected abstract PrometheusMetricDataParser createPrometheusMetricDataParser(); 119 | 120 | /** 121 | * This method converts the metrics from the specific data format found in the input stream 122 | * to the common metric format. 123 | * 124 | * @param metricFamily the metric family (and its metrics) that need to be converted 125 | * @return the common MetricFamily object 126 | */ 127 | protected abstract MetricFamily convert(T metricFamily); 128 | } 129 | -------------------------------------------------------------------------------- /src/main/java/prometheus/PrometheusScraper.java: -------------------------------------------------------------------------------- 1 | package prometheus; 2 | 3 | import java.io.File; 4 | import java.io.IOException; 5 | import java.io.InputStream; 6 | import java.net.MalformedURLException; 7 | import java.net.URL; 8 | import java.net.URLConnection; 9 | import java.util.List; 10 | 11 | import org.jboss.logging.Logger; 12 | import prometheus.binary.BinaryPrometheusMetricsProcessor; 13 | import prometheus.text.TextPrometheusMetricsProcessor; 14 | import prometheus.types.MetricFamily; 15 | import prometheus.walkers.CollectorPrometheusMetricsWalker; 16 | import prometheus.walkers.PrometheusMetricsWalker; 17 | 18 | /** 19 | * Given a Prometheus protocol endpoint, this will scrape the Prometheus data it finds there. 20 | * {@link #scrape()} is typically the API most consumers will want to use. Given a URL or file, this is 21 | * able to give you all the metric data found there, regardless of the format of the data. 22 | */ 23 | public class PrometheusScraper { 24 | private static final Logger log = Logger.getLogger(PrometheusScraper.class); 25 | 26 | private final URL url; 27 | private final PrometheusDataFormat knownDataFormat; 28 | 29 | // see openConnection() for where this is used 30 | protected class OpenConnectionDetails { 31 | public final InputStream inputStream; 32 | public final String contentType; 33 | 34 | public OpenConnectionDetails(InputStream is, String contentType) { 35 | this.inputStream = is; 36 | this.contentType = contentType; 37 | } 38 | } 39 | 40 | public PrometheusScraper(String host, int port, String context) throws MalformedURLException { 41 | if (host == null) { 42 | host = "127.0.0.1"; 43 | } 44 | if (port == 0) { 45 | port = 9090; 46 | } 47 | if (context == null || context.isEmpty()) { 48 | context = "/metrics"; 49 | } 50 | this.url = new URL("http", host, port, context); 51 | this.knownDataFormat = null; 52 | log.debugf("Will scrape Permetheus data from URL [%s]", this.url); 53 | } 54 | 55 | public PrometheusScraper(URL url) { 56 | this(url, null); 57 | } 58 | 59 | /** 60 | * This constructor allows you to explicitly indicate what data format is expected. 61 | * If the URL cannot provide a content type, this data format will determine what data format 62 | * will be assumed. If the URL does provide a content type, the given data format is ignored. 63 | * This is useful if you are providing a URL that actually refers to a file in which case 64 | * the URL connection will not be able to provide a content type. 65 | * 66 | * @see #PrometheusScraperUrl(File, PrometheusDataFormat) 67 | * 68 | * @param url the URL where the Prometheus metric data is found 69 | * @param dataFormat the data format of the metric data found at the URL, or null if 70 | * the URL endpoint can provide it for us via content negotiation. 71 | */ 72 | public PrometheusScraper(URL url, PrometheusDataFormat dataFormat) { 73 | if (url == null) { 74 | throw new IllegalArgumentException("URL must not be null"); 75 | } 76 | this.url = url; 77 | this.knownDataFormat = dataFormat; 78 | log.debugf("Will scrape Permetheus data from URL [%s] with data format [%s]", 79 | this.url, (this.knownDataFormat == null) ? "" : this.knownDataFormat); 80 | } 81 | 82 | /** 83 | * Scrape data from the given file. The data format will indicate if it 84 | * is binary protocol buffer data or text data ("text/plain"). 85 | * 86 | * @param file the file to scrape 87 | * @param dataFormat the format of the metric data in the file. 88 | */ 89 | public PrometheusScraper(File file, PrometheusDataFormat dataFormat) { 90 | if (file == null) { 91 | throw new IllegalArgumentException("File must not be null"); 92 | } 93 | if (dataFormat == null) { 94 | throw new IllegalArgumentException("Must provide the content type for the file"); 95 | } 96 | 97 | try { 98 | this.url = file.toURI().toURL(); 99 | } catch (MalformedURLException e) { 100 | throw new IllegalArgumentException("File does not have valid URL: " + file); 101 | } 102 | 103 | this.knownDataFormat = dataFormat; 104 | 105 | log.debugf("Will scrape Permetheus data from file [%s] with data format [%s]", this.url, 106 | this.knownDataFormat); 107 | } 108 | 109 | /** 110 | * This will collect all metric data from the endpoint and 111 | * return the entire list of all metric families found there. 112 | * @return all metric data found at the endpoint 113 | * @throws IOException if failed to scrape data 114 | */ 115 | public List scrape() throws IOException { 116 | CollectorPrometheusMetricsWalker collector = new CollectorPrometheusMetricsWalker(); 117 | scrape(collector); 118 | return collector.getAllMetricFamilies(); 119 | } 120 | 121 | public void scrape(PrometheusMetricsWalker walker) throws IOException { 122 | OpenConnectionDetails connectionDetails = openConnection(this.url); 123 | if (connectionDetails == null || connectionDetails.inputStream == null) { 124 | throw new IOException("Failed to open the connection to the Prometheus endpoint"); 125 | } 126 | 127 | try (InputStream inputStream = connectionDetails.inputStream) { 128 | String contentType = connectionDetails.contentType; 129 | 130 | // if we were given a content type - we use it always. If we were not given a content type, 131 | // then use the one given to the constructor (if one was given). 132 | if (contentType == null || contentType.contains("unknown")) { 133 | contentType = this.knownDataFormat.getContentType(); 134 | } 135 | 136 | PrometheusMetricsProcessor processor; 137 | 138 | if (contentType.contains("application/vnd.google.protobuf")) { 139 | processor = new BinaryPrometheusMetricsProcessor(inputStream, walker); 140 | } else if (contentType.contains("text/plain")) { 141 | processor = new TextPrometheusMetricsProcessor(inputStream, walker); 142 | } else { 143 | // unknown - since all Prometheus endpoints are required to support text, try it 144 | log.debugf("Unknown content type for URL [%s]. Trying text format.", url); 145 | processor = new TextPrometheusMetricsProcessor(inputStream, walker); 146 | } 147 | 148 | processor.walk(); 149 | } 150 | } 151 | 152 | /** 153 | * This is the content type of the supported Prometheus binary format. 154 | * This can be used in the Accept header when making the HTTP request to the Prometheus endpoint. 155 | * 156 | * @return binary format content type 157 | */ 158 | protected String getBinaryFormatContentType() { 159 | return "application/vnd.google.protobuf; proto=io.prometheus.client.MetricFamily; encoding=delimited"; 160 | } 161 | 162 | /** 163 | * This is the content type of the supported Prometheus text format. 164 | * This can be used in the Accept header when making the HTTP request to the Prometheus endpoint. 165 | * 166 | * @return text format content type 167 | */ 168 | protected String getTextFormatContentType() { 169 | return "text/plain; version 0.0.4"; 170 | } 171 | 172 | /** 173 | * This provides a hook for subclasses to be able to connect to the Prometheus endpoint 174 | * and tell us what the content type is and to give us the actual stream to the data. 175 | * 176 | * This is useful in case callers need to connect securely to the Prometheus endpoint. 177 | * Subclasses need to open the secure connection with their specific secure credentials 178 | * and other security details and return the input stream to the data (as well as its content type). 179 | * 180 | * If subclasses return a null content type in the returned object the data format passed to this 181 | * object's constructor will be assumed as the data format in the input stream. 182 | * 183 | * The default implementation is to simply open an unsecured connection to the URL. 184 | * 185 | * @param url the Prometheus endpoint 186 | * @return connection details for the Prometheus endpoint 187 | * 188 | * @throws IOException if the connection could not be opened 189 | */ 190 | protected OpenConnectionDetails openConnection(URL endpointUrl) throws IOException { 191 | URLConnection conn = endpointUrl.openConnection(); 192 | conn.setRequestProperty("Accept", getBinaryFormatContentType()); 193 | InputStream stream = conn.getInputStream(); 194 | String contentType = conn.getContentType(); 195 | return new OpenConnectionDetails(stream, contentType); 196 | } 197 | } 198 | -------------------------------------------------------------------------------- /src/main/java/prometheus/PrometheusScraperCli.java: -------------------------------------------------------------------------------- 1 | package prometheus; 2 | 3 | import java.net.URL; 4 | 5 | import org.jboss.logging.Logger.Level; 6 | import prometheus.walkers.JSONPrometheusMetricsWalker; 7 | import prometheus.walkers.LoggingPrometheusMetricsWalker; 8 | import prometheus.walkers.PrometheusMetricsWalker; 9 | import prometheus.walkers.SimplePrometheusMetricsWalker; 10 | import prometheus.walkers.XMLPrometheusMetricsWalker; 11 | 12 | /** 13 | * This is a command line utility that can scrape a Prometheus protocol endpoint and outputs the metric data it finds. 14 | * You provide a single required argument on the command line - the URL of the Prometheus protocol endpoint, which is 15 | * typically something like "http://localhost:9090/metrics". 16 | */ 17 | public class PrometheusScraperCli { 18 | 19 | enum PrometheusMetricsWalkerType { 20 | LOG, SIMPLE, XML, JSON 21 | } 22 | 23 | public static void main(String[] args) throws Exception { 24 | if (args.length == 0) { 25 | throw new Exception("Specify the URL of the Prometheus protocol endpoint."); 26 | } 27 | 28 | PrometheusMetricsWalkerType walkerType = PrometheusMetricsWalkerType.SIMPLE; 29 | URL url = null; 30 | 31 | for (String arg : args) { 32 | if (arg.startsWith("--")) { 33 | if (arg.equalsIgnoreCase("--xml")) { 34 | walkerType = PrometheusMetricsWalkerType.XML; 35 | } else if (arg.equalsIgnoreCase("--json")) { 36 | walkerType = PrometheusMetricsWalkerType.JSON; 37 | } else if (arg.equalsIgnoreCase("--simple")) { 38 | walkerType = PrometheusMetricsWalkerType.SIMPLE; 39 | } else if (arg.equalsIgnoreCase("--log")) { 40 | walkerType = PrometheusMetricsWalkerType.LOG; 41 | } else { 42 | throw new Exception("Invalid argument: " + arg); 43 | } 44 | } else { 45 | url = new URL(arg); 46 | break; 47 | } 48 | } 49 | 50 | if (url == null) { 51 | throw new Exception("Specify the URL of the Prometheus protocol endpoint."); 52 | } 53 | 54 | PrometheusMetricsWalker walker; 55 | switch (walkerType) { 56 | case SIMPLE: 57 | walker = new SimplePrometheusMetricsWalker(url); 58 | break; 59 | case XML: 60 | walker = new XMLPrometheusMetricsWalker(url); 61 | break; 62 | case JSON: 63 | walker = new JSONPrometheusMetricsWalker(); 64 | break; 65 | case LOG: 66 | walker = new LoggingPrometheusMetricsWalker(Level.INFO); 67 | break; 68 | default: 69 | throw new Exception("Invalid walker type: " + walkerType); 70 | } 71 | 72 | PrometheusScraper scraper = new PrometheusScraper(url); 73 | scraper.scrape(walker); 74 | } 75 | 76 | } 77 | -------------------------------------------------------------------------------- /src/main/java/prometheus/Util.java: -------------------------------------------------------------------------------- 1 | package prometheus; 2 | 3 | public class Util { 4 | 5 | public static double convertStringToDouble(String valueString) { 6 | double doubleValue; 7 | if (valueString.equalsIgnoreCase("NaN")) { 8 | doubleValue = Double.NaN; 9 | } else if (valueString.equalsIgnoreCase("+Inf")) { 10 | doubleValue = Double.POSITIVE_INFINITY; 11 | } else if (valueString.equalsIgnoreCase("-Inf")) { 12 | doubleValue = Double.NEGATIVE_INFINITY; 13 | } else { 14 | doubleValue = Double.valueOf(valueString).doubleValue(); 15 | } 16 | return doubleValue; 17 | } 18 | 19 | public static String convertDoubleToString(double value) { 20 | // Prometheus spec requires positive infinity to be denoted as "+Inf" and negative infinity as "-Inf" 21 | if (Double.isInfinite(value)) { 22 | return (value < 0.0) ? "-Inf" : "+Inf"; 23 | } 24 | return String.format("%f", value); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /src/main/java/prometheus/binary/BinaryPrometheusMetricDataParser.java: -------------------------------------------------------------------------------- 1 | package prometheus.binary; 2 | 3 | import java.io.IOException; 4 | import java.io.InputStream; 5 | 6 | import io.prometheus.client.Metrics; 7 | import io.prometheus.client.Metrics.MetricFamily; 8 | import prometheus.PrometheusMetricDataParser; 9 | 10 | /** 11 | * Provides a method that can scrape Permetheus binary metric data from input streams. 12 | */ 13 | public class BinaryPrometheusMetricDataParser extends PrometheusMetricDataParser { 14 | 15 | /** 16 | * Provides the input stream where the parser will look for metric data. 17 | * NOTE: this object will not own this stream - it will never attempt to close it. 18 | * 19 | * @param inputStream the stream where the metric data can be found 20 | */ 21 | public BinaryPrometheusMetricDataParser(InputStream inputStream) { 22 | super(inputStream); 23 | } 24 | 25 | public MetricFamily parse() throws IOException { 26 | return Metrics.MetricFamily.parseDelimitedFrom(getInputStream()); 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /src/main/java/prometheus/binary/BinaryPrometheusMetricsProcessor.java: -------------------------------------------------------------------------------- 1 | package prometheus.binary; 2 | 3 | import java.io.InputStream; 4 | import java.util.ArrayList; 5 | import java.util.List; 6 | 7 | import io.prometheus.client.Metrics.LabelPair; 8 | import io.prometheus.client.Metrics.Metric; 9 | import io.prometheus.client.Metrics.MetricFamily; 10 | import io.prometheus.client.Metrics.Quantile; 11 | import io.prometheus.client.Metrics.Summary; 12 | import prometheus.PrometheusMetricsProcessor; 13 | import prometheus.types.MetricType; 14 | import prometheus.walkers.PrometheusMetricsWalker; 15 | 16 | /** 17 | * This will iterate over a list of Prometheus metrics that are given as binary protocol buffer data. 18 | */ 19 | public class BinaryPrometheusMetricsProcessor extends PrometheusMetricsProcessor { 20 | public BinaryPrometheusMetricsProcessor(InputStream inputStream, PrometheusMetricsWalker theWalker) { 21 | super(inputStream, theWalker); 22 | } 23 | 24 | @Override 25 | public BinaryPrometheusMetricDataParser createPrometheusMetricDataParser() { 26 | return new BinaryPrometheusMetricDataParser(getInputStream()); 27 | } 28 | 29 | @Override 30 | protected prometheus.types.MetricFamily convert(MetricFamily family) { 31 | prometheus.types.MetricFamily.Builder convertedFamilyBuilder; 32 | MetricType convertedFamilyType = MetricType.valueOf(family.getType().name()); 33 | 34 | convertedFamilyBuilder = new prometheus.types.MetricFamily.Builder(); 35 | convertedFamilyBuilder.setName(family.getName()); 36 | convertedFamilyBuilder.setHelp(family.getHelp()); 37 | convertedFamilyBuilder.setType(convertedFamilyType); 38 | 39 | for (Metric metric : family.getMetricList()) { 40 | prometheus.types.Metric.Builder convertedMetricBuilder = null; 41 | switch (convertedFamilyType) { 42 | case COUNTER: 43 | convertedMetricBuilder = new prometheus.types.Counter.Builder() 44 | .setValue(metric.getCounter().getValue()); 45 | break; 46 | case GAUGE: 47 | convertedMetricBuilder = new prometheus.types.Gauge.Builder() 48 | .setValue(metric.getGauge().getValue()); 49 | break; 50 | case SUMMARY: 51 | Summary summary = metric.getSummary(); 52 | List pqList = summary.getQuantileList(); 53 | List hqList; 54 | hqList = new ArrayList<>(pqList.size()); 55 | for (Quantile pq : pqList) { 56 | prometheus.types.Summary.Quantile hq; 57 | hq = new prometheus.types.Summary.Quantile( 58 | pq.getQuantile(), pq.getValue()); 59 | hqList.add(hq); 60 | } 61 | convertedMetricBuilder = new prometheus.types.Summary.Builder() 62 | .setSampleCount(metric.getSummary().getSampleCount()) 63 | .setSampleSum(metric.getSummary().getSampleSum()) 64 | .addQuantiles(hqList); 65 | break; 66 | case HISTOGRAM: 67 | /* NO HISTOGRAM SUPPORT IN PROMETHEUS JAVA MODEL API 0.0.2. Uncomment when 0.0.3 is released 68 | Histogram histo = metric.getHistogram(); 69 | List pbList = histo.getBucketList(); 70 | List hbList; 71 | hbList = new ArrayList<>(pbList.size()); 72 | for (Bucket pb : pbList) { 73 | prometheus.types.Histogram.Bucket hb; 74 | hb = new prometheus.types.Histogram.Bucket(pb.getUpperBound(), 75 | pb.getCumulativeCount()); 76 | hbList.add(hb); 77 | } 78 | convertedMetricBuilder = new prometheus.types.Histogram.Builder() 79 | .setSampleCount(metric.getHistogram().getSampleCount()) 80 | .setSampleSum(metric.getHistogram().getSampleSum()) 81 | .addBuckets(hbList); 82 | */ 83 | break; 84 | } 85 | convertedMetricBuilder.setName(family.getName()); 86 | for (LabelPair labelPair : metric.getLabelList()) { 87 | convertedMetricBuilder.addLabel(labelPair.getName(), labelPair.getValue()); 88 | } 89 | convertedFamilyBuilder.addMetric(convertedMetricBuilder.build()); 90 | } 91 | 92 | return convertedFamilyBuilder.build(); 93 | } 94 | } 95 | -------------------------------------------------------------------------------- /src/main/java/prometheus/text/TextPrometheusMetricDataParser.java: -------------------------------------------------------------------------------- 1 | package prometheus.text; 2 | 3 | import java.io.ByteArrayOutputStream; 4 | import java.io.IOException; 5 | import java.io.InputStream; 6 | import java.util.ArrayList; 7 | import java.util.LinkedHashMap; 8 | import java.util.List; 9 | import java.util.Map; 10 | 11 | import org.jboss.logging.Logger; 12 | import prometheus.PrometheusMetricDataParser; 13 | import prometheus.Util; 14 | import prometheus.types.Counter; 15 | import prometheus.types.Gauge; 16 | import prometheus.types.Histogram; 17 | import prometheus.types.Metric; 18 | import prometheus.types.MetricFamily; 19 | import prometheus.types.MetricType; 20 | import prometheus.types.Summary; 21 | 22 | /** 23 | * Provides a method that can scrape Permetheus text metric data from input streams. 24 | */ 25 | public class TextPrometheusMetricDataParser extends PrometheusMetricDataParser { 26 | private static final Logger log = Logger.getLogger(TextPrometheusMetricDataParser.class); 27 | 28 | private String lastLineReadFromStream; // this is only set when we break from the while loop in parse() 29 | 30 | /** 31 | * Provides the input stream where the parser will look for metric data. 32 | * NOTE: this object will not own this stream - it should never attempt to close it. 33 | * 34 | * @param inputStream the stream where the metric data can be found 35 | */ 36 | public TextPrometheusMetricDataParser(InputStream inputStream) { 37 | super(inputStream); 38 | } 39 | 40 | private class ParserContext { 41 | // this is the metric family that has been fully built 42 | public MetricFamily finishedMetricFamily; 43 | 44 | // these are used when building a metric family 45 | public String name = ""; 46 | public String help = ""; 47 | public MetricType type = null; 48 | public List allowedNames = new ArrayList<>(); 49 | public List textSamples = new ArrayList<>(); 50 | 51 | // starts a fresh metric family 52 | public void clear() { 53 | name = ""; 54 | help = ""; 55 | type = null; 56 | allowedNames.clear(); 57 | textSamples.clear(); 58 | } 59 | 60 | // complete the construction of the metric family 61 | public void finishMetricFamily() { 62 | if (finishedMetricFamily != null) { 63 | return; 64 | } 65 | 66 | MetricFamily.Builder metricFamilyBuilder = new MetricFamily.Builder(); 67 | metricFamilyBuilder.setName(name); 68 | metricFamilyBuilder.setHelp(help); 69 | metricFamilyBuilder.setType(type); 70 | 71 | // need to convert the samples to metrics 72 | // We know if the family is a counter or a gauge, all samples are full metrics 73 | // so we can convert them easily one-for-one. 74 | // For summary metrics, we need to combine all quantile samples, sum, and count. 75 | // For histogram metrics, we need to combine all bucket samples, sum, and count. 76 | 77 | Map, Metric.Builder> builders = new LinkedHashMap<>(); 78 | 79 | for (TextSample textSample : textSamples) { 80 | try { 81 | switch (type) { 82 | case COUNTER: 83 | builders.put(textSample.getLabels(), 84 | new Counter.Builder().setName(name) 85 | .setValue(Util.convertStringToDouble(textSample.getValue())) 86 | .addLabels(textSample.getLabels())); 87 | break; 88 | case GAUGE: 89 | builders.put(textSample.getLabels(), 90 | new Gauge.Builder().setName(name) 91 | .setValue(Util.convertStringToDouble(textSample.getValue())) 92 | .addLabels(textSample.getLabels())); 93 | break; 94 | case SUMMARY: 95 | // Get the builder that we are using to build up the current metric. Remember we need to 96 | // get the builder for this specific metric identified with a unique set of labels. 97 | 98 | // First we need to remove any existing quantile label since it isn't a "real" label. 99 | // This is to ensure our lookup uses all but only "real" labels. 100 | String quantileValue = textSample.getLabels().remove("quantile"); // may be null 101 | 102 | Summary.Builder sBuilder = (Summary.Builder) builders.get(textSample.getLabels()); 103 | if (sBuilder == null) { 104 | sBuilder = new Summary.Builder(); 105 | builders.put(textSample.getLabels(), sBuilder); 106 | } 107 | sBuilder.setName(name); 108 | sBuilder.addLabels(textSample.getLabels()); 109 | if (textSample.getName().endsWith("_count")) { 110 | sBuilder.setSampleCount((long)Util.convertStringToDouble(textSample.getValue())); 111 | } else if (textSample.getName().endsWith("_sum")) { 112 | sBuilder.setSampleSum(Util.convertStringToDouble(textSample.getValue())); 113 | } else { 114 | // This must be a quantile sample 115 | if (quantileValue == null) { 116 | log.debugf("Summary quantile sample is missing the 'quantile' label: %s", 117 | textSample.getLine()); 118 | } 119 | sBuilder.addQuantile(Util.convertStringToDouble(quantileValue), 120 | Util.convertStringToDouble(textSample.getValue())); 121 | } 122 | break; 123 | case HISTOGRAM: 124 | // Get the builder that we are using to build up the current metric. Remember we need to 125 | // get the builder for this specific metric identified with a unique set of labels. 126 | 127 | // First we need to remove any existing le label since it isn't a "real" label. 128 | // This is to ensure our lookup uses all but only "real" labels. 129 | String bucket = textSample.getLabels().remove("le"); // may be null 130 | 131 | Histogram.Builder hBuilder = (Histogram.Builder) builders.get(textSample.getLabels()); 132 | if (hBuilder == null) { 133 | hBuilder = new Histogram.Builder(); 134 | builders.put(textSample.getLabels(), hBuilder); 135 | } 136 | hBuilder.setName(name); 137 | hBuilder.addLabels(textSample.getLabels()); 138 | if (textSample.getName().endsWith("_count")) { 139 | hBuilder.setSampleCount((long)Util.convertStringToDouble(textSample.getValue())); 140 | } else if (textSample.getName().endsWith("_sum")) { 141 | hBuilder.setSampleSum(Util.convertStringToDouble(textSample.getValue())); 142 | } else { 143 | // This must be a bucket sample 144 | if (bucket == null) { 145 | throw new Exception("Histogram bucket sample is missing the 'le' label"); 146 | } 147 | hBuilder.addBucket(Util.convertStringToDouble(bucket), 148 | (long)Util.convertStringToDouble(textSample.getValue())); 149 | } 150 | break; 151 | } 152 | } catch (Exception e) { 153 | log.debugf(e, "Error processing sample. This metric sample will be ignored: %s", 154 | textSample.getLine()); 155 | } 156 | } 157 | 158 | // now that we've combined everything into individual metric builders, we can build all our metrics 159 | for (Metric.Builder builder : builders.values()) { 160 | try { 161 | metricFamilyBuilder.addMetric(builder.build()); 162 | } catch (Exception e) { 163 | log.debugf(e, "Error building metric for metric family [%s] - it will be ignored", name); 164 | } 165 | } 166 | 167 | finishedMetricFamily = metricFamilyBuilder.build(); 168 | } 169 | } 170 | 171 | @Override 172 | public MetricFamily parse() throws IOException { 173 | 174 | // determine the first line we should process. If we were previously called, we already 175 | // read a line - start from that last line read. Otherwise, prime the pump and read 176 | // the first line from the stream. 177 | String line; 178 | if (lastLineReadFromStream != null) { 179 | line = lastLineReadFromStream; 180 | lastLineReadFromStream = null; 181 | } else { 182 | line = readLine(getInputStream()); 183 | } 184 | 185 | if (line == null) { 186 | return null; 187 | } 188 | 189 | // do a quick check to see if we are getting passed in binary format rather than text 190 | if (!line.isEmpty() && !new String(new char[] { line.charAt(0) }).matches("\\p{ASCII}*")) { 191 | throw new IOException("Doesn't look like the metric data is in text format"); 192 | } 193 | 194 | ParserContext context = new ParserContext(); 195 | 196 | while (line != null) { 197 | line = line.trim(); 198 | 199 | try { 200 | if (line.isEmpty()) { 201 | // ignore blank lines 202 | } else if (line.charAt(0) == '#') { 203 | String[] parts = line.split("[ \t]+", 4); // 0 is #, 1 is HELP or TYPE, 2 is metric name, 3 is doc 204 | if (parts.length < 2) { 205 | // ignore line - probably a comment 206 | } else if (parts[1].equals("HELP")) { 207 | if (!parts[2].equals(context.name)) { 208 | // we are hitting a new metric family 209 | if (!context.name.isEmpty()) { 210 | // break and we'll finish the metric family we previously were building up 211 | this.lastLineReadFromStream = line; 212 | break; 213 | } 214 | // start anew 215 | context.clear(); 216 | context.name = parts[2]; 217 | context.type = MetricType.GAUGE; // default in case we don't get a TYPE 218 | context.allowedNames.add(parts[2]); 219 | } 220 | 221 | if (parts.length == 4) { 222 | context.help = unescapeHelp(parts[3]); 223 | } else { 224 | context.help = ""; 225 | } 226 | } else if (parts[1].equals("TYPE")) { 227 | if (!parts[2].equals(context.name)) { 228 | if (!context.name.isEmpty()) { 229 | // break and we'll finish the metric family we previously were building up 230 | this.lastLineReadFromStream = line; 231 | break; 232 | } 233 | // start anew 234 | context.clear(); 235 | context.name = parts[2]; 236 | } 237 | context.type = MetricType.valueOf(parts[3].toUpperCase()); 238 | context.allowedNames.clear(); 239 | switch (context.type) { 240 | case COUNTER: 241 | context.allowedNames.add(context.name); 242 | break; 243 | case GAUGE: 244 | context.allowedNames.add(context.name); 245 | break; 246 | case SUMMARY: 247 | context.allowedNames.add(context.name + "_count"); 248 | context.allowedNames.add(context.name + "_sum"); 249 | context.allowedNames.add(context.name); 250 | break; 251 | case HISTOGRAM: 252 | context.allowedNames.add(context.name + "_count"); 253 | context.allowedNames.add(context.name + "_sum"); 254 | context.allowedNames.add(context.name + "_bucket"); 255 | break; 256 | } 257 | } else { 258 | // ignore other tokens - probably a comment 259 | } 260 | } else { 261 | // parse the sample line that contains a single metric (or part of a metric as in summary/histo) 262 | TextSample sample = parseSampleLine(line); 263 | if (!context.allowedNames.contains(sample.getName())) { 264 | if (!context.name.isEmpty()) { 265 | // break and we'll finish the metric family we previously were building up 266 | this.lastLineReadFromStream = line; 267 | break; 268 | } 269 | context.clear(); 270 | log.debugf("Ignoring an unexpected metric: " + line); 271 | } else { 272 | // add the sample to the family we are building up 273 | context.textSamples.add(sample); 274 | } 275 | } 276 | } catch (Exception e) { 277 | log.debugf("Failed to process line - it will be ignored: %s", line); 278 | } 279 | 280 | // go to the next line 281 | line = readLine(getInputStream()); 282 | } 283 | 284 | if (!context.name.isEmpty()) { 285 | // finish the metric family we previously were building up 286 | context.finishMetricFamily(); 287 | } 288 | 289 | return context.finishedMetricFamily; 290 | } 291 | 292 | private TextSample parseSampleLine(String line) { 293 | // algorithm from parser.py 294 | StringBuilder name = new StringBuilder(); 295 | StringBuilder labelname = new StringBuilder(); 296 | StringBuilder labelvalue = new StringBuilder(); 297 | StringBuilder value = new StringBuilder(); 298 | Map labels = new LinkedHashMap<>(); 299 | 300 | String state = "name"; 301 | 302 | for (int c = 0; c < line.length(); c++) { 303 | char charAt = line.charAt(c); 304 | if (state.equals("name")) { 305 | if (charAt == '{') { 306 | state = "startoflabelname"; 307 | } else if (charAt == ' ' || charAt == '\t') { 308 | state = "endofname"; 309 | } else { 310 | name.append(charAt); 311 | } 312 | } else if (state.equals("endofname")) { 313 | if (charAt == ' ' || charAt == '\t') { 314 | // do nothing 315 | } else if (charAt == '{') { 316 | state = "startoflabelname"; 317 | } else { 318 | value.append(charAt); 319 | state = "value"; 320 | } 321 | } else if (state.equals("startoflabelname")) { 322 | if (charAt == ' ' || charAt == '\t') { 323 | // do nothing 324 | } else if (charAt == '}') { 325 | state = "endoflabels"; 326 | } else { 327 | labelname.append(charAt); 328 | state = "labelname"; 329 | } 330 | } else if (state.equals("labelname")) { 331 | if (charAt == '=') { 332 | state = "labelvaluequote"; 333 | } else if (charAt == '}') { 334 | state = "endoflabels"; 335 | } else if (charAt == ' ' || charAt == '\t') { 336 | state = "labelvalueequals"; 337 | } else { 338 | labelname.append(charAt); 339 | } 340 | } else if (state.equals("labelvalueequals")) { 341 | if (charAt == '=') { 342 | state = "labelvaluequote"; 343 | } else if (charAt == ' ' || charAt == '\t') { 344 | // do nothing 345 | } else { 346 | throw new IllegalStateException("Invalid line: " + line); 347 | } 348 | } else if (state.equals("labelvaluequote")) { 349 | if (charAt == '"') { 350 | state = "labelvalue"; 351 | } else if (charAt == ' ' || charAt == '\t') { 352 | // do nothing 353 | } else { 354 | throw new IllegalStateException("Invalid line: " + line); 355 | } 356 | } else if (state.equals("labelvalue")) { 357 | if (charAt == '\\') { 358 | state = "labelvalueslash"; 359 | } else if (charAt == '"') { 360 | labels.put(labelname.toString(), labelvalue.toString()); 361 | labelname.setLength(0); 362 | labelvalue.setLength(0); 363 | state = "nextlabel"; 364 | } else { 365 | labelvalue.append(charAt); 366 | } 367 | } else if (state.equals("labelvalueslash")) { 368 | state = "labelvalue"; 369 | if (charAt == '\\') { 370 | labelvalue.append('\\'); 371 | } else if (charAt == 'n') { 372 | labelvalue.append('\n'); 373 | } else if (charAt == '"') { 374 | labelvalue.append('"'); 375 | } else { 376 | labelvalue.append('\\').append(charAt); 377 | } 378 | } else if (state.equals("nextlabel")) { 379 | if (charAt == ',') { 380 | state = "labelname"; 381 | } else if (charAt == '}') { 382 | state = "endoflabels"; 383 | } else if (charAt == ' ' || charAt == '\t') { 384 | // do nothing 385 | } else { 386 | throw new IllegalStateException("Invalid line: " + line); 387 | } 388 | } else if (state.equals("endoflabels")) { 389 | if (charAt == ' ' || charAt == '\t') { 390 | // do nothing 391 | } else { 392 | value.append(charAt); 393 | state = "value"; 394 | } 395 | } else if (state.equals("value")) { 396 | if (charAt == ' ' || charAt == '\t') { 397 | break; // timestamps are NOT supported - ignoring 398 | } else { 399 | value.append(charAt); 400 | } 401 | } 402 | } 403 | 404 | TextSample sample = new TextSample.Builder() 405 | .setLine(line) 406 | .setName(name.toString()) 407 | .setValue(value.toString()) 408 | .addLabels(labels).build(); 409 | 410 | return sample; 411 | } 412 | 413 | private String unescapeHelp(String text) { 414 | // algorithm from parser.py 415 | if (text == null || !text.contains("\\")) { 416 | return text; 417 | } 418 | 419 | StringBuilder result = new StringBuilder(); 420 | boolean slash = false; 421 | for (int c = 0; c < text.length(); c++) { 422 | char charAt = text.charAt(c); 423 | if (slash) { 424 | if (charAt == '\\') { 425 | result.append('\\'); 426 | } else if (charAt == 'n') { 427 | result.append('\n'); 428 | } else { 429 | result.append('\\').append(charAt); 430 | } 431 | slash = false; 432 | } else { 433 | if (charAt == '\\') { 434 | slash = true; 435 | } else { 436 | result.append(charAt); 437 | } 438 | } 439 | } 440 | if (slash) { 441 | result.append("\\"); 442 | } 443 | return result.toString(); 444 | } 445 | 446 | private String readLine(InputStream inputStream) throws IOException { 447 | int lineChar; 448 | ByteArrayOutputStream baos = new ByteArrayOutputStream(); 449 | 450 | // Prometheus end of line character is a newline 451 | for (lineChar = inputStream.read(); (lineChar != '\n' && lineChar != -1); lineChar = inputStream.read()) { 452 | baos.write(lineChar); 453 | } 454 | 455 | if (lineChar == -1 && baos.size() == 0) { 456 | // EOF 457 | return null; 458 | } 459 | 460 | return baos.toString("UTF-8"); 461 | } 462 | 463 | } 464 | -------------------------------------------------------------------------------- /src/main/java/prometheus/text/TextPrometheusMetricsProcessor.java: -------------------------------------------------------------------------------- 1 | package prometheus.text; 2 | 3 | import java.io.InputStream; 4 | 5 | import prometheus.PrometheusMetricsProcessor; 6 | import prometheus.types.MetricFamily; 7 | import prometheus.walkers.PrometheusMetricsWalker; 8 | 9 | /** 10 | * This will iterate over a list of Prometheus metrics that are given as text data. 11 | */ 12 | public class TextPrometheusMetricsProcessor extends PrometheusMetricsProcessor { 13 | public TextPrometheusMetricsProcessor(InputStream inputStream, PrometheusMetricsWalker theWalker) { 14 | super(inputStream, theWalker); 15 | } 16 | 17 | @Override 18 | public TextPrometheusMetricDataParser createPrometheusMetricDataParser() { 19 | return new TextPrometheusMetricDataParser(getInputStream()); 20 | } 21 | 22 | @Override 23 | protected MetricFamily convert(MetricFamily metricFamily) { 24 | return metricFamily; // no conversion necessary - our text parser already uses the common api 25 | } 26 | 27 | } 28 | -------------------------------------------------------------------------------- /src/main/java/prometheus/text/TextSample.java: -------------------------------------------------------------------------------- 1 | package prometheus.text; 2 | 3 | import prometheus.types.Metric; 4 | 5 | /** 6 | * This represents a sample as found in the text data. This may or may not represent 7 | * a full metric. In the case of a counter or gauge, it will represent the full metric. 8 | * In the case of a summary or histogram, this only represents one quantile or one bucket 9 | * in a full summary or histogram metric. For those two cases, additional processing needs to 10 | * be made to combine multiple TextMetric objects into a single SummaryMetric or HistogramMetric. 11 | */ 12 | public class TextSample extends Metric { 13 | 14 | public static class Builder extends Metric.Builder { 15 | private String value; 16 | private String line; 17 | 18 | public TextSample build() { 19 | return new TextSample(this); 20 | } 21 | 22 | public Builder setValue(String value) { 23 | this.value = value; 24 | return this; 25 | } 26 | 27 | public Builder setLine(String line) { 28 | this.line = line; 29 | return this; 30 | } 31 | } 32 | 33 | private final String value; 34 | private final String line; 35 | 36 | public TextSample(Builder builder) { 37 | super(builder); 38 | this.value = builder.value; 39 | this.line = builder.line; 40 | } 41 | 42 | public String getValue() { 43 | return value; 44 | } 45 | 46 | /** 47 | * This is the line of text in the text data where this sample came from. 48 | * This can be used for debugging purposes so you know what the sample 49 | * looked like before being parsed. 50 | * 51 | * @return the sample text line 52 | */ 53 | public String getLine() { 54 | return line; 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /src/main/java/prometheus/types/Counter.java: -------------------------------------------------------------------------------- 1 | package prometheus.types; 2 | 3 | public class Counter extends Metric { 4 | 5 | public static class Builder extends Metric.Builder { 6 | private double value = Double.NaN; 7 | 8 | public Counter build() { 9 | return new Counter(this); 10 | } 11 | 12 | public Builder setValue(double value) { 13 | this.value = value; 14 | return this; 15 | } 16 | } 17 | 18 | private final double value; 19 | 20 | public Counter(Builder builder) { 21 | super(builder); 22 | this.value = builder.value; 23 | } 24 | 25 | public double getValue() { 26 | return value; 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /src/main/java/prometheus/types/Gauge.java: -------------------------------------------------------------------------------- 1 | package prometheus.types; 2 | 3 | public class Gauge extends Metric { 4 | 5 | public static class Builder extends Metric.Builder { 6 | private double value = Double.NaN; 7 | 8 | public Gauge build() { 9 | return new Gauge(this); 10 | } 11 | 12 | public Builder setValue(double value) { 13 | this.value = value; 14 | return this; 15 | } 16 | } 17 | 18 | private final double value; 19 | 20 | public Gauge(Builder builder) { 21 | super(builder); 22 | this.value = builder.value; 23 | } 24 | 25 | public double getValue() { 26 | return value; 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /src/main/java/prometheus/types/Histogram.java: -------------------------------------------------------------------------------- 1 | package prometheus.types; 2 | 3 | import java.util.ArrayList; 4 | import java.util.Collections; 5 | import java.util.List; 6 | 7 | import prometheus.Util; 8 | 9 | public class Histogram extends Metric { 10 | 11 | public static class Builder extends Metric.Builder { 12 | private long sampleCount = 0; 13 | private double sampleSum = Double.NaN; 14 | private List buckets; 15 | 16 | public Histogram build() { 17 | return new Histogram(this); 18 | } 19 | 20 | public Builder setSampleCount(long sampleCount) { 21 | this.sampleCount = sampleCount; 22 | return this; 23 | } 24 | 25 | public Builder setSampleSum(double sampleSum) { 26 | this.sampleSum = sampleSum; 27 | return this; 28 | } 29 | 30 | public Builder addBucket(double upperBound, long cumulativeCount) { 31 | if (buckets == null) { 32 | buckets = new ArrayList<>(); 33 | } 34 | buckets.add(new Bucket(upperBound, cumulativeCount)); 35 | return this; 36 | } 37 | 38 | public Builder addBuckets(List buckets) { 39 | if (this.buckets == null) { 40 | this.buckets = new ArrayList<>(); 41 | } 42 | this.buckets.addAll(buckets); 43 | return this; 44 | } 45 | } 46 | 47 | public static class Bucket { 48 | private final double upperBound; 49 | private final long cumulativeCount; 50 | 51 | public Bucket(double upperBound, long cumulativeCount) { 52 | this.upperBound = upperBound; 53 | this.cumulativeCount = cumulativeCount; 54 | } 55 | 56 | public double getUpperBound() { 57 | return upperBound; 58 | } 59 | 60 | public long getCumulativeCount() { 61 | return cumulativeCount; 62 | } 63 | 64 | @Override 65 | public String toString() { 66 | return String.format("%s:%d", Util.convertDoubleToString(upperBound), cumulativeCount); 67 | } 68 | } 69 | 70 | private final long sampleCount; 71 | private final double sampleSum; 72 | private final List buckets; 73 | 74 | private Histogram(Builder builder) { 75 | super(builder); 76 | getLabels().remove("le"); 77 | this.sampleCount = builder.sampleCount; 78 | this.sampleSum = builder.sampleSum; 79 | this.buckets = builder.buckets; 80 | } 81 | 82 | public long getSampleCount() { 83 | return sampleCount; 84 | } 85 | 86 | public double getSampleSum() { 87 | return sampleSum; 88 | } 89 | 90 | public List getBuckets() { 91 | if (buckets == null) { 92 | return Collections.emptyList(); 93 | } 94 | return buckets; 95 | } 96 | } 97 | -------------------------------------------------------------------------------- /src/main/java/prometheus/types/Metric.java: -------------------------------------------------------------------------------- 1 | package prometheus.types; 2 | 3 | import java.util.Collections; 4 | import java.util.LinkedHashMap; 5 | import java.util.Map; 6 | 7 | /** 8 | * Superclass to all metrics. All metrics have name and labels. 9 | */ 10 | public abstract class Metric { 11 | 12 | public abstract static class Builder> { 13 | private String name; 14 | private Map labels; 15 | 16 | public B setName(String name) { 17 | this.name = name; 18 | return (B) this; 19 | } 20 | 21 | public B addLabel(String name, String value) { 22 | if (labels == null) { 23 | labels = new LinkedHashMap<>(); // used linked hash map to retain ordering 24 | } 25 | labels.put(name, value); 26 | return (B) this; 27 | } 28 | 29 | public B addLabels(Map map) { 30 | if (labels == null) { 31 | labels = new LinkedHashMap<>(); // used linked hash map to retain ordering 32 | } 33 | labels.putAll(map); 34 | return (B) this; 35 | } 36 | 37 | public abstract T build(); 38 | } 39 | 40 | private final String name; 41 | private final Map labels; 42 | 43 | protected Metric(Builder builder) { 44 | if (builder.name == null) { 45 | throw new IllegalArgumentException("Need to set name"); 46 | } 47 | 48 | this.name = builder.name; 49 | this.labels = builder.labels; 50 | } 51 | 52 | public String getName() { 53 | return name; 54 | } 55 | 56 | public Map getLabels() { 57 | if (labels == null) { 58 | return Collections.emptyMap(); 59 | } 60 | return labels; 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /src/main/java/prometheus/types/MetricFamily.java: -------------------------------------------------------------------------------- 1 | package prometheus.types; 2 | 3 | import java.util.ArrayList; 4 | import java.util.Collections; 5 | import java.util.List; 6 | 7 | /** 8 | * Contains all metrics within a family (that is, of the same name). All metrics in a family have the same type. 9 | */ 10 | public class MetricFamily { 11 | 12 | public static class Builder { 13 | private String name; 14 | private String help; 15 | private MetricType type; 16 | private List metrics; 17 | 18 | public Builder setName(String name) { 19 | this.name = name; 20 | return this; 21 | } 22 | 23 | public Builder setHelp(String help) { 24 | this.help = help; 25 | return this; 26 | } 27 | 28 | public Builder setType(MetricType type) { 29 | this.type = type; 30 | return this; 31 | } 32 | 33 | public Builder addMetric(Metric metric) { 34 | if (metrics == null) { 35 | metrics = new ArrayList<>(); 36 | } 37 | metrics.add(metric); 38 | return this; 39 | } 40 | 41 | public MetricFamily build() { 42 | return new MetricFamily(this); 43 | } 44 | } 45 | 46 | private final String name; 47 | private final String help; 48 | private final MetricType type; 49 | private final List metrics; 50 | 51 | protected MetricFamily(Builder builder) { 52 | if (builder.name == null) { 53 | throw new IllegalArgumentException("Need to set name"); 54 | } 55 | if (builder.type == null) { 56 | throw new IllegalArgumentException("Need to set type"); 57 | } 58 | 59 | Class expectedMetricClassType; 60 | switch (builder.type) { 61 | case COUNTER: 62 | expectedMetricClassType = Counter.class; 63 | break; 64 | case GAUGE: 65 | expectedMetricClassType = Gauge.class; 66 | break; 67 | case SUMMARY: 68 | expectedMetricClassType = Summary.class; 69 | break; 70 | case HISTOGRAM: 71 | expectedMetricClassType = Histogram.class; 72 | break; 73 | default: 74 | throw new IllegalArgumentException("Invalid type: " + builder.type); 75 | } 76 | 77 | // make sure all the metrics in the family are of the expected type 78 | if (builder.metrics != null && !builder.metrics.isEmpty()) { 79 | for (Metric metric : builder.metrics) { 80 | if (!expectedMetricClassType.isInstance(metric)) { 81 | throw new IllegalArgumentException( 82 | String.format("Metric type is [%s] so instances of class [%s] are expected, " 83 | + "but got metric object of type [%s]", 84 | builder.type, expectedMetricClassType.getName(), metric.getClass().getName())); 85 | } 86 | } 87 | 88 | } 89 | 90 | this.name = builder.name; 91 | this.help = builder.help; 92 | this.type = builder.type; 93 | this.metrics = builder.metrics; 94 | } 95 | 96 | public String getName() { 97 | return name; 98 | } 99 | 100 | public String getHelp() { 101 | return help; 102 | } 103 | 104 | public MetricType getType() { 105 | return type; 106 | } 107 | 108 | public List getMetrics() { 109 | if (metrics == null) { 110 | return Collections.emptyList(); 111 | } 112 | return metrics; 113 | } 114 | } 115 | -------------------------------------------------------------------------------- /src/main/java/prometheus/types/MetricType.java: -------------------------------------------------------------------------------- 1 | package prometheus.types; 2 | 3 | public enum MetricType { 4 | COUNTER, GAUGE, SUMMARY, HISTOGRAM 5 | } 6 | -------------------------------------------------------------------------------- /src/main/java/prometheus/types/Summary.java: -------------------------------------------------------------------------------- 1 | package prometheus.types; 2 | 3 | import java.util.ArrayList; 4 | import java.util.Collections; 5 | import java.util.List; 6 | 7 | import prometheus.Util; 8 | 9 | public class Summary extends Metric { 10 | 11 | public static class Builder extends Metric.Builder { 12 | private long sampleCount = 0; 13 | private double sampleSum = Double.NaN; 14 | private List quantiles; 15 | 16 | public Summary build() { 17 | return new Summary(this); 18 | } 19 | 20 | public Builder setSampleCount(long sampleCount) { 21 | this.sampleCount = sampleCount; 22 | return this; 23 | } 24 | 25 | public Builder setSampleSum(double sampleSum) { 26 | this.sampleSum = sampleSum; 27 | return this; 28 | } 29 | 30 | public Builder addQuantile(double quantile, double value) { 31 | if (quantiles == null) { 32 | quantiles = new ArrayList<>(); 33 | } 34 | quantiles.add(new Quantile(quantile, value)); 35 | return this; 36 | } 37 | 38 | public Builder addQuantiles(List quantiles) { 39 | if (this.quantiles == null) { 40 | this.quantiles = new ArrayList<>(); 41 | } 42 | this.quantiles.addAll(quantiles); 43 | return this; 44 | } 45 | } 46 | 47 | public static class Quantile { 48 | private final double quantile; 49 | private final double value; 50 | 51 | public Quantile(double quantile, double value) { 52 | this.quantile = quantile; 53 | this.value = value; 54 | 55 | } 56 | 57 | public double getQuantile() { 58 | return quantile; 59 | } 60 | 61 | public double getValue() { 62 | return value; 63 | } 64 | 65 | @Override 66 | public String toString() { 67 | return String.format("%s:%s", Util.convertDoubleToString(quantile), Util.convertDoubleToString(value)); 68 | } 69 | } 70 | 71 | private final long sampleCount; 72 | private final double sampleSum; 73 | private final List quantiles; 74 | 75 | private Summary(Builder builder) { 76 | super(builder); 77 | getLabels().remove("quantile"); 78 | this.sampleCount = builder.sampleCount; 79 | this.sampleSum = builder.sampleSum; 80 | this.quantiles = builder.quantiles; 81 | } 82 | 83 | public long getSampleCount() { 84 | return sampleCount; 85 | } 86 | 87 | public double getSampleSum() { 88 | return sampleSum; 89 | } 90 | 91 | public List getQuantiles() { 92 | if (quantiles == null) { 93 | return Collections.emptyList(); 94 | } 95 | return quantiles; 96 | } 97 | } 98 | -------------------------------------------------------------------------------- /src/main/java/prometheus/walkers/CollectorPrometheusMetricsWalker.java: -------------------------------------------------------------------------------- 1 | package prometheus.walkers; 2 | 3 | import java.util.ArrayList; 4 | import java.util.List; 5 | 6 | import prometheus.types.Counter; 7 | import prometheus.types.Gauge; 8 | import prometheus.types.Histogram; 9 | import prometheus.types.MetricFamily; 10 | import prometheus.types.Summary; 11 | 12 | /** 13 | * This simply collects all metrics in all families and provides a list to the families. 14 | */ 15 | public class CollectorPrometheusMetricsWalker implements PrometheusMetricsWalker { 16 | 17 | private List finishedList; 18 | private boolean finished; 19 | 20 | /** 21 | * @return indicates if this walker has finished processing all metric families. 22 | */ 23 | public boolean isFinished() { 24 | return finished; 25 | } 26 | 27 | /** 28 | * @return if this walker has finished processing all metric families, this will return the list of the 29 | * metric families processed. If the walker hasn't finished yet, null is returned. 30 | */ 31 | public List getAllMetricFamilies() { 32 | return (finished) ? finishedList : null; 33 | } 34 | 35 | @Override 36 | public void walkStart() { 37 | finished = false; 38 | finishedList = new ArrayList<>(); 39 | } 40 | 41 | @Override 42 | public void walkFinish(int familiesProcessed, int metricsProcessed) { 43 | finished = true; 44 | } 45 | 46 | @Override 47 | public void walkMetricFamily(MetricFamily family, int index) { 48 | finishedList.add(family); 49 | } 50 | 51 | @Override 52 | public void walkCounterMetric(MetricFamily family, Counter metric, int index) { 53 | } 54 | 55 | @Override 56 | public void walkGaugeMetric(MetricFamily family, Gauge metric, int index) { 57 | } 58 | 59 | @Override 60 | public void walkSummaryMetric(MetricFamily family, Summary metric, int index) { 61 | } 62 | 63 | @Override 64 | public void walkHistogramMetric(MetricFamily family, Histogram metric, int index) { 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /src/main/java/prometheus/walkers/JSONPrometheusMetricsWalker.java: -------------------------------------------------------------------------------- 1 | package prometheus.walkers; 2 | 3 | import java.util.Iterator; 4 | import java.util.Map; 5 | import java.util.Map.Entry; 6 | 7 | import prometheus.Util; 8 | import prometheus.types.Counter; 9 | import prometheus.types.Gauge; 10 | import prometheus.types.Histogram; 11 | import prometheus.types.Histogram.Bucket; 12 | import prometheus.types.MetricFamily; 13 | import prometheus.types.Summary; 14 | import prometheus.types.Summary.Quantile; 15 | 16 | public class JSONPrometheusMetricsWalker implements PrometheusMetricsWalker { 17 | 18 | @Override 19 | public void walkStart() { 20 | System.out.println("["); 21 | } 22 | 23 | @Override 24 | public void walkFinish(int familiesProcessed, int metricsProcessed) { 25 | if (familiesProcessed > 0) { 26 | System.out.println(" ]"); 27 | System.out.println(" }"); 28 | } 29 | System.out.println("]"); 30 | } 31 | 32 | @Override 33 | public void walkMetricFamily(MetricFamily familyInfo, int index) { 34 | if (index > 0) { 35 | System.out.printf(" ]\n"); 36 | System.out.printf(" },\n"); 37 | } 38 | 39 | System.out.printf(" {\n"); 40 | System.out.printf(" \"name\":\"%s\",\n", familyInfo.getName()); 41 | System.out.printf(" \"help\":\"%s\",\n", familyInfo.getHelp()); 42 | System.out.printf(" \"type\":\"%s\",\n", familyInfo.getType()); 43 | System.out.printf(" \"metrics\":[\n"); 44 | } 45 | 46 | @Override 47 | public void walkCounterMetric(MetricFamily family, Counter metric, int index) { 48 | System.out.printf(" {\n"); 49 | outputLabels(metric.getLabels()); 50 | System.out.printf(" \"value\":\"%s\"\n", Util.convertDoubleToString(metric.getValue())); 51 | if ((index + 1) == family.getMetrics().size()) { 52 | System.out.printf(" }\n"); 53 | } else { 54 | System.out.printf(" },\n"); // there are more coming 55 | } 56 | } 57 | 58 | @Override 59 | public void walkGaugeMetric(MetricFamily family, Gauge metric, int index) { 60 | System.out.printf(" {\n"); 61 | outputLabels(metric.getLabels()); 62 | System.out.printf(" \"value\":\"%s\"\n", Util.convertDoubleToString(metric.getValue())); 63 | if ((index + 1) == family.getMetrics().size()) { 64 | System.out.printf(" }\n"); 65 | } else { 66 | System.out.printf(" },\n"); // there are more coming 67 | } 68 | } 69 | 70 | @Override 71 | public void walkSummaryMetric(MetricFamily family, Summary metric, int index) { 72 | System.out.printf(" {\n"); 73 | outputLabels(metric.getLabels()); 74 | if (!metric.getQuantiles().isEmpty()) { 75 | System.out.printf(" \"quantiles\":{\n"); 76 | Iterator iter = metric.getQuantiles().iterator(); 77 | while (iter.hasNext()) { 78 | Quantile quantile = iter.next(); 79 | System.out.printf(" \"%f\":\"%f\"%s\n", 80 | quantile.getQuantile(), quantile.getValue(), (iter.hasNext()) ? "," : ""); 81 | } 82 | System.out.printf(" },\n"); 83 | } 84 | System.out.printf(" \"count\":\"%d\",\n", metric.getSampleCount()); 85 | System.out.printf(" \"sum\":\"%s\"\n", Util.convertDoubleToString(metric.getSampleSum())); 86 | if ((index + 1) == family.getMetrics().size()) { 87 | System.out.printf(" }\n"); 88 | } else { 89 | System.out.printf(" },\n"); // there are more coming 90 | } 91 | } 92 | 93 | @Override 94 | public void walkHistogramMetric(MetricFamily family, Histogram metric, int index) { 95 | System.out.printf(" {\n"); 96 | outputLabels(metric.getLabels()); 97 | if (!metric.getBuckets().isEmpty()) { 98 | System.out.printf(" \"buckets\":{\n"); 99 | Iterator iter = metric.getBuckets().iterator(); 100 | while (iter.hasNext()) { 101 | Bucket bucket = iter.next(); 102 | System.out.printf(" \"%f\":\"%d\"%s\n", 103 | bucket.getUpperBound(), bucket.getCumulativeCount(), (iter.hasNext()) ? "," : ""); 104 | } 105 | System.out.printf(" },\n"); 106 | } 107 | System.out.printf(" \"count\":\"%d\",\n", metric.getSampleCount()); 108 | System.out.printf(" \"sum\":\"%s\"\n", Util.convertDoubleToString(metric.getSampleSum())); 109 | if ((index + 1) == family.getMetrics().size()) { 110 | System.out.printf(" }\n"); 111 | } else { 112 | System.out.printf(" },\n"); // there are more coming 113 | } 114 | } 115 | 116 | private void outputLabels(Map labels) { 117 | if (labels == null || labels.isEmpty()) { 118 | return; 119 | } 120 | System.out.printf(" \"labels\":{\n"); 121 | Iterator> iter = labels.entrySet().iterator(); 122 | while (iter.hasNext()) { 123 | Entry labelPair = iter.next(); 124 | String comma = (iter.hasNext()) ? "," : ""; 125 | System.out.printf(" \"%s\":\"%s\"%s\n", labelPair.getKey(), labelPair.getValue(), comma); 126 | } 127 | System.out.printf(" },\n"); 128 | } 129 | } 130 | -------------------------------------------------------------------------------- /src/main/java/prometheus/walkers/LoggingPrometheusMetricsWalker.java: -------------------------------------------------------------------------------- 1 | package prometheus.walkers; 2 | 3 | import java.util.Map; 4 | 5 | import org.jboss.logging.Logger; 6 | import org.jboss.logging.Logger.Level; 7 | import prometheus.types.Counter; 8 | import prometheus.types.Gauge; 9 | import prometheus.types.Histogram; 10 | import prometheus.types.MetricFamily; 11 | import prometheus.types.Summary; 12 | 13 | /** 14 | * This implementation simply logs the metric values. 15 | */ 16 | public class LoggingPrometheusMetricsWalker implements PrometheusMetricsWalker { 17 | private static final Logger log = Logger.getLogger(LoggingPrometheusMetricsWalker.class); 18 | private Level logLevel; 19 | 20 | public LoggingPrometheusMetricsWalker() { 21 | this(null); 22 | } 23 | 24 | public LoggingPrometheusMetricsWalker(Level logLevel) { 25 | this.logLevel = (logLevel != null) ? logLevel : Level.DEBUG; 26 | } 27 | 28 | @Override 29 | public void walkStart() { 30 | } 31 | 32 | @Override 33 | public void walkFinish(int familiesProcessed, int metricsProcessed) { 34 | } 35 | 36 | @Override 37 | public void walkMetricFamily(MetricFamily family, int index) { 38 | log.logf(getLogLevel(), "Metric Family [%s] of type [%s] has [%d] metrics: %s", 39 | family.getName(), 40 | family.getType(), 41 | family.getMetrics().size(), 42 | family.getHelp()); 43 | } 44 | 45 | @Override 46 | public void walkCounterMetric(MetricFamily family, Counter metric, int index) { 47 | log.logf(getLogLevel(), "COUNTER: %s%s=%f", 48 | metric.getName(), 49 | buildLabelListString(metric.getLabels()), 50 | metric.getValue()); 51 | } 52 | 53 | @Override 54 | public void walkGaugeMetric(MetricFamily family, Gauge metric, int index) { 55 | log.logf(getLogLevel(), "GAUGE: %s%s=%f", 56 | metric.getName(), 57 | buildLabelListString(metric.getLabels()), 58 | metric.getValue()); 59 | } 60 | 61 | @Override 62 | public void walkSummaryMetric(MetricFamily family, Summary metric, int index) { 63 | log.logf(getLogLevel(), "SUMMARY: %s%s: count=%d, sum=%f, quantiles=%s", 64 | metric.getName(), 65 | buildLabelListString(metric.getLabels()), 66 | metric.getSampleCount(), 67 | metric.getSampleSum(), 68 | metric.getQuantiles()); 69 | } 70 | 71 | @Override 72 | public void walkHistogramMetric(MetricFamily family, Histogram metric, int index) { 73 | log.logf(getLogLevel(), "HISTOGRAM: %s%s: count=%d, sum=%f, buckets=%s", 74 | metric.getName(), 75 | buildLabelListString(metric.getLabels()), 76 | metric.getSampleCount(), 77 | metric.getSampleSum(), 78 | metric.getBuckets()); 79 | } 80 | 81 | /** 82 | * The default implementations of the walk methods will log the metric data with this given log level. 83 | * 84 | * @return the log level 85 | */ 86 | protected Level getLogLevel() { 87 | return this.logLevel; 88 | } 89 | 90 | protected String buildLabelListString(Map labels) { 91 | return buildLabelListString(labels, "{", "}"); 92 | } 93 | } 94 | -------------------------------------------------------------------------------- /src/main/java/prometheus/walkers/PrometheusMetricsWalker.java: -------------------------------------------------------------------------------- 1 | package prometheus.walkers; 2 | 3 | import java.util.Map; 4 | 5 | import prometheus.types.Counter; 6 | import prometheus.types.Gauge; 7 | import prometheus.types.Histogram; 8 | import prometheus.types.MetricFamily; 9 | import prometheus.types.Summary; 10 | 11 | /** 12 | * Implementors iterate a collection of metric families and their metrics. 13 | */ 14 | public interface PrometheusMetricsWalker { 15 | 16 | /** 17 | * Called when a walk has been started. 18 | */ 19 | void walkStart(); 20 | 21 | /** 22 | * Called when a walk has traversed all the metrics. 23 | * @param familiesProcessed total number of families processed 24 | * @param metricsProcessed total number of metrics across all families processed 25 | */ 26 | void walkFinish(int familiesProcessed, int metricsProcessed); 27 | 28 | /** 29 | * Called when a new metric family is about to be traversed. 30 | * 31 | * @param family information about the family being traversed such as the name, help description, etc. 32 | * @param index index of the family being processed, where 0 is the first one. 33 | */ 34 | void walkMetricFamily(MetricFamily family, int index); 35 | 36 | /** 37 | * Called when a new counter metric is found. 38 | * 39 | * @param family information about the family being traversed such as the name, help description, etc. 40 | * @param counter the metric being processed 41 | * @param index index of the metric being processed, where 0 is the first one. 42 | */ 43 | void walkCounterMetric(MetricFamily family, Counter counter, int index); 44 | 45 | /** 46 | * Called when a new gauge metric is found. 47 | * 48 | * @param family information about the family being traversed such as the name, help description, etc. 49 | * @param gauge the metric being processed 50 | * @param index index of the metric being processed, where 0 is the first one. 51 | */ 52 | void walkGaugeMetric(MetricFamily family, Gauge gauge, int index); 53 | 54 | /** 55 | * Called when a new summary metric is found. 56 | * 57 | * @param family information about the family being traversed such as the name, help description, etc. 58 | * @param summary the metric being processed 59 | * @param index index of the metric being processed, where 0 is the first one. 60 | */ 61 | void walkSummaryMetric(MetricFamily family, Summary summary, int index); 62 | 63 | /** 64 | * Called when a new histogram metric is found. 65 | * 66 | * @param family information about the family being traversed such as the name, help description, etc. 67 | * @param histogram the metric being processed 68 | * @param index index of the metric being processed, where 0 is the first one. 69 | */ 70 | void walkHistogramMetric(MetricFamily family, Histogram histogram, int index); 71 | 72 | /** 73 | * Convienence method that takes the given label list and returns a string in the form of 74 | * "labelName1=labelValue1,labelName2=labelValue2,..." 75 | * 76 | * @param labels the label list 77 | * @param prefix if not null, these characters will prefix the label list 78 | * @param suffix if not null, these characters will suffix the label list 79 | * @return the string form of the labels, optionally prefixed and suffixed 80 | */ 81 | default String buildLabelListString(Map labels, String prefix, String suffix) { 82 | if (prefix == null) { 83 | prefix = ""; 84 | } 85 | if (suffix == null) { 86 | suffix = ""; 87 | } 88 | if (labels == null) { 89 | return String.format("%s%s", prefix, suffix); 90 | } 91 | 92 | StringBuilder str = new StringBuilder(""); 93 | for (Map.Entry pair : labels.entrySet()) { 94 | if (str.length() > 0) { 95 | str.append(","); 96 | } 97 | str.append(pair.getKey()).append("=").append(pair.getValue()); 98 | } 99 | return String.format("%s%s%s", prefix, str, suffix); 100 | } 101 | 102 | } 103 | -------------------------------------------------------------------------------- /src/main/java/prometheus/walkers/SimplePrometheusMetricsWalker.java: -------------------------------------------------------------------------------- 1 | package prometheus.walkers; 2 | 3 | import java.net.URL; 4 | 5 | import prometheus.types.Counter; 6 | import prometheus.types.Gauge; 7 | import prometheus.types.Histogram; 8 | import prometheus.types.MetricFamily; 9 | import prometheus.types.Summary; 10 | 11 | public class SimplePrometheusMetricsWalker implements PrometheusMetricsWalker { 12 | 13 | private final URL url; 14 | 15 | public SimplePrometheusMetricsWalker() { 16 | this(null); 17 | } 18 | 19 | /** 20 | * Use this constructor if you know the URL where the metric data came from. 21 | * 22 | * @param url the protocol endpoint that supplied the Prometheus metric data 23 | */ 24 | public SimplePrometheusMetricsWalker(URL url) { 25 | this.url = url; 26 | } 27 | 28 | @Override 29 | public void walkStart() { 30 | if (url != null) { 31 | System.out.println("Scraping metrics from Prometheus protocol endpoint: " + url); 32 | } 33 | } 34 | 35 | @Override 36 | public void walkFinish(int familiesProcessed, int metricsProcessed) { 37 | if (metricsProcessed == 0) { 38 | System.out.println("There are no metrics"); 39 | } 40 | } 41 | 42 | @Override 43 | public void walkMetricFamily(MetricFamily family, int index) { 44 | System.out.printf("* %s (%s): %s\n", family.getName(), family.getType(), family.getHelp()); 45 | } 46 | 47 | @Override 48 | public void walkCounterMetric(MetricFamily family, Counter metric, int index) { 49 | System.out.printf(" +%2d. %s%s [%f]\n", 50 | index, 51 | metric.getName(), 52 | buildLabelListString(metric.getLabels(), "{", "}"), 53 | metric.getValue()); 54 | } 55 | 56 | @Override 57 | public void walkGaugeMetric(MetricFamily family, Gauge metric, int index) { 58 | System.out.printf(" +%2d. %s%s [%f]\n", 59 | index, 60 | metric.getName(), 61 | buildLabelListString(metric.getLabels(), "{", "}"), 62 | metric.getValue()); 63 | } 64 | 65 | @Override 66 | public void walkSummaryMetric(MetricFamily family, Summary metric, int index) { 67 | System.out.printf(" +%2d. %s%s [%d/%f] {%s}\n", 68 | index, 69 | metric.getName(), 70 | buildLabelListString(metric.getLabels(), "{", "}"), 71 | metric.getSampleCount(), 72 | metric.getSampleSum(), 73 | metric.getQuantiles()); 74 | } 75 | 76 | @Override 77 | public void walkHistogramMetric(MetricFamily family, Histogram metric, int index) { 78 | System.out.printf(" +%2d. %s%s [%d/%f] {%s}\n", 79 | index, 80 | metric.getName(), 81 | buildLabelListString(metric.getLabels(), "{", "}"), 82 | metric.getSampleCount(), 83 | metric.getSampleSum(), 84 | metric.getBuckets()); 85 | } 86 | } 87 | -------------------------------------------------------------------------------- /src/main/java/prometheus/walkers/XMLPrometheusMetricsWalker.java: -------------------------------------------------------------------------------- 1 | package prometheus.walkers; 2 | 3 | import java.net.URL; 4 | 5 | import prometheus.Util; 6 | import prometheus.types.Counter; 7 | import prometheus.types.Gauge; 8 | import prometheus.types.Histogram; 9 | import prometheus.types.Histogram.Bucket; 10 | import prometheus.types.MetricFamily; 11 | import prometheus.types.MetricType; 12 | import prometheus.types.Summary; 13 | import prometheus.types.Summary.Quantile; 14 | 15 | public class XMLPrometheusMetricsWalker implements PrometheusMetricsWalker { 16 | 17 | private final URL url; 18 | 19 | public XMLPrometheusMetricsWalker() { 20 | this(null); 21 | } 22 | 23 | /** 24 | * Use this constructor if you know the URL where the metric data came from. 25 | * 26 | * @param metricFamilies 27 | * @param url the protocol endpoint that supplied the Prometheus metric data 28 | */ 29 | public XMLPrometheusMetricsWalker(URL url) { 30 | this.url = url; 31 | } 32 | 33 | @Override 34 | public void walkStart() { 35 | System.out.printf("\n"); 36 | 37 | // only provide the URL endpoint element if we know the URL where the metrics came from 38 | if (url != null) { 39 | System.out.printf(" %s\n", url); 40 | } 41 | } 42 | 43 | @Override 44 | public void walkFinish(int familiesProcessed, int metricsProcessed) { 45 | if (familiesProcessed > 0) { 46 | System.out.printf(" \n"); 47 | } 48 | System.out.println(""); 49 | } 50 | 51 | @Override 52 | public void walkMetricFamily(MetricFamily family, int index) { 53 | if (index > 0) { 54 | System.out.printf(" \n"); 55 | } 56 | 57 | System.out.printf(" \n"); 58 | System.out.printf(" %s\n", family.getName()); 59 | System.out.printf(" %s\n", family.getType()); 60 | System.out.printf(" %s\n", family.getHelp()); 61 | } 62 | 63 | @Override 64 | public void walkCounterMetric(MetricFamily family, Counter metric, int index) { 65 | System.out.printf(" \n"); 66 | System.out.printf(" %s\n", family.getName()); 67 | System.out.printf(" %s\n", MetricType.COUNTER); 68 | System.out.printf(" %s\n", buildLabelListString(metric.getLabels(), null, null)); 69 | System.out.printf(" %s\n", Util.convertDoubleToString(metric.getValue())); 70 | System.out.printf(" \n"); 71 | } 72 | 73 | @Override 74 | public void walkGaugeMetric(MetricFamily family, Gauge metric, int index) { 75 | System.out.printf(" \n"); 76 | System.out.printf(" %s\n", family.getName()); 77 | System.out.printf(" %s\n", MetricType.GAUGE); 78 | System.out.printf(" %s\n", buildLabelListString(metric.getLabels(), null, null)); 79 | System.out.printf(" %s\n", Util.convertDoubleToString(metric.getValue())); 80 | System.out.printf(" \n"); 81 | } 82 | 83 | @Override 84 | public void walkSummaryMetric(MetricFamily family, Summary metric, int index) { 85 | System.out.printf(" \n"); 86 | System.out.printf(" %s\n", family.getName()); 87 | System.out.printf(" %s\n", MetricType.SUMMARY); 88 | System.out.printf(" %s\n", buildLabelListString(metric.getLabels(), null, null)); 89 | System.out.printf(" %d\n", metric.getSampleCount()); 90 | System.out.printf(" %s\n", Util.convertDoubleToString(metric.getSampleSum())); 91 | if (!metric.getQuantiles().isEmpty()) { 92 | System.out.printf(" \n"); 93 | for (Quantile quantile : metric.getQuantiles()) { 94 | System.out.printf(" %s\n", quantile); 95 | } 96 | System.out.printf(" \n"); 97 | } 98 | System.out.printf(" \n"); 99 | } 100 | 101 | @Override 102 | public void walkHistogramMetric(MetricFamily family, Histogram metric, int index) { 103 | System.out.printf(" \n"); 104 | System.out.printf(" %s\n", family.getName()); 105 | System.out.printf(" %s\n", MetricType.HISTOGRAM); 106 | System.out.printf(" %s\n", buildLabelListString(metric.getLabels(), null, null)); 107 | System.out.printf(" %d\n", metric.getSampleCount()); 108 | System.out.printf(" %s\n", Util.convertDoubleToString(metric.getSampleSum())); 109 | if (!metric.getBuckets().isEmpty()) { 110 | System.out.printf(" \n"); 111 | for (Bucket bucket : metric.getBuckets()) { 112 | System.out.printf(" %s\n", bucket); 113 | } 114 | System.out.printf(" \n"); 115 | } 116 | System.out.printf(" \n"); 117 | } 118 | } 119 | -------------------------------------------------------------------------------- /src/test/java/org/hawkular/agent/prometheus/BinaryPrometheusParserTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2015-2016 Red Hat, Inc. and/or its affiliates 3 | * and other contributors as indicated by the @author tags. 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | */ 17 | 18 | package prometheus; 19 | 20 | import java.io.File; 21 | import java.io.InputStream; 22 | import java.net.URISyntaxException; 23 | import java.net.URL; 24 | import java.util.ArrayList; 25 | import java.util.List; 26 | import java.util.concurrent.atomic.AtomicInteger; 27 | 28 | import org.jboss.logging.Logger.Level; 29 | import org.junit.Assert; 30 | import org.junit.Test; 31 | import prometheus.binary.BinaryPrometheusMetricDataParser; 32 | import prometheus.binary.BinaryPrometheusMetricsProcessor; 33 | import prometheus.walkers.LoggingPrometheusMetricsWalker; 34 | import prometheus.walkers.PrometheusMetricsWalker; 35 | 36 | import io.prometheus.client.Metrics.MetricFamily; 37 | 38 | public class BinaryPrometheusParserTest { 39 | @Test 40 | public void testGetMetricsFromStream() throws Exception { 41 | 42 | List metricFamilies = new ArrayList<>(); 43 | try (InputStream testData = this.getClass().getClassLoader().getResourceAsStream("prometheus.data")) { 44 | BinaryPrometheusMetricDataParser parser = new BinaryPrometheusMetricDataParser(testData); 45 | while (true) { 46 | MetricFamily family = parser.parse(); 47 | if (family == null) { 48 | break; 49 | } 50 | metricFamilies.add(family); 51 | } 52 | } 53 | Assert.assertNotNull(metricFamilies); 54 | Assert.assertEquals(71, metricFamilies.size()); 55 | 56 | // walk the data and make sure it has what we expect 57 | final AtomicInteger familyCount = new AtomicInteger(0); 58 | final AtomicInteger fullCount = new AtomicInteger(0); 59 | PrometheusMetricsWalker walker = new LoggingPrometheusMetricsWalker(Level.INFO) { 60 | public void walkMetricFamily(prometheus.types.MetricFamily family, int index) { 61 | super.walkMetricFamily(family, index); 62 | familyCount.incrementAndGet(); 63 | fullCount.addAndGet(family.getMetrics().size()); 64 | } 65 | }; 66 | 67 | try (InputStream testData = this.getClass().getClassLoader().getResourceAsStream("prometheus.data")) { 68 | new BinaryPrometheusMetricsProcessor(testData, walker).walk(); 69 | } 70 | Assert.assertEquals(metricFamilies.size(), familyCount.get()); 71 | Assert.assertEquals(126, fullCount.get()); 72 | } 73 | 74 | @Test 75 | public void testGetMetricsFromUrlAndFile() throws Exception { 76 | URL testDataUrl = this.getClass().getClassLoader().getResource("prometheus.data"); 77 | File testDataFile; 78 | try { 79 | testDataFile = new File(testDataUrl.toURI()); 80 | } catch (URISyntaxException e) { 81 | testDataFile = new File(testDataUrl.getPath()); 82 | } 83 | 84 | // walks the data and counts some things that we'll look at later to make sure we get what we expect 85 | final AtomicInteger familyCount = new AtomicInteger(0); 86 | final AtomicInteger fullCount = new AtomicInteger(0); 87 | PrometheusMetricsWalker walker = new LoggingPrometheusMetricsWalker(Level.INFO) { 88 | public void walkMetricFamily(prometheus.types.MetricFamily family, int index) { 89 | super.walkMetricFamily(family, index); 90 | familyCount.incrementAndGet(); 91 | fullCount.addAndGet(family.getMetrics().size()); 92 | } 93 | }; 94 | 95 | // test the URL constructor 96 | PrometheusScraper scraper = new PrometheusScraper(testDataUrl, PrometheusDataFormat.BINARY); 97 | scraper.scrape(walker); 98 | Assert.assertEquals(71, familyCount.get()); 99 | Assert.assertEquals(126, fullCount.get()); 100 | 101 | // test the File constructor 102 | familyCount.set(0); 103 | fullCount.set(0); 104 | scraper = new PrometheusScraper(testDataFile, PrometheusDataFormat.BINARY); 105 | scraper.scrape(walker); 106 | Assert.assertEquals(71, familyCount.get()); 107 | Assert.assertEquals(126, fullCount.get()); 108 | 109 | // test the scrape() method 110 | scraper = new PrometheusScraper(testDataUrl, PrometheusDataFormat.BINARY); 111 | List allFamilies = scraper.scrape(); 112 | Assert.assertEquals(71, allFamilies.size()); 113 | } 114 | } 115 | -------------------------------------------------------------------------------- /src/test/java/org/hawkular/agent/prometheus/CounterTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2015-2016 Red Hat, Inc. and/or its affiliates 3 | * and other contributors as indicated by the @author tags. 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | */ 17 | package prometheus; 18 | 19 | import org.junit.Assert; 20 | import org.junit.Test; 21 | import prometheus.types.Counter; 22 | 23 | public class CounterTest { 24 | @Test 25 | public void testBuild() { 26 | Counter counter; 27 | 28 | try { 29 | counter = new Counter.Builder().build(); 30 | Assert.fail("Should have thrown exception because name is not set"); 31 | } catch (IllegalArgumentException expected) { 32 | } 33 | 34 | counter = new Counter.Builder().setName("foo").setValue(0.1).build(); 35 | Assert.assertEquals("foo", counter.getName()); 36 | Assert.assertEquals(0.1, counter.getValue(), 0.001); 37 | Assert.assertTrue(counter.getLabels().isEmpty()); 38 | 39 | counter = new Counter.Builder().setName("foo").setValue(0.1).addLabel("one", "111").build(); 40 | Assert.assertEquals("foo", counter.getName()); 41 | Assert.assertEquals(0.1, counter.getValue(), 0.001); 42 | Assert.assertEquals(1, counter.getLabels().size()); 43 | Assert.assertEquals("111", counter.getLabels().get("one")); 44 | 45 | counter = new Counter.Builder().setName("foo").setValue(0.1).addLabel("one", "1").addLabel("two", "2").build(); 46 | Assert.assertEquals("foo", counter.getName()); 47 | Assert.assertEquals(0.1, counter.getValue(), 0.001); 48 | Assert.assertEquals(2, counter.getLabels().size()); 49 | Assert.assertEquals("1", counter.getLabels().get("one")); 50 | Assert.assertEquals("2", counter.getLabels().get("two")); 51 | 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /src/test/java/org/hawkular/agent/prometheus/GaugeTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2015-2016 Red Hat, Inc. and/or its affiliates 3 | * and other contributors as indicated by the @author tags. 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | */ 17 | package prometheus; 18 | 19 | import org.junit.Assert; 20 | import org.junit.Test; 21 | import prometheus.types.Gauge; 22 | 23 | public class GaugeTest { 24 | @Test 25 | public void testBuild() { 26 | Gauge gauge; 27 | 28 | try { 29 | gauge = new Gauge.Builder().build(); 30 | Assert.fail("Should have thrown exception because name is not set"); 31 | } catch (IllegalArgumentException expected) { 32 | } 33 | 34 | gauge = new Gauge.Builder().setName("foo").setValue(0.1).build(); 35 | Assert.assertEquals("foo", gauge.getName()); 36 | Assert.assertEquals(0.1, gauge.getValue(), 0.001); 37 | Assert.assertTrue(gauge.getLabels().isEmpty()); 38 | 39 | gauge = new Gauge.Builder().setName("foo").setValue(0.1).addLabel("one", "111").build(); 40 | Assert.assertEquals("foo", gauge.getName()); 41 | Assert.assertEquals(0.1, gauge.getValue(), 0.001); 42 | Assert.assertEquals(1, gauge.getLabels().size()); 43 | Assert.assertEquals("111", gauge.getLabels().get("one")); 44 | 45 | gauge = new Gauge.Builder().setName("foo").setValue(0.1).addLabel("one", "1").addLabel("two", "2").build(); 46 | Assert.assertEquals("foo", gauge.getName()); 47 | Assert.assertEquals(0.1, gauge.getValue(), 0.001); 48 | Assert.assertEquals(2, gauge.getLabels().size()); 49 | Assert.assertEquals("1", gauge.getLabels().get("one")); 50 | Assert.assertEquals("2", gauge.getLabels().get("two")); 51 | 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /src/test/java/org/hawkular/agent/prometheus/HistogramTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2015-2016 Red Hat, Inc. and/or its affiliates 3 | * and other contributors as indicated by the @author tags. 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | */ 17 | package prometheus; 18 | 19 | import org.junit.Assert; 20 | import org.junit.Test; 21 | import prometheus.types.Histogram; 22 | 23 | public class HistogramTest { 24 | @Test 25 | public void testBuild() { 26 | Histogram histogram; 27 | 28 | try { 29 | histogram = new Histogram.Builder().build(); 30 | Assert.fail("Should have thrown exception because name is not set"); 31 | } catch (IllegalArgumentException expected) { 32 | } 33 | 34 | histogram = new Histogram.Builder().setName("foo").setSampleCount(123).setSampleSum(0.5) 35 | .addBucket(0.25, 100).addBucket(1.0, 200) 36 | .addLabel("one", "111").build(); 37 | Assert.assertEquals("foo", histogram.getName()); 38 | Assert.assertEquals(123, histogram.getSampleCount()); 39 | Assert.assertEquals(0.5, histogram.getSampleSum(), 0.001); 40 | Assert.assertEquals(2, histogram.getBuckets().size()); 41 | Assert.assertEquals(0.25, histogram.getBuckets().get(0).getUpperBound(), 0.01); 42 | Assert.assertEquals(100, histogram.getBuckets().get(0).getCumulativeCount()); 43 | Assert.assertEquals(1.0, histogram.getBuckets().get(1).getUpperBound(), 0.01); 44 | Assert.assertEquals(200, histogram.getBuckets().get(1).getCumulativeCount()); 45 | Assert.assertEquals(1, histogram.getLabels().size()); 46 | Assert.assertEquals("111", histogram.getLabels().get("one")); 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /src/test/java/org/hawkular/agent/prometheus/MetricFamilyTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2015-2016 Red Hat, Inc. and/or its affiliates 3 | * and other contributors as indicated by the @author tags. 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | */ 17 | package prometheus; 18 | 19 | import org.junit.Assert; 20 | import org.junit.Test; 21 | import prometheus.types.Counter; 22 | import prometheus.types.Gauge; 23 | import prometheus.types.Histogram; 24 | import prometheus.types.MetricFamily; 25 | import prometheus.types.MetricType; 26 | import prometheus.types.Summary; 27 | 28 | public class MetricFamilyTest { 29 | @Test 30 | public void testBuild() { 31 | Counter counter = new Counter.Builder().setName("foo").setValue(0.1).build(); 32 | Gauge gauge = new Gauge.Builder().setName("foo").setValue(0.1).build(); 33 | Summary summary = new Summary.Builder().setName("foo").setSampleCount(123).setSampleSum(0.5) 34 | .addQuantile(0.25, 100.1).addQuantile(0.75, 200.2).build(); 35 | Histogram histogram = new Histogram.Builder().setName("foo").setSampleCount(123).setSampleSum(0.5) 36 | .addBucket(0.25, 100).addBucket(1.0, 200).build(); 37 | 38 | try { 39 | new MetricFamily.Builder().build(); 40 | Assert.fail("Should have thrown exception because name is not set"); 41 | } catch (IllegalArgumentException expected) { 42 | } 43 | 44 | try { 45 | new MetricFamily.Builder().setName("foo").build(); 46 | Assert.fail("Should have thrown exception because type is not set"); 47 | } catch (IllegalArgumentException expected) { 48 | } 49 | 50 | try { 51 | new MetricFamily.Builder().setName("foo").setType(MetricType.COUNTER).addMetric(gauge).build(); 52 | Assert.fail("Should have thrown exception because type did not match the metrics"); 53 | } catch (IllegalArgumentException expected) { 54 | } 55 | 56 | try { 57 | new MetricFamily.Builder().setName("foo").setType(MetricType.GAUGE).addMetric(counter).build(); 58 | Assert.fail("Should have thrown exception because type did not match the metrics"); 59 | } catch (IllegalArgumentException expected) { 60 | } 61 | 62 | try { 63 | new MetricFamily.Builder().setName("foo").setType(MetricType.SUMMARY).addMetric(counter).build(); 64 | Assert.fail("Should have thrown exception because type did not match the metrics"); 65 | } catch (IllegalArgumentException expected) { 66 | } 67 | 68 | try { 69 | new MetricFamily.Builder().setName("foo").setType(MetricType.HISTOGRAM).addMetric(counter) 70 | .build(); 71 | Assert.fail("Should have thrown exception because type did not match the metrics"); 72 | } catch (IllegalArgumentException expected) { 73 | } 74 | 75 | try { 76 | new MetricFamily.Builder().setName("foo").setType(MetricType.COUNTER) 77 | .addMetric(counter).addMetric(counter).addMetric(gauge).build(); // last one isn't the right type 78 | Assert.fail("Should have thrown exception because type did not match the metrics"); 79 | } catch (IllegalArgumentException expected) { 80 | } 81 | 82 | MetricFamily family = new MetricFamily.Builder().setName("foo").setType(MetricType.SUMMARY).build(); 83 | Assert.assertEquals("foo", family.getName()); 84 | Assert.assertNull(family.getHelp()); 85 | Assert.assertEquals(MetricType.SUMMARY, family.getType()); 86 | Assert.assertNotNull(family.getMetrics()); 87 | Assert.assertTrue(family.getMetrics().isEmpty()); 88 | 89 | family = new MetricFamily.Builder().setName("foo").setHelp("foo help").setType(MetricType.COUNTER) 90 | .addMetric(counter).addMetric(counter).build(); 91 | Assert.assertEquals("foo", family.getName()); 92 | Assert.assertEquals("foo help", family.getHelp()); 93 | Assert.assertEquals(MetricType.COUNTER, family.getType()); 94 | Assert.assertEquals(2, family.getMetrics().size()); 95 | } 96 | } 97 | -------------------------------------------------------------------------------- /src/test/java/org/hawkular/agent/prometheus/SummaryTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2015-2016 Red Hat, Inc. and/or its affiliates 3 | * and other contributors as indicated by the @author tags. 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | */ 17 | package prometheus; 18 | 19 | import org.junit.Assert; 20 | import org.junit.Test; 21 | import prometheus.types.Summary; 22 | 23 | public class SummaryTest { 24 | @Test 25 | public void testBuild() { 26 | Summary summary; 27 | 28 | try { 29 | summary = new Summary.Builder().build(); 30 | Assert.fail("Should have thrown exception because name is not set"); 31 | } catch (IllegalArgumentException expected) { 32 | } 33 | 34 | summary = new Summary.Builder().setName("foo").setSampleCount(123).setSampleSum(0.5) 35 | .addQuantile(0.25, 100.1).addQuantile(0.75, 200.2) 36 | .addLabel("one", "111").build(); 37 | Assert.assertEquals("foo", summary.getName()); 38 | Assert.assertEquals(123, summary.getSampleCount()); 39 | Assert.assertEquals(0.5, summary.getSampleSum(), 0.001); 40 | Assert.assertEquals(2, summary.getQuantiles().size()); 41 | Assert.assertEquals(0.25, summary.getQuantiles().get(0).getQuantile(), 0.01); 42 | Assert.assertEquals(100.1, summary.getQuantiles().get(0).getValue(), 0.01); 43 | Assert.assertEquals(0.75, summary.getQuantiles().get(1).getQuantile(), 0.01); 44 | Assert.assertEquals(200.2, summary.getQuantiles().get(1).getValue(), 0.01); 45 | Assert.assertEquals(1, summary.getLabels().size()); 46 | Assert.assertEquals("111", summary.getLabels().get("one")); 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /src/test/java/org/hawkular/agent/prometheus/TextPrometheusParserTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2015-2016 Red Hat, Inc. and/or its affiliates 3 | * and other contributors as indicated by the @author tags. 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | */ 17 | 18 | package prometheus; 19 | 20 | import java.io.InputStream; 21 | import java.net.URL; 22 | import java.util.ArrayList; 23 | import java.util.List; 24 | import java.util.concurrent.atomic.AtomicInteger; 25 | 26 | import org.jboss.logging.Logger.Level; 27 | import org.junit.Assert; 28 | import org.junit.Test; 29 | import prometheus.text.TextPrometheusMetricDataParser; 30 | import prometheus.text.TextPrometheusMetricsProcessor; 31 | import prometheus.types.Counter; 32 | import prometheus.types.Gauge; 33 | import prometheus.types.Histogram; 34 | import prometheus.types.MetricFamily; 35 | import prometheus.types.MetricType; 36 | import prometheus.types.Summary; 37 | import prometheus.walkers.LoggingPrometheusMetricsWalker; 38 | import prometheus.walkers.PrometheusMetricsWalker; 39 | 40 | public class TextPrometheusParserTest { 41 | 42 | private List parseTestFile(String fileName) throws Exception { 43 | List metricFamilies = new ArrayList<>(); 44 | 45 | try (InputStream testData = getClass().getClassLoader().getResourceAsStream(fileName)) { 46 | TextPrometheusMetricDataParser parser = new TextPrometheusMetricDataParser(testData); 47 | while (true) { 48 | MetricFamily family = parser.parse(); 49 | if (family == null) { 50 | break; 51 | } 52 | metricFamilies.add(family); 53 | } 54 | } 55 | 56 | return metricFamilies; 57 | } 58 | 59 | @Test 60 | public void testCounter() throws Exception { 61 | List metricFamilies = parseTestFile("prometheus-counter.txt"); 62 | 63 | Assert.assertNotNull(metricFamilies); 64 | Assert.assertEquals(1, metricFamilies.size()); 65 | Assert.assertEquals("http_requests_total", metricFamilies.get(0).getName()); 66 | Assert.assertEquals("Total number of HTTP requests made.", metricFamilies.get(0).getHelp()); 67 | Assert.assertEquals(MetricType.COUNTER, metricFamilies.get(0).getType()); 68 | Assert.assertEquals(5, metricFamilies.get(0).getMetrics().size()); 69 | 70 | Counter counter = (Counter) metricFamilies.get(0).getMetrics().get(0); 71 | Assert.assertEquals("http_requests_total", counter.getName()); 72 | Assert.assertEquals(162030, counter.getValue(), 0.1); 73 | Assert.assertEquals("200", counter.getLabels().get("code")); 74 | Assert.assertEquals("prometheus", counter.getLabels().get("handler")); 75 | Assert.assertEquals("get", counter.getLabels().get("method")); 76 | } 77 | 78 | @Test 79 | public void testGauge() throws Exception { 80 | List metricFamilies = parseTestFile("prometheus-gauge.txt"); 81 | 82 | Assert.assertNotNull(metricFamilies); 83 | Assert.assertEquals(1, metricFamilies.size()); 84 | Assert.assertEquals("go_memstats_alloc_bytes", metricFamilies.get(0).getName()); 85 | Assert.assertEquals("", metricFamilies.get(0).getHelp()); 86 | Assert.assertEquals(MetricType.GAUGE, metricFamilies.get(0).getType()); 87 | Assert.assertEquals(1, metricFamilies.get(0).getMetrics().size()); 88 | 89 | Gauge gauge = (Gauge) metricFamilies.get(0).getMetrics().get(0); 90 | Assert.assertEquals("go_memstats_alloc_bytes", gauge.getName()); 91 | Assert.assertEquals(4.14422136e+08, gauge.getValue(), 0.1); 92 | Assert.assertEquals(0, gauge.getLabels().size()); 93 | 94 | } 95 | 96 | @Test 97 | public void testSummary() throws Exception { 98 | List metricFamilies = parseTestFile("prometheus-summary.txt"); 99 | 100 | Assert.assertNotNull(metricFamilies); 101 | Assert.assertEquals(1, metricFamilies.size()); 102 | Assert.assertEquals("prometheus_local_storage_maintain_series_duration_milliseconds", 103 | metricFamilies.get(0).getName()); 104 | Assert.assertEquals("The duration (in milliseconds) it took to perform maintenance on a series.", 105 | metricFamilies.get(0).getHelp()); 106 | Assert.assertEquals(MetricType.SUMMARY, metricFamilies.get(0).getType()); 107 | Assert.assertEquals(2, metricFamilies.get(0).getMetrics().size()); 108 | 109 | // the metrics should appear in order as they appear in the text. First is "memory", second is "disk" 110 | Summary first = (Summary) metricFamilies.get(0).getMetrics().get(0); 111 | Summary second = (Summary) metricFamilies.get(0).getMetrics().get(1); 112 | Assert.assertEquals(1, first.getLabels().size()); 113 | Assert.assertTrue(first.getLabels().containsKey("location")); 114 | Assert.assertEquals("memory", first.getLabels().get("location")); 115 | Assert.assertEquals(1, second.getLabels().size()); 116 | Assert.assertTrue(second.getLabels().containsKey("location")); 117 | Assert.assertEquals("disk", second.getLabels().get("location")); 118 | Assert.assertEquals(12345, second.getSampleCount()); 119 | Assert.assertEquals(1.5, second.getSampleSum(), 0.01); 120 | Assert.assertEquals(0.50, second.getQuantiles().get(0).getQuantile(), 0.01); 121 | Assert.assertEquals(40.0, second.getQuantiles().get(0).getValue(), 0.01); 122 | Assert.assertEquals(0.90, second.getQuantiles().get(1).getQuantile(), 0.01); 123 | Assert.assertEquals(50.0, second.getQuantiles().get(1).getValue(), 0.01); 124 | Assert.assertEquals(0.99, second.getQuantiles().get(2).getQuantile(), 0.01); 125 | Assert.assertEquals(60.0, second.getQuantiles().get(2).getValue(), 0.01); 126 | 127 | } 128 | 129 | @Test 130 | public void testHistogram() throws Exception { 131 | List metricFamilies = parseTestFile("prometheus-histogram.txt"); 132 | 133 | Assert.assertNotNull(metricFamilies); 134 | Assert.assertEquals(1, metricFamilies.size()); 135 | Assert.assertEquals("http_request_duration_seconds", metricFamilies.get(0).getName()); 136 | Assert.assertEquals("A histogram of the request duration.", metricFamilies.get(0).getHelp()); 137 | Assert.assertEquals(MetricType.HISTOGRAM, metricFamilies.get(0).getType()); 138 | Assert.assertEquals(1, metricFamilies.get(0).getMetrics().size()); 139 | Histogram metric = (Histogram) metricFamilies.get(0).getMetrics().get(0); 140 | Assert.assertEquals(1, metric.getLabels().size()); 141 | Assert.assertTrue(metric.getLabels().containsKey("mylabel")); 142 | Assert.assertEquals("wotgorilla?", metric.getLabels().get("mylabel")); 143 | Assert.assertEquals(144320, metric.getSampleCount()); 144 | Assert.assertEquals(53423, metric.getSampleSum(), 0.01); 145 | Assert.assertEquals(0.05, metric.getBuckets().get(0).getUpperBound(), 0.001); 146 | Assert.assertEquals(24054, metric.getBuckets().get(0).getCumulativeCount()); 147 | Assert.assertEquals(0.1, metric.getBuckets().get(1).getUpperBound(), 0.001); 148 | Assert.assertEquals(33444, metric.getBuckets().get(1).getCumulativeCount()); 149 | Assert.assertEquals(0.2, metric.getBuckets().get(2).getUpperBound(), 0.001); 150 | Assert.assertEquals(100392, metric.getBuckets().get(2).getCumulativeCount()); 151 | Assert.assertEquals(0.5, metric.getBuckets().get(3).getUpperBound(), 0.001); 152 | Assert.assertEquals(129389, metric.getBuckets().get(3).getCumulativeCount()); 153 | Assert.assertEquals(1.0, metric.getBuckets().get(4).getUpperBound(), 0.001); 154 | Assert.assertEquals(133988, metric.getBuckets().get(4).getCumulativeCount()); 155 | Assert.assertEquals(Double.POSITIVE_INFINITY, metric.getBuckets().get(5).getUpperBound(), 0.001); 156 | Assert.assertEquals(144320, metric.getBuckets().get(5).getCumulativeCount()); 157 | } 158 | 159 | @Test 160 | public void testThreeCounters() throws Exception { 161 | List metricFamilies = parseTestFile("prometheus-three-counters.txt"); 162 | 163 | Assert.assertNotNull(metricFamilies); 164 | Assert.assertEquals(3, metricFamilies.size()); 165 | 166 | // the first one 167 | 168 | int familyIndex = 0; 169 | Assert.assertEquals("one_counter_total", metricFamilies.get(familyIndex).getName()); 170 | Assert.assertEquals("This is the first", metricFamilies.get(familyIndex).getHelp()); 171 | Assert.assertEquals(MetricType.COUNTER, metricFamilies.get(familyIndex).getType()); 172 | Assert.assertEquals(1, metricFamilies.get(familyIndex).getMetrics().size()); 173 | 174 | Counter counter = (Counter) metricFamilies.get(familyIndex).getMetrics().get(0); 175 | Assert.assertEquals("one_counter_total", counter.getName()); 176 | Assert.assertEquals(111, counter.getValue(), 0.1); 177 | Assert.assertEquals(0, counter.getLabels().size()); 178 | 179 | // the second one - tests that the text data parser can properly buffer lines between families 180 | 181 | familyIndex = 1; 182 | Assert.assertEquals("two_counter_total", metricFamilies.get(familyIndex).getName()); 183 | Assert.assertEquals("This is the second", metricFamilies.get(familyIndex).getHelp()); 184 | Assert.assertEquals(MetricType.COUNTER, metricFamilies.get(familyIndex).getType()); 185 | Assert.assertEquals(1, metricFamilies.get(familyIndex).getMetrics().size()); 186 | 187 | counter = (Counter) metricFamilies.get(familyIndex).getMetrics().get(0); 188 | Assert.assertEquals("two_counter_total", counter.getName()); 189 | Assert.assertEquals(222, counter.getValue(), 0.1); 190 | Assert.assertEquals(0, counter.getLabels().size()); 191 | 192 | // the third one 193 | 194 | familyIndex = 2; 195 | Assert.assertEquals("three_counter_total", metricFamilies.get(familyIndex).getName()); 196 | Assert.assertEquals("This is the third with type specified first", metricFamilies.get(familyIndex).getHelp()); 197 | Assert.assertEquals(MetricType.COUNTER, metricFamilies.get(familyIndex).getType()); 198 | Assert.assertEquals(1, metricFamilies.get(familyIndex).getMetrics().size()); 199 | 200 | counter = (Counter) metricFamilies.get(familyIndex).getMetrics().get(0); 201 | Assert.assertEquals("three_counter_total", counter.getName()); 202 | Assert.assertEquals(333, counter.getValue(), 0.1); 203 | Assert.assertEquals(0, counter.getLabels().size()); 204 | } 205 | 206 | @Test 207 | public void testGetMetricsFromStream() throws Exception { 208 | 209 | List metricFamilies = parseTestFile("prometheus.txt"); 210 | 211 | Assert.assertNotNull(metricFamilies); 212 | Assert.assertEquals(72, metricFamilies.size()); 213 | 214 | // walk the data and make sure it has what we expect 215 | final AtomicInteger familyCount = new AtomicInteger(0); 216 | final AtomicInteger fullCount = new AtomicInteger(0); 217 | PrometheusMetricsWalker walker = new LoggingPrometheusMetricsWalker(Level.INFO) { 218 | public void walkMetricFamily(MetricFamily family, int index) { 219 | super.walkMetricFamily(family, index); 220 | familyCount.incrementAndGet(); 221 | fullCount.addAndGet(family.getMetrics().size()); 222 | } 223 | }; 224 | 225 | try (InputStream testData = this.getClass().getClassLoader().getResourceAsStream("prometheus.txt")) { 226 | new TextPrometheusMetricsProcessor(testData, walker).walk(); 227 | } 228 | Assert.assertEquals(metricFamilies.size(), familyCount.get()); 229 | Assert.assertEquals(127, fullCount.get()); 230 | } 231 | 232 | @Test 233 | public void testGetMetricsFromUrl() throws Exception { 234 | URL testDataUrl = getClass().getClassLoader().getResource("prometheus.txt"); 235 | PrometheusScraper scraper = new PrometheusScraper(testDataUrl, PrometheusDataFormat.TEXT); 236 | 237 | // walk the data and make sure it has what we expect 238 | final AtomicInteger familyCount = new AtomicInteger(0); 239 | final AtomicInteger fullCount = new AtomicInteger(0); 240 | PrometheusMetricsWalker walker = new LoggingPrometheusMetricsWalker(Level.INFO) { 241 | public void walkMetricFamily(MetricFamily family, int index) { 242 | super.walkMetricFamily(family, index); 243 | familyCount.incrementAndGet(); 244 | fullCount.addAndGet(family.getMetrics().size()); 245 | } 246 | }; 247 | scraper.scrape(walker); 248 | 249 | Assert.assertEquals(72, familyCount.get()); 250 | Assert.assertEquals(127, fullCount.get()); 251 | 252 | // test the scrape() method 253 | scraper = new PrometheusScraper(testDataUrl, PrometheusDataFormat.TEXT); 254 | List allFamilies = scraper.scrape(); 255 | Assert.assertEquals(72, allFamilies.size()); 256 | } 257 | } 258 | -------------------------------------------------------------------------------- /src/test/java/org/hawkular/agent/prometheus/UtilTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2015-2016 Red Hat, Inc. and/or its affiliates 3 | * and other contributors as indicated by the @author tags. 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | */ 17 | package prometheus; 18 | 19 | import org.junit.Assert; 20 | import org.junit.Test; 21 | 22 | public class UtilTest { 23 | 24 | @Test 25 | public void testConvertDoubleToString() { 26 | Assert.assertEquals("-Inf", Util.convertDoubleToString(Double.NEGATIVE_INFINITY)); 27 | Assert.assertEquals("+Inf", Util.convertDoubleToString(Double.POSITIVE_INFINITY)); 28 | Assert.assertEquals("NaN", Util.convertDoubleToString(Double.NaN)); 29 | } 30 | 31 | @Test 32 | public void testConvertStringToDouble() { 33 | Assert.assertEquals(Double.NEGATIVE_INFINITY, Util.convertStringToDouble("-Inf"), 0.001); 34 | Assert.assertEquals(Double.POSITIVE_INFINITY, Util.convertStringToDouble("+Inf"), 0.001); 35 | Assert.assertEquals(Double.NaN, Util.convertStringToDouble("NaN"), 0.001); 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /src/test/resources/prometheus-counter.txt: -------------------------------------------------------------------------------- 1 | # just 2 | # a 3 | # small comment 4 | # HELP http_requests_total Total number of HTTP requests made. 5 | # TYPE http_requests_total counter 6 | 7 | # and 8 | # yet more 9 | # comments are here 10 | http_requests_total{code="200",handler="prometheus",method="get"} 162030 11 | http_requests_total{code="200",handler="query",method="get"} 40 12 | http_requests_total{code="200",handler="series",method="get"} NaN 13 | http_requests_total{code="400",handler="query",method="get"} +Inf 14 | http_requests_total{code="400",handler="series",method="get"} -Inf 15 | 16 | # just a commment 17 | # 18 | -------------------------------------------------------------------------------- /src/test/resources/prometheus-gauge.txt: -------------------------------------------------------------------------------- 1 | # note this gauge has a timestamp - which is ignored/unsupported. 2 | 3 | # TYPE go_memstats_alloc_bytes gauge 4 | go_memstats_alloc_bytes 4.14422136e+08 123456 5 | -------------------------------------------------------------------------------- /src/test/resources/prometheus-histogram.txt: -------------------------------------------------------------------------------- 1 | 2 | # TESTING: A histogram, which has a pretty complex representation in the text format 3 | # Notice all have timestamps. 4 | # HELP http_request_duration_seconds A histogram of the request duration. 5 | # TYPE http_request_duration_seconds histogram 6 | http_request_duration_seconds_bucket{mylabel="wotgorilla?",le="0.05"} 24054 123456789 7 | http_request_duration_seconds_bucket{mylabel="wotgorilla?",le="0.1"} 33444 123456789 8 | http_request_duration_seconds_bucket{mylabel="wotgorilla?",le="0.2"} 100392 123456789 9 | http_request_duration_seconds_bucket{mylabel="wotgorilla?",le="0.5"} 129389 123456789 10 | http_request_duration_seconds_bucket{mylabel="wotgorilla?",le="1"} 1.33988E5 123456789 11 | http_request_duration_seconds_bucket{mylabel="wotgorilla?",le="+Inf"} 144320.0 123456789 12 | http_request_duration_seconds_sum{mylabel="wotgorilla?"} 53423.0 123456789 13 | http_request_duration_seconds_count{mylabel="wotgorilla?"} 1.4432E5 123456789 14 | -------------------------------------------------------------------------------- /src/test/resources/prometheus-summary.txt: -------------------------------------------------------------------------------- 1 | # HELP prometheus_local_storage_maintain_series_duration_milliseconds The duration (in milliseconds) it took to perform maintenance on a series. 2 | # TYPE prometheus_local_storage_maintain_series_duration_milliseconds summary 3 | prometheus_local_storage_maintain_series_duration_milliseconds{location="memory",quantile="0.5"} 50.467295 4 | prometheus_local_storage_maintain_series_duration_milliseconds{location="memory",quantile="0.9"} 63.920446 5 | prometheus_local_storage_maintain_series_duration_milliseconds{location="memory",quantile="0.99"} 73.236479 6 | prometheus_local_storage_maintain_series_duration_milliseconds_sum{location="memory"} 4.238477484786966e+06 7 | prometheus_local_storage_maintain_series_duration_milliseconds_count{location="memory"} 80608 8 | prometheus_local_storage_maintain_series_duration_milliseconds{location="disk",quantile="0.5"} 40.0 9 | prometheus_local_storage_maintain_series_duration_milliseconds{location="disk",quantile="0.9"} 50.0 10 | prometheus_local_storage_maintain_series_duration_milliseconds{location="disk",quantile="0.99"} 60.0 11 | prometheus_local_storage_maintain_series_duration_milliseconds_sum{location="disk"} 1.5 12 | prometheus_local_storage_maintain_series_duration_milliseconds_count{location="disk"} 12345 13 | -------------------------------------------------------------------------------- /src/test/resources/prometheus-three-counters.txt: -------------------------------------------------------------------------------- 1 | # HELP one_counter_total This is the first 2 | # TYPE one_counter_total counter 3 | one_counter_total 111 4 | # HELP two_counter_total This is the second 5 | # TYPE two_counter_total counter 6 | two_counter_total 222 7 | # TYPE three_counter_total counter 8 | # HELP three_counter_total This is the third with type specified first 9 | three_counter_total 333 10 | -------------------------------------------------------------------------------- /src/test/resources/prometheus.data: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jmazzitelli/prometheus-scraper/220c3c330d157dd3364bc05eaa8780f1bcd9ba1e/src/test/resources/prometheus.data -------------------------------------------------------------------------------- /src/test/resources/prometheus.txt: -------------------------------------------------------------------------------- 1 | # HELP go_gc_duration_seconds A summary of the GC invocation durations. 2 | # TYPE go_gc_duration_seconds summary 3 | go_gc_duration_seconds{quantile="0"} 0.0025097400000000003 4 | go_gc_duration_seconds{quantile="0.25"} 0.002858241 5 | go_gc_duration_seconds{quantile="0.5"} 0.0030191600000000003 6 | go_gc_duration_seconds{quantile="0.75"} 0.003817389 7 | go_gc_duration_seconds{quantile="1"} 0.007688749000000001 8 | go_gc_duration_seconds_sum 14.316955358 9 | go_gc_duration_seconds_count 6943 10 | # HELP go_goroutines Number of goroutines that currently exist. 11 | # TYPE go_goroutines gauge 12 | go_goroutines 79 13 | # HELP go_memstats_alloc_bytes Number of bytes allocated and still in use. 14 | # TYPE go_memstats_alloc_bytes gauge 15 | go_memstats_alloc_bytes 4.14422136e+08 16 | # HELP go_memstats_alloc_bytes_total Total number of bytes allocated, even if freed. 17 | # TYPE go_memstats_alloc_bytes_total counter 18 | go_memstats_alloc_bytes_total 3.7473082544e+11 19 | # HELP go_memstats_buck_hash_sys_bytes Number of bytes used by the profiling bucket hash table. 20 | # TYPE go_memstats_buck_hash_sys_bytes gauge 21 | go_memstats_buck_hash_sys_bytes 1.633621e+06 22 | # HELP go_memstats_frees_total Total number of frees. 23 | # TYPE go_memstats_frees_total counter 24 | go_memstats_frees_total 2.347028572e+09 25 | # HELP go_memstats_gc_sys_bytes Number of bytes used for garbage collection system metadata. 26 | # TYPE go_memstats_gc_sys_bytes gauge 27 | go_memstats_gc_sys_bytes 1.4961408e+07 28 | # HELP go_memstats_heap_alloc_bytes Number of heap bytes allocated and still in use. 29 | # TYPE go_memstats_heap_alloc_bytes gauge 30 | go_memstats_heap_alloc_bytes 4.14422136e+08 31 | # HELP go_memstats_heap_idle_bytes Number of heap bytes waiting to be used. 32 | # TYPE go_memstats_heap_idle_bytes gauge 33 | go_memstats_heap_idle_bytes 3.0318592e+07 34 | # HELP go_memstats_heap_inuse_bytes Number of heap bytes that are in use. 35 | # TYPE go_memstats_heap_inuse_bytes gauge 36 | go_memstats_heap_inuse_bytes 4.34987008e+08 37 | # HELP go_memstats_heap_objects Number of allocated objects. 38 | # TYPE go_memstats_heap_objects gauge 39 | go_memstats_heap_objects 1.346828e+06 40 | # HELP go_memstats_heap_released_bytes_total Total number of heap bytes released to OS. 41 | # TYPE go_memstats_heap_released_bytes_total counter 42 | go_memstats_heap_released_bytes_total 0 43 | # HELP go_memstats_heap_sys_bytes Number of heap bytes obtained from system. 44 | # TYPE go_memstats_heap_sys_bytes gauge 45 | go_memstats_heap_sys_bytes 4.653056e+08 46 | # HELP go_memstats_last_gc_time_seconds Number of seconds since 1970 of last garbage collection. 47 | # TYPE go_memstats_last_gc_time_seconds gauge 48 | go_memstats_last_gc_time_seconds 1.4607636037672102e+19 49 | # HELP go_memstats_lookups_total Total number of pointer lookups. 50 | # TYPE go_memstats_lookups_total counter 51 | go_memstats_lookups_total 3.066191e+06 52 | # HELP go_memstats_mallocs_total Total number of mallocs. 53 | # TYPE go_memstats_mallocs_total counter 54 | go_memstats_mallocs_total 2.3483754e+09 55 | # HELP go_memstats_mcache_inuse_bytes Number of bytes in use by mcache structures. 56 | # TYPE go_memstats_mcache_inuse_bytes gauge 57 | go_memstats_mcache_inuse_bytes 4832 58 | # HELP go_memstats_mcache_sys_bytes Number of bytes used for mcache structures obtained from system. 59 | # TYPE go_memstats_mcache_sys_bytes gauge 60 | go_memstats_mcache_sys_bytes 16384 61 | # HELP go_memstats_mspan_inuse_bytes Number of bytes in use by mspan structures. 62 | # TYPE go_memstats_mspan_inuse_bytes gauge 63 | go_memstats_mspan_inuse_bytes 5.291776e+06 64 | # HELP go_memstats_mspan_sys_bytes Number of bytes used for mspan structures obtained from system. 65 | # TYPE go_memstats_mspan_sys_bytes gauge 66 | go_memstats_mspan_sys_bytes 5.586944e+06 67 | # HELP go_memstats_next_gc_bytes Number of heap bytes when next garbage collection will take place. 68 | # TYPE go_memstats_next_gc_bytes gauge 69 | go_memstats_next_gc_bytes 7.28100906e+08 70 | # HELP go_memstats_other_sys_bytes Number of bytes used for other system allocations. 71 | # TYPE go_memstats_other_sys_bytes gauge 72 | go_memstats_other_sys_bytes 2.120515e+06 73 | # HELP go_memstats_stack_inuse_bytes Number of bytes in use by the stack allocator. 74 | # TYPE go_memstats_stack_inuse_bytes gauge 75 | go_memstats_stack_inuse_bytes 1.31072e+06 76 | # HELP go_memstats_stack_sys_bytes Number of bytes obtained from system for stack allocator. 77 | # TYPE go_memstats_stack_sys_bytes gauge 78 | go_memstats_stack_sys_bytes 1.31072e+06 79 | # HELP go_memstats_sys_bytes Number of bytes obtained by system. Sum of all system allocations. 80 | # TYPE go_memstats_sys_bytes gauge 81 | go_memstats_sys_bytes 4.90935192e+08 82 | # HELP http_request_duration_microseconds The HTTP request latencies in microseconds. 83 | # TYPE http_request_duration_microseconds summary 84 | http_request_duration_microseconds{handler="alerts",quantile="0.5"} NaN 85 | http_request_duration_microseconds{handler="alerts",quantile="0.9"} NaN 86 | http_request_duration_microseconds{handler="alerts",quantile="0.99"} NaN 87 | http_request_duration_microseconds_sum{handler="alerts"} 0 88 | http_request_duration_microseconds_count{handler="alerts"} 0 89 | http_request_duration_microseconds{handler="consoles",quantile="0.5"} NaN 90 | http_request_duration_microseconds{handler="consoles",quantile="0.9"} NaN 91 | http_request_duration_microseconds{handler="consoles",quantile="0.99"} NaN 92 | http_request_duration_microseconds_sum{handler="consoles"} 0 93 | http_request_duration_microseconds_count{handler="consoles"} 0 94 | http_request_duration_microseconds{handler="drop_series",quantile="0.5"} NaN 95 | http_request_duration_microseconds{handler="drop_series",quantile="0.9"} NaN 96 | http_request_duration_microseconds{handler="drop_series",quantile="0.99"} NaN 97 | http_request_duration_microseconds_sum{handler="drop_series"} 0 98 | http_request_duration_microseconds_count{handler="drop_series"} 0 99 | http_request_duration_microseconds{handler="federate",quantile="0.5"} NaN 100 | http_request_duration_microseconds{handler="federate",quantile="0.9"} NaN 101 | http_request_duration_microseconds{handler="federate",quantile="0.99"} NaN 102 | http_request_duration_microseconds_sum{handler="federate"} 0 103 | http_request_duration_microseconds_count{handler="federate"} 0 104 | http_request_duration_microseconds{handler="graph",quantile="0.5"} NaN 105 | http_request_duration_microseconds{handler="graph",quantile="0.9"} NaN 106 | http_request_duration_microseconds{handler="graph",quantile="0.99"} NaN 107 | http_request_duration_microseconds_sum{handler="graph"} 0 108 | http_request_duration_microseconds_count{handler="graph"} 0 109 | http_request_duration_microseconds{handler="heap",quantile="0.5"} NaN 110 | http_request_duration_microseconds{handler="heap",quantile="0.9"} NaN 111 | http_request_duration_microseconds{handler="heap",quantile="0.99"} NaN 112 | http_request_duration_microseconds_sum{handler="heap"} 0 113 | http_request_duration_microseconds_count{handler="heap"} 0 114 | http_request_duration_microseconds{handler="label_values",quantile="0.5"} NaN 115 | http_request_duration_microseconds{handler="label_values",quantile="0.9"} NaN 116 | http_request_duration_microseconds{handler="label_values",quantile="0.99"} NaN 117 | http_request_duration_microseconds_sum{handler="label_values"} 0 118 | http_request_duration_microseconds_count{handler="label_values"} 0 119 | http_request_duration_microseconds{handler="metrics",quantile="0.5"} NaN 120 | http_request_duration_microseconds{handler="metrics",quantile="0.9"} NaN 121 | http_request_duration_microseconds{handler="metrics",quantile="0.99"} NaN 122 | http_request_duration_microseconds_sum{handler="metrics"} 0 123 | http_request_duration_microseconds_count{handler="metrics"} 0 124 | http_request_duration_microseconds{handler="options",quantile="0.5"} NaN 125 | http_request_duration_microseconds{handler="options",quantile="0.9"} NaN 126 | http_request_duration_microseconds{handler="options",quantile="0.99"} NaN 127 | http_request_duration_microseconds_sum{handler="options"} 0 128 | http_request_duration_microseconds_count{handler="options"} 0 129 | http_request_duration_microseconds{handler="prometheus",quantile="0.5"} 4083.644 130 | http_request_duration_microseconds{handler="prometheus",quantile="0.9"} 4370.735 131 | http_request_duration_microseconds{handler="prometheus",quantile="0.99"} 4781.504 132 | http_request_duration_microseconds_sum{handler="prometheus"} 5.97183323748989e+08 133 | http_request_duration_microseconds_count{handler="prometheus"} 162030 134 | http_request_duration_microseconds{handler="query",quantile="0.5"} NaN 135 | http_request_duration_microseconds{handler="query",quantile="0.9"} NaN 136 | http_request_duration_microseconds{handler="query",quantile="0.99"} NaN 137 | http_request_duration_microseconds_sum{handler="query"} 390693.079 138 | http_request_duration_microseconds_count{handler="query"} 46 139 | http_request_duration_microseconds{handler="query_range",quantile="0.5"} NaN 140 | http_request_duration_microseconds{handler="query_range",quantile="0.9"} NaN 141 | http_request_duration_microseconds{handler="query_range",quantile="0.99"} NaN 142 | http_request_duration_microseconds_sum{handler="query_range"} 0 143 | http_request_duration_microseconds_count{handler="query_range"} 0 144 | http_request_duration_microseconds{handler="series",quantile="0.5"} NaN 145 | http_request_duration_microseconds{handler="series",quantile="0.9"} NaN 146 | http_request_duration_microseconds{handler="series",quantile="0.99"} NaN 147 | http_request_duration_microseconds_sum{handler="series"} 54343.969999999994 148 | http_request_duration_microseconds_count{handler="series"} 10 149 | http_request_duration_microseconds{handler="static",quantile="0.5"} NaN 150 | http_request_duration_microseconds{handler="static",quantile="0.9"} NaN 151 | http_request_duration_microseconds{handler="static",quantile="0.99"} NaN 152 | http_request_duration_microseconds_sum{handler="static"} 0 153 | http_request_duration_microseconds_count{handler="static"} 0 154 | http_request_duration_microseconds{handler="status",quantile="0.5"} NaN 155 | http_request_duration_microseconds{handler="status",quantile="0.9"} NaN 156 | http_request_duration_microseconds{handler="status",quantile="0.99"} NaN 157 | http_request_duration_microseconds_sum{handler="status"} 0 158 | http_request_duration_microseconds_count{handler="status"} 0 159 | http_request_duration_microseconds{handler="version",quantile="0.5"} NaN 160 | http_request_duration_microseconds{handler="version",quantile="0.9"} NaN 161 | http_request_duration_microseconds{handler="version",quantile="0.99"} NaN 162 | http_request_duration_microseconds_sum{handler="version"} 0 163 | http_request_duration_microseconds_count{handler="version"} 0 164 | # HELP http_request_size_bytes The HTTP request sizes in bytes. 165 | # TYPE http_request_size_bytes summary 166 | http_request_size_bytes{handler="alerts",quantile="0.5"} NaN 167 | http_request_size_bytes{handler="alerts",quantile="0.9"} NaN 168 | http_request_size_bytes{handler="alerts",quantile="0.99"} NaN 169 | http_request_size_bytes_sum{handler="alerts"} 0 170 | http_request_size_bytes_count{handler="alerts"} 0 171 | http_request_size_bytes{handler="consoles",quantile="0.5"} NaN 172 | http_request_size_bytes{handler="consoles",quantile="0.9"} NaN 173 | http_request_size_bytes{handler="consoles",quantile="0.99"} NaN 174 | http_request_size_bytes_sum{handler="consoles"} 0 175 | http_request_size_bytes_count{handler="consoles"} 0 176 | http_request_size_bytes{handler="drop_series",quantile="0.5"} NaN 177 | http_request_size_bytes{handler="drop_series",quantile="0.9"} NaN 178 | http_request_size_bytes{handler="drop_series",quantile="0.99"} NaN 179 | http_request_size_bytes_sum{handler="drop_series"} 0 180 | http_request_size_bytes_count{handler="drop_series"} 0 181 | http_request_size_bytes{handler="federate",quantile="0.5"} NaN 182 | http_request_size_bytes{handler="federate",quantile="0.9"} NaN 183 | http_request_size_bytes{handler="federate",quantile="0.99"} NaN 184 | http_request_size_bytes_sum{handler="federate"} 0 185 | http_request_size_bytes_count{handler="federate"} 0 186 | http_request_size_bytes{handler="graph",quantile="0.5"} NaN 187 | http_request_size_bytes{handler="graph",quantile="0.9"} NaN 188 | http_request_size_bytes{handler="graph",quantile="0.99"} NaN 189 | http_request_size_bytes_sum{handler="graph"} 0 190 | http_request_size_bytes_count{handler="graph"} 0 191 | http_request_size_bytes{handler="heap",quantile="0.5"} NaN 192 | http_request_size_bytes{handler="heap",quantile="0.9"} NaN 193 | http_request_size_bytes{handler="heap",quantile="0.99"} NaN 194 | http_request_size_bytes_sum{handler="heap"} 0 195 | http_request_size_bytes_count{handler="heap"} 0 196 | http_request_size_bytes{handler="label_values",quantile="0.5"} NaN 197 | http_request_size_bytes{handler="label_values",quantile="0.9"} NaN 198 | http_request_size_bytes{handler="label_values",quantile="0.99"} NaN 199 | http_request_size_bytes_sum{handler="label_values"} 0 200 | http_request_size_bytes_count{handler="label_values"} 0 201 | http_request_size_bytes{handler="metrics",quantile="0.5"} NaN 202 | http_request_size_bytes{handler="metrics",quantile="0.9"} NaN 203 | http_request_size_bytes{handler="metrics",quantile="0.99"} NaN 204 | http_request_size_bytes_sum{handler="metrics"} 0 205 | http_request_size_bytes_count{handler="metrics"} 0 206 | http_request_size_bytes{handler="options",quantile="0.5"} NaN 207 | http_request_size_bytes{handler="options",quantile="0.9"} NaN 208 | http_request_size_bytes{handler="options",quantile="0.99"} NaN 209 | http_request_size_bytes_sum{handler="options"} 0 210 | http_request_size_bytes_count{handler="options"} 0 211 | http_request_size_bytes{handler="prometheus",quantile="0.5"} 305 212 | http_request_size_bytes{handler="prometheus",quantile="0.9"} 305 213 | http_request_size_bytes{handler="prometheus",quantile="0.99"} 305 214 | http_request_size_bytes_sum{handler="prometheus"} 4.9403201e+07 215 | http_request_size_bytes_count{handler="prometheus"} 162030 216 | http_request_size_bytes{handler="query",quantile="0.5"} NaN 217 | http_request_size_bytes{handler="query",quantile="0.9"} NaN 218 | http_request_size_bytes{handler="query",quantile="0.99"} NaN 219 | http_request_size_bytes_sum{handler="query"} 46169 220 | http_request_size_bytes_count{handler="query"} 46 221 | http_request_size_bytes{handler="query_range",quantile="0.5"} NaN 222 | http_request_size_bytes{handler="query_range",quantile="0.9"} NaN 223 | http_request_size_bytes{handler="query_range",quantile="0.99"} NaN 224 | http_request_size_bytes_sum{handler="query_range"} 0 225 | http_request_size_bytes_count{handler="query_range"} 0 226 | http_request_size_bytes{handler="series",quantile="0.5"} NaN 227 | http_request_size_bytes{handler="series",quantile="0.9"} NaN 228 | http_request_size_bytes{handler="series",quantile="0.99"} NaN 229 | http_request_size_bytes_sum{handler="series"} 12533 230 | http_request_size_bytes_count{handler="series"} 10 231 | http_request_size_bytes{handler="static",quantile="0.5"} NaN 232 | http_request_size_bytes{handler="static",quantile="0.9"} NaN 233 | http_request_size_bytes{handler="static",quantile="0.99"} NaN 234 | http_request_size_bytes_sum{handler="static"} 0 235 | http_request_size_bytes_count{handler="static"} 0 236 | http_request_size_bytes{handler="status",quantile="0.5"} NaN 237 | http_request_size_bytes{handler="status",quantile="0.9"} NaN 238 | http_request_size_bytes{handler="status",quantile="0.99"} NaN 239 | http_request_size_bytes_sum{handler="status"} 0 240 | http_request_size_bytes_count{handler="status"} 0 241 | http_request_size_bytes{handler="version",quantile="0.5"} NaN 242 | http_request_size_bytes{handler="version",quantile="0.9"} NaN 243 | http_request_size_bytes{handler="version",quantile="0.99"} NaN 244 | http_request_size_bytes_sum{handler="version"} 0 245 | http_request_size_bytes_count{handler="version"} 0 246 | # HELP http_requests_total Total number of HTTP requests made. 247 | # TYPE http_requests_total counter 248 | http_requests_total{code="200",handler="prometheus",method="get",} 162030 249 | http_requests_total{code="200",handler="query",method="get",} 40 250 | http_requests_total{code="200",handler="series",method="get",} 4 251 | http_requests_total{code="400",handler="query",method="get",} 6 252 | http_requests_total{code="400",handler="series",method="get",} 6 253 | # HELP http_response_size_bytes The HTTP response sizes in bytes. 254 | # TYPE http_response_size_bytes summary 255 | http_response_size_bytes{handler="alerts",quantile="0.5"} NaN 256 | http_response_size_bytes{handler="alerts",quantile="0.9"} NaN 257 | http_response_size_bytes{handler="alerts",quantile="0.99"} NaN 258 | http_response_size_bytes_sum{handler="alerts"} 0 259 | http_response_size_bytes_count{handler="alerts"} 0 260 | http_response_size_bytes{handler="consoles",quantile="0.5"} NaN 261 | http_response_size_bytes{handler="consoles",quantile="0.9"} NaN 262 | http_response_size_bytes{handler="consoles",quantile="0.99"} NaN 263 | http_response_size_bytes_sum{handler="consoles"} 0 264 | http_response_size_bytes_count{handler="consoles"} 0 265 | http_response_size_bytes{handler="drop_series",quantile="0.5"} NaN 266 | http_response_size_bytes{handler="drop_series",quantile="0.9"} NaN 267 | http_response_size_bytes{handler="drop_series",quantile="0.99"} NaN 268 | http_response_size_bytes_sum{handler="drop_series"} 0 269 | http_response_size_bytes_count{handler="drop_series"} 0 270 | http_response_size_bytes{handler="federate",quantile="0.5"} NaN 271 | http_response_size_bytes{handler="federate",quantile="0.9"} NaN 272 | http_response_size_bytes{handler="federate",quantile="0.99"} NaN 273 | http_response_size_bytes_sum{handler="federate"} 0 274 | http_response_size_bytes_count{handler="federate"} 0 275 | http_response_size_bytes{handler="graph",quantile="0.5"} NaN 276 | http_response_size_bytes{handler="graph",quantile="0.9"} NaN 277 | http_response_size_bytes{handler="graph",quantile="0.99"} NaN 278 | http_response_size_bytes_sum{handler="graph"} 0 279 | http_response_size_bytes_count{handler="graph"} 0 280 | http_response_size_bytes{handler="heap",quantile="0.5"} NaN 281 | http_response_size_bytes{handler="heap",quantile="0.9"} NaN 282 | http_response_size_bytes{handler="heap",quantile="0.99"} NaN 283 | http_response_size_bytes_sum{handler="heap"} 0 284 | http_response_size_bytes_count{handler="heap"} 0 285 | http_response_size_bytes{handler="label_values",quantile="0.5"} NaN 286 | http_response_size_bytes{handler="label_values",quantile="0.9"} NaN 287 | http_response_size_bytes{handler="label_values",quantile="0.99"} NaN 288 | http_response_size_bytes_sum{handler="label_values"} 0 289 | http_response_size_bytes_count{handler="label_values"} 0 290 | http_response_size_bytes{handler="metrics",quantile="0.5"} NaN 291 | http_response_size_bytes{handler="metrics",quantile="0.9"} NaN 292 | http_response_size_bytes{handler="metrics",quantile="0.99"} NaN 293 | http_response_size_bytes_sum{handler="metrics"} 0 294 | http_response_size_bytes_count{handler="metrics"} 0 295 | http_response_size_bytes{handler="options",quantile="0.5"} NaN 296 | http_response_size_bytes{handler="options",quantile="0.9"} NaN 297 | http_response_size_bytes{handler="options",quantile="0.99"} NaN 298 | http_response_size_bytes_sum{handler="options"} 0 299 | http_response_size_bytes_count{handler="options"} 0 300 | http_response_size_bytes{handler="prometheus",quantile="0.5"} 3645 301 | http_response_size_bytes{handler="prometheus",quantile="0.9"} 3651 302 | http_response_size_bytes{handler="prometheus",quantile="0.99"} 3655 303 | http_response_size_bytes_sum{handler="prometheus"} 5.88076997e+08 304 | http_response_size_bytes_count{handler="prometheus"} 162030 305 | http_response_size_bytes{handler="query",quantile="0.5"} NaN 306 | http_response_size_bytes{handler="query",quantile="0.9"} NaN 307 | http_response_size_bytes{handler="query",quantile="0.99"} NaN 308 | http_response_size_bytes_sum{handler="query"} 9329 309 | http_response_size_bytes_count{handler="query"} 46 310 | http_response_size_bytes{handler="query_range",quantile="0.5"} NaN 311 | http_response_size_bytes{handler="query_range",quantile="0.9"} NaN 312 | http_response_size_bytes{handler="query_range",quantile="0.99"} NaN 313 | http_response_size_bytes_sum{handler="query_range"} 0 314 | http_response_size_bytes_count{handler="query_range"} 0 315 | http_response_size_bytes{handler="series",quantile="0.5"} NaN 316 | http_response_size_bytes{handler="series",quantile="0.9"} NaN 317 | http_response_size_bytes{handler="series",quantile="0.99"} NaN 318 | http_response_size_bytes_sum{handler="series"} 1372 319 | http_response_size_bytes_count{handler="series"} 10 320 | http_response_size_bytes{handler="static",quantile="0.5"} NaN 321 | http_response_size_bytes{handler="static",quantile="0.9"} NaN 322 | http_response_size_bytes{handler="static",quantile="0.99"} NaN 323 | http_response_size_bytes_sum{handler="static"} 0 324 | http_response_size_bytes_count{handler="static"} 0 325 | http_response_size_bytes{handler="status",quantile="0.5"} NaN 326 | http_response_size_bytes{handler="status",quantile="0.9"} NaN 327 | http_response_size_bytes{handler="status",quantile="0.99"} NaN 328 | http_response_size_bytes_sum{handler="status"} 0 329 | http_response_size_bytes_count{handler="status"} 0 330 | http_response_size_bytes{handler="version",quantile="0.5"} NaN 331 | http_response_size_bytes{handler="version",quantile="0.9"} NaN 332 | http_response_size_bytes{handler="version",quantile="0.99"} NaN 333 | http_response_size_bytes_sum{handler="version"} 0 334 | http_response_size_bytes_count{handler="version"} 0 335 | # HELP process_cpu_seconds_total Total user and system CPU time spent in seconds. 336 | # TYPE process_cpu_seconds_total counter 337 | process_cpu_seconds_total 5509.23 338 | # HELP process_max_fds Maximum number of open file descriptors. 339 | # TYPE process_max_fds gauge 340 | process_max_fds 1024 341 | # HELP process_open_fds Number of open file descriptors. 342 | # TYPE process_open_fds gauge 343 | process_open_fds 25 344 | # HELP process_resident_memory_bytes Resident memory size in bytes. 345 | # TYPE process_resident_memory_bytes gauge 346 | process_resident_memory_bytes 4.71502848e+08 347 | # HELP process_start_time_seconds Start time of the process since unix epoch in seconds. 348 | # TYPE process_start_time_seconds gauge 349 | process_start_time_seconds 1.45995424476e+09 350 | # HELP process_virtual_memory_bytes Virtual memory size in bytes. 351 | # TYPE process_virtual_memory_bytes gauge 352 | process_virtual_memory_bytes 8.74504192e+08 353 | # HELP prometheus_build_info A metric with a constant '1' value labeled by version, revision, and branch from which Prometheus was built. 354 | # TYPE prometheus_build_info gauge 355 | prometheus_build_info{branch="stable",revision="6e8d4e9",version="0.17.0"} 1 356 | # HELP prometheus_config_last_reload_success_timestamp_seconds Timestamp of the last successful configuration reload. 357 | # TYPE prometheus_config_last_reload_success_timestamp_seconds gauge 358 | prometheus_config_last_reload_success_timestamp_seconds 1.459954245e+09 359 | # HELP prometheus_config_last_reload_successful Whether the last configuration reload attempt was successful. 360 | # TYPE prometheus_config_last_reload_successful gauge 361 | prometheus_config_last_reload_successful 1 362 | # HELP prometheus_dns_sd_lookup_failures_total The number of DNS-SD lookup failures. 363 | # TYPE prometheus_dns_sd_lookup_failures_total counter 364 | prometheus_dns_sd_lookup_failures_total 0 365 | # HELP prometheus_dns_sd_lookups_total The number of DNS-SD lookups. 366 | # TYPE prometheus_dns_sd_lookups_total counter 367 | prometheus_dns_sd_lookups_total 0 368 | # HELP prometheus_evaluator_duration_seconds The duration of rule group evaluations. 369 | # TYPE prometheus_evaluator_duration_seconds summary 370 | prometheus_evaluator_duration_seconds{quantile="0.01"} 0.00304 371 | prometheus_evaluator_duration_seconds{quantile="0.05"} 0.00304 372 | prometheus_evaluator_duration_seconds{quantile="0.5"} 0.003469 373 | prometheus_evaluator_duration_seconds{quantile="0.9"} 0.006935 374 | prometheus_evaluator_duration_seconds{quantile="0.99"} 0.012124 375 | prometheus_evaluator_duration_seconds_sum 255.8617329999977 376 | prometheus_evaluator_duration_seconds_count 53964 377 | # HELP prometheus_evaluator_iterations_skipped_total The total number of rule group evaluations skipped due to throttled metric storage. 378 | # TYPE prometheus_evaluator_iterations_skipped_total counter 379 | prometheus_evaluator_iterations_skipped_total 0 380 | # HELP prometheus_local_storage_checkpoint_duration_milliseconds The duration (in milliseconds) it took to checkpoint in-memory metrics and head chunks. 381 | # TYPE prometheus_local_storage_checkpoint_duration_milliseconds gauge 382 | prometheus_local_storage_checkpoint_duration_milliseconds 923.41701 383 | # HELP prometheus_local_storage_chunk_ops_total The total number of chunk operations by their type. 384 | # TYPE prometheus_local_storage_chunk_ops_total counter 385 | prometheus_local_storage_chunk_ops_total{type="create"} 297812 386 | prometheus_local_storage_chunk_ops_total{type="persist"} 296725 387 | prometheus_local_storage_chunk_ops_total{type="pin"} 93 388 | prometheus_local_storage_chunk_ops_total{type="transcode"} 112769 389 | prometheus_local_storage_chunk_ops_total{type="unpin"} 93 390 | # HELP prometheus_local_storage_chunks_to_persist The current number of chunks waiting for persistence. 391 | # TYPE prometheus_local_storage_chunks_to_persist gauge 392 | prometheus_local_storage_chunks_to_persist 733 393 | # HELP prometheus_local_storage_fingerprint_mappings_total The total number of fingerprints being mapped to avoid collisions. 394 | # TYPE prometheus_local_storage_fingerprint_mappings_total counter 395 | prometheus_local_storage_fingerprint_mappings_total 0 396 | # HELP prometheus_local_storage_inconsistencies_total A counter incremented each time an inconsistency in the local storage is detected. If this is greater zero, restart the server as soon as possible. 397 | # TYPE prometheus_local_storage_inconsistencies_total counter 398 | prometheus_local_storage_inconsistencies_total 0 399 | # HELP prometheus_local_storage_indexing_batch_duration_milliseconds Quantiles for batch indexing duration in milliseconds. 400 | # TYPE prometheus_local_storage_indexing_batch_duration_milliseconds summary 401 | prometheus_local_storage_indexing_batch_duration_milliseconds{quantile="0.5"} NaN 402 | prometheus_local_storage_indexing_batch_duration_milliseconds{quantile="0.9"} NaN 403 | prometheus_local_storage_indexing_batch_duration_milliseconds{quantile="0.99"} NaN 404 | prometheus_local_storage_indexing_batch_duration_milliseconds_sum 110.092814 405 | prometheus_local_storage_indexing_batch_duration_milliseconds_count 8 406 | # HELP prometheus_local_storage_indexing_batch_sizes Quantiles for indexing batch sizes (number of metrics per batch). 407 | # TYPE prometheus_local_storage_indexing_batch_sizes summary 408 | prometheus_local_storage_indexing_batch_sizes{quantile="0.5"} NaN 409 | prometheus_local_storage_indexing_batch_sizes{quantile="0.9"} NaN 410 | prometheus_local_storage_indexing_batch_sizes{quantile="0.99"} NaN 411 | prometheus_local_storage_indexing_batch_sizes_sum 354 412 | prometheus_local_storage_indexing_batch_sizes_count 8 413 | # HELP prometheus_local_storage_indexing_queue_capacity The capacity of the indexing queue. 414 | # TYPE prometheus_local_storage_indexing_queue_capacity gauge 415 | prometheus_local_storage_indexing_queue_capacity 16384 416 | # HELP prometheus_local_storage_indexing_queue_length The number of metrics waiting to be indexed. 417 | # TYPE prometheus_local_storage_indexing_queue_length gauge 418 | prometheus_local_storage_indexing_queue_length 0 419 | # HELP prometheus_local_storage_ingested_samples_total The total number of samples ingested. 420 | # TYPE prometheus_local_storage_ingested_samples_total counter 421 | prometheus_local_storage_ingested_samples_total 5.720288e+07 422 | # HELP prometheus_local_storage_invalid_preload_requests_total The total number of preload requests referring to a non-existent series. This is an indication of outdated label indexes. 423 | # TYPE prometheus_local_storage_invalid_preload_requests_total counter 424 | prometheus_local_storage_invalid_preload_requests_total 0 425 | # HELP prometheus_local_storage_maintain_series_duration_milliseconds The duration (in milliseconds) it took to perform maintenance on a series. 426 | # TYPE prometheus_local_storage_maintain_series_duration_milliseconds summary 427 | prometheus_local_storage_maintain_series_duration_milliseconds{location="memory",quantile="0.5"} 50.467295 428 | prometheus_local_storage_maintain_series_duration_milliseconds{location="memory",quantile="0.9"} 63.920446 429 | prometheus_local_storage_maintain_series_duration_milliseconds{location="memory",quantile="0.99"} 73.236479 430 | prometheus_local_storage_maintain_series_duration_milliseconds_sum{location="memory"} 4.238477484786966e+06 431 | prometheus_local_storage_maintain_series_duration_milliseconds_count{location="memory"} 80608 432 | # HELP prometheus_local_storage_max_chunks_to_persist The maximum number of chunks that can be waiting for persistence before sample ingestion will stop. 433 | # TYPE prometheus_local_storage_max_chunks_to_persist gauge 434 | prometheus_local_storage_max_chunks_to_persist 524288 435 | # HELP prometheus_local_storage_memory_chunkdescs The current number of chunk descriptors in memory. 436 | # TYPE prometheus_local_storage_memory_chunkdescs gauge 437 | prometheus_local_storage_memory_chunkdescs 297812 438 | # HELP prometheus_local_storage_memory_chunks The current number of chunks in memory, excluding cloned chunks (i.e. chunks without a descriptor). 439 | # TYPE prometheus_local_storage_memory_chunks gauge 440 | prometheus_local_storage_memory_chunks 297812 441 | # HELP prometheus_local_storage_memory_series The current number of series in memory. 442 | # TYPE prometheus_local_storage_memory_series gauge 443 | prometheus_local_storage_memory_series 354 444 | # HELP prometheus_local_storage_out_of_order_samples_total The total number of samples that were discarded because their timestamps were at or before the last received sample for a series. 445 | # TYPE prometheus_local_storage_out_of_order_samples_total counter 446 | prometheus_local_storage_out_of_order_samples_total 0 447 | # HELP prometheus_local_storage_persist_errors_total The total number of errors while persisting chunks. 448 | # TYPE prometheus_local_storage_persist_errors_total counter 449 | prometheus_local_storage_persist_errors_total 0 450 | # HELP prometheus_local_storage_persistence_urgency_score A score of urgency to persist chunks, 0 is least urgent, 1 most. 451 | # TYPE prometheus_local_storage_persistence_urgency_score gauge 452 | prometheus_local_storage_persistence_urgency_score 0.0013980865478515625 453 | # HELP prometheus_local_storage_rushed_mode 1 if the storage is in rushed mode, 0 otherwise. In rushed mode, the system behaves as if the persistence_urgency_score is 1. 454 | # TYPE prometheus_local_storage_rushed_mode gauge 455 | prometheus_local_storage_rushed_mode 0 456 | # HELP prometheus_local_storage_series_ops_total The total number of series operations by their type. 457 | # TYPE prometheus_local_storage_series_ops_total counter 458 | prometheus_local_storage_series_ops_total{type="create"} 354 459 | prometheus_local_storage_series_ops_total{type="maintenance_in_memory"} 80608 460 | # HELP prometheus_notifications_dropped_total Total number of alerts dropped due to alert manager missing in configuration. 461 | # TYPE prometheus_notifications_dropped_total counter 462 | prometheus_notifications_dropped_total 0 463 | # HELP prometheus_notifications_errors_total Total number of errors sending alert notifications. 464 | # TYPE prometheus_notifications_errors_total counter 465 | prometheus_notifications_errors_total 0 466 | # HELP prometheus_notifications_latency_seconds Latency quantiles for sending alert notifications (not including dropped notifications). 467 | # TYPE prometheus_notifications_latency_seconds summary 468 | prometheus_notifications_latency_seconds{quantile="0.5"} NaN 469 | prometheus_notifications_latency_seconds{quantile="0.9"} NaN 470 | prometheus_notifications_latency_seconds{quantile="0.99"} NaN 471 | prometheus_notifications_latency_seconds_sum 0 472 | prometheus_notifications_latency_seconds_count 0 473 | # HELP prometheus_notifications_queue_capacity The capacity of the alert notifications queue. 474 | # TYPE prometheus_notifications_queue_capacity gauge 475 | prometheus_notifications_queue_capacity 10000 476 | # HELP prometheus_notifications_queue_length The number of alert notifications in the queue. 477 | # TYPE prometheus_notifications_queue_length gauge 478 | prometheus_notifications_queue_length 0 479 | # HELP prometheus_notifications_sent_total Total number of alerts successfully sent. 480 | # TYPE prometheus_notifications_sent_total counter 481 | prometheus_notifications_sent_total 0 482 | # HELP prometheus_rule_evaluation_failures_total The total number of rule evaluation failures. 483 | # TYPE prometheus_rule_evaluation_failures_total counter 484 | prometheus_rule_evaluation_failures_total{rule_type="alerting"} 0 485 | prometheus_rule_evaluation_failures_total{rule_type="recording"} 0 486 | # HELP prometheus_target_interval_length_seconds Actual intervals between scrapes. 487 | # TYPE prometheus_target_interval_length_seconds summary 488 | prometheus_target_interval_length_seconds{interval="5s",quantile="0.01"} 4.999070539 489 | prometheus_target_interval_length_seconds{interval="5s",quantile="0.05"} 4.999815767 490 | prometheus_target_interval_length_seconds{interval="5s",quantile="0.5"} 4.9999948 491 | prometheus_target_interval_length_seconds{interval="5s",quantile="0.9"} 5.000153491 492 | prometheus_target_interval_length_seconds{interval="5s",quantile="0.99"} 5.000213767 493 | prometheus_target_interval_length_seconds_sum{interval="5s"} 809454.9278622258 494 | prometheus_target_interval_length_seconds_count{interval="5s"} 161891 495 | # TESTING: A histogram, which has a pretty complex representation in the text format 496 | # HELP http_request_duration_seconds A histogram of the request duration. 497 | # TYPE http_request_duration_seconds histogram 498 | http_request_duration_seconds_bucket{le="0.05"} 24054 499 | http_request_duration_seconds_bucket{le="0.1"} 33444 500 | http_request_duration_seconds_bucket{le="0.2"} 100392 501 | http_request_duration_seconds_bucket{le="0.5"} 129389 502 | http_request_duration_seconds_bucket{le="1"} 133988 503 | http_request_duration_seconds_bucket{le="+Inf"} 144320 504 | http_request_duration_seconds_sum 53423 505 | http_request_duration_seconds_count 144320 506 | --------------------------------------------------------------------------------