diff --git a/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/metrics/jdbc/DataSourcePoolMetricsAutoConfiguration.java b/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/metrics/jdbc/DataSourcePoolMetricsAutoConfiguration.java index 7ffe0efaa4..29e28d3c04 100644 --- a/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/metrics/jdbc/DataSourcePoolMetricsAutoConfiguration.java +++ b/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/metrics/jdbc/DataSourcePoolMetricsAutoConfiguration.java @@ -34,6 +34,7 @@ import org.springframework.boot.autoconfigure.condition.ConditionalOnBean; import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration; import org.springframework.boot.jdbc.metadata.DataSourcePoolMetadataProvider; import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.Import; import org.springframework.util.StringUtils; /** @@ -48,6 +49,7 @@ import org.springframework.util.StringUtils; SimpleMetricsExportAutoConfiguration.class }) @ConditionalOnBean({ DataSource.class, DataSourcePoolMetadataProvider.class, MeterRegistry.class }) +@Import(HikariDataSourceMetricsConfiguration.class) public class DataSourcePoolMetricsAutoConfiguration { private static final String DATASOURCE_SUFFIX = "dataSource"; diff --git a/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/metrics/jdbc/HikariDataSourceMetricsConfiguration.java b/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/metrics/jdbc/HikariDataSourceMetricsConfiguration.java new file mode 100644 index 0000000000..a120d10ee6 --- /dev/null +++ b/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/metrics/jdbc/HikariDataSourceMetricsConfiguration.java @@ -0,0 +1,60 @@ +/* + * 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.autoconfigure.condition.ConditionalOnClass; +import org.springframework.context.annotation.Configuration; + +/** + * Specific configuration for {@link HikariDataSource} metrics. + * + * @author Tommy Ludwig + * @author Stephane Nicoll + */ +@Configuration +@ConditionalOnClass(HikariDataSource.class) +class HikariDataSourceMetricsConfiguration { + + private final MeterRegistry registry; + + HikariDataSourceMetricsConfiguration(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/test/java/org/springframework/boot/actuate/autoconfigure/metrics/jdbc/DataSourcePoolMetricsAutoConfigurationTests.java b/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/metrics/jdbc/DataSourcePoolMetricsAutoConfigurationTests.java index e5f4b81784..0c9d5efd42 100644 --- a/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/metrics/jdbc/DataSourcePoolMetricsAutoConfigurationTests.java +++ b/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/metrics/jdbc/DataSourcePoolMetricsAutoConfigurationTests.java @@ -20,7 +20,9 @@ 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; @@ -39,21 +41,20 @@ import static org.assertj.core.api.Assertions.assertThat; * * @author Stephane Nicoll * @author Andy Wilkinson + * @author Tommy Ludwig */ public class DataSourcePoolMetricsAutoConfigurationTests { private ApplicationContextRunner contextRunner = new ApplicationContextRunner() + .withPropertyValues("spring.datasource.generate-unique-name=true") .with(MetricsRun.simple()) - .withConfiguration( - AutoConfigurations.of(DataSourcePoolMetricsAutoConfiguration.class)) + .withConfiguration(AutoConfigurations.of(DataSourceAutoConfiguration.class, + DataSourcePoolMetricsAutoConfiguration.class)) .withUserConfiguration(BaseConfiguration.class); @Test public void autoConfiguredDataSourceIsInstrumented() { this.contextRunner - .withConfiguration( - AutoConfigurations.of(DataSourceAutoConfiguration.class)) - .withPropertyValues("spring.datasource.generate-unique-name=true") .run((context) -> { context.getBean(DataSource.class).getConnection().getMetaData(); MeterRegistry registry = context.getBean(MeterRegistry.class); @@ -65,10 +66,7 @@ public class DataSourcePoolMetricsAutoConfigurationTests { @Test public void dataSourceInstrumentationCanBeDisabled() { this.contextRunner - .withConfiguration( - AutoConfigurations.of(DataSourceAutoConfiguration.class)) - .withPropertyValues("spring.datasource.generate-unique-name=true", - "management.metrics.enable.jdbc=false") + .withPropertyValues("management.metrics.enable.jdbc=false") .run((context) -> { context.getBean(DataSource.class).getConnection().getMetaData(); MeterRegistry registry = context.getBean(MeterRegistry.class); @@ -80,8 +78,6 @@ public class DataSourcePoolMetricsAutoConfigurationTests { @Test public void allDataSourcesCanBeInstrumented() { this.contextRunner.withUserConfiguration(TwoDataSourcesConfiguration.class) - .withConfiguration( - AutoConfigurations.of(DataSourceAutoConfiguration.class)) .run((context) -> { context.getBean("firstDataSource", DataSource.class).getConnection() .getMetaData(); @@ -94,6 +90,55 @@ public class DataSourcePoolMetricsAutoConfigurationTests { }); } + @Test + public void autoConfiguredHikariDataSourceIsInstrumented() { + this.contextRunner + .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("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 { @@ -124,4 +169,57 @@ public class DataSourcePoolMetricsAutoConfigurationTests { } + @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(); + } + + } + }