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