Skip to content

Commit 4fc813b

Browse files
committed
Merge pull request #16221 from Johnny Lim
* gh-16221: Polish "Prevent double update of metrics when CompositeMeterRegistry exists" Prevent double update of metrics when CompositeMeterRegistry exists Closes gh-16221
2 parents ba0279b + 6b20d13 commit 4fc813b

File tree

5 files changed

+107
-15
lines changed

5 files changed

+107
-15
lines changed

spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/metrics/MeterRegistryConfigurer.java

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2012-2018 the original author or authors.
2+
* Copyright 2012-2019 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -22,6 +22,7 @@
2222
import io.micrometer.core.instrument.MeterRegistry;
2323
import io.micrometer.core.instrument.Metrics;
2424
import io.micrometer.core.instrument.binder.MeterBinder;
25+
import io.micrometer.core.instrument.composite.CompositeMeterRegistry;
2526
import io.micrometer.core.instrument.config.MeterFilter;
2627

2728
import org.springframework.beans.factory.ObjectProvider;
@@ -45,21 +46,27 @@ class MeterRegistryConfigurer {
4546

4647
private final boolean addToGlobalRegistry;
4748

49+
private final boolean hasCompositeMeterRegistry;
50+
4851
MeterRegistryConfigurer(ObjectProvider<MeterRegistryCustomizer<?>> customizers,
4952
ObjectProvider<MeterFilter> filters, ObjectProvider<MeterBinder> binders,
50-
boolean addToGlobalRegistry) {
53+
boolean addToGlobalRegistry, boolean hasCompositeMeterRegistry) {
5154
this.customizers = customizers;
5255
this.filters = filters;
5356
this.binders = binders;
5457
this.addToGlobalRegistry = addToGlobalRegistry;
58+
this.hasCompositeMeterRegistry = hasCompositeMeterRegistry;
5559
}
5660

5761
void configure(MeterRegistry registry) {
5862
// Customizers must be applied before binders, as they may add custom
5963
// tags or alter timer or summary configuration.
6064
customize(registry);
6165
addFilters(registry);
62-
addBinders(registry);
66+
if (!this.hasCompositeMeterRegistry
67+
|| registry instanceof CompositeMeterRegistry) {
68+
addBinders(registry);
69+
}
6370
if (this.addToGlobalRegistry && registry != Metrics.globalRegistry) {
6471
Metrics.addRegistry(registry);
6572
}

spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/metrics/MeterRegistryPostProcessor.java

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,11 +18,13 @@
1818

1919
import io.micrometer.core.instrument.MeterRegistry;
2020
import io.micrometer.core.instrument.binder.MeterBinder;
21+
import io.micrometer.core.instrument.composite.CompositeMeterRegistry;
2122
import io.micrometer.core.instrument.config.MeterFilter;
2223

2324
import org.springframework.beans.BeansException;
2425
import org.springframework.beans.factory.ObjectProvider;
2526
import org.springframework.beans.factory.config.BeanPostProcessor;
27+
import org.springframework.context.ApplicationContext;
2628

2729
/**
2830
* {@link BeanPostProcessor} that delegates to a lazily created
@@ -44,14 +46,18 @@ class MeterRegistryPostProcessor implements BeanPostProcessor {
4446

4547
private volatile MeterRegistryConfigurer configurer;
4648

49+
private final ApplicationContext applicationContext;
50+
4751
MeterRegistryPostProcessor(ObjectProvider<MeterBinder> meterBinders,
4852
ObjectProvider<MeterFilter> meterFilters,
4953
ObjectProvider<MeterRegistryCustomizer<?>> meterRegistryCustomizers,
50-
ObjectProvider<MetricsProperties> metricsProperties) {
54+
ObjectProvider<MetricsProperties> metricsProperties,
55+
ApplicationContext applicationContext) {
5156
this.meterBinders = meterBinders;
5257
this.meterFilters = meterFilters;
5358
this.meterRegistryCustomizers = meterRegistryCustomizers;
5459
this.metricsProperties = metricsProperties;
60+
this.applicationContext = applicationContext;
5561
}
5662

5763
@Override
@@ -65,9 +71,13 @@ public Object postProcessAfterInitialization(Object bean, String beanName)
6571

6672
private MeterRegistryConfigurer getConfigurer() {
6773
if (this.configurer == null) {
74+
boolean hasCompositeMeterRegistry = this.applicationContext
75+
.getBeanNamesForType(CompositeMeterRegistry.class, false,
76+
false).length != 0;
6877
this.configurer = new MeterRegistryConfigurer(this.meterRegistryCustomizers,
6978
this.meterFilters, this.meterBinders,
70-
this.metricsProperties.getObject().isUseGlobalRegistry());
79+
this.metricsProperties.getObject().isUseGlobalRegistry(),
80+
hasCompositeMeterRegistry);
7181
}
7282
return this.configurer;
7383
}

spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/metrics/MetricsAutoConfiguration.java

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@
2727
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
2828
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
2929
import org.springframework.boot.context.properties.EnableConfigurationProperties;
30+
import org.springframework.context.ApplicationContext;
3031
import org.springframework.context.annotation.Bean;
3132
import org.springframework.context.annotation.Configuration;
3233
import org.springframework.core.annotation.Order;
@@ -55,9 +56,10 @@ public static MeterRegistryPostProcessor meterRegistryPostProcessor(
5556
ObjectProvider<MeterBinder> meterBinders,
5657
ObjectProvider<MeterFilter> meterFilters,
5758
ObjectProvider<MeterRegistryCustomizer<?>> meterRegistryCustomizers,
58-
ObjectProvider<MetricsProperties> metricsProperties) {
59+
ObjectProvider<MetricsProperties> metricsProperties,
60+
ApplicationContext applicationContext) {
5961
return new MeterRegistryPostProcessor(meterBinders, meterFilters,
60-
meterRegistryCustomizers, metricsProperties);
62+
meterRegistryCustomizers, metricsProperties, applicationContext);
6163
}
6264

6365
@Bean

spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/metrics/MeterRegistryConfigurerIntegrationTests.java

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,14 +16,20 @@
1616

1717
package org.springframework.boot.actuate.autoconfigure.metrics;
1818

19+
import java.util.Map;
20+
21+
import ch.qos.logback.classic.Logger;
22+
import ch.qos.logback.classic.LoggerContext;
1923
import io.micrometer.core.instrument.MeterRegistry;
2024
import io.micrometer.core.instrument.binder.MeterBinder;
2125
import io.micrometer.core.instrument.composite.CompositeMeterRegistry;
2226
import org.junit.Test;
27+
import org.slf4j.impl.StaticLoggerBinder;
2328

2429
import org.springframework.beans.BeansException;
2530
import org.springframework.beans.factory.config.BeanPostProcessor;
2631
import org.springframework.boot.actuate.autoconfigure.metrics.export.atlas.AtlasMetricsExportAutoConfiguration;
32+
import org.springframework.boot.actuate.autoconfigure.metrics.export.jmx.JmxMetricsExportAutoConfiguration;
2733
import org.springframework.boot.actuate.autoconfigure.metrics.export.prometheus.PrometheusMetricsExportAutoConfiguration;
2834
import org.springframework.boot.actuate.autoconfigure.metrics.export.simple.SimpleMetricsExportAutoConfiguration;
2935
import org.springframework.boot.actuate.autoconfigure.metrics.test.MetricsRun;
@@ -33,6 +39,8 @@
3339
import org.springframework.context.annotation.Bean;
3440
import org.springframework.context.annotation.Configuration;
3541

42+
import static org.assertj.core.api.Assertions.assertThat;
43+
3644
/**
3745
* Integration tests for {@link MeterRegistryConfigurer}.
3846
*
@@ -66,6 +74,47 @@ public void customizersAreAppliedBeforeBindersAreCreated() {
6674
});
6775
}
6876

77+
@Test
78+
public void counterIsIncrementedOncePerEventWithoutCompositeMeterRegistry() {
79+
new ApplicationContextRunner()
80+
.with(MetricsRun.limitedTo(JmxMetricsExportAutoConfiguration.class))
81+
.withConfiguration(
82+
AutoConfigurations.of(LogbackMetricsAutoConfiguration.class))
83+
.run((context) -> {
84+
Logger logger = ((LoggerContext) StaticLoggerBinder.getSingleton()
85+
.getLoggerFactory()).getLogger("test-logger");
86+
logger.error("Error.");
87+
88+
Map<String, MeterRegistry> registriesByName = context
89+
.getBeansOfType(MeterRegistry.class);
90+
assertThat(registriesByName).hasSize(1);
91+
MeterRegistry registry = registriesByName.values().iterator().next();
92+
assertThat(registry.get("logback.events").tag("level", "error")
93+
.counter().count()).isEqualTo(1);
94+
});
95+
}
96+
97+
@Test
98+
public void counterIsIncrementedOncePerEventWithCompositeMeterRegistry() {
99+
new ApplicationContextRunner()
100+
.with(MetricsRun.limitedTo(JmxMetricsExportAutoConfiguration.class,
101+
PrometheusMetricsExportAutoConfiguration.class))
102+
.withConfiguration(
103+
AutoConfigurations.of(LogbackMetricsAutoConfiguration.class))
104+
.run((context) -> {
105+
Logger logger = ((LoggerContext) StaticLoggerBinder.getSingleton()
106+
.getLoggerFactory()).getLogger("test-logger");
107+
logger.error("Error.");
108+
Map<String, MeterRegistry> registriesByName = context
109+
.getBeansOfType(MeterRegistry.class);
110+
assertThat(registriesByName).hasSize(3);
111+
registriesByName.forEach((name,
112+
registry) -> assertThat(registry.get("logback.events")
113+
.tag("level", "error").counter().count())
114+
.isEqualTo(1));
115+
});
116+
}
117+
69118
@Configuration
70119
static class TestConfiguration {
71120

spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/metrics/MeterRegistryConfigurerTests.java

Lines changed: 32 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2012-2018 the original author or authors.
2+
* Copyright 2012-2019 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -38,6 +38,7 @@
3838
import static org.mockito.Mockito.inOrder;
3939
import static org.mockito.Mockito.mock;
4040
import static org.mockito.Mockito.verify;
41+
import static org.mockito.Mockito.verifyZeroInteractions;
4142

4243
/**
4344
* Tests for {@link MeterRegistryConfigurer}.
@@ -80,7 +81,7 @@ public void configureWhenCompositeShouldApplyCustomizer() {
8081
MeterRegistryConfigurer configurer = new MeterRegistryConfigurer(
8182
createObjectProvider(this.customizers),
8283
createObjectProvider(this.filters), createObjectProvider(this.binders),
83-
false);
84+
false, false);
8485
CompositeMeterRegistry composite = new CompositeMeterRegistry();
8586
configurer.configure(composite);
8687
verify(this.mockCustomizer).customize(composite);
@@ -92,7 +93,7 @@ public void configureShouldApplyCustomizer() {
9293
MeterRegistryConfigurer configurer = new MeterRegistryConfigurer(
9394
createObjectProvider(this.customizers),
9495
createObjectProvider(this.filters), createObjectProvider(this.binders),
95-
false);
96+
false, false);
9697
configurer.configure(this.mockRegistry);
9798
verify(this.mockCustomizer).customize(this.mockRegistry);
9899
}
@@ -103,7 +104,7 @@ public void configureShouldApplyFilter() {
103104
MeterRegistryConfigurer configurer = new MeterRegistryConfigurer(
104105
createObjectProvider(this.customizers),
105106
createObjectProvider(this.filters), createObjectProvider(this.binders),
106-
false);
107+
false, false);
107108
configurer.configure(this.mockRegistry);
108109
verify(this.mockConfig).meterFilter(this.mockFilter);
109110
}
@@ -114,11 +115,34 @@ public void configureShouldApplyBinder() {
114115
MeterRegistryConfigurer configurer = new MeterRegistryConfigurer(
115116
createObjectProvider(this.customizers),
116117
createObjectProvider(this.filters), createObjectProvider(this.binders),
117-
false);
118+
false, false);
118119
configurer.configure(this.mockRegistry);
119120
verify(this.mockBinder).bindTo(this.mockRegistry);
120121
}
121122

123+
@Test
124+
public void configureShouldApplyBinderToComposite() {
125+
this.binders.add(this.mockBinder);
126+
MeterRegistryConfigurer configurer = new MeterRegistryConfigurer(
127+
createObjectProvider(this.customizers),
128+
createObjectProvider(this.filters), createObjectProvider(this.binders),
129+
false, true);
130+
CompositeMeterRegistry composite = new CompositeMeterRegistry();
131+
configurer.configure(composite);
132+
verify(this.mockBinder).bindTo(composite);
133+
}
134+
135+
@Test
136+
public void configureShouldNotApplyBinderWhenCompositeExists() {
137+
this.binders.add(this.mockBinder);
138+
MeterRegistryConfigurer configurer = new MeterRegistryConfigurer(
139+
createObjectProvider(this.customizers),
140+
createObjectProvider(this.filters), createObjectProvider(this.binders),
141+
false, true);
142+
configurer.configure(this.mockRegistry);
143+
verifyZeroInteractions(this.mockBinder);
144+
}
145+
122146
@Test
123147
public void configureShouldBeCalledInOrderCustomizerFilterBinder() {
124148
this.customizers.add(this.mockCustomizer);
@@ -127,7 +151,7 @@ public void configureShouldBeCalledInOrderCustomizerFilterBinder() {
127151
MeterRegistryConfigurer configurer = new MeterRegistryConfigurer(
128152
createObjectProvider(this.customizers),
129153
createObjectProvider(this.filters), createObjectProvider(this.binders),
130-
false);
154+
false, false);
131155
configurer.configure(this.mockRegistry);
132156
InOrder ordered = inOrder(this.mockBinder, this.mockConfig, this.mockCustomizer);
133157
ordered.verify(this.mockCustomizer).customize(this.mockRegistry);
@@ -140,7 +164,7 @@ public void configureWhenAddToGlobalRegistryShouldAddToGlobalRegistry() {
140164
MeterRegistryConfigurer configurer = new MeterRegistryConfigurer(
141165
createObjectProvider(this.customizers),
142166
createObjectProvider(this.filters), createObjectProvider(this.binders),
143-
true);
167+
true, false);
144168
try {
145169
configurer.configure(this.mockRegistry);
146170
assertThat(Metrics.globalRegistry.getRegistries())
@@ -156,7 +180,7 @@ public void configureWhenNotAddToGlobalRegistryShouldAddToGlobalRegistry() {
156180
MeterRegistryConfigurer configurer = new MeterRegistryConfigurer(
157181
createObjectProvider(this.customizers),
158182
createObjectProvider(this.filters), createObjectProvider(this.binders),
159-
false);
183+
false, false);
160184
configurer.configure(this.mockRegistry);
161185
assertThat(Metrics.globalRegistry.getRegistries())
162186
.doesNotContain(this.mockRegistry);

0 commit comments

Comments
 (0)