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}