diff --git a/spring-boot-actuator/pom.xml b/spring-boot-actuator/pom.xml index b2df4ffca0b..f81e2485e34 100644 --- a/spring-boot-actuator/pom.xml +++ b/spring-boot-actuator/pom.xml @@ -127,6 +127,16 @@ spring-data-solr true + + org.springframework.integration + spring-integration-jmx + true + + + org.springframework.integration + spring-integration-core + true + org.elasticsearch elasticsearch diff --git a/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/autoconfigure/ActuatorMetricRepository.java b/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/autoconfigure/ActuatorMetricReader.java similarity index 96% rename from spring-boot-actuator/src/main/java/org/springframework/boot/actuate/autoconfigure/ActuatorMetricRepository.java rename to spring-boot-actuator/src/main/java/org/springframework/boot/actuate/autoconfigure/ActuatorMetricReader.java index d18bcf68a51..1618aef333c 100644 --- a/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/autoconfigure/ActuatorMetricRepository.java +++ b/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/autoconfigure/ActuatorMetricReader.java @@ -37,6 +37,6 @@ import org.springframework.beans.factory.annotation.Qualifier; @Retention(RetentionPolicy.RUNTIME) @Inherited @Documented -public @interface ActuatorMetricRepository { +public @interface ActuatorMetricReader { } diff --git a/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/autoconfigure/ActuatorMetricWriter.java b/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/autoconfigure/ActuatorMetricWriter.java new file mode 100644 index 00000000000..61b3d26ddc2 --- /dev/null +++ b/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/autoconfigure/ActuatorMetricWriter.java @@ -0,0 +1,42 @@ +/* + * 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.lang.annotation.Documented; +import java.lang.annotation.ElementType; +import java.lang.annotation.Inherited; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +import org.springframework.beans.factory.annotation.Qualifier; + +/** + * Qualifier annotation for a metric repository that is used by the actuator (to + * distinguish it from others that might be installed by the user). + * + * @author Dave Syer + */ +@Qualifier +@Target({ ElementType.FIELD, ElementType.METHOD, ElementType.PARAMETER, ElementType.TYPE, + ElementType.ANNOTATION_TYPE }) +@Retention(RetentionPolicy.RUNTIME) +@Inherited +@Documented +public @interface ActuatorMetricWriter { + +} 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 index 7c6672d7ef0..20b6db54252 100644 --- 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 @@ -18,6 +18,7 @@ package org.springframework.boot.actuate.autoconfigure; import java.util.Collections; import java.util.HashMap; +import java.util.List; import java.util.Map; import org.springframework.beans.factory.annotation.Autowired; @@ -48,11 +49,11 @@ public class MetricExportAutoConfiguration { private MetricExportProperties metrics; @Autowired(required = false) - @ActuatorMetricRepository - private MetricWriter actuatorMetricRepository; + @ActuatorMetricWriter + private List actuatorMetrics = Collections.emptyList(); @Autowired(required = false) - @ActuatorMetricRepository + @ActuatorMetricReader private MetricReader reader; @Bean @@ -61,12 +62,9 @@ public class MetricExportAutoConfiguration { 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); - } + for (String name : this.writers.keySet()) { + if (this.actuatorMetrics.contains(writers.get(name))) { + writers.remove(name); } } MetricExporters exporters = new MetricExporters(this.reader, writers, @@ -80,5 +78,4 @@ public class MetricExportAutoConfiguration { } }; } - } 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 1d1e8d148aa..c68f13482f8 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 @@ -90,7 +90,7 @@ public class MetricRepositoryAutoConfiguration { static class LegacyMetricServicesConfiguration { @Autowired - @ActuatorMetricRepository + @ActuatorMetricReader private MetricWriter writer; @Bean @@ -125,7 +125,7 @@ public class MetricRepositoryAutoConfiguration { } @Bean - @ActuatorMetricRepository + @ActuatorMetricReader @ConditionalOnMissingBean public BufferMetricReader actuatorMetricReader(CounterBuffers counters, GaugeBuffers gauges) { @@ -151,7 +151,7 @@ public class MetricRepositoryAutoConfiguration { static class LegacyMetricRepositoryConfiguration { @Bean - @ActuatorMetricRepository + @ActuatorMetricReader public InMemoryMetricRepository actuatorMetricRepository() { return new InMemoryMetricRepository(); } diff --git a/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/autoconfigure/PublicMetricsAutoConfiguration.java b/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/autoconfigure/PublicMetricsAutoConfiguration.java index 4580e197c58..b3c0b8a3ba1 100644 --- a/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/autoconfigure/PublicMetricsAutoConfiguration.java +++ b/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/autoconfigure/PublicMetricsAutoConfiguration.java @@ -16,6 +16,9 @@ package org.springframework.boot.actuate.autoconfigure; +import java.util.Collections; +import java.util.List; + import javax.servlet.Servlet; import javax.sql.DataSource; @@ -29,8 +32,9 @@ import org.springframework.boot.actuate.endpoint.PublicMetrics; import org.springframework.boot.actuate.endpoint.RichGaugeReaderPublicMetrics; import org.springframework.boot.actuate.endpoint.SystemPublicMetrics; import org.springframework.boot.actuate.endpoint.TomcatPublicMetrics; +import org.springframework.boot.actuate.metrics.integration.SpringIntegrationMetricReader; +import org.springframework.boot.actuate.metrics.reader.CompositeMetricReader; import org.springframework.boot.actuate.metrics.reader.MetricReader; -import org.springframework.boot.actuate.metrics.repository.InMemoryMetricRepository; import org.springframework.boot.actuate.metrics.rich.RichGaugeReader; import org.springframework.boot.autoconfigure.AutoConfigureAfter; import org.springframework.boot.autoconfigure.AutoConfigureBefore; @@ -38,12 +42,17 @@ import org.springframework.boot.autoconfigure.EnableAutoConfiguration; import org.springframework.boot.autoconfigure.cache.CacheAutoConfiguration; import org.springframework.boot.autoconfigure.condition.ConditionalOnBean; import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; +import org.springframework.boot.autoconfigure.condition.ConditionalOnJava; +import org.springframework.boot.autoconfigure.condition.ConditionalOnJava.JavaVersion; import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; +import org.springframework.boot.autoconfigure.integration.IntegrationAutoConfiguration; import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration; import org.springframework.boot.autoconfigure.jdbc.metadata.DataSourcePoolMetadataProvider; import org.springframework.cache.CacheManager; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; +import org.springframework.integration.monitor.IntegrationMBeanExporter; +import org.springframework.lang.UsesJava7; /** * {@link EnableAutoConfiguration Auto-configuration} for {@link PublicMetrics}. @@ -56,12 +65,13 @@ import org.springframework.context.annotation.Configuration; @Configuration @AutoConfigureBefore(EndpointAutoConfiguration.class) @AutoConfigureAfter({ DataSourceAutoConfiguration.class, CacheAutoConfiguration.class, - MetricRepositoryAutoConfiguration.class, CacheStatisticsAutoConfiguration.class }) + MetricRepositoryAutoConfiguration.class, CacheStatisticsAutoConfiguration.class, + IntegrationAutoConfiguration.class }) public class PublicMetricsAutoConfiguration { @Autowired(required = false) - @ActuatorMetricRepository - private MetricReader metricReader = new InMemoryMetricRepository(); + @ActuatorMetricReader + private List metricReaders = Collections.emptyList(); @Bean public SystemPublicMetrics systemPublicMetrics() { @@ -70,7 +80,7 @@ public class PublicMetricsAutoConfiguration { @Bean public MetricReaderPublicMetrics metricReaderPublicMetrics() { - return new MetricReaderPublicMetrics(this.metricReader); + return new MetricReaderPublicMetrics(new CompositeMetricReader(this.metricReaders.toArray(new MetricReader[0]))); } @Bean @@ -120,4 +130,21 @@ public class PublicMetricsAutoConfiguration { } + @Configuration + @ConditionalOnClass(IntegrationMBeanExporter.class) + @ConditionalOnBean(IntegrationMBeanExporter.class) + @ConditionalOnJava(JavaVersion.SEVEN) + @UsesJava7 + static class IntegrationMetricsConfiguration { + + @Bean + @ConditionalOnMissingBean + public MetricReaderPublicMetrics springIntegrationPublicMetrics( + IntegrationMBeanExporter exporter) { + return new MetricReaderPublicMetrics(new SpringIntegrationMetricReader( + exporter)); + } + + } + } diff --git a/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/metrics/integration/SpringIntegrationMetricReader.java b/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/metrics/integration/SpringIntegrationMetricReader.java new file mode 100644 index 00000000000..407e89c6b59 --- /dev/null +++ b/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/metrics/integration/SpringIntegrationMetricReader.java @@ -0,0 +1,81 @@ +/* + * Copyright 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.metrics.integration; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; + +import org.springframework.boot.actuate.metrics.Metric; +import org.springframework.boot.actuate.metrics.reader.MetricReader; +import org.springframework.integration.monitor.IntegrationMBeanExporter; +import org.springframework.integration.support.management.Statistics; +import org.springframework.lang.UsesJava7; + +/** + * A {@link MetricReader} for Spring Integration metrics (as provided by spring-integration-jmx). + * + * @author Dave Syer + * + */ +@UsesJava7 +public class SpringIntegrationMetricReader implements MetricReader { + + private final IntegrationMBeanExporter exporter; + + public SpringIntegrationMetricReader(IntegrationMBeanExporter exporter) { + this.exporter = exporter; + } + + @Override + public Metric findOne(String metricName) { + return null; + } + + @Override + public Iterable> findAll() { + List> metrics = new ArrayList>(); + for (String name : exporter.getChannelNames()) { + metrics.addAll(getStatistics("integration.channel." + name + ".errorRate", exporter.getChannelErrorRate(name))); + metrics.addAll(getStatistics("integration.channel." + name + ".sendRate", exporter.getChannelSendRate(name))); + metrics.add(new Metric("integration.channel." + name + ".receiveCount", exporter.getChannelReceiveCountLong(name))); + } + for (String name : exporter.getHandlerNames()) { + metrics.addAll(getStatistics("integration.handler." + name + ".duration", exporter.getHandlerDuration(name))); + } + metrics.add(new Metric("integration.activeHandlerCount", exporter.getActiveHandlerCountLong())); + metrics.add(new Metric("integration.handlerCount", exporter.getHandlerCount())); + metrics.add(new Metric("integration.channelCount", exporter.getChannelCount())); + metrics.add(new Metric("integration.queuedMessageCount", exporter.getQueuedMessageCount())); + return metrics; + } + + private Collection> getStatistics(String name, Statistics statistic) { + List> metrics = new ArrayList>(); + metrics.add(new Metric(name + ".mean", statistic.getMean())); + metrics.add(new Metric(name + ".max", statistic.getMax())); + metrics.add(new Metric(name + ".min", statistic.getMin())); + metrics.add(new Metric(name + ".stdev", statistic.getStandardDeviation())); + metrics.add(new Metric(name + ".count", statistic.getCountLong())); + return metrics; + } + + @Override + public long count() { + return exporter.getChannelCount()*11 + exporter.getHandlerCount()*5 + 4; + } + +} diff --git a/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/metrics/integration/SpringIntegrationMetricReaderTests.java b/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/metrics/integration/SpringIntegrationMetricReaderTests.java new file mode 100644 index 00000000000..b14c5468f72 --- /dev/null +++ b/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/metrics/integration/SpringIntegrationMetricReaderTests.java @@ -0,0 +1,43 @@ +package org.springframework.boot.actuate.metrics.integration; + +import static org.junit.Assert.assertTrue; + +import org.junit.Test; +import org.junit.runner.RunWith; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.actuate.metrics.integration.SpringIntegrationMetricReaderTests.TestConfiguration; +import org.springframework.boot.autoconfigure.integration.IntegrationAutoConfiguration; +import org.springframework.boot.autoconfigure.jmx.JmxAutoConfiguration; +import org.springframework.boot.test.IntegrationTest; +import org.springframework.boot.test.SpringApplicationConfiguration; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.Import; +import org.springframework.integration.monitor.IntegrationMBeanExporter; +import org.springframework.test.annotation.DirtiesContext; +import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; + +@RunWith(SpringJUnit4ClassRunner.class) +@SpringApplicationConfiguration(classes=TestConfiguration.class) +@IntegrationTest("spring.jmx.enabled=true") +@DirtiesContext +public class SpringIntegrationMetricReaderTests { + + @Autowired + private SpringIntegrationMetricReader reader; + + @Test + public void test() { + assertTrue(reader.count()>0); + } + + @Configuration + @Import({JmxAutoConfiguration.class, IntegrationAutoConfiguration.class}) + protected static class TestConfiguration { + @Bean + public SpringIntegrationMetricReader reader(IntegrationMBeanExporter exporter) { + return new SpringIntegrationMetricReader(exporter); + } + } + +}