diff --git a/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/metrics/web/client/WebClientMetricsConfiguration.java b/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/metrics/web/client/WebClientMetricsConfiguration.java index cb9609c232..7220c45fd7 100644 --- a/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/metrics/web/client/WebClientMetricsConfiguration.java +++ b/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/metrics/web/client/WebClientMetricsConfiguration.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2019 the original author or authors. + * Copyright 2012-2020 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -17,6 +17,7 @@ package org.springframework.boot.actuate.autoconfigure.metrics.web.client; import io.micrometer.core.instrument.MeterRegistry; +import reactor.netty.http.client.HttpClient; import org.springframework.boot.actuate.autoconfigure.metrics.MetricsProperties; import org.springframework.boot.actuate.autoconfigure.metrics.MetricsProperties.Web.Client.ClientRequest; @@ -25,6 +26,7 @@ import org.springframework.boot.actuate.metrics.web.reactive.client.MetricsWebCl import org.springframework.boot.actuate.metrics.web.reactive.client.WebClientExchangeTagsProvider; import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; +import org.springframework.boot.autoconfigure.web.reactive.function.client.ReactorNettyHttpClientMapper; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.web.reactive.function.client.WebClient; @@ -53,4 +55,14 @@ class WebClientMetricsConfiguration { request.getAutotime()); } + @ConditionalOnClass(HttpClient.class) + static class ReactorNettyClientMetricsConfiguration { + + @Bean + ReactorNettyHttpClientMapper metricsHttpClientMapper() { + return (httpClient) -> httpClient.tcpConfiguration((tcpClient) -> tcpClient.metrics(true)); + } + + } + } diff --git a/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/metrics/web/netty/NettyMetricsAutoConfiguration.java b/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/metrics/web/netty/NettyMetricsAutoConfiguration.java new file mode 100644 index 0000000000..813aca0b27 --- /dev/null +++ b/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/metrics/web/netty/NettyMetricsAutoConfiguration.java @@ -0,0 +1,45 @@ +/* + * Copyright 2012-2020 the original author or authors. + * + * Licensed 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 org.springframework.boot.actuate.autoconfigure.metrics.web.netty; + +import io.micrometer.core.instrument.MeterRegistry; +import reactor.netty.http.server.HttpServer; + +import org.springframework.boot.autoconfigure.EnableAutoConfiguration; +import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; +import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication; +import org.springframework.boot.web.embedded.netty.NettyServerCustomizer; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +/** + * {@link EnableAutoConfiguration Auto-configuration} for Reactor Netty metrics. + * + * @author Brian Clozel + * @since 2.3.0 + */ +@Configuration(proxyBeanMethods = false) +@ConditionalOnWebApplication +@ConditionalOnClass({ MeterRegistry.class, HttpServer.class }) +public class NettyMetricsAutoConfiguration { + + @Bean + public NettyServerCustomizer nettyServerMetricsCustomizer() { + return (httpServer) -> httpServer.tcpConfiguration((tcpServer) -> tcpServer.metrics(true)); + } + +} diff --git a/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/metrics/web/netty/package-info.java b/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/metrics/web/netty/package-info.java new file mode 100644 index 0000000000..2633bed6a9 --- /dev/null +++ b/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/metrics/web/netty/package-info.java @@ -0,0 +1,20 @@ +/* + * Copyright 2012-2020 the original author or authors. + * + * Licensed 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. + */ + +/** + * Auto-configuration for Reactor Netty actuator metrics. + */ +package org.springframework.boot.actuate.autoconfigure.metrics.web.netty; diff --git a/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/resources/META-INF/spring.factories b/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/resources/META-INF/spring.factories index e297520518..560dc63b37 100644 --- a/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/resources/META-INF/spring.factories +++ b/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/resources/META-INF/spring.factories @@ -71,6 +71,7 @@ org.springframework.boot.actuate.autoconfigure.metrics.orm.jpa.HibernateMetricsA org.springframework.boot.actuate.autoconfigure.metrics.r2dbc.ConnectionPoolMetricsAutoConfiguration,\ org.springframework.boot.actuate.autoconfigure.metrics.web.client.HttpClientMetricsAutoConfiguration,\ org.springframework.boot.actuate.autoconfigure.metrics.web.jetty.JettyMetricsAutoConfiguration,\ +org.springframework.boot.actuate.autoconfigure.metrics.web.netty.NettyMetricsAutoConfiguration,\ org.springframework.boot.actuate.autoconfigure.metrics.web.reactive.WebFluxMetricsAutoConfiguration,\ org.springframework.boot.actuate.autoconfigure.metrics.web.servlet.WebMvcMetricsAutoConfiguration,\ org.springframework.boot.actuate.autoconfigure.metrics.web.tomcat.TomcatMetricsAutoConfiguration,\ diff --git a/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/metrics/web/client/WebClientMetricsConfigurationTests.java b/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/metrics/web/client/WebClientMetricsConfigurationTests.java index 7eea946366..6fd541faf7 100644 --- a/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/metrics/web/client/WebClientMetricsConfigurationTests.java +++ b/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/metrics/web/client/WebClientMetricsConfigurationTests.java @@ -18,16 +18,25 @@ package org.springframework.boot.actuate.autoconfigure.metrics.web.client; import java.time.Duration; +import io.micrometer.core.instrument.Clock; import io.micrometer.core.instrument.MeterRegistry; +import io.micrometer.core.instrument.Metrics; import io.micrometer.core.instrument.Timer; +import io.micrometer.core.instrument.composite.CompositeMeterRegistry; import io.micrometer.core.instrument.distribution.HistogramSnapshot; +import io.micrometer.core.instrument.simple.SimpleConfig; +import io.micrometer.core.instrument.simple.SimpleMeterRegistry; +import okhttp3.mockwebserver.MockResponse; +import okhttp3.mockwebserver.MockWebServer; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; import reactor.core.publisher.Mono; +import reactor.netty.http.client.HttpClient; import org.springframework.boot.actuate.autoconfigure.metrics.test.MetricsRun; import org.springframework.boot.actuate.metrics.web.reactive.client.WebClientExchangeTagsProvider; import org.springframework.boot.autoconfigure.AutoConfigurations; +import org.springframework.boot.autoconfigure.web.reactive.function.client.ClientHttpConnectorAutoConfiguration; import org.springframework.boot.autoconfigure.web.reactive.function.client.WebClientAutoConfiguration; import org.springframework.boot.test.context.assertj.AssertableApplicationContext; import org.springframework.boot.test.context.runner.ApplicationContextRunner; @@ -107,6 +116,47 @@ class WebClientMetricsConfigurationTests { }); } + @Test + void shouldConfigureMetricsForReactorNetty() { + this.contextRunner.withConfiguration(AutoConfigurations.of(ClientHttpConnectorAutoConfiguration.class)) + .run((context) -> { + CompositeMeterRegistry registry = Metrics.globalRegistry; + MockWebServer server = new MockWebServer(); + try { + server.start(); + server.enqueue(new MockResponse()); + String serverAddress = "http://" + server.getHostName() + ":" + server.getPort(); + WebClient.Builder builder = context.getBean(WebClient.Builder.class); + WebClient client = builder.baseUrl(serverAddress).build(); + assertThat(registry.find("reactor.netty.tcp.client.connect.time").timer()).isNull(); + client.get().uri("/test").retrieve().toBodilessEntity().block(); + assertThat(registry.find("reactor.netty.tcp.client.connect.time").timer()).isNotNull(); + } + finally { + server.shutdown(); + } + }); + } + + @Test + void sanityTest() throws Exception { + MockWebServer server = new MockWebServer(); + try { + server.start(); + server.enqueue(new MockResponse()); + SimpleMeterRegistry registry = new SimpleMeterRegistry(SimpleConfig.DEFAULT, Clock.SYSTEM); + Metrics.addRegistry(registry); + HttpClient client = HttpClient.create().metrics(true); + assertThat(registry.find("reactor.netty.http.client.connect.time").timer()).isNull(); + client.get().uri("http://" + server.getHostName() + ":" + server.getPort()).response().block(); + assertThat(registry.find("reactor.netty.http.client.connect.time").timer()).isNotNull(); + } + finally { + server.shutdown(); + } + + } + private MeterRegistry getInitializedMeterRegistry(AssertableApplicationContext context) { WebClient webClient = mockWebClient(context.getBean(WebClient.Builder.class)); MeterRegistry registry = context.getBean(MeterRegistry.class); diff --git a/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/metrics/web/netty/NettyMetricsAutoConfigurationTests.java b/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/metrics/web/netty/NettyMetricsAutoConfigurationTests.java new file mode 100644 index 0000000000..573bddc89c --- /dev/null +++ b/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/metrics/web/netty/NettyMetricsAutoConfigurationTests.java @@ -0,0 +1,83 @@ +/* + * Copyright 2012-2020 the original author or authors. + * + * Licensed 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 org.springframework.boot.actuate.autoconfigure.metrics.web.netty; + +import java.util.stream.Collectors; + +import io.micrometer.core.instrument.MeterRegistry; +import io.micrometer.core.instrument.Metrics; +import org.junit.jupiter.api.Test; + +import org.springframework.beans.factory.ObjectProvider; +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.AutoConfigurations; +import org.springframework.boot.autoconfigure.web.reactive.ReactiveWebServerFactoryAutoConfiguration; +import org.springframework.boot.context.event.ApplicationStartedEvent; +import org.springframework.boot.test.context.runner.ReactiveWebApplicationContextRunner; +import org.springframework.boot.web.embedded.netty.NettyReactiveWebServerFactory; +import org.springframework.boot.web.embedded.netty.NettyServerCustomizer; +import org.springframework.boot.web.reactive.context.AnnotationConfigReactiveWebServerApplicationContext; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.http.server.reactive.HttpHandler; +import org.springframework.web.reactive.function.client.WebClient; + +import static org.assertj.core.api.Assertions.assertThat; + +/** + * Tests for {@link NettyMetricsAutoConfiguration} + * + * @author Brian Clozel + */ +class NettyMetricsAutoConfigurationTests { + + @Test + void autoConfiguresTcpMetricsWithReactorNettyServer() { + MeterRegistry registry = Metrics.globalRegistry; + assertThat(registry.find("reactor.netty.tcp.server.data.received").summary()).isNull(); + new ReactiveWebApplicationContextRunner(AnnotationConfigReactiveWebServerApplicationContext::new) + .withConfiguration(AutoConfigurations.of(NettyMetricsAutoConfiguration.class, + ReactiveWebServerFactoryAutoConfiguration.class)) + .withUserConfiguration(ReactiveWebServerConfiguration.class).run((context) -> { + AnnotationConfigReactiveWebServerApplicationContext serverContext = context + .getSourceApplicationContext(AnnotationConfigReactiveWebServerApplicationContext.class); + context.publishEvent(new ApplicationStartedEvent(new SpringApplication(), null, + context.getSourceApplicationContext())); + WebClient.create("http://localhost:" + serverContext.getWebServer().getPort()).get().retrieve() + .toBodilessEntity().block(); + assertThat(registry.find("reactor.netty.tcp.server.data.received").summary()).isNotNull(); + }); + } + + @Configuration(proxyBeanMethods = false) + static class ReactiveWebServerConfiguration { + + @Bean + NettyReactiveWebServerFactory nettyFactory(ObjectProvider customizers) { + NettyReactiveWebServerFactory serverFactory = new NettyReactiveWebServerFactory(0); + serverFactory.setServerCustomizers(customizers.orderedStream().collect(Collectors.toList())); + return serverFactory; + } + + @Bean + HttpHandler httpHandler() { + return (req, res) -> res.setComplete(); + } + + } + +} diff --git a/spring-boot-project/spring-boot-docs/src/docs/asciidoc/production-ready-features.adoc b/spring-boot-project/spring-boot-docs/src/docs/asciidoc/production-ready-features.adoc index be87be2228..c17c8cc546 100644 --- a/spring-boot-project/spring-boot-docs/src/docs/asciidoc/production-ready-features.adoc +++ b/spring-boot-project/spring-boot-docs/src/docs/asciidoc/production-ready-features.adoc @@ -1813,6 +1813,7 @@ Spring Boot registers the following core metrics when applicable: * Logback metrics: record the number of events logged to Logback at each level * Uptime metrics: report a gauge for uptime and a fixed gauge representing the application's absolute start time * Tomcat metrics (`server.tomcat.mbeanregistry.enabled` must be set to `true` for all Tomcat metrics to be registered) +* Reactor Netty metrics (TCP and allocator metrics for client and server) * {spring-integration-docs}system-management.html#micrometer-integration[Spring Integration] metrics