001package io.prometheus.client.exporter;
002
003import io.prometheus.client.CollectorRegistry;
004import io.prometheus.client.exporter.common.TextFormat;
005
006import java.io.ByteArrayOutputStream;
007import java.io.IOException;
008import java.io.OutputStreamWriter;
009import java.net.InetSocketAddress;
010import java.net.URLDecoder;
011import java.util.Set;
012import java.util.HashSet;
013import java.util.concurrent.Executors;
014
015import com.sun.net.httpserver.HttpHandler;
016import com.sun.net.httpserver.HttpServer;
017import com.sun.net.httpserver.HttpExchange;
018
019/**
020 * Expose Prometheus metrics using a plain Java HttpServer.
021 * <p>
022 * Example Usage:
023 * <pre>
024 * {@code
025 * HTTPServer server = new HTTPServer(1234);
026 * }
027 * </pre>
028 * */
029public class HTTPServer {
030    static class HTTPMetricHandler implements HttpHandler {
031        private CollectorRegistry registry;
032
033        HTTPMetricHandler(CollectorRegistry registry) {
034          this.registry = registry;
035        }
036
037
038        public void handle(HttpExchange t) throws IOException {
039            String query = t.getRequestURI().getRawQuery();
040
041            ByteArrayOutputStream response = new ByteArrayOutputStream(1 << 20);
042            OutputStreamWriter osw = new OutputStreamWriter(response);
043            TextFormat.write004(osw,
044                    registry.filteredMetricFamilySamples(parseQuery(query)));
045            osw.flush();
046            osw.close();
047            response.flush();
048            response.close();
049
050            t.getResponseHeaders().set("Content-Type",
051                    TextFormat.CONTENT_TYPE_004);
052            t.getResponseHeaders().set("Content-Length",
053                    String.valueOf(response.size()));
054            t.sendResponseHeaders(200, response.size());
055            response.writeTo(t.getResponseBody());
056            t.close();
057        }
058
059    }
060
061    protected static Set<String> parseQuery(String query) throws IOException {
062        Set<String> names = new HashSet<String>();
063        if (query != null) {
064            String[] pairs = query.split("&");
065            for (String pair : pairs) {
066                int idx = pair.indexOf("=");
067                if (idx != -1 && URLDecoder.decode(pair.substring(0, idx), "UTF-8").equals("name[]")) {
068                    names.add(URLDecoder.decode(pair.substring(idx + 1), "UTF-8"));
069                }
070            }
071        }
072        return names;
073    }
074
075    protected HttpServer server;
076
077
078    /**
079     * Start a HTTP server serving Prometheus metrics from the given registry.
080     */
081    public HTTPServer(InetSocketAddress addr, CollectorRegistry registry) throws IOException {
082        server = HttpServer.create();
083        server.bind(addr, 3);
084        HttpHandler mHandler = new HTTPMetricHandler(registry);
085        server.createContext("/", mHandler);
086        server.createContext("/metrics", mHandler);
087        server.setExecutor(Executors.newFixedThreadPool(5));
088        server.start();
089    }
090
091    /**
092     * Start a HTTP server serving the default Prometheus registry.
093     */
094    public HTTPServer(int port) throws IOException {
095        this(new InetSocketAddress(port), CollectorRegistry.defaultRegistry);
096    }
097
098    /**
099     * 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