Reduce duplicate binding of meters to user-defined composites

Fixes gh-42396
This commit is contained in:
Andy Wilkinson 2024-09-20 10:23:04 +01:00
parent 0aeea6f069
commit 267a642e0f
2 changed files with 106 additions and 33 deletions

View File

@ -1,5 +1,5 @@
/*
* Copyright 2012-2023 the original author or authors.
* Copyright 2012-2024 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.
@ -42,7 +42,7 @@ import org.springframework.context.ApplicationContext;
*/
class MeterRegistryPostProcessor implements BeanPostProcessor, SmartInitializingSingleton {
private final boolean hasNoCompositeMeterRegistryBeans;
private final CompositeMeterRegistries compositeMeterRegistries;
private final ObjectProvider<MetricsProperties> properties;
@ -59,17 +59,13 @@ class MeterRegistryPostProcessor implements BeanPostProcessor, SmartInitializing
MeterRegistryPostProcessor(ApplicationContext applicationContext,
ObjectProvider<MetricsProperties> metricsProperties, ObjectProvider<MeterRegistryCustomizer<?>> customizers,
ObjectProvider<MeterFilter> filters, ObjectProvider<MeterBinder> binders) {
this(hasNoCompositeMeterRegistryBeans(applicationContext), metricsProperties, customizers, filters, binders);
this(CompositeMeterRegistries.of(applicationContext), metricsProperties, customizers, filters, binders);
}
private static boolean hasNoCompositeMeterRegistryBeans(ApplicationContext applicationContext) {
return applicationContext.getBeanNamesForType(CompositeMeterRegistry.class, false, false).length == 0;
}
MeterRegistryPostProcessor(boolean hasNoCompositeMeterRegistryBeans, ObjectProvider<MetricsProperties> properties,
ObjectProvider<MeterRegistryCustomizer<?>> customizers, ObjectProvider<MeterFilter> filters,
ObjectProvider<MeterBinder> binders) {
this.hasNoCompositeMeterRegistryBeans = hasNoCompositeMeterRegistryBeans;
MeterRegistryPostProcessor(CompositeMeterRegistries compositeMeterRegistries,
ObjectProvider<MetricsProperties> properties, ObjectProvider<MeterRegistryCustomizer<?>> customizers,
ObjectProvider<MeterFilter> filters, ObjectProvider<MeterBinder> binders) {
this.compositeMeterRegistries = compositeMeterRegistries;
this.properties = properties;
this.customizers = customizers;
this.filters = filters;
@ -130,11 +126,21 @@ class MeterRegistryPostProcessor implements BeanPostProcessor, SmartInitializing
}
private boolean isBindable(MeterRegistry meterRegistry) {
return this.hasNoCompositeMeterRegistryBeans || isCompositeMeterRegistry(meterRegistry);
return isAutoConfiguredComposite(meterRegistry) || isCompositeWithOnlyUserDefinedComposites(meterRegistry)
|| noCompositeMeterRegistries();
}
private boolean isCompositeMeterRegistry(MeterRegistry meterRegistry) {
return meterRegistry instanceof CompositeMeterRegistry;
private boolean isAutoConfiguredComposite(MeterRegistry meterRegistry) {
return meterRegistry instanceof AutoConfiguredCompositeMeterRegistry;
}
private boolean isCompositeWithOnlyUserDefinedComposites(MeterRegistry meterRegistry) {
return this.compositeMeterRegistries == CompositeMeterRegistries.ONLY_USER_DEFINED
&& meterRegistry instanceof CompositeMeterRegistry;
}
private boolean noCompositeMeterRegistries() {
return this.compositeMeterRegistries == CompositeMeterRegistries.NONE;
}
void applyBinders(MeterRegistry meterRegistry) {
@ -149,4 +155,21 @@ class MeterRegistryPostProcessor implements BeanPostProcessor, SmartInitializing
this.binders.orderedStream().forEach((binder) -> binder.bindTo(meterRegistry));
}
enum CompositeMeterRegistries {
NONE, AUTO_CONFIGURED, ONLY_USER_DEFINED;
private static CompositeMeterRegistries of(ApplicationContext context) {
if (hasBeansOfType(AutoConfiguredCompositeMeterRegistry.class, context)) {
return AUTO_CONFIGURED;
}
return hasBeansOfType(CompositeMeterRegistry.class, context) ? ONLY_USER_DEFINED : NONE;
}
private static boolean hasBeansOfType(Class<?> type, ApplicationContext context) {
return context.getBeanNamesForType(type, false, false).length > 0;
}
}
}

View File

@ -1,5 +1,5 @@
/*
* Copyright 2012-2022 the original author or authors.
* Copyright 2012-2024 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.
@ -17,8 +17,10 @@
package org.springframework.boot.actuate.autoconfigure.metrics;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import io.micrometer.core.instrument.Clock;
import io.micrometer.core.instrument.MeterRegistry;
import io.micrometer.core.instrument.MeterRegistry.Config;
import io.micrometer.core.instrument.Metrics;
@ -32,6 +34,7 @@ import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension;
import org.springframework.beans.factory.ObjectProvider;
import org.springframework.boot.actuate.autoconfigure.metrics.MeterRegistryPostProcessor.CompositeMeterRegistries;
import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.BDDMockito.given;
@ -76,21 +79,34 @@ class MeterRegistryPostProcessorTests {
}
@Test
void postProcessAndInitializeWhenCompositeAppliesCustomizer() {
void postProcessAndInitializeWhenUserDefinedCompositeAppliesCustomizer() {
this.customizers.add(this.mockCustomizer);
MeterRegistryPostProcessor processor = new MeterRegistryPostProcessor(false,
createObjectProvider(this.properties), createObjectProvider(this.customizers),
createObjectProvider(this.filters), createObjectProvider(this.binders));
MeterRegistryPostProcessor processor = new MeterRegistryPostProcessor(
CompositeMeterRegistries.ONLY_USER_DEFINED, createObjectProvider(this.properties),
createObjectProvider(this.customizers), createObjectProvider(this.filters),
createObjectProvider(this.binders));
CompositeMeterRegistry composite = new CompositeMeterRegistry();
postProcessAndInitialize(processor, composite);
then(this.mockCustomizer).should().customize(composite);
}
@Test
void postProcessAndInitializeWhenAutoConfiguredCompositeAppliesCustomizer() {
this.customizers.add(this.mockCustomizer);
MeterRegistryPostProcessor processor = new MeterRegistryPostProcessor(CompositeMeterRegistries.AUTO_CONFIGURED,
createObjectProvider(this.properties), createObjectProvider(this.customizers), null,
createObjectProvider(this.binders));
AutoConfiguredCompositeMeterRegistry composite = new AutoConfiguredCompositeMeterRegistry(Clock.SYSTEM,
Collections.emptyList());
postProcessAndInitialize(processor, composite);
then(this.mockCustomizer).should().customize(composite);
}
@Test
void postProcessAndInitializeAppliesCustomizer() {
given(this.mockRegistry.config()).willReturn(this.mockConfig);
this.customizers.add(this.mockCustomizer);
MeterRegistryPostProcessor processor = new MeterRegistryPostProcessor(true,
MeterRegistryPostProcessor processor = new MeterRegistryPostProcessor(CompositeMeterRegistries.NONE,
createObjectProvider(this.properties), createObjectProvider(this.customizers),
createObjectProvider(this.filters), createObjectProvider(this.binders));
postProcessAndInitialize(processor, this.mockRegistry);
@ -101,7 +117,7 @@ class MeterRegistryPostProcessorTests {
void postProcessAndInitializeAppliesFilter() {
given(this.mockRegistry.config()).willReturn(this.mockConfig);
this.filters.add(this.mockFilter);
MeterRegistryPostProcessor processor = new MeterRegistryPostProcessor(true,
MeterRegistryPostProcessor processor = new MeterRegistryPostProcessor(CompositeMeterRegistries.NONE,
createObjectProvider(this.properties), createObjectProvider(this.customizers),
createObjectProvider(this.filters), createObjectProvider(this.binders));
postProcessAndInitialize(processor, this.mockRegistry);
@ -112,7 +128,7 @@ class MeterRegistryPostProcessorTests {
void postProcessAndInitializeBindsTo() {
given(this.mockRegistry.config()).willReturn(this.mockConfig);
this.binders.add(this.mockBinder);
MeterRegistryPostProcessor processor = new MeterRegistryPostProcessor(true,
MeterRegistryPostProcessor processor = new MeterRegistryPostProcessor(CompositeMeterRegistries.NONE,
createObjectProvider(this.properties), createObjectProvider(this.customizers),
createObjectProvider(this.filters), createObjectProvider(this.binders));
postProcessAndInitialize(processor, this.mockRegistry);
@ -120,20 +136,54 @@ class MeterRegistryPostProcessorTests {
}
@Test
void postProcessAndInitializeWhenCompositeBindsTo() {
void whenUserDefinedCompositeThenPostProcessAndInitializeCompositeBindsTo() {
this.binders.add(this.mockBinder);
MeterRegistryPostProcessor processor = new MeterRegistryPostProcessor(false,
createObjectProvider(this.properties), createObjectProvider(this.customizers),
createObjectProvider(this.filters), createObjectProvider(this.binders));
MeterRegistryPostProcessor processor = new MeterRegistryPostProcessor(
CompositeMeterRegistries.ONLY_USER_DEFINED, createObjectProvider(this.properties),
createObjectProvider(this.customizers), createObjectProvider(this.filters),
createObjectProvider(this.binders));
CompositeMeterRegistry composite = new CompositeMeterRegistry();
postProcessAndInitialize(processor, composite);
then(this.mockBinder).should().bindTo(composite);
}
@Test
void postProcessAndInitializeWhenCompositeExistsDoesNotBindTo() {
void whenUserDefinedCompositeThenPostProcessAndInitializeStandardRegistryDoesNotBindTo() {
given(this.mockRegistry.config()).willReturn(this.mockConfig);
MeterRegistryPostProcessor processor = new MeterRegistryPostProcessor(false,
MeterRegistryPostProcessor processor = new MeterRegistryPostProcessor(
CompositeMeterRegistries.ONLY_USER_DEFINED, createObjectProvider(this.properties),
createObjectProvider(this.customizers), createObjectProvider(this.filters), null);
postProcessAndInitialize(processor, this.mockRegistry);
then(this.mockBinder).shouldHaveNoInteractions();
}
@Test
void whenAutoConfiguredCompositeThenPostProcessAndInitializeAutoConfiguredCompositeBindsTo() {
this.binders.add(this.mockBinder);
MeterRegistryPostProcessor processor = new MeterRegistryPostProcessor(CompositeMeterRegistries.AUTO_CONFIGURED,
createObjectProvider(this.properties), createObjectProvider(this.customizers), null,
createObjectProvider(this.binders));
AutoConfiguredCompositeMeterRegistry composite = new AutoConfiguredCompositeMeterRegistry(Clock.SYSTEM,
Collections.emptyList());
postProcessAndInitialize(processor, composite);
then(this.mockBinder).should().bindTo(composite);
}
@Test
void whenAutoConfiguredCompositeThenPostProcessAndInitializeCompositeDoesNotBindTo() {
this.binders.add(this.mockBinder);
MeterRegistryPostProcessor processor = new MeterRegistryPostProcessor(CompositeMeterRegistries.AUTO_CONFIGURED,
createObjectProvider(this.properties), createObjectProvider(this.customizers),
createObjectProvider(this.filters), null);
CompositeMeterRegistry composite = new CompositeMeterRegistry();
postProcessAndInitialize(processor, composite);
then(this.mockBinder).shouldHaveNoInteractions();
}
@Test
void whenAutoConfiguredCompositeThenPostProcessAndInitializeStandardRegistryDoesNotBindTo() {
given(this.mockRegistry.config()).willReturn(this.mockConfig);
MeterRegistryPostProcessor processor = new MeterRegistryPostProcessor(CompositeMeterRegistries.AUTO_CONFIGURED,
createObjectProvider(this.properties), createObjectProvider(this.customizers),
createObjectProvider(this.filters), null);
postProcessAndInitialize(processor, this.mockRegistry);
@ -141,12 +191,12 @@ class MeterRegistryPostProcessorTests {
}
@Test
void postProcessAndInitializeBeOrderedCustomizerThenFilterThenBindTo() {
void postProcessAndInitializeIsOrderedCustomizerThenFilterThenBindTo() {
given(this.mockRegistry.config()).willReturn(this.mockConfig);
this.customizers.add(this.mockCustomizer);
this.filters.add(this.mockFilter);
this.binders.add(this.mockBinder);
MeterRegistryPostProcessor processor = new MeterRegistryPostProcessor(true,
MeterRegistryPostProcessor processor = new MeterRegistryPostProcessor(CompositeMeterRegistries.NONE,
createObjectProvider(this.properties), createObjectProvider(this.customizers),
createObjectProvider(this.filters), createObjectProvider(this.binders));
postProcessAndInitialize(processor, this.mockRegistry);
@ -160,7 +210,7 @@ class MeterRegistryPostProcessorTests {
void postProcessAndInitializeWhenUseGlobalRegistryTrueAddsToGlobalRegistry() {
given(this.mockRegistry.config()).willReturn(this.mockConfig);
this.properties.setUseGlobalRegistry(true);
MeterRegistryPostProcessor processor = new MeterRegistryPostProcessor(true,
MeterRegistryPostProcessor processor = new MeterRegistryPostProcessor(CompositeMeterRegistries.NONE,
createObjectProvider(this.properties), createObjectProvider(this.customizers),
createObjectProvider(this.filters), createObjectProvider(this.binders));
try {
@ -175,7 +225,7 @@ class MeterRegistryPostProcessorTests {
@Test
void postProcessAndInitializeWhenUseGlobalRegistryFalseDoesNotAddToGlobalRegistry() {
given(this.mockRegistry.config()).willReturn(this.mockConfig);
MeterRegistryPostProcessor processor = new MeterRegistryPostProcessor(true,
MeterRegistryPostProcessor processor = new MeterRegistryPostProcessor(CompositeMeterRegistries.NONE,
createObjectProvider(this.properties), createObjectProvider(this.customizers),
createObjectProvider(this.filters), createObjectProvider(this.binders));
postProcessAndInitialize(processor, this.mockRegistry);
@ -186,7 +236,7 @@ class MeterRegistryPostProcessorTests {
void postProcessDoesNotBindToUntilSingletonsInitialized() {
given(this.mockRegistry.config()).willReturn(this.mockConfig);
this.binders.add(this.mockBinder);
MeterRegistryPostProcessor processor = new MeterRegistryPostProcessor(true,
MeterRegistryPostProcessor processor = new MeterRegistryPostProcessor(CompositeMeterRegistries.NONE,
createObjectProvider(this.properties), createObjectProvider(this.customizers),
createObjectProvider(this.filters), createObjectProvider(this.binders));
processor.postProcessAfterInitialization(this.mockRegistry, "meterRegistry");