Skip to content

Commit f2caac5

Browse files
committed
Merge pull request #100 from prometheus/graphite
Add graphite bridge.
2 parents 437987f + 58c3b67 commit f2caac5

File tree

4 files changed

+229
-0
lines changed

4 files changed

+229
-0
lines changed

pom.xml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,7 @@
4747
<module>simpleclient_common</module>
4848
<module>simpleclient_hotspot</module>
4949
<module>simpleclient_servlet</module>
50+
<module>simpleclient_graphite_bridge</module>
5051
<module>simpleclient_log4j</module>
5152
<module>simpleclient_logback</module>
5253
<module>simpleclient_pushgateway</module>
Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
3+
<modelVersion>4.0.0</modelVersion>
4+
5+
<parent>
6+
<groupId>io.prometheus</groupId>
7+
<artifactId>parent</artifactId>
8+
<version>0.0.13-SNAPSHOT</version>
9+
</parent>
10+
11+
<groupId>io.prometheus</groupId>
12+
<artifactId>simpleclient_graphite_bridge</artifactId>
13+
<packaging>bundle</packaging>
14+
15+
<name>Prometheus Java Simpleclient Graphite Bridge</name>
16+
<description>
17+
Graphite bridge for the simpleclient.
18+
</description>
19+
20+
<licenses>
21+
<license>
22+
<name>The Apache Software License, Version 2.0</name>
23+
<url>http://www.apache.org/licenses/LICENSE-2.0.txt</url>
24+
<distribution>repo</distribution>
25+
</license>
26+
</licenses>
27+
28+
<developers>
29+
<developer>
30+
<id>brian-brazil</id>
31+
<name>Brian Brazil</name>
32+
<email>[email protected]</email>
33+
</developer>
34+
</developers>
35+
36+
37+
<dependencies>
38+
<dependency>
39+
<groupId>io.prometheus</groupId>
40+
<artifactId>simpleclient</artifactId>
41+
<version>0.0.13-SNAPSHOT</version>
42+
</dependency>
43+
<!-- Test Dependencies Follow -->
44+
<dependency>
45+
<groupId>junit</groupId>
46+
<artifactId>junit</artifactId>
47+
<version>4.11</version>
48+
<scope>test</scope>
49+
</dependency>
50+
</dependencies>
51+
</project>
Lines changed: 120 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,120 @@
1+
package io.prometheus.client.bridge;
2+
3+
import io.prometheus.client.Collector;
4+
import io.prometheus.client.CollectorRegistry;
5+
6+
import java.io.BufferedWriter;
7+
import java.io.IOException;
8+
import java.io.PrintWriter;
9+
import java.net.Socket;
10+
import java.util.Collections;
11+
import java.util.logging.Level;
12+
import java.util.logging.Logger;
13+
import java.util.regex.Pattern;
14+
import java.util.regex.Matcher;
15+
16+
/**
17+
* Export metrics in the Graphite plaintext format.
18+
* <p>
19+
* <pre>
20+
* {@code
21+
* Graphite g = new Graphite("localhost", 2003);
22+
* // Push the default registry once.
23+
* g.push(CollectorRegistry.defaultRegistry);
24+
*
25+
* // Push the default registry every 60 seconds.
26+
* Thread thread = g.start(CollectorRegistry.defaultRegistry, 60);
27+
* // Stop pushing.
28+
* thread.interrupt();
29+
* thread.joi();
30+
* }
31+
* </pre>
32+
* <p>
33+
* See <a href="https://github.com/prometheus/pushgateway">https://github.com/prometheus/pushgateway</a>
34+
*/
35+
public class Graphite {
36+
private static final Logger logger = Logger.getLogger(Graphite.class.getName());
37+
38+
private final String host;
39+
private final int port;
40+
private static final Pattern INVALID_GRAPHITE_CHARS = Pattern.compile("[^a-zA-Z0-9_-]");
41+
/**
42+
* Construct a Graphite Bridge with the given host:port.
43+
*/
44+
public Graphite(String host, int port) {
45+
this.host = host;
46+
this.port = port;
47+
}
48+
49+
/**
50+
* Push samples from the given registry to Graphite.
51+
*/
52+
public void push(CollectorRegistry registry) throws IOException {
53+
Socket s = new Socket(host, port);
54+
BufferedWriter writer = new BufferedWriter(new PrintWriter(s.getOutputStream()));
55+
Matcher m = INVALID_GRAPHITE_CHARS.matcher("");
56+
long now = System.currentTimeMillis() / 1000;
57+
for (Collector.MetricFamilySamples metricFamilySamples: Collections.list(registry.metricFamilySamples())) {
58+
for (Collector.MetricFamilySamples.Sample sample: metricFamilySamples.samples) {
59+
m.reset(sample.name);
60+
writer.write(m.replaceAll("_"));
61+
for (int i = 0; i < sample.labelNames.size(); ++i) {
62+
m.reset(sample.labelValues.get(i));
63+
writer.write("." + sample.labelNames.get(i) + "." + m.replaceAll("_"));
64+
}
65+
writer.write(" " + sample.value + " " + now + "\n");
66+
}
67+
}
68+
writer.close();
69+
s.close();
70+
}
71+
72+
/**
73+
* Push samples from the given registry to Graphite every minute.
74+
*/
75+
public Thread start(CollectorRegistry registry) {
76+
return start(registry, 60);
77+
}
78+
79+
/**
80+
* Push samples from the given registry to Graphite at the given interval.
81+
*/
82+
public Thread start(CollectorRegistry registry, int intervalSeconds) {
83+
Thread thread = new PushThread(registry, intervalSeconds);
84+
thread.setDaemon(true);
85+
thread.start();
86+
return thread;
87+
}
88+
89+
private class PushThread extends Thread {
90+
private final CollectorRegistry registry;
91+
private final int intervalSeconds;
92+
93+
PushThread(CollectorRegistry registry, int intervalSeconds) {
94+
this.registry = registry;
95+
this.intervalSeconds = intervalSeconds;
96+
}
97+
98+
public void run() {
99+
long waitUntil = System.currentTimeMillis();
100+
while (true) {
101+
try {
102+
push(registry);
103+
} catch (IOException e) {
104+
logger.log(Level.WARNING, "Exception " + e + " pushing to " + host + ":" + port, e);
105+
}
106+
107+
long now = System.currentTimeMillis();
108+
// We may skip some pushes if we're falling behind.
109+
while (now >= waitUntil) {
110+
waitUntil += intervalSeconds * 1000;
111+
}
112+
try {
113+
Thread.sleep(waitUntil - now);
114+
} catch (InterruptedException e) {
115+
return;
116+
}
117+
}
118+
}
119+
}
120+
}
Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
package io.prometheus.client.bridge;
2+
3+
import static org.junit.Assert.assertEquals;
4+
import static org.junit.Assert.fail;
5+
6+
import org.junit.Test;
7+
8+
import io.prometheus.client.CollectorRegistry;
9+
import io.prometheus.client.Gauge;
10+
11+
import java.io.BufferedReader;
12+
import java.io.IOException;
13+
import java.io.InputStreamReader;
14+
import java.net.ServerSocket;
15+
import java.net.Socket;
16+
17+
public class GraphiteTest {
18+
@Test
19+
public void testPush() throws Exception {
20+
// Create a metric.
21+
CollectorRegistry registry = new CollectorRegistry();
22+
Gauge labels = Gauge.build().name("labels").help("help").labelNames("l").register(registry);
23+
labels.labels("fo*o").inc();
24+
25+
26+
// Server to accept push.
27+
final ServerSocket ss = new ServerSocket(0);
28+
final StringBuilder result = new StringBuilder();
29+
Thread t = new Thread() {
30+
public void run() {
31+
try {
32+
Socket s = ss.accept();
33+
BufferedReader reader = new BufferedReader(new InputStreamReader(s.getInputStream()));
34+
result.append(reader.readLine());
35+
s.close();
36+
} catch (Exception e) {
37+
e.printStackTrace();
38+
fail();
39+
}
40+
}
41+
};
42+
t.start();
43+
44+
// Push.
45+
Graphite g = new Graphite("localhost", ss.getLocalPort());
46+
g.push(registry);
47+
t.join();
48+
ss.close();
49+
50+
// Check result.
51+
String[] parts = result.toString().split(" ");
52+
assertEquals(3, parts.length);
53+
assertEquals("labels.l.fo_o", parts[0]);
54+
assertEquals("1.0", parts[1]);
55+
Integer.parseInt(parts[2]); // This shouldn't throw an exception.
56+
}
57+
}

0 commit comments

Comments
 (0)