Skip to content

Commit ff40c8b

Browse files
committed
Use MeterBinders to bind DataSource metrics
Closes gh-30282
1 parent 5e76671 commit ff40c8b

File tree

2 files changed

+102
-44
lines changed

2 files changed

+102
-44
lines changed

spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/metrics/jdbc/DataSourcePoolMetricsAutoConfiguration.java

Lines changed: 72 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -28,11 +28,11 @@
2828
import com.zaxxer.hikari.HikariDataSource;
2929
import com.zaxxer.hikari.metrics.micrometer.MicrometerMetricsTrackerFactory;
3030
import io.micrometer.core.instrument.MeterRegistry;
31+
import io.micrometer.core.instrument.binder.MeterBinder;
3132
import org.apache.commons.logging.Log;
3233
import org.apache.commons.logging.LogFactory;
3334

3435
import org.springframework.beans.factory.ObjectProvider;
35-
import org.springframework.beans.factory.annotation.Autowired;
3636
import org.springframework.boot.actuate.autoconfigure.metrics.MetricsAutoConfiguration;
3737
import org.springframework.boot.actuate.autoconfigure.metrics.export.simple.SimpleMetricsExportAutoConfiguration;
3838
import org.springframework.boot.actuate.metrics.jdbc.DataSourcePoolMetrics;
@@ -43,6 +43,7 @@
4343
import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration;
4444
import org.springframework.boot.jdbc.DataSourceUnwrapper;
4545
import org.springframework.boot.jdbc.metadata.DataSourcePoolMetadataProvider;
46+
import org.springframework.context.annotation.Bean;
4647
import org.springframework.context.annotation.Configuration;
4748
import org.springframework.core.log.LogMessage;
4849
import org.springframework.util.StringUtils;
@@ -66,33 +67,52 @@ static class DataSourcePoolMetadataMetricsConfiguration {
6667

6768
private static final String DATASOURCE_SUFFIX = "dataSource";
6869

69-
@Autowired
70-
void bindDataSourcesToRegistry(Map<String, DataSource> dataSources, MeterRegistry registry,
70+
@Bean
71+
DataSourcePoolMetadataMeterBinder dataSourcePoolMetadataMeterBinder(Map<String, DataSource> dataSources,
7172
ObjectProvider<DataSourcePoolMetadataProvider> metadataProviders) {
72-
List<DataSourcePoolMetadataProvider> metadataProvidersList = metadataProviders.stream()
73-
.collect(Collectors.toList());
74-
dataSources.forEach(
75-
(name, dataSource) -> bindDataSourceToRegistry(name, dataSource, metadataProvidersList, registry));
73+
return new DataSourcePoolMetadataMeterBinder(dataSources, metadataProviders);
7674
}
7775

78-
private void bindDataSourceToRegistry(String beanName, DataSource dataSource,
79-
Collection<DataSourcePoolMetadataProvider> metadataProviders, MeterRegistry registry) {
80-
String dataSourceName = getDataSourceName(beanName);
81-
new DataSourcePoolMetrics(dataSource, metadataProviders, dataSourceName, Collections.emptyList())
82-
.bindTo(registry);
83-
}
76+
static class DataSourcePoolMetadataMeterBinder implements MeterBinder {
77+
78+
private final Map<String, DataSource> dataSources;
79+
80+
private final ObjectProvider<DataSourcePoolMetadataProvider> metadataProviders;
81+
82+
DataSourcePoolMetadataMeterBinder(Map<String, DataSource> dataSources,
83+
ObjectProvider<DataSourcePoolMetadataProvider> metadataProviders) {
84+
this.dataSources = dataSources;
85+
this.metadataProviders = metadataProviders;
86+
}
8487

85-
/**
86-
* Get the name of a DataSource based on its {@code beanName}.
87-
* @param beanName the name of the data source bean
88-
* @return a name for the given data source
89-
*/
90-
private String getDataSourceName(String beanName) {
91-
if (beanName.length() > DATASOURCE_SUFFIX.length()
92-
&& StringUtils.endsWithIgnoreCase(beanName, DATASOURCE_SUFFIX)) {
93-
return beanName.substring(0, beanName.length() - DATASOURCE_SUFFIX.length());
88+
@Override
89+
public void bindTo(MeterRegistry registry) {
90+
List<DataSourcePoolMetadataProvider> metadataProvidersList = this.metadataProviders.stream()
91+
.collect(Collectors.toList());
92+
this.dataSources.forEach((name, dataSource) -> bindDataSourceToRegistry(name, dataSource,
93+
metadataProvidersList, registry));
94+
}
95+
96+
private void bindDataSourceToRegistry(String beanName, DataSource dataSource,
97+
Collection<DataSourcePoolMetadataProvider> metadataProviders, MeterRegistry registry) {
98+
String dataSourceName = getDataSourceName(beanName);
99+
new DataSourcePoolMetrics(dataSource, metadataProviders, dataSourceName, Collections.emptyList())
100+
.bindTo(registry);
101+
}
102+
103+
/**
104+
* Get the name of a DataSource based on its {@code beanName}.
105+
* @param beanName the name of the data source bean
106+
* @return a name for the given data source
107+
*/
108+
private String getDataSourceName(String beanName) {
109+
if (beanName.length() > DATASOURCE_SUFFIX.length()
110+
&& StringUtils.endsWithIgnoreCase(beanName, DATASOURCE_SUFFIX)) {
111+
return beanName.substring(0, beanName.length() - DATASOURCE_SUFFIX.length());
112+
}
113+
return beanName;
94114
}
95-
return beanName;
115+
96116
}
97117

98118
}
@@ -101,34 +121,43 @@ private String getDataSourceName(String beanName) {
101121
@ConditionalOnClass(HikariDataSource.class)
102122
static class HikariDataSourceMetricsConfiguration {
103123

104-
private static final Log logger = LogFactory.getLog(HikariDataSourceMetricsConfiguration.class);
124+
@Bean
125+
HikariDataSourceMeterBinder hikariDataSourceMeterBinder(ObjectProvider<DataSource> dataSources) {
126+
return new HikariDataSourceMeterBinder(dataSources);
127+
}
105128

106-
private final MeterRegistry registry;
129+
static class HikariDataSourceMeterBinder implements MeterBinder {
107130

108-
HikariDataSourceMetricsConfiguration(MeterRegistry registry) {
109-
this.registry = registry;
110-
}
131+
private static final Log logger = LogFactory.getLog(HikariDataSourceMeterBinder.class);
111132

112-
@Autowired
113-
void bindMetricsRegistryToHikariDataSources(Collection<DataSource> dataSources) {
114-
for (DataSource dataSource : dataSources) {
115-
HikariDataSource hikariDataSource = DataSourceUnwrapper.unwrap(dataSource, HikariConfigMXBean.class,
116-
HikariDataSource.class);
117-
if (hikariDataSource != null) {
118-
bindMetricsRegistryToHikariDataSource(hikariDataSource);
119-
}
133+
private final ObjectProvider<DataSource> dataSources;
134+
135+
HikariDataSourceMeterBinder(ObjectProvider<DataSource> dataSources) {
136+
this.dataSources = dataSources;
120137
}
121-
}
122138

123-
private void bindMetricsRegistryToHikariDataSource(HikariDataSource hikari) {
124-
if (hikari.getMetricRegistry() == null && hikari.getMetricsTrackerFactory() == null) {
125-
try {
126-
hikari.setMetricsTrackerFactory(new MicrometerMetricsTrackerFactory(this.registry));
139+
@Override
140+
public void bindTo(MeterRegistry registry) {
141+
for (DataSource dataSource : this.dataSources) {
142+
HikariDataSource hikariDataSource = DataSourceUnwrapper.unwrap(dataSource, HikariConfigMXBean.class,
143+
HikariDataSource.class);
144+
if (hikariDataSource != null) {
145+
bindMetricsRegistryToHikariDataSource(hikariDataSource, registry);
146+
}
127147
}
128-
catch (Exception ex) {
129-
logger.warn(LogMessage.format("Failed to bind Hikari metrics: %s", ex.getMessage()));
148+
}
149+
150+
private void bindMetricsRegistryToHikariDataSource(HikariDataSource hikari, MeterRegistry registry) {
151+
if (hikari.getMetricRegistry() == null && hikari.getMetricsTrackerFactory() == null) {
152+
try {
153+
hikari.setMetricsTrackerFactory(new MicrometerMetricsTrackerFactory(registry));
154+
}
155+
catch (Exception ex) {
156+
logger.warn(LogMessage.format("Failed to bind Hikari metrics: %s", ex.getMessage()));
157+
}
130158
}
131159
}
160+
132161
}
133162

134163
}

spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/metrics/jdbc/DataSourcePoolMetricsAutoConfigurationTests.java

Lines changed: 30 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2012-2021 the original author or authors.
2+
* Copyright 2012-2022 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -29,6 +29,7 @@
2929

3030
import org.springframework.aop.framework.ProxyFactory;
3131
import org.springframework.beans.factory.config.BeanPostProcessor;
32+
import org.springframework.boot.LazyInitializationBeanFactoryPostProcessor;
3233
import org.springframework.boot.actuate.autoconfigure.metrics.test.MetricsRun;
3334
import org.springframework.boot.autoconfigure.AutoConfigurations;
3435
import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration;
@@ -90,6 +91,19 @@ void allDataSourcesCanBeInstrumented() {
9091
});
9192
}
9293

94+
@Test
95+
void allDataSourcesCanBeInstrumentedWithLazyInitialization() {
96+
this.contextRunner.withConfiguration(AutoConfigurations.of(DataSourceAutoConfiguration.class)).withInitializer(
97+
(context) -> context.addBeanFactoryPostProcessor(new LazyInitializationBeanFactoryPostProcessor()))
98+
.withUserConfiguration(TwoDataSourcesConfiguration.class).run((context) -> {
99+
context.getBean("firstDataSource", DataSource.class).getConnection().getMetaData();
100+
context.getBean("secondOne", DataSource.class).getConnection().getMetaData();
101+
MeterRegistry registry = context.getBean(MeterRegistry.class);
102+
registry.get("jdbc.connections.max").tags("name", "first").meter();
103+
registry.get("jdbc.connections.max").tags("name", "secondOne").meter();
104+
});
105+
}
106+
93107
@Test
94108
void autoConfiguredHikariDataSourceIsInstrumented() {
95109
this.contextRunner.withConfiguration(AutoConfigurations.of(DataSourceAutoConfiguration.class))
@@ -167,6 +181,21 @@ void someHikariDataSourcesCanBeInstrumented() {
167181
});
168182
}
169183

184+
@Test
185+
void allHikariDataSourcesCanBeInstrumentedWhenUsingLazyInitialization() {
186+
this.contextRunner.withUserConfiguration(TwoHikariDataSourcesConfiguration.class)
187+
.withConfiguration(AutoConfigurations.of(DataSourceAutoConfiguration.class))
188+
.withInitializer((context) -> context
189+
.addBeanFactoryPostProcessor(new LazyInitializationBeanFactoryPostProcessor()))
190+
.run((context) -> {
191+
context.getBean("firstDataSource", DataSource.class).getConnection();
192+
context.getBean("secondOne", DataSource.class).getConnection();
193+
MeterRegistry registry = context.getBean(MeterRegistry.class);
194+
registry.get("hikaricp.connections").tags("pool", "firstDataSource").meter();
195+
registry.get("hikaricp.connections").tags("pool", "secondOne").meter();
196+
});
197+
}
198+
170199
@Test
171200
void hikariProxiedDataSourceCanBeInstrumented() {
172201
this.contextRunner.withUserConfiguration(ProxiedHikariDataSourcesConfiguration.class)

0 commit comments

Comments
 (0)