diff --git a/integration_tests/example_application/src/main/java/io/prometheus/client/smoketest/Server.java b/integration_tests/example_application/src/main/java/io/prometheus/client/smoketest/Server.java
index 1a5f481f0..5630ac63b 100644
--- a/integration_tests/example_application/src/main/java/io/prometheus/client/smoketest/Server.java
+++ b/integration_tests/example_application/src/main/java/io/prometheus/client/smoketest/Server.java
@@ -1,5 +1,6 @@
package io.prometheus.client.smoketest;
+import io.prometheus.client.CollectorRegistry;
import io.prometheus.client.Counter;
import io.prometheus.client.exporter.HTTPServer;
import io.prometheus.client.hotspot.DefaultExports;
@@ -19,6 +20,7 @@ public static void main(String[] args) throws IOException, InterruptedException
.labelNames("path")
.register();
counter.labels("/hello-world").inc();
+ CollectorRegistry.defaultRegistry.metricFamilySamples();
new HTTPServer(9000);
Thread.currentThread().join(); // sleep forever
}
diff --git a/pom.xml b/pom.xml
index 2b4a05bde..3d0b571aa 100644
--- a/pom.xml
+++ b/pom.xml
@@ -62,6 +62,7 @@
simpleclient_bom
benchmark
integration_tests
+ simpleclient_nightingale_bridge
@@ -224,8 +225,8 @@
org.apache.maven.plugins
maven-compiler-plugin
- 1.6
- 1.6
+ 1.8
+ 1.8
diff --git a/simpleclient/src/main/java/io/prometheus/client/CollectorRegistry.java b/simpleclient/src/main/java/io/prometheus/client/CollectorRegistry.java
index 0d37cca27..98ddd5de8 100644
--- a/simpleclient/src/main/java/io/prometheus/client/CollectorRegistry.java
+++ b/simpleclient/src/main/java/io/prometheus/client/CollectorRegistry.java
@@ -31,6 +31,15 @@ public class CollectorRegistry {
private final Map> collectorsToNames = new HashMap>();
private final Map namesToCollectors = new HashMap();
+ public Map getGlobalTags() {
+ return globalTags;
+ }
+
+ public void setGlobalTags(Map globalTags) {
+ this.globalTags = globalTags;
+ }
+
+ private Map globalTags;
private final boolean autoDescribe;
public CollectorRegistry() {
diff --git a/simpleclient_common/src/main/java/io/prometheus/client/exporter/common/TextFormat.java b/simpleclient_common/src/main/java/io/prometheus/client/exporter/common/TextFormat.java
index db2de3a5b..d99a64187 100644
--- a/simpleclient_common/src/main/java/io/prometheus/client/exporter/common/TextFormat.java
+++ b/simpleclient_common/src/main/java/io/prometheus/client/exporter/common/TextFormat.java
@@ -12,292 +12,382 @@
import io.prometheus.client.Collector;
public class TextFormat {
- /**
- * Content-type for Prometheus text version 0.0.4.
- */
- public final static String CONTENT_TYPE_004 = "text/plain; version=0.0.4; charset=utf-8";
+ /**
+ * Content-type for Prometheus text version 0.0.4.
+ */
+ public final static String CONTENT_TYPE_004 = "text/plain; version=0.0.4; charset=utf-8";
- /**
- * Content-type for Openmetrics text version 1.0.0.
- *
- * @since 0.10.0
- */
- public final static String CONTENT_TYPE_OPENMETRICS_100 = "application/openmetrics-text; version=1.0.0; charset=utf-8";
+ /**
+ * Content-type for Openmetrics text version 1.0.0.
+ *
+ * @since 0.10.0
+ */
+ public final static String CONTENT_TYPE_OPENMETRICS_100 = "application/openmetrics-text; version=1.0.0; charset=utf-8";
- /**
- * Return the content type that should be used for a given Accept HTTP header.
- *
- * @since 0.10.0
- */
- public static String chooseContentType(String acceptHeader) {
- if (acceptHeader == null) {
- return CONTENT_TYPE_004;
- }
+ /**
+ * Return the content type that should be used for a given Accept HTTP header.
+ *
+ * @since 0.10.0
+ */
+ public static String chooseContentType(String acceptHeader) {
+ if (acceptHeader == null) {
+ return CONTENT_TYPE_004;
+ }
- for (String accepts : acceptHeader.split(",")) {
- if ("application/openmetrics-text".equals(accepts.split(";")[0].trim())) {
- return CONTENT_TYPE_OPENMETRICS_100;
- }
- }
+ for (String accepts : acceptHeader.split(",")) {
+ if ("application/openmetrics-text".equals(accepts.split(";")[0].trim())) {
+ return CONTENT_TYPE_OPENMETRICS_100;
+ }
+ }
- return CONTENT_TYPE_004;
- }
+ return CONTENT_TYPE_004;
+ }
- /**
- * Write out the given MetricFamilySamples in a format per the contentType.
- *
- * @since 0.10.0
- */
- public static void writeFormat(String contentType, Writer writer, Enumeration mfs) throws IOException {
- if (CONTENT_TYPE_004.equals(contentType)) {
- write004(writer, mfs);
- return;
+ /**
+ * Write out the given MetricFamilySamples in a format per the contentType.
+ *
+ * @since 0.10.0
+ */
+ public static void writeFormat(String contentType, Writer writer, Enumeration mfs) throws IOException {
+ if (CONTENT_TYPE_004.equals(contentType)) {
+ write004(writer, mfs);
+ return;
+ }
+ if (CONTENT_TYPE_OPENMETRICS_100.equals(contentType)) {
+ writeOpenMetrics100(writer, mfs);
+ return;
+ }
+ throw new IllegalArgumentException("Unknown contentType " + contentType);
}
- if (CONTENT_TYPE_OPENMETRICS_100.equals(contentType)) {
- writeOpenMetrics100(writer, mfs);
- return;
+
+ public static void writeFormat(String contentType, Writer writer, Enumeration mfs, String globalTagsString) throws IOException {
+ if (CONTENT_TYPE_004.equals(contentType)) {
+ write004(writer, mfs, globalTagsString);
+ return;
+ }
+ if (CONTENT_TYPE_OPENMETRICS_100.equals(contentType)) {
+ writeOpenMetrics100(writer, mfs);
+ return;
+ }
+ throw new IllegalArgumentException("Unknown contentType " + contentType);
}
- throw new IllegalArgumentException("Unknown contentType " + contentType);
- }
- /**
- * Write out the text version 0.0.4 of the given MetricFamilySamples.
- */
- public static void write004(Writer writer, Enumeration mfs) throws IOException {
- Map omFamilies = new TreeMap();
- /* See http://prometheus.io/docs/instrumenting/exposition_formats/
- * for the output format specification. */
- while(mfs.hasMoreElements()) {
- Collector.MetricFamilySamples metricFamilySamples = mfs.nextElement();
- String name = metricFamilySamples.name;
- writer.write("# HELP ");
- writer.write(name);
- if (metricFamilySamples.type == Collector.Type.COUNTER) {
- writer.write("_total");
- }
- if (metricFamilySamples.type == Collector.Type.INFO) {
- writer.write("_info");
- }
- writer.write(' ');
- writeEscapedHelp(writer, metricFamilySamples.help);
- writer.write('\n');
+ /**
+ * Write out the text version 0.0.4 of the given MetricFamilySamples.
+ */
+ public static void write004(Writer writer, Enumeration mfs) throws IOException {
+ Map omFamilies = new TreeMap();
+ /* See http://prometheus.io/docs/instrumenting/exposition_formats/
+ * for the output format specification. */
+ while (mfs.hasMoreElements()) {
+ Collector.MetricFamilySamples metricFamilySamples = mfs.nextElement();
+ String name = metricFamilySamples.name;
+ writer.write("# HELP ");
+ writer.write(name);
+ if (metricFamilySamples.type == Collector.Type.COUNTER) {
+ writer.write("_total");
+ }
+ if (metricFamilySamples.type == Collector.Type.INFO) {
+ writer.write("_info");
+ }
+ writer.write(' ');
+ writeEscapedHelp(writer, metricFamilySamples.help);
+ writer.write('\n');
- writer.write("# TYPE ");
- writer.write(name);
- if (metricFamilySamples.type == Collector.Type.COUNTER) {
- writer.write("_total");
- }
- if (metricFamilySamples.type == Collector.Type.INFO) {
- writer.write("_info");
- }
- writer.write(' ');
- writer.write(typeString(metricFamilySamples.type));
- writer.write('\n');
+ writer.write("# TYPE ");
+ writer.write(name);
+ if (metricFamilySamples.type == Collector.Type.COUNTER) {
+ writer.write("_total");
+ }
+ if (metricFamilySamples.type == Collector.Type.INFO) {
+ writer.write("_info");
+ }
+ writer.write(' ');
+ writer.write(typeString(metricFamilySamples.type));
+ writer.write('\n');
- String createdName = name + "_created";
- String gcountName = name + "_gcount";
- String gsumName = name + "_gsum";
- for (Collector.MetricFamilySamples.Sample sample: metricFamilySamples.samples) {
- /* OpenMetrics specific sample, put in a gauge at the end. */
- if (sample.name.equals(createdName)
- || sample.name.equals(gcountName)
- || sample.name.equals(gsumName)) {
- Collector.MetricFamilySamples omFamily = omFamilies.get(sample.name);
- if (omFamily == null) {
- omFamily = new Collector.MetricFamilySamples(sample.name, Collector.Type.GAUGE, metricFamilySamples.help, new ArrayList());
- omFamilies.put(sample.name, omFamily);
- }
- omFamily.samples.add(sample);
- continue;
- }
- writer.write(sample.name);
- if (sample.labelNames.size() > 0) {
- writer.write('{');
- for (int i = 0; i < sample.labelNames.size(); ++i) {
- writer.write(sample.labelNames.get(i));
- writer.write("=\"");
- writeEscapedLabelValue(writer, sample.labelValues.get(i));
- writer.write("\",");
- }
- writer.write('}');
+ String createdName = name + "_created";
+ String gcountName = name + "_gcount";
+ String gsumName = name + "_gsum";
+ for (Collector.MetricFamilySamples.Sample sample : metricFamilySamples.samples) {
+ /* OpenMetrics specific sample, put in a gauge at the end. */
+ if (sample.name.equals(createdName)
+ || sample.name.equals(gcountName)
+ || sample.name.equals(gsumName)) {
+ Collector.MetricFamilySamples omFamily = omFamilies.get(sample.name);
+ if (omFamily == null) {
+ omFamily = new Collector.MetricFamilySamples(sample.name, Collector.Type.GAUGE, metricFamilySamples.help, new ArrayList());
+ omFamilies.put(sample.name, omFamily);
+ }
+ omFamily.samples.add(sample);
+ continue;
+ }
+ writer.write(sample.name);
+ if (sample.labelNames.size() > 0) {
+ writer.write('{');
+ for (int i = 0; i < sample.labelNames.size(); ++i) {
+ writer.write(sample.labelNames.get(i));
+ writer.write("=\"");
+ writeEscapedLabelValue(writer, sample.labelValues.get(i));
+ writer.write("\",");
+ }
+ writer.write('}');
+ }
+ writer.write(' ');
+ writer.write(Collector.doubleToGoString(sample.value));
+ if (sample.timestampMs != null) {
+ writer.write(' ');
+ writer.write(sample.timestampMs.toString());
+ }
+ writer.write('\n');
+ }
}
- writer.write(' ');
- writer.write(Collector.doubleToGoString(sample.value));
- if (sample.timestampMs != null){
- writer.write(' ');
- writer.write(sample.timestampMs.toString());
+ // Write out any OM-specific samples.
+ if (!omFamilies.isEmpty()) {
+ write004(writer, Collections.enumeration(omFamilies.values()));
}
- writer.write('\n');
- }
- }
- // Write out any OM-specific samples.
- if (!omFamilies.isEmpty()) {
- write004(writer, Collections.enumeration(omFamilies.values()));
}
- }
- private static void writeEscapedHelp(Writer writer, String s) throws IOException {
- for (int i = 0; i < s.length(); i++) {
- char c = s.charAt(i);
- switch (c) {
- case '\\':
- writer.append("\\\\");
- break;
- case '\n':
- writer.append("\\n");
- break;
- default:
- writer.append(c);
- }
+ public static void write004(Writer writer, Enumeration mfs, String tags) throws IOException {
+ Map omFamilies = new TreeMap();
+ /* See http://prometheus.io/docs/instrumenting/exposition_formats/
+ * for the output format specification. */
+ while (mfs.hasMoreElements()) {
+ Collector.MetricFamilySamples metricFamilySamples = mfs.nextElement();
+ String name = metricFamilySamples.name;
+ writer.write("# HELP ");
+ writer.write(name);
+ if (metricFamilySamples.type == Collector.Type.COUNTER) {
+ writer.write("_total");
+ }
+ if (metricFamilySamples.type == Collector.Type.INFO) {
+ writer.write("_info");
+ }
+ writer.write(' ');
+ writeEscapedHelp(writer, metricFamilySamples.help);
+ writer.write('\n');
+
+ writer.write("# TYPE ");
+ writer.write(name);
+ if (metricFamilySamples.type == Collector.Type.COUNTER) {
+ writer.write("_total");
+ }
+ if (metricFamilySamples.type == Collector.Type.INFO) {
+ writer.write("_info");
+ }
+ writer.write(' ');
+ writer.write(typeString(metricFamilySamples.type));
+ writer.write('\n');
+
+ String createdName = name + "_created";
+ String gcountName = name + "_gcount";
+ String gsumName = name + "_gsum";
+ for (Collector.MetricFamilySamples.Sample sample : metricFamilySamples.samples) {
+ /* OpenMetrics specific sample, put in a gauge at the end. */
+ if (sample.name.equals(createdName)
+ || sample.name.equals(gcountName)
+ || sample.name.equals(gsumName)) {
+ Collector.MetricFamilySamples omFamily = omFamilies.get(sample.name);
+ if (omFamily == null) {
+ omFamily = new Collector.MetricFamilySamples(sample.name, Collector.Type.GAUGE, metricFamilySamples.help, new ArrayList());
+ omFamilies.put(sample.name, omFamily);
+ }
+ omFamily.samples.add(sample);
+ continue;
+ }
+ writer.write(sample.name);
+ if (sample.labelNames.size() > 0) {
+ writer.write('{');
+ writer.write("");
+ if (tags != null) {
+ writer.write(tags);
+ writer.write(',');
+ }
+ for (int i = 0; i < sample.labelNames.size(); ++i) {
+ writer.write(sample.labelNames.get(i));
+ writer.write("=\"");
+ writeEscapedLabelValue(writer, sample.labelValues.get(i));
+ writer.write("\",");
+ }
+ writer.write('}');
+ }
+ writer.write(' ');
+ writer.write(Collector.doubleToGoString(sample.value));
+ if (sample.timestampMs != null) {
+ writer.write(' ');
+ writer.write(sample.timestampMs.toString());
+ }
+ writer.write('\n');
+ }
+ }
+ // Write out any OM-specific samples.
+ if (!omFamilies.isEmpty()) {
+ write004(writer, Collections.enumeration(omFamilies.values()));
+ }
}
- }
- private static void writeEscapedLabelValue(Writer writer, String s) throws IOException {
- for (int i = 0; i < s.length(); i++) {
- char c = s.charAt(i);
- switch (c) {
- case '\\':
- writer.append("\\\\");
- break;
- case '\"':
- writer.append("\\\"");
- break;
- case '\n':
- writer.append("\\n");
- break;
- default:
- writer.append(c);
- }
+ private static void writeEscapedHelp(Writer writer, String s) throws IOException {
+ for (int i = 0; i < s.length(); i++) {
+ char c = s.charAt(i);
+ switch (c) {
+ case '\\':
+ writer.append("\\\\");
+ break;
+ case '\n':
+ writer.append("\\n");
+ break;
+ default:
+ writer.append(c);
+ }
+ }
}
- }
- private static String typeString(Collector.Type t) {
- switch (t) {
- case GAUGE:
- return "gauge";
- case COUNTER:
- return "counter";
- case SUMMARY:
- return "summary";
- case HISTOGRAM:
- return "histogram";
- case GAUGE_HISTOGRAM:
- return "histogram";
- case STATE_SET:
- return "gauge";
- case INFO:
- return "gauge";
- default:
- return "untyped";
+ private static void writeEscapedLabelValue(Writer writer, String s) throws IOException {
+ for (int i = 0; i < s.length(); i++) {
+ char c = s.charAt(i);
+ switch (c) {
+ case '\\':
+ writer.append("\\\\");
+ break;
+ case '\"':
+ writer.append("\\\"");
+ break;
+ case '\n':
+ writer.append("\\n");
+ break;
+ default:
+ writer.append(c);
+ }
+ }
}
- }
- /**
- * Write out the OpenMetrics text version 1.0.0 of the given MetricFamilySamples.
- *
- * @since 0.10.0
- */
- public static void writeOpenMetrics100(Writer writer, Enumeration mfs) throws IOException {
- while(mfs.hasMoreElements()) {
- Collector.MetricFamilySamples metricFamilySamples = mfs.nextElement();
- String name = metricFamilySamples.name;
+ private static String typeString(Collector.Type t) {
+ switch (t) {
+ case GAUGE:
+ return "gauge";
+ case COUNTER:
+ return "counter";
+ case SUMMARY:
+ return "summary";
+ case HISTOGRAM:
+ return "histogram";
+ case GAUGE_HISTOGRAM:
+ return "histogram";
+ case STATE_SET:
+ return "gauge";
+ case INFO:
+ return "gauge";
+ default:
+ return "untyped";
+ }
+ }
- writer.write("# TYPE ");
- writer.write(name);
- writer.write(' ');
- writer.write(omTypeString(metricFamilySamples.type));
- writer.write('\n');
+ /**
+ * Write out the OpenMetrics text version 1.0.0 of the given MetricFamilySamples.
+ *
+ * @since 0.10.0
+ */
+ public static void writeOpenMetrics100(Writer writer, Enumeration mfs) throws IOException {
+ while (mfs.hasMoreElements()) {
+ Collector.MetricFamilySamples metricFamilySamples = mfs.nextElement();
+ String name = metricFamilySamples.name;
- if (!metricFamilySamples.unit.isEmpty()) {
- writer.write("# UNIT ");
- writer.write(name);
- writer.write(' ');
- writer.write(metricFamilySamples.unit);
- writer.write('\n');
- }
+ writer.write("# TYPE ");
+ writer.write(name);
+ writer.write(' ');
+ writer.write(omTypeString(metricFamilySamples.type));
+ writer.write('\n');
- writer.write("# HELP ");
- writer.write(name);
- writer.write(' ');
- writeEscapedLabelValue(writer, metricFamilySamples.help);
- writer.write('\n');
-
- for (Collector.MetricFamilySamples.Sample sample: metricFamilySamples.samples) {
- writer.write(sample.name);
- if (sample.labelNames.size() > 0) {
- writer.write('{');
- for (int i = 0; i < sample.labelNames.size(); ++i) {
- if (i > 0) {
- writer.write(",");
- }
- writer.write(sample.labelNames.get(i));
- writer.write("=\"");
- writeEscapedLabelValue(writer, sample.labelValues.get(i));
- writer.write("\"");
- }
- writer.write('}');
- }
- writer.write(' ');
- writer.write(Collector.doubleToGoString(sample.value));
- if (sample.timestampMs != null){
- writer.write(' ');
- omWriteTimestamp(writer, sample.timestampMs);
- }
- if (sample.exemplar != null) {
- writer.write(" # {");
- for (int i=0; i 0) {
- writer.write(",");
+ if (!metricFamilySamples.unit.isEmpty()) {
+ writer.write("# UNIT ");
+ writer.write(name);
+ writer.write(' ');
+ writer.write(metricFamilySamples.unit);
+ writer.write('\n');
}
- writer.write(sample.exemplar.getLabelName(i));
- writer.write("=\"");
- writeEscapedLabelValue(writer, sample.exemplar.getLabelValue(i));
- writer.write("\"");
- }
- writer.write("} ");
- writer.write(Collector.doubleToGoString(sample.exemplar.getValue()));
- if (sample.exemplar.getTimestampMs() != null) {
+
+ writer.write("# HELP ");
+ writer.write(name);
writer.write(' ');
- omWriteTimestamp(writer, sample.exemplar.getTimestampMs());
- }
+ writeEscapedLabelValue(writer, metricFamilySamples.help);
+ writer.write('\n');
+
+ for (Collector.MetricFamilySamples.Sample sample : metricFamilySamples.samples) {
+ writer.write(sample.name);
+ if (sample.labelNames.size() > 0) {
+ writer.write('{');
+ for (int i = 0; i < sample.labelNames.size(); ++i) {
+ if (i > 0) {
+ writer.write(",");
+ }
+ writer.write(sample.labelNames.get(i));
+ writer.write("=\"");
+ writeEscapedLabelValue(writer, sample.labelValues.get(i));
+ writer.write("\"");
+ }
+ writer.write('}');
+ }
+ writer.write(' ');
+ writer.write(Collector.doubleToGoString(sample.value));
+ if (sample.timestampMs != null) {
+ writer.write(' ');
+ omWriteTimestamp(writer, sample.timestampMs);
+ }
+ if (sample.exemplar != null) {
+ writer.write(" # {");
+ for (int i = 0; i < sample.exemplar.getNumberOfLabels(); i++) {
+ if (i > 0) {
+ writer.write(",");
+ }
+ writer.write(sample.exemplar.getLabelName(i));
+ writer.write("=\"");
+ writeEscapedLabelValue(writer, sample.exemplar.getLabelValue(i));
+ writer.write("\"");
+ }
+ writer.write("} ");
+ writer.write(Collector.doubleToGoString(sample.exemplar.getValue()));
+ if (sample.exemplar.getTimestampMs() != null) {
+ writer.write(' ');
+ omWriteTimestamp(writer, sample.exemplar.getTimestampMs());
+ }
+ }
+ writer.write('\n');
+ }
}
- writer.write('\n');
- }
+ writer.write("# EOF\n");
}
- writer.write("# EOF\n");
- }
- static void omWriteTimestamp(Writer writer, long timestampMs) throws IOException {
- writer.write(Long.toString(timestampMs / 1000L));
- writer.write(".");
- long ms = timestampMs % 1000;
- if (ms < 100) {
- writer.write("0");
- }
- if (ms < 10) {
- writer.write("0");
+ static void omWriteTimestamp(Writer writer, long timestampMs) throws IOException {
+ writer.write(Long.toString(timestampMs / 1000L));
+ writer.write(".");
+ long ms = timestampMs % 1000;
+ if (ms < 100) {
+ writer.write("0");
+ }
+ if (ms < 10) {
+ writer.write("0");
+ }
+ writer.write(Long.toString(timestampMs % 1000));
}
- writer.write(Long.toString(timestampMs % 1000));
- }
- private static String omTypeString(Collector.Type t) {
- switch (t) {
- case GAUGE:
- return "gauge";
- case COUNTER:
- return "counter";
- case SUMMARY:
- return "summary";
- case HISTOGRAM:
- return "histogram";
- case GAUGE_HISTOGRAM:
- return "gaugehistogram";
- case STATE_SET:
- return "stateset";
- case INFO:
- return "info";
- default:
- return "unknown";
+ private static String omTypeString(Collector.Type t) {
+ switch (t) {
+ case GAUGE:
+ return "gauge";
+ case COUNTER:
+ return "counter";
+ case SUMMARY:
+ return "summary";
+ case HISTOGRAM:
+ return "histogram";
+ case GAUGE_HISTOGRAM:
+ return "gaugehistogram";
+ case STATE_SET:
+ return "stateset";
+ case INFO:
+ return "info";
+ default:
+ return "unknown";
+ }
}
- }
}
diff --git a/simpleclient_graphite_bridge/src/main/java/io/prometheus/client/bridge/Graphite.java b/simpleclient_graphite_bridge/src/main/java/io/prometheus/client/sdk/Graphite.java
similarity index 98%
rename from simpleclient_graphite_bridge/src/main/java/io/prometheus/client/bridge/Graphite.java
rename to simpleclient_graphite_bridge/src/main/java/io/prometheus/client/sdk/Graphite.java
index 5e292800c..120fa416f 100644
--- a/simpleclient_graphite_bridge/src/main/java/io/prometheus/client/bridge/Graphite.java
+++ b/simpleclient_graphite_bridge/src/main/java/io/prometheus/client/sdk/Graphite.java
@@ -1,4 +1,4 @@
-package io.prometheus.client.bridge;
+package io.prometheus.client.sdk;
import io.prometheus.client.Collector;
import io.prometheus.client.CollectorRegistry;
diff --git a/simpleclient_graphite_bridge/src/test/java/io/prometheus/client/bridge/GraphiteTest.java b/simpleclient_graphite_bridge/src/test/java/io/prometheus/client/sdk/GraphiteTest.java
similarity index 95%
rename from simpleclient_graphite_bridge/src/test/java/io/prometheus/client/bridge/GraphiteTest.java
rename to simpleclient_graphite_bridge/src/test/java/io/prometheus/client/sdk/GraphiteTest.java
index dbe6099ef..4a2fc3246 100644
--- a/simpleclient_graphite_bridge/src/test/java/io/prometheus/client/bridge/GraphiteTest.java
+++ b/simpleclient_graphite_bridge/src/test/java/io/prometheus/client/sdk/GraphiteTest.java
@@ -1,4 +1,4 @@
-package io.prometheus.client.bridge;
+package io.prometheus.client.sdk;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.fail;
@@ -9,7 +9,6 @@
import io.prometheus.client.Gauge;
import java.io.BufferedReader;
-import java.io.IOException;
import java.io.InputStreamReader;
import java.net.ServerSocket;
import java.net.Socket;
diff --git a/simpleclient_hotspot/src/main/java/io/prometheus/client/hotspot/DefaultExports.java b/simpleclient_hotspot/src/main/java/io/prometheus/client/hotspot/DefaultExports.java
index 79cd6ed5e..737978c6d 100644
--- a/simpleclient_hotspot/src/main/java/io/prometheus/client/hotspot/DefaultExports.java
+++ b/simpleclient_hotspot/src/main/java/io/prometheus/client/hotspot/DefaultExports.java
@@ -30,6 +30,8 @@ public static synchronized void initialize() {
}
}
+
+
/**
* Register the default Hotspot collectors with the given registry.
*/
diff --git a/simpleclient_httpserver/src/main/java/io/prometheus/client/exporter/HTTPServer.java b/simpleclient_httpserver/src/main/java/io/prometheus/client/exporter/HTTPServer.java
index d6afdcd69..a08775b49 100644
--- a/simpleclient_httpserver/src/main/java/io/prometheus/client/exporter/HTTPServer.java
+++ b/simpleclient_httpserver/src/main/java/io/prometheus/client/exporter/HTTPServer.java
@@ -12,6 +12,7 @@
import java.nio.charset.Charset;
import java.util.HashSet;
import java.util.List;
+import java.util.Map;
import java.util.Set;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
@@ -19,6 +20,7 @@
import java.util.concurrent.FutureTask;
import java.util.concurrent.ThreadFactory;
import java.util.concurrent.atomic.AtomicInteger;
+import java.util.stream.Collectors;
import java.util.zip.GZIPOutputStream;
import com.sun.net.httpserver.HttpExchange;
@@ -34,7 +36,7 @@
* HTTPServer server = new HTTPServer(1234);
* }
*
- * */
+ */
public class HTTPServer {
static {
@@ -49,8 +51,7 @@ public class HTTPServer {
private static class LocalByteArray extends ThreadLocal {
@Override
- protected ByteArrayOutputStream initialValue()
- {
+ protected ByteArrayOutputStream initialValue() {
return new ByteArrayOutputStream(1 << 20);
}
}
@@ -62,9 +63,16 @@ public static class HTTPMetricHandler implements HttpHandler {
private final CollectorRegistry registry;
private final LocalByteArray response = new LocalByteArray();
private final static String HEALTHY_RESPONSE = "Exporter is Healthy.";
+ private String tagsString;
HTTPMetricHandler(CollectorRegistry registry) {
- this.registry = registry;
+ this.registry = registry;
+ Map tags = registry.getGlobalTags();
+ if (tags != null && tags.size() > 0) {
+ tagsString = tags.entrySet().stream().map((e) ->
+ e.getKey() + "=\"" + e.getValue()+"\""
+ ).collect(Collectors.joining(","));
+ }
}
@Override
@@ -81,7 +89,7 @@ public void handle(HttpExchange t) throws IOException {
String contentType = TextFormat.chooseContentType(t.getRequestHeaders().getFirst("Accept"));
t.getResponseHeaders().set("Content-Type", contentType);
TextFormat.writeFormat(contentType, osw,
- registry.filteredMetricFamilySamples(parseQuery(query)));
+ registry.filteredMetricFamilySamples(parseQuery(query)),tagsString);
}
osw.close();
diff --git a/simpleclient_sdk/pom.xml b/simpleclient_sdk/pom.xml
new file mode 100644
index 000000000..2362298ad
--- /dev/null
+++ b/simpleclient_sdk/pom.xml
@@ -0,0 +1,68 @@
+
+
+ 4.0.0
+
+
+ io.prometheus
+ parent
+ 0.11.1-SNAPSHOT
+
+
+ io.prometheus
+ simpleclient_nightingale_bridge
+ bundle
+
+ Prometheus Java Simpleclient Nightingale Bridge
+
+ Nightingale bridge for the simpleclient.
+
+
+
+
+ The Apache Software License, Version 2.0
+ http://www.apache.org/licenses/LICENSE-2.0.txt
+ repo
+
+
+
+
+
+ brian-brazil
+ Brian Brazil
+ brian.brazil@robustperception.io
+
+
+
+
+
+
+ io.prometheus
+ simpleclient
+ 0.11.1-SNAPSHOT
+
+
+ io.prometheus
+ simpleclient_httpserver
+ 0.11.1-SNAPSHOT
+
+
+
+ com.alibaba
+ fastjson
+ 1.2.76
+
+
+
+ org.apache.httpcomponents
+ httpclient
+ 4.5.13
+
+
+
+ junit
+ junit
+ 4.13.2
+ test
+
+
+
diff --git a/simpleclient_sdk/src/main/java/io/prometheus/client/sdk/Encoder.java b/simpleclient_sdk/src/main/java/io/prometheus/client/sdk/Encoder.java
new file mode 100644
index 000000000..5fc105ee7
--- /dev/null
+++ b/simpleclient_sdk/src/main/java/io/prometheus/client/sdk/Encoder.java
@@ -0,0 +1,147 @@
+package io.prometheus.client.sdk;
+
+import io.prometheus.client.Collector;
+import org.apache.http.HttpEntity;
+import org.apache.http.HttpResponse;
+import org.apache.http.client.HttpClient;
+import org.apache.http.client.methods.HttpPost;
+import org.apache.http.entity.ContentType;
+import org.apache.http.entity.StringEntity;
+import org.apache.http.impl.client.HttpClients;
+import org.apache.http.util.EntityUtils;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.ArrayList;
+import java.util.Map;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+import java.util.stream.Collectors;
+
+import static io.prometheus.client.sdk.StringEscapeUtils.escapeJson;
+
+public class Encoder {
+ private final HttpClient client;
+ private ArrayList samples;
+ private static final Logger logger = Logger.getLogger(Encoder.class.getName());
+ private final String url;
+ private final int batchSize;
+ private final long stepS;
+ private final String nid;
+ private StringBuilder builder = new StringBuilder();
+ private final Map tags;
+ private String tagStr;
+
+ Encoder(String url, int batchSize, long stepS, String nid, Map tags) {
+ this.stepS = stepS;
+ this.nid = nid;
+ this.url = url;
+ this.client = HttpClients.createDefault();
+ this.batchSize = batchSize;
+ this.samples = new ArrayList<>(batchSize);
+ this.tags = tags;
+ if (this.tags!=null){
+ this.tagStr = this.tags.entrySet().stream().map((e) -> {
+ return escapeJson(e.getKey()) + "=" + escapeJson(e.getValue());
+ }).collect(Collectors.joining(","));
+ }
+
+
+
+ }
+
+
+ void add(Collector.MetricFamilySamples.Sample sample) {
+ this.samples.add(sample);
+ }
+
+
+ protected Long generateTimestamp() {
+ return System.currentTimeMillis() / 1000;
+ }
+
+ void writeMessage(StringBuilder sb, Collector.MetricFamilySamples.Sample sample,Long timestamp) {
+ if (sample.labelNames.size() != sample.labelValues.size()) {
+ return;
+ }
+ String type = "GAUGE";
+ String name = sample.name;
+ sb.append("{\"").append("timestamp").append("\":").append(timestamp)
+ .append(",\"metric\":\"").append(escapeJson(name)).append('"')
+ .append(",\"counterType\":\"").append(type).append('"')
+ .append(",\"step\":").append(stepS);
+ sb.append(",\"nid\":\"").append(nid).append('"');
+ writeTags(sb, sample);
+ sb.append(",\"value\":");
+ sb.append(sample.value);
+ sb.append("}");
+ }
+
+ void writeTags(StringBuilder sb, Collector.MetricFamilySamples.Sample sample) {
+ if (sample.labelNames.size() == 0) {
+ return;
+ }
+ sb.append(",\"tags\":\"");
+ sb.append(this.tagStr);
+ for (int i = 0; i < sample.labelNames.size(); ++i) {
+ sb.append(',').append(escapeJson(sample.labelNames.get(i))).append("=")
+ .append(escapeJson(sample.labelValues.get(i).replace(" ", "-")));
+ }
+ sb.append('"');
+ }
+
+ void end() {
+ send();
+ }
+
+ String marshalBatch(Long timestamp) {
+
+ builder.append("[");
+ int len = this.samples.size();
+ for (int i = 0; i < len; i++) {
+ if (i != 0) {
+ builder.append(",\n");
+ }
+ writeMessage(builder, this.samples.get(i),timestamp);
+ }
+ builder.append("]");
+ String body = builder.toString();
+ builder.setLength(0);
+ this.samples.clear();
+ return body;
+ }
+
+ private void send() {
+ Long timestamp = generateTimestamp();
+ if (this.samples.size() > 0) {
+ String body = marshalBatch(timestamp);
+ HttpPost post = new HttpPost(this.url);
+ StringEntity requestEntity = new StringEntity(
+ body, ContentType.APPLICATION_JSON);
+ post.setEntity(requestEntity);
+ try {
+ HttpResponse response = client.execute(post);
+ final HttpEntity entity = response.getEntity();
+ String resp = EntityUtils.toString(entity);
+ if (!resp.startsWith("{\"dat\":\"ok\"")) {
+ logger.log(Level.WARNING, "nightingale get response" + resp + " from " + this.url);
+ }
+ if (entity != null) {
+ try (final InputStream inStream = entity.getContent()) {
+ //inStream.read();
+
+ // do something useful with the response
+ } catch (final IOException ex) {
+ logger.log(Level.WARNING, "Exception " + ex + " get response from " + this.url, ex);
+
+ // In case of an IOException the connection will be released
+ // back to the connection manager automatically
+ }
+ }
+ } catch (Exception e) {
+ logger.log(Level.WARNING, "Exception " + e + " pushing to " + this.url, e);
+ }
+
+ }
+ }
+}
diff --git a/simpleclient_sdk/src/main/java/io/prometheus/client/sdk/Metrics.java b/simpleclient_sdk/src/main/java/io/prometheus/client/sdk/Metrics.java
new file mode 100644
index 000000000..5dfbbf67d
--- /dev/null
+++ b/simpleclient_sdk/src/main/java/io/prometheus/client/sdk/Metrics.java
@@ -0,0 +1,60 @@
+package io.prometheus.client.sdk;
+
+import io.prometheus.client.CollectorRegistry;
+import io.prometheus.client.exporter.HTTPServer;
+
+import java.io.IOException;
+import java.lang.management.ManagementFactory;
+import java.net.InetSocketAddress;
+import java.util.HashMap;
+import java.util.Map;
+
+public class Metrics {
+ private CollectorRegistry registry;
+ private HashMap tags;
+
+ public Metrics(String bu, String project, String app, Map globalTags) {
+ this(bu, project, app, globalTags, CollectorRegistry.defaultRegistry);
+
+ }
+
+ public Metrics(String bu, String project, String app) {
+ this(bu, project, app, null);
+
+ }
+
+ public Metrics(String bu, String project, String app, Map globalTags, CollectorRegistry registry) {
+ this.registry = registry;
+ this.tags = new HashMap<>();
+ if (globalTags != null) {
+ globalTags.forEach((k, v) -> {
+ this.tags.put(k, v);
+ });
+ }
+ this.tags.put("bu", bu);
+ this.tags.put("project", project);
+ this.tags.put("app", app);
+ String podName = System.getenv("MY_POD_NAME");
+ if (podName == null) {
+ podName = ManagementFactory.getRuntimeMXBean().getName();
+ }
+ this.tags.put("pid", podName);
+ this.registry.setGlobalTags(this.tags);
+ }
+
+ public void StartPushLoop(int interval) {
+ this.StartPushLoop(interval, Nightingale.defaultUrl);
+ }
+
+ public void StartPushLoop(int interval, String url) {
+ Nightingale nightingale = new Nightingale(url, interval, this.tags);
+ nightingale.start(this.registry, interval);
+ }
+
+ public HTTPServer StartPullHttpServer(int port) throws IOException {
+ HTTPServer server = new HTTPServer(new InetSocketAddress(port), this.registry, false);
+ return server;
+ }
+
+
+}
diff --git a/simpleclient_sdk/src/main/java/io/prometheus/client/sdk/Nightingale.java b/simpleclient_sdk/src/main/java/io/prometheus/client/sdk/Nightingale.java
new file mode 100644
index 000000000..feb6553fe
--- /dev/null
+++ b/simpleclient_sdk/src/main/java/io/prometheus/client/sdk/Nightingale.java
@@ -0,0 +1,80 @@
+package io.prometheus.client.sdk;
+
+import io.prometheus.client.Collector;
+import io.prometheus.client.CollectorRegistry;
+
+import java.util.Collections;
+import java.util.Map;
+import java.util.logging.Logger;
+
+
+public class Nightingale {
+ private static final Logger logger = Logger.getLogger(Nightingale.class.getName());
+ static final String defaultUrl = "http://localhost:2080/v1/push";
+ private final Encoder encoder;
+
+ public Nightingale(String url, int batchSize, int interval, String tenantId, Map globalTags) {
+ this.encoder = new Encoder(url, 100, interval, tenantId,globalTags);
+ }
+
+ public Nightingale(String url,int interval, Map globalTags) {
+ this(url, 1000, interval, "1", globalTags);
+ }
+
+
+ /**
+ * Push samples from the given registry to Graphite.
+ */
+ public void push(CollectorRegistry registry) {
+ for (Collector.MetricFamilySamples metricFamilySamples : Collections.list(registry.metricFamilySamples())) {
+ for (Collector.MetricFamilySamples.Sample sample : metricFamilySamples.samples) {
+ this.encoder.add(sample);
+ }
+ }
+ this.encoder.end();
+ }
+
+ /**
+ * Push samples from the given registry to Graphite every minute.
+ */
+ public Thread start(CollectorRegistry registry) {
+ return start(registry, 60);
+ }
+
+ /**
+ * Push samples from the given registry to Graphite at the given interval.
+ */
+ public Thread start(CollectorRegistry registry, int intervalSeconds) {
+ Thread thread = new PushThread(registry, intervalSeconds);
+ thread.setDaemon(true);
+ thread.start();
+ return thread;
+ }
+
+ private class PushThread extends Thread {
+ private final CollectorRegistry registry;
+ private final int intervalSeconds;
+
+ PushThread(CollectorRegistry registry, int intervalSeconds) {
+ this.registry = registry;
+ this.intervalSeconds = intervalSeconds;
+ }
+
+ public void run() {
+ long waitUntil = System.currentTimeMillis();
+ while (true) {
+ push(registry);
+ long now = System.currentTimeMillis();
+ // We may skip some pushes if we're falling behind.
+ while (now >= waitUntil) {
+ waitUntil += intervalSeconds * 1000;
+ }
+ try {
+ Thread.sleep(waitUntil - now);
+ } catch (InterruptedException e) {
+ return;
+ }
+ }
+ }
+ }
+}
diff --git a/simpleclient_sdk/src/main/java/io/prometheus/client/sdk/StringEscapeUtils.java b/simpleclient_sdk/src/main/java/io/prometheus/client/sdk/StringEscapeUtils.java
new file mode 100644
index 000000000..75fe55918
--- /dev/null
+++ b/simpleclient_sdk/src/main/java/io/prometheus/client/sdk/StringEscapeUtils.java
@@ -0,0 +1,88 @@
+//
+// Source code recreated from a .class file by IntelliJ IDEA
+// (powered by FernFlower decompiler)
+//
+
+package io.prometheus.client.sdk;
+
+
+public final class StringEscapeUtils {
+ private static final String[] REPLACEMENT_CHARS = new String[128];
+ private static final String U2028 = "\\u2028";
+ private static final String U2029 = "\\u2029";
+
+ public static String escapeJson(String v) {
+ if (v == null) {
+ return "";
+ } else {
+ int length = v.length();
+ if (length == 0) {
+ return v;
+ } else {
+ int afterReplacement = 0;
+ StringBuilder builder = null;
+
+ for(int i = 0; i < length; ++i) {
+ char c = v.charAt(i);
+ String replacement;
+ if (c < 128) {
+ replacement = REPLACEMENT_CHARS[c];
+ if (replacement == null) {
+ continue;
+ }
+ } else if (c == 8232) {
+ replacement = "\\u2028";
+ } else {
+ if (c != 8233) {
+ continue;
+ }
+
+ replacement = "\\u2029";
+ }
+
+ if (afterReplacement < i) {
+ if (builder == null) {
+ builder = new StringBuilder(length);
+ }
+
+ builder.append(v, afterReplacement, i);
+ }
+
+ if (builder == null) {
+ builder = new StringBuilder(length);
+ }
+
+ builder.append(replacement);
+ afterReplacement = i + 1;
+ }
+
+ if (builder == null) {
+ return v;
+ } else {
+ if (afterReplacement < length) {
+ builder.append(v, afterReplacement, length);
+ }
+
+ return builder.toString();
+ }
+ }
+ }
+ }
+
+ private StringEscapeUtils() {
+ }
+
+ static {
+ for(int i = 0; i <= 31; ++i) {
+ REPLACEMENT_CHARS[i] = String.format("\\u%04x", i);
+ }
+
+ REPLACEMENT_CHARS[34] = "\\\"";
+ REPLACEMENT_CHARS[92] = "\\\\";
+ REPLACEMENT_CHARS[9] = "\\t";
+ REPLACEMENT_CHARS[8] = "\\b";
+ REPLACEMENT_CHARS[10] = "\\n";
+ REPLACEMENT_CHARS[13] = "\\r";
+ REPLACEMENT_CHARS[12] = "\\f";
+ }
+}
diff --git a/simpleclient_sdk/src/test/java/io/prometheus/client/sdk/NightingaleTest.java b/simpleclient_sdk/src/test/java/io/prometheus/client/sdk/NightingaleTest.java
new file mode 100644
index 000000000..ab473f52f
--- /dev/null
+++ b/simpleclient_sdk/src/test/java/io/prometheus/client/sdk/NightingaleTest.java
@@ -0,0 +1,78 @@
+package io.prometheus.client.sdk;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.fail;
+
+import io.prometheus.client.*;
+import org.junit.Test;
+
+import java.io.IOException;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Map;
+
+public class NightingaleTest {
+ @Test
+ public void testMarshalBatch(){
+ CollectorRegistry registry = new CollectorRegistry();
+
+ for (int i=0;i<1;i++){
+ Gauge labels = Gauge.build().name("labels"+i).help("help").labelNames("l").register(registry);
+ labels.labels("fo*o").inc();
+ }
+ Encoder encoder =new Encoder("dsadf",100,1000,"11",null);
+ for (Collector.MetricFamilySamples metricFamilySamples : Collections.list(registry.metricFamilySamples())) {
+ for (Collector.MetricFamilySamples.Sample sample : metricFamilySamples.samples) {
+ encoder.add(sample);
+ }
+ }
+ String body=encoder.marshalBatch(2212111l);
+ System.out.println(body);
+ }
+ @Test
+ public void testPush() throws Exception {
+ // Create a metric.
+ CollectorRegistry registry = new CollectorRegistry();
+ Gauge labels = Gauge.build().name("test_test").help("help").labelNames("l").register(registry);
+ labels.labels("fo*o").inc();
+ // Push.
+ Nightingale g = new Nightingale("http://n9e.example.com/api/transfer/push",10,null);
+ g.push(registry);
+ }
+ @Test
+ public void testPushLoop(){
+ Metrics metric= new Metrics("infra","metrics","test");
+ Gauge labels = Gauge.build().name("test_test3").help("help").labelNames("l").register();
+ labels.labels("foo").inc();
+ labels.labels("foo").inc();
+ labels.labels("foo").inc();
+ Histogram s= Histogram.build().name("histogram1").help("help").labelNames("sl").register();
+ s.labels("l1").observe(0.88);
+ s.labels("l1").observe(0.77);
+ s.labels("l2").observe(0.66);
+ Gauge labels2 = Gauge.build().name("test_test4").help("help").labelNames("l").register();
+ labels2.labels("foo").inc();
+ metric.StartPushLoop(1,"http://10.110.20.100:8080/api/transfer/push");
+ try {
+ Thread.sleep(1000*60);
+ } catch (InterruptedException e) {
+ e.printStackTrace();
+ }
+ }
+ @Test
+ public void testExport() throws IOException {
+ Metrics metric= new Metrics("infra","metrics","test");
+ Gauge labels = Gauge.build().name("test_test2").help("help").labelNames("l","l2").register();
+ labels.labels("foo","f2").inc();
+ Histogram s= Histogram.build().name("histogram1").help("help").labelNames("sl").register();
+ s.labels("l1").observe(0.88);
+ s.labels("l1").observe(0.77);
+ s.labels("l2").observe(0.66);
+ metric.StartPullHttpServer(9000);
+ try {
+ Thread.sleep(1000*60);
+ } catch (InterruptedException e) {
+ e.printStackTrace();
+ }
+ }
+}