Make /metrics the only Prometheus metrics endpoint (#6476)
Signed-off-by: Fabian Stäber <fabian@fstab.de> Co-authored-by: Jack Berg <jberg@newrelic.com>
This commit is contained in:
parent
0f99d70d98
commit
7da7037717
|
@ -10,6 +10,7 @@
|
||||||
|
|
||||||
package io.opentelemetry.exporter.prometheus;
|
package io.opentelemetry.exporter.prometheus;
|
||||||
|
|
||||||
|
import com.sun.net.httpserver.HttpHandler;
|
||||||
import io.opentelemetry.sdk.common.CompletableResultCode;
|
import io.opentelemetry.sdk.common.CompletableResultCode;
|
||||||
import io.opentelemetry.sdk.common.export.MemoryMode;
|
import io.opentelemetry.sdk.common.export.MemoryMode;
|
||||||
import io.opentelemetry.sdk.internal.DaemonThreadFactory;
|
import io.opentelemetry.sdk.internal.DaemonThreadFactory;
|
||||||
|
@ -18,7 +19,6 @@ import io.opentelemetry.sdk.metrics.data.AggregationTemporality;
|
||||||
import io.opentelemetry.sdk.metrics.export.CollectionRegistration;
|
import io.opentelemetry.sdk.metrics.export.CollectionRegistration;
|
||||||
import io.opentelemetry.sdk.metrics.export.MetricReader;
|
import io.opentelemetry.sdk.metrics.export.MetricReader;
|
||||||
import io.prometheus.metrics.exporter.httpserver.HTTPServer;
|
import io.prometheus.metrics.exporter.httpserver.HTTPServer;
|
||||||
import io.prometheus.metrics.exporter.httpserver.MetricsHandler;
|
|
||||||
import io.prometheus.metrics.model.registry.PrometheusRegistry;
|
import io.prometheus.metrics.model.registry.PrometheusRegistry;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.UncheckedIOException;
|
import java.io.UncheckedIOException;
|
||||||
|
@ -64,7 +64,8 @@ public final class PrometheusHttpServer implements MetricReader {
|
||||||
PrometheusRegistry prometheusRegistry,
|
PrometheusRegistry prometheusRegistry,
|
||||||
boolean otelScopeEnabled,
|
boolean otelScopeEnabled,
|
||||||
@Nullable Predicate<String> allowedResourceAttributesFilter,
|
@Nullable Predicate<String> allowedResourceAttributesFilter,
|
||||||
MemoryMode memoryMode) {
|
MemoryMode memoryMode,
|
||||||
|
@Nullable HttpHandler defaultHandler) {
|
||||||
this.builder = builder;
|
this.builder = builder;
|
||||||
this.prometheusMetricReader =
|
this.prometheusMetricReader =
|
||||||
new PrometheusMetricReader(otelScopeEnabled, allowedResourceAttributesFilter);
|
new PrometheusMetricReader(otelScopeEnabled, allowedResourceAttributesFilter);
|
||||||
|
@ -86,7 +87,7 @@ public final class PrometheusHttpServer implements MetricReader {
|
||||||
.port(port)
|
.port(port)
|
||||||
.executorService(executor)
|
.executorService(executor)
|
||||||
.registry(prometheusRegistry)
|
.registry(prometheusRegistry)
|
||||||
.defaultHandler(new MetricsHandler(prometheusRegistry))
|
.defaultHandler(defaultHandler)
|
||||||
.buildAndStart();
|
.buildAndStart();
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
throw new UncheckedIOException("Could not create Prometheus HTTP server", e);
|
throw new UncheckedIOException("Could not create Prometheus HTTP server", e);
|
||||||
|
|
|
@ -8,6 +8,7 @@ package io.opentelemetry.exporter.prometheus;
|
||||||
import static io.opentelemetry.api.internal.Utils.checkArgument;
|
import static io.opentelemetry.api.internal.Utils.checkArgument;
|
||||||
import static java.util.Objects.requireNonNull;
|
import static java.util.Objects.requireNonNull;
|
||||||
|
|
||||||
|
import com.sun.net.httpserver.HttpHandler;
|
||||||
import io.opentelemetry.sdk.common.export.MemoryMode;
|
import io.opentelemetry.sdk.common.export.MemoryMode;
|
||||||
import io.prometheus.metrics.model.registry.PrometheusRegistry;
|
import io.prometheus.metrics.model.registry.PrometheusRegistry;
|
||||||
import java.util.concurrent.ExecutorService;
|
import java.util.concurrent.ExecutorService;
|
||||||
|
@ -29,6 +30,7 @@ public final class PrometheusHttpServerBuilder {
|
||||||
@Nullable private Predicate<String> allowedResourceAttributesFilter;
|
@Nullable private Predicate<String> allowedResourceAttributesFilter;
|
||||||
@Nullable private ExecutorService executor;
|
@Nullable private ExecutorService executor;
|
||||||
private MemoryMode memoryMode = DEFAULT_MEMORY_MODE;
|
private MemoryMode memoryMode = DEFAULT_MEMORY_MODE;
|
||||||
|
@Nullable private HttpHandler defaultHandler;
|
||||||
|
|
||||||
PrometheusHttpServerBuilder() {}
|
PrometheusHttpServerBuilder() {}
|
||||||
|
|
||||||
|
@ -107,6 +109,23 @@ public final class PrometheusHttpServerBuilder {
|
||||||
return this;
|
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
|
* Returns a new {@link PrometheusHttpServer} with the configuration of this builder which can be
|
||||||
* registered with a {@link io.opentelemetry.sdk.metrics.SdkMeterProvider}.
|
* registered with a {@link io.opentelemetry.sdk.metrics.SdkMeterProvider}.
|
||||||
|
@ -120,6 +139,7 @@ public final class PrometheusHttpServerBuilder {
|
||||||
prometheusRegistry,
|
prometheusRegistry,
|
||||||
otelScopeEnabled,
|
otelScopeEnabled,
|
||||||
allowedResourceAttributesFilter,
|
allowedResourceAttributesFilter,
|
||||||
memoryMode);
|
memoryMode,
|
||||||
|
defaultHandler);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -35,6 +35,7 @@ import io.opentelemetry.sdk.metrics.internal.data.ImmutableMetricData;
|
||||||
import io.opentelemetry.sdk.metrics.internal.data.ImmutableSumData;
|
import io.opentelemetry.sdk.metrics.internal.data.ImmutableSumData;
|
||||||
import io.opentelemetry.sdk.resources.Resource;
|
import io.opentelemetry.sdk.resources.Resource;
|
||||||
import io.prometheus.metrics.exporter.httpserver.HTTPServer;
|
import io.prometheus.metrics.exporter.httpserver.HTTPServer;
|
||||||
|
import io.prometheus.metrics.exporter.httpserver.MetricsHandler;
|
||||||
import io.prometheus.metrics.model.registry.PrometheusRegistry;
|
import io.prometheus.metrics.model.registry.PrometheusRegistry;
|
||||||
import java.io.ByteArrayInputStream;
|
import java.io.ByteArrayInputStream;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
@ -229,7 +230,7 @@ class PrometheusHttpServerTest {
|
||||||
void fetchFiltered() {
|
void fetchFiltered() {
|
||||||
AggregatedHttpResponse response =
|
AggregatedHttpResponse response =
|
||||||
client
|
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()
|
.aggregate()
|
||||||
.join();
|
.join();
|
||||||
assertThat(response.status()).isEqualTo(HttpStatus.OK);
|
assertThat(response.status()).isEqualTo(HttpStatus.OK);
|
||||||
|
@ -245,6 +246,47 @@ class PrometheusHttpServerTest {
|
||||||
+ "target_info{kr=\"vr\"} 1\n");
|
+ "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")
|
@SuppressWarnings("resource")
|
||||||
@Test
|
@Test
|
||||||
void fetchPrometheusCompressed() throws IOException {
|
void fetchPrometheusCompressed() throws IOException {
|
||||||
|
@ -275,7 +317,7 @@ class PrometheusHttpServerTest {
|
||||||
@SuppressWarnings("resource")
|
@SuppressWarnings("resource")
|
||||||
@Test
|
@Test
|
||||||
void fetchHead() {
|
void fetchHead() {
|
||||||
AggregatedHttpResponse response = client.head("/").aggregate().join();
|
AggregatedHttpResponse response = client.head("/metrics").aggregate().join();
|
||||||
assertThat(response.status()).isEqualTo(HttpStatus.OK);
|
assertThat(response.status()).isEqualTo(HttpStatus.OK);
|
||||||
assertThat(response.headers().get(HttpHeaderNames.CONTENT_TYPE))
|
assertThat(response.headers().get(HttpHeaderNames.CONTENT_TYPE))
|
||||||
.isEqualTo("text/plain; version=0.0.4; charset=utf-8");
|
.isEqualTo("text/plain; version=0.0.4; charset=utf-8");
|
||||||
|
|
Loading…
Reference in New Issue