diff --git a/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/autoconfigure/ExportMetricReader.java b/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/autoconfigure/ExportMetricReader.java new file mode 100644 index 00000000000..6ddca1b12fb --- /dev/null +++ b/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/autoconfigure/ExportMetricReader.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 reader that can be exported (to distinguish it from + * others that might be installed by the user for other purposes). + * + * @author Dave Syer + */ +@Qualifier +@Target({ ElementType.FIELD, ElementType.METHOD, ElementType.PARAMETER, ElementType.TYPE, + ElementType.ANNOTATION_TYPE }) +@Retention(RetentionPolicy.RUNTIME) +@Inherited +@Documented +public @interface ExportMetricReader { + +} diff --git a/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/autoconfigure/ActuatorMetricReader.java b/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/autoconfigure/ExportMetricWriter.java similarity index 86% rename from spring-boot-actuator/src/main/java/org/springframework/boot/actuate/autoconfigure/ActuatorMetricReader.java rename to spring-boot-actuator/src/main/java/org/springframework/boot/actuate/autoconfigure/ExportMetricWriter.java index 1618aef333c..0f1ee64d0bd 100644 --- a/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/autoconfigure/ActuatorMetricReader.java +++ b/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/autoconfigure/ExportMetricWriter.java @@ -26,8 +26,8 @@ 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). + * Qualifier annotation for a metric repository that is to be used to export metrics from + * the {@link ExportMetricReader} readers. * * @author Dave Syer */ @@ -37,6 +37,6 @@ import org.springframework.beans.factory.annotation.Qualifier; @Retention(RetentionPolicy.RUNTIME) @Inherited @Documented -public @interface ActuatorMetricReader { +public @interface ExportMetricWriter { } 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 43dc3218b81..427fa20b542 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 @@ -47,17 +47,14 @@ import org.springframework.scheduling.config.ScheduledTaskRegistrar; public class MetricExportAutoConfiguration { @Autowired(required = false) + @ExportMetricWriter private Map writers = Collections.emptyMap(); @Autowired private MetricExportProperties metrics; @Autowired(required = false) - @ActuatorMetricWriter - private List actuatorMetrics = Collections.emptyList(); - - @Autowired(required = false) - @ActuatorMetricReader + @ExportMetricReader private List readers; @Autowired(required = false) @@ -77,11 +74,6 @@ public class MetricExportAutoConfiguration { if (reader != null) { writers.putAll(this.writers); - for (String name : this.writers.keySet()) { - if (this.actuatorMetrics.contains(writers.get(name))) { - writers.remove(name); - } - } MetricExporters exporters = new MetricExporters(reader, writers, this.metrics); return exporters; } 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 600881104ab..2d6052de5c8 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 @@ -125,7 +125,7 @@ public class MetricRepositoryAutoConfiguration { } @Bean - @ActuatorMetricReader + @ExportMetricReader @ConditionalOnMissingBean public BufferMetricReader actuatorMetricReader(CounterBuffers counters, GaugeBuffers gauges) { @@ -151,7 +151,7 @@ public class MetricRepositoryAutoConfiguration { static class LegacyMetricRepositoryConfiguration { @Bean - @ActuatorMetricReader + @ExportMetricReader @ActuatorMetricWriter 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 b3c0b8a3ba1..eb9d1b05746 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 @@ -70,7 +70,7 @@ import org.springframework.lang.UsesJava7; public class PublicMetricsAutoConfiguration { @Autowired(required = false) - @ActuatorMetricReader + @ExportMetricReader private List metricReaders = Collections.emptyList(); @Bean diff --git a/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/metrics/export/MetricExportProperties.java b/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/metrics/export/MetricExportProperties.java index 53c390ebf32..dae1d7f8546 100644 --- a/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/metrics/export/MetricExportProperties.java +++ b/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/metrics/export/MetricExportProperties.java @@ -208,8 +208,14 @@ class Trigger { */ private Boolean sendLatest; + /** + * List of patterns for metric names to include. + */ private String[] includes; + /** + * List of patterns for metric names to exclude. Applied after the includes. + */ private String[] excludes; public String[] getIncludes() { diff --git a/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/autoconfigure/MetricExportAutoConfigurationTests.java b/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/autoconfigure/MetricExportAutoConfigurationTests.java index 6402ceb5753..40493b03f26 100644 --- a/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/autoconfigure/MetricExportAutoConfigurationTests.java +++ b/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/autoconfigure/MetricExportAutoConfigurationTests.java @@ -116,6 +116,7 @@ public class MetricExportAutoConfigurationTests { public static class WriterConfig { @Bean + @ExportMetricWriter public MetricWriter writer() { return Mockito.mock(MetricWriter.class); } @@ -126,6 +127,7 @@ public class MetricExportAutoConfigurationTests { public static class MetricEndpointConfiguration { @Bean + @ExportMetricReader public MetricsEndpointMetricReader endpointReader() { return Mockito.mock(MetricsEndpointMetricReader.class); } diff --git a/spring-boot-docs/src/main/asciidoc/production-ready-features.adoc b/spring-boot-docs/src/main/asciidoc/production-ready-features.adoc index c7b1fdf0ee0..b61633a679e 100644 --- a/spring-boot-docs/src/main/asciidoc/production-ready-features.adoc +++ b/spring-boot-docs/src/main/asciidoc/production-ready-features.adoc @@ -912,12 +912,18 @@ used by default if you are on Java 8 or if you are using Dropwizard metrics. [[production-ready-metric-writers]] === Metric writers, exporters and aggregation -Spring Boot provides a couple of implementations of a marker interface called `Exporter` -which can be used to copy metric readings from the in-memory buffers to a place where they -can be analysed and displayed. Indeed, if you provide a `@Bean` that implements the -`MetricWriter` interface, then it will automatically be hooked up to an `Exporter` and fed -metric updates every 5 seconds (configured via `spring.metrics.export.delayMillis`) via a -`@Scheduled` annotation in `MetricRepositoryAutoConfiguration`. +Spring Boot provides a couple of implementations of a marker interface +called `Exporter` which can be used to copy metric readings from the +in-memory buffers to a place where they can be analysed and +displayed. Indeed, if you provide a `@Bean` that implements the +`MetricWriter` interface and mark it `@ExportMetricWriter`, then it +will automatically be hooked up to an `Exporter` and fed metric +updates every 5 seconds (configured via +`spring.metrics.export.delayMillis`) via a `@Scheduled` annotation in +`MetricRepositoryAutoConfiguration`. In addition, any `MetricReader` +that you define and mark as `@ExportMetricReader` will have its values +exported by the default exporter. + The default exporter is a `MetricCopyExporter` which tries to optimize itself by not copying values that haven't changed since it was last @@ -939,19 +945,23 @@ name (or pattern for matching bean names). [[production-ready-metric-writers-export-to-redis]] ==== Example: Export to Redis -If you provide a `@Bean` of type `RedisMetricRepository` the metrics are exported to a -Redis cache for aggregation. The `RedisMetricRepository` has 2 important parameters to -configure it for this purpose: `prefix` and `key` (passed into its constructor). It is -best to use a prefix that is unique to the application instance (e.g. using a random value -and maybe the logical name of the application to make it possible to correlate with other -instances of the same application). The "key" is used to keep a global index of all -metric names, so it should be unique "globally", whatever that means for your system (e.g. -2 instances of the same system could share a Redis cache if they have distinct keys). +If you provide a `@Bean` of type `RedisMetricRepository` and mark it +`@ExportMetricWriter` the metrics are exported to a Redis cache for +aggregation. The `RedisMetricRepository` has 2 important parameters to +configure it for this purpose: `prefix` and `key` (passed into its +constructor). It is best to use a prefix that is unique to the +application instance (e.g. using a random value and maybe the logical +name of the application to make it possible to correlate with other +instances of the same application). The "key" is used to keep a +global index of all metric names, so it should be unique "globally", +whatever that means for your system (e.g. 2 instances of the same +system could share a Redis cache if they have distinct keys). Example: [source,java,indent=0] ---- @Bean +@ExportMetricWriter MetricWriter metricWriter(MetricExportProperties export) { return new RedisMetricRepository(connectionFactory, export.getRedis().getPrefix(), export.getRedis().getKey()); @@ -987,15 +997,19 @@ the recommendations. [[production-ready-metric-writers-export-to-open-tdsb]] ==== Example: Export to Open TSDB -If you provide a `@Bean` of type `OpenTsdbHttpMetricWriter` the metrics are exported to -http://opentsdb.net/[Open TSDB] for aggregation. The `OpenTsdbHttpMetricWriter` has a -`url` property that you need to set to the Open TSDB "/put" endpoint, e.g. -`http://localhost:4242/api/put`). It also has a `namingStrategy` that you can customize or -configure to make the metrics match the data structure you need on the server. By default -it just passes through the metric name as an Open TSDB metric name and adds a tag "domain" -with value "org.springframework.metrics" and another tag "process" with value equals to -the object hash of the naming strategy. Thus, after running the application and generating -some metrics (e.g. by pinging the home page) you can inspect the metrics in the TDB UI +If you provide a `@Bean` of type `OpenTsdbHttpMetricWriter` and mark +it `@ExportMetricWriter` the metrics are exported to +http://opentsdb.net/[Open TSDB] for aggregation. The +`OpenTsdbHttpMetricWriter` has a `url` property that you need to set +to the Open TSDB "/put" endpoint, e.g. +`http://localhost:4242/api/put`). It also has a `namingStrategy` that +you can customize or configure to make the metrics match the data +structure you need on the server. By default it just passes through +the metric name as an Open TSDB metric name and adds a tag "domain" +with value "org.springframework.metrics" and another tag "process" +with value equals to the object hash of the naming strategy. Thus, +after running the application and generating some metrics (e.g. by +pinging the home page) you can inspect the metrics in the TDB UI (http://localhost:4242 by default). Example: [source,indent=0] @@ -1021,7 +1035,7 @@ curl localhost:4242/api/query?start=1h-ago&m=max:counter.status.200.root [[production-ready-metric-writers-export-to-statsd]] ==== Example: Export to Statsd -If you provide a `@Bean` of type `StatsdMetricWriter` the metrics are exported to a +If you provide a `@Bean` of type `StatsdMetricWriter` and mark it `@ExportMetricWriter` the metrics are exported to a statsd server: [source,java,indent=0] @@ -1036,6 +1050,7 @@ private String host = "localhost"; private int port; @Bean +@ExportMetricWriter MetricWriter metricWriter() { return new StatsdMetricWriter(prefix, host, port); } @@ -1044,7 +1059,7 @@ MetricWriter metricWriter() { [[production-ready-metric-writers-export-to-jmx]] ==== Example: Export to JMX -If you provide a `@Bean` of type `JmxMetricWriter` the metrics are exported as MBeans to +If you provide a `@Bean` of type `JmxMetricWriter` marked `@ExportMetricWriter` the metrics are exported as MBeans to the local server (the `MBeanExporter` is provided by Spring Boot JMX autoconfiguration as long as it is switched on). Metrics can then be inspected, graphed, alerted etc. using any tool that understands JMX (e.g. JConsole or JVisualVM). Example: @@ -1052,6 +1067,7 @@ tool that understands JMX (e.g. JConsole or JVisualVM). Example: [source,java,indent=0] ---- @Bean +@ExportMetricWriter MetricWriter metricWriter(MBeanExporter exporter) { return new JmxMetricWriter(exporter); } @@ -1084,19 +1100,17 @@ results to the "/metrics" endpoint. Example: @Bean public PublicMetrics metricsAggregate() { - return new MetricReaderPublicMetrics(aggregates()); + return new MetricReaderPublicMetrics(aggregatesMetricReader()); } - @Bean - protected MetricReader repository(RedisConnectionFactory connectionFactory) { - RedisMetricRepository repository = new RedisMetricRepository(connectionFactory, + private MetricReader globalMetricsForAggregation() { + return new RedisMetricRepository(this.connectionFactory, this.export.getRedis().getAggregatePrefix(), this.export.getRedis().getKey()); - return repository; } - @Bean - protected MetricReader aggregates() { - AggregateMetricReader repository = new AggregateMetricReader(repository()); + private MetricReader aggregatesMetricReader() { + AggregateMetricReader repository = new AggregateMetricReader( + globalMetricsForAggregation()); return repository; } ---- @@ -1105,6 +1119,10 @@ NOTE: the example above uses `MetricExportProperties` to inject and extract the key and prefix. This is provided to you as a convenience by Spring Boot, and the defaults for that will be sensible. +NOTE: the `MetricReaders` above are not `@Beans` and are not marked as +`@ExportMetricReader` because they are just collecting and analysing +data from other repositories, and don't want to export their values. + [[production-ready-dropwizard-metrics]] === Dropwizard Metrics diff --git a/spring-boot-samples/spring-boot-sample-metrics-redis/src/main/java/sample/metrics/redis/SampleRedisExportApplication.java b/spring-boot-samples/spring-boot-sample-metrics-redis/src/main/java/sample/metrics/redis/SampleRedisExportApplication.java index 53e9206d1e0..8286581115a 100644 --- a/spring-boot-samples/spring-boot-sample-metrics-redis/src/main/java/sample/metrics/redis/SampleRedisExportApplication.java +++ b/spring-boot-samples/spring-boot-sample-metrics-redis/src/main/java/sample/metrics/redis/SampleRedisExportApplication.java @@ -19,6 +19,7 @@ package sample.metrics.redis; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.boot.SpringApplication; +import org.springframework.boot.actuate.autoconfigure.ExportMetricWriter; import org.springframework.boot.actuate.metrics.export.MetricExportProperties; import org.springframework.boot.actuate.metrics.jmx.JmxMetricWriter; import org.springframework.boot.actuate.metrics.repository.redis.RedisMetricRepository; @@ -38,6 +39,7 @@ public class SampleRedisExportApplication { } @Bean + @ExportMetricWriter public RedisMetricRepository redisMetricWriter( RedisConnectionFactory connectionFactory) { return new RedisMetricRepository(connectionFactory, this.export.getRedis().getPrefix(), @@ -45,6 +47,7 @@ public class SampleRedisExportApplication { } @Bean + @ExportMetricWriter public JmxMetricWriter jmxMetricWriter( @Qualifier("mbeanExporter") MBeanExporter exporter) { return new JmxMetricWriter(exporter);