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 be86563129..041386d047 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 @@ -21,11 +21,14 @@ import java.util.concurrent.Executor; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Qualifier; +import org.springframework.boot.actuate.endpoint.PublicMetrics; +import org.springframework.boot.actuate.endpoint.RichGaugeReaderPublicMetrics; import org.springframework.boot.actuate.metrics.CounterService; import org.springframework.boot.actuate.metrics.GaugeService; import org.springframework.boot.actuate.metrics.export.Exporter; import org.springframework.boot.actuate.metrics.repository.InMemoryMetricRepository; import org.springframework.boot.actuate.metrics.repository.MetricRepository; +import org.springframework.boot.actuate.metrics.rich.RichGaugeReader; import org.springframework.boot.actuate.metrics.writer.CodahaleMetricWriter; import org.springframework.boot.actuate.metrics.writer.CompositeMetricWriter; import org.springframework.boot.actuate.metrics.writer.DefaultCounterService; @@ -34,6 +37,7 @@ import org.springframework.boot.actuate.metrics.writer.MessageChannelMetricWrite import org.springframework.boot.actuate.metrics.writer.MetricWriter; import org.springframework.boot.actuate.metrics.writer.MetricWriterMessageHandler; import org.springframework.boot.autoconfigure.EnableAutoConfiguration; +import org.springframework.boot.autoconfigure.condition.ConditionalOnBean; import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingClass; @@ -69,7 +73,8 @@ import com.codahale.metrics.MetricRegistry; * In addition if Codahale's metrics library is on the classpath a {@link MetricRegistry} * will be created and wired up to the counter and gauge services in addition to the basic * repository. Users can create Codahale metrics by prefixing their metric names with the - * appropriate type (e.g. "histogram.*", "meter.*"). + * appropriate type (e.g. "histogram.*", "meter.*") and sending them to the standard + * GaugeService or CounterService. *

*

* By default all metric updates go to all {@link MetricWriter} instances in the @@ -117,6 +122,12 @@ public class MetricRepositoryAutoConfiguration { } + @Bean + @ConditionalOnBean(RichGaugeReader.class) + public PublicMetrics richGaugePublicMetrics(RichGaugeReader richGaugeReader) { + return new RichGaugeReaderPublicMetrics(richGaugeReader); + } + @Configuration @ConditionalOnClass(MessageChannel.class) static class MetricsChannelConfiguration { diff --git a/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/endpoint/RichGaugeReaderPublicMetrics.java b/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/endpoint/RichGaugeReaderPublicMetrics.java new file mode 100644 index 0000000000..a644e2926d --- /dev/null +++ b/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/endpoint/RichGaugeReaderPublicMetrics.java @@ -0,0 +1,67 @@ +/* + * Copyright 2012-2014 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.endpoint; + +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.rich.RichGauge; +import org.springframework.boot.actuate.metrics.rich.RichGaugeReader; +import org.springframework.util.Assert; + +/** + * {@link PublicMetrics} exposed from a {@link RichGaugeReader}. + * + * @author Johannes Stelzer + * @since 1.2 + */ +public class RichGaugeReaderPublicMetrics implements PublicMetrics { + + private final RichGaugeReader richGaugeReader; + + public RichGaugeReaderPublicMetrics(RichGaugeReader richGaugeReader) { + Assert.notNull(richGaugeReader, "RichGaugeReader must not be null"); + this.richGaugeReader = richGaugeReader; + } + + @Override + public Collection> metrics() { + List> result = new ArrayList>(); + for (RichGauge richGauge : this.richGaugeReader.findAll()) { + result.addAll(convert(richGauge)); + } + return result; + } + + private List> convert(RichGauge richGauge) { + List> result = new ArrayList>(6); + + result.add(new Metric(richGauge.getName() + RichGauge.AVG, richGauge + .getAverage())); + result.add(new Metric(richGauge.getName() + RichGauge.VAL, richGauge.getValue())); + result.add(new Metric(richGauge.getName() + RichGauge.MIN, richGauge.getMin())); + result.add(new Metric(richGauge.getName() + RichGauge.MAX, richGauge.getMax())); + result.add(new Metric(richGauge.getName() + RichGauge.ALPHA, richGauge + .getAlpha())); + result.add(new Metric(richGauge.getName() + RichGauge.COUNT, richGauge.getCount())); + + return result; + } + +} diff --git a/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/metrics/rich/MultiMetricRichGaugeReader.java b/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/metrics/rich/MultiMetricRichGaugeReader.java index 047d5fc2c3..40d4dbf19c 100644 --- a/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/metrics/rich/MultiMetricRichGaugeReader.java +++ b/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/metrics/rich/MultiMetricRichGaugeReader.java @@ -33,13 +33,6 @@ import org.springframework.boot.actuate.metrics.repository.MultiMetricRepository */ public class MultiMetricRichGaugeReader implements RichGaugeReader { - private static final String COUNT = ".count"; - private static final String MAX = ".max"; - private static final String MIN = ".min"; - private static final String AVG = ".avg"; - private static final String ALPHA = ".alpha"; - private static final String VAL = ".val"; - private final MultiMetricRepository repository; public MultiMetricRichGaugeReader(MultiMetricRepository repository) { @@ -56,22 +49,22 @@ public class MultiMetricRichGaugeReader implements RichGaugeReader { double max = 0.; long count = 0; for (Metric metric : metrics) { - if (metric.getName().endsWith(VAL)) { + if (metric.getName().endsWith(RichGauge.VAL)) { value = metric.getValue().doubleValue(); } - else if (metric.getName().endsWith(ALPHA)) { + else if (metric.getName().endsWith(RichGauge.ALPHA)) { alpha = metric.getValue().doubleValue(); } - else if (metric.getName().endsWith(AVG)) { + else if (metric.getName().endsWith(RichGauge.AVG)) { average = metric.getValue().doubleValue(); } - else if (metric.getName().endsWith(MIN)) { + else if (metric.getName().endsWith(RichGauge.MIN)) { min = metric.getValue().doubleValue(); } - else if (metric.getName().endsWith(MAX)) { + else if (metric.getName().endsWith(RichGauge.MAX)) { max = metric.getValue().doubleValue(); } - else if (metric.getName().endsWith(COUNT)) { + else if (metric.getName().endsWith(RichGauge.COUNT)) { count = metric.getValue().longValue(); } } diff --git a/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/metrics/rich/RichGauge.java b/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/metrics/rich/RichGauge.java index 6ba418e332..c5677fd8e3 100644 --- a/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/metrics/rich/RichGauge.java +++ b/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/metrics/rich/RichGauge.java @@ -31,6 +31,13 @@ import org.springframework.util.Assert; */ public final class RichGauge { + public static final String COUNT = ".count"; + public static final String MAX = ".max"; + public static final String MIN = ".min"; + public static final String AVG = ".avg"; + public static final String ALPHA = ".alpha"; + public static final String VAL = ".val"; + private final String name; private double value; 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 4cba627a57..59c848f376 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 @@ -16,13 +16,18 @@ package org.springframework.boot.actuate.autoconfigure; +import java.util.Collection; +import java.util.Collections; import java.util.concurrent.Executor; import org.junit.Test; +import org.springframework.boot.actuate.endpoint.RichGaugeReaderPublicMetrics; import org.springframework.boot.actuate.metrics.CounterService; import org.springframework.boot.actuate.metrics.GaugeService; import org.springframework.boot.actuate.metrics.Metric; import org.springframework.boot.actuate.metrics.reader.MetricReader; +import org.springframework.boot.actuate.metrics.rich.RichGauge; +import org.springframework.boot.actuate.metrics.rich.RichGaugeReader; import org.springframework.boot.actuate.metrics.writer.DefaultCounterService; import org.springframework.boot.actuate.metrics.writer.DefaultGaugeService; import org.springframework.boot.actuate.metrics.writer.MetricWriter; @@ -41,9 +46,11 @@ import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertThat; import static org.junit.Assert.assertTrue; +import static org.junit.Assert.fail; import static org.mockito.Matchers.any; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; /** * Tests for {@link MetricRepositoryAutoConfiguration}. @@ -115,6 +122,44 @@ public class MetricRepositoryAutoConfigurationTests { context.close(); } + @Test + public void richGaugePublicMetrics() { + AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext( + RichGaugeReaderConfig.class, MetricRepositoryAutoConfiguration.class); + + RichGaugeReader richGaugeReader = context.getBean(RichGaugeReader.class); + assertNotNull(richGaugeReader); + when(richGaugeReader.findAll()).thenReturn( + Collections.singletonList(new RichGauge("bar", 3.7d))); + + RichGaugeReaderPublicMetrics publicMetrics = context + .getBean(RichGaugeReaderPublicMetrics.class); + assertNotNull(publicMetrics); + + Collection> metrics = publicMetrics.metrics(); + assertNotNull(metrics); + assertEquals(metrics.size(), 6); + + assertHasMetric(metrics, new Metric("bar.val", 3.7d)); + assertHasMetric(metrics, new Metric("bar.avg", 3.7d)); + assertHasMetric(metrics, new Metric("bar.min", 3.7d)); + assertHasMetric(metrics, new Metric("bar.max", 3.7d)); + assertHasMetric(metrics, new Metric("bar.alpha", -1.d)); + assertHasMetric(metrics, new Metric("bar.count", 1L)); + + context.close(); + } + + private void assertHasMetric(Collection> metrics, Metric metric) { + for (Metric m : metrics) { + if (m.getValue().equals(metric.getValue()) + && m.getName().equals(metric.getName())) { + return; + } + } + fail("Metric " + metric.toString() + " not found in " + metrics.toString()); + } + @Configuration public static class SyncTaskExecutorConfiguration { @@ -149,4 +194,12 @@ public class MetricRepositoryAutoConfigurationTests { } } + + @Configuration + static class RichGaugeReaderConfig { + @Bean + public RichGaugeReader richGaugeReader() { + return mock(RichGaugeReader.class); + } + } } diff --git a/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/endpoint/RichGaugeReaderPublicMetricsTests.java b/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/endpoint/RichGaugeReaderPublicMetricsTests.java new file mode 100644 index 0000000000..d21bdd3bbf --- /dev/null +++ b/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/endpoint/RichGaugeReaderPublicMetricsTests.java @@ -0,0 +1,71 @@ +/* + * Copyright 2012-2014 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.endpoint; + +import java.util.Date; +import java.util.HashMap; +import java.util.Map; + +import org.junit.Test; +import org.springframework.boot.actuate.metrics.Metric; +import org.springframework.boot.actuate.metrics.rich.InMemoryRichGaugeRepository; + +import static org.hamcrest.Matchers.equalTo; +import static org.junit.Assert.assertThat; +import static org.junit.Assert.assertTrue; + +/** + * Tests for {@link RichGaugeReaderPublicMetrics}. + * + * @author Johannes Stelzer + */ +public class RichGaugeReaderPublicMetricsTests { + + @Test + public void testMetrics() throws Exception { + InMemoryRichGaugeRepository repository = new InMemoryRichGaugeRepository(); + + repository.set(new Metric("a", 0.d, new Date())); + repository.set(new Metric("a", 0.5d, new Date())); + + RichGaugeReaderPublicMetrics metrics = new RichGaugeReaderPublicMetrics( + repository); + + Map> results = new HashMap>(); + for (Metric metric : metrics.metrics()) { + results.put(metric.getName(), metric); + } + assertTrue(results.containsKey("a.val")); + assertThat(results.get("a.val").getValue().doubleValue(), equalTo(0.5d)); + + assertTrue(results.containsKey("a.avg")); + assertThat(results.get("a.avg").getValue().doubleValue(), equalTo(0.25d)); + + assertTrue(results.containsKey("a.min")); + assertThat(results.get("a.min").getValue().doubleValue(), equalTo(0.0d)); + + assertTrue(results.containsKey("a.max")); + assertThat(results.get("a.max").getValue().doubleValue(), equalTo(0.5d)); + + assertTrue(results.containsKey("a.count")); + assertThat(results.get("a.count").getValue().longValue(), equalTo(2L)); + + assertTrue(results.containsKey("a.alpha")); + assertThat(results.get("a.alpha").getValue().doubleValue(), equalTo(-1.d)); + } + +}