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 gh-11799

Co-authored-by: Jon Schneider <jschneider@pivotal.io>
pull/11839/merge
Phillip Webb 7 years ago
parent 798882bd3f
commit 9a8c182d19

@ -0,0 +1,44 @@
/*
* Copyright 2012-2018 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
*
* http://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 org.springframework.boot.actuate.autoconfigure.metrics.export.atlas.AtlasExportConfiguration;
import org.springframework.boot.actuate.autoconfigure.metrics.export.datadog.DatadogExportConfiguration;
import org.springframework.boot.actuate.autoconfigure.metrics.export.ganglia.GangliaExportConfiguration;
import org.springframework.boot.actuate.autoconfigure.metrics.export.graphite.GraphiteExportConfiguration;
import org.springframework.boot.actuate.autoconfigure.metrics.export.influx.InfluxExportConfiguration;
import org.springframework.boot.actuate.autoconfigure.metrics.export.jmx.JmxExportConfiguration;
import org.springframework.boot.actuate.autoconfigure.metrics.export.prometheus.PrometheusExportConfiguration;
import org.springframework.boot.actuate.autoconfigure.metrics.export.simple.SimpleExportConfiguration;
import org.springframework.boot.actuate.autoconfigure.metrics.export.statsd.StatsdExportConfiguration;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
/**
* Imports for registry configurations.
*
* @author Jon Schneider
*/
@Configuration
@Import({ AtlasExportConfiguration.class, DatadogExportConfiguration.class,
GangliaExportConfiguration.class, GraphiteExportConfiguration.class,
InfluxExportConfiguration.class, JmxExportConfiguration.class,
PrometheusExportConfiguration.class, SimpleExportConfiguration.class,
StatsdExportConfiguration.class })
class MeterRegistriesConfiguration {
}

@ -1,5 +1,5 @@
/*
* Copyright 2012-2017 the original author or authors.
* Copyright 2012-2018 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.
@ -23,19 +23,20 @@ import io.micrometer.core.instrument.MeterRegistry;
* Callback interface that can be used to customize auto-configured {@link MeterRegistry
* MeterRegistries}.
* <p>
* Configurers are guaranteed to be applied before any {@link Meter} is registered with
* Customizers are guaranteed to be applied before any {@link Meter} is registered with
* the registry.
*
* @author Jon Schneider
* @param <T> The registry type to customize
* @since 2.0.0
*/
@FunctionalInterface
public interface MeterRegistryConfigurer {
public interface MeterRegistryCustomizer<T extends MeterRegistry> {
/**
* Configure the given {@code registry}.
* @param registry the registry to configure
* Customize the given {@code registry}.
* @param registry the registry to customize
*/
void configureRegistry(MeterRegistry registry);
void customize(T registry);
}

@ -0,0 +1,94 @@
/*
* Copyright 2012-2018 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
*
* http://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 java.util.Collection;
import java.util.Collections;
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 org.springframework.beans.factory.ObjectProvider;
import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.boot.util.LambdaSafe;
/**
* {@link BeanPostProcessor} to apply {@link MeterRegistryCustomizer customizers} and
* {@link MeterBinder binters} and {@link Metrics#addRegistry global registration} to
* {@link MeterRegistry meter registries}. This post processor intentionally skips
* {@link CompositeMeterRegistry} with the assumptions that the registries it contains are
* beans and will be customized directly.
*
* @author Jon Schneider
* @author Phillip Webb
*/
class MeterRegistryPostProcessor implements BeanPostProcessor {
private final Collection<MeterRegistryCustomizer<?>> customizers;
private final Collection<MeterBinder> binders;
private final boolean addToGlobalRegistry;
MeterRegistryPostProcessor(ObjectProvider<Collection<MeterBinder>> binders,
ObjectProvider<Collection<MeterRegistryCustomizer<?>>> customizers,
boolean addToGlobalRegistry) {
this.binders = binders.getIfAvailable(Collections::emptyList);
this.customizers = customizers.getIfAvailable(Collections::emptyList);
this.addToGlobalRegistry = addToGlobalRegistry;
}
@Override
public Object postProcessBeforeInitialization(Object bean, String beanName) {
return bean;
}
@Override
public Object postProcessAfterInitialization(Object bean, String beanName) {
if (bean instanceof MeterRegistry) {
postProcess((MeterRegistry) bean);
}
return bean;
}
private void postProcess(MeterRegistry registry) {
if (registry instanceof CompositeMeterRegistry) {
return;
}
// Customizers must be applied before binders, as they may add custom tags or
// alter timer or summary configuration.
customize(registry);
addBinders(registry);
if (this.addToGlobalRegistry && registry != Metrics.globalRegistry) {
Metrics.addRegistry(registry);
}
}
@SuppressWarnings("unchecked")
private void customize(MeterRegistry registry) {
LambdaSafe.callbacks(MeterRegistryCustomizer.class, this.customizers, registry)
.withLogger(MeterRegistryPostProcessor.class)
.invoke((customizer) -> customizer.customize(registry));
}
private void addBinders(MeterRegistry registry) {
this.binders.forEach((binder) -> binder.bindTo(registry));
}
}

@ -17,28 +17,17 @@
package org.springframework.boot.actuate.autoconfigure.metrics;
import java.util.Collection;
import java.util.Collections;
import io.micrometer.core.annotation.Timed;
import io.micrometer.core.instrument.Clock;
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 org.springframework.beans.factory.ObjectProvider;
import org.springframework.boot.actuate.autoconfigure.endpoint.condition.ConditionalOnEnabledEndpoint;
import org.springframework.boot.actuate.autoconfigure.metrics.amqp.RabbitMetricsConfiguration;
import org.springframework.boot.actuate.autoconfigure.metrics.cache.CacheMetricsConfiguration;
import org.springframework.boot.actuate.autoconfigure.metrics.export.MetricsExporter;
import org.springframework.boot.actuate.autoconfigure.metrics.export.atlas.AtlasExportConfiguration;
import org.springframework.boot.actuate.autoconfigure.metrics.export.datadog.DatadogExportConfiguration;
import org.springframework.boot.actuate.autoconfigure.metrics.export.ganglia.GangliaExportConfiguration;
import org.springframework.boot.actuate.autoconfigure.metrics.export.graphite.GraphiteExportConfiguration;
import org.springframework.boot.actuate.autoconfigure.metrics.export.influx.InfluxExportConfiguration;
import org.springframework.boot.actuate.autoconfigure.metrics.export.jmx.JmxExportConfiguration;
import org.springframework.boot.actuate.autoconfigure.metrics.export.prometheus.PrometheusExportConfiguration;
import org.springframework.boot.actuate.autoconfigure.metrics.export.simple.SimpleExportConfiguration;
import org.springframework.boot.actuate.autoconfigure.metrics.export.statsd.StatsdExportConfiguration;
import org.springframework.boot.actuate.autoconfigure.metrics.export.CompositeMeterRegistryConfiguration;
import org.springframework.boot.actuate.autoconfigure.metrics.jdbc.DataSourcePoolMetricsConfiguration;
import org.springframework.boot.actuate.autoconfigure.metrics.reactive.server.WebFluxMetricsConfiguration;
import org.springframework.boot.actuate.autoconfigure.metrics.web.client.RestTemplateMetricsConfiguration;
@ -61,14 +50,13 @@ import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
import org.springframework.integration.config.EnableIntegrationManagement;
import org.springframework.integration.support.management.IntegrationManagementConfigurer;
import org.springframework.util.Assert;
/**
* {@link EnableAutoConfiguration Auto-configuration} for Micrometer-based metrics.
*
* @since 2.0.0
* @author Jon Schneider
* @author Stephane Nicoll
* @since 2.0.0
*/
@Configuration
@ConditionalOnClass(Timed.class)
@ -76,32 +64,19 @@ import org.springframework.util.Assert;
@Import({ MeterBindersConfiguration.class, WebMvcMetricsConfiguration.class,
WebFluxMetricsConfiguration.class, RestTemplateMetricsConfiguration.class,
CacheMetricsConfiguration.class, DataSourcePoolMetricsConfiguration.class,
RabbitMetricsConfiguration.class, AtlasExportConfiguration.class,
DatadogExportConfiguration.class, GangliaExportConfiguration.class,
GraphiteExportConfiguration.class, InfluxExportConfiguration.class,
JmxExportConfiguration.class, PrometheusExportConfiguration.class,
SimpleExportConfiguration.class, StatsdExportConfiguration.class })
RabbitMetricsConfiguration.class, MeterRegistriesConfiguration.class,
CompositeMeterRegistryConfiguration.class })
@AutoConfigureAfter({ CacheAutoConfiguration.class, DataSourceAutoConfiguration.class,
RabbitAutoConfiguration.class, RestTemplateAutoConfiguration.class })
public class MetricsAutoConfiguration {
@Bean
@ConditionalOnMissingBean(MeterRegistry.class)
public CompositeMeterRegistry compositeMeterRegistry(
MetricsProperties metricsProperties,
ObjectProvider<Collection<MetricsExporter>> exporters,
ObjectProvider<Collection<MeterRegistryConfigurer>> configurers) {
CompositeMeterRegistry composite = metricsProperties.isUseGlobalRegistry()
? Metrics.globalRegistry : new CompositeMeterRegistry();
configurers.getIfAvailable(Collections::emptyList)
.forEach((configurer) -> configurer.configureRegistry(composite));
exporters.getIfAvailable(Collections::emptyList).forEach((exporter) -> {
MeterRegistry childRegistry = exporter.registry();
Assert.state(composite != childRegistry,
"cannot add a CompositeMeterRegistry to itself");
composite.add(childRegistry);
});
return composite;
public static MeterRegistryPostProcessor meterRegistryPostProcessor(
ObjectProvider<Collection<MeterBinder>> binders,
ObjectProvider<Collection<MeterRegistryCustomizer<?>>> customizers,
MetricsProperties properties) {
return new MeterRegistryPostProcessor(binders, customizers,
properties.isUseGlobalRegistry());
}
@Bean
@ -112,6 +87,15 @@ public class MetricsAutoConfiguration {
return new MetricsEndpoint(registry);
}
@Bean
@ConditionalOnMissingBean
public Clock micrometerClock() {
return Clock.SYSTEM;
}
/**
* Binds metrics from Spring Integration.
*/
@Configuration
@ConditionalOnClass(EnableIntegrationManagement.class)
static class MetricsIntegrationConfiguration {
@ -133,19 +117,4 @@ public class MetricsAutoConfiguration {
}
@Configuration
static class MeterRegistryConfigurationSupport {
MeterRegistryConfigurationSupport(MeterRegistry registry,
MetricsProperties config,
ObjectProvider<Collection<MeterBinder>> binders) {
binders.getIfAvailable(Collections::emptyList)
.forEach((binder) -> binder.bindTo(registry));
if (config.isUseGlobalRegistry() && registry != Metrics.globalRegistry) {
Metrics.addRegistry(registry);
}
}
}
}

@ -1,5 +1,5 @@
/*
* Copyright 2012-2017 the original author or authors.
* Copyright 2012-2018 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.
@ -16,23 +16,21 @@
package org.springframework.boot.actuate.autoconfigure.metrics.export;
import io.micrometer.core.instrument.MeterRegistry;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
/**
* A {@code MetricsExporter} can be used to export metrics, typically to an external
* server running as a separate process.
* Conditionally build a composite registry when more than one registry present.
*
* @author Jon Schneider
* @author Andy Wilkinson
* @since 2.0.0
*/
@FunctionalInterface
public interface MetricsExporter {
@Configuration
public class CompositeMeterRegistryConfiguration {
/**
* Returns the {@link MeterRegistry} used to register metrics with the exporter.
* @return the meter registry
*/
MeterRegistry registry();
@Bean
public static CompositeMeterRegistryPostProcessor compositeMeterRegistryPostProcessor() {
return new CompositeMeterRegistryPostProcessor();
}
}

@ -0,0 +1,122 @@
/*
* Copyright 2012-2018 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
*
* http://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.export;
import java.util.Arrays;
import io.micrometer.core.instrument.Clock;
import io.micrometer.core.instrument.MeterRegistry;
import io.micrometer.core.instrument.composite.CompositeMeterRegistry;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.BeanFactoryAware;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.config.BeanFactoryPostProcessor;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.beans.factory.config.ConstructorArgumentValues;
import org.springframework.beans.factory.config.ConstructorArgumentValues.ValueHolder;
import org.springframework.beans.factory.config.RuntimeBeanReference;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.beans.factory.support.BeanDefinitionRegistryPostProcessor;
import org.springframework.beans.factory.support.GenericBeanDefinition;
import org.springframework.beans.factory.support.ManagedList;
import org.springframework.core.Ordered;
import org.springframework.core.annotation.Order;
import org.springframework.util.ObjectUtils;
/**
* {@link BeanFactoryPostProcessor} to register a {@link CompositeMeterRegistry} when
* necessary.
*
* @author Jon Schneider
* @author Phillip Webb
*/
@Order(Ordered.LOWEST_PRECEDENCE - 1)
class CompositeMeterRegistryPostProcessor
implements BeanDefinitionRegistryPostProcessor, BeanFactoryAware {
private static final String COMPOSITE_BEAN_NAME = "compositeMeterRegistry";
private ConfigurableListableBeanFactory beanFactory;
@Override
public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
if (beanFactory instanceof ConfigurableListableBeanFactory) {
this.beanFactory = (ConfigurableListableBeanFactory) beanFactory;
}
}
@Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory)
throws BeansException {
}
@Override
public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry)
throws BeansException {
if (this.beanFactory == null) {
return;
}
String[] registryBeans = this.beanFactory.getBeanNamesForType(MeterRegistry.class,
true, false);
registerCompositeIfNecessary(registry, registryBeans);
}
private void registerCompositeIfNecessary(BeanDefinitionRegistry registry,
String[] registryBeans) {
if (ObjectUtils.isEmpty(registryBeans)) {
registerNoOpMeterRegistry(registry);
}
if (registryBeans.length > 1 && !hasPrimaryDefinition(registryBeans)) {
registerPrimaryCompositeMeterRegistry(registry, registryBeans);
}
}
private boolean hasPrimaryDefinition(String[] registryBeans) {
return Arrays.stream(registryBeans).map(this.beanFactory::getBeanDefinition)
.anyMatch(BeanDefinition::isPrimary);
}
private void registerNoOpMeterRegistry(BeanDefinitionRegistry registry) {
// If there are no meter registries configured, we register an empty composite
// that effectively no-ops metrics instrumentation throughout the app.
GenericBeanDefinition definition = new GenericBeanDefinition();
definition.setBeanClass(CompositeMeterRegistry.class);
registry.registerBeanDefinition(COMPOSITE_BEAN_NAME, definition);
}
private void registerPrimaryCompositeMeterRegistry(BeanDefinitionRegistry registry,
String[] registryBeans) {
GenericBeanDefinition definition = new GenericBeanDefinition();
definition.setBeanClass(CompositeMeterRegistry.class);
definition.setPrimary(true);
ConstructorArgumentValues arguments = new ConstructorArgumentValues();
arguments.addIndexedArgumentValue(0,
new ValueHolder(null, Clock.class.getName()));
arguments.addIndexedArgumentValue(1, getBeanReferences(registryBeans));
definition.setConstructorArgumentValues(arguments);
registry.registerBeanDefinition(COMPOSITE_BEAN_NAME, definition);
}
private ManagedList<RuntimeBeanReference> getBeanReferences(String[] names) {
ManagedList<RuntimeBeanReference> references = new ManagedList<>(names.length);
Arrays.stream(names).map(RuntimeBeanReference::new).forEach(references::add);
return references;
}
}

@ -1,5 +1,5 @@
/*
* Copyright 2012-2017 the original author or authors.
* Copyright 2012-2018 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.
@ -20,7 +20,6 @@ import com.netflix.spectator.atlas.AtlasConfig;
import io.micrometer.atlas.AtlasMeterRegistry;
import io.micrometer.core.instrument.Clock;
import org.springframework.boot.actuate.autoconfigure.metrics.export.MetricsExporter;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
@ -48,14 +47,8 @@ public class AtlasExportConfiguration {
@Bean
@ConditionalOnProperty(value = "management.metrics.export.atlas.enabled", matchIfMissing = true)
public MetricsExporter atlasExporter(AtlasConfig atlasConfig, Clock clock) {
return () -> new AtlasMeterRegistry(atlasConfig, clock);
}
@Bean
@ConditionalOnMissingBean
public Clock micrometerClock() {
return Clock.SYSTEM;
public AtlasMeterRegistry atlasMeterRegistry(AtlasConfig atlasConfig, Clock clock) {
return new AtlasMeterRegistry(atlasConfig, clock);
}
}

@ -1,5 +1,5 @@
/*
* Copyright 2012-2017 the original author or authors.
* Copyright 2012-2018 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.
@ -20,7 +20,6 @@ import io.micrometer.core.instrument.Clock;
import io.micrometer.datadog.DatadogConfig;
import io.micrometer.datadog.DatadogMeterRegistry;
import org.springframework.boot.actuate.autoconfigure.metrics.export.MetricsExporter;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
@ -48,14 +47,9 @@ public class DatadogExportConfiguration {
@Bean
@ConditionalOnProperty(value = "management.metrics.export.datadog.enabled", matchIfMissing = true)
public MetricsExporter datadogExporter(DatadogConfig datadogConfig, Clock clock) {
return () -> new DatadogMeterRegistry(datadogConfig, clock);
}
@Bean
@ConditionalOnMissingBean
public Clock micrometerClock() {
return Clock.SYSTEM;
public DatadogMeterRegistry datadogMeterRegistry(DatadogConfig datadogConfig,
Clock clock) {
return new DatadogMeterRegistry(datadogConfig, clock);
}
}

@ -21,7 +21,6 @@ import io.micrometer.core.instrument.util.HierarchicalNameMapper;
import io.micrometer.ganglia.GangliaConfig;
import io.micrometer.ganglia.GangliaMeterRegistry;
import org.springframework.boot.actuate.autoconfigure.metrics.export.MetricsExporter;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
@ -48,15 +47,9 @@ public class GangliaExportConfiguration {
@Bean
@ConditionalOnProperty(value = "management.metrics.export.ganglia.enabled", matchIfMissing = true)
public MetricsExporter gangliaExporter(GangliaConfig gangliaConfig,
public GangliaMeterRegistry gangliaMeterRegistry(GangliaConfig gangliaConfig,
HierarchicalNameMapper nameMapper, Clock clock) {
return () -> new GangliaMeterRegistry(gangliaConfig, clock, nameMapper);
}
@Bean
@ConditionalOnMissingBean
public Clock micrometerClock() {
return Clock.SYSTEM;
return new GangliaMeterRegistry(gangliaConfig, clock, nameMapper);
}
@Bean

@ -21,7 +21,6 @@ import io.micrometer.core.instrument.util.HierarchicalNameMapper;
import io.micrometer.graphite.GraphiteConfig;
import io.micrometer.graphite.GraphiteMeterRegistry;
import org.springframework.boot.actuate.autoconfigure.metrics.export.MetricsExporter;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
@ -48,15 +47,9 @@ public class GraphiteExportConfiguration {
@Bean
@ConditionalOnProperty(value = "management.metrics.export.graphite.enabled", matchIfMissing = true)
public MetricsExporter graphiteExporter(GraphiteConfig graphiteConfig,
public GraphiteMeterRegistry graphiteMeterRegistry(GraphiteConfig graphiteConfig,
HierarchicalNameMapper nameMapper, Clock clock) {
return () -> new GraphiteMeterRegistry(graphiteConfig, clock, nameMapper);
}
@Bean
@ConditionalOnMissingBean
public Clock micrometerClock() {
return Clock.SYSTEM;
return new GraphiteMeterRegistry(graphiteConfig, clock, nameMapper);
}
@Bean

@ -1,5 +1,5 @@
/*
* Copyright 2012-2017 the original author or authors.
* Copyright 2012-2018 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.
@ -20,7 +20,6 @@ import io.micrometer.core.instrument.Clock;
import io.micrometer.influx.InfluxConfig;
import io.micrometer.influx.InfluxMeterRegistry;
import org.springframework.boot.actuate.autoconfigure.metrics.export.MetricsExporter;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
@ -47,14 +46,9 @@ public class InfluxExportConfiguration {
@Bean
@ConditionalOnProperty(value = "management.metrics.export.influx.enabled", matchIfMissing = true)
public MetricsExporter influxExporter(InfluxConfig influxConfig, Clock clock) {
return () -> new InfluxMeterRegistry(influxConfig, clock);
}
@Bean
@ConditionalOnMissingBean
public Clock micrometerClock() {
return Clock.SYSTEM;
public InfluxMeterRegistry influxMeterRegistry(InfluxConfig influxConfig,
Clock clock) {
return new InfluxMeterRegistry(influxConfig, clock);
}
}

@ -21,7 +21,6 @@ import io.micrometer.core.instrument.util.HierarchicalNameMapper;
import io.micrometer.jmx.JmxConfig;
import io.micrometer.jmx.JmxMeterRegistry;
import org.springframework.boot.actuate.autoconfigure.metrics.export.MetricsExporter;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
@ -48,15 +47,9 @@ public class JmxExportConfiguration {
@Bean
@ConditionalOnProperty(value = "management.metrics.export.jmx.enabled", matchIfMissing = true)
public MetricsExporter jmxExporter(JmxConfig config,
public JmxMeterRegistry jmxMeterRegistry(JmxConfig config,
HierarchicalNameMapper nameMapper, Clock clock) {
return () -> new JmxMeterRegistry(config, clock, nameMapper);
}
@Bean
@ConditionalOnMissingBean
public Clock micrometerClock() {
return Clock.SYSTEM;
return new JmxMeterRegistry(config, clock, nameMapper);
}
@Bean

@ -1,5 +1,5 @@
/*
* Copyright 2012-2017 the original author or authors.
* Copyright 2012-2018 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.
@ -21,7 +21,6 @@ import io.micrometer.prometheus.PrometheusConfig;
import io.micrometer.prometheus.PrometheusMeterRegistry;
import io.prometheus.client.CollectorRegistry;
import org.springframework.boot.actuate.autoconfigure.metrics.export.MetricsExporter;
import org.springframework.boot.actuate.autoconfigure.web.ManagementContextConfiguration;
import org.springframework.boot.actuate.metrics.export.prometheus.PrometheusScrapeEndpoint;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
@ -50,10 +49,10 @@ public class PrometheusExportConfiguration {
@Bean
@ConditionalOnProperty(value = "management.metrics.export.prometheus.enabled", matchIfMissing = true)
public MetricsExporter prometheusExporter(PrometheusConfig prometheusConfig,
CollectorRegistry collectorRegistry, Clock clock) {
return () -> new PrometheusMeterRegistry(prometheusConfig, collectorRegistry,
clock);
public PrometheusMeterRegistry prometheusMeterRegistry(
PrometheusConfig prometheusConfig, CollectorRegistry collectorRegistry,
Clock clock) {
return new PrometheusMeterRegistry(prometheusConfig, collectorRegistry, clock);
}
@Bean
@ -62,12 +61,6 @@ public class PrometheusExportConfiguration {
return new CollectorRegistry(true);
}
@Bean
@ConditionalOnMissingBean
public Clock micrometerClock() {
return Clock.SYSTEM;
}
@ManagementContextConfiguration
public static class PrometheusScrapeEndpointConfiguration {

@ -1,5 +1,5 @@
/*
* Copyright 2012-2017 the original author or authors.
* Copyright 2012-2018 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,10 +17,10 @@
package org.springframework.boot.actuate.autoconfigure.metrics.export.simple;
import io.micrometer.core.instrument.Clock;
import io.micrometer.core.instrument.MeterRegistry;
import io.micrometer.core.instrument.simple.SimpleConfig;
import io.micrometer.core.instrument.simple.SimpleMeterRegistry;
import org.springframework.boot.actuate.autoconfigure.metrics.export.MetricsExporter;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
@ -39,15 +39,9 @@ public class SimpleExportConfiguration {
@Bean
@ConditionalOnProperty(value = "management.metrics.export.simple.enabled", matchIfMissing = true)
@ConditionalOnMissingBean(MetricsExporter.class)
public MetricsExporter simpleExporter(SimpleConfig config, Clock clock) {
return () -> new SimpleMeterRegistry(config, clock);
}
@Bean
@ConditionalOnMissingBean
public Clock micrometerClock() {
return Clock.SYSTEM;
@ConditionalOnMissingBean(MeterRegistry.class)
public SimpleMeterRegistry simpleMeterRegistry(SimpleConfig config, Clock clock) {
return new SimpleMeterRegistry(config, clock);
}
@Bean

@ -1,5 +1,5 @@
/*
* Copyright 2012-2017 the original author or authors.
* Copyright 2012-2018 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.
@ -21,7 +21,6 @@ import io.micrometer.core.instrument.util.HierarchicalNameMapper;
import io.micrometer.statsd.StatsdConfig;
import io.micrometer.statsd.StatsdMeterRegistry;
import org.springframework.boot.actuate.autoconfigure.metrics.export.MetricsExporter;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
@ -48,15 +47,9 @@ public class StatsdExportConfiguration {
@Bean
@ConditionalOnProperty(value = "management.metrics.export.statsd.enabled", matchIfMissing = true)
public MetricsExporter statsdExporter(StatsdConfig statsdConfig,
public StatsdMeterRegistry statsdMeterRegistry(StatsdConfig statsdConfig,
HierarchicalNameMapper hierarchicalNameMapper, Clock clock) {
return () -> new StatsdMeterRegistry(statsdConfig, hierarchicalNameMapper, clock);
}
@Bean
@ConditionalOnMissingBean
public Clock micrometerClock() {
return Clock.SYSTEM;
return new StatsdMeterRegistry(statsdConfig, hierarchicalNameMapper, clock);
}
@Bean

@ -1,5 +1,5 @@
/*
* Copyright 2012-2017 the original author or authors.
* Copyright 2012-2018 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.
@ -19,6 +19,7 @@ package org.springframework.boot.actuate.autoconfigure;
import org.junit.After;
import org.junit.Test;
import org.springframework.boot.actuate.autoconfigure.metrics.MetricsAutoConfiguration;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.autoconfigure.cassandra.CassandraAutoConfiguration;
import org.springframework.boot.autoconfigure.data.cassandra.CassandraDataAutoConfiguration;
@ -67,8 +68,8 @@ public class SpringApplicationHierarchyTests {
CassandraAutoConfiguration.class, CassandraDataAutoConfiguration.class,
MongoDataAutoConfiguration.class, Neo4jDataAutoConfiguration.class,
Neo4jRepositoriesAutoConfiguration.class, RedisAutoConfiguration.class,
RedisRepositoriesAutoConfiguration.class,
FlywayAutoConfiguration.class }, excludeName = {
RedisRepositoriesAutoConfiguration.class, FlywayAutoConfiguration.class,
MetricsAutoConfiguration.class }, excludeName = {
"org.springframework.boot.autoconfigure.data.elasticsearch.ElasticsearchAutoConfiguration" })
public static class Child {
@ -79,8 +80,8 @@ public class SpringApplicationHierarchyTests {
CassandraAutoConfiguration.class, CassandraDataAutoConfiguration.class,
MongoDataAutoConfiguration.class, Neo4jDataAutoConfiguration.class,
Neo4jRepositoriesAutoConfiguration.class, RedisAutoConfiguration.class,
RedisRepositoriesAutoConfiguration.class,
FlywayAutoConfiguration.class }, excludeName = {
RedisRepositoriesAutoConfiguration.class, FlywayAutoConfiguration.class,
MetricsAutoConfiguration.class }, excludeName = {
"org.springframework.boot.autoconfigure.data.elasticsearch.ElasticsearchAutoConfiguration" })
public static class Parent {

@ -17,20 +17,22 @@
package org.springframework.boot.actuate.autoconfigure.metrics;
import io.micrometer.core.instrument.MeterRegistry;
import io.micrometer.core.instrument.MeterRegistry.Config;
import org.junit.Test;
import org.springframework.boot.test.context.runner.ApplicationContextRunner;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import static org.assertj.core.api.Assertions.assertThat;
/**
* Tests for applying {@link MeterRegistryConfigurer MeterRegistryConfigurers}.
* Tests for applying {@link MeterRegistryCustomizer} beans.
*
* @author Jon Schneider
* @author Andy Wilkinson
*/
public class MeterRegistryConfigurerTests {
public class MeterRegistryCustomizerTests {
private ApplicationContextRunner contextRunner = new ApplicationContextRunner()
.with(MetricsRun.simple());
@ -38,26 +40,35 @@ public class MeterRegistryConfigurerTests {
@Test
public void commonTagsAreAppliedToAutoConfiguredBinders() {
this.contextRunner
.withUserConfiguration(MeterRegistryConfigurerConfiguration.class)
.run((context) -> assertThat(context.getBean(MeterRegistry.class)
.get("jvm.memory.used").tags("region", "us-east-1").gauge())
.isNotNull());
.withUserConfiguration(MeterRegistryCustomizerConfiguration.class)
.run((context) -> {
MeterRegistry registry = context.getBean(MeterRegistry.class);
assertThat(registry.get("jvm.memory.used").tags("region", "us-east-1")
.gauge()).isNotNull();
});
}
@Test
public void commonTagsAreAppliedBeforeRegistryIsInjectableElsewhere() {
this.contextRunner
.withUserConfiguration(MeterRegistryConfigurerConfiguration.class)
.run((context) -> assertThat(context.getBean(MeterRegistry.class)
.get("my.thing").tags("region", "us-east-1").gauge())
.isNotNull());
.withUserConfiguration(MeterRegistryCustomizerConfiguration.class)
.run((context) -> {
MeterRegistry registry = context.getBean(MeterRegistry.class);
assertThat(
registry.get("my.thing").tags("region", "us-east-1").gauge())
.isNotNull();
});
}
static class MeterRegistryConfigurerConfiguration {
@Configuration
static class MeterRegistryCustomizerConfiguration {
@Bean
public MeterRegistryConfigurer registryConfigurer() {
return (registry) -> registry.config().commonTags("region", "us-east-1");
public MeterRegistryCustomizer<MeterRegistry> commonTags() {
return (registry) -> {
Config config = registry.config();
config.commonTags("region", "us-east-1");
};
}
@Bean

@ -0,0 +1,184 @@
/*
* Copyright 2012-2018 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
*
* http://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 java.util.ArrayList;
import java.util.Collection;
import java.util.List;
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 org.junit.Before;
import org.junit.Test;
import org.mockito.InOrder;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.ObjectProvider;
import org.springframework.util.Assert;
import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.Mockito.inOrder;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.verifyZeroInteractions;
/**
* Tests for {@link MeterRegistryPostProcessor}.
*
* @author Phillip Webb
*/
public class MeterRegistryPostProcessorTests {
private List<MeterBinder> binderBeans = new ArrayList<>();
private List<MeterRegistryCustomizer<?>> customizerBeans = new ArrayList<>();
private ObjectProvider<Collection<MeterBinder>> binders = TestObjectProvider
.of(this.binderBeans);
private ObjectProvider<Collection<MeterRegistryCustomizer<?>>> customizers = TestObjectProvider
.of(this.customizerBeans);
@Mock
private MeterBinder mockBinder;
@Mock
private MeterRegistryCustomizer<MeterRegistry> mockCustomizer;
@Mock
private MeterRegistry mockRegistry;
@Before
public void setup() {
MockitoAnnotations.initMocks(this);
}
@Test
public void postProcessWhenNotRegistryShouldReturnBean() {
MeterRegistryPostProcessor processor = new MeterRegistryPostProcessor(
this.binders, this.customizers, false);
Object bean = new Object();
String beanName = "name";
assertThat(processor.postProcessBeforeInitialization(bean, beanName))
.isEqualTo(bean);
assertThat(processor.postProcessAfterInitialization(bean, beanName))
.isEqualTo(bean);
}
@Test
public void postProcessorWhenCompositeShouldSkip() {
this.binderBeans.add(this.mockBinder);
this.customizerBeans.add(this.mockCustomizer);
MeterRegistryPostProcessor processor = new MeterRegistryPostProcessor(
this.binders, this.customizers, false);
assertThat(processor.postProcessAfterInitialization(new CompositeMeterRegistry(),
"name"));
verifyZeroInteractions(this.mockBinder, this.mockCustomizer);
}
@Test
public void postProcessShouldApplyCustomizer() {
this.customizerBeans.add(this.mockCustomizer);
MeterRegistryPostProcessor processor = new MeterRegistryPostProcessor(
this.binders, this.customizers, false);
assertThat(processor.postProcessAfterInitialization(this.mockRegistry, "name"));
verify(this.mockCustomizer).customize(this.mockRegistry);
}
@Test
public void postProcessShouldApplyBinder() {
this.binderBeans.add(this.mockBinder);
MeterRegistryPostProcessor processor = new MeterRegistryPostProcessor(
this.binders, this.customizers, false);
assertThat(processor.postProcessAfterInitialization(this.mockRegistry, "name"));
verify(this.mockBinder).bindTo(this.mockRegistry);
}
@Test
public void postProcessShouldCustomizeBeforeApplyingBinder() {
this.binderBeans.add(this.mockBinder);
this.customizerBeans.add(this.mockCustomizer);
MeterRegistryPostProcessor processor = new MeterRegistryPostProcessor(
this.binders, this.customizers, false);
processor.postProcessAfterInitialization(this.mockRegistry, "name");
InOrder ordered = inOrder(this.mockCustomizer, this.mockBinder);
ordered.verify(this.mockCustomizer).customize(this.mockRegistry);
ordered.verify(this.mockBinder).bindTo(this.mockRegistry);
}
@Test
public void postProcessWhenAddToGlobalRegistryShouldAddToGlobalRegistry() {
MeterRegistryPostProcessor processor = new MeterRegistryPostProcessor(
this.binders, this.customizers, true);
try {
processor.postProcessAfterInitialization(this.mockRegistry, "name");
assertThat(Metrics.globalRegistry.getRegistries())
.contains(this.mockRegistry);
}
finally {
Metrics.removeRegistry(this.mockRegistry);
}
}
@Test
public void postProcessWhenNotAddToGlobalRegistryShouldAddToGlobalRegistry() {
MeterRegistryPostProcessor processor = new MeterRegistryPostProcessor(
this.binders, this.customizers, false);
processor.postProcessAfterInitialization(this.mockRegistry, "name");
assertThat(Metrics.globalRegistry.getRegistries())
.doesNotContain(this.mockRegistry);
}
private static class TestObjectProvider<T> implements ObjectProvider<T> {
private final T value;
TestObjectProvider(T value) {
this.value = value;
}
@Override
public T getObject() throws BeansException {
Assert.state(this.value != null, "No value");
return this.value;
}
@Override
public T getObject(Object... args) throws BeansException {
return this.value;
}
@Override
public T getIfAvailable() throws BeansException {
return this.value;
}
@Override
public T getIfUnique() throws BeansException {
throw new UnsupportedOperationException();
}
public static <T> TestObjectProvider<T> of(T value) {
return new TestObjectProvider<>(value);
}
}
}

@ -51,6 +51,7 @@ import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
import org.springframework.context.annotation.Primary;
import org.springframework.http.HttpMethod;
import org.springframework.http.MediaType;
import org.springframework.test.context.TestPropertySource;
@ -140,6 +141,7 @@ public class MetricsAutoConfigurationIntegrationTests {
@Import(PersonController.class)
static class MetricsApp {
@Primary
@Bean
public MeterRegistry registry() {
return new SimpleMeterRegistry(SimpleConfig.DEFAULT, new MockClock());

@ -1,47 +0,0 @@
/*
* Copyright 2012-2018 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
*
* http://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.composite.CompositeMeterRegistry;
import io.micrometer.graphite.GraphiteMeterRegistry;
import io.micrometer.prometheus.PrometheusMeterRegistry;
import org.junit.Test;
import org.springframework.boot.autoconfigure.AutoConfigurations;
import org.springframework.boot.test.context.runner.ApplicationContextRunner;
import static org.assertj.core.api.Assertions.assertThat;
/**
* Tests for {@link MetricsAutoConfiguration} creating a {@link CompositeMeterRegistry}.
*
* @author Jon Schneider
*/
public class MetricsConfigurationCompositeTests {
@Test
public void compositeContainsImplementationsOnClasspath() {
new ApplicationContextRunner()
.withConfiguration(AutoConfigurations.of(MetricsAutoConfiguration.class))
.withPropertyValues("management.metrics.use-global-registry=false")
.run((context) -> assertThat(
context.getBean(CompositeMeterRegistry.class).getRegistries())
.hasAtLeastOneElementOfType(PrometheusMeterRegistry.class)
.hasAtLeastOneElementOfType(GraphiteMeterRegistry.class));
}
}

@ -0,0 +1,111 @@
/*
* Copyright 2012-2018 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
*
* http://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.export;
import io.micrometer.core.instrument.MeterRegistry;
import io.micrometer.core.instrument.MockClock;
import io.micrometer.core.instrument.composite.CompositeMeterRegistry;
import io.micrometer.core.instrument.simple.SimpleConfig;
import io.micrometer.core.instrument.simple.SimpleMeterRegistry;
import io.micrometer.graphite.GraphiteMeterRegistry;
import io.micrometer.jmx.JmxMeterRegistry;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.boot.actuate.autoconfigure.metrics.MetricsRun;
import org.springframework.boot.test.context.runner.ApplicationContextRunner;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import org.springframework.test.context.junit4.SpringRunner;
import static org.assertj.core.api.Assertions.assertThat;
/**
* Tests for {@link CompositeMeterRegistryConfiguration}.
*
* @author Jon Schneider
*/
@RunWith(SpringRunner.class)
public class CompositeMeterRegistryConfigurationTests {
private ApplicationContextRunner contextRunner = new ApplicationContextRunner()
.with(MetricsRun.simple());
/**
* The simple registry is off by default UNLESS there is no other registry
* implementation on the classpath, in which case it is on.
*/
@Test
public void simpleWithNoCompositeCreated() {
this.contextRunner
.run((context) -> assertThat(context.getBean(MeterRegistry.class))
.isInstanceOf(SimpleMeterRegistry.class));
}
/**
* An empty composite is created in the absence of any other registry implementation.
* This effectively no-ops instrumentation code throughout the application.
*/
@Test
public void emptyCompositeCreated() {
new ApplicationContextRunner().with(MetricsRun.limitedTo()).run((context) -> {
MeterRegistry registry = context.getBean(MeterRegistry.class);
assertThat(registry).isInstanceOf(CompositeMeterRegistry.class);
assertThat(((CompositeMeterRegistry) registry).getRegistries()).isEmpty();
});
}
@Test
public void noCompositeCreatedWhenSingleImplementationIsEnabled() {
new ApplicationContextRunner().with(MetricsRun.limitedTo("graphite"))
.run((context) -> assertThat(context.getBean(MeterRegistry.class))
.isInstanceOf(GraphiteMeterRegistry.class));
}
@Test
public void noCompositeCreatedWhenMultipleRegistriesButOneMarkedAsPrimary() {
new ApplicationContextRunner().with(MetricsRun.limitedTo("graphite", "jmx"))
.withUserConfiguration(PrimarySimpleMeterRegistryConfiguration.class)
.run((context) -> assertThat(context.getBean(MeterRegistry.class))
.isInstanceOf(SimpleMeterRegistry.class));
}
@Test
public void compositeCreatedWhenMultipleImplementationsAreEnabled() {
new ApplicationContextRunner().with(MetricsRun.limitedTo("graphite", "jmx"))
.run((context) -> {
MeterRegistry registry = context.getBean(MeterRegistry.class);
assertThat(registry).isInstanceOf(CompositeMeterRegistry.class);
assertThat(((CompositeMeterRegistry) registry).getRegistries())
.hasAtLeastOneElementOfType(GraphiteMeterRegistry.class)
.hasAtLeastOneElementOfType(JmxMeterRegistry.class);
});
}
@Configuration
static class PrimarySimpleMeterRegistryConfiguration {
@Primary
@Bean
public MeterRegistry simpleMeterRegistry() {
return new SimpleMeterRegistry(SimpleConfig.DEFAULT, new MockClock());
}
}
}

@ -0,0 +1,134 @@
/*
* Copyright 2012-2018 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
*
* http://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.export;
import io.micrometer.core.instrument.Clock;
import io.micrometer.core.instrument.MeterRegistry;
import io.micrometer.core.instrument.composite.CompositeMeterRegistry;
import io.micrometer.core.instrument.simple.SimpleMeterRegistry;
import org.junit.Test;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.test.context.runner.ApplicationContextRunner;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import static org.assertj.core.api.Assertions.assertThat;
/**
* Tests for {@link CompositeMeterRegistryPostProcessor}.
*
* @author Phillip Webb
*/
public class CompositeMeterRegistryPostProcessorTests {
private static final String COMPOSITE_NAME = "compositeMeterRegistry";
private ApplicationContextRunner contextRunner = new ApplicationContextRunner()
.withUserConfiguration(BaseConfig.class);
@Test
public void registerWhenHasNoMeterRegistryShouldRegisterEmptyComposite() {
this.contextRunner.withUserConfiguration(NoMeterRegistryConfig.class)
.run((context) -> {
assertThat(context).hasSingleBean(MeterRegistry.class);
CompositeMeterRegistry registry = context.getBean(COMPOSITE_NAME,
CompositeMeterRegistry.class);
assertThat(registry.getRegistries()).isEmpty();
});
}
@Test
public void registerWhenHasSingleMeterRegistryShouldDoNothing() {
this.contextRunner.withUserConfiguration(SingleMeterRegistryConfig.class)
.run((context) -> {
assertThat(context).hasSingleBean(MeterRegistry.class);
MeterRegistry registry = context.getBean(MeterRegistry.class);
assertThat(registry).isInstanceOf(TestMeterRegistry.class);
});
}
@Test
public void registerWhenHasMultipleMeterRegistriesShouldAddPrimaryComposite() {
this.contextRunner.withUserConfiguration(MultipleMeterRegistriesConfig.class)
.run((context) -> {
assertThat(context.getBeansOfType(MeterRegistry.class)).hasSize(3)
.containsKeys("meterRegistryOne", "meterRegistryTwo",
COMPOSITE_NAME);
MeterRegistry primary = context.getBean(MeterRegistry.class);
assertThat(primary).isInstanceOf(CompositeMeterRegistry.class);
assertThat(((CompositeMeterRegistry) primary).getRegistries())
.hasSize(2);
});
}
@Test
public void registerWhenHasMultipleRegistriesAndOneIsPrimaryShouldDoNothing() {
}
@Configuration
static class BaseConfig {
@Bean
public CompositeMeterRegistryPostProcessor compositeMeterRegistryPostProcessor() {
return new CompositeMeterRegistryPostProcessor();
}
@Bean
@ConditionalOnMissingBean
public Clock micrometerClock() {
return Clock.SYSTEM;
}
}
@Configuration
static class NoMeterRegistryConfig {
}
@Configuration
static class SingleMeterRegistryConfig {
@Bean
public MeterRegistry meterRegistry() {
return new TestMeterRegistry();
}
}
@Configuration
static class MultipleMeterRegistriesConfig {
@Bean
public MeterRegistry meterRegistryOne() {
return new TestMeterRegistry();
}
@Bean
public MeterRegistry meterRegistryTwo() {
return new SimpleMeterRegistry();
}
}
static class TestMeterRegistry extends SimpleMeterRegistry {
}
}

@ -1,61 +0,0 @@
/*
* Copyright 2012-2018 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
*
* http://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.export.simple;
import io.micrometer.core.instrument.composite.CompositeMeterRegistry;
import io.micrometer.core.instrument.simple.SimpleMeterRegistry;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.boot.actuate.autoconfigure.metrics.MetricsAutoConfiguration;
import org.springframework.boot.autoconfigure.AutoConfigurations;
import org.springframework.boot.test.context.runner.ApplicationContextRunner;
import org.springframework.test.context.junit4.SpringRunner;
import static org.assertj.core.api.Assertions.assertThat;
/**
* Tests for {@link SimpleExportConfiguration}.
*
* @author Jon Schneider
*/
@RunWith(SpringRunner.class)
public class SimpleExportConfigurationTests {
@Test
public void simpleMeterRegistryIsInTheCompositeWhenNoOtherRegistryIs() {
new ApplicationContextRunner()
.withPropertyValues("management.metrics.export.atlas.enabled=false",
"management.metrics.export.datadog.enabled=false",
"management.metrics.export.ganglia.enabled=false",
"management.metrics.export.graphite.enabled=false",
"management.metrics.export.influx.enabled=false",
"management.metrics.export.jmx.enabled=false",
"management.metrics.export.prometheus.enabled=false",
"management.metrics.export.statsd.enabled=false",
"management.metrics.use-global-registry=false")
.withConfiguration(AutoConfigurations.of(MetricsAutoConfiguration.class))
.run((context) -> {
CompositeMeterRegistry meterRegistry = context
.getBean(CompositeMeterRegistry.class);
assertThat(meterRegistry.getRegistries()).hasSize(1);
assertThat(meterRegistry.getRegistries())
.hasOnlyElementsOfType(SimpleMeterRegistry.class);
});
}
}
Loading…
Cancel
Save