Flexible registration of additional PublicMetrics

This commit permits the use of several PublicMetrics instances by
default. Previously, only one PublicMetrics service could be specified
and a user configuration would remove all the defaulting.

VanillaPublicMetrics now takes a collection of PublicMetrics and
invokes them in sequence to build the final collection of metrics.
The system-related metrics have been moved to SystemPublicMetrics and
are registered by default.

Also updated the documentation to mention this feature and how it
could be fully overridden.

Fixes gh-1094
pull/1253/merge
Stephane Nicoll 11 years ago
parent 99971a6578
commit 2be6b3e419

@ -16,6 +16,7 @@
package org.springframework.boot.actuate.autoconfigure; package org.springframework.boot.actuate.autoconfigure;
import java.util.Collection;
import java.util.HashMap; import java.util.HashMap;
import java.util.LinkedHashMap; import java.util.LinkedHashMap;
import java.util.Map; import java.util.Map;
@ -35,6 +36,7 @@ import org.springframework.boot.actuate.endpoint.MetricsEndpoint;
import org.springframework.boot.actuate.endpoint.PublicMetrics; import org.springframework.boot.actuate.endpoint.PublicMetrics;
import org.springframework.boot.actuate.endpoint.RequestMappingEndpoint; import org.springframework.boot.actuate.endpoint.RequestMappingEndpoint;
import org.springframework.boot.actuate.endpoint.ShutdownEndpoint; import org.springframework.boot.actuate.endpoint.ShutdownEndpoint;
import org.springframework.boot.actuate.endpoint.SystemPublicMetrics;
import org.springframework.boot.actuate.endpoint.TraceEndpoint; import org.springframework.boot.actuate.endpoint.TraceEndpoint;
import org.springframework.boot.actuate.endpoint.VanillaPublicMetrics; import org.springframework.boot.actuate.endpoint.VanillaPublicMetrics;
import org.springframework.boot.actuate.health.HealthAggregator; import org.springframework.boot.actuate.health.HealthAggregator;
@ -68,6 +70,7 @@ import org.springframework.web.servlet.handler.AbstractHandlerMethodMapping;
* @author Phillip Webb * @author Phillip Webb
* @author Greg Turnquist * @author Greg Turnquist
* @author Christian Dupuis * @author Christian Dupuis
* @author Stephane Nicoll
*/ */
@Configuration @Configuration
public class EndpointAutoConfiguration { public class EndpointAutoConfiguration {
@ -85,7 +88,7 @@ public class EndpointAutoConfiguration {
private MetricReader metricRepository = new InMemoryMetricRepository(); private MetricReader metricRepository = new InMemoryMetricRepository();
@Autowired(required = false) @Autowired(required = false)
private PublicMetrics metrics; private Collection<PublicMetrics> allMetrics;
@Autowired(required = false) @Autowired(required = false)
private TraceRepository traceRepository = new InMemoryTraceRepository(); private TraceRepository traceRepository = new InMemoryTraceRepository();
@ -126,10 +129,8 @@ public class EndpointAutoConfiguration {
@Bean @Bean
@ConditionalOnMissingBean @ConditionalOnMissingBean
public MetricsEndpoint metricsEndpoint() { public MetricsEndpoint metricsEndpoint() {
if (this.metrics == null) { PublicMetrics metrics = new VanillaPublicMetrics(this.metricRepository, this.allMetrics);
this.metrics = new VanillaPublicMetrics(this.metricRepository); return new MetricsEndpoint(metrics);
}
return new MetricsEndpoint(this.metrics);
} }
@Bean @Bean
@ -178,6 +179,16 @@ public class EndpointAutoConfiguration {
return endpoint; return endpoint;
} }
@Configuration
protected static class CorePublicMetrics {
@Bean
SystemPublicMetrics systemPublicMetrics() {
return new SystemPublicMetrics();
}
}
@Configuration @Configuration
protected static class InfoPropertiesConfiguration { protected static class InfoPropertiesConfiguration {

@ -25,6 +25,7 @@ import org.springframework.boot.actuate.metrics.Metric;
* *
* @author Dave Syer * @author Dave Syer
* @see VanillaPublicMetrics * @see VanillaPublicMetrics
* @see SystemPublicMetrics
*/ */
public interface PublicMetrics { public interface PublicMetrics {

@ -0,0 +1,136 @@
/*
* 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.lang.management.ClassLoadingMXBean;
import java.lang.management.GarbageCollectorMXBean;
import java.lang.management.ManagementFactory;
import java.lang.management.MemoryUsage;
import java.lang.management.ThreadMXBean;
import java.util.Collection;
import java.util.LinkedHashSet;
import java.util.List;
import org.springframework.boot.actuate.metrics.Metric;
import org.springframework.util.StringUtils;
/**
* A {@link PublicMetrics} implementation that provides various
* system-related metrics.
*
* @author Dave Syer
* @author Stephane Nicoll
* @since 1.2.0
*/
public class SystemPublicMetrics implements PublicMetrics {
private long timestamp;
public SystemPublicMetrics() {
this.timestamp = System.currentTimeMillis();
}
@Override
public Collection<Metric<?>> metrics() {
Collection<Metric<?>> result = new LinkedHashSet<Metric<?>>();
addBasicMetrics(result);
addHeapMetrics(result);
addThreadMetrics(result);
addClassLoadingMetrics(result);
addGarbageCollectionMetrics(result);
return result;
}
/**
* Add basic system metrics.
*/
protected void addBasicMetrics(Collection<Metric<?>> result) {
result.add(new Metric<Long>("mem",
Runtime.getRuntime().totalMemory() / 1024));
result.add(new Metric<Long>("mem.free", Runtime.getRuntime()
.freeMemory() / 1024));
result.add(new Metric<Integer>("processors", Runtime.getRuntime()
.availableProcessors()));
// Add JVM up time in ms
result.add(new Metric<Long>("uptime", ManagementFactory
.getRuntimeMXBean().getUptime()));
result.add(new Metric<Long>("instance.uptime", System.currentTimeMillis()
- this.timestamp));
}
/**
* Add JVM heap metrics.
*/
protected void addHeapMetrics(Collection<Metric<?>> result) {
MemoryUsage memoryUsage = ManagementFactory.getMemoryMXBean()
.getHeapMemoryUsage();
result.add(new Metric<Long>("heap.committed", memoryUsage.getCommitted() / 1024));
result.add(new Metric<Long>("heap.init", memoryUsage.getInit() / 1024));
result.add(new Metric<Long>("heap.used", memoryUsage.getUsed() / 1024));
result.add(new Metric<Long>("heap", memoryUsage.getMax() / 1024));
}
/**
* Add thread metrics.
*/
protected void addThreadMetrics(Collection<Metric<?>> result) {
ThreadMXBean threadMxBean = ManagementFactory.getThreadMXBean();
result.add(new Metric<Long>("threads.peak", (long) threadMxBean
.getPeakThreadCount()));
result.add(new Metric<Long>("threads.daemon", (long) threadMxBean
.getDaemonThreadCount()));
result.add(new Metric<Long>("threads", (long) threadMxBean.getThreadCount()));
}
/**
* Add class loading metrics.
*/
protected void addClassLoadingMetrics(Collection<Metric<?>> result) {
ClassLoadingMXBean classLoadingMxBean = ManagementFactory.getClassLoadingMXBean();
result.add(new Metric<Long>("classes", (long) classLoadingMxBean
.getLoadedClassCount()));
result.add(new Metric<Long>("classes.loaded", classLoadingMxBean
.getTotalLoadedClassCount()));
result.add(new Metric<Long>("classes.unloaded", classLoadingMxBean
.getUnloadedClassCount()));
}
/**
* Add garbage collection metrics.
*/
protected void addGarbageCollectionMetrics(Collection<Metric<?>> result) {
List<GarbageCollectorMXBean> garbageCollectorMxBeans = ManagementFactory
.getGarbageCollectorMXBeans();
for (GarbageCollectorMXBean garbageCollectorMXBean : garbageCollectorMxBeans) {
String name = beautifyGcName(garbageCollectorMXBean.getName());
result.add(new Metric<Long>("gc." + name + ".count", garbageCollectorMXBean.getCollectionCount()));
result.add(new Metric<Long>("gc." + name + ".time", garbageCollectorMXBean.getCollectionTime()));
}
}
/**
* Turn GC names like 'PS Scavenge' or 'PS MarkSweep' into something that is more
* metrics friendly.
*/
private String beautifyGcName(String name) {
return StringUtils.replace(name, " ", "_").toLowerCase();
}
}

@ -16,36 +16,37 @@
package org.springframework.boot.actuate.endpoint; package org.springframework.boot.actuate.endpoint;
import java.lang.management.ClassLoadingMXBean;
import java.lang.management.GarbageCollectorMXBean;
import java.lang.management.ManagementFactory;
import java.lang.management.MemoryUsage;
import java.lang.management.ThreadMXBean;
import java.util.Collection; import java.util.Collection;
import java.util.Collections;
import java.util.LinkedHashSet; import java.util.LinkedHashSet;
import java.util.List;
import org.springframework.boot.actuate.metrics.Metric; import org.springframework.boot.actuate.metrics.Metric;
import org.springframework.boot.actuate.metrics.reader.MetricReader; import org.springframework.boot.actuate.metrics.reader.MetricReader;
import org.springframework.util.Assert; import org.springframework.util.Assert;
import org.springframework.util.StringUtils;
/** /**
* Default implementation of {@link PublicMetrics} that exposes all metrics from a * Default implementation of {@link PublicMetrics} that exposes all metrics from a
* {@link MetricReader} along with memory information. * {@link MetricReader} along with a collection of configurable {@link PublicMetrics}
* instances.
* *
* @author Dave Syer * @author Dave Syer
* @author Christian Dupuis * @author Christian Dupuis
* @author Stephane Nicoll
*/ */
public class VanillaPublicMetrics implements PublicMetrics { public class VanillaPublicMetrics implements PublicMetrics {
private final MetricReader reader; private final MetricReader reader;
private long timestamp; private final Collection<PublicMetrics> publicMetrics;
public VanillaPublicMetrics(MetricReader reader) { public VanillaPublicMetrics(MetricReader reader, Collection<PublicMetrics> publicMetrics) {
Assert.notNull(reader, "MetricReader must not be null"); Assert.notNull(reader, "MetricReader must not be null");
Assert.notNull(publicMetrics, "PublicMetrics must not be null");
this.reader = reader; this.reader = reader;
this.timestamp = System.currentTimeMillis(); this.publicMetrics = publicMetrics;
}
public VanillaPublicMetrics(MetricReader reader) {
this(reader, Collections.<PublicMetrics>emptyList());
} }
@Override @Override
@ -54,92 +55,11 @@ public class VanillaPublicMetrics implements PublicMetrics {
for (Metric<?> metric : this.reader.findAll()) { for (Metric<?> metric : this.reader.findAll()) {
result.add(metric); result.add(metric);
} }
for (PublicMetrics publicMetric : publicMetrics) {
addMetrics(result); result.addAll(publicMetric.metrics());
addHeapMetrics(result);
addThreadMetrics(result);
addClassLoadingMetrics(result);
addGarbageCollecitonMetrics(result);
return result;
}
/**
* Add basic system metrics.
*/
protected void addMetrics(Collection<Metric<?>> result) {
result.add(new Metric<Long>("mem",
new Long(Runtime.getRuntime().totalMemory()) / 1024));
result.add(new Metric<Long>("mem.free", new Long(Runtime.getRuntime()
.freeMemory()) / 1024));
result.add(new Metric<Integer>("processors", Runtime.getRuntime()
.availableProcessors()));
// Add JVM uptime in ms
result.add(new Metric<Long>("uptime", new Long(ManagementFactory
.getRuntimeMXBean().getUptime())));
result.add(new Metric<Long>("instance.uptime", System.currentTimeMillis()
- this.timestamp));
}
/**
* Add JVM heap metrics.
*/
protected void addHeapMetrics(Collection<Metric<?>> result) {
MemoryUsage memoryUsage = ManagementFactory.getMemoryMXBean()
.getHeapMemoryUsage();
result.add(new Metric<Long>("heap.committed", memoryUsage.getCommitted() / 1024));
result.add(new Metric<Long>("heap.init", memoryUsage.getInit() / 1024));
result.add(new Metric<Long>("heap.used", memoryUsage.getUsed() / 1024));
result.add(new Metric<Long>("heap", memoryUsage.getMax() / 1024));
}
/**
* Add thread metrics.
*/
protected void addThreadMetrics(Collection<Metric<?>> result) {
ThreadMXBean threadMxBean = ManagementFactory.getThreadMXBean();
result.add(new Metric<Long>("threads.peak", new Long(threadMxBean
.getPeakThreadCount())));
result.add(new Metric<Long>("threads.daemon", new Long(threadMxBean
.getDaemonThreadCount())));
result.add(new Metric<Long>("threads", new Long(threadMxBean.getThreadCount())));
}
/**
* Add class loading metrics.
*/
protected void addClassLoadingMetrics(Collection<Metric<?>> result) {
ClassLoadingMXBean classLoadingMxBean = ManagementFactory.getClassLoadingMXBean();
result.add(new Metric<Long>("classes", new Long(classLoadingMxBean
.getLoadedClassCount())));
result.add(new Metric<Long>("classes.loaded", new Long(classLoadingMxBean
.getTotalLoadedClassCount())));
result.add(new Metric<Long>("classes.unloaded", new Long(classLoadingMxBean
.getUnloadedClassCount())));
} }
/** return result;
* Add garbage collection metrics.
*/
protected void addGarbageCollecitonMetrics(Collection<Metric<?>> result) {
List<GarbageCollectorMXBean> garbageCollectorMxBeans = ManagementFactory
.getGarbageCollectorMXBeans();
for (int i = 0; i < garbageCollectorMxBeans.size(); i++) {
GarbageCollectorMXBean garbageCollectorMXBean = garbageCollectorMxBeans
.get(i);
String name = beautifyGcName(garbageCollectorMXBean.getName());
result.add(new Metric<Long>("gc." + name + ".count", new Long(
garbageCollectorMXBean.getCollectionCount())));
result.add(new Metric<Long>("gc." + name + ".time", new Long(
garbageCollectorMXBean.getCollectionTime())));
}
} }
/**
* Turn GC names like 'PS Scavenge' or 'PS MarkSweep' into something that is more
* metrics friendly.
*/
private String beautifyGcName(String name) {
return StringUtils.replace(name, " ", "_").toLowerCase();
}
} }

@ -17,7 +17,6 @@
package org.springframework.boot.actuate.autoconfigure; package org.springframework.boot.actuate.autoconfigure;
import org.junit.After; import org.junit.After;
import org.junit.Before;
import org.junit.Test; import org.junit.Test;
import org.springframework.boot.actuate.endpoint.AutoConfigurationReportEndpoint; import org.springframework.boot.actuate.endpoint.AutoConfigurationReportEndpoint;
import org.springframework.boot.actuate.endpoint.BeansEndpoint; import org.springframework.boot.actuate.endpoint.BeansEndpoint;
@ -26,20 +25,28 @@ import org.springframework.boot.actuate.endpoint.EnvironmentEndpoint;
import org.springframework.boot.actuate.endpoint.HealthEndpoint; import org.springframework.boot.actuate.endpoint.HealthEndpoint;
import org.springframework.boot.actuate.endpoint.InfoEndpoint; import org.springframework.boot.actuate.endpoint.InfoEndpoint;
import org.springframework.boot.actuate.endpoint.MetricsEndpoint; import org.springframework.boot.actuate.endpoint.MetricsEndpoint;
import org.springframework.boot.actuate.endpoint.PublicMetrics;
import org.springframework.boot.actuate.endpoint.RequestMappingEndpoint; import org.springframework.boot.actuate.endpoint.RequestMappingEndpoint;
import org.springframework.boot.actuate.endpoint.ShutdownEndpoint; import org.springframework.boot.actuate.endpoint.ShutdownEndpoint;
import org.springframework.boot.actuate.endpoint.TraceEndpoint; import org.springframework.boot.actuate.endpoint.TraceEndpoint;
import org.springframework.boot.actuate.health.Health; import org.springframework.boot.actuate.health.Health;
import org.springframework.boot.actuate.metrics.Metric;
import org.springframework.boot.autoconfigure.condition.ConditionEvaluationReport; import org.springframework.boot.autoconfigure.condition.ConditionEvaluationReport;
import org.springframework.boot.autoconfigure.jdbc.EmbeddedDataSourceConfiguration; import org.springframework.boot.autoconfigure.jdbc.EmbeddedDataSourceConfiguration;
import org.springframework.boot.test.EnvironmentTestUtils; import org.springframework.boot.test.EnvironmentTestUtils;
import org.springframework.context.annotation.AnnotationConfigApplicationContext; import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull; import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue; import static org.junit.Assert.assertTrue;
import java.util.Collection;
import java.util.Collections;
import java.util.Map;
/** /**
* Tests for {@link EndpointAutoConfiguration}. * Tests for {@link EndpointAutoConfiguration}.
* *
@ -47,18 +54,12 @@ import static org.junit.Assert.assertTrue;
* @author Phillip Webb * @author Phillip Webb
* @author Greg Turnquist * @author Greg Turnquist
* @author Christian Dupuis * @author Christian Dupuis
* @author Stephane Nicoll
*/ */
public class EndpointAutoConfigurationTests { public class EndpointAutoConfigurationTests {
private AnnotationConfigApplicationContext context; private AnnotationConfigApplicationContext context;
@Before
public void setup() {
this.context = new AnnotationConfigApplicationContext();
this.context.register(EndpointAutoConfiguration.class);
this.context.refresh();
}
@After @After
public void close() { public void close() {
if (this.context != null) { if (this.context != null) {
@ -68,6 +69,7 @@ public class EndpointAutoConfigurationTests {
@Test @Test
public void endpoints() throws Exception { public void endpoints() throws Exception {
load(EndpointAutoConfiguration.class);
assertNotNull(this.context.getBean(BeansEndpoint.class)); assertNotNull(this.context.getBean(BeansEndpoint.class));
assertNotNull(this.context.getBean(DumpEndpoint.class)); assertNotNull(this.context.getBean(DumpEndpoint.class));
assertNotNull(this.context.getBean(EnvironmentEndpoint.class)); assertNotNull(this.context.getBean(EnvironmentEndpoint.class));
@ -81,10 +83,8 @@ public class EndpointAutoConfigurationTests {
@Test @Test
public void healthEndpoint() { public void healthEndpoint() {
this.context = new AnnotationConfigApplicationContext(); load(EmbeddedDataSourceConfiguration.class,
this.context.register(EmbeddedDataSourceConfiguration.class,
EndpointAutoConfiguration.class, HealthIndicatorAutoConfiguration.class); EndpointAutoConfiguration.class, HealthIndicatorAutoConfiguration.class);
this.context.refresh();
HealthEndpoint bean = this.context.getBean(HealthEndpoint.class); HealthEndpoint bean = this.context.getBean(HealthEndpoint.class);
assertNotNull(bean); assertNotNull(bean);
Health result = bean.invoke(); Health result = bean.invoke();
@ -94,10 +94,8 @@ public class EndpointAutoConfigurationTests {
@Test @Test
public void healthEndpointWithDefaultHealthIndicator() { public void healthEndpointWithDefaultHealthIndicator() {
this.context = new AnnotationConfigApplicationContext(); load(EndpointAutoConfiguration.class,
this.context.register(EndpointAutoConfiguration.class,
HealthIndicatorAutoConfiguration.class); HealthIndicatorAutoConfiguration.class);
this.context.refresh();
HealthEndpoint bean = this.context.getBean(HealthEndpoint.class); HealthEndpoint bean = this.context.getBean(HealthEndpoint.class);
assertNotNull(bean); assertNotNull(bean);
Health result = bean.invoke(); Health result = bean.invoke();
@ -105,11 +103,33 @@ public class EndpointAutoConfigurationTests {
} }
@Test @Test
public void autoconfigurationAuditEndpoints() { public void metricEndpointsHasSystemMetricsByDefault() {
this.context = new AnnotationConfigApplicationContext(); load(EndpointAutoConfiguration.class);
this.context.register(EndpointAutoConfiguration.class, MetricsEndpoint endpoint = this.context.getBean(MetricsEndpoint.class);
Map<String, Object> metrics = endpoint.invoke();
assertTrue(metrics.containsKey("mem"));
assertTrue(metrics.containsKey("heap.used"));
}
@Test
public void metricEndpointCustomPublicMetrics() {
load(CustomPublicMetricsConfig.class, EndpointAutoConfiguration.class);
MetricsEndpoint endpoint = this.context.getBean(MetricsEndpoint.class);
Map<String, Object> metrics = endpoint.invoke();
// Custom metrics
assertTrue(metrics.containsKey("foo"));
// System metrics still available
assertTrue(metrics.containsKey("mem"));
assertTrue(metrics.containsKey("heap.used"));
}
@Test
public void autoConfigurationAuditEndpoints() {
load(EndpointAutoConfiguration.class,
ConditionEvaluationReport.class); ConditionEvaluationReport.class);
this.context.refresh();
assertNotNull(this.context.getBean(AutoConfigurationReportEndpoint.class)); assertNotNull(this.context.getBean(AutoConfigurationReportEndpoint.class));
} }
@ -136,4 +156,25 @@ public class EndpointAutoConfigurationTests {
assertNotNull(endpoint); assertNotNull(endpoint);
assertNull(endpoint.invoke().get("git")); assertNull(endpoint.invoke().get("git"));
} }
private void load(Class<?>... config) {
this.context = new AnnotationConfigApplicationContext();
this.context.register(config);
this.context.refresh();
}
@Configuration
static class CustomPublicMetricsConfig {
@Bean
PublicMetrics customPublicMetrics() {
return new PublicMetrics() {
@Override
public Collection<Metric<?>> metrics() {
return Collections.<Metric<?>>singleton(new Metric<Integer>("foo", 1));
}
};
}
}
} }

@ -0,0 +1,61 @@
/*
* 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 static org.junit.Assert.*;
import java.util.HashMap;
import java.util.Map;
import org.junit.Test;
import org.springframework.boot.actuate.metrics.Metric;
/**
* Tests for {@link SystemPublicMetrics}
*
* @author Stephane Nicoll
*/
public class SystemPublicMetricsTests {
@Test
public void testSystemMetrics() throws Exception {
SystemPublicMetrics publicMetrics = new SystemPublicMetrics();
Map<String, Metric<?>> results = new HashMap<String, Metric<?>>();
for (Metric<?> metric : publicMetrics.metrics()) {
results.put(metric.getName(), metric);
}
assertTrue(results.containsKey("mem"));
assertTrue(results.containsKey("mem.free"));
assertTrue(results.containsKey("processors"));
assertTrue(results.containsKey("uptime"));
assertTrue(results.containsKey("heap.committed"));
assertTrue(results.containsKey("heap.init"));
assertTrue(results.containsKey("heap.used"));
assertTrue(results.containsKey("heap"));
assertTrue(results.containsKey("threads.peak"));
assertTrue(results.containsKey("threads.daemon"));
assertTrue(results.containsKey("threads"));
assertTrue(results.containsKey("classes.loaded"));
assertTrue(results.containsKey("classes.unloaded"));
assertTrue(results.containsKey("classes"));
}
}

@ -16,8 +16,12 @@
package org.springframework.boot.actuate.endpoint; package org.springframework.boot.actuate.endpoint;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Date; import java.util.Date;
import java.util.HashMap; import java.util.HashMap;
import java.util.LinkedHashSet;
import java.util.Map; import java.util.Map;
import org.junit.Test; import org.junit.Test;
@ -25,14 +29,14 @@ import org.springframework.boot.actuate.metrics.Metric;
import org.springframework.boot.actuate.metrics.repository.InMemoryMetricRepository; import org.springframework.boot.actuate.metrics.repository.InMemoryMetricRepository;
import static org.hamcrest.Matchers.equalTo; import static org.hamcrest.Matchers.equalTo;
import static org.junit.Assert.assertThat; import static org.junit.Assert.*;
import static org.junit.Assert.assertTrue;
/** /**
* Tests for {@link VanillaPublicMetrics}. * Tests for {@link VanillaPublicMetrics}.
* *
* @author Phillip Webb * @author Phillip Webb
* @author Christian Dupuis * @author Christian Dupuis
* @author Stephane Nicoll
*/ */
public class VanillaPublicMetricsTests { public class VanillaPublicMetricsTests {
@ -45,36 +49,38 @@ public class VanillaPublicMetricsTests {
for (Metric<?> metric : publicMetrics.metrics()) { for (Metric<?> metric : publicMetrics.metrics()) {
results.put(metric.getName(), metric); results.put(metric.getName(), metric);
} }
assertTrue(results.containsKey("mem"));
assertTrue(results.containsKey("mem.free"));
assertThat(results.get("a").getValue().doubleValue(), equalTo(0.5)); assertThat(results.get("a").getValue().doubleValue(), equalTo(0.5));
} }
@Test @Test
public void testSystemMetrics() throws Exception { public void testAdditionalMetrics() throws Exception {
InMemoryMetricRepository repository = new InMemoryMetricRepository(); InMemoryMetricRepository repository = new InMemoryMetricRepository();
repository.set(new Metric<Double>("a", 0.5, new Date())); Collection<PublicMetrics> allMetrics = new ArrayList<PublicMetrics>();
VanillaPublicMetrics publicMetrics = new VanillaPublicMetrics(repository); allMetrics.add(new ImmutablePublicMetrics(new Metric<Number>("first", 2L)));
allMetrics.add(new ImmutablePublicMetrics(new Metric<Number>("second", 4L)));
VanillaPublicMetrics publicMetrics = new VanillaPublicMetrics(repository, allMetrics);
Map<String, Metric<?>> results = new HashMap<String, Metric<?>>(); Map<String, Metric<?>> results = new HashMap<String, Metric<?>>();
for (Metric<?> metric : publicMetrics.metrics()) { for (Metric<?> metric : publicMetrics.metrics()) {
results.put(metric.getName(), metric); results.put(metric.getName(), metric);
} }
assertTrue(results.containsKey("mem")); assertTrue(results.containsKey("first"));
assertTrue(results.containsKey("mem.free")); assertTrue(results.containsKey("second"));
assertTrue(results.containsKey("processors")); assertEquals(2, results.size());
assertTrue(results.containsKey("uptime")); }
assertTrue(results.containsKey("heap.committed"));
assertTrue(results.containsKey("heap.init"));
assertTrue(results.containsKey("heap.used"));
assertTrue(results.containsKey("heap"));
assertTrue(results.containsKey("threads.peak")); private static class ImmutablePublicMetrics implements PublicMetrics {
assertTrue(results.containsKey("threads.daemon")); private final Collection<Metric<?>> metrics;
assertTrue(results.containsKey("threads"));
assertTrue(results.containsKey("classes.loaded")); private ImmutablePublicMetrics(Metric<?> metrics) {
assertTrue(results.containsKey("classes.unloaded")); this.metrics = new LinkedHashSet<Metric<?>>();
assertTrue(results.containsKey("classes")); this.metrics.addAll(Arrays.asList(metrics));
}
@Override
public Collection<Metric<?>> metrics() {
return metrics;
}
} }
} }

@ -583,8 +583,14 @@ documentation].
== Metrics == Metrics
Spring Boot Actuator includes a metrics service with ``gauge'' and ``counter'' support. Spring Boot Actuator includes a metrics service with ``gauge'' and ``counter'' support.
A ``gauge'' records a single value; and a ``counter'' records a delta (an increment or A ``gauge'' records a single value; and a ``counter'' records a delta (an increment or
decrement). Metrics for all HTTP requests are automatically recorded, so if you hit the decrement). Spring Boot Actuator also provides a
`metrics` endpoint should should see a response similar to this: {sc-spring-boot-actuator}/endpoint/PublicMetrics.{sc-ext}[`PublicMetrics`] interface that
you can implement to expose metrics that you cannot record via one of those two mechanisms. Look
at {sc-spring-boot-actuator}/endpoint/SystemPublicMetrics.{sc-ext}[`SystemPublicMetrics`]
for an example.
Metrics for all HTTP requests are automatically recorded, so if you hit the
`metrics` endpoint you should see a response similar to this:
[source,json,indent=0] [source,json,indent=0]
---- ----
@ -612,9 +618,10 @@ decrement). Metrics for all HTTP requests are automatically recorded, so if you
---- ----
Here we can see basic `memory`, `heap`, `class loading`, `processor` and `thread pool` Here we can see basic `memory`, `heap`, `class loading`, `processor` and `thread pool`
information along with some HTTP metrics. In this instance the `root` (``/'') and `/metrics` information provided by `SystemPublicMetrics` along with some HTTP metrics. In this
URLs have returned `HTTP 200` responses `20` and `3` times respectively. It also appears instance the `root` (``/'') and `/metrics` URLs have returned `HTTP 200` responses `20`
that the `root` URL returned `HTTP 401` (unauthorized) `4` times. and `3` times respectively. It also appears that the `root` URL returned `HTTP 401`
(unauthorized) `4` times.
The `gauge` shows the last response time for a request. So the last request to `root` took The `gauge` shows the last response time for a request. So the last request to `root` took
`2ms` to respond and the last to `/metrics` took `3ms`. `2ms` to respond and the last to `/metrics` took `3ms`.
@ -661,6 +668,13 @@ TIP: You can use any string as a metric name but you should follow guidelines of
store/graphing technology. Some good guidelines for Graphite are available on store/graphing technology. Some good guidelines for Graphite are available on
http://matt.aimonetti.net/posts/2013/06/26/practical-guide-to-graphite-monitoring/[Matt Aimonetti's Blog]. http://matt.aimonetti.net/posts/2013/06/26/practical-guide-to-graphite-monitoring/[Matt Aimonetti's Blog].
[[production-ready-public-metrics]]
=== Adding your own public metrics
To add additional metrics that are computed every time the metrics endpoint is invoked,
simply register additional `PublicMetrics` implementation bean(s). By default, all such
beans are gathered by the endpoint. You can easily change that by defining your own
`MetricsEndpoint`.
[[production-ready-metric-repositories]] [[production-ready-metric-repositories]]

Loading…
Cancel
Save