001/* 002 * Copyright 2012-2018 the original author or authors. 003 * 004 * Licensed under the Apache License, Version 2.0 (the "License"); 005 * you may not use this file except in compliance with the License. 006 * You may obtain a copy of the License at 007 * 008 * http://www.apache.org/licenses/LICENSE-2.0 009 * 010 * Unless required by applicable law or agreed to in writing, software 011 * distributed under the License is distributed on an "AS IS" BASIS, 012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 013 * See the License for the specific language governing permissions and 014 * limitations under the License. 015 */ 016 017package org.springframework.boot.actuate.autoconfigure.metrics.jdbc; 018 019import java.util.Collection; 020import java.util.Collections; 021import java.util.Map; 022 023import javax.sql.DataSource; 024 025import com.zaxxer.hikari.HikariDataSource; 026import com.zaxxer.hikari.metrics.micrometer.MicrometerMetricsTrackerFactory; 027import io.micrometer.core.instrument.MeterRegistry; 028import org.apache.commons.logging.Log; 029import org.apache.commons.logging.LogFactory; 030 031import org.springframework.beans.factory.annotation.Autowired; 032import org.springframework.boot.actuate.autoconfigure.metrics.MetricsAutoConfiguration; 033import org.springframework.boot.actuate.autoconfigure.metrics.export.simple.SimpleMetricsExportAutoConfiguration; 034import org.springframework.boot.actuate.metrics.jdbc.DataSourcePoolMetrics; 035import org.springframework.boot.autoconfigure.AutoConfigureAfter; 036import org.springframework.boot.autoconfigure.EnableAutoConfiguration; 037import org.springframework.boot.autoconfigure.condition.ConditionalOnBean; 038import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; 039import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration; 040import org.springframework.boot.jdbc.DataSourceUnwrapper; 041import org.springframework.boot.jdbc.metadata.DataSourcePoolMetadataProvider; 042import org.springframework.context.annotation.Configuration; 043import org.springframework.util.StringUtils; 044 045/** 046 * {@link EnableAutoConfiguration Auto-configuration} for metrics on all available 047 * {@link DataSource datasources}. 048 * 049 * @author Stephane Nicoll 050 * @since 2.0.0 051 */ 052@Configuration 053@AutoConfigureAfter({ MetricsAutoConfiguration.class, DataSourceAutoConfiguration.class, 054 SimpleMetricsExportAutoConfiguration.class }) 055@ConditionalOnClass({ DataSource.class, MeterRegistry.class }) 056@ConditionalOnBean({ DataSource.class, MeterRegistry.class }) 057public class DataSourcePoolMetricsAutoConfiguration { 058 059 @Configuration 060 @ConditionalOnBean(DataSourcePoolMetadataProvider.class) 061 static class DataSourcePoolMetadataMetricsConfiguration { 062 063 private static final String DATASOURCE_SUFFIX = "dataSource"; 064 065 private final MeterRegistry registry; 066 067 private final Collection<DataSourcePoolMetadataProvider> metadataProviders; 068 069 DataSourcePoolMetadataMetricsConfiguration(MeterRegistry registry, 070 Collection<DataSourcePoolMetadataProvider> metadataProviders) { 071 this.registry = registry; 072 this.metadataProviders = metadataProviders; 073 } 074 075 @Autowired 076 public void bindDataSourcesToRegistry(Map<String, DataSource> dataSources) { 077 dataSources.forEach(this::bindDataSourceToRegistry); 078 } 079 080 private void bindDataSourceToRegistry(String beanName, DataSource dataSource) { 081 String dataSourceName = getDataSourceName(beanName); 082 new DataSourcePoolMetrics(dataSource, this.metadataProviders, dataSourceName, 083 Collections.emptyList()).bindTo(this.registry); 084 } 085 086 /** 087 * Get the name of a DataSource based on its {@code beanName}. 088 * @param beanName the name of the data source bean 089 * @return a name for the given data source 090 */ 091 private String getDataSourceName(String beanName) { 092 if (beanName.length() > DATASOURCE_SUFFIX.length() 093 && StringUtils.endsWithIgnoreCase(beanName, DATASOURCE_SUFFIX)) { 094 return beanName.substring(0, 095 beanName.length() - DATASOURCE_SUFFIX.length()); 096 } 097 return beanName; 098 } 099 100 } 101 102 @Configuration 103 @ConditionalOnClass(HikariDataSource.class) 104 static class HikariDataSourceMetricsConfiguration { 105 106 private static final Log logger = LogFactory 107 .getLog(HikariDataSourceMetricsConfiguration.class); 108 109 private final MeterRegistry registry; 110 111 HikariDataSourceMetricsConfiguration(MeterRegistry registry) { 112 this.registry = registry; 113 } 114 115 @Autowired 116 public void bindMetricsRegistryToHikariDataSources( 117 Collection<DataSource> dataSources) { 118 for (DataSource dataSource : dataSources) { 119 HikariDataSource hikariDataSource = DataSourceUnwrapper.unwrap(dataSource, 120 HikariDataSource.class); 121 if (hikariDataSource != null) { 122 bindMetricsRegistryToHikariDataSource(hikariDataSource); 123 } 124 } 125 } 126 127 private void bindMetricsRegistryToHikariDataSource(HikariDataSource hikari) { 128 if (hikari.getMetricRegistry() == null 129 && hikari.getMetricsTrackerFactory() == null) { 130 try { 131 hikari.setMetricsTrackerFactory( 132 new MicrometerMetricsTrackerFactory(this.registry)); 133 } 134 catch (Exception ex) { 135 logger.warn("Failed to bind Hikari metrics: " + ex.getMessage()); 136 } 137 } 138 } 139 140 } 141 142}