Apply micrometer MeterFilter beans automatically

Update `MeterRegistryPostProcessor` and `MetricsAutoConfiguration` so
that micrometer `MeterFilter` beans are automatically applied.

Fixes gh-11843
This commit is contained in:
Phillip Webb 2018-01-30 00:15:33 -08:00
parent 3f5adfbccc
commit 8f23ee4e58
3 changed files with 63 additions and 74 deletions

View File

@ -23,17 +23,17 @@ 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.ObjectProvider;
import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.boot.util.LambdaSafe;
/**
* {@link BeanPostProcessor} to apply {@link MeterRegistryCustomizer customizers} and
* {@link MeterBinder binters} 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} 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
@ -42,15 +42,19 @@ class MeterRegistryPostProcessor implements BeanPostProcessor {
private final Collection<MeterRegistryCustomizer<?>> customizers;
private final Collection<MeterFilter> filters;
private final Collection<MeterBinder> binders;
private final boolean addToGlobalRegistry;
MeterRegistryPostProcessor(ObjectProvider<Collection<MeterBinder>> binders,
ObjectProvider<Collection<MeterRegistryCustomizer<?>>> customizers,
MeterRegistryPostProcessor(Collection<MeterBinder> binders,
Collection<MeterFilter> filters,
Collection<MeterRegistryCustomizer<?>> customizers,
boolean addToGlobalRegistry) {
this.binders = binders.getIfAvailable(Collections::emptyList);
this.customizers = customizers.getIfAvailable(Collections::emptyList);
this.binders = (binders != null ? binders : Collections.emptyList());
this.filters = (filters != null ? filters : Collections.emptyList());
this.customizers = (customizers != null ? customizers : Collections.emptyList());
this.addToGlobalRegistry = addToGlobalRegistry;
}
@ -74,6 +78,7 @@ class MeterRegistryPostProcessor implements BeanPostProcessor {
// 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);
@ -87,6 +92,10 @@ class MeterRegistryPostProcessor implements BeanPostProcessor {
.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));
}

View File

@ -22,6 +22,7 @@ import io.micrometer.core.annotation.Timed;
import io.micrometer.core.instrument.Clock;
import io.micrometer.core.instrument.MeterRegistry;
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.endpoint.condition.ConditionalOnEnabledEndpoint;
@ -73,9 +74,11 @@ 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, customizers,
return new MeterRegistryPostProcessor(binders.getIfAvailable(),
filters.getIfAvailable(), customizers.getIfAvailable(),
properties.isUseGlobalRegistry());
}

View File

@ -17,24 +17,22 @@
package org.springframework.boot.actuate.autoconfigure.metrics;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import io.micrometer.core.instrument.MeterRegistry;
import io.micrometer.core.instrument.MeterRegistry.Config;
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.junit.Before;
import org.junit.Test;
import org.mockito.InOrder;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.ObjectProvider;
import org.springframework.util.Assert;
import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.BDDMockito.given;
import static org.mockito.Mockito.inOrder;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.verifyZeroInteractions;
@ -46,34 +44,37 @@ import static org.mockito.Mockito.verifyZeroInteractions;
*/
public class MeterRegistryPostProcessorTests {
private List<MeterBinder> binderBeans = new ArrayList<>();
private List<MeterBinder> binders = new ArrayList<>();
private List<MeterRegistryCustomizer<?>> customizerBeans = new ArrayList<>();
private List<MeterFilter> filters = new ArrayList<>();
private ObjectProvider<Collection<MeterBinder>> binders = TestObjectProvider
.of(this.binderBeans);
private ObjectProvider<Collection<MeterRegistryCustomizer<?>>> customizers = TestObjectProvider
.of(this.customizerBeans);
private List<MeterRegistryCustomizer<?>> customizers = new ArrayList<>();
@Mock
private MeterBinder mockBinder;
@Mock
private MeterFilter mockFilter;
@Mock
private MeterRegistryCustomizer<MeterRegistry> mockCustomizer;
@Mock
private MeterRegistry mockRegistry;
@Mock
private Config mockConfig;
@Before
public void setup() {
MockitoAnnotations.initMocks(this);
given(this.mockRegistry.config()).willReturn(this.mockConfig);
}
@Test
public void postProcessWhenNotRegistryShouldReturnBean() {
MeterRegistryPostProcessor processor = new MeterRegistryPostProcessor(
this.binders, this.customizers, false);
this.binders, this.filters, this.customizers, false);
Object bean = new Object();
String beanName = "name";
assertThat(processor.postProcessBeforeInitialization(bean, beanName))
@ -84,10 +85,10 @@ public class MeterRegistryPostProcessorTests {
@Test
public void postProcessorWhenCompositeShouldSkip() {
this.binderBeans.add(this.mockBinder);
this.customizerBeans.add(this.mockCustomizer);
this.binders.add(this.mockBinder);
this.customizers.add(this.mockCustomizer);
MeterRegistryPostProcessor processor = new MeterRegistryPostProcessor(
this.binders, this.customizers, false);
this.binders, this.filters, this.customizers, false);
assertThat(processor.postProcessAfterInitialization(new CompositeMeterRegistry(),
"name"));
verifyZeroInteractions(this.mockBinder, this.mockCustomizer);
@ -95,38 +96,49 @@ public class MeterRegistryPostProcessorTests {
@Test
public void postProcessShouldApplyCustomizer() {
this.customizerBeans.add(this.mockCustomizer);
this.customizers.add(this.mockCustomizer);
MeterRegistryPostProcessor processor = new MeterRegistryPostProcessor(
this.binders, this.customizers, false);
this.binders, this.filters, this.customizers, false);
assertThat(processor.postProcessAfterInitialization(this.mockRegistry, "name"));
verify(this.mockCustomizer).customize(this.mockRegistry);
}
@Test
public void postProcessShouldApplyBinder() {
this.binderBeans.add(this.mockBinder);
public void postProcessShouldApplyFilter() {
this.filters.add(this.mockFilter);
MeterRegistryPostProcessor processor = new MeterRegistryPostProcessor(
this.binders, this.customizers, false);
this.binders, this.filters, this.customizers, false);
assertThat(processor.postProcessAfterInitialization(this.mockRegistry, "name"));
verify(this.mockConfig).meterFilter(this.mockFilter);
}
@Test
public void postProcessShouldApplyBinder() {
this.binders.add(this.mockBinder);
MeterRegistryPostProcessor processor = new MeterRegistryPostProcessor(
this.binders, this.filters, this.customizers, false);
assertThat(processor.postProcessAfterInitialization(this.mockRegistry, "name"));
verify(this.mockBinder).bindTo(this.mockRegistry);
}
@Test
public void postProcessShouldCustomizeBeforeApplyingBinder() {
this.binderBeans.add(this.mockBinder);
this.customizerBeans.add(this.mockCustomizer);
public void postProcessShouldBeCallInOrderCustomizeFilterBinder() {
this.customizers.add(this.mockCustomizer);
this.filters.add(this.mockFilter);
this.binders.add(this.mockBinder);
MeterRegistryPostProcessor processor = new MeterRegistryPostProcessor(
this.binders, this.customizers, false);
this.binders, this.filters, this.customizers, false);
processor.postProcessAfterInitialization(this.mockRegistry, "name");
InOrder ordered = inOrder(this.mockCustomizer, this.mockBinder);
InOrder ordered = inOrder(this.mockBinder, this.mockConfig, this.mockCustomizer);
ordered.verify(this.mockCustomizer).customize(this.mockRegistry);
ordered.verify(this.mockConfig).meterFilter(this.mockFilter);
ordered.verify(this.mockBinder).bindTo(this.mockRegistry);
}
@Test
public void postProcessWhenAddToGlobalRegistryShouldAddToGlobalRegistry() {
MeterRegistryPostProcessor processor = new MeterRegistryPostProcessor(
this.binders, this.customizers, true);
this.binders, this.filters, this.customizers, true);
try {
processor.postProcessAfterInitialization(this.mockRegistry, "name");
assertThat(Metrics.globalRegistry.getRegistries())
@ -140,45 +152,10 @@ public class MeterRegistryPostProcessorTests {
@Test
public void postProcessWhenNotAddToGlobalRegistryShouldAddToGlobalRegistry() {
MeterRegistryPostProcessor processor = new MeterRegistryPostProcessor(
this.binders, this.customizers, false);
this.binders, this.filters, this.customizers, false);
processor.postProcessAfterInitialization(this.mockRegistry, "name");
assertThat(Metrics.globalRegistry.getRegistries())
.doesNotContain(this.mockRegistry);
}
private static class TestObjectProvider<T> implements ObjectProvider<T> {
private final T value;
TestObjectProvider(T value) {
this.value = value;
}
@Override
public T getObject() throws BeansException {
Assert.state(this.value != null, "No value");
return this.value;
}
@Override
public T getObject(Object... args) throws BeansException {
return this.value;
}
@Override
public T getIfAvailable() throws BeansException {
return this.value;
}
@Override
public T getIfUnique() throws BeansException {
throw new UnsupportedOperationException();
}
public static <T> TestObjectProvider<T> of(T value) {
return new TestObjectProvider<>(value);
}
}
}