Skip to content

Commit 7da7037

Browse files
fstabjack-berg
andauthored
Make /metrics the only Prometheus metrics endpoint (#6476)
Signed-off-by: Fabian Stäber <[email protected]> Co-authored-by: Jack Berg <[email protected]>
1 parent 0f99d70 commit 7da7037

File tree

3 files changed

+69
-6
lines changed

3 files changed

+69
-6
lines changed

exporters/prometheus/src/main/java/io/opentelemetry/exporter/prometheus/PrometheusHttpServer.java

+4-3
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010

1111
package io.opentelemetry.exporter.prometheus;
1212

13+
import com.sun.net.httpserver.HttpHandler;
1314
import io.opentelemetry.sdk.common.CompletableResultCode;
1415
import io.opentelemetry.sdk.common.export.MemoryMode;
1516
import io.opentelemetry.sdk.internal.DaemonThreadFactory;
@@ -18,7 +19,6 @@
1819
import io.opentelemetry.sdk.metrics.export.CollectionRegistration;
1920
import io.opentelemetry.sdk.metrics.export.MetricReader;
2021
import io.prometheus.metrics.exporter.httpserver.HTTPServer;
21-
import io.prometheus.metrics.exporter.httpserver.MetricsHandler;
2222
import io.prometheus.metrics.model.registry.PrometheusRegistry;
2323
import java.io.IOException;
2424
import java.io.UncheckedIOException;
@@ -64,7 +64,8 @@ public static PrometheusHttpServerBuilder builder() {
6464
PrometheusRegistry prometheusRegistry,
6565
boolean otelScopeEnabled,
6666
@Nullable Predicate<String> allowedResourceAttributesFilter,
67-
MemoryMode memoryMode) {
67+
MemoryMode memoryMode,
68+
@Nullable HttpHandler defaultHandler) {
6869
this.builder = builder;
6970
this.prometheusMetricReader =
7071
new PrometheusMetricReader(otelScopeEnabled, allowedResourceAttributesFilter);
@@ -86,7 +87,7 @@ public static PrometheusHttpServerBuilder builder() {
8687
.port(port)
8788
.executorService(executor)
8889
.registry(prometheusRegistry)
89-
.defaultHandler(new MetricsHandler(prometheusRegistry))
90+
.defaultHandler(defaultHandler)
9091
.buildAndStart();
9192
} catch (IOException e) {
9293
throw new UncheckedIOException("Could not create Prometheus HTTP server", e);

exporters/prometheus/src/main/java/io/opentelemetry/exporter/prometheus/PrometheusHttpServerBuilder.java

+21-1
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
import static io.opentelemetry.api.internal.Utils.checkArgument;
99
import static java.util.Objects.requireNonNull;
1010

11+
import com.sun.net.httpserver.HttpHandler;
1112
import io.opentelemetry.sdk.common.export.MemoryMode;
1213
import io.prometheus.metrics.model.registry.PrometheusRegistry;
1314
import java.util.concurrent.ExecutorService;
@@ -29,6 +30,7 @@ public final class PrometheusHttpServerBuilder {
2930
@Nullable private Predicate<String> allowedResourceAttributesFilter;
3031
@Nullable private ExecutorService executor;
3132
private MemoryMode memoryMode = DEFAULT_MEMORY_MODE;
33+
@Nullable private HttpHandler defaultHandler;
3234

3335
PrometheusHttpServerBuilder() {}
3436

@@ -107,6 +109,23 @@ public PrometheusHttpServerBuilder setMemoryMode(MemoryMode memoryMode) {
107109
return this;
108110
}
109111

112+
/**
113+
* Override the default handler for serving the "/", "/**" endpoint.
114+
*
115+
* <p>This can be used to serve metrics on additional paths besides the default "/metrics". For
116+
* example: <code>
117+
* PrometheusHttpServer.builder()
118+
* .setPrometheusRegistry(prometheusRegistry)
119+
* .setDefaultHandler(new MetricsHandler(prometheusRegistry))
120+
* .build()
121+
* </code>
122+
*/
123+
public PrometheusHttpServerBuilder setDefaultHandler(HttpHandler defaultHandler) {
124+
requireNonNull(defaultHandler, "defaultHandler");
125+
this.defaultHandler = defaultHandler;
126+
return this;
127+
}
128+
110129
/**
111130
* Returns a new {@link PrometheusHttpServer} with the configuration of this builder which can be
112131
* registered with a {@link io.opentelemetry.sdk.metrics.SdkMeterProvider}.
@@ -120,6 +139,7 @@ public PrometheusHttpServer build() {
120139
prometheusRegistry,
121140
otelScopeEnabled,
122141
allowedResourceAttributesFilter,
123-
memoryMode);
142+
memoryMode,
143+
defaultHandler);
124144
}
125145
}

exporters/prometheus/src/test/java/io/opentelemetry/exporter/prometheus/PrometheusHttpServerTest.java

+44-2
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@
3535
import io.opentelemetry.sdk.metrics.internal.data.ImmutableSumData;
3636
import io.opentelemetry.sdk.resources.Resource;
3737
import io.prometheus.metrics.exporter.httpserver.HTTPServer;
38+
import io.prometheus.metrics.exporter.httpserver.MetricsHandler;
3839
import io.prometheus.metrics.model.registry.PrometheusRegistry;
3940
import java.io.ByteArrayInputStream;
4041
import java.io.IOException;
@@ -229,7 +230,7 @@ void fetchOpenMetrics() {
229230
void fetchFiltered() {
230231
AggregatedHttpResponse response =
231232
client
232-
.get("/?name[]=grpc_name_unit_total&name[]=bears_total&name[]=target_info")
233+
.get("/metrics?name[]=grpc_name_unit_total&name[]=bears_total&name[]=target_info")
233234
.aggregate()
234235
.join();
235236
assertThat(response.status()).isEqualTo(HttpStatus.OK);
@@ -245,6 +246,47 @@ void fetchFiltered() {
245246
+ "target_info{kr=\"vr\"} 1\n");
246247
}
247248

249+
@Test
250+
void fetchOverrideDefaultHandler() {
251+
PrometheusRegistry registry = new PrometheusRegistry();
252+
try (PrometheusHttpServer prometheusServer =
253+
PrometheusHttpServer.builder()
254+
.setHost("localhost")
255+
.setPort(0)
256+
.setPrometheusRegistry(registry)
257+
// Set the default handler to serve metrics on /**
258+
.setDefaultHandler(new MetricsHandler(registry))
259+
.build()) {
260+
prometheusServer.register(
261+
new CollectionRegistration() {
262+
@Override
263+
public Collection<MetricData> collectAllMetrics() {
264+
return metricData.get();
265+
}
266+
});
267+
WebClient client =
268+
WebClient.builder("http://localhost:" + prometheusServer.getAddress().getPort())
269+
.decorator(RetryingClient.newDecorator(RetryRule.failsafe()))
270+
.build();
271+
272+
// Fetch metrics from / instead of /metrics
273+
AggregatedHttpResponse response = client.get("/").aggregate().join();
274+
assertThat(response.status()).isEqualTo(HttpStatus.OK);
275+
assertThat(response.headers().get(HttpHeaderNames.CONTENT_TYPE))
276+
.isEqualTo("text/plain; version=0.0.4; charset=utf-8");
277+
assertThat(response.contentUtf8())
278+
.isEqualTo(
279+
"# HELP grpc_name_unit_total long_description\n"
280+
+ "# TYPE grpc_name_unit_total counter\n"
281+
+ "grpc_name_unit_total{kp=\"vp\",otel_scope_name=\"grpc\",otel_scope_version=\"version\"} 5.0\n"
282+
+ "# HELP http_name_unit_total double_description\n"
283+
+ "# TYPE http_name_unit_total counter\n"
284+
+ "http_name_unit_total{kp=\"vp\",otel_scope_name=\"http\",otel_scope_version=\"version\"} 3.5\n"
285+
+ "# TYPE target_info gauge\n"
286+
+ "target_info{kr=\"vr\"} 1\n");
287+
}
288+
}
289+
248290
@SuppressWarnings("resource")
249291
@Test
250292
void fetchPrometheusCompressed() throws IOException {
@@ -275,7 +317,7 @@ void fetchPrometheusCompressed() throws IOException {
275317
@SuppressWarnings("resource")
276318
@Test
277319
void fetchHead() {
278-
AggregatedHttpResponse response = client.head("/").aggregate().join();
320+
AggregatedHttpResponse response = client.head("/metrics").aggregate().join();
279321
assertThat(response.status()).isEqualTo(HttpStatus.OK);
280322
assertThat(response.headers().get(HttpHeaderNames.CONTENT_TYPE))
281323
.isEqualTo("text/plain; version=0.0.4; charset=utf-8");

0 commit comments

Comments
 (0)