From ce9b2b8266ca5f49069617c4765794c96d163130 Mon Sep 17 00:00:00 2001 From: Andy Wilkinson Date: Tue, 13 Feb 2018 12:47:25 +0000 Subject: [PATCH] =?UTF-8?q?Auto-configure=20Micrometer=E2=80=99s=20Tomcat?= =?UTF-8?q?=20metrics?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Closes gh-11916 --- .../TomcatMetricsAutoConfiguration.java | 74 ++++++++++ .../main/resources/META-INF/spring.factories | 1 + .../TomcatMetricsAutoConfigurationTests.java | 137 ++++++++++++++++++ 3 files changed, 212 insertions(+) create mode 100644 spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/metrics/web/tomcat/TomcatMetricsAutoConfiguration.java create mode 100644 spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/metrics/web/tomcat/TomcatMetricsAutoConfigurationTests.java diff --git a/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/metrics/web/tomcat/TomcatMetricsAutoConfiguration.java b/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/metrics/web/tomcat/TomcatMetricsAutoConfiguration.java new file mode 100644 index 0000000000..7403157ba3 --- /dev/null +++ b/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/metrics/web/tomcat/TomcatMetricsAutoConfiguration.java @@ -0,0 +1,74 @@ +/* + * Copyright 2012-2018 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 + * + * http://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.tomcat; + +import java.util.Collections; + +import io.micrometer.core.instrument.binder.tomcat.TomcatMetrics; +import org.apache.catalina.Container; +import org.apache.catalina.Context; +import org.apache.catalina.Manager; + +import org.springframework.boot.autoconfigure.EnableAutoConfiguration; +import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; +import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; +import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication; +import org.springframework.boot.web.context.WebServerApplicationContext; +import org.springframework.boot.web.embedded.tomcat.TomcatWebServer; +import org.springframework.boot.web.server.WebServer; +import org.springframework.context.ApplicationContext; +import org.springframework.context.annotation.Bean; + +/** + * {@link EnableAutoConfiguration Auto-configuration} for {@link TomcatMetrics}. + * + * @author Andy Wilkinson + * @since 2.0.0 + */ +@ConditionalOnWebApplication +@ConditionalOnClass({ TomcatMetrics.class, Manager.class }) +public class TomcatMetricsAutoConfiguration { + + @Bean + @ConditionalOnMissingBean(TomcatMetrics.class) + public TomcatMetrics tomcatMetrics(ApplicationContext applicationContext) { + Context context = findContext(applicationContext); + return new TomcatMetrics(context == null ? null : context.getManager(), + Collections.emptyList()); + } + + private Context findContext(ApplicationContext context) { + if (!(context instanceof WebServerApplicationContext)) { + return null; + } + WebServer webServer = ((WebServerApplicationContext) context).getWebServer(); + if (!(webServer instanceof TomcatWebServer)) { + return null; + } + return findContext((TomcatWebServer) webServer); + } + + private Context findContext(TomcatWebServer webServer) { + for (Container child : webServer.getTomcat().getHost().findChildren()) { + if (child instanceof Context) { + return (Context) child; + } + } + return null; + } + +} 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 26616260e9..77b972eaa0 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 @@ -52,6 +52,7 @@ org.springframework.boot.actuate.autoconfigure.metrics.integration.MetricsIntegr org.springframework.boot.actuate.autoconfigure.metrics.web.client.RestTemplateMetricsAutoConfiguration,\ 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,\ org.springframework.boot.actuate.autoconfigure.mongo.MongoHealthIndicatorAutoConfiguration,\ org.springframework.boot.actuate.autoconfigure.neo4j.Neo4jHealthIndicatorAutoConfiguration,\ org.springframework.boot.actuate.autoconfigure.redis.RedisHealthIndicatorAutoConfiguration,\ diff --git a/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/metrics/web/tomcat/TomcatMetricsAutoConfigurationTests.java b/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/metrics/web/tomcat/TomcatMetricsAutoConfigurationTests.java new file mode 100644 index 0000000000..dad59466a3 --- /dev/null +++ b/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/metrics/web/tomcat/TomcatMetricsAutoConfigurationTests.java @@ -0,0 +1,137 @@ +/* + * Copyright 2012-2018 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 + * + * http://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.tomcat; + +import java.util.Collections; + +import io.micrometer.core.instrument.binder.tomcat.TomcatMetrics; +import io.micrometer.core.instrument.simple.SimpleMeterRegistry; +import org.junit.Test; + +import org.springframework.boot.autoconfigure.AutoConfigurations; +import org.springframework.boot.test.context.runner.ReactiveWebApplicationContextRunner; +import org.springframework.boot.test.context.runner.WebApplicationContextRunner; +import org.springframework.boot.web.embedded.tomcat.TomcatReactiveWebServerFactory; +import org.springframework.boot.web.embedded.tomcat.TomcatServletWebServerFactory; +import org.springframework.boot.web.reactive.context.AnnotationConfigReactiveWebServerApplicationContext; +import org.springframework.boot.web.servlet.context.AnnotationConfigServletWebServerApplicationContext; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.http.server.reactive.HttpHandler; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.Mockito.mock; + +/** + * Tests for {@link TomcatMetricsAutoConfiguration}. + * + * @author Andy Wilkinson + */ +public class TomcatMetricsAutoConfigurationTests { + + @Test + public void autoConfiguresTomcatMetricsWithEmbeddedServletTomcat() { + new WebApplicationContextRunner( + AnnotationConfigServletWebServerApplicationContext::new) + .withConfiguration(AutoConfigurations + .of(TomcatMetricsAutoConfiguration.class)) + .withUserConfiguration(ServletWebServerConfiguration.class) + .run((context) -> { + assertThat(context).hasSingleBean(TomcatMetrics.class); + SimpleMeterRegistry registry = new SimpleMeterRegistry(); + context.getBean(TomcatMetrics.class).bindTo(registry); + assertThat( + registry.find("tomcat.sessions.active.max").meter()) + .isNotNull(); + assertThat(registry.find("tomcat.threads.current").meter()) + .isNotNull(); + }); + } + + @Test + public void autoConfiguresTomcatMetricsWithEmbeddedReactiveTomcat() { + new ReactiveWebApplicationContextRunner( + AnnotationConfigReactiveWebServerApplicationContext::new) + .withConfiguration(AutoConfigurations + .of(TomcatMetricsAutoConfiguration.class)) + .withUserConfiguration(ReactiveWebServerConfiguration.class) + .run((context) -> { + assertThat(context).hasSingleBean(TomcatMetrics.class); + SimpleMeterRegistry registry = new SimpleMeterRegistry(); + context.getBean(TomcatMetrics.class).bindTo(registry); + assertThat( + registry.find("tomcat.sessions.active.max").meter()) + .isNotNull(); + assertThat(registry.find("tomcat.threads.current").meter()) + .isNotNull(); + }); + } + + @Test + public void autoConfiguresTomcatMetricsWithStandaloneTomcat() { + new WebApplicationContextRunner() + .withConfiguration( + AutoConfigurations.of(TomcatMetricsAutoConfiguration.class)) + .run((context) -> assertThat(context).hasSingleBean(TomcatMetrics.class)); + } + + @Test + public void allowsCustomTomcatMetricsToBeUsed() { + new WebApplicationContextRunner() + .withConfiguration( + AutoConfigurations.of(TomcatMetricsAutoConfiguration.class)) + .withUserConfiguration(CustomTomcatMetrics.class) + .run((context) -> assertThat(context).hasSingleBean(TomcatMetrics.class) + .hasBean("customTomcatMetrics")); + } + + @Configuration + static class ServletWebServerConfiguration { + + @Bean + public TomcatServletWebServerFactory tomcatFactory() { + return new TomcatServletWebServerFactory(0); + } + + } + + @Configuration + static class ReactiveWebServerConfiguration { + + @Bean + public TomcatReactiveWebServerFactory tomcatFactory() { + return new TomcatReactiveWebServerFactory(0); + } + + @Bean + public HttpHandler httpHandler() { + return mock(HttpHandler.class); + } + + } + + @Configuration + static class CustomTomcatMetrics { + + @Bean + public TomcatMetrics customTomcatMetrics() { + return new TomcatMetrics(null, Collections.emptyList()); + } + + } + +}