Skip to content

Commit fd7bef6

Browse files
authored
Add metricsHandlerPath and registerHealthHandler configuration options to HTTPServer (#1735)
Hey @fstab, long time no see. We would like to use this exporter in our project, but need to customize the metrics endpoint and disable the health handler. I hope this small contribution can make into a release. ## Summary This pull request enhances the configurability of the Prometheus Java HTTP server by allowing customization of the metrics endpoint path and making the health endpoint registration optional. It also updates the default HTML handler to reflect these changes and adds tests to ensure correct behavior. ### Configuration improvements * Added support for customizing the metrics endpoint via the new `metricsHandlerEndpoint` option in the `HTTPServer.Builder`. The default remains `/metrics`, but it can now be changed as needed. [[1]](diffhunk://#diff-fd57f399229770ac99d7f2b18cee74741a3f70c7c4c0800e17a04cb1b1bc321eL60-R82) [[2]](diffhunk://#diff-fd57f399229770ac99d7f2b18cee74741a3f70c7c4c0800e17a04cb1b1bc321eR187-R191) [[3]](diffhunk://#diff-fd57f399229770ac99d7f2b18cee74741a3f70c7c4c0800e17a04cb1b1bc321eR264-R278) [[4]](diffhunk://#diff-fd57f399229770ac99d7f2b18cee74741a3f70c7c4c0800e17a04cb1b1bc321eL278-R302) * Introduced a `registerHealthHandler` option in the `HTTPServer.Builder` to control whether the health endpoint (`/-/healthy`) is registered. By default, it is enabled. [[1]](diffhunk://#diff-fd57f399229770ac99d7f2b18cee74741a3f70c7c4c0800e17a04cb1b1bc321eL60-R82) [[2]](diffhunk://#diff-fd57f399229770ac99d7f2b18cee74741a3f70c7c4c0800e17a04cb1b1bc321eR187-R191) [[3]](diffhunk://#diff-fd57f399229770ac99d7f2b18cee74741a3f70c7c4c0800e17a04cb1b1bc321eR264-R278) [[4]](diffhunk://#diff-fd57f399229770ac99d7f2b18cee74741a3f70c7c4c0800e17a04cb1b1bc321eL278-R302) ### Default handler updates * Updated the `DefaultHandler` to accept the metrics path as a parameter and generate all documentation links and examples based on the configured path, ensuring consistency in the UI and documentation. [[1]](diffhunk://#diff-99311d9bc5b7879fc572852eb8106a440466befcdc072a07632818e6425dbb07L14-R34) [[2]](diffhunk://#diff-99311d9bc5b7879fc572852eb8106a440466befcdc072a07632818e6425dbb07L53-R66) ### Testing * Added tests to verify that the metrics endpoint can be customized and that the health endpoint can be enabled or disabled as configured. [[1]](diffhunk://#diff-c5c2cdc04572f6033ea66e1b7cd4859178b7da6adea0f8fc46993ae83ce8fa6cR108-R120) [[2]](diffhunk://#diff-c5c2cdc04572f6033ea66e1b7cd4859178b7da6adea0f8fc46993ae83ce8fa6cR163-R188) Signed-off-by: David Sondermann <[email protected]>
1 parent f4640e4 commit fd7bef6

File tree

3 files changed

+94
-17
lines changed

3 files changed

+94
-17
lines changed

prometheus-metrics-exporter-httpserver/src/main/java/io/prometheus/metrics/exporter/httpserver/DefaultHandler.java

Lines changed: 17 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -11,24 +11,27 @@ public class DefaultHandler implements HttpHandler {
1111
private final byte[] responseBytes;
1212
private final String contentType;
1313

14-
public DefaultHandler() {
14+
public DefaultHandler(String metricsPath) {
15+
String metrics = metricsPath.startsWith("/") ? metricsPath.substring(1) : metricsPath;
1516
String responseString =
1617
"<html>\n"
1718
+ "<head><title>Prometheus Java Client</title></head>\n"
1819
+ "<body>\n"
1920
+ "<h1>Prometheus Java Client</h1>\n"
2021
+ "<h2>Metrics Path</h2>\n"
21-
+ "The metrics path is <a href=\"metrics\">/metrics</a>.\n"
22+
+ String.format("The metrics path is <a href=\"%s\">%s</a>.\n", metrics, metricsPath)
2223
+ "<h2>Name Filter</h2>\n"
2324
+ "If you want to scrape only specific metrics, "
2425
+ "use the <tt>name[]</tt> parameter like this:\n"
2526
+ "<ul>\n"
26-
+ "<li><a href=\"metrics?name[]=my_metric\">/metrics?name[]=my_metric</a></li>\n"
27+
+ String.format(
28+
"<li><a href=\"%s?name[]=my_metric\">%s?name[]=my_metric</a></li>\n",
29+
metrics, metricsPath)
2730
+ "</ul>\n"
2831
+ "You can also use multiple <tt>name[]</tt> parameters to query multiple metrics:\n"
2932
+ "<ul>\n"
30-
+ "<li><a href=\"metrics?name[]=my_metric_a&name=my_metrics_b\">"
31-
+ "/metrics?name[]=my_metric_a&amp;name=[]=my_metric_b</a></li>\n"
33+
+ String.format("<li><a href=\"%s?name[]=my_metric_a&name[]=my_metric_b\">", metrics)
34+
+ String.format("%s?name[]=my_metric_a&amp;name[]=my_metric_b</a></li>\n", metricsPath)
3235
+ "</ul>\n"
3336
+ "The <tt>name[]</tt> parameter can be used by the Prometheus server for scraping. "
3437
+ "Add the following snippet to your scrape job configuration in "
@@ -50,13 +53,17 @@ public DefaultHandler() {
5053
+ "The Prometheus Java metrics library supports a <tt>debug</tt> query parameter "
5154
+ "for viewing the different formats in a Web browser:\n"
5255
+ "<ul>\n"
53-
+ "<li><a href=\"metrics?debug=openmetrics\">/metrics?debug=openmetrics</a>: "
56+
+ String.format(
57+
"<li><a href=\"%s?debug=openmetrics\">%s?debug=openmetrics</a>: ",
58+
metrics, metricsPath)
5459
+ "View OpenMetrics text format.</li>\n"
55-
+ "<li><a href=\"metrics?debug=text\">/metrics?debug=text</a>: "
60+
+ String.format(
61+
"<li><a href=\"%s?debug=text\">%s?debug=text</a>: ", metrics, metricsPath)
5662
+ "View Prometheus text format (this is the default when accessing the "
57-
+ "<a href=\"metrics\">/metrics</a> endpoint with a Web browser).</li>\n"
58-
+ "<li><a href=\"metrics?debug=prometheus-protobuf\">"
59-
+ "/metrics?debug=prometheus-protobuf</a>: "
63+
+ String.format(
64+
"<a href=\"%s\">%s</a> endpoint with a Web browser).</li>\n", metrics, metricsPath)
65+
+ String.format("<li><a href=\"%s?debug=prometheus-protobuf\">", metrics)
66+
+ String.format("%s?debug=prometheus-protobuf</a>: ", metricsPath)
6067
+ "View a text representation of the Prometheus protobuf format.</li>\n"
6168
+ "</ul>\n"
6269
+ "Note that the <tt>debug</tt> parameter is only for viewing different formats in a "

prometheus-metrics-exporter-httpserver/src/main/java/io/prometheus/metrics/exporter/httpserver/HTTPServer.java

Lines changed: 38 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -57,24 +57,29 @@ private HTTPServer(
5757
PrometheusRegistry registry,
5858
@Nullable Authenticator authenticator,
5959
@Nullable String authenticatedSubjectAttributeName,
60-
@Nullable HttpHandler defaultHandler) {
60+
@Nullable HttpHandler defaultHandler,
61+
@Nullable String metricsHandlerPath,
62+
@Nullable Boolean registerHealthHandler) {
6163
if (httpServer.getAddress() == null) {
6264
throw new IllegalArgumentException("HttpServer hasn't been bound to an address");
6365
}
6466
this.server = httpServer;
6567
this.executorService = executorService;
68+
String metricsPath = getMetricsPath(metricsHandlerPath);
6669
registerHandler(
6770
"/",
68-
defaultHandler == null ? new DefaultHandler() : defaultHandler,
71+
defaultHandler == null ? new DefaultHandler(metricsPath) : defaultHandler,
6972
authenticator,
7073
authenticatedSubjectAttributeName);
7174
registerHandler(
72-
"/metrics",
75+
metricsPath,
7376
new MetricsHandler(config, registry),
7477
authenticator,
7578
authenticatedSubjectAttributeName);
76-
registerHandler(
77-
"/-/healthy", new HealthyHandler(), authenticator, authenticatedSubjectAttributeName);
79+
if (registerHealthHandler == null || registerHealthHandler) {
80+
registerHandler(
81+
"/-/healthy", new HealthyHandler(), authenticator, authenticatedSubjectAttributeName);
82+
}
7883
try {
7984
// HttpServer.start() starts the HttpServer in a new background thread.
8085
// If we call HttpServer.start() from a thread of the executorService,
@@ -88,6 +93,16 @@ private HTTPServer(
8893
}
8994
}
9095

96+
private String getMetricsPath(@Nullable String metricsHandlerPath) {
97+
if (metricsHandlerPath == null) {
98+
return "/metrics";
99+
}
100+
if (!metricsHandlerPath.startsWith("/")) {
101+
return "/" + metricsHandlerPath;
102+
}
103+
return metricsHandlerPath;
104+
}
105+
91106
private void registerHandler(
92107
String path,
93108
HttpHandler handler,
@@ -179,9 +194,11 @@ public static class Builder {
179194
@Nullable private ExecutorService executorService = null;
180195
@Nullable private PrometheusRegistry registry = null;
181196
@Nullable private Authenticator authenticator = null;
197+
@Nullable private String authenticatedSubjectAttributeName = null;
182198
@Nullable private HttpsConfigurator httpsConfigurator = null;
183199
@Nullable private HttpHandler defaultHandler = null;
184-
@Nullable private String authenticatedSubjectAttributeName = null;
200+
@Nullable private String metricsHandlerPath = null;
201+
@Nullable private Boolean registerHealthHandler = null;
185202

186203
private Builder(PrometheusProperties config) {
187204
this.config = config;
@@ -254,6 +271,18 @@ public Builder defaultHandler(HttpHandler defaultHandler) {
254271
return this;
255272
}
256273

274+
/** Optional: Override default path for the metrics endpoint. Default is {@code /metrics}. */
275+
public Builder metricsHandlerPath(String metricsHandlerPath) {
276+
this.metricsHandlerPath = metricsHandlerPath;
277+
return this;
278+
}
279+
280+
/** Optional: Override if the health handler should be registered. Default is {@code true}. */
281+
public Builder registerHealthHandler(boolean registerHealthHandler) {
282+
this.registerHealthHandler = registerHealthHandler;
283+
return this;
284+
}
285+
257286
/** Build and start the HTTPServer. */
258287
public HTTPServer buildAndStart() throws IOException {
259288
if (registry == null) {
@@ -275,7 +304,9 @@ public HTTPServer buildAndStart() throws IOException {
275304
registry,
276305
authenticator,
277306
authenticatedSubjectAttributeName,
278-
defaultHandler);
307+
defaultHandler,
308+
metricsHandlerPath,
309+
registerHealthHandler);
279310
}
280311

281312
private InetSocketAddress makeInetSocketAddress() {

prometheus-metrics-exporter-httpserver/src/test/java/io/prometheus/metrics/exporter/httpserver/HTTPServerTest.java

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -105,6 +105,19 @@ void metrics() throws IOException {
105105
"/metrics");
106106
}
107107

108+
@Test
109+
void metricsCustomPath() throws IOException {
110+
run(
111+
HTTPServer.builder()
112+
.port(0)
113+
.registry(new PrometheusRegistry())
114+
.metricsHandlerPath("/my-metrics")
115+
.executorService(Executors.newFixedThreadPool(1))
116+
.buildAndStart(),
117+
"200",
118+
"/my-metrics");
119+
}
120+
108121
@Test
109122
void registryThrows() throws IOException {
110123
HTTPServer server =
@@ -147,4 +160,30 @@ void config() throws NoSuchAlgorithmException, IOException {
147160
void health() throws IOException {
148161
run(HTTPServer.builder().port(0).buildAndStart(), "200", "/-/healthy");
149162
}
163+
164+
@Test
165+
void healthEnabled() throws IOException {
166+
HttpHandler handler = exchange -> exchange.sendResponseHeaders(204, -1);
167+
run(
168+
HTTPServer.builder()
169+
.port(0)
170+
.defaultHandler(handler)
171+
.registerHealthHandler(true)
172+
.buildAndStart(),
173+
"200",
174+
"/-/healthy");
175+
}
176+
177+
@Test
178+
void healthDisabled() throws IOException {
179+
HttpHandler handler = exchange -> exchange.sendResponseHeaders(204, -1);
180+
run(
181+
HTTPServer.builder()
182+
.port(0)
183+
.defaultHandler(handler)
184+
.registerHealthHandler(false)
185+
.buildAndStart(),
186+
"204",
187+
"/-/healthy");
188+
}
150189
}

0 commit comments

Comments
 (0)