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
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 {
|
||||
|
||||
}
|
13
spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/metrics/MeterRegistryConfigurer.java → spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/metrics/MeterRegistryCustomizer.java
13
spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/metrics/MeterRegistryConfigurer.java → spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/metrics/MeterRegistryCustomizer.java
@ -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));
|
||||
}
|
||||
|
||||
}
|
22
spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/metrics/export/MetricsExporter.java → spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/metrics/export/CompositeMeterRegistryConfiguration.java
22
spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/metrics/export/MetricsExporter.java → spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/metrics/export/CompositeMeterRegistryConfiguration.java
@ -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;
|
||||
}
|
||||
|
||||
}
|
37
spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/metrics/MeterRegistryConfigurerTests.java → spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/metrics/MeterRegistryCustomizerTests.java
37
spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/metrics/MeterRegistryConfigurerTests.java → spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/metrics/MeterRegistryCustomizerTests.java
@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
@ -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…
Reference in New Issue