Commit 8f23ee4e authored by Phillip Webb's avatar Phillip Webb

Apply micrometer MeterFilter beans automatically

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

Fixes gh-11843
parent 3f5adfbc
...@@ -23,17 +23,17 @@ import io.micrometer.core.instrument.MeterRegistry; ...@@ -23,17 +23,17 @@ import io.micrometer.core.instrument.MeterRegistry;
import io.micrometer.core.instrument.Metrics; 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.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.beans.factory.config.BeanPostProcessor;
import org.springframework.boot.util.LambdaSafe; import org.springframework.boot.util.LambdaSafe;
/** /**
* {@link BeanPostProcessor} to apply {@link MeterRegistryCustomizer customizers} and * {@link BeanPostProcessor} to apply {@link MeterRegistryCustomizer customizers},
* {@link MeterBinder binters} and {@link Metrics#addRegistry global registration} to * {@link MeterFilter filters}, {@link MeterBinder binders} and {@link Metrics#addRegistry
* {@link MeterRegistry meter registries}. This post processor intentionally skips * global registration} to {@link MeterRegistry meter registries}. This post processor
* {@link CompositeMeterRegistry} with the assumptions that the registries it contains are * intentionally skips {@link CompositeMeterRegistry} with the assumptions that the
* beans and will be customized directly. * registries it contains are beans and will be customized directly.
* *
* @author Jon Schneider * @author Jon Schneider
* @author Phillip Webb * @author Phillip Webb
...@@ -42,15 +42,19 @@ class MeterRegistryPostProcessor implements BeanPostProcessor { ...@@ -42,15 +42,19 @@ class MeterRegistryPostProcessor implements BeanPostProcessor {
private final Collection<MeterRegistryCustomizer<?>> customizers; private final Collection<MeterRegistryCustomizer<?>> customizers;
private final Collection<MeterFilter> filters;
private final Collection<MeterBinder> binders; private final Collection<MeterBinder> binders;
private final boolean addToGlobalRegistry; private final boolean addToGlobalRegistry;
MeterRegistryPostProcessor(ObjectProvider<Collection<MeterBinder>> binders, MeterRegistryPostProcessor(Collection<MeterBinder> binders,
ObjectProvider<Collection<MeterRegistryCustomizer<?>>> customizers, Collection<MeterFilter> filters,
Collection<MeterRegistryCustomizer<?>> customizers,
boolean addToGlobalRegistry) { boolean addToGlobalRegistry) {
this.binders = binders.getIfAvailable(Collections::emptyList); this.binders = (binders != null ? binders : Collections.emptyList());
this.customizers = customizers.getIfAvailable(Collections::emptyList); this.filters = (filters != null ? filters : Collections.emptyList());
this.customizers = (customizers != null ? customizers : Collections.emptyList());
this.addToGlobalRegistry = addToGlobalRegistry; this.addToGlobalRegistry = addToGlobalRegistry;
} }
...@@ -74,6 +78,7 @@ class MeterRegistryPostProcessor implements BeanPostProcessor { ...@@ -74,6 +78,7 @@ class MeterRegistryPostProcessor implements BeanPostProcessor {
// Customizers must be applied before binders, as they may add custom tags or // Customizers must be applied before binders, as they may add custom tags or
// alter timer or summary configuration. // alter timer or summary configuration.
customize(registry); customize(registry);
addFilters(registry);
addBinders(registry); addBinders(registry);
if (this.addToGlobalRegistry && registry != Metrics.globalRegistry) { if (this.addToGlobalRegistry && registry != Metrics.globalRegistry) {
Metrics.addRegistry(registry); Metrics.addRegistry(registry);
...@@ -87,6 +92,10 @@ class MeterRegistryPostProcessor implements BeanPostProcessor { ...@@ -87,6 +92,10 @@ class MeterRegistryPostProcessor implements BeanPostProcessor {
.invoke((customizer) -> customizer.customize(registry)); .invoke((customizer) -> customizer.customize(registry));
} }
private void addFilters(MeterRegistry registry) {
this.filters.forEach(registry.config()::meterFilter);
}
private void addBinders(MeterRegistry registry) { private void addBinders(MeterRegistry registry) {
this.binders.forEach((binder) -> binder.bindTo(registry)); this.binders.forEach((binder) -> binder.bindTo(registry));
} }
......
...@@ -22,6 +22,7 @@ import io.micrometer.core.annotation.Timed; ...@@ -22,6 +22,7 @@ import io.micrometer.core.annotation.Timed;
import io.micrometer.core.instrument.Clock; import io.micrometer.core.instrument.Clock;
import io.micrometer.core.instrument.MeterRegistry; import io.micrometer.core.instrument.MeterRegistry;
import io.micrometer.core.instrument.binder.MeterBinder; import io.micrometer.core.instrument.binder.MeterBinder;
import io.micrometer.core.instrument.config.MeterFilter;
import org.springframework.beans.factory.ObjectProvider; import org.springframework.beans.factory.ObjectProvider;
import org.springframework.boot.actuate.autoconfigure.endpoint.condition.ConditionalOnEnabledEndpoint; import org.springframework.boot.actuate.autoconfigure.endpoint.condition.ConditionalOnEnabledEndpoint;
...@@ -73,9 +74,11 @@ public class MetricsAutoConfiguration { ...@@ -73,9 +74,11 @@ public class MetricsAutoConfiguration {
@Bean @Bean
public static MeterRegistryPostProcessor meterRegistryPostProcessor( public static MeterRegistryPostProcessor meterRegistryPostProcessor(
ObjectProvider<Collection<MeterBinder>> binders, ObjectProvider<Collection<MeterBinder>> binders,
ObjectProvider<Collection<MeterFilter>> filters,
ObjectProvider<Collection<MeterRegistryCustomizer<?>>> customizers, ObjectProvider<Collection<MeterRegistryCustomizer<?>>> customizers,
MetricsProperties properties) { MetricsProperties properties) {
return new MeterRegistryPostProcessor(binders, customizers, return new MeterRegistryPostProcessor(binders.getIfAvailable(),
filters.getIfAvailable(), customizers.getIfAvailable(),
properties.isUseGlobalRegistry()); properties.isUseGlobalRegistry());
} }
......
...@@ -17,24 +17,22 @@ ...@@ -17,24 +17,22 @@
package org.springframework.boot.actuate.autoconfigure.metrics; package org.springframework.boot.actuate.autoconfigure.metrics;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collection;
import java.util.List; import java.util.List;
import io.micrometer.core.instrument.MeterRegistry; import io.micrometer.core.instrument.MeterRegistry;
import io.micrometer.core.instrument.MeterRegistry.Config;
import io.micrometer.core.instrument.Metrics; 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.composite.CompositeMeterRegistry;
import io.micrometer.core.instrument.config.MeterFilter;
import org.junit.Before; import org.junit.Before;
import org.junit.Test; import org.junit.Test;
import org.mockito.InOrder; import org.mockito.InOrder;
import org.mockito.Mock; import org.mockito.Mock;
import org.mockito.MockitoAnnotations; 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.assertj.core.api.Assertions.assertThat;
import static org.mockito.BDDMockito.given;
import static org.mockito.Mockito.inOrder; import static org.mockito.Mockito.inOrder;
import static org.mockito.Mockito.verify; import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.verifyZeroInteractions; import static org.mockito.Mockito.verifyZeroInteractions;
...@@ -46,34 +44,37 @@ import static org.mockito.Mockito.verifyZeroInteractions; ...@@ -46,34 +44,37 @@ import static org.mockito.Mockito.verifyZeroInteractions;
*/ */
public class MeterRegistryPostProcessorTests { public class MeterRegistryPostProcessorTests {
private List<MeterBinder> binderBeans = new ArrayList<>(); private List<MeterBinder> binders = new ArrayList<>();
private List<MeterRegistryCustomizer<?>> customizerBeans = new ArrayList<>();
private ObjectProvider<Collection<MeterBinder>> binders = TestObjectProvider private List<MeterFilter> filters = new ArrayList<>();
.of(this.binderBeans);
private ObjectProvider<Collection<MeterRegistryCustomizer<?>>> customizers = TestObjectProvider private List<MeterRegistryCustomizer<?>> customizers = new ArrayList<>();
.of(this.customizerBeans);
@Mock @Mock
private MeterBinder mockBinder; private MeterBinder mockBinder;
@Mock
private MeterFilter mockFilter;
@Mock @Mock
private MeterRegistryCustomizer<MeterRegistry> mockCustomizer; private MeterRegistryCustomizer<MeterRegistry> mockCustomizer;
@Mock @Mock
private MeterRegistry mockRegistry; private MeterRegistry mockRegistry;
@Mock
private Config mockConfig;
@Before @Before
public void setup() { public void setup() {
MockitoAnnotations.initMocks(this); MockitoAnnotations.initMocks(this);
given(this.mockRegistry.config()).willReturn(this.mockConfig);
} }
@Test @Test
public void postProcessWhenNotRegistryShouldReturnBean() { public void postProcessWhenNotRegistryShouldReturnBean() {
MeterRegistryPostProcessor processor = new MeterRegistryPostProcessor( MeterRegistryPostProcessor processor = new MeterRegistryPostProcessor(
this.binders, this.customizers, false); this.binders, this.filters, this.customizers, false);
Object bean = new Object(); Object bean = new Object();
String beanName = "name"; String beanName = "name";
assertThat(processor.postProcessBeforeInitialization(bean, beanName)) assertThat(processor.postProcessBeforeInitialization(bean, beanName))
...@@ -84,10 +85,10 @@ public class MeterRegistryPostProcessorTests { ...@@ -84,10 +85,10 @@ public class MeterRegistryPostProcessorTests {
@Test @Test
public void postProcessorWhenCompositeShouldSkip() { public void postProcessorWhenCompositeShouldSkip() {
this.binderBeans.add(this.mockBinder); this.binders.add(this.mockBinder);
this.customizerBeans.add(this.mockCustomizer); this.customizers.add(this.mockCustomizer);
MeterRegistryPostProcessor processor = new MeterRegistryPostProcessor( MeterRegistryPostProcessor processor = new MeterRegistryPostProcessor(
this.binders, this.customizers, false); this.binders, this.filters, this.customizers, false);
assertThat(processor.postProcessAfterInitialization(new CompositeMeterRegistry(), assertThat(processor.postProcessAfterInitialization(new CompositeMeterRegistry(),
"name")); "name"));
verifyZeroInteractions(this.mockBinder, this.mockCustomizer); verifyZeroInteractions(this.mockBinder, this.mockCustomizer);
...@@ -95,38 +96,49 @@ public class MeterRegistryPostProcessorTests { ...@@ -95,38 +96,49 @@ public class MeterRegistryPostProcessorTests {
@Test @Test
public void postProcessShouldApplyCustomizer() { public void postProcessShouldApplyCustomizer() {
this.customizerBeans.add(this.mockCustomizer); this.customizers.add(this.mockCustomizer);
MeterRegistryPostProcessor processor = new MeterRegistryPostProcessor( MeterRegistryPostProcessor processor = new MeterRegistryPostProcessor(
this.binders, this.customizers, false); this.binders, this.filters, this.customizers, false);
assertThat(processor.postProcessAfterInitialization(this.mockRegistry, "name")); assertThat(processor.postProcessAfterInitialization(this.mockRegistry, "name"));
verify(this.mockCustomizer).customize(this.mockRegistry); verify(this.mockCustomizer).customize(this.mockRegistry);
} }
@Test
public void postProcessShouldApplyFilter() {
this.filters.add(this.mockFilter);
MeterRegistryPostProcessor processor = new MeterRegistryPostProcessor(
this.binders, this.filters, this.customizers, false);
assertThat(processor.postProcessAfterInitialization(this.mockRegistry, "name"));
verify(this.mockConfig).meterFilter(this.mockFilter);
}
@Test @Test
public void postProcessShouldApplyBinder() { public void postProcessShouldApplyBinder() {
this.binderBeans.add(this.mockBinder); this.binders.add(this.mockBinder);
MeterRegistryPostProcessor processor = new MeterRegistryPostProcessor( MeterRegistryPostProcessor processor = new MeterRegistryPostProcessor(
this.binders, this.customizers, false); this.binders, this.filters, this.customizers, false);
assertThat(processor.postProcessAfterInitialization(this.mockRegistry, "name")); assertThat(processor.postProcessAfterInitialization(this.mockRegistry, "name"));
verify(this.mockBinder).bindTo(this.mockRegistry); verify(this.mockBinder).bindTo(this.mockRegistry);
} }
@Test @Test
public void postProcessShouldCustomizeBeforeApplyingBinder() { public void postProcessShouldBeCallInOrderCustomizeFilterBinder() {
this.binderBeans.add(this.mockBinder); this.customizers.add(this.mockCustomizer);
this.customizerBeans.add(this.mockCustomizer); this.filters.add(this.mockFilter);
this.binders.add(this.mockBinder);
MeterRegistryPostProcessor processor = new MeterRegistryPostProcessor( MeterRegistryPostProcessor processor = new MeterRegistryPostProcessor(
this.binders, this.customizers, false); this.binders, this.filters, this.customizers, false);
processor.postProcessAfterInitialization(this.mockRegistry, "name"); 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.mockCustomizer).customize(this.mockRegistry);
ordered.verify(this.mockConfig).meterFilter(this.mockFilter);
ordered.verify(this.mockBinder).bindTo(this.mockRegistry); ordered.verify(this.mockBinder).bindTo(this.mockRegistry);
} }
@Test @Test
public void postProcessWhenAddToGlobalRegistryShouldAddToGlobalRegistry() { public void postProcessWhenAddToGlobalRegistryShouldAddToGlobalRegistry() {
MeterRegistryPostProcessor processor = new MeterRegistryPostProcessor( MeterRegistryPostProcessor processor = new MeterRegistryPostProcessor(
this.binders, this.customizers, true); this.binders, this.filters, this.customizers, true);
try { try {
processor.postProcessAfterInitialization(this.mockRegistry, "name"); processor.postProcessAfterInitialization(this.mockRegistry, "name");
assertThat(Metrics.globalRegistry.getRegistries()) assertThat(Metrics.globalRegistry.getRegistries())
...@@ -140,45 +152,10 @@ public class MeterRegistryPostProcessorTests { ...@@ -140,45 +152,10 @@ public class MeterRegistryPostProcessorTests {
@Test @Test
public void postProcessWhenNotAddToGlobalRegistryShouldAddToGlobalRegistry() { public void postProcessWhenNotAddToGlobalRegistryShouldAddToGlobalRegistry() {
MeterRegistryPostProcessor processor = new MeterRegistryPostProcessor( MeterRegistryPostProcessor processor = new MeterRegistryPostProcessor(
this.binders, this.customizers, false); this.binders, this.filters, this.customizers, false);
processor.postProcessAfterInitialization(this.mockRegistry, "name"); processor.postProcessAfterInitialization(this.mockRegistry, "name");
assertThat(Metrics.globalRegistry.getRegistries()) assertThat(Metrics.globalRegistry.getRegistries())
.doesNotContain(this.mockRegistry); .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);
}
}
} }
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