Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Make /metrics the only Prometheus metrics endpoint #6476

Merged
merged 3 commits into from
May 29, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@

package io.opentelemetry.exporter.prometheus;

import com.sun.net.httpserver.HttpHandler;
import io.opentelemetry.sdk.common.CompletableResultCode;
import io.opentelemetry.sdk.common.export.MemoryMode;
import io.opentelemetry.sdk.internal.DaemonThreadFactory;
Expand All @@ -18,7 +19,6 @@
import io.opentelemetry.sdk.metrics.export.CollectionRegistration;
import io.opentelemetry.sdk.metrics.export.MetricReader;
import io.prometheus.metrics.exporter.httpserver.HTTPServer;
import io.prometheus.metrics.exporter.httpserver.MetricsHandler;
import io.prometheus.metrics.model.registry.PrometheusRegistry;
import java.io.IOException;
import java.io.UncheckedIOException;
Expand Down Expand Up @@ -64,7 +64,8 @@ public static PrometheusHttpServerBuilder builder() {
PrometheusRegistry prometheusRegistry,
boolean otelScopeEnabled,
@Nullable Predicate<String> allowedResourceAttributesFilter,
MemoryMode memoryMode) {
MemoryMode memoryMode,
@Nullable HttpHandler defaultHandler) {
this.builder = builder;
this.prometheusMetricReader =
new PrometheusMetricReader(otelScopeEnabled, allowedResourceAttributesFilter);
Expand All @@ -86,7 +87,7 @@ public static PrometheusHttpServerBuilder builder() {
.port(port)
.executorService(executor)
.registry(prometheusRegistry)
.defaultHandler(new MetricsHandler(prometheusRegistry))
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's add a configuration option for this to PrometheusHttpServerBuilder:

public PrometheuesHttpServerBuilder setDefaultHandler(com.sun.net.httpserver.HttpHandler defaultHandler)

This will allow users to depend on this behavior to restore the current behavior (although it requires programmatic configuration) while aligning with standard prometheus client library behavior.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I pushed a commit to add this but it broke the animalsniffer check because com.sun.net.httpserver.HttpHandler isn't available in the android API. Prometheus exporter doesn't need to support android environments, and doesn't work on them currently despite animalsniffer passing (animalsniffer apparently can't detect usage of unsupported APIs in dependencies).

I opened #6478 to separately address this. Can merge that, then merge main into this branch and merge this PR.

.defaultHandler(defaultHandler)
.buildAndStart();
} catch (IOException e) {
throw new UncheckedIOException("Could not create Prometheus HTTP server", e);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
import static io.opentelemetry.api.internal.Utils.checkArgument;
import static java.util.Objects.requireNonNull;

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

PrometheusHttpServerBuilder() {}

Expand Down Expand Up @@ -107,6 +109,23 @@ public PrometheusHttpServerBuilder setMemoryMode(MemoryMode memoryMode) {
return this;
}

/**
* Override the default handler for serving the "/", "/**" endpoint.
*
* <p>This can be used to serve metrics on additional paths besides the default "/metrics". For
* example: <code>
* PrometheusHttpServer.builder()
* .setPrometheusRegistry(prometheusRegistry)
* .setDefaultHandler(new MetricsHandler(prometheusRegistry))
* .build()
* </code>
*/
public PrometheusHttpServerBuilder setDefaultHandler(HttpHandler defaultHandler) {
requireNonNull(defaultHandler, "defaultHandler");
this.defaultHandler = defaultHandler;
return this;
}

/**
* Returns a new {@link PrometheusHttpServer} with the configuration of this builder which can be
* registered with a {@link io.opentelemetry.sdk.metrics.SdkMeterProvider}.
Expand All @@ -120,6 +139,7 @@ public PrometheusHttpServer build() {
prometheusRegistry,
otelScopeEnabled,
allowedResourceAttributesFilter,
memoryMode);
memoryMode,
defaultHandler);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@
import io.opentelemetry.sdk.metrics.internal.data.ImmutableSumData;
import io.opentelemetry.sdk.resources.Resource;
import io.prometheus.metrics.exporter.httpserver.HTTPServer;
import io.prometheus.metrics.exporter.httpserver.MetricsHandler;
import io.prometheus.metrics.model.registry.PrometheusRegistry;
import java.io.ByteArrayInputStream;
import java.io.IOException;
Expand Down Expand Up @@ -229,7 +230,7 @@ void fetchOpenMetrics() {
void fetchFiltered() {
AggregatedHttpResponse response =
client
.get("/?name[]=grpc_name_unit_total&name[]=bears_total&name[]=target_info")
.get("/metrics?name[]=grpc_name_unit_total&name[]=bears_total&name[]=target_info")
.aggregate()
.join();
assertThat(response.status()).isEqualTo(HttpStatus.OK);
Expand All @@ -245,6 +246,47 @@ void fetchFiltered() {
+ "target_info{kr=\"vr\"} 1\n");
}

@Test
void fetchOverrideDefaultHandler() {
PrometheusRegistry registry = new PrometheusRegistry();
try (PrometheusHttpServer prometheusServer =
PrometheusHttpServer.builder()
.setHost("localhost")
.setPort(0)
.setPrometheusRegistry(registry)
// Set the default handler to serve metrics on /**
.setDefaultHandler(new MetricsHandler(registry))
.build()) {
prometheusServer.register(
new CollectionRegistration() {
@Override
public Collection<MetricData> collectAllMetrics() {
return metricData.get();
}
});
WebClient client =
WebClient.builder("http://localhost:" + prometheusServer.getAddress().getPort())
.decorator(RetryingClient.newDecorator(RetryRule.failsafe()))
.build();

// Fetch metrics from / instead of /metrics
AggregatedHttpResponse response = client.get("/").aggregate().join();
assertThat(response.status()).isEqualTo(HttpStatus.OK);
assertThat(response.headers().get(HttpHeaderNames.CONTENT_TYPE))
.isEqualTo("text/plain; version=0.0.4; charset=utf-8");
assertThat(response.contentUtf8())
.isEqualTo(
"# HELP grpc_name_unit_total long_description\n"
+ "# TYPE grpc_name_unit_total counter\n"
+ "grpc_name_unit_total{kp=\"vp\",otel_scope_name=\"grpc\",otel_scope_version=\"version\"} 5.0\n"
+ "# HELP http_name_unit_total double_description\n"
+ "# TYPE http_name_unit_total counter\n"
+ "http_name_unit_total{kp=\"vp\",otel_scope_name=\"http\",otel_scope_version=\"version\"} 3.5\n"
+ "# TYPE target_info gauge\n"
+ "target_info{kr=\"vr\"} 1\n");
}
}

@SuppressWarnings("resource")
@Test
void fetchPrometheusCompressed() throws IOException {
Expand Down Expand Up @@ -275,7 +317,7 @@ void fetchPrometheusCompressed() throws IOException {
@SuppressWarnings("resource")
@Test
void fetchHead() {
AggregatedHttpResponse response = client.head("/").aggregate().join();
AggregatedHttpResponse response = client.head("/metrics").aggregate().join();
assertThat(response.status()).isEqualTo(HttpStatus.OK);
assertThat(response.headers().get(HttpHeaderNames.CONTENT_TYPE))
.isEqualTo("text/plain; version=0.0.4; charset=utf-8");
Expand Down
Loading