Skip to content

Commit

Permalink
Order MeterFilters and MeterRegistryCustomizers
Browse files Browse the repository at this point in the history
Closes gh-918
  • Loading branch information
izeye authored and jkschneider committed Oct 12, 2018
1 parent d0845cf commit 492fa69
Show file tree
Hide file tree
Showing 5 changed files with 291 additions and 115 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -18,21 +18,42 @@
import io.micrometer.core.instrument.MeterRegistry;
import io.micrometer.core.instrument.binder.MeterBinder;
import io.micrometer.core.instrument.config.MeterFilter;
import io.micrometer.core.lang.NonNullApi;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.ObjectProvider;
import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.context.ApplicationContext;

import java.util.Collection;
import java.util.Collections;
import java.util.List;

@NonNullApi
public class MeterRegistryPostProcessor implements BeanPostProcessor {
private final ApplicationContext context;
/**
* {@link BeanPostProcessor} that delegates to a lazily created
* {@link MeterRegistryConfigurer} to post-process {@link MeterRegistry} beans.
*
* @author Jon Schneider
* @author Phillip Webb
* @author Andy Wilkinson
*/
class MeterRegistryPostProcessor implements BeanPostProcessor {

private final ObjectProvider<List<MeterBinder>> meterBinders;

private final ObjectProvider<List<MeterFilter>> meterFilters;

private final ObjectProvider<List<MeterRegistryCustomizer<?>>> meterRegistryCustomizers;

private final ObjectProvider<MetricsProperties> metricsProperties;

private volatile MeterRegistryConfigurer configurer;

MeterRegistryPostProcessor(ApplicationContext context) {
this.context = context;
MeterRegistryPostProcessor(
ObjectProvider<List<MeterBinder>> meterBinders,
ObjectProvider<List<MeterFilter>> meterFilters,
ObjectProvider<List<MeterRegistryCustomizer<?>>> meterRegistryCustomizers,
ObjectProvider<MetricsProperties> metricsProperties) {
this.meterBinders = meterBinders;
this.meterFilters = meterFilters;
this.meterRegistryCustomizers = meterRegistryCustomizers;
this.metricsProperties = metricsProperties;
}

@Override
Expand All @@ -41,27 +62,26 @@ public Object postProcessBeforeInitialization(Object bean, String beanName) thro
}

@Override
public Object postProcessAfterInitialization(Object bean, String beanName)
throws BeansException {
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
if (bean instanceof MeterRegistry) {
getConfigurer().configure((MeterRegistry) bean);
}
return bean;
}

@SuppressWarnings("unchecked")
private MeterRegistryConfigurer getConfigurer() {
if (this.configurer == null) {
this.configurer = new MeterRegistryConfigurer(beansOfType(MeterBinder.class),
beansOfType(MeterFilter.class),
(Collection<MeterRegistryCustomizer<?>>) (Object) beansOfType(
MeterRegistryCustomizer.class),
this.context.getBean(MetricsProperties.class).isUseGlobalRegistry());
this.configurer = new MeterRegistryConfigurer(
getOrEmpty(this.meterBinders.getIfAvailable()),
getOrEmpty(this.meterFilters.getIfAvailable()),
getOrEmpty(this.meterRegistryCustomizers.getIfAvailable()),
this.metricsProperties.getObject().isUseGlobalRegistry());
}
return this.configurer;
}

private <T> Collection<T> beansOfType(Class<T> type) {
return this.context.getBeansOfType(type).values();
private <T> List<T> getOrEmpty(List<T> list) {
return list != null ? list : Collections.emptyList();
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -15,15 +15,21 @@
*/
package io.micrometer.spring.autoconfigure;

import java.util.List;

import io.micrometer.core.annotation.Timed;
import io.micrometer.core.instrument.Clock;
import io.micrometer.core.instrument.MeterRegistry;
import io.micrometer.core.instrument.binder.MeterBinder;
import io.micrometer.core.instrument.binder.hystrix.HystrixMetricsBinder;
import io.micrometer.core.instrument.config.MeterFilter;
import io.micrometer.spring.PropertiesMeterFilter;
import io.micrometer.spring.autoconfigure.jersey2.server.JerseyServerMetricsConfiguration;
import io.micrometer.spring.autoconfigure.web.tomcat.TomcatMetricsConfiguration;
import io.micrometer.spring.integration.SpringIntegrationMetrics;
import io.micrometer.spring.scheduling.ScheduledMethodMetrics;

import org.springframework.beans.factory.ObjectProvider;
import org.springframework.boot.autoconfigure.AutoConfigureAfter;
import org.springframework.boot.autoconfigure.AutoConfigureBefore;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
Expand All @@ -35,7 +41,6 @@
import org.springframework.boot.autoconfigure.condition.SearchStrategy;
import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
Expand Down Expand Up @@ -73,14 +78,18 @@ public Clock micrometerClock() {
}

@Bean
public static MeterRegistryPostProcessor meterRegistryPostProcessor(ApplicationContext context) {
return new MeterRegistryPostProcessor(context);
public static MeterRegistryPostProcessor meterRegistryPostProcessor(
ObjectProvider<List<MeterBinder>> meterBinders,
ObjectProvider<List<MeterFilter>> meterFilters,
ObjectProvider<List<MeterRegistryCustomizer<?>>> meterRegistryCustomizers,
ObjectProvider<MetricsProperties> metricsProperties) {
return new MeterRegistryPostProcessor(meterBinders, meterFilters, meterRegistryCustomizers, metricsProperties);
}

@Bean
@Order(0)
public MeterRegistryCustomizer<MeterRegistry> propertyBasedFilter(MetricsProperties props) {
return r -> r.config().meterFilter(new PropertiesMeterFilter(props));
public PropertiesMeterFilter propertiesMeterFilter(MetricsProperties properties) {
return new PropertiesMeterFilter(properties);
}

// If AOP is not enabled, scheduled interception will not work.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,6 @@
import io.micrometer.core.Issue;
import io.micrometer.core.instrument.MeterRegistry;
import io.micrometer.core.instrument.Metrics;
import io.micrometer.spring.autoconfigure.MeterRegistryPostProcessor;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
Expand Down Expand Up @@ -59,7 +58,7 @@ public AsyncTaskExecutor executor(MeterRegistry registry) {

/**
* Injecting the registry instead would cause early evaluation of the registry and the registry
* wouldn't be discovered by {@link MeterRegistryPostProcessor}.
* wouldn't be discovered by {@code MeterRegistryPostProcessor}.
*/
@Configuration
@EnableAsync
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,149 @@
/**
* Copyright 2017 Pivotal Software, Inc.
* <p>
* 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
* <p>
* http://www.apache.org/licenses/LICENSE-2.0
* <p>
* 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 io.micrometer.spring.autoconfigure;

import io.micrometer.core.instrument.MeterRegistry;
import io.micrometer.core.instrument.MockClock;
import io.micrometer.core.instrument.binder.MeterBinder;
import io.micrometer.core.instrument.binder.jvm.ClassLoaderMetrics;
import io.micrometer.core.instrument.binder.jvm.JvmGcMetrics;
import io.micrometer.core.instrument.binder.jvm.JvmThreadMetrics;
import io.micrometer.core.instrument.binder.logging.LogbackMetrics;
import io.micrometer.core.instrument.binder.system.FileDescriptorMetrics;
import io.micrometer.core.instrument.binder.system.ProcessorMetrics;
import io.micrometer.core.instrument.binder.system.UptimeMetrics;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.test.context.SpringBootTest.WebEnvironment;
import org.springframework.boot.test.web.client.TestRestTemplate;
import org.springframework.boot.web.client.RestTemplateBuilder;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Import;
import org.springframework.http.HttpMethod;
import org.springframework.http.MediaType;
import org.springframework.test.context.junit4.SpringRunner;
import org.springframework.test.web.client.MockRestServiceServer;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.RestTemplate;

import static org.assertj.core.api.Assertions.assertThat;
import static org.springframework.test.web.client.ExpectedCount.once;
import static org.springframework.test.web.client.match.MockRestRequestMatchers.method;
import static org.springframework.test.web.client.match.MockRestRequestMatchers.requestTo;
import static org.springframework.test.web.client.response.MockRestResponseCreators.withSuccess;

/**
* Integration tests for {@link MetricsAutoConfiguration}.
*
* @author Jon Schneider
*/
@RunWith(SpringRunner.class)
@SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT, classes = MetricsAutoConfigurationIntegrationTest.MetricsApp.class)
public class MetricsAutoConfigurationIntegrationTest {

@Autowired
private ApplicationContext context;

@Autowired
private RestTemplate external;

@Autowired
private TestRestTemplate loopback;

@Autowired
private MeterRegistry registry;

@SuppressWarnings("unchecked")
@Test
public void restTemplateIsInstrumented() {
MockRestServiceServer server = MockRestServiceServer.bindTo(external).build();
server.expect(once(), requestTo("/api/external"))
.andExpect(method(HttpMethod.GET)).andRespond(withSuccess(
"hello", MediaType.APPLICATION_JSON));

assertThat(external.getForObject("/api/external", String.class)).isEqualTo("hello");

assertThat(registry.get("http.client.requests").timer().count()).isEqualTo(1L);
}

@Test
public void requestMappingIsInstrumented() {
loopback.getForObject("/api/people", String.class);

assertThat(registry.get("http.server.requests").timer().count()).isEqualTo(1L);
}

@Test
public void automaticallyRegisteredBinders() {
assertThat(context.getBeansOfType(MeterBinder.class).values())
.hasAtLeastOneElementOfType(LogbackMetrics.class)
.hasAtLeastOneElementOfType(JvmGcMetrics.class)
.hasAtLeastOneElementOfType(JvmGcMetrics.class)
.hasAtLeastOneElementOfType(JvmThreadMetrics.class)
.hasAtLeastOneElementOfType(ClassLoaderMetrics.class)
.hasAtLeastOneElementOfType(UptimeMetrics.class)
.hasAtLeastOneElementOfType(ProcessorMetrics.class)
.hasAtLeastOneElementOfType(FileDescriptorMetrics.class);
}

@Test
public void registryCustomizersAreAppliedBeforeRegistryIsInjectableElsewhere() {
registry.get("my.thing").tags("common", "tag").gauge();
}

@SpringBootApplication(scanBasePackages = "ignored")
@Import(PersonController.class)
static class MetricsApp {
@Bean
MockClock mockClock() {
return new MockClock();
}

@Bean
public MeterRegistryCustomizer commonTags() {
return r -> r.config().commonTags("common", "tag");
}

@Bean
public MyThing myBinder(MeterRegistry registry) {
// this should have the common tag
registry.gauge("my.thing", 0);
return new MyThing();
}

@Bean
public RestTemplate restTemplate(RestTemplateBuilder restTemplateBuilder) {
return restTemplateBuilder.build();
}

private class MyThing {
}

}

@RestController
static class PersonController {
@GetMapping("/api/people")
String personName() {
return "Jon";
}
}
}
Loading

0 comments on commit 492fa69

Please sign in to comment.