Configure MeterBinders after beans have been created

Update `MeterRegistryPostProcessor` to configure `MeterRegistry` beans
in two distinct sweeps. The first sweep applies customizers and filters
as the `MeterRegistry` bean is initialized, the second sweep applies
`MeterBinder` beans once all singletons have been instantiated.

Prior to this commit, it was not possible for a `MeterBinder` bean to
directly or indirectly use a `MeterRegistry`. It was also possible for
bound meters to cause a deadlock during refresh processing if those
meters could be updated on a thread other than main, such as GC
notifications.

Fixes gh-30636
Fixes gh-33070
This commit is contained in:
Phillip Webb
2022-05-18 15:21:04 -07:00
committed by Andy Wilkinson
parent e60084112e
commit e5a0b164ac
7 changed files with 244 additions and 166 deletions

View File

@@ -1,86 +0,0 @@
/*
* Copyright 2012-2022 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
*
* https://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 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.boot.util.LambdaSafe;
/**
* Configurer to apply {@link MeterRegistryCustomizer customizers}, {@link MeterFilter
* filters}, {@link MeterBinder binders} and {@link Metrics#addRegistry global
* registration} to {@link MeterRegistry meter registries}.
*
* @author Jon Schneider
* @author Phillip Webb
*/
class MeterRegistryConfigurer {
private final ObjectProvider<MeterRegistryCustomizer<?>> customizers;
private final ObjectProvider<MeterFilter> filters;
private final ObjectProvider<MeterBinder> binders;
private final boolean addToGlobalRegistry;
private final boolean hasCompositeMeterRegistry;
MeterRegistryConfigurer(ObjectProvider<MeterRegistryCustomizer<?>> customizers, ObjectProvider<MeterFilter> filters,
ObjectProvider<MeterBinder> binders, boolean addToGlobalRegistry, boolean hasCompositeMeterRegistry) {
this.customizers = customizers;
this.filters = filters;
this.binders = binders;
this.addToGlobalRegistry = addToGlobalRegistry;
this.hasCompositeMeterRegistry = hasCompositeMeterRegistry;
}
void configure(MeterRegistry registry) {
// Customizers must be applied before binders, as they may add custom
// tags or alter timer or summary configuration.
customize(registry);
if (!(registry instanceof AutoConfiguredCompositeMeterRegistry)) {
addFilters(registry);
}
if (!this.hasCompositeMeterRegistry || registry instanceof CompositeMeterRegistry) {
addBinders(registry);
}
if (this.addToGlobalRegistry && registry != Metrics.globalRegistry) {
Metrics.addRegistry(registry);
}
}
@SuppressWarnings("unchecked")
private void customize(MeterRegistry registry) {
LambdaSafe.callbacks(MeterRegistryCustomizer.class, this.customizers.orderedStream().toList(), registry)
.withLogger(MeterRegistryConfigurer.class).invoke((customizer) -> customizer.customize(registry));
}
private void addFilters(MeterRegistry registry) {
this.filters.orderedStream().forEach(registry.config()::meterFilter);
}
private void addBinders(MeterRegistry registry) {
this.binders.orderedStream().forEach((binder) -> binder.bindTo(registry));
}
}

View File

@@ -16,65 +16,138 @@
package org.springframework.boot.actuate.autoconfigure.metrics;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Set;
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.ObjectProvider;
import org.springframework.beans.factory.SmartInitializingSingleton;
import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.boot.util.LambdaSafe;
import org.springframework.context.ApplicationContext;
/**
* {@link BeanPostProcessor} that delegates to a lazily created
* {@link MeterRegistryConfigurer} to post-process {@link MeterRegistry} beans.
* {@link BeanPostProcessor} for {@link MeterRegistry} beans.
*
* @author Jon Schneider
* @author Phillip Webb
* @author Andy Wilkinson
*/
class MeterRegistryPostProcessor implements BeanPostProcessor {
class MeterRegistryPostProcessor implements BeanPostProcessor, SmartInitializingSingleton {
private final ObjectProvider<MeterBinder> meterBinders;
private final boolean hasNoCompositeMeterRegistryBeans;
private final ObjectProvider<MeterFilter> meterFilters;
private final boolean useGlobalRegistry;
private final ObjectProvider<MeterRegistryCustomizer<?>> meterRegistryCustomizers;
private final ObjectProvider<MeterRegistryCustomizer<?>> customizers;
private final ObjectProvider<MetricsProperties> metricsProperties;
private final ObjectProvider<MeterFilter> filters;
private volatile MeterRegistryConfigurer configurer;
private final ObjectProvider<MeterBinder> binders;
private final ApplicationContext applicationContext;
private volatile boolean deferBinding = true;
private final Set<MeterRegistry> deferredBindings = new LinkedHashSet<>();
MeterRegistryPostProcessor(ApplicationContext applicationContext, MetricsProperties metricsProperties,
ObjectProvider<MeterRegistryCustomizer<?>> customizers, ObjectProvider<MeterFilter> filters,
ObjectProvider<MeterBinder> binders) {
this(hasNoCompositeMeterRegistryBeans(applicationContext), metricsProperties.isUseGlobalRegistry(), customizers,
filters, binders);
}
private static boolean hasNoCompositeMeterRegistryBeans(ApplicationContext applicationContext) {
return applicationContext.getBeanNamesForType(CompositeMeterRegistry.class, false, false).length == 0;
}
MeterRegistryPostProcessor(boolean hasNoCompositeMeterRegistryBeans, boolean useGlobalRegistry,
ObjectProvider<MeterRegistryCustomizer<?>> customizers, ObjectProvider<MeterFilter> filters,
ObjectProvider<MeterBinder> binders) {
this.hasNoCompositeMeterRegistryBeans = hasNoCompositeMeterRegistryBeans;
this.useGlobalRegistry = useGlobalRegistry;
this.customizers = customizers;
this.filters = filters;
this.binders = binders;
MeterRegistryPostProcessor(ObjectProvider<MeterBinder> meterBinders, ObjectProvider<MeterFilter> meterFilters,
ObjectProvider<MeterRegistryCustomizer<?>> meterRegistryCustomizers,
ObjectProvider<MetricsProperties> metricsProperties, ApplicationContext applicationContext) {
this.meterBinders = meterBinders;
this.meterFilters = meterFilters;
this.meterRegistryCustomizers = meterRegistryCustomizers;
this.metricsProperties = metricsProperties;
this.applicationContext = applicationContext;
}
@Override
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
if (bean instanceof MeterRegistry meterRegistry) {
getConfigurer().configure(meterRegistry);
postProcessMeterRegistry(meterRegistry);
}
return bean;
}
private MeterRegistryConfigurer getConfigurer() {
if (this.configurer == null) {
boolean hasCompositeMeterRegistry = this.applicationContext
.getBeanNamesForType(CompositeMeterRegistry.class, false, false).length != 0;
this.configurer = new MeterRegistryConfigurer(this.meterRegistryCustomizers, this.meterFilters,
this.meterBinders, this.metricsProperties.getObject().isUseGlobalRegistry(),
hasCompositeMeterRegistry);
@Override
public void afterSingletonsInstantiated() {
synchronized (this.deferredBindings) {
this.deferBinding = false;
this.deferredBindings.forEach(this::applyBinders);
}
return this.configurer;
}
private void postProcessMeterRegistry(MeterRegistry meterRegistry) {
// Customizers must be applied before binders, as they may add custom tags or
// alter timer or summary configuration.
applyCustomizers(meterRegistry);
applyFilters(meterRegistry);
addToGlobalRegistryIfNecessary(meterRegistry);
if (isBindable(meterRegistry)) {
applyBinders(meterRegistry);
}
}
@SuppressWarnings("unchecked")
private void applyCustomizers(MeterRegistry meterRegistry) {
List<MeterRegistryCustomizer<?>> customizers = this.customizers.orderedStream().toList();
LambdaSafe.callbacks(MeterRegistryCustomizer.class, customizers, meterRegistry)
.withLogger(MeterRegistryPostProcessor.class)
.invoke((customizer) -> customizer.customize(meterRegistry));
}
private void applyFilters(MeterRegistry meterRegistry) {
if (meterRegistry instanceof AutoConfiguredCompositeMeterRegistry) {
return;
}
this.filters.orderedStream().forEach(meterRegistry.config()::meterFilter);
}
private void addToGlobalRegistryIfNecessary(MeterRegistry meterRegistry) {
if (this.useGlobalRegistry && !isGlobalRegistry(meterRegistry)) {
Metrics.addRegistry(meterRegistry);
}
}
private boolean isGlobalRegistry(MeterRegistry meterRegistry) {
return meterRegistry == Metrics.globalRegistry;
}
private boolean isBindable(MeterRegistry meterRegistry) {
return this.hasNoCompositeMeterRegistryBeans || isCompositeMeterRegistry(meterRegistry);
}
private boolean isCompositeMeterRegistry(MeterRegistry meterRegistry) {
return meterRegistry instanceof CompositeMeterRegistry;
}
void applyBinders(MeterRegistry meterRegistry) {
if (this.deferBinding) {
synchronized (this.deferredBindings) {
if (this.deferBinding) {
this.deferredBindings.add(meterRegistry);
return;
}
}
}
this.binders.orderedStream().forEach((binder) -> binder.bindTo(meterRegistry));
}
}

View File

@@ -50,12 +50,11 @@ public class MetricsAutoConfiguration {
}
@Bean
public static MeterRegistryPostProcessor meterRegistryPostProcessor(ObjectProvider<MeterBinder> meterBinders,
ObjectProvider<MeterFilter> meterFilters,
ObjectProvider<MeterRegistryCustomizer<?>> meterRegistryCustomizers,
ObjectProvider<MetricsProperties> metricsProperties, ApplicationContext applicationContext) {
return new MeterRegistryPostProcessor(meterBinders, meterFilters, meterRegistryCustomizers, metricsProperties,
applicationContext);
public static MeterRegistryPostProcessor meterRegistryPostProcessor(ApplicationContext applicationContext,
MetricsProperties metricsProperties, ObjectProvider<MeterRegistryCustomizer<?>> meterRegistryCustomizers,
ObjectProvider<MeterFilter> meterFilters, ObjectProvider<MeterBinder> meterBinders) {
return new MeterRegistryPostProcessor(applicationContext, metricsProperties, meterRegistryCustomizers,
meterFilters, meterBinders);
}
@Bean

View File

@@ -0,0 +1,66 @@
/*
* Copyright 2012-2022 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
*
* https://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.amqp;
import io.micrometer.core.instrument.MeterRegistry;
import io.micrometer.core.instrument.binder.MeterBinder;
import org.junit.jupiter.api.Test;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.boot.actuate.autoconfigure.metrics.MetricsAutoConfiguration;
import org.springframework.boot.actuate.autoconfigure.metrics.amqp.RabbitMetricsAutoConfiguration;
import org.springframework.boot.actuate.autoconfigure.metrics.export.simple.SimpleMetricsExportAutoConfiguration;
import org.springframework.boot.autoconfigure.amqp.RabbitAutoConfiguration;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
/**
* Integration test to check that {@link RabbitMetricsAutoConfiguration} does not cause a
* dependency cycle when used with {@link MeterBinder}.
*
* @author Phillip Webb
* @see <a href="https://github.com/spring-projects/spring-boot/issues/30636">gh-30636</a>
*/
class RabbitMetricsAutoConfigurationMeterBinderCycleIntegrationTests {
@Test
void doesNotFormCycle() {
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(TestConfig.class);
context.getBean(TestService.class);
context.close();
}
@Configuration
@Import({ TestService.class, RabbitAutoConfiguration.class, MetricsAutoConfiguration.class,
SimpleMetricsExportAutoConfiguration.class, RabbitMetricsAutoConfiguration.class })
static class TestConfig {
}
static class TestService implements MeterBinder {
TestService(RabbitTemplate rabbitTemplate) {
}
@Override
public void bindTo(MeterRegistry registry) {
}
}
}

View File

@@ -40,28 +40,28 @@ import static org.mockito.Mockito.inOrder;
import static org.mockito.Mockito.mock;
/**
* Tests for {@link MeterRegistryConfigurer}.
* Tests for {@link MeterRegistryPostProcessor}.
*
* @author Phillip Webb
* @author Andy Wilkinson
*/
@ExtendWith(MockitoExtension.class)
class MeterRegistryConfigurerTests {
private List<MeterBinder> binders = new ArrayList<>();
private List<MeterFilter> filters = new ArrayList<>();
class MeterRegistryPostProcessorTests {
private List<MeterRegistryCustomizer<?>> customizers = new ArrayList<>();
private List<MeterFilter> filters = new ArrayList<>();
private List<MeterBinder> binders = new ArrayList<>();
@Mock
private MeterBinder mockBinder;
private MeterRegistryCustomizer<MeterRegistry> mockCustomizer;
@Mock
private MeterFilter mockFilter;
@Mock
private MeterRegistryCustomizer<MeterRegistry> mockCustomizer;
private MeterBinder mockBinder;
@Mock
private MeterRegistry mockRegistry;
@@ -70,73 +70,79 @@ class MeterRegistryConfigurerTests {
private Config mockConfig;
@Test
void configureWhenCompositeShouldApplyCustomizer() {
void postProcessAndInitializeWhenCompositeAppliesCustomizer() {
this.customizers.add(this.mockCustomizer);
MeterRegistryConfigurer configurer = new MeterRegistryConfigurer(createObjectProvider(this.customizers),
createObjectProvider(this.filters), createObjectProvider(this.binders), false, false);
MeterRegistryPostProcessor processor = new MeterRegistryPostProcessor(false, false,
createObjectProvider(this.customizers), createObjectProvider(this.filters),
createObjectProvider(this.binders));
CompositeMeterRegistry composite = new CompositeMeterRegistry();
configurer.configure(composite);
postProcessAndInitialize(processor, composite);
then(this.mockCustomizer).should().customize(composite);
}
@Test
void configureShouldApplyCustomizer() {
void postProcessAndInitializeAppliesCustomizer() {
given(this.mockRegistry.config()).willReturn(this.mockConfig);
this.customizers.add(this.mockCustomizer);
MeterRegistryConfigurer configurer = new MeterRegistryConfigurer(createObjectProvider(this.customizers),
createObjectProvider(this.filters), createObjectProvider(this.binders), false, false);
configurer.configure(this.mockRegistry);
MeterRegistryPostProcessor processor = new MeterRegistryPostProcessor(true, false,
createObjectProvider(this.customizers), createObjectProvider(this.filters),
createObjectProvider(this.binders));
postProcessAndInitialize(processor, this.mockRegistry);
then(this.mockCustomizer).should().customize(this.mockRegistry);
}
@Test
void configureShouldApplyFilter() {
void postProcessAndInitializeAppliesFilter() {
given(this.mockRegistry.config()).willReturn(this.mockConfig);
this.filters.add(this.mockFilter);
MeterRegistryConfigurer configurer = new MeterRegistryConfigurer(createObjectProvider(this.customizers),
createObjectProvider(this.filters), createObjectProvider(this.binders), false, false);
configurer.configure(this.mockRegistry);
MeterRegistryPostProcessor processor = new MeterRegistryPostProcessor(true, false,
createObjectProvider(this.customizers), createObjectProvider(this.filters),
createObjectProvider(this.binders));
postProcessAndInitialize(processor, this.mockRegistry);
then(this.mockConfig).should().meterFilter(this.mockFilter);
}
@Test
void configureShouldApplyBinder() {
void postProcessAndInitializeBindsTo() {
given(this.mockRegistry.config()).willReturn(this.mockConfig);
this.binders.add(this.mockBinder);
MeterRegistryConfigurer configurer = new MeterRegistryConfigurer(createObjectProvider(this.customizers),
createObjectProvider(this.filters), createObjectProvider(this.binders), false, false);
configurer.configure(this.mockRegistry);
MeterRegistryPostProcessor processor = new MeterRegistryPostProcessor(true, false,
createObjectProvider(this.customizers), createObjectProvider(this.filters),
createObjectProvider(this.binders));
postProcessAndInitialize(processor, this.mockRegistry);
then(this.mockBinder).should().bindTo(this.mockRegistry);
}
@Test
void configureShouldApplyBinderToComposite() {
void postProcessAndInitializeWhenCompositeBindsTo() {
this.binders.add(this.mockBinder);
MeterRegistryConfigurer configurer = new MeterRegistryConfigurer(createObjectProvider(this.customizers),
createObjectProvider(this.filters), createObjectProvider(this.binders), false, true);
MeterRegistryPostProcessor processor = new MeterRegistryPostProcessor(false, false,
createObjectProvider(this.customizers), createObjectProvider(this.filters),
createObjectProvider(this.binders));
CompositeMeterRegistry composite = new CompositeMeterRegistry();
configurer.configure(composite);
postProcessAndInitialize(processor, composite);
then(this.mockBinder).should().bindTo(composite);
}
@Test
void configureShouldNotApplyBinderWhenCompositeExists() {
void postProcessAndInitializeWhenCompositeExistsDoesNotBindTo() {
given(this.mockRegistry.config()).willReturn(this.mockConfig);
MeterRegistryConfigurer configurer = new MeterRegistryConfigurer(createObjectProvider(this.customizers),
createObjectProvider(this.filters), null, false, true);
configurer.configure(this.mockRegistry);
MeterRegistryPostProcessor processor = new MeterRegistryPostProcessor(false, false,
createObjectProvider(this.customizers), createObjectProvider(this.filters), null);
postProcessAndInitialize(processor, this.mockRegistry);
then(this.mockBinder).shouldHaveNoInteractions();
}
@Test
void configureShouldBeCalledInOrderCustomizerFilterBinder() {
void postProcessAndInitializeBeOrderedCustomizerThenFilterThenBindTo() {
given(this.mockRegistry.config()).willReturn(this.mockConfig);
this.customizers.add(this.mockCustomizer);
this.filters.add(this.mockFilter);
this.binders.add(this.mockBinder);
MeterRegistryConfigurer configurer = new MeterRegistryConfigurer(createObjectProvider(this.customizers),
createObjectProvider(this.filters), createObjectProvider(this.binders), false, false);
configurer.configure(this.mockRegistry);
MeterRegistryPostProcessor processor = new MeterRegistryPostProcessor(true, false,
createObjectProvider(this.customizers), createObjectProvider(this.filters),
createObjectProvider(this.binders));
postProcessAndInitialize(processor, this.mockRegistry);
InOrder ordered = inOrder(this.mockBinder, this.mockConfig, this.mockCustomizer);
then(this.mockCustomizer).should(ordered).customize(this.mockRegistry);
then(this.mockConfig).should(ordered).meterFilter(this.mockFilter);
@@ -144,12 +150,13 @@ class MeterRegistryConfigurerTests {
}
@Test
void configureWhenAddToGlobalRegistryShouldAddToGlobalRegistry() {
void postProcessAndInitializeWhenUseGlobalRegistryTrueAddsToGlobalRegistry() {
given(this.mockRegistry.config()).willReturn(this.mockConfig);
MeterRegistryConfigurer configurer = new MeterRegistryConfigurer(createObjectProvider(this.customizers),
createObjectProvider(this.filters), createObjectProvider(this.binders), true, false);
MeterRegistryPostProcessor processor = new MeterRegistryPostProcessor(true, true,
createObjectProvider(this.customizers), createObjectProvider(this.filters),
createObjectProvider(this.binders));
try {
configurer.configure(this.mockRegistry);
postProcessAndInitialize(processor, this.mockRegistry);
assertThat(Metrics.globalRegistry.getRegistries()).contains(this.mockRegistry);
}
finally {
@@ -158,14 +165,33 @@ class MeterRegistryConfigurerTests {
}
@Test
void configureWhenNotAddToGlobalRegistryShouldAddToGlobalRegistry() {
void postProcessAndInitializeWhenUseGlobalRegistryFalseDoesNotAddToGlobalRegistry() {
given(this.mockRegistry.config()).willReturn(this.mockConfig);
MeterRegistryConfigurer configurer = new MeterRegistryConfigurer(createObjectProvider(this.customizers),
createObjectProvider(this.filters), createObjectProvider(this.binders), false, false);
configurer.configure(this.mockRegistry);
MeterRegistryPostProcessor processor = new MeterRegistryPostProcessor(true, false,
createObjectProvider(this.customizers), createObjectProvider(this.filters),
createObjectProvider(this.binders));
postProcessAndInitialize(processor, this.mockRegistry);
assertThat(Metrics.globalRegistry.getRegistries()).doesNotContain(this.mockRegistry);
}
@Test
void postProcessDoesNotBindToUntilSingletonsInitialized() {
given(this.mockRegistry.config()).willReturn(this.mockConfig);
this.binders.add(this.mockBinder);
MeterRegistryPostProcessor processor = new MeterRegistryPostProcessor(true, false,
createObjectProvider(this.customizers), createObjectProvider(this.filters),
createObjectProvider(this.binders));
processor.postProcessAfterInitialization(this.mockRegistry, "meterRegistry");
then(this.mockBinder).shouldHaveNoInteractions();
processor.afterSingletonsInstantiated();
then(this.mockBinder).should().bindTo(this.mockRegistry);
}
private void postProcessAndInitialize(MeterRegistryPostProcessor processor, MeterRegistry registry) {
processor.postProcessAfterInitialization(registry, "meterRegistry");
processor.afterSingletonsInstantiated();
}
@SuppressWarnings("unchecked")
private <T> ObjectProvider<T> createObjectProvider(List<T> objects) {
ObjectProvider<T> objectProvider = mock(ObjectProvider.class);

View File

@@ -41,11 +41,12 @@ import org.springframework.context.annotation.Configuration;
import static org.assertj.core.api.Assertions.assertThat;
/**
* Integration tests for {@link MeterRegistryConfigurer}.
* Integration tests for {@link MeterRegistryPostProcessor} configured by
* {@link MetricsAutoConfiguration}.
*
* @author Jon Schneider
*/
class MeterRegistryConfigurerIntegrationTests {
class MetricsAutoConfigurationMeterRegistryPostProcessorIntegrationTests {
private final ApplicationContextRunner contextRunner = new ApplicationContextRunner()
.with(MetricsRun.limitedTo(AtlasMetricsExportAutoConfiguration.class,
@@ -68,7 +69,6 @@ class MeterRegistryConfigurerIntegrationTests {
.withConfiguration(AutoConfigurations.of(MetricsAutoConfiguration.class,
SimpleMetricsExportAutoConfiguration.class))
.withUserConfiguration(TestConfiguration.class).run((context) -> {
});
}

View File

@@ -11,7 +11,7 @@
<suppress files="LogbackLoggingSystem\.java" checks="IllegalImport" />
<suppress files="LogbackLoggingSystemTests\.java" checks="IllegalImport" />
<suppress files="LogbackConfigurationAotContributionTests\.java" checks="IllegalImport" />
<suppress files="MeterRegistryConfigurerIntegrationTests\.java" checks="IllegalImport" />
<suppress files="MetricsAutoConfigurationMeterRegistryPostProcessorIntegrationTests\.java" checks="IllegalImport" message="LoggerFactory"/>
<suppress files="SpringApplicationTests\.java" checks="FinalClass" />
<suppress files=".+Configuration\.java" checks="HideUtilityClassConstructor" />
<suppress files=".+Application\.java" checks="HideUtilityClassConstructor" />