From 64af3272a16c4859892405642b604e4fa5c8cca4 Mon Sep 17 00:00:00 2001 From: Dave Syer Date: Tue, 12 May 2015 18:01:34 +0100 Subject: [PATCH] Extract metric export into a separate autoconfig class This avoids a potential problems with ordering between Dropwizard and normal repository configuration. A Dropwizard sample has been added to verify the behaviour. --- spring-boot-actuator/pom.xml | 1 - .../MetricExportAutoConfiguration.java | 84 +++++++++++++++++++ .../MetricRepositoryAutoConfiguration.java | 42 ---------- .../main/resources/META-INF/spring.factories | 1 + ...etricRepositoryAutoConfigurationTests.java | 5 +- spring-boot-samples/pom.xml | 1 + .../pom.xml | 48 +++++++++++ .../metrics/dropwizard/HelloWorldService.java | 40 +++++++++ .../metrics/dropwizard/SampleController.java | 63 ++++++++++++++ .../SampleDropwizardMetricsApplication.java | 29 +++++++ .../src/main/resources/application.properties | 1 + ...mpleDropwizardMetricsApplicationTests.java | 57 +++++++++++++ .../SampleRedisExportApplicationTests.java | 3 +- 13 files changed, 329 insertions(+), 46 deletions(-) create mode 100644 spring-boot-actuator/src/main/java/org/springframework/boot/actuate/autoconfigure/MetricExportAutoConfiguration.java create mode 100644 spring-boot-samples/spring-boot-sample-metrics-dropwizard/pom.xml create mode 100644 spring-boot-samples/spring-boot-sample-metrics-dropwizard/src/main/java/sample/metrics/dropwizard/HelloWorldService.java create mode 100644 spring-boot-samples/spring-boot-sample-metrics-dropwizard/src/main/java/sample/metrics/dropwizard/SampleController.java create mode 100644 spring-boot-samples/spring-boot-sample-metrics-dropwizard/src/main/java/sample/metrics/dropwizard/SampleDropwizardMetricsApplication.java create mode 100644 spring-boot-samples/spring-boot-sample-metrics-dropwizard/src/main/resources/application.properties create mode 100644 spring-boot-samples/spring-boot-sample-metrics-dropwizard/src/test/java/sample/metrics/dropwizard/SampleDropwizardMetricsApplicationTests.java diff --git a/spring-boot-actuator/pom.xml b/spring-boot-actuator/pom.xml index 5ef4819a9d..b2df4ffca0 100644 --- a/spring-boot-actuator/pom.xml +++ b/spring-boot-actuator/pom.xml @@ -18,7 +18,6 @@ ${basedir}/.. - 1.8 diff --git a/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/autoconfigure/MetricExportAutoConfiguration.java b/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/autoconfigure/MetricExportAutoConfiguration.java new file mode 100644 index 0000000000..7c6672d7ef --- /dev/null +++ b/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/autoconfigure/MetricExportAutoConfiguration.java @@ -0,0 +1,84 @@ +/* + * Copyright 2012-2015 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; + +import java.util.Collections; +import java.util.HashMap; +import java.util.Map; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.actuate.metrics.export.MetricExportProperties; +import org.springframework.boot.actuate.metrics.export.MetricExporters; +import org.springframework.boot.actuate.metrics.reader.MetricReader; +import org.springframework.boot.actuate.metrics.writer.MetricWriter; +import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; +import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.scheduling.annotation.EnableScheduling; +import org.springframework.scheduling.annotation.SchedulingConfigurer; +import org.springframework.scheduling.config.ScheduledTaskRegistrar; + +/** + * @author Dave Syer + */ +@Configuration +@EnableScheduling +@ConditionalOnProperty(value = "spring.metrics.export.enabled", matchIfMissing = true) +public class MetricExportAutoConfiguration { + + @Autowired(required = false) + private Map writers = Collections.emptyMap(); + + @Autowired + private MetricExportProperties metrics; + + @Autowired(required = false) + @ActuatorMetricRepository + private MetricWriter actuatorMetricRepository; + + @Autowired(required = false) + @ActuatorMetricRepository + private MetricReader reader; + + @Bean + @ConditionalOnMissingBean + public SchedulingConfigurer metricWritersMetricExporter() { + Map writers = new HashMap(); + if (this.reader != null) { + writers.putAll(this.writers); + if (this.actuatorMetricRepository != null + && writers.containsValue(this.actuatorMetricRepository)) { + for (String name : this.writers.keySet()) { + if (writers.get(name).equals(this.actuatorMetricRepository)) { + writers.remove(name); + } + } + } + MetricExporters exporters = new MetricExporters(this.reader, writers, + this.metrics); + return exporters; + } + return new SchedulingConfigurer() { + + @Override + public void configureTasks(ScheduledTaskRegistrar taskRegistrar) { + } + }; + } + +} diff --git a/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/autoconfigure/MetricRepositoryAutoConfiguration.java b/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/autoconfigure/MetricRepositoryAutoConfiguration.java index 8a847bb955..1d1e8d148a 100644 --- a/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/autoconfigure/MetricRepositoryAutoConfiguration.java +++ b/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/autoconfigure/MetricRepositoryAutoConfiguration.java @@ -16,10 +16,6 @@ package org.springframework.boot.actuate.autoconfigure; -import java.util.Collections; -import java.util.HashMap; -import java.util.Map; - import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.actuate.metrics.CounterService; import org.springframework.boot.actuate.metrics.GaugeService; @@ -31,8 +27,6 @@ import org.springframework.boot.actuate.metrics.buffer.GaugeBuffers; import org.springframework.boot.actuate.metrics.export.Exporter; import org.springframework.boot.actuate.metrics.export.MetricCopyExporter; import org.springframework.boot.actuate.metrics.export.MetricExportProperties; -import org.springframework.boot.actuate.metrics.export.MetricExporters; -import org.springframework.boot.actuate.metrics.reader.MetricReader; import org.springframework.boot.actuate.metrics.repository.InMemoryMetricRepository; import org.springframework.boot.actuate.metrics.repository.MetricRepository; import org.springframework.boot.actuate.metrics.writer.DefaultCounterService; @@ -43,12 +37,10 @@ import org.springframework.boot.autoconfigure.condition.ConditionalOnJava; import org.springframework.boot.autoconfigure.condition.ConditionalOnJava.JavaVersion; import org.springframework.boot.autoconfigure.condition.ConditionalOnJava.Range; import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; -import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; import org.springframework.boot.context.properties.EnableConfigurationProperties; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.messaging.MessageChannel; -import org.springframework.scheduling.annotation.EnableScheduling; import com.codahale.metrics.MetricRegistry; @@ -166,38 +158,4 @@ public class MetricRepositoryAutoConfiguration { } - @Configuration - @EnableScheduling - @ConditionalOnProperty(value = "spring.metrics.export.enabled", matchIfMissing = true) - static class DefaultMetricsExporterConfiguration { - - @Autowired(required = false) - private Map writers = Collections.emptyMap(); - - @Autowired - private MetricExportProperties metrics; - - @Autowired(required = false) - @ActuatorMetricRepository - private MetricWriter actuatorMetricRepository; - - @Bean - @ConditionalOnMissingBean - public MetricExporters metricWritersMetricExporter( - @ActuatorMetricRepository MetricReader reader) { - Map writers = new HashMap( - this.writers); - if (this.actuatorMetricRepository != null - && writers.containsValue(this.actuatorMetricRepository)) { - for (String name : this.writers.keySet()) { - if (writers.get(name).equals(this.actuatorMetricRepository)) { - writers.remove(name); - } - } - } - MetricExporters exporters = new MetricExporters(reader, writers, this.metrics); - return exporters; - } - } - } diff --git a/spring-boot-actuator/src/main/resources/META-INF/spring.factories b/spring-boot-actuator/src/main/resources/META-INF/spring.factories index 98b52fd295..fea96720c6 100644 --- a/spring-boot-actuator/src/main/resources/META-INF/spring.factories +++ b/spring-boot-actuator/src/main/resources/META-INF/spring.factories @@ -13,6 +13,7 @@ org.springframework.boot.actuate.autoconfigure.MetricFilterAutoConfiguration,\ org.springframework.boot.actuate.autoconfigure.MetricRepositoryAutoConfiguration,\ org.springframework.boot.actuate.autoconfigure.MetricsDropwizardAutoConfiguration,\ org.springframework.boot.actuate.autoconfigure.MetricsChannelAutoConfiguration,\ +org.springframework.boot.actuate.autoconfigure.MetricExportAutoConfiguration,\ org.springframework.boot.actuate.autoconfigure.PublicMetricsAutoConfiguration,\ org.springframework.boot.actuate.autoconfigure.TraceRepositoryAutoConfiguration,\ org.springframework.boot.actuate.autoconfigure.TraceWebFilterAutoConfiguration diff --git a/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/autoconfigure/MetricRepositoryAutoConfigurationTests.java b/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/autoconfigure/MetricRepositoryAutoConfigurationTests.java index 6c3f348db0..12a1d47aff 100644 --- a/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/autoconfigure/MetricRepositoryAutoConfigurationTests.java +++ b/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/autoconfigure/MetricRepositoryAutoConfigurationTests.java @@ -84,8 +84,10 @@ public class MetricRepositoryAutoConfigurationTests { @Test public void defaultExporterWhenMessageChannelAvailable() throws Exception { this.context = new AnnotationConfigApplicationContext( - MessageChannelConfiguration.class, MetricsChannelAutoConfiguration.class, + MessageChannelConfiguration.class, MetricRepositoryAutoConfiguration.class, + MetricsChannelAutoConfiguration.class, + MetricExportAutoConfiguration.class, PropertyPlaceholderAutoConfiguration.class); MetricExporters exporter = this.context.getBean(MetricExporters.class); assertNotNull(exporter); @@ -95,6 +97,7 @@ public class MetricRepositoryAutoConfigurationTests { public void provideAdditionalWriter() { this.context = new AnnotationConfigApplicationContext(WriterConfig.class, MetricRepositoryAutoConfiguration.class, + MetricExportAutoConfiguration.class, PropertyPlaceholderAutoConfiguration.class); GaugeService gaugeService = this.context.getBean(GaugeService.class); assertNotNull(gaugeService); diff --git a/spring-boot-samples/pom.xml b/spring-boot-samples/pom.xml index 7edc05fae0..277050ddfb 100644 --- a/spring-boot-samples/pom.xml +++ b/spring-boot-samples/pom.xml @@ -52,6 +52,7 @@ spring-boot-sample-jta-bitronix spring-boot-sample-jta-jndi spring-boot-sample-liquibase + spring-boot-sample-metrics-dropwizard spring-boot-sample-metrics-opentsdb spring-boot-sample-metrics-redis spring-boot-sample-parent-context diff --git a/spring-boot-samples/spring-boot-sample-metrics-dropwizard/pom.xml b/spring-boot-samples/spring-boot-sample-metrics-dropwizard/pom.xml new file mode 100644 index 0000000000..e3bfbce0ed --- /dev/null +++ b/spring-boot-samples/spring-boot-sample-metrics-dropwizard/pom.xml @@ -0,0 +1,48 @@ + + + 4.0.0 + + + org.springframework.boot + spring-boot-samples + 1.3.0.BUILD-SNAPSHOT + + spring-boot-sample-metrics-dropwizard + spring-boot-sample-metrics-dropwizard + Spring Boot Metrics Redis Sample + http://projects.spring.io/spring-boot/ + + Pivotal Software, Inc. + http://www.spring.io + + + ${basedir}/../.. + + + + org.springframework.boot + spring-boot-starter-actuator + + + org.springframework.boot + spring-boot-starter-web + + + io.dropwizard.metrics + metrics-core + + + org.springframework.boot + spring-boot-starter-test + test + + + + + + org.springframework.boot + spring-boot-maven-plugin + + + + diff --git a/spring-boot-samples/spring-boot-sample-metrics-dropwizard/src/main/java/sample/metrics/dropwizard/HelloWorldService.java b/spring-boot-samples/spring-boot-sample-metrics-dropwizard/src/main/java/sample/metrics/dropwizard/HelloWorldService.java new file mode 100644 index 0000000000..6f7dd258c7 --- /dev/null +++ b/spring-boot-samples/spring-boot-sample-metrics-dropwizard/src/main/java/sample/metrics/dropwizard/HelloWorldService.java @@ -0,0 +1,40 @@ +/* + * Copyright 2012-2015 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 sample.metrics.dropwizard; + +import org.springframework.boot.context.properties.ConfigurationProperties; +import org.springframework.stereotype.Component; + +@Component +@ConfigurationProperties(prefix = "service", ignoreUnknownFields = false) +public class HelloWorldService { + + private String name = "World"; + + public String getName() { + return this.name; + } + + public void setName(String name) { + this.name = name; + } + + public String getHelloMessage() { + return "Hello " + this.name; + } + +} diff --git a/spring-boot-samples/spring-boot-sample-metrics-dropwizard/src/main/java/sample/metrics/dropwizard/SampleController.java b/spring-boot-samples/spring-boot-sample-metrics-dropwizard/src/main/java/sample/metrics/dropwizard/SampleController.java new file mode 100644 index 0000000000..42ec65fbd2 --- /dev/null +++ b/spring-boot-samples/spring-boot-sample-metrics-dropwizard/src/main/java/sample/metrics/dropwizard/SampleController.java @@ -0,0 +1,63 @@ +/* + * Copyright 2012-2015 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 sample.metrics.dropwizard; + +import java.util.Collections; +import java.util.Map; + +import org.hibernate.validator.constraints.NotBlank; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.actuate.metrics.GaugeService; +import org.springframework.context.annotation.Description; +import org.springframework.stereotype.Controller; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestMethod; +import org.springframework.web.bind.annotation.ResponseBody; + +@Controller +@Description("A controller for handling requests for hello messages") +public class SampleController { + + @Autowired + private HelloWorldService helloWorldService; + + @Autowired + private GaugeService gauges; + + @RequestMapping(value = "/", method = RequestMethod.GET) + @ResponseBody + public Map hello() { + this.gauges.submit("timer.test.value", Math.random() * 1000 + 1000); + return Collections.singletonMap("message", + this.helloWorldService.getHelloMessage()); + } + + protected static class Message { + + @NotBlank(message = "Message value cannot be empty") + private String value; + + public String getValue() { + return this.value; + } + + public void setValue(String value) { + this.value = value; + } + } + +} diff --git a/spring-boot-samples/spring-boot-sample-metrics-dropwizard/src/main/java/sample/metrics/dropwizard/SampleDropwizardMetricsApplication.java b/spring-boot-samples/spring-boot-sample-metrics-dropwizard/src/main/java/sample/metrics/dropwizard/SampleDropwizardMetricsApplication.java new file mode 100644 index 0000000000..ff902cf54c --- /dev/null +++ b/spring-boot-samples/spring-boot-sample-metrics-dropwizard/src/main/java/sample/metrics/dropwizard/SampleDropwizardMetricsApplication.java @@ -0,0 +1,29 @@ +/* + * Copyright 2012-2015 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 sample.metrics.dropwizard; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; + +@SpringBootApplication +public class SampleDropwizardMetricsApplication { + + public static void main(String[] args) throws Exception { + SpringApplication.run(SampleDropwizardMetricsApplication.class, args); + } + +} diff --git a/spring-boot-samples/spring-boot-sample-metrics-dropwizard/src/main/resources/application.properties b/spring-boot-samples/spring-boot-sample-metrics-dropwizard/src/main/resources/application.properties new file mode 100644 index 0000000000..4107192475 --- /dev/null +++ b/spring-boot-samples/spring-boot-sample-metrics-dropwizard/src/main/resources/application.properties @@ -0,0 +1 @@ +service.name: Phil diff --git a/spring-boot-samples/spring-boot-sample-metrics-dropwizard/src/test/java/sample/metrics/dropwizard/SampleDropwizardMetricsApplicationTests.java b/spring-boot-samples/spring-boot-sample-metrics-dropwizard/src/test/java/sample/metrics/dropwizard/SampleDropwizardMetricsApplicationTests.java new file mode 100644 index 0000000000..ec6f70a2c0 --- /dev/null +++ b/spring-boot-samples/spring-boot-sample-metrics-dropwizard/src/test/java/sample/metrics/dropwizard/SampleDropwizardMetricsApplicationTests.java @@ -0,0 +1,57 @@ +/* + * Copyright 2012-2015 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 sample.metrics.dropwizard; + +import org.junit.Test; +import org.junit.runner.RunWith; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.actuate.metrics.GaugeService; +import org.springframework.boot.test.IntegrationTest; +import org.springframework.boot.test.SpringApplicationConfiguration; +import org.springframework.test.annotation.DirtiesContext; +import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; +import org.springframework.test.context.web.WebAppConfiguration; + +import com.codahale.metrics.MetricRegistry; + +import static org.junit.Assert.assertEquals; + +/** + * Basic integration tests for {@link SampleDropwizardMetricsApplication}. + * + * @author Dave Syer + */ +@RunWith(SpringJUnit4ClassRunner.class) +@SpringApplicationConfiguration(classes = SampleDropwizardMetricsApplication.class) +@WebAppConfiguration +@IntegrationTest("server.port=0") +@DirtiesContext +public class SampleDropwizardMetricsApplicationTests { + + @Autowired + private MetricRegistry registry; + + @Autowired + private GaugeService gauges; + + @Test + public void timerCreated() { + this.gauges.submit("timer.test", 1234); + assertEquals(1, this.registry.getTimers().get("timer.test").getCount()); + } + +} diff --git a/spring-boot-samples/spring-boot-sample-metrics-redis/src/test/java/sample/metrics/redis/SampleRedisExportApplicationTests.java b/spring-boot-samples/spring-boot-sample-metrics-redis/src/test/java/sample/metrics/redis/SampleRedisExportApplicationTests.java index 9009dc390a..65180f4c44 100644 --- a/spring-boot-samples/spring-boot-sample-metrics-redis/src/test/java/sample/metrics/redis/SampleRedisExportApplicationTests.java +++ b/spring-boot-samples/spring-boot-sample-metrics-redis/src/test/java/sample/metrics/redis/SampleRedisExportApplicationTests.java @@ -32,13 +32,12 @@ import org.springframework.test.context.web.WebAppConfiguration; @RunWith(SpringJUnit4ClassRunner.class) @SpringApplicationConfiguration(classes = SampleRedisExportApplication.class) @WebAppConfiguration -@IntegrationTest("server.port=0") +@IntegrationTest({ "server.port=0", "spring.jmx.enabled=true" }) @DirtiesContext public class SampleRedisExportApplicationTests { @Test public void contextLoads() { - } }