Skip to content

Commit aca30e6

Browse files
authored
Add a Httpserver (#276)
* Copy over key bit of PR#73 for HttpServer exposition. #73 by tgulacsi * Cleanup httpserver, allow mutliple simultaneous requests. * Support specifying host. * Add support for ?name[]=
1 parent c462578 commit aca30e6

File tree

5 files changed

+208
-2
lines changed

5 files changed

+208
-2
lines changed

README.md

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -515,9 +515,14 @@ There are several options for exporting metrics.
515515

516516
Metrics are usually exposed over HTTP, to be read by the Prometheus server.
517517

518-
There are Servlet, SpringBoot, and Vert.x integrations included in the client library.
518+
There are HTTPServer, Servlet, SpringBoot, and Vert.x integrations included in the client library.
519+
The simplest of these is the HTTPServer:
519520

520-
To add Prometheus exposition to an existing HTTP server, see the `MetricsServlet`.
521+
```java
522+
HTTPServer server = new HTTPServer(1234);
523+
```
524+
525+
To add Prometheus exposition to an existing HTTP server using servlets, see the `MetricsServlet`.
521526
It also serves as a simple example of how to write a custom endpoint.
522527

523528
To expose the metrics used in your code, you would add the Prometheus servlet to your Jetty server:
@@ -531,6 +536,9 @@ server.setHandler(context);
531536
context.addServlet(new ServletHolder(new MetricsServlet()), "/metrics");
532537
```
533538

539+
All HTTP expostion integrations support restricting which time series to return
540+
using `?name[]=` URL parameters. Due to implementation limitations, this may
541+
have false negatives.
534542

535543

536544
## Exporting to a Pushgateway

pom.xml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,7 @@
5151
<module>simpleclient_hibernate</module>
5252
<module>simpleclient_guava</module>
5353
<module>simpleclient_hotspot</module>
54+
<module>simpleclient_httpserver</module>
5455
<module>simpleclient_log4j</module>
5556
<module>simpleclient_log4j2</module>
5657
<module>simpleclient_logback</module>

simpleclient_httpserver/pom.xml

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
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.24-SNAPSHOT</version>
9+
</parent>
10+
11+
<groupId>io.prometheus</groupId>
12+
<artifactId>simpleclient_httpserver</artifactId>
13+
<packaging>bundle</packaging>
14+
15+
<name>Prometheus Java Simpleclient Httpserver</name>
16+
<description>
17+
Httpserver exposition 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+
<dependencies>
37+
<dependency>
38+
<groupId>io.prometheus</groupId>
39+
<artifactId>simpleclient</artifactId>
40+
<version>0.0.24-SNAPSHOT</version>
41+
</dependency>
42+
<dependency>
43+
<groupId>io.prometheus</groupId>
44+
<artifactId>simpleclient_common</artifactId>
45+
<version>0.0.24-SNAPSHOT</version>
46+
</dependency>
47+
<!-- Test Dependencies Follow -->
48+
<dependency>
49+
<groupId>junit</groupId>
50+
<artifactId>junit</artifactId>
51+
<version>4.11</version>
52+
<scope>test</scope>
53+
</dependency>
54+
<dependency>
55+
<groupId>org.assertj</groupId>
56+
<artifactId>assertj-core</artifactId>
57+
<version>2.6.0</version>
58+
<scope>test</scope>
59+
</dependency>
60+
</dependencies>
61+
</project>
Lines changed: 112 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,112 @@
1+
package io.prometheus.client.exporter;
2+
3+
import io.prometheus.client.CollectorRegistry;
4+
import io.prometheus.client.exporter.common.TextFormat;
5+
6+
import java.io.ByteArrayOutputStream;
7+
import java.io.IOException;
8+
import java.io.OutputStreamWriter;
9+
import java.net.InetSocketAddress;
10+
import java.net.URLDecoder;
11+
import java.util.Set;
12+
import java.util.HashSet;
13+
import java.util.concurrent.Executors;
14+
15+
import com.sun.net.httpserver.HttpHandler;
16+
import com.sun.net.httpserver.HttpServer;
17+
import com.sun.net.httpserver.HttpExchange;
18+
19+
/**
20+
* Expose Prometheus metrics using a plain Java HttpServer.
21+
* <p>
22+
* Example Usage:
23+
* <pre>
24+
* {@code
25+
* HTTPServer server = new HTTPServer(1234);
26+
* }
27+
* </pre>
28+
* */
29+
public class HTTPServer {
30+
static class HTTPMetricHandler implements HttpHandler {
31+
private CollectorRegistry registry;
32+
33+
HTTPMetricHandler(CollectorRegistry registry) {
34+
this.registry = registry;
35+
}
36+
37+
38+
public void handle(HttpExchange t) throws IOException {
39+
String query = t.getRequestURI().getRawQuery();
40+
41+
ByteArrayOutputStream response = new ByteArrayOutputStream(1 << 20);
42+
OutputStreamWriter osw = new OutputStreamWriter(response);
43+
TextFormat.write004(osw,
44+
registry.filteredMetricFamilySamples(parseQuery(query)));
45+
osw.flush();
46+
osw.close();
47+
response.flush();
48+
response.close();
49+
50+
t.getResponseHeaders().set("Content-Type",
51+
TextFormat.CONTENT_TYPE_004);
52+
t.getResponseHeaders().set("Content-Length",
53+
String.valueOf(response.size()));
54+
t.sendResponseHeaders(200, response.size());
55+
response.writeTo(t.getResponseBody());
56+
t.close();
57+
}
58+
59+
}
60+
61+
protected static Set<String> parseQuery(String query) throws IOException {
62+
Set<String> names = new HashSet<String>();
63+
if (query != null) {
64+
String[] pairs = query.split("&");
65+
for (String pair : pairs) {
66+
int idx = pair.indexOf("=");
67+
if (idx != -1 && URLDecoder.decode(pair.substring(0, idx), "UTF-8").equals("name[]")) {
68+
names.add(URLDecoder.decode(pair.substring(idx + 1), "UTF-8"));
69+
}
70+
}
71+
}
72+
return names;
73+
}
74+
75+
protected HttpServer server;
76+
77+
78+
/**
79+
* Start a HTTP server serving Prometheus metrics from the given registry.
80+
*/
81+
public HTTPServer(InetSocketAddress addr, CollectorRegistry registry) throws IOException {
82+
server = HttpServer.create();
83+
server.bind(addr, 3);
84+
HttpHandler mHandler = new HTTPMetricHandler(registry);
85+
server.createContext("/", mHandler);
86+
server.createContext("/metrics", mHandler);
87+
server.setExecutor(Executors.newFixedThreadPool(5));
88+
server.start();
89+
}
90+
91+
/**
92+
* Start a HTTP server serving the default Prometheus registry.
93+
*/
94+
public HTTPServer(int port) throws IOException {
95+
this(new InetSocketAddress(port), CollectorRegistry.defaultRegistry);
96+
}
97+
98+
/**
99+
* Start a HTTP server serving the default Prometheus registry.
100+
*/
101+
public HTTPServer(String host, int port) throws IOException {
102+
this(new InetSocketAddress(host, port), CollectorRegistry.defaultRegistry);
103+
}
104+
105+
/**
106+
* Stop the HTTP server.
107+
*/
108+
public void stop() {
109+
server.stop(0);
110+
}
111+
}
112+
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
package io.prometheus.client.exporter;
2+
3+
import io.prometheus.client.Counter;
4+
import io.prometheus.client.Gauge;
5+
import io.prometheus.client.Histogram;
6+
import io.prometheus.client.Summary;
7+
8+
public class ExampleExporter {
9+
10+
static final Gauge g = Gauge.build().name("gauge").help("blah").register();
11+
static final Counter c = Counter.build().name("counter").help("meh").register();
12+
static final Summary s = Summary.build().name("summary").help("meh").register();
13+
static final Histogram h = Histogram.build().name("histogram").help("meh").register();
14+
static final Gauge l = Gauge.build().name("labels").help("blah").labelNames("l").register();
15+
16+
public static void main(String[] args) throws Exception {
17+
new HTTPServer(1234);
18+
g.set(1);
19+
c.inc(2);
20+
s.observe(3);
21+
h.observe(4);
22+
l.labels("foo").inc(5);
23+
}
24+
}

0 commit comments

Comments
 (0)