Commit 8229733f authored by Andy Wilkinson's avatar Andy Wilkinson

Avoid triggering early init when creating MeterRegistryPostProceesor

Closes gh-11890
parent f19e0258
/*
* 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;
import java.util.Collection;
import java.util.Collections;
import io.micrometer.core.instrument.MeterRegistry;
import io.micrometer.core.instrument.Metrics;
import io.micrometer.core.instrument.binder.MeterBinder;
import io.micrometer.core.instrument.composite.CompositeMeterRegistry;
import io.micrometer.core.instrument.config.MeterFilter;
import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.boot.util.LambdaSafe;
/**
* {@link BeanPostProcessor} to apply {@link MeterRegistryCustomizer customizers},
* {@link MeterFilter filters}, {@link MeterBinder binders} and {@link Metrics#addRegistry
* global registration} to {@link MeterRegistry meter registries}. This post processor
* intentionally skips {@link CompositeMeterRegistry} with the assumptions that the
* registries it contains are beans and will be customized directly.
*
* @author Jon Schneider
* @author Phillip Webb
*/
class MeterRegistryConfigurer {
private final Collection<MeterRegistryCustomizer<?>> customizers;
private final Collection<MeterFilter> filters;
private final Collection<MeterBinder> binders;
private final boolean addToGlobalRegistry;
MeterRegistryConfigurer(Collection<MeterBinder> binders,
Collection<MeterFilter> filters,
Collection<MeterRegistryCustomizer<?>> customizers,
boolean addToGlobalRegistry) {
this.binders = (binders != null ? binders : Collections.emptyList());
this.filters = (filters != null ? filters : Collections.emptyList());
this.customizers = (customizers != null ? customizers : Collections.emptyList());
this.addToGlobalRegistry = addToGlobalRegistry;
}
void configure(MeterRegistry registry) {
if (registry instanceof CompositeMeterRegistry) {
return;
}
// Customizers must be applied before binders, as they may add custom tags or
// alter timer or summary configuration.
customize(registry);
addFilters(registry);
addBinders(registry);
if (this.addToGlobalRegistry && registry != Metrics.globalRegistry) {
Metrics.addRegistry(registry);
}
}
@SuppressWarnings("unchecked")
private void customize(MeterRegistry registry) {
LambdaSafe.callbacks(MeterRegistryCustomizer.class, this.customizers, registry)
.withLogger(MeterRegistryConfigurer.class)
.invoke((customizer) -> customizer.customize(registry));
}
private void addFilters(MeterRegistry registry) {
this.filters.forEach(registry.config()::meterFilter);
}
private void addBinders(MeterRegistry registry) {
this.binders.forEach((binder) -> binder.bindTo(registry));
}
}
...@@ -17,87 +17,56 @@ ...@@ -17,87 +17,56 @@
package org.springframework.boot.actuate.autoconfigure.metrics; package org.springframework.boot.actuate.autoconfigure.metrics;
import java.util.Collection; import java.util.Collection;
import java.util.Collections;
import io.micrometer.core.instrument.MeterRegistry; import io.micrometer.core.instrument.MeterRegistry;
import io.micrometer.core.instrument.Metrics;
import io.micrometer.core.instrument.binder.MeterBinder; import io.micrometer.core.instrument.binder.MeterBinder;
import io.micrometer.core.instrument.composite.CompositeMeterRegistry;
import io.micrometer.core.instrument.config.MeterFilter; import io.micrometer.core.instrument.config.MeterFilter;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanPostProcessor; import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.boot.util.LambdaSafe; import org.springframework.context.ApplicationContext;
/** /**
* {@link BeanPostProcessor} to apply {@link MeterRegistryCustomizer customizers}, * {@link BeanPostProcessor} that delegates to a lazily created
* {@link MeterFilter filters}, {@link MeterBinder binders} and {@link Metrics#addRegistry * {@link MeterRegistryConfigurer} to post-process {@link MeterRegistry} beans.
* global registration} to {@link MeterRegistry meter registries}. This post processor
* intentionally skips {@link CompositeMeterRegistry} with the assumptions that the
* registries it contains are beans and will be customized directly.
* *
* @author Jon Schneider * @author Jon Schneider
* @author Phillip Webb * @author Phillip Webb
* @author Andy Wilkinson
*/ */
class MeterRegistryPostProcessor implements BeanPostProcessor { class MeterRegistryPostProcessor implements BeanPostProcessor {
private final Collection<MeterRegistryCustomizer<?>> customizers; private final ApplicationContext context;
private final Collection<MeterFilter> filters; private volatile MeterRegistryConfigurer configurer;
private final Collection<MeterBinder> binders; MeterRegistryPostProcessor(ApplicationContext context) {
this.context = context;
private final boolean addToGlobalRegistry;
MeterRegistryPostProcessor(Collection<MeterBinder> binders,
Collection<MeterFilter> filters,
Collection<MeterRegistryCustomizer<?>> customizers,
boolean addToGlobalRegistry) {
this.binders = (binders != null ? binders : Collections.emptyList());
this.filters = (filters != null ? filters : Collections.emptyList());
this.customizers = (customizers != null ? customizers : Collections.emptyList());
this.addToGlobalRegistry = addToGlobalRegistry;
}
@Override
public Object postProcessBeforeInitialization(Object bean, String beanName) {
return bean;
} }
@Override @Override
public Object postProcessAfterInitialization(Object bean, String beanName) { public Object postProcessAfterInitialization(Object bean, String beanName)
throws BeansException {
if (bean instanceof MeterRegistry) { if (bean instanceof MeterRegistry) {
postProcess((MeterRegistry) bean); getConfigurer().configure((MeterRegistry) bean);
} }
return bean; return bean;
} }
private void postProcess(MeterRegistry registry) {
if (registry instanceof CompositeMeterRegistry) {
return;
}
// Customizers must be applied before binders, as they may add custom tags or
// alter timer or summary configuration.
customize(registry);
addFilters(registry);
addBinders(registry);
if (this.addToGlobalRegistry && registry != Metrics.globalRegistry) {
Metrics.addRegistry(registry);
}
}
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
private void customize(MeterRegistry registry) { private MeterRegistryConfigurer getConfigurer() {
LambdaSafe.callbacks(MeterRegistryCustomizer.class, this.customizers, registry) if (this.configurer == null) {
.withLogger(MeterRegistryPostProcessor.class) this.configurer = new MeterRegistryConfigurer(beansOfType(MeterBinder.class),
.invoke((customizer) -> customizer.customize(registry)); beansOfType(MeterFilter.class),
(Collection<MeterRegistryCustomizer<?>>) (Object) beansOfType(
MeterRegistryCustomizer.class),
this.context.getBean(MetricsProperties.class).isUseGlobalRegistry());
} }
return this.configurer;
private void addFilters(MeterRegistry registry) {
this.filters.forEach(registry.config()::meterFilter);
} }
private void addBinders(MeterRegistry registry) { private <T> Collection<T> beansOfType(Class<T> type) {
this.binders.forEach((binder) -> binder.bindTo(registry)); return this.context.getBeansOfType(type).values();
} }
} }
...@@ -16,13 +16,8 @@ ...@@ -16,13 +16,8 @@
package org.springframework.boot.actuate.autoconfigure.metrics; package org.springframework.boot.actuate.autoconfigure.metrics;
import java.util.Collection;
import io.micrometer.core.annotation.Timed; import io.micrometer.core.annotation.Timed;
import io.micrometer.core.instrument.binder.MeterBinder;
import io.micrometer.core.instrument.config.MeterFilter;
import org.springframework.beans.factory.ObjectProvider;
import org.springframework.boot.actuate.autoconfigure.metrics.amqp.RabbitMetricsConfiguration; import org.springframework.boot.actuate.autoconfigure.metrics.amqp.RabbitMetricsConfiguration;
import org.springframework.boot.actuate.autoconfigure.metrics.cache.CacheMetricsConfiguration; import org.springframework.boot.actuate.autoconfigure.metrics.cache.CacheMetricsConfiguration;
import org.springframework.boot.actuate.autoconfigure.metrics.jdbc.DataSourcePoolMetricsConfiguration; import org.springframework.boot.actuate.autoconfigure.metrics.jdbc.DataSourcePoolMetricsConfiguration;
...@@ -40,6 +35,7 @@ import org.springframework.boot.autoconfigure.condition.SearchStrategy; ...@@ -40,6 +35,7 @@ import org.springframework.boot.autoconfigure.condition.SearchStrategy;
import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration; import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration;
import org.springframework.boot.autoconfigure.web.client.RestTemplateAutoConfiguration; import org.springframework.boot.autoconfigure.web.client.RestTemplateAutoConfiguration;
import org.springframework.boot.context.properties.EnableConfigurationProperties; import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import; import org.springframework.context.annotation.Import;
...@@ -72,13 +68,8 @@ public class MetricsAutoConfiguration { ...@@ -72,13 +68,8 @@ public class MetricsAutoConfiguration {
@Bean @Bean
public static MeterRegistryPostProcessor meterRegistryPostProcessor( public static MeterRegistryPostProcessor meterRegistryPostProcessor(
ObjectProvider<Collection<MeterBinder>> binders, ApplicationContext context) {
ObjectProvider<Collection<MeterFilter>> filters, return new MeterRegistryPostProcessor(context);
ObjectProvider<Collection<MeterRegistryCustomizer<?>>> customizers,
MetricsProperties properties) {
return new MeterRegistryPostProcessor(binders.getIfAvailable(),
filters.getIfAvailable(), customizers.getIfAvailable(),
properties.isUseGlobalRegistry());
} }
@Bean @Bean
......
...@@ -38,11 +38,12 @@ import static org.mockito.Mockito.verify; ...@@ -38,11 +38,12 @@ import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.verifyZeroInteractions; import static org.mockito.Mockito.verifyZeroInteractions;
/** /**
* Tests for {@link MeterRegistryPostProcessor}. * Tests for {@link MeterRegistryConfigurer}.
* *
* @author Phillip Webb * @author Phillip Webb
* @author Andy Wilkinson
*/ */
public class MeterRegistryPostProcessorTests { public class MeterRegistryConfigurerTests {
private List<MeterBinder> binders = new ArrayList<>(); private List<MeterBinder> binders = new ArrayList<>();
...@@ -72,62 +73,50 @@ public class MeterRegistryPostProcessorTests { ...@@ -72,62 +73,50 @@ public class MeterRegistryPostProcessorTests {
} }
@Test @Test
public void postProcessWhenNotRegistryShouldReturnBean() { public void configureWhenCompositeShouldSkip() {
MeterRegistryPostProcessor processor = new MeterRegistryPostProcessor(
this.binders, this.filters, this.customizers, false);
Object bean = new Object();
String beanName = "name";
assertThat(processor.postProcessBeforeInitialization(bean, beanName))
.isEqualTo(bean);
assertThat(processor.postProcessAfterInitialization(bean, beanName))
.isEqualTo(bean);
}
@Test
public void postProcessorWhenCompositeShouldSkip() {
this.binders.add(this.mockBinder); this.binders.add(this.mockBinder);
this.customizers.add(this.mockCustomizer); this.customizers.add(this.mockCustomizer);
MeterRegistryPostProcessor processor = new MeterRegistryPostProcessor( MeterRegistryConfigurer configurer = new MeterRegistryConfigurer(this.binders,
this.binders, this.filters, this.customizers, false); this.filters, this.customizers, false);
processor.postProcessAfterInitialization(new CompositeMeterRegistry(), "name"); configurer.configure(new CompositeMeterRegistry());
verifyZeroInteractions(this.mockBinder, this.mockCustomizer); verifyZeroInteractions(this.mockBinder, this.mockCustomizer);
} }
@Test @Test
public void postProcessShouldApplyCustomizer() { public void configureShouldApplyCustomizer() {
this.customizers.add(this.mockCustomizer); this.customizers.add(this.mockCustomizer);
MeterRegistryPostProcessor processor = new MeterRegistryPostProcessor( MeterRegistryConfigurer configurer = new MeterRegistryConfigurer(this.binders,
this.binders, this.filters, this.customizers, false); this.filters, this.customizers, false);
processor.postProcessAfterInitialization(this.mockRegistry, "name"); configurer.configure(this.mockRegistry);
verify(this.mockCustomizer).customize(this.mockRegistry); verify(this.mockCustomizer).customize(this.mockRegistry);
} }
@Test @Test
public void postProcessShouldApplyFilter() { public void configureShouldApplyFilter() {
this.filters.add(this.mockFilter); this.filters.add(this.mockFilter);
MeterRegistryPostProcessor processor = new MeterRegistryPostProcessor( MeterRegistryConfigurer configurer = new MeterRegistryConfigurer(this.binders,
this.binders, this.filters, this.customizers, false); this.filters, this.customizers, false);
processor.postProcessAfterInitialization(this.mockRegistry, "name"); configurer.configure(this.mockRegistry);
verify(this.mockConfig).meterFilter(this.mockFilter); verify(this.mockConfig).meterFilter(this.mockFilter);
} }
@Test @Test
public void postProcessShouldApplyBinder() { public void configureShouldApplyBinder() {
this.binders.add(this.mockBinder); this.binders.add(this.mockBinder);
MeterRegistryPostProcessor processor = new MeterRegistryPostProcessor( MeterRegistryConfigurer configurer = new MeterRegistryConfigurer(this.binders,
this.binders, this.filters, this.customizers, false); this.filters, this.customizers, false);
processor.postProcessAfterInitialization(this.mockRegistry, "name"); configurer.configure(this.mockRegistry);
verify(this.mockBinder).bindTo(this.mockRegistry); verify(this.mockBinder).bindTo(this.mockRegistry);
} }
@Test @Test
public void postProcessShouldBeCallInOrderCustomizeFilterBinder() { public void configureShouldBeCalledInOrderCustomizerFilterBinder() {
this.customizers.add(this.mockCustomizer); this.customizers.add(this.mockCustomizer);
this.filters.add(this.mockFilter); this.filters.add(this.mockFilter);
this.binders.add(this.mockBinder); this.binders.add(this.mockBinder);
MeterRegistryPostProcessor processor = new MeterRegistryPostProcessor( MeterRegistryConfigurer configurer = new MeterRegistryConfigurer(this.binders,
this.binders, this.filters, this.customizers, false); this.filters, this.customizers, false);
processor.postProcessAfterInitialization(this.mockRegistry, "name"); configurer.configure(this.mockRegistry);
InOrder ordered = inOrder(this.mockBinder, this.mockConfig, this.mockCustomizer); InOrder ordered = inOrder(this.mockBinder, this.mockConfig, this.mockCustomizer);
ordered.verify(this.mockCustomizer).customize(this.mockRegistry); ordered.verify(this.mockCustomizer).customize(this.mockRegistry);
ordered.verify(this.mockConfig).meterFilter(this.mockFilter); ordered.verify(this.mockConfig).meterFilter(this.mockFilter);
...@@ -135,11 +124,11 @@ public class MeterRegistryPostProcessorTests { ...@@ -135,11 +124,11 @@ public class MeterRegistryPostProcessorTests {
} }
@Test @Test
public void postProcessWhenAddToGlobalRegistryShouldAddToGlobalRegistry() { public void configureWhenAddToGlobalRegistryShouldAddToGlobalRegistry() {
MeterRegistryPostProcessor processor = new MeterRegistryPostProcessor( MeterRegistryConfigurer configurer = new MeterRegistryConfigurer(this.binders,
this.binders, this.filters, this.customizers, true); this.filters, this.customizers, true);
try { try {
processor.postProcessAfterInitialization(this.mockRegistry, "name"); configurer.configure(this.mockRegistry);
assertThat(Metrics.globalRegistry.getRegistries()) assertThat(Metrics.globalRegistry.getRegistries())
.contains(this.mockRegistry); .contains(this.mockRegistry);
} }
...@@ -149,10 +138,10 @@ public class MeterRegistryPostProcessorTests { ...@@ -149,10 +138,10 @@ public class MeterRegistryPostProcessorTests {
} }
@Test @Test
public void postProcessWhenNotAddToGlobalRegistryShouldAddToGlobalRegistry() { public void configureWhenNotAddToGlobalRegistryShouldAddToGlobalRegistry() {
MeterRegistryPostProcessor processor = new MeterRegistryPostProcessor( MeterRegistryConfigurer configurer = new MeterRegistryConfigurer(this.binders,
this.binders, this.filters, this.customizers, false); this.filters, this.customizers, false);
processor.postProcessAfterInitialization(this.mockRegistry, "name"); configurer.configure(this.mockRegistry);
assertThat(Metrics.globalRegistry.getRegistries()) assertThat(Metrics.globalRegistry.getRegistries())
.doesNotContain(this.mockRegistry); .doesNotContain(this.mockRegistry);
} }
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment