diff --git a/core/build.gradle b/core/build.gradle
index 4741aa3b4f2..18a23cbf366 100644
--- a/core/build.gradle
+++ b/core/build.gradle
@@ -105,9 +105,9 @@ dependencies {
// Micrometer and other metric-related stuff
api libs.micrometer.core
- optionalApi libs.micrometer.prometheus
+ optionalApi libs.micrometer.prometheus.legacy
optionalApi libs.dropwizard.metrics.core
- optionalApi libs.prometheus
+ optionalApi libs.prometheus.legacy
// Netty
api libs.netty.transport
@@ -154,6 +154,8 @@ dependencies {
// Jetty, for testing interoperability with other servers.
testImplementation libs.jetty94.webapp
+ testImplementation project(':prometheus1')
+
// Brotli
implementation libs.brotli4j
optionalImplementation libs.brotli4j.linux
diff --git a/core/src/main/java/com/linecorp/armeria/common/metric/PrometheusMeterRegistries.java b/core/src/main/java/com/linecorp/armeria/common/metric/PrometheusMeterRegistries.java
index 54ec2e0588f..07b62340472 100644
--- a/core/src/main/java/com/linecorp/armeria/common/metric/PrometheusMeterRegistries.java
+++ b/core/src/main/java/com/linecorp/armeria/common/metric/PrometheusMeterRegistries.java
@@ -27,7 +27,10 @@
/**
* Provides the convenient factory methods for {@link PrometheusMeterRegistry} with more sensible defaults for
* {@link NamingConvention}.
+ *
+ * @deprecated Use {@code PrometheusMeterRegistries} in {@code armeria-prometheus1} module instead.
*/
+@Deprecated
public final class PrometheusMeterRegistries {
private static final PrometheusMeterRegistry defaultRegistry =
diff --git a/core/src/main/java/com/linecorp/armeria/server/metric/PrometheusExpositionService.java b/core/src/main/java/com/linecorp/armeria/server/metric/PrometheusExpositionService.java
index 311acd83841..93c911eff76 100644
--- a/core/src/main/java/com/linecorp/armeria/server/metric/PrometheusExpositionService.java
+++ b/core/src/main/java/com/linecorp/armeria/server/metric/PrometheusExpositionService.java
@@ -42,7 +42,10 @@
/**
* Exposes Prometheus metrics in text
* format 0.0.4 or OpenMetrics format.
+ *
+ * @deprecated Use {@code PrometheusExpositionService} in {@code armeria-prometheus1} module instead.
*/
+@Deprecated
public final class PrometheusExpositionService extends AbstractHttpService implements TransientHttpService {
/**
@@ -106,7 +109,7 @@ protected HttpResponse doGet(ServiceRequestContext ctx, HttpRequest req) throws
final ByteBuf buffer = ctx.alloc().buffer();
boolean success = false;
try (ByteBufOutputStream byteBufOutputStream = new ByteBufOutputStream(buffer);
- OutputStreamWriter writer = new OutputStreamWriter(byteBufOutputStream)) {
+ OutputStreamWriter writer = new OutputStreamWriter(byteBufOutputStream)) {
TextFormat.writeFormat(format, writer, collectorRegistry.metricFamilySamples());
success = true;
} finally {
diff --git a/core/src/main/java/com/linecorp/armeria/server/metric/PrometheusExpositionServiceBuilder.java b/core/src/main/java/com/linecorp/armeria/server/metric/PrometheusExpositionServiceBuilder.java
index 2335e843502..d4ac6c1c8fa 100644
--- a/core/src/main/java/com/linecorp/armeria/server/metric/PrometheusExpositionServiceBuilder.java
+++ b/core/src/main/java/com/linecorp/armeria/server/metric/PrometheusExpositionServiceBuilder.java
@@ -25,7 +25,10 @@
/**
* Builds a {@link PrometheusExpositionService}.
+ *
+ * @deprecated Use {@code PrometheusExpositionServiceBuilder} in {@code armeria-prometheus1} module instead.
*/
+@Deprecated
public final class PrometheusExpositionServiceBuilder implements TransientServiceBuilder {
private final CollectorRegistry collectorRegistry;
diff --git a/core/src/test/java/com/linecorp/armeria/client/ConnectionPoolCollectingMetricTest.java b/core/src/test/java/com/linecorp/armeria/client/ConnectionPoolCollectingMetricTest.java
index ce0e33e7b4a..ffd06347c00 100644
--- a/core/src/test/java/com/linecorp/armeria/client/ConnectionPoolCollectingMetricTest.java
+++ b/core/src/test/java/com/linecorp/armeria/client/ConnectionPoolCollectingMetricTest.java
@@ -24,7 +24,7 @@
import com.linecorp.armeria.common.SessionProtocol;
import com.linecorp.armeria.common.metric.MoreMeters;
-import com.linecorp.armeria.common.metric.PrometheusMeterRegistries;
+import com.linecorp.armeria.common.prometheus.PrometheusMeterRegistries;
import io.micrometer.core.instrument.MeterRegistry;
import io.netty.util.AttributeMap;
diff --git a/core/src/test/java/com/linecorp/armeria/client/RefreshingAddressResolverTest.java b/core/src/test/java/com/linecorp/armeria/client/RefreshingAddressResolverTest.java
index ef705d04d68..3a524b90d2d 100644
--- a/core/src/test/java/com/linecorp/armeria/client/RefreshingAddressResolverTest.java
+++ b/core/src/test/java/com/linecorp/armeria/client/RefreshingAddressResolverTest.java
@@ -52,7 +52,7 @@
import com.linecorp.armeria.client.endpoint.dns.TestDnsServer;
import com.linecorp.armeria.client.retry.Backoff;
import com.linecorp.armeria.common.annotation.Nullable;
-import com.linecorp.armeria.common.metric.PrometheusMeterRegistries;
+import com.linecorp.armeria.common.prometheus.PrometheusMeterRegistries;
import com.linecorp.armeria.internal.client.dns.ByteArrayDnsRecord;
import com.linecorp.armeria.internal.client.dns.DnsQuestionWithoutTrailingDot;
import com.linecorp.armeria.testing.junit5.common.EventLoopExtension;
diff --git a/core/src/test/java/com/linecorp/armeria/client/circuitbreaker/MetricCollectingCircuitBreakerListenerTest.java b/core/src/test/java/com/linecorp/armeria/client/circuitbreaker/MetricCollectingCircuitBreakerListenerTest.java
index b6c9e14f1cb..33af877c7f9 100644
--- a/core/src/test/java/com/linecorp/armeria/client/circuitbreaker/MetricCollectingCircuitBreakerListenerTest.java
+++ b/core/src/test/java/com/linecorp/armeria/client/circuitbreaker/MetricCollectingCircuitBreakerListenerTest.java
@@ -21,7 +21,7 @@
import org.junit.jupiter.api.Test;
import com.linecorp.armeria.common.metric.MoreMeters;
-import com.linecorp.armeria.common.metric.PrometheusMeterRegistries;
+import com.linecorp.armeria.common.prometheus.PrometheusMeterRegistries;
import io.micrometer.core.instrument.MeterRegistry;
diff --git a/core/src/test/java/com/linecorp/armeria/client/endpoint/healthcheck/HealthCheckedEndpointGroupIntegrationTest.java b/core/src/test/java/com/linecorp/armeria/client/endpoint/healthcheck/HealthCheckedEndpointGroupIntegrationTest.java
index 0785b55ec83..bab65928ada 100644
--- a/core/src/test/java/com/linecorp/armeria/client/endpoint/healthcheck/HealthCheckedEndpointGroupIntegrationTest.java
+++ b/core/src/test/java/com/linecorp/armeria/client/endpoint/healthcheck/HealthCheckedEndpointGroupIntegrationTest.java
@@ -36,7 +36,7 @@
import com.linecorp.armeria.client.logging.LoggingClient;
import com.linecorp.armeria.common.SessionProtocol;
import com.linecorp.armeria.common.metric.MoreMeters;
-import com.linecorp.armeria.common.metric.PrometheusMeterRegistries;
+import com.linecorp.armeria.common.prometheus.PrometheusMeterRegistries;
import com.linecorp.armeria.server.ServerBuilder;
import com.linecorp.armeria.server.healthcheck.HealthCheckService;
import com.linecorp.armeria.testing.junit5.server.ServerExtension;
diff --git a/core/src/test/java/com/linecorp/armeria/common/metric/DropwizardMeterRegistriesTest.java b/core/src/test/java/com/linecorp/armeria/common/metric/DropwizardMeterRegistriesTest.java
index 0bdf4d28ea4..8727bf0d824 100644
--- a/core/src/test/java/com/linecorp/armeria/common/metric/DropwizardMeterRegistriesTest.java
+++ b/core/src/test/java/com/linecorp/armeria/common/metric/DropwizardMeterRegistriesTest.java
@@ -21,23 +21,26 @@
import static org.assertj.core.api.Assertions.fail;
import java.time.Duration;
-import java.util.Enumeration;
+import java.util.List;
import java.util.Map;
import java.util.concurrent.TimeUnit;
import org.junit.jupiter.api.Test;
import com.codahale.metrics.MetricRegistry;
-import com.google.common.collect.ImmutableList;
+
+import com.linecorp.armeria.common.prometheus.PrometheusMeterRegistries;
import io.micrometer.core.instrument.Clock;
import io.micrometer.core.instrument.DistributionSummary;
import io.micrometer.core.instrument.Timer;
import io.micrometer.core.instrument.composite.CompositeMeterRegistry;
import io.micrometer.core.instrument.dropwizard.DropwizardMeterRegistry;
-import io.micrometer.prometheus.PrometheusMeterRegistry;
-import io.prometheus.client.Collector.MetricFamilySamples;
-import io.prometheus.client.Collector.MetricFamilySamples.Sample;
+import io.micrometer.prometheusmetrics.PrometheusMeterRegistry;
+import io.prometheus.metrics.model.snapshots.DataPointSnapshot;
+import io.prometheus.metrics.model.snapshots.MetricMetadata;
+import io.prometheus.metrics.model.snapshots.MetricSnapshot;
+import io.prometheus.metrics.model.snapshots.SummarySnapshot.SummaryDataPointSnapshot;
class DropwizardMeterRegistriesTest {
@Test
@@ -131,20 +134,28 @@ void filteredGaugesDoNotAffectOthers() {
assertThat(dropwizard.getDropwizardRegistry().getMetrics()).containsOnlyKeys("summary");
// Make sure Prometheus registry collects all samples.
- final MetricFamilySamples prometheusSamples = findPrometheusSample(prometheus, "summary");
- assertThat(prometheusSamples.samples).containsExactly(
- new Sample("summary", ImmutableList.of("quantile"), ImmutableList.of("0.5"), 42),
- new Sample("summary", ImmutableList.of("quantile"), ImmutableList.of("0.99"), 42),
- new Sample("summary_count", ImmutableList.of(), ImmutableList.of(), 1),
- new Sample("summary_sum", ImmutableList.of(), ImmutableList.of(), 42));
+ final List extends DataPointSnapshot> dataPointSnapshots =
+ findPrometheusDataPointSnapshot(prometheus, "summary");
+ assertThat(dataPointSnapshots.size()).isOne();
+ final DataPointSnapshot snapshot = dataPointSnapshots.get(0);
+ assertThat(snapshot).isInstanceOf(SummaryDataPointSnapshot.class);
+ // SummaryDataPointSnapshot and its values do not override equals().
+ final SummaryDataPointSnapshot summarySnapshot = (SummaryDataPointSnapshot) snapshot;
+ assertThat(summarySnapshot.getCount()).isOne();
+ assertThat(summarySnapshot.getSum()).isEqualTo(42.0);
+ assertThat(summarySnapshot.getQuantiles().size()).isEqualTo(2);
+ assertThat(summarySnapshot.getQuantiles().get(0).getQuantile()).isEqualTo(0.5);
+ assertThat(summarySnapshot.getQuantiles().get(0).getValue()).isEqualTo(42.0);
+ assertThat(summarySnapshot.getQuantiles().get(1).getQuantile()).isEqualTo(0.99);
+ assertThat(summarySnapshot.getQuantiles().get(1).getValue()).isEqualTo(42.0);
}
- private static MetricFamilySamples findPrometheusSample(PrometheusMeterRegistry registry, String name) {
- for (final Enumeration e = registry.getPrometheusRegistry().metricFamilySamples();
- e.hasMoreElements();) {
- final MetricFamilySamples samples = e.nextElement();
- if (name.equals(samples.name)) {
- return samples;
+ private static List extends DataPointSnapshot> findPrometheusDataPointSnapshot(
+ PrometheusMeterRegistry registry, String name) {
+ for (final MetricSnapshot snapshot : registry.getPrometheusRegistry().scrape()) {
+ final MetricMetadata metadata = snapshot.getMetadata();
+ if (name.equals(metadata.getName())) {
+ return snapshot.getDataPoints();
}
}
@@ -152,3 +163,4 @@ private static MetricFamilySamples findPrometheusSample(PrometheusMeterRegistry
throw new Error(); // Never reaches here.
}
}
+
diff --git a/core/src/test/java/com/linecorp/armeria/common/metric/MeterIdPrefixFunctionTest.java b/core/src/test/java/com/linecorp/armeria/common/metric/MeterIdPrefixFunctionTest.java
index 916e2e0c382..96ec3f8c47d 100644
--- a/core/src/test/java/com/linecorp/armeria/common/metric/MeterIdPrefixFunctionTest.java
+++ b/core/src/test/java/com/linecorp/armeria/common/metric/MeterIdPrefixFunctionTest.java
@@ -31,6 +31,7 @@
import com.linecorp.armeria.common.annotation.Nullable;
import com.linecorp.armeria.common.logging.RequestLog;
import com.linecorp.armeria.common.logging.RequestOnlyLog;
+import com.linecorp.armeria.common.prometheus.PrometheusMeterRegistries;
import com.linecorp.armeria.server.ServiceRequestContext;
import io.micrometer.core.instrument.MeterRegistry;
diff --git a/core/src/test/java/com/linecorp/armeria/internal/common/metric/CaffeineMetricSupportTest.java b/core/src/test/java/com/linecorp/armeria/internal/common/metric/CaffeineMetricSupportTest.java
index e7de85d054c..7d7df459466 100644
--- a/core/src/test/java/com/linecorp/armeria/internal/common/metric/CaffeineMetricSupportTest.java
+++ b/core/src/test/java/com/linecorp/armeria/internal/common/metric/CaffeineMetricSupportTest.java
@@ -39,7 +39,7 @@
import com.linecorp.armeria.common.metric.MeterIdPrefix;
import com.linecorp.armeria.common.metric.MoreMeters;
-import com.linecorp.armeria.common.metric.PrometheusMeterRegistries;
+import com.linecorp.armeria.common.prometheus.PrometheusMeterRegistries;
import io.micrometer.core.instrument.MeterRegistry;
diff --git a/core/src/test/java/com/linecorp/armeria/internal/common/metric/MicrometerUtilTest.java b/core/src/test/java/com/linecorp/armeria/internal/common/metric/MicrometerUtilTest.java
index 021e57bb1f3..6b376cfb930 100644
--- a/core/src/test/java/com/linecorp/armeria/internal/common/metric/MicrometerUtilTest.java
+++ b/core/src/test/java/com/linecorp/armeria/internal/common/metric/MicrometerUtilTest.java
@@ -22,7 +22,7 @@
import org.junit.jupiter.api.Test;
import com.linecorp.armeria.common.metric.MeterIdPrefix;
-import com.linecorp.armeria.common.metric.PrometheusMeterRegistries;
+import com.linecorp.armeria.common.prometheus.PrometheusMeterRegistries;
import io.micrometer.core.instrument.MeterRegistry;
diff --git a/core/src/test/java/com/linecorp/armeria/internal/common/metric/RequestMetricSupportTest.java b/core/src/test/java/com/linecorp/armeria/internal/common/metric/RequestMetricSupportTest.java
index 19a7557b1d6..5c8c840ca80 100644
--- a/core/src/test/java/com/linecorp/armeria/internal/common/metric/RequestMetricSupportTest.java
+++ b/core/src/test/java/com/linecorp/armeria/internal/common/metric/RequestMetricSupportTest.java
@@ -33,7 +33,7 @@
import com.linecorp.armeria.common.SuccessFunction;
import com.linecorp.armeria.common.logging.ClientConnectionTimings;
import com.linecorp.armeria.common.metric.MeterIdPrefixFunction;
-import com.linecorp.armeria.common.metric.PrometheusMeterRegistries;
+import com.linecorp.armeria.common.prometheus.PrometheusMeterRegistries;
import com.linecorp.armeria.common.util.SafeCloseable;
import com.linecorp.armeria.internal.testing.ImmediateEventLoop;
import com.linecorp.armeria.server.RequestTimeoutException;
diff --git a/core/src/test/java/com/linecorp/armeria/server/ServerBuilderTest.java b/core/src/test/java/com/linecorp/armeria/server/ServerBuilderTest.java
index 6b17f7d6b50..fba4af0b045 100644
--- a/core/src/test/java/com/linecorp/armeria/server/ServerBuilderTest.java
+++ b/core/src/test/java/com/linecorp/armeria/server/ServerBuilderTest.java
@@ -52,7 +52,7 @@
import com.linecorp.armeria.common.HttpResponse;
import com.linecorp.armeria.common.HttpStatus;
import com.linecorp.armeria.common.SessionProtocol;
-import com.linecorp.armeria.common.metric.PrometheusMeterRegistries;
+import com.linecorp.armeria.common.prometheus.PrometheusMeterRegistries;
import com.linecorp.armeria.common.util.DomainSocketAddress;
import com.linecorp.armeria.common.util.TransportType;
import com.linecorp.armeria.internal.common.util.MinifiedBouncyCastleProvider;
@@ -61,7 +61,7 @@
import com.linecorp.armeria.testing.junit5.server.ServerExtension;
import io.micrometer.core.instrument.Metrics;
-import io.micrometer.prometheus.PrometheusMeterRegistry;
+import io.micrometer.prometheusmetrics.PrometheusMeterRegistry;
import io.netty.channel.ChannelOption;
import io.netty.handler.ssl.SslContextBuilder;
import reactor.core.scheduler.Schedulers;
diff --git a/core/src/test/java/com/linecorp/armeria/server/ServerListenerTest.java b/core/src/test/java/com/linecorp/armeria/server/ServerListenerTest.java
index 4fb22ecfe91..dc6949a12d7 100644
--- a/core/src/test/java/com/linecorp/armeria/server/ServerListenerTest.java
+++ b/core/src/test/java/com/linecorp/armeria/server/ServerListenerTest.java
@@ -34,7 +34,7 @@
import org.junit.jupiter.params.provider.ArgumentsSource;
import com.linecorp.armeria.common.HttpResponse;
-import com.linecorp.armeria.common.metric.PrometheusMeterRegistries;
+import com.linecorp.armeria.common.prometheus.PrometheusMeterRegistries;
import com.linecorp.armeria.testing.junit5.server.ServerExtension;
class ServerListenerTest {
diff --git a/core/src/test/java/com/linecorp/armeria/server/ServerTest.java b/core/src/test/java/com/linecorp/armeria/server/ServerTest.java
index 4c0b7814fbe..54886bec62d 100644
--- a/core/src/test/java/com/linecorp/armeria/server/ServerTest.java
+++ b/core/src/test/java/com/linecorp/armeria/server/ServerTest.java
@@ -69,7 +69,7 @@
import com.linecorp.armeria.common.ResponseHeaders;
import com.linecorp.armeria.common.SessionProtocol;
import com.linecorp.armeria.common.metric.MeterIdPrefix;
-import com.linecorp.armeria.common.metric.PrometheusMeterRegistries;
+import com.linecorp.armeria.common.prometheus.PrometheusMeterRegistries;
import com.linecorp.armeria.common.util.CompletionActions;
import com.linecorp.armeria.common.util.Exceptions;
import com.linecorp.armeria.common.util.ThreadFactories;
diff --git a/core/src/test/java/com/linecorp/armeria/server/ServerTlsCertificateMetricsTest.java b/core/src/test/java/com/linecorp/armeria/server/ServerTlsCertificateMetricsTest.java
index 3c2827ce166..d78ab0e4002 100644
--- a/core/src/test/java/com/linecorp/armeria/server/ServerTlsCertificateMetricsTest.java
+++ b/core/src/test/java/com/linecorp/armeria/server/ServerTlsCertificateMetricsTest.java
@@ -27,7 +27,7 @@
import org.junit.jupiter.api.Test;
import com.linecorp.armeria.common.HttpResponse;
-import com.linecorp.armeria.common.metric.PrometheusMeterRegistries;
+import com.linecorp.armeria.common.prometheus.PrometheusMeterRegistries;
import com.linecorp.armeria.internal.common.util.SelfSignedCertificate;
import io.micrometer.core.instrument.Gauge;
diff --git a/core/src/test/java/com/linecorp/armeria/server/ServerTlsHandshakeMetricsTest.java b/core/src/test/java/com/linecorp/armeria/server/ServerTlsHandshakeMetricsTest.java
index 4838d5fba48..3bf17942903 100644
--- a/core/src/test/java/com/linecorp/armeria/server/ServerTlsHandshakeMetricsTest.java
+++ b/core/src/test/java/com/linecorp/armeria/server/ServerTlsHandshakeMetricsTest.java
@@ -35,7 +35,7 @@
import com.linecorp.armeria.common.HttpStatus;
import com.linecorp.armeria.common.SessionProtocol;
import com.linecorp.armeria.common.metric.MoreMeters;
-import com.linecorp.armeria.common.metric.PrometheusMeterRegistries;
+import com.linecorp.armeria.common.prometheus.PrometheusMeterRegistries;
import com.linecorp.armeria.testing.junit5.server.ServerExtension;
import io.micrometer.core.instrument.Counter;
diff --git a/core/src/test/java/com/linecorp/armeria/server/logging/ContentPreviewerCancellationTest.java b/core/src/test/java/com/linecorp/armeria/server/logging/ContentPreviewerCancellationTest.java
index 3b6b2ea0818..f0d058e8ffd 100644
--- a/core/src/test/java/com/linecorp/armeria/server/logging/ContentPreviewerCancellationTest.java
+++ b/core/src/test/java/com/linecorp/armeria/server/logging/ContentPreviewerCancellationTest.java
@@ -39,7 +39,7 @@
import com.linecorp.armeria.common.annotation.Nullable;
import com.linecorp.armeria.common.logging.ContentPreviewerFactory;
import com.linecorp.armeria.common.metric.MeterIdPrefixFunction;
-import com.linecorp.armeria.common.metric.PrometheusMeterRegistries;
+import com.linecorp.armeria.common.prometheus.PrometheusMeterRegistries;
import com.linecorp.armeria.common.stream.CancelledSubscriptionException;
import com.linecorp.armeria.common.util.Functions;
import com.linecorp.armeria.internal.logging.ContentPreviewingUtil;
@@ -51,10 +51,10 @@
import com.linecorp.armeria.server.annotation.Get;
import com.linecorp.armeria.server.cors.CorsService;
import com.linecorp.armeria.server.metric.MetricCollectingService;
-import com.linecorp.armeria.server.metric.PrometheusExpositionService;
+import com.linecorp.armeria.server.prometheus.PrometheusExpositionService;
import com.linecorp.armeria.testing.junit5.server.ServerExtension;
-import io.micrometer.prometheus.PrometheusMeterRegistry;
+import io.micrometer.prometheusmetrics.PrometheusMeterRegistry;
class ContentPreviewerCancellationTest {
@@ -77,7 +77,8 @@ protected void configure(ServerBuilder sb) {
.newDecorator()
)
.annotatedService(new TestService())
- .service("/metrics", PrometheusExpositionService.of(registry.getPrometheusRegistry()));
+ .service("/metrics",
+ PrometheusExpositionService.of(registry.getPrometheusRegistry()));
}
};
diff --git a/core/src/test/java/com/linecorp/armeria/server/metric/MetricCollectingServiceTest.java b/core/src/test/java/com/linecorp/armeria/server/metric/MetricCollectingServiceTest.java
index 8d1e4882d90..42016b2cdf6 100644
--- a/core/src/test/java/com/linecorp/armeria/server/metric/MetricCollectingServiceTest.java
+++ b/core/src/test/java/com/linecorp/armeria/server/metric/MetricCollectingServiceTest.java
@@ -27,11 +27,11 @@
import com.linecorp.armeria.common.HttpResponse;
import com.linecorp.armeria.common.metric.MeterIdPrefixFunction;
-import com.linecorp.armeria.common.metric.PrometheusMeterRegistries;
+import com.linecorp.armeria.common.prometheus.PrometheusMeterRegistries;
import com.linecorp.armeria.server.ServerBuilder;
import com.linecorp.armeria.testing.junit5.server.ServerExtension;
-import io.micrometer.prometheus.PrometheusMeterRegistry;
+import io.micrometer.prometheusmetrics.PrometheusMeterRegistry;
class MetricCollectingServiceTest {
diff --git a/core/src/test/java/com/linecorp/armeria/server/metric/PrometheusExpositionServiceTest.java b/core/src/test/java/com/linecorp/armeria/server/metric/PrometheusExpositionServiceTest.java
index a102ac377de..62c2a9a57e3 100644
--- a/core/src/test/java/com/linecorp/armeria/server/metric/PrometheusExpositionServiceTest.java
+++ b/core/src/test/java/com/linecorp/armeria/server/metric/PrometheusExpositionServiceTest.java
@@ -130,9 +130,9 @@ class FormatTest {
void prometheusRequestsPrometheusFormat() throws InterruptedException {
final WebClient client = WebClient.of(server.httpUri());
final HttpRequest request = HttpRequest.builder()
- .get("/enabled")
- .header(HttpHeaderNames.ACCEPT, TextFormat.CONTENT_TYPE_004)
- .build();
+ .get("/enabled")
+ .header(HttpHeaderNames.ACCEPT, TextFormat.CONTENT_TYPE_004)
+ .build();
final AggregatedHttpResponse response = client.execute(request).aggregate().join();
assertThat(response.headers().get(HttpHeaderNames.CONTENT_TYPE))
.isEqualTo(TextFormat.CONTENT_TYPE_004);
@@ -142,9 +142,10 @@ void prometheusRequestsPrometheusFormat() throws InterruptedException {
void prometheusRequestsOpenMetricsFormat() throws InterruptedException {
final WebClient client = WebClient.of(server.httpUri());
final HttpRequest request = HttpRequest.builder()
- .get("/enabled")
- .header(HttpHeaderNames.ACCEPT, TextFormat.CONTENT_TYPE_OPENMETRICS_100)
- .build();
+ .get("/enabled")
+ .header(HttpHeaderNames.ACCEPT,
+ TextFormat.CONTENT_TYPE_OPENMETRICS_100)
+ .build();
final AggregatedHttpResponse response = client.execute(request).aggregate().join();
assertThat(response.headers().get(HttpHeaderNames.CONTENT_TYPE))
.isEqualTo(TextFormat.CONTENT_TYPE_OPENMETRICS_100);
diff --git a/dependencies.toml b/dependencies.toml
index 1c9c2eb275a..a4b19016f16 100644
--- a/dependencies.toml
+++ b/dependencies.toml
@@ -87,10 +87,9 @@ kubernetes-client = "6.12.1"
logback12 = "1.2.13"
logback13 = "1.3.14"
logback14 = "1.4.14"
-micrometer = "1.12.4"
+micrometer = "1.13.0"
micrometer-tracing = "1.2.4"
micrometer-docs-generator = "1.0.2"
-micrometer13 = "1.3.20"
# Don't uprade mockito to 5.x.x that requires Java 11
mockito = "4.11.0"
monix = "3.4.1"
@@ -108,7 +107,8 @@ osdetector = "1.7.3"
# Used for kubernetes-chaos-tests
picocli = "4.7.6"
proguard = "7.4.2"
-prometheus = "0.16.0"
+prometheus = "1.3.0"
+prometheus-legacy = "0.16.0"
# Ensure that we use the same Protobuf version as what gRPC depends on.
# See: https://github.com/grpc/grpc-java/blob/master/gradle/libs.versions.toml
# (Switch to the right tag and look for "protobuf".)
@@ -868,7 +868,11 @@ version.ref = "micrometer"
[libraries.micrometer-prometheus]
module = "io.micrometer:micrometer-registry-prometheus"
version.ref = "micrometer"
-javadocs = "https://www.javadoc.io/doc/io.micrometer/micrometer-registry-prometheus/1.10.8/"
+javadocs = "https://www.javadoc.io/doc/io.micrometer/micrometer-registry-prometheus/1.13.0/"
+[libraries.micrometer-prometheus-legacy]
+module = "io.micrometer:micrometer-registry-prometheus-simpleclient"
+version.ref = "micrometer"
+javadocs = "https://www.javadoc.io/doc/io.micrometer/micrometer-registry-prometheus-simpleclient/1.13.0/"
[libraries.micrometer-spring-legacy]
module = "io.micrometer:micrometer-spring-legacy"
version.ref = "micrometer"
@@ -885,17 +889,6 @@ module = "io.micrometer:micrometer-docs-generator"
version.ref = "micrometer-docs-generator"
javadocs = "https://www.javadoc.io/doc/io.micrometer/micrometer-docs-generator/1.0.2/"
-[libraries.micrometer13-core]
-module = "io.micrometer:micrometer-core"
-version.ref = "micrometer13"
-[libraries.micrometer13-prometheus]
-module = "io.micrometer:micrometer-registry-prometheus"
-version.ref = "micrometer13"
-[libraries.micrometer13-spring-legacy]
-module = "io.micrometer:micrometer-spring-legacy"
-version.ref = "micrometer13"
-exclusions = ["org.springframework:spring-web", "org.springframework:spring-webmvc"]
-
[libraries.mockito]
module = "org.mockito:mockito-core"
version.ref = "mockito"
@@ -954,10 +947,13 @@ version.ref = "netty-incubator-transport-native-io_uring"
module = "info.picocli:picocli"
version.ref = "picocli"
-[libraries.prometheus]
-module = "io.prometheus:simpleclient_common"
+[libraries.prometheus-metrics-exposition-formats]
+module = "io.prometheus:prometheus-metrics-exposition-formats"
version.ref = "prometheus"
javadocs = "https://prometheus.github.io/client_java/"
+[libraries.prometheus-legacy]
+module = "io.prometheus:simpleclient_common"
+version.ref = "prometheus-legacy"
[libraries.protobuf-java]
module = "com.google.protobuf:protobuf-java"
diff --git a/dropwizard1/build.gradle b/dropwizard1/build.gradle
index 5eaef271eb3..7c89efa5f2c 100644
--- a/dropwizard1/build.gradle
+++ b/dropwizard1/build.gradle
@@ -1,5 +1,3 @@
-final def DROPWIZARD_VERSION = '1.3.29'
-
dependencies {
implementation project(':jetty9')
// Dropwizard
diff --git a/examples/resilience4j-spring/build.gradle b/examples/resilience4j-spring/build.gradle
index 10b73bca035..fac2d70915b 100644
--- a/examples/resilience4j-spring/build.gradle
+++ b/examples/resilience4j-spring/build.gradle
@@ -1,11 +1,11 @@
dependencies {
+ implementation project(':prometheus1')
implementation project(':resilience4j2')
implementation project(':spring:boot2-starter')
runtimeOnly project(':spring:boot2-actuator-starter')
implementation libs.resilience4j.springboot2
implementation libs.spring.boot2.starter.web
implementation libs.micrometer.prometheus
- implementation libs.prometheus
implementation libs.resilience4j.micrometer
testImplementation libs.spring.boot2.starter.test
diff --git a/examples/resilience4j-spring/src/main/java/example/armeria/resilience4j/spring/ServerConfiguration.java b/examples/resilience4j-spring/src/main/java/example/armeria/resilience4j/spring/ServerConfiguration.java
index 8ae1c6ee083..2636bb829eb 100644
--- a/examples/resilience4j-spring/src/main/java/example/armeria/resilience4j/spring/ServerConfiguration.java
+++ b/examples/resilience4j-spring/src/main/java/example/armeria/resilience4j/spring/ServerConfiguration.java
@@ -4,13 +4,13 @@
import org.springframework.context.annotation.Configuration;
import com.linecorp.armeria.common.HttpResponse;
-import com.linecorp.armeria.common.metric.PrometheusMeterRegistries;
+import com.linecorp.armeria.common.prometheus.PrometheusMeterRegistries;
import com.linecorp.armeria.spring.ArmeriaServerConfigurator;
import io.micrometer.core.instrument.MeterRegistry;
import io.micrometer.core.instrument.composite.CompositeMeterRegistry;
import io.micrometer.core.instrument.simple.SimpleMeterRegistry;
-import io.micrometer.prometheus.PrometheusMeterRegistry;
+import io.micrometer.prometheusmetrics.PrometheusMeterRegistry;
@Configuration
public class ServerConfiguration {
diff --git a/grpc/build.gradle b/grpc/build.gradle
index 96985516ba8..b898b81065e 100644
--- a/grpc/build.gradle
+++ b/grpc/build.gradle
@@ -20,6 +20,7 @@ dependencies {
testImplementation(libs.gax.grpc) {
exclude group: 'com.google.protobuf', module: 'protobuf-java'
}
+ testImplementation project(':prometheus1')
testImplementation libs.grpc.okhttp
testImplementation libs.grpc.testing
testImplementation libs.micrometer.prometheus
diff --git a/grpc/src/test/java/com/linecorp/armeria/common/grpc/GrpcMeterIdPrefixFunctionTest.java b/grpc/src/test/java/com/linecorp/armeria/common/grpc/GrpcMeterIdPrefixFunctionTest.java
index 14bc6fe69da..898090c1c99 100644
--- a/grpc/src/test/java/com/linecorp/armeria/common/grpc/GrpcMeterIdPrefixFunctionTest.java
+++ b/grpc/src/test/java/com/linecorp/armeria/common/grpc/GrpcMeterIdPrefixFunctionTest.java
@@ -48,7 +48,7 @@
import com.linecorp.armeria.common.logging.RequestLogAccess;
import com.linecorp.armeria.common.logging.RequestLogProperty;
import com.linecorp.armeria.common.metric.MeterIdPrefix;
-import com.linecorp.armeria.common.metric.PrometheusMeterRegistries;
+import com.linecorp.armeria.common.prometheus.PrometheusMeterRegistries;
import com.linecorp.armeria.server.ServerBuilder;
import com.linecorp.armeria.server.grpc.GrpcService;
import com.linecorp.armeria.testing.junit5.server.ServerExtension;
@@ -58,7 +58,7 @@
import io.grpc.StatusRuntimeException;
import io.grpc.stub.StreamObserver;
import io.micrometer.core.instrument.Statistic;
-import io.micrometer.prometheus.PrometheusMeterRegistry;
+import io.micrometer.prometheusmetrics.PrometheusMeterRegistry;
import testing.grpc.EmptyProtos.Empty;
import testing.grpc.Messages.SimpleRequest;
import testing.grpc.Messages.SimpleResponse;
diff --git a/grpc/src/test/java/com/linecorp/armeria/it/grpc/GrpcMetricsIntegrationTest.java b/grpc/src/test/java/com/linecorp/armeria/it/grpc/GrpcMetricsIntegrationTest.java
index 1603df0987e..44bac4f4586 100644
--- a/grpc/src/test/java/com/linecorp/armeria/it/grpc/GrpcMetricsIntegrationTest.java
+++ b/grpc/src/test/java/com/linecorp/armeria/it/grpc/GrpcMetricsIntegrationTest.java
@@ -44,7 +44,7 @@
import com.linecorp.armeria.common.grpc.GrpcMeterIdPrefixFunction;
import com.linecorp.armeria.common.metric.MeterIdPrefix;
import com.linecorp.armeria.common.metric.MoreMeters;
-import com.linecorp.armeria.common.metric.PrometheusMeterRegistries;
+import com.linecorp.armeria.common.prometheus.PrometheusMeterRegistries;
import com.linecorp.armeria.internal.testing.GenerateNativeImageTrace;
import com.linecorp.armeria.server.ServerBuilder;
import com.linecorp.armeria.server.grpc.GrpcService;
diff --git a/it/micrometer1.3/build.gradle b/it/micrometer1.3/build.gradle
deleted file mode 100644
index c36ac1aeadf..00000000000
--- a/it/micrometer1.3/build.gradle
+++ /dev/null
@@ -1,40 +0,0 @@
-def thriftVersion = ""
-if (project.ext.targetJavaVersion >= 11) {
- thriftVersion = '0.18'
-} else {
- thriftVersion = '0.17'
-}
-
-dependencies {
- testImplementation project(":thrift$thriftVersion")
- testImplementation project(':grpc')
- testImplementation libs.dropwizard.metrics.core
- testImplementation libs.prometheus
-
- testImplementation libs.micrometer13.core
- testImplementation libs.micrometer13.prometheus
-}
-
-tasks.compileTestJava.source "${rootProject.projectDir}/core/src/test/java/com/linecorp/armeria/internal/common/metric/RequestMetricSupportTest.java",
- "${rootProject.projectDir}/thrift/src/test/java/com/linecorp/armeria/it/metric/PrometheusMetricsIntegrationTest.java",
- "${rootProject.projectDir}/thrift/src/test/java/com/linecorp/armeria/it/metric/DropwizardMetricsIntegrationTest.java",
- "${rootProject.projectDir}/grpc/src/test/java/com/linecorp/armeria/it/grpc/GrpcMetricsIntegrationTest.java"
-
-task copyThriftFiles(type: Copy) {
- dependsOn project(":thrift$thriftVersion").tasks.compileTestThrift
- dependsOn project(":thrift$thriftVersion").tasks.generateTestSources
-
- from "${rootProject.projectDir}/thrift/thrift$thriftVersion/gen-src/test/java"
- into "${project.ext.genSrcDir}/test/java"
- include '**/HelloService.java'
-}
-
-task copyGrpcFiles(type: Copy) {
- dependsOn project(':grpc').tasks.generateTestProto
-
- from "${rootProject.projectDir}/grpc/gen-src/test/java", "${rootProject.projectDir}/grpc/gen-src/test/grpc"
- into "${project.ext.genSrcDir}/test/java"
-}
-
-tasks.compileTestJava.dependsOn(copyThriftFiles)
-tasks.compileTestJava.dependsOn(copyGrpcFiles)
diff --git a/it/thrift0.9.1/build.gradle b/it/thrift0.9.1/build.gradle
index 5ce82e82cc7..71f2c6636ad 100644
--- a/it/thrift0.9.1/build.gradle
+++ b/it/thrift0.9.1/build.gradle
@@ -15,7 +15,6 @@ dependencies {
// Dropwizard and Prometheus, for testing metrics integration
testImplementation libs.dropwizard.metrics.core
testImplementation libs.micrometer.prometheus
- testImplementation libs.prometheus
}
def suffix = rootProject.osdetector.classifier
diff --git a/prometheus1/build.gradle b/prometheus1/build.gradle
new file mode 100644
index 00000000000..e0bf811022d
--- /dev/null
+++ b/prometheus1/build.gradle
@@ -0,0 +1,5 @@
+dependencies {
+ api libs.micrometer.prometheus
+
+ implementation libs.prometheus.metrics.exposition.formats
+}
diff --git a/prometheus1/src/main/java/com/linecorp/armeria/common/prometheus/PrometheusMeterRegistries.java b/prometheus1/src/main/java/com/linecorp/armeria/common/prometheus/PrometheusMeterRegistries.java
new file mode 100644
index 00000000000..fe83d916677
--- /dev/null
+++ b/prometheus1/src/main/java/com/linecorp/armeria/common/prometheus/PrometheusMeterRegistries.java
@@ -0,0 +1,69 @@
+/*
+ * Copyright 2023 LINE Corporation
+ *
+ * LINE Corporation licenses this file to you under the Apache License,
+ * version 2.0 (the "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at:
+ *
+ * https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ */
+
+package com.linecorp.armeria.common.prometheus;
+
+import static java.util.Objects.requireNonNull;
+
+import com.linecorp.armeria.common.annotation.UnstableApi;
+
+import io.micrometer.core.instrument.Clock;
+import io.micrometer.prometheusmetrics.PrometheusConfig;
+import io.micrometer.prometheusmetrics.PrometheusMeterRegistry;
+import io.prometheus.metrics.model.registry.PrometheusRegistry;
+
+/**
+ * Provides the convenient factory methods for {@link PrometheusMeterRegistry}.
+ */
+@UnstableApi
+public final class PrometheusMeterRegistries {
+
+ private static final PrometheusMeterRegistry defaultRegistry =
+ newRegistry(PrometheusRegistry.defaultRegistry);
+
+ /**
+ * Returns the default {@link PrometheusMeterRegistry} that uses {@link PrometheusRegistry#defaultRegistry}.
+ */
+ public static PrometheusMeterRegistry defaultRegistry() {
+ return defaultRegistry;
+ }
+
+ /**
+ * Returns a newly-created {@link PrometheusMeterRegistry} instance with a new {@link PrometheusRegistry}.
+ */
+ public static PrometheusMeterRegistry newRegistry() {
+ return newRegistry(new PrometheusRegistry());
+ }
+
+ /**
+ * Returns a newly-created {@link PrometheusMeterRegistry} instance with the specified
+ * {@link PrometheusRegistry}.
+ */
+ public static PrometheusMeterRegistry newRegistry(PrometheusRegistry registry) {
+ return newRegistry(registry, Clock.SYSTEM);
+ }
+
+ /**
+ * Returns a newly-created {@link PrometheusMeterRegistry} instance with the specified
+ * {@link PrometheusRegistry} and {@link Clock}.
+ */
+ public static PrometheusMeterRegistry newRegistry(PrometheusRegistry registry, Clock clock) {
+ return new PrometheusMeterRegistry(
+ PrometheusConfig.DEFAULT, requireNonNull(registry, "registry"), requireNonNull(clock, "clock"));
+ }
+
+ private PrometheusMeterRegistries() {}
+}
diff --git a/prometheus1/src/main/java/com/linecorp/armeria/common/prometheus/package-info.java b/prometheus1/src/main/java/com/linecorp/armeria/common/prometheus/package-info.java
new file mode 100644
index 00000000000..71c0716891b
--- /dev/null
+++ b/prometheus1/src/main/java/com/linecorp/armeria/common/prometheus/package-info.java
@@ -0,0 +1,23 @@
+/*
+ * Copyright 2024 LINE Corporation
+ *
+ * LINE Corporation licenses this file to you under the Apache License,
+ * version 2.0 (the "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at:
+ *
+ * https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ */
+
+/**
+ * Prometheus version 1 metrics.
+ */
+@NonNullByDefault
+package com.linecorp.armeria.common.prometheus;
+
+import com.linecorp.armeria.common.annotation.NonNullByDefault;
diff --git a/prometheus1/src/main/java/com/linecorp/armeria/server/prometheus/PrometheusExpositionService.java b/prometheus1/src/main/java/com/linecorp/armeria/server/prometheus/PrometheusExpositionService.java
new file mode 100644
index 00000000000..2406f548ee7
--- /dev/null
+++ b/prometheus1/src/main/java/com/linecorp/armeria/server/prometheus/PrometheusExpositionService.java
@@ -0,0 +1,121 @@
+/*
+ * Copyright 2024 LINE Corporation
+ *
+ * LINE Corporation licenses this file to you under the Apache License,
+ * version 2.0 (the "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at:
+ *
+ * https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ */
+package com.linecorp.armeria.server.prometheus;
+
+import static java.util.Objects.requireNonNull;
+
+import java.util.Set;
+
+import com.google.common.collect.ImmutableSet;
+
+import com.linecorp.armeria.common.HttpData;
+import com.linecorp.armeria.common.HttpHeaderNames;
+import com.linecorp.armeria.common.HttpRequest;
+import com.linecorp.armeria.common.HttpResponse;
+import com.linecorp.armeria.common.HttpStatus;
+import com.linecorp.armeria.common.MediaType;
+import com.linecorp.armeria.common.annotation.UnstableApi;
+import com.linecorp.armeria.server.AbstractHttpService;
+import com.linecorp.armeria.server.ServiceRequestContext;
+import com.linecorp.armeria.server.TransientHttpService;
+import com.linecorp.armeria.server.TransientServiceOption;
+
+import io.netty.buffer.ByteBuf;
+import io.netty.buffer.ByteBufOutputStream;
+import io.prometheus.metrics.expositionformats.ExpositionFormatWriter;
+import io.prometheus.metrics.expositionformats.ExpositionFormats;
+import io.prometheus.metrics.model.registry.PrometheusRegistry;
+
+/**
+ * Exposes Prometheus metrics in
+ * EXPOSITION FORMATS.
+ */
+@UnstableApi
+public final class PrometheusExpositionService extends AbstractHttpService
+ implements TransientHttpService {
+
+ /**
+ * Returns a new {@link PrometheusExpositionService} that exposes Prometheus metrics from
+ * {@link PrometheusRegistry#defaultRegistry}.
+ */
+ public static PrometheusExpositionService of() {
+ return of(PrometheusRegistry.defaultRegistry);
+ }
+
+ /**
+ * Returns a new {@link PrometheusExpositionService} that exposes Prometheus metrics from
+ * the specified {@link PrometheusRegistry}.
+ */
+ public static PrometheusExpositionService of(PrometheusRegistry prometheusRegistry) {
+ return builder(prometheusRegistry).build();
+ }
+
+ /**
+ * Returns a new {@link PrometheusExpositionServiceBuilder} created with
+ * {@link PrometheusRegistry#defaultRegistry}.
+ */
+ public static PrometheusExpositionServiceBuilder builder() {
+ return builder(PrometheusRegistry.defaultRegistry);
+ }
+
+ /**
+ * Returns a new {@link PrometheusExpositionServiceBuilder} created with the specified
+ * {@link PrometheusRegistry}.
+ */
+ public static PrometheusExpositionServiceBuilder builder(PrometheusRegistry prometheusRegistry) {
+ return new PrometheusExpositionServiceBuilder(
+ requireNonNull(prometheusRegistry, "prometheusRegistry"));
+ }
+
+ private final PrometheusRegistry prometheusRegistry;
+ private final ExpositionFormats expositionFormats = ExpositionFormats.init();
+ private final Set transientServiceOptions;
+
+ PrometheusExpositionService(PrometheusRegistry prometheusRegistry,
+ Set transientServiceOptions) {
+ this.prometheusRegistry = prometheusRegistry;
+ this.transientServiceOptions = ImmutableSet.copyOf(transientServiceOptions);
+ }
+
+ @Override
+ protected HttpResponse doGet(ServiceRequestContext ctx, HttpRequest req) throws Exception {
+ final String accept = req.headers().get(HttpHeaderNames.ACCEPT);
+ final ByteBuf buffer = ctx.alloc().buffer();
+ final String format;
+ boolean success = false;
+ try (ByteBufOutputStream byteBufOutputStream = new ByteBufOutputStream(buffer)) {
+ final ExpositionFormatWriter writer = expositionFormats.findWriter(accept);
+ format = writer.getContentType();
+ writer.write(byteBufOutputStream, prometheusRegistry.scrape());
+ success = true;
+ } finally {
+ if (!success) {
+ buffer.release();
+ }
+ }
+ return HttpResponse.of(HttpStatus.OK, MediaType.parse(format), HttpData.wrap(buffer));
+ }
+
+ @Override
+ protected HttpResponse doPost(ServiceRequestContext ctx, HttpRequest req) throws Exception {
+ return doGet(ctx, req);
+ }
+
+ @Override
+ public Set transientServiceOptions() {
+ return transientServiceOptions;
+ }
+}
diff --git a/prometheus1/src/main/java/com/linecorp/armeria/server/prometheus/PrometheusExpositionServiceBuilder.java b/prometheus1/src/main/java/com/linecorp/armeria/server/prometheus/PrometheusExpositionServiceBuilder.java
new file mode 100644
index 00000000000..0a4506c9f77
--- /dev/null
+++ b/prometheus1/src/main/java/com/linecorp/armeria/server/prometheus/PrometheusExpositionServiceBuilder.java
@@ -0,0 +1,64 @@
+/*
+ * Copyright 2024 LINE Corporation
+ *
+ * LINE Corporation licenses this file to you under the Apache License,
+ * version 2.0 (the "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at:
+ *
+ * https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ */
+package com.linecorp.armeria.server.prometheus;
+
+import static java.util.Objects.requireNonNull;
+
+import com.linecorp.armeria.common.annotation.UnstableApi;
+import com.linecorp.armeria.internal.server.TransientServiceOptionsBuilder;
+import com.linecorp.armeria.server.TransientServiceBuilder;
+import com.linecorp.armeria.server.TransientServiceOption;
+
+import io.prometheus.metrics.model.registry.PrometheusRegistry;
+
+/**
+ * Builds a {@link PrometheusExpositionService}.
+ */
+@UnstableApi
+public final class PrometheusExpositionServiceBuilder implements TransientServiceBuilder {
+
+ private final PrometheusRegistry prometheusRegistry;
+
+ private final TransientServiceOptionsBuilder
+ transientServiceOptionsBuilder = new TransientServiceOptionsBuilder();
+
+ PrometheusExpositionServiceBuilder(PrometheusRegistry prometheusRegistry) {
+ this.prometheusRegistry = requireNonNull(prometheusRegistry, "prometheusRegistry");
+ }
+
+ @Override
+ public PrometheusExpositionServiceBuilder transientServiceOptions(
+ TransientServiceOption... transientServiceOptions) {
+ transientServiceOptionsBuilder.transientServiceOptions(transientServiceOptions);
+ return this;
+ }
+
+ @Override
+ public PrometheusExpositionServiceBuilder transientServiceOptions(
+ Iterable transientServiceOptions) {
+ transientServiceOptionsBuilder.transientServiceOptions(transientServiceOptions);
+ return this;
+ }
+
+ /**
+ * Returns a newly-created {@link PrometheusExpositionService} based on the properties
+ * of this builder.
+ */
+ public PrometheusExpositionService build() {
+ return new PrometheusExpositionService(prometheusRegistry,
+ transientServiceOptionsBuilder.build());
+ }
+}
diff --git a/prometheus1/src/main/java/com/linecorp/armeria/server/prometheus/package-info.java b/prometheus1/src/main/java/com/linecorp/armeria/server/prometheus/package-info.java
new file mode 100644
index 00000000000..631bae0d214
--- /dev/null
+++ b/prometheus1/src/main/java/com/linecorp/armeria/server/prometheus/package-info.java
@@ -0,0 +1,23 @@
+/*
+ * Copyright 2024 LINE Corporation
+ *
+ * LINE Corporation licenses this file to you under the Apache License,
+ * version 2.0 (the "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at:
+ *
+ * https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ */
+
+/**
+ * Prometheus version 1 metrics.
+ */
+@NonNullByDefault
+package com.linecorp.armeria.server.prometheus;
+
+import com.linecorp.armeria.common.annotation.NonNullByDefault;
diff --git a/prometheus1/src/test/java/com/linecorp/armeria/server/prometheus/PrometheusExpositionServiceTest.java b/prometheus1/src/test/java/com/linecorp/armeria/server/prometheus/PrometheusExpositionServiceTest.java
new file mode 100644
index 00000000000..f02fef62d03
--- /dev/null
+++ b/prometheus1/src/test/java/com/linecorp/armeria/server/prometheus/PrometheusExpositionServiceTest.java
@@ -0,0 +1,160 @@
+/*
+ * Copyright 2020 LINE Corporation
+ *
+ * LINE Corporation licenses this file to you under the Apache License,
+ * version 2.0 (the "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at:
+ *
+ * https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ */
+package com.linecorp.armeria.server.prometheus;
+
+import static com.linecorp.armeria.common.metric.MoreMeters.measureAll;
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.awaitility.Awaitility.await;
+import static org.mockito.ArgumentMatchers.anyString;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import java.util.Map;
+import java.util.Queue;
+import java.util.concurrent.ConcurrentLinkedQueue;
+import java.util.concurrent.TimeUnit;
+
+import org.junit.jupiter.api.Nested;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.extension.RegisterExtension;
+import org.slf4j.Logger;
+
+import com.linecorp.armeria.client.WebClient;
+import com.linecorp.armeria.common.AggregatedHttpResponse;
+import com.linecorp.armeria.common.HttpHeaderNames;
+import com.linecorp.armeria.common.HttpRequest;
+import com.linecorp.armeria.common.HttpResponse;
+import com.linecorp.armeria.common.HttpStatus;
+import com.linecorp.armeria.common.logging.LogWriter;
+import com.linecorp.armeria.common.logging.RequestLog;
+import com.linecorp.armeria.common.metric.MeterIdPrefixFunction;
+import com.linecorp.armeria.common.prometheus.PrometheusMeterRegistries;
+import com.linecorp.armeria.server.ServerBuilder;
+import com.linecorp.armeria.server.TransientServiceOption;
+import com.linecorp.armeria.server.logging.LoggingService;
+import com.linecorp.armeria.server.metric.MetricCollectingService;
+import com.linecorp.armeria.testing.junit5.server.ServerExtension;
+
+import io.micrometer.prometheusmetrics.PrometheusMeterRegistry;
+import io.prometheus.metrics.expositionformats.OpenMetricsTextFormatWriter;
+import io.prometheus.metrics.expositionformats.PrometheusTextFormatWriter;
+
+class PrometheusExpositionServiceTest {
+
+ private static final PrometheusMeterRegistry registry = PrometheusMeterRegistries.defaultRegistry();
+
+ private static final Queue logs = new ConcurrentLinkedQueue<>();
+
+ private static final Logger logger = mock(Logger.class);
+
+ @RegisterExtension
+ static final ServerExtension server = new ServerExtension() {
+ @Override
+ protected void configure(ServerBuilder sb) throws Exception {
+ sb.route().path("/api").defaultServiceName("Hello").build((ctx, req) -> HttpResponse.of(200));
+ sb.meterRegistry(registry)
+ .decorator(MetricCollectingService.newDecorator(MeterIdPrefixFunction.ofDefault("foo")))
+ .service("/disabled", PrometheusExpositionService.of(registry.getPrometheusRegistry()))
+ .service("/enabled",
+ PrometheusExpositionService.builder(registry.getPrometheusRegistry())
+ .transientServiceOptions(
+ TransientServiceOption.allOf())
+ .build());
+ sb.accessLogWriter(logs::add, false);
+ sb.decorator(LoggingService.builder()
+ .logWriter(LogWriter.of(logger))
+ .newDecorator());
+ }
+ };
+
+ @Test
+ void prometheusRequests() throws InterruptedException {
+ when(logger.isDebugEnabled()).thenReturn(true);
+ final WebClient client = WebClient.of(server.httpUri());
+ assertThat(client.get("/api").aggregate().join().status()).isSameAs(HttpStatus.OK);
+ await().until(() -> logs.size() == 1);
+ verify(logger, times(2)).isDebugEnabled();
+ verify(logger, times(2)).debug(anyString());
+
+ final String exportedContent = client.get("/disabled").aggregate().join().contentUtf8();
+ assertThat(exportedContent).contains("armeria_build_info{");
+ // The last line must end with a line feed character.
+ // see https://prometheus.io/docs/instrumenting/exposition_formats/
+ assertThat(exportedContent).endsWith("\n");
+
+ // prometheus requests are not collected.
+ await().untilAsserted(() -> {
+ final Map measurements = measureAll(registry);
+ assertThat(measurements)
+ .containsEntry("foo.active.requests#value{hostname.pattern=*,method=GET,service=Hello}",
+ 0.0)
+ .doesNotContainKey(
+ "foo.active.requests#value{hostname.pattern=*,method=GET,service=" +
+ "com.linecorp.armeria.server.prometheus.PrometheusExpositionService}");
+ });
+ // Access log is not written.
+ await().pollDelay(500, TimeUnit.MILLISECONDS).then().until(() -> logs.size() == 1);
+ verify(logger, times(2)).isDebugEnabled();
+ verify(logger, times(2)).debug(anyString());
+
+ client.get("/enabled").aggregate().join();
+ // prometheus requests are collected.
+ await().untilAsserted(() -> {
+ final Map measurements = measureAll(registry);
+ assertThat(measurements)
+ .containsEntry("foo.active.requests#value{hostname.pattern=*,method=GET,service=Hello}",
+ 0.0)
+ .containsEntry("foo.active.requests#value{hostname.pattern=*,method=GET,service=" +
+ "com.linecorp.armeria.server.prometheus.PrometheusExpositionService}",
+ 0.0);
+ });
+ // Access log is written.
+ await().pollDelay(500, TimeUnit.MILLISECONDS).until(() -> logs.size() == 2);
+ verify(logger, times(4)).isDebugEnabled();
+ verify(logger, times(4)).debug(anyString());
+ }
+
+ @Nested
+ class FormatTest {
+ @Test
+ void prometheusRequestsPrometheusFormat() throws InterruptedException {
+ final WebClient client = WebClient.of(server.httpUri());
+ final HttpRequest request = HttpRequest.builder()
+ .get("/enabled")
+ .header(HttpHeaderNames.ACCEPT,
+ PrometheusTextFormatWriter.CONTENT_TYPE)
+ .build();
+ final AggregatedHttpResponse response = client.execute(request).aggregate().join();
+ assertThat(response.headers().get(HttpHeaderNames.CONTENT_TYPE))
+ .isEqualTo(PrometheusTextFormatWriter.CONTENT_TYPE);
+ }
+
+ @Test
+ void prometheusRequestsOpenMetricsFormat() throws InterruptedException {
+ final WebClient client = WebClient.of(server.httpUri());
+ final HttpRequest request = HttpRequest.builder()
+ .get("/enabled")
+ .header(HttpHeaderNames.ACCEPT,
+ OpenMetricsTextFormatWriter.CONTENT_TYPE)
+ .build();
+ final AggregatedHttpResponse response = client.execute(request).aggregate().join();
+ assertThat(response.headers().get(HttpHeaderNames.CONTENT_TYPE))
+ .isEqualTo(OpenMetricsTextFormatWriter.CONTENT_TYPE);
+ }
+ }
+}
diff --git a/settings.gradle b/settings.gradle
index 41d6a2ecc73..b57bf7f875d 100644
--- a/settings.gradle
+++ b/settings.gradle
@@ -112,6 +112,7 @@ includeWithFlags ':logback14', 'java11', 'publish', 'r
project(':logback14').projectDir = file('logback/logback14')
includeWithFlags ':native-image-config'
includeWithFlags ':oauth2', 'java', 'publish', 'relocate', 'native'
+includeWithFlags ':prometheus1', 'java', 'publish', 'relocate', 'native'
includeWithFlags ':protobuf', 'java', 'publish', 'relocate', 'native'
includeWithFlags ':reactor3', 'java', 'publish', 'relocate', 'native'
includeWithFlags ':resilience4j2', 'java17', 'publish', 'relocate', 'native'
@@ -200,7 +201,6 @@ includeWithFlags ':it:jackson-provider', 'java', 'relocate
includeWithFlags ':it:kotlin', 'java', 'relocate', 'kotlin'
includeWithFlags ':it:kubernetes-chaos-tests', 'java', 'relocate'
includeWithFlags ':it:logback1.4', 'java11', 'relocate'
-includeWithFlags ':it:micrometer1.3', 'java', 'relocate'
includeWithFlags ':it:multipart', 'java17', 'relocate'
includeWithFlags ':it:nio', 'java', 'relocate'
includeWithFlags ':it:okhttp', 'java', 'relocate'
diff --git a/spring/boot2-actuator-autoconfigure/build.gradle b/spring/boot2-actuator-autoconfigure/build.gradle
index 6b90f1ebaab..e822159d162 100644
--- a/spring/boot2-actuator-autoconfigure/build.gradle
+++ b/spring/boot2-actuator-autoconfigure/build.gradle
@@ -3,8 +3,8 @@ dependencies {
api libs.javax.inject
compileOnly libs.javax.validation
- // TODO(anuraaga): Consider removing these since this module does not have related functionality.
- optionalApi libs.micrometer.prometheus
+ optionalApi project(':prometheus1')
+ optionalApi libs.micrometer.prometheus.legacy
optionalApi libs.dropwizard.metrics.json
implementation libs.spring.boot2.autoconfigure
diff --git a/spring/boot2-autoconfigure/build.gradle b/spring/boot2-autoconfigure/build.gradle
index a0153052be9..60167f63333 100644
--- a/spring/boot2-autoconfigure/build.gradle
+++ b/spring/boot2-autoconfigure/build.gradle
@@ -7,8 +7,8 @@ dependencies {
}
implementation project(':logback')
- // TODO(anuraaga): Consider removing these since this module does not have related functionality.
- optionalApi libs.micrometer.prometheus
+ optionalApi project(':prometheus1')
+ optionalApi libs.micrometer.prometheus.legacy
optionalApi libs.dropwizard.metrics.json
api libs.javax.inject
diff --git a/spring/boot2-webflux-autoconfigure/build.gradle b/spring/boot2-webflux-autoconfigure/build.gradle
index 6e51f6be215..a3f58820359 100644
--- a/spring/boot2-webflux-autoconfigure/build.gradle
+++ b/spring/boot2-webflux-autoconfigure/build.gradle
@@ -15,7 +15,8 @@ dependencies {
}
implementation project(':logback')
- optionalApi libs.micrometer.prometheus
+ optionalApi project(':prometheus1')
+ optionalApi libs.micrometer.prometheus.legacy
optionalApi libs.dropwizard.metrics.json
api libs.javax.inject
compileOnly libs.javax.validation
@@ -57,6 +58,7 @@ task copyBoot3Sources(type: Copy) {
include '**/LocalArmeriaPort.java'
include '**/LocalArmeriaPorts.java'
include '**/PrometheusSupport.java'
+ include '**/PrometheusLegacySupport.java'
include '**/SpringDependencyInjector.java'
include '**/Ssl.java'
}
diff --git a/spring/boot3-actuator-autoconfigure/build.gradle b/spring/boot3-actuator-autoconfigure/build.gradle
index a4c084d6028..a0cd3ab60f7 100644
--- a/spring/boot3-actuator-autoconfigure/build.gradle
+++ b/spring/boot3-actuator-autoconfigure/build.gradle
@@ -3,8 +3,8 @@ dependencies {
api libs.jakarta.inject
compileOnly libs.jakarta.validation
- // TODO(anuraaga): Consider removing these since this module does not have related functionality.
- optionalApi libs.micrometer.prometheus
+ optionalApi project(':prometheus1')
+ optionalApi libs.micrometer.prometheus.legacy
optionalApi libs.dropwizard.metrics.json
implementation libs.spring.boot3.autoconfigure
diff --git a/spring/boot3-actuator-autoconfigure/src/main/java/com/linecorp/armeria/spring/actuate/WebOperationService.java b/spring/boot3-actuator-autoconfigure/src/main/java/com/linecorp/armeria/spring/actuate/WebOperationService.java
index 78cc1b52731..4fddc4b7fec 100644
--- a/spring/boot3-actuator-autoconfigure/src/main/java/com/linecorp/armeria/spring/actuate/WebOperationService.java
+++ b/spring/boot3-actuator-autoconfigure/src/main/java/com/linecorp/armeria/spring/actuate/WebOperationService.java
@@ -251,6 +251,10 @@ static HttpResponse handleResult0(HttpCodeStatusMapper statusMapper,
return HttpResponse.of(status, contentType, (CharSequence) body);
}
+ if (body instanceof byte[]) {
+ return HttpResponse.of(status, contentType, (byte[]) body);
+ }
+
if (body instanceof Resource) {
final Resource resource = (Resource) body;
final String filename = resource.getFilename();
diff --git a/spring/boot3-autoconfigure/build.gradle b/spring/boot3-autoconfigure/build.gradle
index 1fe31ad461c..89bde8a6fd4 100644
--- a/spring/boot3-autoconfigure/build.gradle
+++ b/spring/boot3-autoconfigure/build.gradle
@@ -3,8 +3,8 @@ dependencies {
compileOnly project(':thrift0.18')
implementation project(':logback')
- // TODO(anuraaga): Consider removing these since this module does not have related functionality.
- optionalApi libs.micrometer.prometheus
+ optionalApi project(':prometheus1')
+ optionalApi libs.micrometer.prometheus.legacy
optionalApi libs.dropwizard.metrics.json
api libs.jakarta.inject
diff --git a/spring/boot3-autoconfigure/src/main/java/com/linecorp/armeria/spring/AbstractArmeriaAutoConfiguration.java b/spring/boot3-autoconfigure/src/main/java/com/linecorp/armeria/spring/AbstractArmeriaAutoConfiguration.java
index 5d31767cb1b..c576410fc5b 100644
--- a/spring/boot3-autoconfigure/src/main/java/com/linecorp/armeria/spring/AbstractArmeriaAutoConfiguration.java
+++ b/spring/boot3-autoconfigure/src/main/java/com/linecorp/armeria/spring/AbstractArmeriaAutoConfiguration.java
@@ -45,7 +45,7 @@
import com.linecorp.armeria.server.docs.DocService;
import com.linecorp.armeria.server.healthcheck.HealthCheckService;
import com.linecorp.armeria.server.healthcheck.HealthChecker;
-import com.linecorp.armeria.server.metric.PrometheusExpositionService;
+import com.linecorp.armeria.server.prometheus.PrometheusExpositionService;
import com.linecorp.armeria.spring.ArmeriaSettings.Port;
import io.micrometer.core.instrument.MeterRegistry;
diff --git a/spring/boot3-autoconfigure/src/main/java/com/linecorp/armeria/spring/ArmeriaSettings.java b/spring/boot3-autoconfigure/src/main/java/com/linecorp/armeria/spring/ArmeriaSettings.java
index d3226fab384..1fbb60f3bb0 100644
--- a/spring/boot3-autoconfigure/src/main/java/com/linecorp/armeria/spring/ArmeriaSettings.java
+++ b/spring/boot3-autoconfigure/src/main/java/com/linecorp/armeria/spring/ArmeriaSettings.java
@@ -37,10 +37,9 @@
import com.linecorp.armeria.server.docs.DocService;
import com.linecorp.armeria.server.healthcheck.HealthCheckService;
import com.linecorp.armeria.server.metric.MetricCollectingService;
-import com.linecorp.armeria.server.metric.PrometheusExpositionService;
+import com.linecorp.armeria.server.prometheus.PrometheusExpositionService;
import io.micrometer.core.instrument.dropwizard.DropwizardMeterRegistry;
-import io.micrometer.prometheus.PrometheusMeterRegistry;
import io.netty.channel.EventLoopGroup;
/**
@@ -691,7 +690,7 @@ public void setDocsPath(@Nullable String docsPath) {
/**
* Returns the path of the metrics exposition service. {@link PrometheusExpositionService} will be used if
- * {@link PrometheusMeterRegistry} is available. Otherwise, Dropwizard's {@link MetricsModule} will be used
+ * {@code armeria-prometheus1} module is added. Otherwise, Dropwizard's {@link MetricsModule} will be used
* if {@link DropwizardMeterRegistry} is available.
*/
@Nullable
@@ -701,7 +700,7 @@ public String getMetricsPath() {
/**
* Sets the path of the metrics exposition service. {@link PrometheusExpositionService} will be used if
- * {@link PrometheusMeterRegistry} is available. Otherwise, Dropwizard's {@link MetricsModule} will be used
+ * {@code armeria-prometheus1} module is added. Otherwise, Dropwizard's {@link MetricsModule} will be used
* if {@link DropwizardMeterRegistry} is available.
*/
public void setMetricsPath(@Nullable String metricsPath) {
diff --git a/spring/boot3-autoconfigure/src/main/java/com/linecorp/armeria/spring/InternalServiceId.java b/spring/boot3-autoconfigure/src/main/java/com/linecorp/armeria/spring/InternalServiceId.java
index dfc4a077a3a..dd48baece99 100644
--- a/spring/boot3-autoconfigure/src/main/java/com/linecorp/armeria/spring/InternalServiceId.java
+++ b/spring/boot3-autoconfigure/src/main/java/com/linecorp/armeria/spring/InternalServiceId.java
@@ -23,7 +23,7 @@
import com.linecorp.armeria.common.annotation.UnstableApi;
import com.linecorp.armeria.server.HttpService;
import com.linecorp.armeria.server.docs.DocService;
-import com.linecorp.armeria.server.metric.PrometheusExpositionService;
+import com.linecorp.armeria.server.prometheus.PrometheusExpositionService;
/**
* Defines the IDs of internal {@code HttpService}s that should not be exposed to the external network.
diff --git a/spring/boot3-autoconfigure/src/main/java/com/linecorp/armeria/spring/InternalServices.java b/spring/boot3-autoconfigure/src/main/java/com/linecorp/armeria/spring/InternalServices.java
index 23d4c337b15..ee5134be816 100644
--- a/spring/boot3-autoconfigure/src/main/java/com/linecorp/armeria/spring/InternalServices.java
+++ b/spring/boot3-autoconfigure/src/main/java/com/linecorp/armeria/spring/InternalServices.java
@@ -95,15 +95,27 @@ public static InternalServices of(
HttpService expositionService = null;
if (settings.isEnableMetrics() && !Strings.isNullOrEmpty(settings.getMetricsPath())) {
- final String prometheusMeterRegistryClassName = "io.micrometer.prometheus.PrometheusMeterRegistry";
+ final String prometheusMeterRegistryClassName =
+ "io.micrometer.prometheusmetrics.PrometheusMeterRegistry";
final boolean hasPrometheus = hasAllClasses(
prometheusMeterRegistryClassName,
- "io.prometheus.client.CollectorRegistry");
+ "io.prometheus.metrics.model.registry.PrometheusRegistry",
+ "com.linecorp.armeria.server.prometheus.PrometheusExpositionService");
if (hasPrometheus) {
expositionService = PrometheusSupport.newExpositionService(meterRegistry);
}
+ final String legacyPrometheusMeterRegistryClassName =
+ "io.micrometer.prometheus.PrometheusMeterRegistry";
+ final boolean hasLegacyPrometheus = hasAllClasses(
+ legacyPrometheusMeterRegistryClassName,
+ "io.prometheus.client.CollectorRegistry");
+
+ if (hasLegacyPrometheus) {
+ expositionService = PrometheusLegacySupport.newExpositionService(meterRegistry);
+ }
+
final String dropwizardMeterRegistryClassName =
"io.micrometer.core.instrument.dropwizard.DropwizardMeterRegistry";
if (expositionService == null) {
@@ -117,7 +129,7 @@ public static InternalServices of(
}
if (expositionService == null) {
logger.debug("Failed to expose metrics to '{}' with {} (expected: either {} or {})",
- settings.getMetricsPath(), meterRegistry, prometheusMeterRegistryClassName,
+ settings.getMetricsPath(), meterRegistry, legacyPrometheusMeterRegistryClassName,
dropwizardMeterRegistryClassName);
}
}
diff --git a/spring/boot3-autoconfigure/src/main/java/com/linecorp/armeria/spring/PrometheusLegacySupport.java b/spring/boot3-autoconfigure/src/main/java/com/linecorp/armeria/spring/PrometheusLegacySupport.java
new file mode 100644
index 00000000000..0f7b7187dc4
--- /dev/null
+++ b/spring/boot3-autoconfigure/src/main/java/com/linecorp/armeria/spring/PrometheusLegacySupport.java
@@ -0,0 +1,60 @@
+/*
+ * Copyright 2024 LINE Corporation
+ *
+ * LINE Corporation licenses this file to you under the Apache License,
+ * version 2.0 (the "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at:
+ *
+ * https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ */
+package com.linecorp.armeria.spring;
+
+import static com.linecorp.armeria.spring.PrometheusSupport.find;
+
+import java.util.Optional;
+import java.util.Set;
+
+import com.linecorp.armeria.common.annotation.Nullable;
+import com.linecorp.armeria.server.metric.PrometheusExpositionService;
+
+import io.micrometer.core.instrument.MeterRegistry;
+import io.micrometer.core.instrument.composite.CompositeMeterRegistry;
+import io.micrometer.prometheus.PrometheusMeterRegistry;
+import io.prometheus.client.CollectorRegistry;
+
+final class PrometheusLegacySupport {
+
+ @Nullable
+ static PrometheusExpositionService newExpositionService(MeterRegistry meterRegistry) {
+ for (;;) {
+ if (meterRegistry instanceof PrometheusMeterRegistry) {
+ final CollectorRegistry prometheusRegistry =
+ ((PrometheusMeterRegistry) meterRegistry).getPrometheusRegistry();
+ return PrometheusExpositionService.of(prometheusRegistry);
+ }
+
+ if (meterRegistry instanceof CompositeMeterRegistry) {
+ final Set childRegistries =
+ ((CompositeMeterRegistry) meterRegistry).getRegistries();
+ final Optional opt =
+ find(PrometheusMeterRegistry.class, childRegistries);
+ if (opt.isPresent()) {
+ meterRegistry = opt.get();
+ continue;
+ }
+
+ return null;
+ }
+
+ return null;
+ }
+ }
+
+ private PrometheusLegacySupport() {}
+}
diff --git a/spring/boot3-autoconfigure/src/main/java/com/linecorp/armeria/spring/PrometheusSupport.java b/spring/boot3-autoconfigure/src/main/java/com/linecorp/armeria/spring/PrometheusSupport.java
index cc4c53de7e4..463627d5dcc 100644
--- a/spring/boot3-autoconfigure/src/main/java/com/linecorp/armeria/spring/PrometheusSupport.java
+++ b/spring/boot3-autoconfigure/src/main/java/com/linecorp/armeria/spring/PrometheusSupport.java
@@ -19,12 +19,12 @@
import java.util.Set;
import com.linecorp.armeria.common.annotation.Nullable;
-import com.linecorp.armeria.server.metric.PrometheusExpositionService;
+import com.linecorp.armeria.server.prometheus.PrometheusExpositionService;
import io.micrometer.core.instrument.MeterRegistry;
import io.micrometer.core.instrument.composite.CompositeMeterRegistry;
-import io.micrometer.prometheus.PrometheusMeterRegistry;
-import io.prometheus.client.CollectorRegistry;
+import io.micrometer.prometheusmetrics.PrometheusMeterRegistry;
+import io.prometheus.metrics.model.registry.PrometheusRegistry;
final class PrometheusSupport {
@@ -32,7 +32,7 @@ final class PrometheusSupport {
static PrometheusExpositionService newExpositionService(MeterRegistry meterRegistry) {
for (;;) {
if (meterRegistry instanceof PrometheusMeterRegistry) {
- final CollectorRegistry prometheusRegistry =
+ final PrometheusRegistry prometheusRegistry =
((PrometheusMeterRegistry) meterRegistry).getPrometheusRegistry();
return PrometheusExpositionService.of(prometheusRegistry);
}
@@ -40,23 +40,27 @@ static PrometheusExpositionService newExpositionService(MeterRegistry meterRegis
if (meterRegistry instanceof CompositeMeterRegistry) {
final Set childRegistries =
((CompositeMeterRegistry) meterRegistry).getRegistries();
- final Optional opt =
- childRegistries.stream()
- .filter(PrometheusMeterRegistry.class::isInstance)
- .map(PrometheusMeterRegistry.class::cast)
- .findAny();
- if (!opt.isPresent()) {
- return null;
+ final Optional opt =
+ find(PrometheusMeterRegistry.class, childRegistries);
+ if (opt.isPresent()) {
+ meterRegistry = opt.get();
+ continue;
}
- meterRegistry = opt.get();
- continue;
+ return null;
}
return null;
}
}
+ static Optional find(Class type, Set childRegistries) {
+ return childRegistries.stream()
+ .filter(type::isInstance)
+ .map(type::cast)
+ .findAny();
+ }
+
private PrometheusSupport() {}
}
diff --git a/spring/boot3-webflux-autoconfigure/build.gradle b/spring/boot3-webflux-autoconfigure/build.gradle
index 8fccde7e96e..a9ba5976192 100644
--- a/spring/boot3-webflux-autoconfigure/build.gradle
+++ b/spring/boot3-webflux-autoconfigure/build.gradle
@@ -22,7 +22,8 @@ dependencies {
implementation libs.jakarta.websocket
implementation libs.jakarta.websocket.client
- optionalApi libs.micrometer.prometheus
+ optionalApi project(':prometheus1')
+ optionalApi libs.micrometer.prometheus.legacy
optionalApi libs.dropwizard.metrics.json
annotationProcessor libs.spring.boot3.configuration.processor
@@ -62,6 +63,7 @@ task generateSources(type: Copy) {
include '**/LocalArmeriaPort.java'
include '**/LocalArmeriaPorts.java'
include '**/PrometheusSupport.java'
+ include '**/PrometheusLegacySupport.java'
include '**/SpringDependencyInjector.java'
include '**/Ssl.java'
}
diff --git a/spring/boot3-webflux-autoconfigure/src/main/java/com/linecorp/armeria/spring/web/reactive/ArmeriaReactiveWebServerFactoryAutoConfiguration.java b/spring/boot3-webflux-autoconfigure/src/main/java/com/linecorp/armeria/spring/web/reactive/ArmeriaReactiveWebServerFactoryAutoConfiguration.java
index 87d046037c1..4577230f662 100644
--- a/spring/boot3-webflux-autoconfigure/src/main/java/com/linecorp/armeria/spring/web/reactive/ArmeriaReactiveWebServerFactoryAutoConfiguration.java
+++ b/spring/boot3-webflux-autoconfigure/src/main/java/com/linecorp/armeria/spring/web/reactive/ArmeriaReactiveWebServerFactoryAutoConfiguration.java
@@ -42,7 +42,7 @@
import com.linecorp.armeria.server.docs.DocService;
import com.linecorp.armeria.server.healthcheck.HealthCheckService;
import com.linecorp.armeria.server.healthcheck.HealthChecker;
-import com.linecorp.armeria.server.metric.PrometheusExpositionService;
+import com.linecorp.armeria.server.prometheus.PrometheusExpositionService;
import com.linecorp.armeria.spring.ArmeriaSettings;
import com.linecorp.armeria.spring.DocServiceConfigurator;
import com.linecorp.armeria.spring.HealthCheckServiceConfigurator;
diff --git a/spring/boot3-webflux-autoconfigure/src/test/java/com/linecorp/armeria/spring/web/reactive/ArmeriaReactiveWebServerFactoryTest.java b/spring/boot3-webflux-autoconfigure/src/test/java/com/linecorp/armeria/spring/web/reactive/ArmeriaReactiveWebServerFactoryTest.java
index ad930cec7fa..58f33f8fb14 100644
--- a/spring/boot3-webflux-autoconfigure/src/test/java/com/linecorp/armeria/spring/web/reactive/ArmeriaReactiveWebServerFactoryTest.java
+++ b/spring/boot3-webflux-autoconfigure/src/test/java/com/linecorp/armeria/spring/web/reactive/ArmeriaReactiveWebServerFactoryTest.java
@@ -66,7 +66,7 @@
import com.linecorp.armeria.common.HttpResponse;
import com.linecorp.armeria.common.MediaType;
import com.linecorp.armeria.common.RequestHeaders;
-import com.linecorp.armeria.common.metric.PrometheusMeterRegistries;
+import com.linecorp.armeria.common.prometheus.PrometheusMeterRegistries;
import com.linecorp.armeria.internal.common.util.PortUtil;
import com.linecorp.armeria.internal.testing.MockAddressResolverGroup;
import com.linecorp.armeria.server.HttpStatusException;
diff --git a/thrift/thrift0.12/build.gradle b/thrift/thrift0.12/build.gradle
index e1c73dfe872..ca04e20422c 100644
--- a/thrift/thrift0.12/build.gradle
+++ b/thrift/thrift0.12/build.gradle
@@ -6,6 +6,8 @@
// See also: ../thrift0.9/build.gradle
dependencies {
+ testImplementation project(':prometheus1')
+
// Use the classes compiled with the latest libthrift,
// but use an older version for test classes.
testImplementation(project(':thrift0.13')) {
@@ -22,8 +24,7 @@ dependencies {
// Dropwizard and Prometheus, for testing metrics integration
testImplementation libs.dropwizard.metrics.core
- testImplementation libs.micrometer.prometheus
- testImplementation libs.prometheus
+ testImplementation libs.prometheus.metrics.exposition.formats
// micrometer tracing
testImplementation (libs.micrometer.tracing.integration.test) {
diff --git a/thrift/thrift0.13/build.gradle b/thrift/thrift0.13/build.gradle
index 6a1a26858b4..5eb0d477e92 100644
--- a/thrift/thrift0.13/build.gradle
+++ b/thrift/thrift0.13/build.gradle
@@ -4,6 +4,8 @@ dependencies {
api libs.javax.annotation
+ testImplementation project(':prometheus1')
+
// thrift api depends on httpclient4
testImplementation libs.apache.httpclient4
@@ -13,8 +15,7 @@ dependencies {
// Dropwizard and Prometheus, for testing metrics integration
testImplementation libs.dropwizard.metrics.core
- testImplementation libs.micrometer.prometheus
- testImplementation libs.prometheus
+ testImplementation libs.prometheus.metrics.exposition.formats
// micrometer tracing
testImplementation (libs.micrometer.tracing.integration.test) {
diff --git a/thrift/thrift0.13/src/test/java/com/linecorp/armeria/it/metric/PrometheusMetricsIntegrationTest.java b/thrift/thrift0.13/src/test/java/com/linecorp/armeria/it/metric/PrometheusMetricsIntegrationTest.java
index 5e3cc5ba597..38ec2133af1 100644
--- a/thrift/thrift0.13/src/test/java/com/linecorp/armeria/it/metric/PrometheusMetricsIntegrationTest.java
+++ b/thrift/thrift0.13/src/test/java/com/linecorp/armeria/it/metric/PrometheusMetricsIntegrationTest.java
@@ -49,23 +49,21 @@
import com.linecorp.armeria.common.logging.RequestOnlyLog;
import com.linecorp.armeria.common.metric.MeterIdPrefix;
import com.linecorp.armeria.common.metric.MeterIdPrefixFunction;
-import com.linecorp.armeria.common.metric.PrometheusMeterRegistries;
+import com.linecorp.armeria.common.prometheus.PrometheusMeterRegistries;
import com.linecorp.armeria.server.ServerBuilder;
import com.linecorp.armeria.server.metric.MetricCollectingService;
-import com.linecorp.armeria.server.metric.PrometheusExpositionService;
+import com.linecorp.armeria.server.prometheus.PrometheusExpositionService;
import com.linecorp.armeria.server.thrift.THttpService;
import com.linecorp.armeria.testing.junit4.server.ServerRule;
import io.micrometer.core.instrument.MeterRegistry;
-import io.micrometer.prometheus.PrometheusMeterRegistry;
-import io.prometheus.client.CollectorRegistry;
+import io.micrometer.prometheusmetrics.PrometheusMeterRegistry;
import testing.thrift.main.HelloService.Iface;
public class PrometheusMetricsIntegrationTest {
private static final Logger logger = LoggerFactory.getLogger(PrometheusMetricsIntegrationTest.class);
private static final PrometheusMeterRegistry registry = PrometheusMeterRegistries.newRegistry();
- private static final CollectorRegistry prometheusRegistry = registry.getPrometheusRegistry();
@ClassRule
public static final ServerRule server = new ServerRule() {
@@ -86,7 +84,8 @@ protected void configure(ServerBuilder sb) throws Exception {
sb.service("/bar", helloService.decorate(
MetricCollectingService.newDecorator(new MeterIdPrefixFunctionImpl("server", "Bar"))));
- sb.service("/internal/prometheus/metrics", PrometheusExpositionService.of(prometheusRegistry));
+ sb.service("/internal/prometheus/metrics",
+ PrometheusExpositionService.of(registry.getPrometheusRegistry()));
}
};
@@ -151,60 +150,60 @@ private static void hello_first_endpoint() throws Exception {
assertThat(content).containsPattern(
multilinePattern("server_request_duration_seconds_count",
"{handler=\"Foo\",hostname_pattern=\"*\",http_status=\"200\",",
- "method=\"hello\",service=\"" + Iface.class.getName() + "\",} 7.0"));
+ "method=\"hello\",service=\"" + Iface.class.getName() + "\"} 7"));
assertThat(content).containsPattern(
multilinePattern("server_request_length_count",
"{handler=\"Foo\",hostname_pattern=\"*\",http_status=\"200\",",
- "method=\"hello\",service=\"" + Iface.class.getName() + "\",} 7.0"));
+ "method=\"hello\",service=\"" + Iface.class.getName() + "\"} 7"));
assertThat(content).containsPattern(
multilinePattern("server_response_length_count",
"{handler=\"Foo\",hostname_pattern=\"*\",http_status=\"200\",",
- "method=\"hello\",service=\"" + Iface.class.getName() + "\",} 7.0"));
+ "method=\"hello\",service=\"" + Iface.class.getName() + "\"} 7"));
// Client entry count check
assertThat(content).containsPattern(
multilinePattern("client_request_duration_seconds_count",
"{handler=\"Foo\",http_status=\"200\",method=\"hello\",service=\"" +
- Iface.class.getName() + "\",} 7.0"));
+ Iface.class.getName() + "\"} 7"));
assertThat(content).containsPattern(
multilinePattern("client_request_length_count",
"{handler=\"Foo\",http_status=\"200\",method=\"hello\",service=\"" +
- Iface.class.getName() + "\",} 7.0"));
+ Iface.class.getName() + "\"} 7"));
assertThat(content).containsPattern(
multilinePattern("client_response_length_count",
"{handler=\"Foo\",http_status=\"200\",method=\"hello\",service=\"" +
- Iface.class.getName() + "\",} 7.0"));
+ Iface.class.getName() + "\"} 7"));
// Failure count
assertThat(content).containsPattern(
multilinePattern("server_requests_total",
"{handler=\"Foo\",hostname_pattern=\"*\",http_status=\"200\",",
"method=\"hello\",result=\"failure\",",
- "service=\"" + Iface.class.getName() + "\",} 3.0"));
+ "service=\"" + Iface.class.getName() + "\"} 3.0"));
assertThat(content).containsPattern(
multilinePattern("client_requests_total",
"{handler=\"Foo\",http_status=\"200\",method=\"hello\"," +
- "result=\"failure\",service=\"" + Iface.class.getName() + "\",} 3.0"));
+ "result=\"failure\",service=\"" + Iface.class.getName() + "\"} 3.0"));
// Success count
assertThat(content).containsPattern(
multilinePattern("server_requests_total",
"{handler=\"Foo\",hostname_pattern=\"*\",http_status=\"200\",",
"method=\"hello\",result=\"success\",",
- "service=\"" + Iface.class.getName() + "\",} 4.0"));
+ "service=\"" + Iface.class.getName() + "\"} 4.0"));
assertThat(content).containsPattern(
multilinePattern("client_requests_total",
"{handler=\"Foo\",http_status=\"200\",method=\"hello\"," +
- "result=\"success\",service=\"" + Iface.class.getName() + "\",} 4.0"));
+ "result=\"success\",service=\"" + Iface.class.getName() + "\"} 4.0"));
// Active Requests 0
assertThat(content).containsPattern(
multilinePattern("server_active_requests",
"{handler=\"Foo\",hostname_pattern=\"*\",",
- "method=\"hello\",service=\"" + Iface.class.getName() + "\",} 0.0"));
+ "method=\"hello\",service=\"" + Iface.class.getName() + "\"} 0.0"));
assertThat(content).containsPattern(
multilinePattern("client_active_requests",
"{handler=\"Foo\",method=\"hello\",service=\"" + Iface.class.getName() +
- "\",} 0.0"));
+ "\"} 0.0"));
}
private static void hello_second_endpoint() throws Exception {
@@ -245,52 +244,52 @@ private static void hello_second_endpoint() throws Exception {
multilinePattern("server_request_duration_seconds_count",
"{handler=\"Bar\",hostname_pattern=\"*\",http_status=\"200\",",
"method=\"hello\",service=\"" + Iface.class.getName() +
- "\",} 1.0"));
+ "\"} 1"));
assertThat(content).containsPattern(
multilinePattern("server_request_length_count",
"{handler=\"Bar\",hostname_pattern=\"*\",http_status=\"200\",",
"method=\"hello\",service=\"" + Iface.class.getName() +
- "\",} 1.0"));
+ "\"} 1"));
assertThat(content).containsPattern(
multilinePattern("server_response_length_count",
"{handler=\"Bar\",hostname_pattern=\"*\",http_status=\"200\",",
"method=\"hello\",service=\"" + Iface.class.getName() +
- "\",} 1.0"));
+ "\"} 1"));
// Client entry count check
assertThat(content).containsPattern(
multilinePattern("client_request_duration_seconds_count",
"{handler=\"Bar\",http_status=\"200\",method=\"hello\",service=\"" +
- Iface.class.getName() + "\",} 1.0"));
+ Iface.class.getName() + "\"} 1"));
assertThat(content).containsPattern(
multilinePattern("client_request_length_count",
"{handler=\"Bar\",http_status=\"200\",method=\"hello\",service=\"" +
- Iface.class.getName() + "\",} 1.0"));
+ Iface.class.getName() + "\"} 1"));
assertThat(content).containsPattern(
multilinePattern("client_response_length_count",
"{handler=\"Bar\",http_status=\"200\",method=\"hello\",service=\"" +
- Iface.class.getName() + "\",} 1.0"));
+ Iface.class.getName() + "\"} 1"));
// Success count
assertThat(content).containsPattern(
multilinePattern("server_requests_total",
"{handler=\"Bar\",hostname_pattern=\"*\",http_status=\"200\",",
"method=\"hello\",result=\"success\",",
- "service=\"" + Iface.class.getName() + "\",} 1.0"));
+ "service=\"" + Iface.class.getName() + "\"} 1.0"));
assertThat(content).containsPattern(
multilinePattern("client_requests_total",
"{handler=\"Bar\",http_status=\"200\",method=\"hello\"," +
- "result=\"success\",service=\"" + Iface.class.getName() + "\",} 1.0"));
+ "result=\"success\",service=\"" + Iface.class.getName() + "\"} 1.0"));
// Active Requests 0
assertThat(content).containsPattern(
multilinePattern("server_active_requests",
"{handler=\"Bar\",hostname_pattern=\"*\",",
"method=\"hello\",service=\"" + Iface.class.getName() +
- "\",} 0.0"));
+ "\"} 0.0"));
assertThat(content).containsPattern(
multilinePattern("client_active_requests",
"{handler=\"Bar\",method=\"hello\",service=\"" + Iface.class.getName() +
- "\",} 0.0"));
+ "\"} 0.0"));
}
private static void makeRequest1(String name) throws TException {
diff --git a/thrift/thrift0.14/build.gradle b/thrift/thrift0.14/build.gradle
index 0ac1042a30d..a93c09493cd 100644
--- a/thrift/thrift0.14/build.gradle
+++ b/thrift/thrift0.14/build.gradle
@@ -5,6 +5,8 @@ dependencies {
api libs.javax.annotation
+ testImplementation project(':prometheus1')
+
// thrift api depends on httpclient4
testImplementation libs.apache.httpclient4
@@ -14,8 +16,7 @@ dependencies {
// Dropwizard and Prometheus, for testing metrics integration
testImplementation libs.dropwizard.metrics.core
- testImplementation libs.micrometer.prometheus
- testImplementation libs.prometheus
+ testImplementation libs.prometheus.metrics.exposition.formats
// micrometer tracing
testImplementation (libs.micrometer.tracing.integration.test) {
diff --git a/thrift/thrift0.15/build.gradle b/thrift/thrift0.15/build.gradle
index 370fb7e4022..5b4dde0ede3 100644
--- a/thrift/thrift0.15/build.gradle
+++ b/thrift/thrift0.15/build.gradle
@@ -5,6 +5,8 @@ dependencies {
api libs.javax.annotation
+ testImplementation project(':prometheus1')
+
// thrift api depends on httpclient4
testImplementation libs.apache.httpclient4
@@ -14,8 +16,7 @@ dependencies {
// Dropwizard and Prometheus, for testing metrics integration
testImplementation libs.dropwizard.metrics.core
- testImplementation libs.micrometer.prometheus
- testImplementation libs.prometheus
+ testImplementation libs.prometheus.metrics.exposition.formats
// micrometer tracing
testImplementation (libs.micrometer.tracing.integration.test) {
diff --git a/thrift/thrift0.16/build.gradle b/thrift/thrift0.16/build.gradle
index 0196ff228e8..9d73fcfa1f2 100644
--- a/thrift/thrift0.16/build.gradle
+++ b/thrift/thrift0.16/build.gradle
@@ -6,6 +6,8 @@ dependencies {
api libs.javax.annotation
+ testImplementation project(':prometheus1')
+
// thrift api depends on httpclient4
testImplementation libs.apache.httpclient4
@@ -15,8 +17,7 @@ dependencies {
// Dropwizard and Prometheus, for testing metrics integration
testImplementation libs.dropwizard.metrics.core
- testImplementation libs.micrometer.prometheus
- testImplementation libs.prometheus
+ testImplementation libs.prometheus.metrics.exposition.formats
// micrometer tracing
testImplementation (libs.micrometer.tracing.integration.test) {
diff --git a/thrift/thrift0.17/build.gradle b/thrift/thrift0.17/build.gradle
index e9d866d30dd..9d0c8053322 100644
--- a/thrift/thrift0.17/build.gradle
+++ b/thrift/thrift0.17/build.gradle
@@ -6,6 +6,8 @@ dependencies {
api libs.javax.annotation
+ testImplementation project(':prometheus1')
+
// thrift api depends on httpclient4
testImplementation libs.apache.httpclient4
@@ -15,8 +17,7 @@ dependencies {
// Dropwizard and Prometheus, for testing metrics integration
testImplementation libs.dropwizard.metrics.core
- testImplementation libs.micrometer.prometheus
- testImplementation libs.prometheus
+ testImplementation libs.prometheus.metrics.exposition.formats
// micrometer tracing
testImplementation (libs.micrometer.tracing.integration.test) {
diff --git a/thrift/thrift0.18/build.gradle b/thrift/thrift0.18/build.gradle
index 4ef57eba0c1..89bf208388e 100644
--- a/thrift/thrift0.18/build.gradle
+++ b/thrift/thrift0.18/build.gradle
@@ -6,6 +6,8 @@ dependencies {
api libs.javax.annotation
+ testImplementation project(':prometheus1')
+
// thrift api depends on httpclient4
testImplementation libs.apache.httpclient4
@@ -15,8 +17,7 @@ dependencies {
// Dropwizard and Prometheus, for testing metrics integration
testImplementation libs.dropwizard.metrics.core
- testImplementation libs.micrometer.prometheus
- testImplementation libs.prometheus
+ testImplementation libs.prometheus.metrics.exposition.formats
// micrometer tracing
testImplementation (libs.micrometer.tracing.integration.test) {
diff --git a/thrift/thrift0.9/build.gradle b/thrift/thrift0.9/build.gradle
index fe239da1530..3af131edb65 100644
--- a/thrift/thrift0.9/build.gradle
+++ b/thrift/thrift0.9/build.gradle
@@ -12,6 +12,8 @@ dependencies {
api libs.javax.annotation
+ testImplementation project(':prometheus1')
+
// thrift api depends on httpclient4
testImplementation libs.apache.httpclient4
@@ -21,8 +23,7 @@ dependencies {
// Dropwizard and Prometheus, for testing metrics integration
testImplementation libs.dropwizard.metrics.core
- testImplementation libs.micrometer.prometheus
- testImplementation libs.prometheus
+ testImplementation libs.prometheus.metrics.exposition.formats
// micrometer tracing
testImplementation (libs.micrometer.tracing.integration.test) {