Skip to content

Commit

Permalink
Merge pull request #100 from prometheus/graphite
Browse files Browse the repository at this point in the history
Add graphite bridge.
  • Loading branch information
brian-brazil committed Jan 25, 2016
2 parents 437987f + 58c3b67 commit f2caac5
Show file tree
Hide file tree
Showing 4 changed files with 229 additions and 0 deletions.
1 change: 1 addition & 0 deletions pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@
<module>simpleclient_common</module>
<module>simpleclient_hotspot</module>
<module>simpleclient_servlet</module>
<module>simpleclient_graphite_bridge</module>
<module>simpleclient_log4j</module>
<module>simpleclient_logback</module>
<module>simpleclient_pushgateway</module>
Expand Down
51 changes: 51 additions & 0 deletions simpleclient_graphite_bridge/pom.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
<?xml version="1.0" encoding="UTF-8"?>
<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">
<modelVersion>4.0.0</modelVersion>

<parent>
<groupId>io.prometheus</groupId>
<artifactId>parent</artifactId>
<version>0.0.13-SNAPSHOT</version>
</parent>

<groupId>io.prometheus</groupId>
<artifactId>simpleclient_graphite_bridge</artifactId>
<packaging>bundle</packaging>

<name>Prometheus Java Simpleclient Graphite Bridge</name>
<description>
Graphite bridge for the simpleclient.
</description>

<licenses>
<license>
<name>The Apache Software License, Version 2.0</name>
<url>http://www.apache.org/licenses/LICENSE-2.0.txt</url>
<distribution>repo</distribution>
</license>
</licenses>

<developers>
<developer>
<id>brian-brazil</id>
<name>Brian Brazil</name>
<email>[email protected]</email>
</developer>
</developers>


<dependencies>
<dependency>
<groupId>io.prometheus</groupId>
<artifactId>simpleclient</artifactId>
<version>0.0.13-SNAPSHOT</version>
</dependency>
<!-- Test Dependencies Follow -->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.11</version>
<scope>test</scope>
</dependency>
</dependencies>
</project>
Original file line number Diff line number Diff line change
@@ -0,0 +1,120 @@
package io.prometheus.client.bridge;

import io.prometheus.client.Collector;
import io.prometheus.client.CollectorRegistry;

import java.io.BufferedWriter;
import java.io.IOException;
import java.io.PrintWriter;
import java.net.Socket;
import java.util.Collections;
import java.util.logging.Level;
import java.util.logging.Logger;
import java.util.regex.Pattern;
import java.util.regex.Matcher;

/**
* Export metrics in the Graphite plaintext format.
* <p>
* <pre>
* {@code
* Graphite g = new Graphite("localhost", 2003);
* // Push the default registry once.
* g.push(CollectorRegistry.defaultRegistry);
*
* // Push the default registry every 60 seconds.
* Thread thread = g.start(CollectorRegistry.defaultRegistry, 60);
* // Stop pushing.
* thread.interrupt();
* thread.joi();
* }
* </pre>
* <p>
* See <a href="https://github.com/prometheus/pushgateway">https://github.com/prometheus/pushgateway</a>
*/
public class Graphite {
private static final Logger logger = Logger.getLogger(Graphite.class.getName());

private final String host;
private final int port;
private static final Pattern INVALID_GRAPHITE_CHARS = Pattern.compile("[^a-zA-Z0-9_-]");
/**
* Construct a Graphite Bridge with the given host:port.
*/
public Graphite(String host, int port) {
this.host = host;
this.port = port;
}

/**
* Push samples from the given registry to Graphite.
*/
public void push(CollectorRegistry registry) throws IOException {
Socket s = new Socket(host, port);
BufferedWriter writer = new BufferedWriter(new PrintWriter(s.getOutputStream()));
Matcher m = INVALID_GRAPHITE_CHARS.matcher("");
long now = System.currentTimeMillis() / 1000;
for (Collector.MetricFamilySamples metricFamilySamples: Collections.list(registry.metricFamilySamples())) {
for (Collector.MetricFamilySamples.Sample sample: metricFamilySamples.samples) {
m.reset(sample.name);
writer.write(m.replaceAll("_"));
for (int i = 0; i < sample.labelNames.size(); ++i) {
m.reset(sample.labelValues.get(i));
writer.write("." + sample.labelNames.get(i) + "." + m.replaceAll("_"));
}
writer.write(" " + sample.value + " " + now + "\n");
}
}
writer.close();
s.close();
}

/**
* 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) {
try {
push(registry);
} catch (IOException e) {
logger.log(Level.WARNING, "Exception " + e + " pushing to " + host + ":" + port, e);
}

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;
}
}
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
package io.prometheus.client.bridge;

import static org.junit.Assert.assertEquals;
import static org.junit.Assert.fail;

import org.junit.Test;

import io.prometheus.client.CollectorRegistry;
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;

public class GraphiteTest {
@Test
public void testPush() throws Exception {
// Create a metric.
CollectorRegistry registry = new CollectorRegistry();
Gauge labels = Gauge.build().name("labels").help("help").labelNames("l").register(registry);
labels.labels("fo*o").inc();


// Server to accept push.
final ServerSocket ss = new ServerSocket(0);
final StringBuilder result = new StringBuilder();
Thread t = new Thread() {
public void run() {
try {
Socket s = ss.accept();
BufferedReader reader = new BufferedReader(new InputStreamReader(s.getInputStream()));
result.append(reader.readLine());
s.close();
} catch (Exception e) {
e.printStackTrace();
fail();
}
}
};
t.start();

// Push.
Graphite g = new Graphite("localhost", ss.getLocalPort());
g.push(registry);
t.join();
ss.close();

// Check result.
String[] parts = result.toString().split(" ");
assertEquals(3, parts.length);
assertEquals("labels.l.fo_o", parts[0]);
assertEquals("1.0", parts[1]);
Integer.parseInt(parts[2]); // This shouldn't throw an exception.
}
}

0 comments on commit f2caac5

Please sign in to comment.