Skip to content

Commit 9a8c182

Browse files
philwebbJon Schneider
and
Jon Schneider
committed
Only use micrometer composites when necessary
Update micrometer auto-configuration so that a `CompositeMeterRegistry` is only created when more than one `MeterRegistry` bean is declared. When a composite is crated, it is marked as `@Primary` so that it can be directly injected. Meter registries can now be defined directly as beans, and auto-configuration can back off in the usual way. The `MeterRegistryConfigurer` is now called `MeterRegistryCustomizer` and is generically types so it's easy to apply customizations to a particular `MeterRegistry` implementation. Fixes spring-projectsgh-11799 Co-authored-by: Jon Schneider <[email protected]>
1 parent 798882b commit 9a8c182

23 files changed

+788
-285
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
/*
2+
* Copyright 2012-2018 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package org.springframework.boot.actuate.autoconfigure.metrics;
18+
19+
import org.springframework.boot.actuate.autoconfigure.metrics.export.atlas.AtlasExportConfiguration;
20+
import org.springframework.boot.actuate.autoconfigure.metrics.export.datadog.DatadogExportConfiguration;
21+
import org.springframework.boot.actuate.autoconfigure.metrics.export.ganglia.GangliaExportConfiguration;
22+
import org.springframework.boot.actuate.autoconfigure.metrics.export.graphite.GraphiteExportConfiguration;
23+
import org.springframework.boot.actuate.autoconfigure.metrics.export.influx.InfluxExportConfiguration;
24+
import org.springframework.boot.actuate.autoconfigure.metrics.export.jmx.JmxExportConfiguration;
25+
import org.springframework.boot.actuate.autoconfigure.metrics.export.prometheus.PrometheusExportConfiguration;
26+
import org.springframework.boot.actuate.autoconfigure.metrics.export.simple.SimpleExportConfiguration;
27+
import org.springframework.boot.actuate.autoconfigure.metrics.export.statsd.StatsdExportConfiguration;
28+
import org.springframework.context.annotation.Configuration;
29+
import org.springframework.context.annotation.Import;
30+
31+
/**
32+
* Imports for registry configurations.
33+
*
34+
* @author Jon Schneider
35+
*/
36+
@Configuration
37+
@Import({ AtlasExportConfiguration.class, DatadogExportConfiguration.class,
38+
GangliaExportConfiguration.class, GraphiteExportConfiguration.class,
39+
InfluxExportConfiguration.class, JmxExportConfiguration.class,
40+
PrometheusExportConfiguration.class, SimpleExportConfiguration.class,
41+
StatsdExportConfiguration.class })
42+
class MeterRegistriesConfiguration {
43+
44+
}
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2012-2017 the original author or authors.
2+
* Copyright 2012-2018 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.
@@ -23,19 +23,20 @@
2323
* Callback interface that can be used to customize auto-configured {@link MeterRegistry
2424
* MeterRegistries}.
2525
* <p>
26-
* Configurers are guaranteed to be applied before any {@link Meter} is registered with
26+
* Customizers are guaranteed to be applied before any {@link Meter} is registered with
2727
* the registry.
2828
*
2929
* @author Jon Schneider
30+
* @param <T> The registry type to customize
3031
* @since 2.0.0
3132
*/
3233
@FunctionalInterface
33-
public interface MeterRegistryConfigurer {
34+
public interface MeterRegistryCustomizer<T extends MeterRegistry> {
3435

3536
/**
36-
* Configure the given {@code registry}.
37-
* @param registry the registry to configure
37+
* Customize the given {@code registry}.
38+
* @param registry the registry to customize
3839
*/
39-
void configureRegistry(MeterRegistry registry);
40+
void customize(T registry);
4041

4142
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,94 @@
1+
/*
2+
* Copyright 2012-2018 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package org.springframework.boot.actuate.autoconfigure.metrics;
18+
19+
import java.util.Collection;
20+
import java.util.Collections;
21+
22+
import io.micrometer.core.instrument.MeterRegistry;
23+
import io.micrometer.core.instrument.Metrics;
24+
import io.micrometer.core.instrument.binder.MeterBinder;
25+
import io.micrometer.core.instrument.composite.CompositeMeterRegistry;
26+
27+
import org.springframework.beans.factory.ObjectProvider;
28+
import org.springframework.beans.factory.config.BeanPostProcessor;
29+
import org.springframework.boot.util.LambdaSafe;
30+
31+
/**
32+
* {@link BeanPostProcessor} to apply {@link MeterRegistryCustomizer customizers} and
33+
* {@link MeterBinder binters} and {@link Metrics#addRegistry global registration} to
34+
* {@link MeterRegistry meter registries}. This post processor intentionally skips
35+
* {@link CompositeMeterRegistry} with the assumptions that the registries it contains are
36+
* beans and will be customized directly.
37+
*
38+
* @author Jon Schneider
39+
* @author Phillip Webb
40+
*/
41+
class MeterRegistryPostProcessor implements BeanPostProcessor {
42+
43+
private final Collection<MeterRegistryCustomizer<?>> customizers;
44+
45+
private final Collection<MeterBinder> binders;
46+
47+
private final boolean addToGlobalRegistry;
48+
49+
MeterRegistryPostProcessor(ObjectProvider<Collection<MeterBinder>> binders,
50+
ObjectProvider<Collection<MeterRegistryCustomizer<?>>> customizers,
51+
boolean addToGlobalRegistry) {
52+
this.binders = binders.getIfAvailable(Collections::emptyList);
53+
this.customizers = customizers.getIfAvailable(Collections::emptyList);
54+
this.addToGlobalRegistry = addToGlobalRegistry;
55+
}
56+
57+
@Override
58+
public Object postProcessBeforeInitialization(Object bean, String beanName) {
59+
return bean;
60+
}
61+
62+
@Override
63+
public Object postProcessAfterInitialization(Object bean, String beanName) {
64+
if (bean instanceof MeterRegistry) {
65+
postProcess((MeterRegistry) bean);
66+
}
67+
return bean;
68+
}
69+
70+
private void postProcess(MeterRegistry registry) {
71+
if (registry instanceof CompositeMeterRegistry) {
72+
return;
73+
}
74+
// Customizers must be applied before binders, as they may add custom tags or
75+
// alter timer or summary configuration.
76+
customize(registry);
77+
addBinders(registry);
78+
if (this.addToGlobalRegistry && registry != Metrics.globalRegistry) {
79+
Metrics.addRegistry(registry);
80+
}
81+
}
82+
83+
@SuppressWarnings("unchecked")
84+
private void customize(MeterRegistry registry) {
85+
LambdaSafe.callbacks(MeterRegistryCustomizer.class, this.customizers, registry)
86+
.withLogger(MeterRegistryPostProcessor.class)
87+
.invoke((customizer) -> customizer.customize(registry));
88+
}
89+
90+
private void addBinders(MeterRegistry registry) {
91+
this.binders.forEach((binder) -> binder.bindTo(registry));
92+
}
93+
94+
}

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

+20-51
Original file line numberDiff line numberDiff line change
@@ -17,28 +17,17 @@
1717
package org.springframework.boot.actuate.autoconfigure.metrics;
1818

1919
import java.util.Collection;
20-
import java.util.Collections;
2120

2221
import io.micrometer.core.annotation.Timed;
22+
import io.micrometer.core.instrument.Clock;
2323
import io.micrometer.core.instrument.MeterRegistry;
24-
import io.micrometer.core.instrument.Metrics;
2524
import io.micrometer.core.instrument.binder.MeterBinder;
26-
import io.micrometer.core.instrument.composite.CompositeMeterRegistry;
2725

2826
import org.springframework.beans.factory.ObjectProvider;
2927
import org.springframework.boot.actuate.autoconfigure.endpoint.condition.ConditionalOnEnabledEndpoint;
3028
import org.springframework.boot.actuate.autoconfigure.metrics.amqp.RabbitMetricsConfiguration;
3129
import org.springframework.boot.actuate.autoconfigure.metrics.cache.CacheMetricsConfiguration;
32-
import org.springframework.boot.actuate.autoconfigure.metrics.export.MetricsExporter;
33-
import org.springframework.boot.actuate.autoconfigure.metrics.export.atlas.AtlasExportConfiguration;
34-
import org.springframework.boot.actuate.autoconfigure.metrics.export.datadog.DatadogExportConfiguration;
35-
import org.springframework.boot.actuate.autoconfigure.metrics.export.ganglia.GangliaExportConfiguration;
36-
import org.springframework.boot.actuate.autoconfigure.metrics.export.graphite.GraphiteExportConfiguration;
37-
import org.springframework.boot.actuate.autoconfigure.metrics.export.influx.InfluxExportConfiguration;
38-
import org.springframework.boot.actuate.autoconfigure.metrics.export.jmx.JmxExportConfiguration;
39-
import org.springframework.boot.actuate.autoconfigure.metrics.export.prometheus.PrometheusExportConfiguration;
40-
import org.springframework.boot.actuate.autoconfigure.metrics.export.simple.SimpleExportConfiguration;
41-
import org.springframework.boot.actuate.autoconfigure.metrics.export.statsd.StatsdExportConfiguration;
30+
import org.springframework.boot.actuate.autoconfigure.metrics.export.CompositeMeterRegistryConfiguration;
4231
import org.springframework.boot.actuate.autoconfigure.metrics.jdbc.DataSourcePoolMetricsConfiguration;
4332
import org.springframework.boot.actuate.autoconfigure.metrics.reactive.server.WebFluxMetricsConfiguration;
4433
import org.springframework.boot.actuate.autoconfigure.metrics.web.client.RestTemplateMetricsConfiguration;
@@ -61,47 +50,33 @@
6150
import org.springframework.context.annotation.Import;
6251
import org.springframework.integration.config.EnableIntegrationManagement;
6352
import org.springframework.integration.support.management.IntegrationManagementConfigurer;
64-
import org.springframework.util.Assert;
6553

6654
/**
6755
* {@link EnableAutoConfiguration Auto-configuration} for Micrometer-based metrics.
6856
*
69-
* @since 2.0.0
7057
* @author Jon Schneider
7158
* @author Stephane Nicoll
59+
* @since 2.0.0
7260
*/
7361
@Configuration
7462
@ConditionalOnClass(Timed.class)
7563
@EnableConfigurationProperties(MetricsProperties.class)
7664
@Import({ MeterBindersConfiguration.class, WebMvcMetricsConfiguration.class,
7765
WebFluxMetricsConfiguration.class, RestTemplateMetricsConfiguration.class,
7866
CacheMetricsConfiguration.class, DataSourcePoolMetricsConfiguration.class,
79-
RabbitMetricsConfiguration.class, AtlasExportConfiguration.class,
80-
DatadogExportConfiguration.class, GangliaExportConfiguration.class,
81-
GraphiteExportConfiguration.class, InfluxExportConfiguration.class,
82-
JmxExportConfiguration.class, PrometheusExportConfiguration.class,
83-
SimpleExportConfiguration.class, StatsdExportConfiguration.class })
67+
RabbitMetricsConfiguration.class, MeterRegistriesConfiguration.class,
68+
CompositeMeterRegistryConfiguration.class })
8469
@AutoConfigureAfter({ CacheAutoConfiguration.class, DataSourceAutoConfiguration.class,
8570
RabbitAutoConfiguration.class, RestTemplateAutoConfiguration.class })
8671
public class MetricsAutoConfiguration {
8772

8873
@Bean
89-
@ConditionalOnMissingBean(MeterRegistry.class)
90-
public CompositeMeterRegistry compositeMeterRegistry(
91-
MetricsProperties metricsProperties,
92-
ObjectProvider<Collection<MetricsExporter>> exporters,
93-
ObjectProvider<Collection<MeterRegistryConfigurer>> configurers) {
94-
CompositeMeterRegistry composite = metricsProperties.isUseGlobalRegistry()
95-
? Metrics.globalRegistry : new CompositeMeterRegistry();
96-
configurers.getIfAvailable(Collections::emptyList)
97-
.forEach((configurer) -> configurer.configureRegistry(composite));
98-
exporters.getIfAvailable(Collections::emptyList).forEach((exporter) -> {
99-
MeterRegistry childRegistry = exporter.registry();
100-
Assert.state(composite != childRegistry,
101-
"cannot add a CompositeMeterRegistry to itself");
102-
composite.add(childRegistry);
103-
});
104-
return composite;
74+
public static MeterRegistryPostProcessor meterRegistryPostProcessor(
75+
ObjectProvider<Collection<MeterBinder>> binders,
76+
ObjectProvider<Collection<MeterRegistryCustomizer<?>>> customizers,
77+
MetricsProperties properties) {
78+
return new MeterRegistryPostProcessor(binders, customizers,
79+
properties.isUseGlobalRegistry());
10580
}
10681

10782
@Bean
@@ -112,6 +87,15 @@ public MetricsEndpoint metricsEndpoint(MeterRegistry registry) {
11287
return new MetricsEndpoint(registry);
11388
}
11489

90+
@Bean
91+
@ConditionalOnMissingBean
92+
public Clock micrometerClock() {
93+
return Clock.SYSTEM;
94+
}
95+
96+
/**
97+
* Binds metrics from Spring Integration.
98+
*/
11599
@Configuration
116100
@ConditionalOnClass(EnableIntegrationManagement.class)
117101
static class MetricsIntegrationConfiguration {
@@ -133,19 +117,4 @@ public SpringIntegrationMetrics springIntegrationMetrics(
133117

134118
}
135119

136-
@Configuration
137-
static class MeterRegistryConfigurationSupport {
138-
139-
MeterRegistryConfigurationSupport(MeterRegistry registry,
140-
MetricsProperties config,
141-
ObjectProvider<Collection<MeterBinder>> binders) {
142-
binders.getIfAvailable(Collections::emptyList)
143-
.forEach((binder) -> binder.bindTo(registry));
144-
if (config.isUseGlobalRegistry() && registry != Metrics.globalRegistry) {
145-
Metrics.addRegistry(registry);
146-
}
147-
}
148-
149-
}
150-
151120
}
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2012-2017 the original author or authors.
2+
* Copyright 2012-2018 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.
@@ -16,23 +16,21 @@
1616

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

19-
import io.micrometer.core.instrument.MeterRegistry;
19+
import org.springframework.context.annotation.Bean;
20+
import org.springframework.context.annotation.Configuration;
2021

2122
/**
22-
* A {@code MetricsExporter} can be used to export metrics, typically to an external
23-
* server running as a separate process.
23+
* Conditionally build a composite registry when more than one registry present.
2424
*
2525
* @author Jon Schneider
26-
* @author Andy Wilkinson
2726
* @since 2.0.0
2827
*/
29-
@FunctionalInterface
30-
public interface MetricsExporter {
28+
@Configuration
29+
public class CompositeMeterRegistryConfiguration {
3130

32-
/**
33-
* Returns the {@link MeterRegistry} used to register metrics with the exporter.
34-
* @return the meter registry
35-
*/
36-
MeterRegistry registry();
31+
@Bean
32+
public static CompositeMeterRegistryPostProcessor compositeMeterRegistryPostProcessor() {
33+
return new CompositeMeterRegistryPostProcessor();
34+
}
3735

3836
}

0 commit comments

Comments
 (0)