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 @@
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.BeansException;
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 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.
* {@link BeanPostProcessor} that delegates to a lazily created
* {@link MeterRegistryConfigurer} to post-process {@link MeterRegistry} beans.
*
* @author Jon Schneider
* @author Phillip Webb
* @author Andy Wilkinson
*/
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;
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;
MeterRegistryPostProcessor(ApplicationContext context) {
this.context = context;
}
@Override
public Object postProcessBeforeInitialization(Object bean, String beanName) {
return bean;
}
@Override
public Object postProcessAfterInitialization(Object bean, String beanName) {
public Object postProcessAfterInitialization(Object bean, String beanName)
throws BeansException {
if (bean instanceof MeterRegistry) {
postProcess((MeterRegistry) bean);
getConfigurer().configure((MeterRegistry) 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")
private void customize(MeterRegistry registry) {
LambdaSafe.callbacks(MeterRegistryCustomizer.class, this.customizers, registry)
.withLogger(MeterRegistryPostProcessor.class)
.invoke((customizer) -> customizer.customize(registry));
}
private void addFilters(MeterRegistry registry) {
this.filters.forEach(registry.config()::meterFilter);
private MeterRegistryConfigurer getConfigurer() {
if (this.configurer == null) {
this.configurer = new MeterRegistryConfigurer(beansOfType(MeterBinder.class),
beansOfType(MeterFilter.class),
(Collection<MeterRegistryCustomizer<?>>) (Object) beansOfType(
MeterRegistryCustomizer.class),
this.context.getBean(MetricsProperties.class).isUseGlobalRegistry());
}
return this.configurer;
}
private void addBinders(MeterRegistry registry) {
this.binders.forEach((binder) -> binder.bindTo(registry));
private <T> Collection<T> beansOfType(Class<T> type) {
return this.context.getBeansOfType(type).values();
}
}
......@@ -16,13 +16,8 @@
package org.springframework.boot.actuate.autoconfigure.metrics;
import java.util.Collection;
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.cache.CacheMetricsConfiguration;
import org.springframework.boot.actuate.autoconfigure.metrics.jdbc.DataSourcePoolMetricsConfiguration;
......@@ -40,6 +35,7 @@ import org.springframework.boot.autoconfigure.condition.SearchStrategy;
import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration;
import org.springframework.boot.autoconfigure.web.client.RestTemplateAutoConfiguration;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
......@@ -72,13 +68,8 @@ public class MetricsAutoConfiguration {
@Bean
public static MeterRegistryPostProcessor meterRegistryPostProcessor(
ObjectProvider<Collection<MeterBinder>> binders,
ObjectProvider<Collection<MeterFilter>> filters,
ObjectProvider<Collection<MeterRegistryCustomizer<?>>> customizers,
MetricsProperties properties) {
return new MeterRegistryPostProcessor(binders.getIfAvailable(),
filters.getIfAvailable(), customizers.getIfAvailable(),
properties.isUseGlobalRegistry());
ApplicationContext context) {
return new MeterRegistryPostProcessor(context);
}
@Bean
......
......@@ -38,11 +38,12 @@ import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.verifyZeroInteractions;
/**
* Tests for {@link MeterRegistryPostProcessor}.
* Tests for {@link MeterRegistryConfigurer}.
*
* @author Phillip Webb
* @author Andy Wilkinson
*/
public class MeterRegistryPostProcessorTests {
public class MeterRegistryConfigurerTests {
private List<MeterBinder> binders = new ArrayList<>();
......@@ -72,62 +73,50 @@ public class MeterRegistryPostProcessorTests {
}
@Test
public void postProcessWhenNotRegistryShouldReturnBean() {
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() {
public void configureWhenCompositeShouldSkip() {
this.binders.add(this.mockBinder);
this.customizers.add(this.mockCustomizer);
MeterRegistryPostProcessor processor = new MeterRegistryPostProcessor(
this.binders, this.filters, this.customizers, false);
processor.postProcessAfterInitialization(new CompositeMeterRegistry(), "name");
MeterRegistryConfigurer configurer = new MeterRegistryConfigurer(this.binders,
this.filters, this.customizers, false);
configurer.configure(new CompositeMeterRegistry());
verifyZeroInteractions(this.mockBinder, this.mockCustomizer);
}
@Test
public void postProcessShouldApplyCustomizer() {
public void configureShouldApplyCustomizer() {
this.customizers.add(this.mockCustomizer);
MeterRegistryPostProcessor processor = new MeterRegistryPostProcessor(
this.binders, this.filters, this.customizers, false);
processor.postProcessAfterInitialization(this.mockRegistry, "name");
MeterRegistryConfigurer configurer = new MeterRegistryConfigurer(this.binders,
this.filters, this.customizers, false);
configurer.configure(this.mockRegistry);
verify(this.mockCustomizer).customize(this.mockRegistry);
}
@Test
public void postProcessShouldApplyFilter() {
public void configureShouldApplyFilter() {
this.filters.add(this.mockFilter);
MeterRegistryPostProcessor processor = new MeterRegistryPostProcessor(
this.binders, this.filters, this.customizers, false);
processor.postProcessAfterInitialization(this.mockRegistry, "name");
MeterRegistryConfigurer configurer = new MeterRegistryConfigurer(this.binders,
this.filters, this.customizers, false);
configurer.configure(this.mockRegistry);
verify(this.mockConfig).meterFilter(this.mockFilter);
}
@Test
public void postProcessShouldApplyBinder() {
public void configureShouldApplyBinder() {
this.binders.add(this.mockBinder);
MeterRegistryPostProcessor processor = new MeterRegistryPostProcessor(
this.binders, this.filters, this.customizers, false);
processor.postProcessAfterInitialization(this.mockRegistry, "name");
MeterRegistryConfigurer configurer = new MeterRegistryConfigurer(this.binders,
this.filters, this.customizers, false);
configurer.configure(this.mockRegistry);
verify(this.mockBinder).bindTo(this.mockRegistry);
}
@Test
public void postProcessShouldBeCallInOrderCustomizeFilterBinder() {
public void configureShouldBeCalledInOrderCustomizerFilterBinder() {
this.customizers.add(this.mockCustomizer);
this.filters.add(this.mockFilter);
this.binders.add(this.mockBinder);
MeterRegistryPostProcessor processor = new MeterRegistryPostProcessor(
this.binders, this.filters, this.customizers, false);
processor.postProcessAfterInitialization(this.mockRegistry, "name");
MeterRegistryConfigurer configurer = new MeterRegistryConfigurer(this.binders,
this.filters, this.customizers, false);
configurer.configure(this.mockRegistry);
InOrder ordered = inOrder(this.mockBinder, this.mockConfig, this.mockCustomizer);
ordered.verify(this.mockCustomizer).customize(this.mockRegistry);
ordered.verify(this.mockConfig).meterFilter(this.mockFilter);
......@@ -135,11 +124,11 @@ public class MeterRegistryPostProcessorTests {
}
@Test
public void postProcessWhenAddToGlobalRegistryShouldAddToGlobalRegistry() {
MeterRegistryPostProcessor processor = new MeterRegistryPostProcessor(
this.binders, this.filters, this.customizers, true);
public void configureWhenAddToGlobalRegistryShouldAddToGlobalRegistry() {
MeterRegistryConfigurer configurer = new MeterRegistryConfigurer(this.binders,
this.filters, this.customizers, true);
try {
processor.postProcessAfterInitialization(this.mockRegistry, "name");
configurer.configure(this.mockRegistry);
assertThat(Metrics.globalRegistry.getRegistries())
.contains(this.mockRegistry);
}
......@@ -149,10 +138,10 @@ public class MeterRegistryPostProcessorTests {
}
@Test
public void postProcessWhenNotAddToGlobalRegistryShouldAddToGlobalRegistry() {
MeterRegistryPostProcessor processor = new MeterRegistryPostProcessor(
this.binders, this.filters, this.customizers, false);
processor.postProcessAfterInitialization(this.mockRegistry, "name");
public void configureWhenNotAddToGlobalRegistryShouldAddToGlobalRegistry() {
MeterRegistryConfigurer configurer = new MeterRegistryConfigurer(this.binders,
this.filters, this.customizers, false);
configurer.configure(this.mockRegistry);
assertThat(Metrics.globalRegistry.getRegistries())
.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