Fix MeterRegistry eager load

If there is a `MeterRegistry` bean (any) in the application context,
we add a `MicrometerMetricsCaptor` bean which populates meters further
from the components.
In some case we may load a `MicrometerMetricsCaptor` bean too early
so, not all the stuff around `MeterRegistry` maybe ready.
See Spring Boot and its `MetricsAutoConfiguration`

* Fix the `MicrometerMetricsCaptorRegistrar` the way to rely on the
`ObjectProvider<MeterRegistry>` instead
* Add package protected ctor to the `MicrometerMetricsCaptor` to
provide a target `MeterRegistry` on demand

All of that will ensure that we use an already post-processed `MeterRegistry`
including Spring Boot auto-configuration

**Cherry-pick to 5.3.x & 5.2.x**
This commit is contained in:
Artem Bilan
2020-10-15 14:50:38 -04:00
committed by Gary Russell
parent cab16c41f4
commit d60f23549c
3 changed files with 40 additions and 26 deletions

View File

@@ -20,6 +20,7 @@ import java.util.concurrent.TimeUnit;
import java.util.function.ToDoubleFunction;
import org.springframework.beans.factory.NoSuchBeanDefinitionException;
import org.springframework.beans.factory.ObjectProvider;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.GenericApplicationContext;
import org.springframework.integration.support.management.metrics.CounterFacade;
@@ -49,31 +50,44 @@ public class MicrometerMetricsCaptor implements MetricsCaptor {
public static final String MICROMETER_CAPTOR_NAME = "integrationMicrometerMetricsCaptor";
protected final MeterRegistry meterRegistry; // NOSONAR
private MeterRegistry meterRegistry;
private ObjectProvider<MeterRegistry> meterRegistryProvider;
public MicrometerMetricsCaptor(MeterRegistry meterRegistry) {
Assert.notNull(meterRegistry, "meterRegistry cannot be null");
this.meterRegistry = meterRegistry;
}
MicrometerMetricsCaptor(ObjectProvider<MeterRegistry> meterRegistryProvider) {
this.meterRegistryProvider = meterRegistryProvider;
}
public MeterRegistry getMeterRegistry() {
if (this.meterRegistry == null) {
this.meterRegistry = this.meterRegistryProvider.getIfUnique();
}
return this.meterRegistry;
}
@Override
public TimerBuilder timerBuilder(String name) {
return new MicroTimerBuilder(this.meterRegistry, name);
return new MicroTimerBuilder(getMeterRegistry(), name);
}
@Override
public CounterBuilder counterBuilder(String name) {
return new MicroCounterBuilder(this.meterRegistry, name);
return new MicroCounterBuilder(getMeterRegistry(), name);
}
@Override
public GaugeBuilder gaugeBuilder(String name, Object obj, ToDoubleFunction<Object> f) {
return new MicroGaugeBuilder(this.meterRegistry, name, obj, f);
return new MicroGaugeBuilder(getMeterRegistry(), name, obj, f);
}
@Override
public SampleFacade start() {
return new MicroSample(Timer.start(this.meterRegistry));
return new MicroSample(Timer.start(getMeterRegistry()));
}
@Override

View File

@@ -51,19 +51,16 @@ public class MicrometerMetricsCaptorRegistrar implements ImportBeanDefinitionReg
@Override
public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
ListableBeanFactory beanFactory = (ListableBeanFactory) registry;
if (METER_REGISTRY_CLASS != null
&& !registry.containsBeanDefinition(MicrometerMetricsCaptor.MICROMETER_CAPTOR_NAME)) {
String[] beanNamesForType =
((ListableBeanFactory) registry).getBeanNamesForType(METER_REGISTRY_CLASS, false, false);
for (String beanName : beanNamesForType) {
registry.registerBeanDefinition(MicrometerMetricsCaptor.MICROMETER_CAPTOR_NAME,
BeanDefinitionBuilder.genericBeanDefinition(MicrometerMetricsCaptor.class)
.addConstructorArgReference(beanName)
.setRole(BeanDefinition.ROLE_INFRASTRUCTURE)
.getBeanDefinition());
return;
}
&& !registry.containsBeanDefinition(MicrometerMetricsCaptor.MICROMETER_CAPTOR_NAME)
&& beanFactory.getBeanNamesForType(METER_REGISTRY_CLASS, false, false).length > 0) {
registry.registerBeanDefinition(MicrometerMetricsCaptor.MICROMETER_CAPTOR_NAME,
BeanDefinitionBuilder.genericBeanDefinition(MicrometerMetricsCaptor.class)
.addConstructorArgValue(beanFactory.getBeanProvider(METER_REGISTRY_CLASS))
.setRole(BeanDefinition.ROLE_INFRASTRUCTURE)
.getBeanDefinition());
}
}

View File

@@ -23,9 +23,10 @@ import static org.mockito.Mockito.when;
import java.util.HashMap;
import java.util.Map;
import org.junit.Test;
import org.junit.jupiter.api.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@@ -41,6 +42,7 @@ import org.springframework.messaging.MessageChannel;
/**
* @author Gary Russell
* @author Artem Bilan
*
* @since 4.2
*
@@ -69,7 +71,7 @@ public class IntegrationManagementConfigurerTests {
channel.setCountsEnabled(true);
channel.setStatsEnabled(true);
ApplicationContext ctx = mock(ApplicationContext.class);
Map<String, IntegrationManagement> beans = new HashMap<String, IntegrationManagement>();
Map<String, IntegrationManagement> beans = new HashMap<>();
beans.put("foo", channel);
beans.put("bar", handler);
beans.put("baz", source);
@@ -88,14 +90,14 @@ public class IntegrationManagementConfigurerTests {
@Test
public void testEmptyAnnotation() {
AnnotationConfigApplicationContext ctx =
new AnnotationConfigApplicationContext(ConfigEmptyAnnotation.class);
AbstractMessageChannel channel = ctx.getBean("channel", AbstractMessageChannel.class);
assertThat(channel.isCountsEnabled()).isTrue();
assertThat(channel.isStatsEnabled()).isTrue();
channel = ctx.getBean("loggingOffChannel", AbstractMessageChannel.class);
assertThat(channel.isLoggingEnabled()).isFalse();
ctx.close();
try (ConfigurableApplicationContext ctx = new AnnotationConfigApplicationContext(ConfigEmptyAnnotation.class)) {
AbstractMessageChannel channel = ctx.getBean("channel", AbstractMessageChannel.class);
assertThat(channel.isCountsEnabled()).isTrue();
assertThat(channel.isStatsEnabled()).isTrue();
assertThat(channel.isLoggingEnabled()).isTrue();
channel = ctx.getBean("loggingOffChannel", AbstractMessageChannel.class);
assertThat(channel.isLoggingEnabled()).isFalse();
}
}
@Configuration
@@ -114,6 +116,7 @@ public class IntegrationManagementConfigurerTests {
directChannel.setLoggingEnabled(false);
return directChannel;
}
}
}