All Downloads are FREE. Search and download functionalities are using the official Maven repository.

shade.polaris.io.prometheus.client.exporter.HTTPServer Maven / Gradle / Ivy

There is a newer version: 2.0.0.0-RC3
Show newest version
package io.prometheus.client.exporter;

import io.prometheus.client.CollectorRegistry;
import io.prometheus.client.exporter.common.TextFormat;

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.OutputStreamWriter;
import java.net.HttpURLConnection;
import java.net.InetSocketAddress;
import java.net.URLDecoder;
import java.nio.charset.Charset;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.FutureTask;
import java.util.concurrent.ThreadFactory;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.zip.GZIPOutputStream;

import com.sun.net.httpserver.HttpExchange;
import com.sun.net.httpserver.HttpHandler;
import com.sun.net.httpserver.HttpServer;

/**
 * Expose Prometheus metrics using a plain Java HttpServer.
 * 

* Example Usage: *

 * {@code
 * HTTPServer server = new HTTPServer(1234);
 * }
 * 
* */ public class HTTPServer { static { if (!System.getProperties().containsKey("sun.net.httpserver.maxReqTime")) { System.setProperty("sun.net.httpserver.maxReqTime", "60"); } if (!System.getProperties().containsKey("sun.net.httpserver.maxRspTime")) { System.setProperty("sun.net.httpserver.maxRspTime", "600"); } } private static class LocalByteArray extends ThreadLocal { @Override protected ByteArrayOutputStream initialValue() { return new ByteArrayOutputStream(1 << 20); } } /** * Handles Metrics collections from the given registry. */ 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."; HTTPMetricHandler(CollectorRegistry registry) { this.registry = registry; } @Override public void handle(HttpExchange t) throws IOException { String query = t.getRequestURI().getRawQuery(); String contextPath = t.getHttpContext().getPath(); ByteArrayOutputStream response = this.response.get(); response.reset(); OutputStreamWriter osw = new OutputStreamWriter(response, Charset.forName("UTF-8")); if ("/-/healthy".equals(contextPath)) { osw.write(HEALTHY_RESPONSE); } else { String contentType = TextFormat.chooseContentType(t.getRequestHeaders().getFirst("Accept")); t.getResponseHeaders().set("Content-Type", contentType); TextFormat.writeFormat(contentType, osw, registry.filteredMetricFamilySamples(parseQuery(query))); } osw.close(); if (shouldUseCompression(t)) { t.getResponseHeaders().set("Content-Encoding", "gzip"); t.sendResponseHeaders(HttpURLConnection.HTTP_OK, 0); final GZIPOutputStream os = new GZIPOutputStream(t.getResponseBody()); try { response.writeTo(os); } finally { os.close(); } } else { t.getResponseHeaders().set("Content-Length", String.valueOf(response.size())); t.sendResponseHeaders(HttpURLConnection.HTTP_OK, response.size()); response.writeTo(t.getResponseBody()); } t.close(); } } protected static boolean shouldUseCompression(HttpExchange exchange) { List encodingHeaders = exchange.getRequestHeaders().get("Accept-Encoding"); if (encodingHeaders == null) return false; for (String encodingHeader : encodingHeaders) { String[] encodings = encodingHeader.split(","); for (String encoding : encodings) { if (encoding.trim().equalsIgnoreCase("gzip")) { return true; } } } return false; } protected static Set parseQuery(String query) throws IOException { Set names = new HashSet(); if (query != null) { String[] pairs = query.split("&"); for (String pair : pairs) { int idx = pair.indexOf("="); if (idx != -1 && URLDecoder.decode(pair.substring(0, idx), "UTF-8").equals("name[]")) { names.add(URLDecoder.decode(pair.substring(idx + 1), "UTF-8")); } } } return names; } static class NamedDaemonThreadFactory implements ThreadFactory { private static final AtomicInteger POOL_NUMBER = new AtomicInteger(1); private final int poolNumber = POOL_NUMBER.getAndIncrement(); private final AtomicInteger threadNumber = new AtomicInteger(1); private final ThreadFactory delegate; private final boolean daemon; NamedDaemonThreadFactory(ThreadFactory delegate, boolean daemon) { this.delegate = delegate; this.daemon = daemon; } @Override public Thread newThread(Runnable r) { Thread t = delegate.newThread(r); t.setName(String.format("prometheus-http-%d-%d", poolNumber, threadNumber.getAndIncrement())); t.setDaemon(daemon); return t; } static ThreadFactory defaultThreadFactory(boolean daemon) { return new NamedDaemonThreadFactory(Executors.defaultThreadFactory(), daemon); } } protected final HttpServer server; protected final ExecutorService executorService; /** * Start a HTTP server serving Prometheus metrics from the given registry using the given {@link HttpServer}. * The {@code httpServer} is expected to already be bound to an address */ public HTTPServer(HttpServer httpServer, CollectorRegistry registry, boolean daemon) throws IOException { if (httpServer.getAddress() == null) throw new IllegalArgumentException("HttpServer hasn't been bound to an address"); server = httpServer; HttpHandler mHandler = new HTTPMetricHandler(registry); server.createContext("/", mHandler); server.createContext("/metrics", mHandler); server.createContext("/-/healthy", mHandler); executorService = Executors.newFixedThreadPool(5, NamedDaemonThreadFactory.defaultThreadFactory(daemon)); server.setExecutor(executorService); start(daemon); } /** * Start a HTTP server serving Prometheus metrics from the given registry. */ public HTTPServer(InetSocketAddress addr, CollectorRegistry registry, boolean daemon) throws IOException { this(HttpServer.create(addr, 3), registry, daemon); } /** * Start a HTTP server serving Prometheus metrics from the given registry using non-daemon threads. */ public HTTPServer(InetSocketAddress addr, CollectorRegistry registry) throws IOException { this(addr, registry, false); } /** * Start a HTTP server serving the default Prometheus registry. */ public HTTPServer(int port, boolean daemon) throws IOException { this(new InetSocketAddress(port), CollectorRegistry.defaultRegistry, daemon); } /** * Start a HTTP server serving the default Prometheus registry using non-daemon threads. */ public HTTPServer(int port) throws IOException { this(port, false); } /** * Start a HTTP server serving the default Prometheus registry. */ public HTTPServer(String host, int port, boolean daemon) throws IOException { this(new InetSocketAddress(host, port), CollectorRegistry.defaultRegistry, daemon); } /** * Start a HTTP server serving the default Prometheus registry using non-daemon threads. */ public HTTPServer(String host, int port) throws IOException { this(new InetSocketAddress(host, port), CollectorRegistry.defaultRegistry, false); } /** * Start a HTTP server by making sure that its background thread inherit proper daemon flag. */ private void start(boolean daemon) { if (daemon == Thread.currentThread().isDaemon()) { server.start(); } else { FutureTask startTask = new FutureTask(new Runnable() { @Override public void run() { server.start(); } }, null); NamedDaemonThreadFactory.defaultThreadFactory(daemon).newThread(startTask).start(); try { startTask.get(); } catch (ExecutionException e) { throw new RuntimeException("Unexpected exception on starting HTTPSever", e); } catch (InterruptedException e) { // This is possible only if the current tread has been interrupted, // but in real use cases this should not happen. // In any case, there is nothing to do, except to propagate interrupted flag. Thread.currentThread().interrupt(); } } } /** * Stop the HTTP server. */ public void stop() { server.stop(0); executorService.shutdown(); // Free any (parked/idle) threads in pool } /** * Gets the port number. */ public int getPort() { return server.getAddress().getPort(); } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy