diff --git a/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/metrics/jdbc/HikariDataSourceMetricsAutoConfiguration.java b/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/metrics/jdbc/HikariDataSourceMetricsAutoConfiguration.java new file mode 100644 index 0000000000..2ac4030839 --- /dev/null +++ b/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/metrics/jdbc/HikariDataSourceMetricsAutoConfiguration.java @@ -0,0 +1,70 @@ +/* + * 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.jdbc; + +import java.util.Collection; + +import javax.sql.DataSource; + +import com.zaxxer.hikari.HikariDataSource; +import com.zaxxer.hikari.metrics.micrometer.MicrometerMetricsTrackerFactory; +import io.micrometer.core.instrument.MeterRegistry; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.actuate.autoconfigure.metrics.MetricsAutoConfiguration; +import org.springframework.boot.actuate.autoconfigure.metrics.export.simple.SimpleMetricsExportAutoConfiguration; +import org.springframework.boot.autoconfigure.AutoConfigureAfter; +import org.springframework.boot.autoconfigure.EnableAutoConfiguration; +import org.springframework.boot.autoconfigure.condition.ConditionalOnBean; +import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; +import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration; +import org.springframework.context.annotation.Configuration; + +/** + * {@link EnableAutoConfiguration Auto-configuration} for metrics on all + * {@link HikariDataSource HikariDataSources}. + * + * @author Tommy Ludwig + * @since 2.0.0 + */ +@Configuration +@AutoConfigureAfter({ MetricsAutoConfiguration.class, DataSourceAutoConfiguration.class, + SimpleMetricsExportAutoConfiguration.class }) +@ConditionalOnClass(HikariDataSource.class) +@ConditionalOnBean({ DataSource.class, MeterRegistry.class }) +public class HikariDataSourceMetricsAutoConfiguration { + + private final MeterRegistry registry; + + public HikariDataSourceMetricsAutoConfiguration(MeterRegistry registry) { + this.registry = registry; + } + + @Autowired + public void bindMetricsRegistryToHikariDataSources( + Collection dataSources) { + dataSources.stream().filter(HikariDataSource.class::isInstance) + .map(HikariDataSource.class::cast) + .forEach(this::bindMetricsRegistryToHikariDataSource); + } + + private void bindMetricsRegistryToHikariDataSource(HikariDataSource hikari) { + hikari.setMetricsTrackerFactory( + new MicrometerMetricsTrackerFactory(this.registry)); + } + +} 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 960c59b9bf..7bf5a749e3 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 @@ -50,6 +50,7 @@ org.springframework.boot.actuate.autoconfigure.metrics.export.signalfx.SignalFxM org.springframework.boot.actuate.autoconfigure.metrics.export.statsd.StatsdMetricsExportAutoConfiguration,\ org.springframework.boot.actuate.autoconfigure.metrics.export.wavefront.WavefrontMetricsExportAutoConfiguration,\ org.springframework.boot.actuate.autoconfigure.metrics.jdbc.DataSourcePoolMetricsAutoConfiguration,\ +org.springframework.boot.actuate.autoconfigure.metrics.jdbc.HikariDataSourceMetricsAutoConfiguration,\ org.springframework.boot.actuate.autoconfigure.metrics.integration.MetricsIntegrationAutoConfiguration,\ org.springframework.boot.actuate.autoconfigure.metrics.web.client.RestTemplateMetricsAutoConfiguration,\ org.springframework.boot.actuate.autoconfigure.metrics.web.reactive.WebFluxMetricsAutoConfiguration,\ diff --git a/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/metrics/jdbc/HikariDataSourceMetricsAutoConfigurationTests.java b/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/metrics/jdbc/HikariDataSourceMetricsAutoConfigurationTests.java new file mode 100644 index 0000000000..88c77312ff --- /dev/null +++ b/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/metrics/jdbc/HikariDataSourceMetricsAutoConfigurationTests.java @@ -0,0 +1,167 @@ +/* + * 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.jdbc; + +import java.util.UUID; + +import javax.sql.DataSource; + +import com.zaxxer.hikari.HikariDataSource; +import io.micrometer.core.instrument.MeterRegistry; +import io.micrometer.core.instrument.Tag; +import io.micrometer.core.instrument.simple.SimpleMeterRegistry; +import org.junit.Test; + +import org.springframework.boot.actuate.autoconfigure.metrics.test.MetricsRun; +import org.springframework.boot.autoconfigure.AutoConfigurations; +import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration; +import org.springframework.boot.jdbc.DataSourceBuilder; +import org.springframework.boot.test.context.runner.ApplicationContextRunner; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +import static org.assertj.core.api.Assertions.assertThat; + +/** + * Tests for {@link HikariDataSourceMetricsAutoConfiguration}. + * + * @author Tommy Ludwig + */ +public class HikariDataSourceMetricsAutoConfigurationTests { + + private ApplicationContextRunner contextRunner = new ApplicationContextRunner() + .with(MetricsRun.simple()) + .withConfiguration( + AutoConfigurations.of(HikariDataSourceMetricsAutoConfiguration.class, + DataSourceAutoConfiguration.class)) + .withUserConfiguration(BaseConfiguration.class); + + @Test + public void autoConfiguredHikariDataSourceIsInstrumented() { + this.contextRunner + .withPropertyValues("spring.datasource.generate-unique-name=true") + .run((context) -> { + context.getBean(DataSource.class).getConnection(); + MeterRegistry registry = context.getBean(MeterRegistry.class); + registry.get("hikaricp.connections").meter(); + }); + } + + @Test + public void hikariDataSourceInstrumentationCanBeDisabled() { + this.contextRunner + .withPropertyValues("spring.datasource.generate-unique-name=true", + "management.metrics.enable.hikaricp=false") + .run((context) -> { + context.getBean(DataSource.class).getConnection(); + MeterRegistry registry = context.getBean(MeterRegistry.class); + assertThat(registry.find("hikaricp.connections").meter()) + .isNull(); + }); + } + + @Test + public void allHikariDataSourcesCanBeInstrumented() { + this.contextRunner.withUserConfiguration(TwoHikariDataSourcesConfiguration.class) + .run((context) -> { + context.getBean("firstDataSource", DataSource.class).getConnection(); + context.getBean("secondOne", DataSource.class).getConnection(); + MeterRegistry registry = context.getBean(MeterRegistry.class); + registry.get("hikaricp.connections").tags("pool", "firstDataSource") + .meter(); + registry.get("hikaricp.connections").tags("pool", "secondOne") + .meter(); + }); + } + + @Test + public void someHikariDataSourcesCanBeInstrumented() { + this.contextRunner.withUserConfiguration(MixedDataSourcesConfiguration.class) + .run((context) -> { + context.getBean("firstDataSource", DataSource.class).getConnection(); + context.getBean("secondOne", DataSource.class).getConnection(); + MeterRegistry registry = context.getBean(MeterRegistry.class); + assertThat(registry.get("hikaricp.connections").meter().getId() + .getTags()) + .containsExactly(Tag.of("pool", "firstDataSource")); + }); + } + + @Configuration + static class BaseConfiguration { + + @Bean + public SimpleMeterRegistry simpleMeterRegistry() { + return new SimpleMeterRegistry(); + } + + } + + @Configuration + static class TwoHikariDataSourcesConfiguration { + + @Bean + public DataSource firstDataSource() { + return createHikariDataSource("firstDataSource"); + } + + @Bean + public DataSource secondOne() { + return createHikariDataSource("secondOne"); + } + + private HikariDataSource createHikariDataSource(String poolName) { + String url = "jdbc:hsqldb:mem:test-" + UUID.randomUUID(); + HikariDataSource hikariDataSource = DataSourceBuilder.create().url(url) + .type(HikariDataSource.class).build(); + hikariDataSource.setPoolName(poolName); + return hikariDataSource; + } + + } + + @Configuration + static class MixedDataSourcesConfiguration { + + @Bean + public DataSource firstDataSource() { + return createHikariDataSource("firstDataSource"); + } + + @Bean + public DataSource secondOne() { + return createTomcatDataSource(); + } + + private HikariDataSource createHikariDataSource(String poolName) { + String url = "jdbc:hsqldb:mem:test-" + UUID.randomUUID(); + HikariDataSource hikariDataSource = DataSourceBuilder.create().url(url) + .type(HikariDataSource.class).build(); + hikariDataSource.setPoolName(poolName); + return hikariDataSource; + } + + private org.apache.tomcat.jdbc.pool.DataSource createTomcatDataSource() { + String url = "jdbc:hsqldb:mem:test-" + UUID.randomUUID(); + return DataSourceBuilder + .create().url(url) + .type(org.apache.tomcat.jdbc.pool.DataSource.class).build(); + } + + } + +}