Commit c2958c27 authored by Jon Schneider's avatar Jon Schneider Committed by Andy Wilkinson

Replace Boot's own metrics with support for Micrometer

Closes gh-9970
parent 306c8d0a
...@@ -33,6 +33,11 @@ ...@@ -33,6 +33,11 @@
<groupId>com.fasterxml.jackson.core</groupId> <groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId> <artifactId>jackson-databind</artifactId>
</dependency> </dependency>
<dependency>
<groupId>io.micrometer</groupId>
<artifactId>micrometer-core</artifactId>
<optional>true</optional>
</dependency>
<dependency> <dependency>
<groupId>org.springframework</groupId> <groupId>org.springframework</groupId>
<artifactId>spring-core</artifactId> <artifactId>spring-core</artifactId>
...@@ -87,6 +92,41 @@ ...@@ -87,6 +92,41 @@
<artifactId>lettuce-core</artifactId> <artifactId>lettuce-core</artifactId>
<optional>true</optional> <optional>true</optional>
</dependency> </dependency>
<dependency>
<groupId>io.micrometer</groupId>
<artifactId>micrometer-atlas-starter</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>io.micrometer</groupId>
<artifactId>micrometer-datadog-starter</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>io.micrometer</groupId>
<artifactId>micrometer-ganglia-starter</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>io.micrometer</groupId>
<artifactId>micrometer-graphite-starter</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>io.micrometer</groupId>
<artifactId>micrometer-influx-starter</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>io.micrometer</groupId>
<artifactId>micrometer-jmx-starter</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>io.micrometer</groupId>
<artifactId>micrometer-prometheus-starter</artifactId>
<optional>true</optional>
</dependency>
<dependency> <dependency>
<groupId>io.searchbox</groupId> <groupId>io.searchbox</groupId>
<artifactId>jest</artifactId> <artifactId>jest</artifactId>
...@@ -143,6 +183,11 @@ ...@@ -143,6 +183,11 @@
<artifactId>tomcat-jdbc</artifactId> <artifactId>tomcat-jdbc</artifactId>
<optional>true</optional> <optional>true</optional>
</dependency> </dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<optional>true</optional>
</dependency>
<dependency> <dependency>
<groupId>org.elasticsearch</groupId> <groupId>org.elasticsearch</groupId>
<artifactId>elasticsearch</artifactId> <artifactId>elasticsearch</artifactId>
...@@ -283,6 +328,11 @@ ...@@ -283,6 +328,11 @@
<artifactId>spring-boot-test</artifactId> <artifactId>spring-boot-test</artifactId>
<scope>test</scope> <scope>test</scope>
</dependency> </dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-test-autoconfigure</artifactId>
<scope>test</scope>
</dependency>
<dependency> <dependency>
<groupId>org.springframework.boot</groupId> <groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-test-support</artifactId> <artifactId>spring-boot-test-support</artifactId>
......
/*
* Copyright 2012-2017 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.cache;
import javax.cache.Caching;
import com.github.benmanes.caffeine.cache.Caffeine;
import com.hazelcast.core.IMap;
import com.hazelcast.spring.cache.HazelcastCache;
import net.sf.ehcache.Ehcache;
import net.sf.ehcache.statistics.StatisticsGateway;
import org.infinispan.spring.provider.SpringCache;
import org.springframework.boot.actuate.cache.CacheStatistics;
import org.springframework.boot.actuate.cache.CacheStatisticsProvider;
import org.springframework.boot.actuate.cache.CaffeineCacheStatisticsProvider;
import org.springframework.boot.actuate.cache.ConcurrentMapCacheStatisticsProvider;
import org.springframework.boot.actuate.cache.DefaultCacheStatistics;
import org.springframework.boot.actuate.cache.EhCacheStatisticsProvider;
import org.springframework.boot.actuate.cache.HazelcastCacheStatisticsProvider;
import org.springframework.boot.actuate.cache.InfinispanCacheStatisticsProvider;
import org.springframework.boot.actuate.cache.JCacheCacheStatisticsProvider;
import org.springframework.boot.autoconfigure.AutoConfigureAfter;
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.cache.Cache;
import org.springframework.cache.CacheManager;
import org.springframework.cache.caffeine.CaffeineCacheManager;
import org.springframework.cache.concurrent.ConcurrentMapCache;
import org.springframework.cache.ehcache.EhCacheCache;
import org.springframework.cache.jcache.JCacheCache;
import org.springframework.cache.support.NoOpCacheManager;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
/**
* {@link EnableAutoConfiguration Auto-configuration} for {@link CacheStatisticsProvider}
* beans.
*
* @author Stephane Nicoll
* @author Phillip Webb
* @author Eddú Meléndez
* @since 2.0.0
*/
@Configuration
@AutoConfigureAfter(CacheAutoConfiguration.class)
@ConditionalOnBean(CacheManager.class)
public class CacheStatisticsAutoConfiguration {
@Configuration
@ConditionalOnClass({ Caching.class, JCacheCache.class })
static class JCacheCacheStatisticsProviderConfiguration {
@Bean
public JCacheCacheStatisticsProvider jCacheCacheStatisticsProvider() {
return new JCacheCacheStatisticsProvider();
}
}
@Configuration
@ConditionalOnClass({ EhCacheCache.class, Ehcache.class, StatisticsGateway.class })
static class EhCacheCacheStatisticsProviderConfiguration {
@Bean
public EhCacheStatisticsProvider ehCacheCacheStatisticsProvider() {
return new EhCacheStatisticsProvider();
}
}
@Configuration
@ConditionalOnClass({ IMap.class, HazelcastCache.class })
static class HazelcastCacheStatisticsConfiguration {
@Bean
public HazelcastCacheStatisticsProvider hazelcastCacheStatisticsProvider() {
return new HazelcastCacheStatisticsProvider();
}
}
@Configuration
@ConditionalOnClass({ SpringCache.class })
static class InfinispanCacheStatisticsProviderConfiguration {
@Bean
public InfinispanCacheStatisticsProvider infinispanCacheStatisticsProvider() {
return new InfinispanCacheStatisticsProvider();
}
}
@Configuration
@ConditionalOnClass({ Caffeine.class, CaffeineCacheManager.class })
static class CaffeineCacheStatisticsProviderConfiguration {
@Bean
public CaffeineCacheStatisticsProvider caffeineCacheStatisticsProvider() {
return new CaffeineCacheStatisticsProvider();
}
}
@Configuration
@ConditionalOnClass(ConcurrentMapCache.class)
static class ConcurrentMapCacheStatisticsConfiguration {
@Bean
public ConcurrentMapCacheStatisticsProvider concurrentMapCacheStatisticsProvider() {
return new ConcurrentMapCacheStatisticsProvider();
}
}
@Configuration
@ConditionalOnClass(NoOpCacheManager.class)
static class NoOpCacheStatisticsConfiguration {
private static final CacheStatistics NO_OP_STATS = new DefaultCacheStatistics();
@Bean
public CacheStatisticsProvider<Cache> noOpCacheStatisticsProvider() {
return (cacheManager, cache) -> {
if (cacheManager instanceof NoOpCacheManager) {
return NO_OP_STATS;
}
return null;
};
}
}
}
/*
* Copyright 2012-2017 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.
*/
/**
* Auto-configuration for actuator cache concerns.
*/
package org.springframework.boot.actuate.autoconfigure.cache;
...@@ -36,9 +36,9 @@ import org.springframework.boot.autoconfigure.condition.ConditionalOnBean; ...@@ -36,9 +36,9 @@ import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration; import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration;
import org.springframework.boot.jdbc.metadata.CompositeDataSourcePoolMetadataProvider;
import org.springframework.boot.jdbc.metadata.DataSourcePoolMetadata; import org.springframework.boot.jdbc.metadata.DataSourcePoolMetadata;
import org.springframework.boot.jdbc.metadata.DataSourcePoolMetadataProvider; import org.springframework.boot.jdbc.metadata.DataSourcePoolMetadataProvider;
import org.springframework.boot.jdbc.metadata.DataSourcePoolMetadataProviders;
import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Configuration;
import org.springframework.jdbc.core.JdbcTemplate; import org.springframework.jdbc.core.JdbcTemplate;
...@@ -94,7 +94,7 @@ public class DataSourceHealthIndicatorAutoConfiguration extends ...@@ -94,7 +94,7 @@ public class DataSourceHealthIndicatorAutoConfiguration extends
@Override @Override
public void afterPropertiesSet() throws Exception { public void afterPropertiesSet() throws Exception {
this.poolMetadataProvider = new DataSourcePoolMetadataProviders( this.poolMetadataProvider = new CompositeDataSourcePoolMetadataProvider(
this.metadataProviders); this.metadataProviders);
} }
......
/*
* Copyright 2012-2017 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.metrics;
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
* @since 2.0.0
*/
@Qualifier
@Target({ ElementType.FIELD, ElementType.METHOD, ElementType.PARAMETER, ElementType.TYPE,
ElementType.ANNOTATION_TYPE })
@Retention(RetentionPolicy.RUNTIME)
@Inherited
@Documented
public @interface ActuatorMetricWriter {
}
/*
* Copyright 2012-2017 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.metrics;
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
* @since 2.0.0
*/
@Qualifier
@Target({ ElementType.FIELD, ElementType.METHOD, ElementType.PARAMETER, ElementType.TYPE,
ElementType.ANNOTATION_TYPE })
@Retention(RetentionPolicy.RUNTIME)
@Inherited
@Documented
public @interface ExportMetricReader {
}
/*
* Copyright 2012-2017 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.metrics;
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 to be used to export metrics from
* the {@link ExportMetricReader} readers.
*
* @author Dave Syer
* @since 2.0.0
*/
@Qualifier
@Target({ ElementType.FIELD, ElementType.METHOD, ElementType.PARAMETER, ElementType.TYPE,
ElementType.ANNOTATION_TYPE })
@Retention(RetentionPolicy.RUNTIME)
@Inherited
@Documented
public @interface ExportMetricWriter {
}
...@@ -16,47 +16,41 @@ ...@@ -16,47 +16,41 @@
package org.springframework.boot.actuate.autoconfigure.metrics; package org.springframework.boot.actuate.autoconfigure.metrics;
import java.util.ArrayList; import io.micrometer.core.instrument.binder.JvmMemoryMetrics;
import java.util.Collections; import io.micrometer.core.instrument.binder.LogbackMetrics;
import java.util.List; import io.micrometer.core.instrument.binder.MeterBinder;
import io.micrometer.core.instrument.binder.UptimeMetrics;
import org.springframework.beans.factory.ObjectProvider;
import org.springframework.boot.actuate.autoconfigure.endpoint.condition.ConditionalOnEnabledEndpoint; import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.actuate.metrics.MetricsEndpoint;
import org.springframework.boot.actuate.metrics.PublicMetrics;
import org.springframework.boot.autoconfigure.AutoConfigureAfter;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Configuration;
import org.springframework.core.annotation.AnnotationAwareOrderComparator;
/** /**
* {@link EnableAutoConfiguration Auto-configuration} for the {@link MetricsEndpoint}. * Configuration for various {@link MeterBinder MeterBinders}.
* *
* @author Phillip Webb * @author Jon Schneider
* @since 2.0.0
*/ */
@Configuration @Configuration
@AutoConfigureAfter(PublicMetricsAutoConfiguration.class) class MeterBindersConfiguration {
public class MetricsEndpointAutoConfiguration {
@Bean @Bean
@ConditionalOnMissingBean @ConditionalOnMissingBean(JvmMemoryMetrics.class)
@ConditionalOnEnabledEndpoint public JvmMemoryMetrics jvmMemoryMetrics() {
public MetricsEndpoint metricsEndpoint( return new JvmMemoryMetrics();
ObjectProvider<List<PublicMetrics>> publicMetrics) {
return metricsEndpoint(publicMetrics.getIfAvailable(Collections::emptyList));
} }
private MetricsEndpoint metricsEndpoint(List<PublicMetrics> publicMetrics) { @Bean
return new MetricsEndpoint(sort(publicMetrics)); @ConditionalOnMissingBean(LogbackMetrics.class)
@ConditionalOnClass(name = "ch.qos.logback.classic.Logger")
public LogbackMetrics logbackMetrics() {
return new LogbackMetrics();
} }
private List<PublicMetrics> sort(List<PublicMetrics> publicMetrics) { @Bean
List<PublicMetrics> sorted = new ArrayList<>(publicMetrics); @ConditionalOnMissingBean(UptimeMetrics.class)
Collections.sort(sorted, AnnotationAwareOrderComparator.INSTANCE); public UptimeMetrics uptimeMetrics() {
return sorted; return new UptimeMetrics();
} }
} }
...@@ -14,7 +14,28 @@ ...@@ -14,7 +14,28 @@
* limitations under the License. * limitations under the License.
*/ */
package org.springframework.boot.actuate.autoconfigure.metrics;
import io.micrometer.core.instrument.Meter;
import io.micrometer.core.instrument.MeterRegistry;
/** /**
* Auto-configuration for actuator metrics concerns. * Callback interface that can be used to customize auto-configured {@link MeterRegistry
* MeterRegistries}.
* <p>
* Configurers are guaranteed to be applied before any {@link Meter} is registered with
* the registry.
*
* @author Jon Schneider
* @since 2.0.0
*/ */
package org.springframework.boot.actuate.autoconfigure.metrics; @FunctionalInterface
public interface MeterRegistryConfigurer {
/**
* Configure the given {@code registry}.
* @param registry the registry to configure
*/
void configureRegistry(MeterRegistry registry);
}
/*
* Copyright 2012-2017 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.metrics;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.springframework.beans.factory.ObjectProvider;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.actuate.metrics.export.Exporter;
import org.springframework.boot.actuate.metrics.export.MetricExportProperties;
import org.springframework.boot.actuate.metrics.export.MetricExporters;
import org.springframework.boot.actuate.metrics.reader.CompositeMetricReader;
import org.springframework.boot.actuate.metrics.reader.MetricReader;
import org.springframework.boot.actuate.metrics.reader.MetricsEndpointMetricReader;
import org.springframework.boot.actuate.metrics.statsd.StatsdMetricWriter;
import org.springframework.boot.actuate.metrics.writer.GaugeWriter;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
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.scheduling.annotation.EnableScheduling;
import org.springframework.scheduling.annotation.SchedulingConfigurer;
import org.springframework.scheduling.config.ScheduledTaskRegistrar;
import org.springframework.util.CollectionUtils;
/**
* {@link EnableAutoConfiguration Auto-configuration} for metrics export.
*
* @author Dave Syer
* @author Simon Buettner
* @since 2.0.0
*/
@Configuration
@EnableScheduling
@ConditionalOnProperty(value = "spring.metrics.export.enabled", matchIfMissing = true)
@EnableConfigurationProperties
public class MetricExportAutoConfiguration {
private final MetricsEndpointMetricReader endpointReader;
private final List<MetricReader> readers;
private final Map<String, GaugeWriter> writers;
private final Map<String, Exporter> exporters;
public MetricExportAutoConfiguration(MetricExportProperties properties,
ObjectProvider<MetricsEndpointMetricReader> endpointReader,
@ExportMetricReader ObjectProvider<List<MetricReader>> readers,
@ExportMetricWriter ObjectProvider<Map<String, GaugeWriter>> writers,
ObjectProvider<Map<String, Exporter>> exporters) {
this.endpointReader = endpointReader.getIfAvailable();
this.readers = readers.getIfAvailable();
this.writers = writers.getIfAvailable();
this.exporters = exporters.getIfAvailable();
}
@Bean
@ConditionalOnMissingBean(name = "metricWritersMetricExporter")
public SchedulingConfigurer metricWritersMetricExporter(
MetricExportProperties properties) {
Map<String, GaugeWriter> writers = new HashMap<>();
MetricReader reader = this.endpointReader;
if (reader == null && !CollectionUtils.isEmpty(this.readers)) {
reader = new CompositeMetricReader(
this.readers.toArray(new MetricReader[this.readers.size()]));
}
if (reader == null && CollectionUtils.isEmpty(this.exporters)) {
return new NoOpSchedulingConfigurer();
}
MetricExporters exporters = new MetricExporters(properties);
if (reader != null) {
if (!CollectionUtils.isEmpty(this.writers)) {
writers.putAll(this.writers);
}
exporters.setReader(reader);
exporters.setWriters(writers);
}
exporters.setExporters(this.exporters == null
? Collections.<String, Exporter>emptyMap() : this.exporters);
return exporters;
}
@Configuration
static class StatsdConfiguration {
@Bean
@ExportMetricWriter
@ConditionalOnMissingBean
@ConditionalOnProperty(prefix = "spring.metrics.export.statsd", name = "host")
public StatsdMetricWriter statsdMetricWriter(MetricExportProperties properties) {
MetricExportProperties.Statsd statsdProperties = properties.getStatsd();
return new StatsdMetricWriter(statsdProperties.getPrefix(),
statsdProperties.getHost(), statsdProperties.getPort());
}
}
@Configuration
protected static class MetricExportPropertiesConfiguration {
@Value("${spring.application.name:application}.${random.value:0000}")
private String prefix = "";
private String aggregateKeyPattern = "k.d";
@Bean(name = "spring.metrics.export-org.springframework.boot.actuate.metrics.export.MetricExportProperties")
@ConditionalOnMissingBean
public MetricExportProperties metricExportProperties() {
MetricExportProperties export = new MetricExportProperties();
export.getRedis().setPrefix("spring.metrics"
+ (this.prefix.length() > 0 ? "." : "") + this.prefix);
export.getAggregate().setPrefix(this.prefix);
export.getAggregate().setKeyPattern(this.aggregateKeyPattern);
return export;
}
}
private static class NoOpSchedulingConfigurer implements SchedulingConfigurer {
@Override
public void configureTasks(ScheduledTaskRegistrar taskRegistrar) {
}
}
}
/*
* Copyright 2012-2017 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.metrics;
import com.codahale.metrics.MetricRegistry;
import org.springframework.boot.actuate.metrics.CounterService;
import org.springframework.boot.actuate.metrics.GaugeService;
import org.springframework.boot.actuate.metrics.buffer.BufferCounterService;
import org.springframework.boot.actuate.metrics.buffer.BufferGaugeService;
import org.springframework.boot.actuate.metrics.buffer.BufferMetricReader;
import org.springframework.boot.actuate.metrics.buffer.CounterBuffers;
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.repository.InMemoryMetricRepository;
import org.springframework.boot.actuate.metrics.writer.MetricWriter;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.messaging.MessageChannel;
/**
* {@link EnableAutoConfiguration Auto-configuration} for metrics services. Creates
* user-facing {@link GaugeService} and {@link CounterService} instances, and also back
* end repositories to catch the data pumped into them.
* <p>
* In general, even if metric data needs to be stored and analysed remotely, it is
* recommended to use in-memory storage to buffer metric updates locally as is done by the
* default {@link CounterBuffers} and {@link GaugeBuffers}. The values can be exported
* (e.g. on a periodic basis) using an {@link Exporter}, most implementations of which
* have optimizations for sending data to remote repositories.
* <p>
* If Spring Messaging is on the classpath and a {@link MessageChannel} called
* "metricsChannel" is also available, all metric update events are published additionally
* as messages on that channel. Additional analysis or actions can be taken by clients
* subscribing to that channel.
* <p>
* In addition if Dropwizard's metrics library is on the classpath a
* {@link MetricRegistry} will be created and the default counter and gauge services will
* switch to using it instead of the default repository. Users can create "special"
* Dropwizard metrics by prefixing their metric names with the appropriate type (e.g.
* "histogram.*", "meter.*". "timer.*") and sending them to the {@code GaugeService} or
* {@code CounterService}.
* <p>
* By default all metric updates go to all {@link MetricWriter} instances in the
* application context via a {@link MetricCopyExporter} firing every 5 seconds (disable
* this by setting {@code spring.metrics.export.enabled=false}).
*
* @see GaugeService
* @see CounterService
* @see MetricWriter
* @see InMemoryMetricRepository
* @see Exporter
* @author Dave Syer
* @since 2.0.0
*/
@Configuration
public class MetricRepositoryAutoConfiguration {
@Configuration
@ConditionalOnMissingBean(GaugeService.class)
static class FastMetricServicesConfiguration {
@Bean
@ConditionalOnMissingBean
public CounterBuffers counterBuffers() {
return new CounterBuffers();
}
@Bean
@ConditionalOnMissingBean
public GaugeBuffers gaugeBuffers() {
return new GaugeBuffers();
}
@Bean
@ExportMetricReader
@ConditionalOnMissingBean
public BufferMetricReader actuatorMetricReader(CounterBuffers counters,
GaugeBuffers gauges) {
return new BufferMetricReader(counters, gauges);
}
@Bean
@ConditionalOnMissingBean(CounterService.class)
public BufferCounterService counterService(CounterBuffers writer) {
return new BufferCounterService(writer);
}
@Bean
@ConditionalOnMissingBean(GaugeService.class)
public BufferGaugeService gaugeService(GaugeBuffers writer) {
return new BufferGaugeService(writer);
}
}
}
...@@ -17,137 +17,84 @@ ...@@ -17,137 +17,84 @@
package org.springframework.boot.actuate.autoconfigure.metrics; package org.springframework.boot.actuate.autoconfigure.metrics;
import java.util.Collection; import java.util.Collection;
import java.util.List; import java.util.Collections;
import java.util.Map;
import javax.servlet.Servlet; import io.micrometer.core.annotation.Timed;
import javax.sql.DataSource; import io.micrometer.core.instrument.MeterRegistry;
import io.micrometer.core.instrument.Metrics;
import org.apache.catalina.startup.Tomcat; import io.micrometer.core.instrument.binder.MeterBinder;
import io.micrometer.core.instrument.composite.CompositeMeterRegistry;
import org.springframework.beans.factory.ObjectProvider; import org.springframework.beans.factory.ObjectProvider;
import org.springframework.boot.actuate.autoconfigure.cache.CacheStatisticsAutoConfiguration; import org.springframework.boot.actuate.autoconfigure.endpoint.condition.ConditionalOnEnabledEndpoint;
import org.springframework.boot.actuate.cache.CachePublicMetrics; import org.springframework.boot.actuate.autoconfigure.metrics.export.MetricsExporter;
import org.springframework.boot.actuate.cache.CacheStatisticsProvider; import org.springframework.boot.actuate.autoconfigure.metrics.export.atlas.AtlasExportConfiguration;
import org.springframework.boot.actuate.metrics.DataSourcePublicMetrics; import org.springframework.boot.actuate.autoconfigure.metrics.export.datadog.DatadogExportConfiguration;
import org.springframework.boot.actuate.metrics.PublicMetrics; import org.springframework.boot.actuate.autoconfigure.metrics.export.ganglia.GangliaExportConfiguration;
import org.springframework.boot.actuate.metrics.SystemPublicMetrics; import org.springframework.boot.actuate.autoconfigure.metrics.export.graphite.GraphiteExportConfiguration;
import org.springframework.boot.actuate.metrics.TomcatPublicMetrics; import org.springframework.boot.actuate.autoconfigure.metrics.export.influx.InfluxExportConfiguration;
import org.springframework.boot.actuate.metrics.integration.SpringIntegrationMetricReader; import org.springframework.boot.actuate.autoconfigure.metrics.export.jmx.JmxExportConfiguration;
import org.springframework.boot.actuate.metrics.reader.CompositeMetricReader; import org.springframework.boot.actuate.autoconfigure.metrics.export.prometheus.PrometheusExportConfiguration;
import org.springframework.boot.actuate.metrics.reader.MetricReader; import org.springframework.boot.actuate.autoconfigure.metrics.export.simple.SimpleExportConfiguration;
import org.springframework.boot.actuate.metrics.reader.MetricReaderPublicMetrics; import org.springframework.boot.actuate.autoconfigure.metrics.reactive.server.WebFluxMetricsConfiguration;
import org.springframework.boot.actuate.metrics.rich.RichGaugeReader; import org.springframework.boot.actuate.autoconfigure.metrics.web.client.RestTemplateMetricsConfiguration;
import org.springframework.boot.actuate.metrics.rich.RichGaugeReaderPublicMetrics; import org.springframework.boot.actuate.autoconfigure.metrics.web.servlet.WebMvcMetricsConfiguration;
import org.springframework.boot.autoconfigure.AutoConfigureAfter; import org.springframework.boot.actuate.metrics.MetricsEndpoint;
import org.springframework.boot.actuate.metrics.integration.SpringIntegrationMetrics;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration; 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.ConditionalOnBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication;
import org.springframework.boot.autoconfigure.condition.SearchStrategy; import org.springframework.boot.autoconfigure.condition.SearchStrategy;
import org.springframework.boot.autoconfigure.integration.IntegrationAutoConfiguration; import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration;
import org.springframework.boot.jdbc.metadata.DataSourcePoolMetadataProvider;
import org.springframework.cache.CacheManager;
import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
import org.springframework.integration.config.EnableIntegrationManagement; import org.springframework.integration.config.EnableIntegrationManagement;
import org.springframework.integration.support.management.IntegrationManagementConfigurer; import org.springframework.integration.support.management.IntegrationManagementConfigurer;
/** /**
* {@link EnableAutoConfiguration Auto-configuration} for {@link PublicMetrics}. * {@link EnableAutoConfiguration Auto-configuration} for Micrometer-based metrics.
* *
* @author Stephane Nicoll
* @author Phillip Webb
* @author Johannes Edmeier
* @author Artem Bilan
* @since 2.0.0 * @since 2.0.0
* @author Jon Schneider
*/ */
@Configuration @Configuration
@AutoConfigureAfter({ DataSourceAutoConfiguration.class, CacheAutoConfiguration.class, @ConditionalOnClass(Timed.class)
MetricRepositoryAutoConfiguration.class, CacheStatisticsAutoConfiguration.class, @EnableConfigurationProperties(MetricsProperties.class)
IntegrationAutoConfiguration.class }) @Import({ MeterBindersConfiguration.class, WebMvcMetricsConfiguration.class,
public class PublicMetricsAutoConfiguration { WebFluxMetricsConfiguration.class, RestTemplateMetricsConfiguration.class,
AtlasExportConfiguration.class, DatadogExportConfiguration.class,
private final List<MetricReader> metricReaders; GangliaExportConfiguration.class, GraphiteExportConfiguration.class,
InfluxExportConfiguration.class, JmxExportConfiguration.class,
public PublicMetricsAutoConfiguration( PrometheusExportConfiguration.class, SimpleExportConfiguration.class })
@ExportMetricReader ObjectProvider<List<MetricReader>> metricReaders) { public class MetricsAutoConfiguration {
this.metricReaders = metricReaders.getIfAvailable();
}
@Bean @Bean
public SystemPublicMetrics systemPublicMetrics() { @ConditionalOnMissingBean(MeterRegistry.class)
return new SystemPublicMetrics(); public CompositeMeterRegistry compositeMeterRegistry(
ObjectProvider<Collection<MetricsExporter>> exporters) {
CompositeMeterRegistry composite = new CompositeMeterRegistry();
exporters.getIfAvailable(Collections::emptyList).stream()
.map(MetricsExporter::registry).forEach(composite::add);
return composite;
} }
@Bean @Bean
public MetricReaderPublicMetrics metricReaderPublicMetrics() { @ConditionalOnBean(MeterRegistry.class)
return new MetricReaderPublicMetrics( @ConditionalOnMissingBean
new CompositeMetricReader(this.metricReaders == null ? new MetricReader[0] @ConditionalOnEnabledEndpoint
: this.metricReaders public MetricsEndpoint metricsEndpoint(MeterRegistry registry) {
.toArray(new MetricReader[this.metricReaders.size()]))); return new MetricsEndpoint(registry);
}
@Bean
@ConditionalOnBean(RichGaugeReader.class)
public RichGaugeReaderPublicMetrics richGaugePublicMetrics(
RichGaugeReader richGaugeReader) {
return new RichGaugeReaderPublicMetrics(richGaugeReader);
}
@Configuration
@ConditionalOnClass(DataSource.class)
@ConditionalOnBean(DataSource.class)
static class DataSourceMetricsConfiguration {
@Bean
@ConditionalOnMissingBean
@ConditionalOnBean(DataSourcePoolMetadataProvider.class)
public DataSourcePublicMetrics dataSourcePublicMetrics() {
return new DataSourcePublicMetrics();
}
}
@Configuration
@ConditionalOnClass({ Servlet.class, Tomcat.class })
@ConditionalOnWebApplication
static class TomcatMetricsConfiguration {
@Bean
@ConditionalOnMissingBean
public TomcatPublicMetrics tomcatPublicMetrics() {
return new TomcatPublicMetrics();
}
}
@Configuration
@ConditionalOnClass(CacheManager.class)
@ConditionalOnBean(CacheManager.class)
static class CacheStatisticsConfiguration {
@Bean
@ConditionalOnMissingBean
@ConditionalOnBean(CacheStatisticsProvider.class)
public CachePublicMetrics cachePublicMetrics(
Map<String, CacheManager> cacheManagers,
Collection<CacheStatisticsProvider<?>> statisticsProviders) {
return new CachePublicMetrics(cacheManagers, statisticsProviders);
}
} }
@Configuration @Configuration
@ConditionalOnClass(EnableIntegrationManagement.class) @ConditionalOnClass(EnableIntegrationManagement.class)
static class IntegrationMetricsConfiguration { static class MetricsIntegrationConfiguration {
@Bean(name = IntegrationManagementConfigurer.MANAGEMENT_CONFIGURER_NAME) @Bean(name = IntegrationManagementConfigurer.MANAGEMENT_CONFIGURER_NAME)
@ConditionalOnMissingBean(value = IntegrationManagementConfigurer.class, name = IntegrationManagementConfigurer.MANAGEMENT_CONFIGURER_NAME, search = SearchStrategy.CURRENT) @ConditionalOnMissingBean(value = IntegrationManagementConfigurer.class, name = IntegrationManagementConfigurer.MANAGEMENT_CONFIGURER_NAME, search = SearchStrategy.CURRENT)
public IntegrationManagementConfigurer managementConfigurer() { public IntegrationManagementConfigurer integrationManagementConfigurer() {
IntegrationManagementConfigurer configurer = new IntegrationManagementConfigurer(); IntegrationManagementConfigurer configurer = new IntegrationManagementConfigurer();
configurer.setDefaultCountsEnabled(true); configurer.setDefaultCountsEnabled(true);
configurer.setDefaultStatsEnabled(true); configurer.setDefaultStatsEnabled(true);
...@@ -155,11 +102,25 @@ public class PublicMetricsAutoConfiguration { ...@@ -155,11 +102,25 @@ public class PublicMetricsAutoConfiguration {
} }
@Bean @Bean
@ConditionalOnMissingBean(name = "springIntegrationPublicMetrics") public SpringIntegrationMetrics springIntegrationMetrics(
public MetricReaderPublicMetrics springIntegrationPublicMetrics( IntegrationManagementConfigurer configurer) {
IntegrationManagementConfigurer managementConfigurer) { return new SpringIntegrationMetrics(configurer);
return new MetricReaderPublicMetrics( }
new SpringIntegrationMetricReader(managementConfigurer));
}
@Configuration
static class MeterRegistryConfigurationSupport {
MeterRegistryConfigurationSupport(MeterRegistry registry,
ObjectProvider<Collection<MeterRegistryConfigurer>> configurers,
MetricsProperties config, Collection<MeterBinder> binders) {
configurers.getIfAvailable(Collections::emptyList)
.forEach((configurer) -> configurer.configureRegistry(registry));
binders.forEach((binder) -> binder.bindTo(registry));
if (config.isUseGlobalRegistry()) {
Metrics.addRegistry(registry);
}
} }
} }
......
/*
* Copyright 2012-2017 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.metrics;
import org.springframework.boot.context.properties.ConfigurationProperties;
/**
* {@link ConfigurationProperties} for configuring Micrometer-based metrics.
*
* @author Jon Schneider
* @since 2.0.0
*/
@ConfigurationProperties("spring.metrics")
public class MetricsProperties {
private Web web = new Web();
/**
* Whether or not auto-configured MeterRegistry implementations should be bound to the
* global static registry on Metrics. For testing, set this to 'false' to maximize
* test independence.
*/
private boolean useGlobalRegistry = true;
public boolean isUseGlobalRegistry() {
return this.useGlobalRegistry;
}
public void setUseGlobalRegistry(boolean useGlobalRegistry) {
this.useGlobalRegistry = useGlobalRegistry;
}
public Web getWeb() {
return this.web;
}
public static class Web {
private Client client = new Client();
private Server server = new Server();
public Client getClient() {
return this.client;
}
public void setClient(Client client) {
this.client = client;
}
public Server getServer() {
return this.server;
}
public void setServer(Server server) {
this.server = server;
}
public static class Client {
/**
* Whether or not instrumented requests record percentiles histogram buckets
* by default.
*/
private boolean recordRequestPercentiles;
/**
* Name of the metric for sent requests.
*/
private String requestsMetricName = "http.client.requests";
public boolean isRecordRequestPercentiles() {
return this.recordRequestPercentiles;
}
public void setRecordRequestPercentiles(boolean recordRequestPercentiles) {
this.recordRequestPercentiles = recordRequestPercentiles;
}
public String getRequestsMetricName() {
return this.requestsMetricName;
}
public void setRequestsMetricName(String requestsMetricName) {
this.requestsMetricName = requestsMetricName;
}
}
public static class Server {
/**
* Whether or not requests handled by Spring MVC or WebFlux should be
* automatically timed. If the number of time series emitted grows too large
* on account of request mapping timings, disable this and use 'Timed' on a
* per request mapping basis as needed.
*/
private boolean autoTimeRequests = true;
/**
* Whether or not instrumented requests record percentiles histogram buckets
* by default. Can be overridden by adding '@Timed' to a request endpoint and
* setting 'percentiles' to true.
*/
private boolean recordRequestPercentiles;
/**
* Name of the metric for received requests.
*/
private String requestsMetricName = "http.server.requests";
public boolean isAutoTimeRequests() {
return this.autoTimeRequests;
}
public void setAutoTimeRequests(boolean autoTimeRequests) {
this.autoTimeRequests = autoTimeRequests;
}
public boolean isRecordRequestPercentiles() {
return this.recordRequestPercentiles;
}
public void setRecordRequestPercentiles(boolean recordRequestPercentiles) {
this.recordRequestPercentiles = recordRequestPercentiles;
}
public String getRequestsMetricName() {
return this.requestsMetricName;
}
public void setRequestsMetricName(String requestsMetricName) {
this.requestsMetricName = requestsMetricName;
}
}
}
}
...@@ -14,23 +14,25 @@ ...@@ -14,23 +14,25 @@
* limitations under the License. * limitations under the License.
*/ */
package org.springframework.boot.actuate.metrics.writer; package org.springframework.boot.actuate.autoconfigure.metrics.export;
import org.springframework.boot.actuate.metrics.Metric; import io.micrometer.core.instrument.MeterRegistry;
/** /**
* Writer for gauge values (simple metric with a number value). * A {@code MetricsExporter} can be used to export metrics, typically to an external
* server running as a separate process.
* *
* @author Dave Syer * @author Jon Schneider
* @since 1.3.0 * @author Andy Wilkinson
* @since 2.0.0
*/ */
@FunctionalInterface @FunctionalInterface
public interface GaugeWriter { public interface MetricsExporter {
/** /**
* Set the value of a metric. * Returns the {@link MeterRegistry} used to register metrics with the exporter.
* @param value the value * @return the meter registry
*/ */
void set(Metric<?> value); MeterRegistry registry();
} }
/* /*
* Copyright 2012-2016 the original author or authors. * Copyright 2012-2017 the original author or authors.
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
...@@ -14,21 +14,31 @@ ...@@ -14,21 +14,31 @@
* limitations under the License. * limitations under the License.
*/ */
package sample.metrics.redis; package org.springframework.boot.actuate.autoconfigure.metrics.export;
import java.util.Properties;
import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.boot.context.properties.ConfigurationProperties;
@ConfigurationProperties(prefix = "service", ignoreUnknownFields = false) /**
public class HelloWorldProperties { * Base {@link ConfigurationProperties} class for configuring a metrics registry.
*
* @author Jon Schneider
* @author Andy Wilkinson
* @since 2.0.0
*/
public abstract class RegistryProperties {
private final Properties properties = new Properties();
private String name = "World"; protected abstract String prefix();
public String getName() { public String get(String key) {
return this.name; return this.properties.getProperty(key);
} }
public void setName(String name) { protected void set(String key, Object value) {
this.name = name; this.properties.put(prefix() + "." + key, value.toString());
} }
} }
...@@ -14,48 +14,45 @@ ...@@ -14,48 +14,45 @@
* limitations under the License. * limitations under the License.
*/ */
package org.springframework.boot.actuate.metrics.opentsdb; package org.springframework.boot.actuate.autoconfigure.metrics.export;
import java.util.LinkedHashMap; import java.time.Duration;
import java.util.Map;
import io.micrometer.core.instrument.spectator.step.StepRegistryConfig;
/** /**
* OpenTSDB Name. * Specialization of {@link RegistryProperties} for configuring a metrics registry that
* pushes aggregated metrics on a regular interval.
* *
* @author Dave Syer * @author Jon Schneider
* @since 1.3.0 * @author Andy Wilkinson
* @since 2.0.0
*/ */
public class OpenTsdbName { public abstract class StepRegistryProperties extends RegistryProperties
implements StepRegistryConfig {
private String metric;
private Map<String, String> tags = new LinkedHashMap<>();
protected OpenTsdbName() {
}
public OpenTsdbName(String metric) { public void setStep(Duration step) {
this.metric = metric; set("step", step);
} }
public String getMetric() { public void setEnabled(Boolean enabled) {
return this.metric; set("enabled", enabled);
} }
public void setMetric(String metric) { public void setBatchSize(Integer batchSize) {
this.metric = metric; set("batchSize", batchSize);
} }
public Map<String, String> getTags() { public void setConnectTimeout(Duration connectTimeout) {
return this.tags; set("connectTimeout", connectTimeout);
} }
public void setTags(Map<String, String> tags) { public void setReadTimeout(Duration readTimeout) {
this.tags.putAll(tags); set("readTimeout", readTimeout);
} }
public void tag(String name, String value) { public void setNumThreads(Integer numThreads) {
this.tags.put(name, value); set("numThreads", numThreads);
} }
} }
...@@ -14,24 +14,26 @@ ...@@ -14,24 +14,26 @@
* limitations under the License. * limitations under the License.
*/ */
package org.springframework.boot.actuate.metrics.web.servlet; package org.springframework.boot.actuate.autoconfigure.metrics.export;
import java.time.Duration;
import org.springframework.boot.context.properties.ConfigurationPropertiesBinding;
import org.springframework.core.convert.converter.Converter;
/** /**
* Submission types that can be made by the {@link MetricsFilter}. * A {@link Converter} to create a {@link Duration} from a {@link String}.
* *
* @author Phillip Webb * @author Jon Schneider
* @author Andy Wilkinson
* @since 2.0.0 * @since 2.0.0
*/ */
public enum MetricsFilterSubmission { @ConfigurationPropertiesBinding
public class StringToDurationConverter implements Converter<String, Duration> {
/**
* Merge all HTTP methods into a single submission.
*/
MERGED,
/** @Override
* Group submissions by the HTTP method of the request. public Duration convert(String source) {
*/ return Duration.parse(source);
PER_HTTP_METHOD }
} }
...@@ -14,62 +14,45 @@ ...@@ -14,62 +14,45 @@
* limitations under the License. * limitations under the License.
*/ */
package org.springframework.boot.actuate.autoconfigure.metrics; package org.springframework.boot.actuate.autoconfigure.metrics.export.atlas;
import javax.servlet.Servlet; import com.netflix.spectator.atlas.AtlasConfig;
import javax.servlet.ServletRegistration; import io.micrometer.atlas.AtlasMeterRegistry;
import io.micrometer.core.instrument.Clock;
import org.springframework.boot.actuate.metrics.CounterService; import org.springframework.boot.actuate.autoconfigure.metrics.export.MetricsExporter;
import org.springframework.boot.actuate.metrics.GaugeService; import org.springframework.boot.actuate.autoconfigure.metrics.export.StringToDurationConverter;
import org.springframework.boot.actuate.metrics.web.servlet.MetricsFilter;
import org.springframework.boot.autoconfigure.AutoConfigureAfter;
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.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.context.properties.EnableConfigurationProperties; import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Configuration;
import org.springframework.web.filter.OncePerRequestFilter; import org.springframework.context.annotation.Import;
import org.springframework.web.servlet.HandlerMapping;
/** /**
* {@link EnableAutoConfiguration Auto-configuration} that records Servlet interactions * Configuration for exporting metrics to Atlas.
* with a {@link CounterService} and {@link GaugeService}.
* *
* @author Dave Syer * @author Jon Schneider
* @author Phillip Webb
* @author Andy Wilkinson * @author Andy Wilkinson
* @author Sebastian Kirsch
* @since 2.0.0 * @since 2.0.0
*/ */
@Configuration @Configuration
@ConditionalOnBean({ CounterService.class, GaugeService.class }) @ConditionalOnClass(AtlasMeterRegistry.class)
@ConditionalOnClass({ Servlet.class, ServletRegistration.class, @Import(StringToDurationConverter.class)
OncePerRequestFilter.class, HandlerMapping.class }) @EnableConfigurationProperties(AtlasProperties.class)
@AutoConfigureAfter(MetricRepositoryAutoConfiguration.class) public class AtlasExportConfiguration {
@ConditionalOnProperty(prefix = "management.metrics.filter", name = "enabled", matchIfMissing = true)
@EnableConfigurationProperties({ MetricFilterProperties.class })
public class MetricFilterAutoConfiguration {
private final CounterService counterService; @Bean
@ConditionalOnProperty(value = "metrics.atlas.enabled", matchIfMissing = true)
private final GaugeService gaugeService; public MetricsExporter atlasExporter(AtlasConfig config, Clock clock) {
return () -> new AtlasMeterRegistry(config, clock);
private final MetricFilterProperties properties;
public MetricFilterAutoConfiguration(CounterService counterService,
GaugeService gaugeService, MetricFilterProperties properties) {
this.counterService = counterService;
this.gaugeService = gaugeService;
this.properties = properties;
} }
@Bean @Bean
public MetricsFilter metricsFilter() { @ConditionalOnMissingBean
return new MetricsFilter(this.counterService, this.gaugeService, public Clock clock() {
this.properties.getCounterSubmissions(), return Clock.SYSTEM;
this.properties.getGaugeSubmissions());
} }
} }
/*
* Copyright 2012-2017 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.metrics.export.atlas;
import java.time.Duration;
import com.netflix.spectator.atlas.AtlasConfig;
import org.springframework.boot.actuate.autoconfigure.metrics.export.RegistryProperties;
import org.springframework.boot.context.properties.ConfigurationProperties;
/**
* {@link ConfigurationProperties} for configuring Atlas metrics export.
*
* @since 2.0.0
* @author Jon Schneider
*/
@ConfigurationProperties(prefix = "metrics.atlas")
public class AtlasProperties extends RegistryProperties implements AtlasConfig {
@Override
protected String prefix() {
return "atlas";
}
public void setStep(Duration step) {
set("step", step);
}
public void setMeterTTL(Duration meterTTL) {
set("meterTTL", meterTTL);
}
public void setEnabled(Boolean enabled) {
set("enabled", enabled);
}
public void setNumThreads(Integer numThreads) {
set("numThreads", numThreads);
}
public void setUri(String uri) {
set("uri", uri);
}
public void setLwcEnabled(boolean lwcEnabled) {
set("lwcEnabled", lwcEnabled);
}
public void setConfigRefreshFrequency(Duration configRefreshFrequency) {
set("configRefreshFrequency", configRefreshFrequency);
}
public void setConfigTTL(Duration configTTL) {
set("configTTL", configTTL);
}
public void setConfigUri(String configUri) {
set("configUri", configUri);
}
public void setEvalUri(String evalUri) {
set("evalUri", evalUri);
}
public void setConnectTimeout(Duration connectTimeout) {
set("connectTimeout", connectTimeout);
}
public void setReadTimeout(Duration readTimeout) {
set("readTimeout", readTimeout);
}
public void setBatchSize(Integer batchSize) {
set("batchSize", batchSize);
}
}
/*
* Copyright 2012-2017 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.metrics.export.datadog;
import io.micrometer.core.instrument.Clock;
import io.micrometer.datadog.DatadogConfig;
import io.micrometer.datadog.DatadogMeterRegistry;
import org.springframework.boot.actuate.autoconfigure.metrics.export.MetricsExporter;
import org.springframework.boot.actuate.autoconfigure.metrics.export.StringToDurationConverter;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
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.context.annotation.Import;
/**
* Configuration for exporting metrics to Datadog.
*
* @author Jon Schneider
* @since 2.0.0
*/
@Configuration
@ConditionalOnClass(DatadogMeterRegistry.class)
@Import(StringToDurationConverter.class)
@EnableConfigurationProperties(DatadogProperties.class)
public class DatadogExportConfiguration {
@Bean
@ConditionalOnProperty(value = "metrics.datadog.enabled", matchIfMissing = true)
public MetricsExporter datadogExporter(DatadogConfig config, Clock clock) {
return () -> new DatadogMeterRegistry(config, clock);
}
@Bean
@ConditionalOnMissingBean
public Clock clock() {
return Clock.SYSTEM;
}
}
/* /*
* Copyright 2012-2016 the original author or authors. * Copyright 2012-2017 the original author or authors.
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
...@@ -14,24 +14,37 @@ ...@@ -14,24 +14,37 @@
* limitations under the License. * limitations under the License.
*/ */
package sample.metrics.dropwizard; package org.springframework.boot.actuate.autoconfigure.metrics.export.datadog;
import io.micrometer.datadog.DatadogConfig;
import org.springframework.boot.actuate.autoconfigure.metrics.export.StepRegistryProperties;
import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.boot.context.properties.ConfigurationProperties;
@ConfigurationProperties(prefix = "service", ignoreUnknownFields = false) /**
public class HelloWorldProperties { * {@link ConfigurationProperties} for configuring Datadog metrics export.
*
* @author Jon Schneider
* @since 2.0.0
*/
@ConfigurationProperties(prefix = "metrics.datadog")
public class DatadogProperties extends StepRegistryProperties implements DatadogConfig {
@Override
public String prefix() {
return "metrics.datadog";
}
/** public DatadogProperties() {
* Name of the service. set("apiKey", "dummyKey"); // FIXME otherwise tests fail
*/ }
private String name = "World";
public String getName() { public void setApiKey(String apiKey) {
return this.name; set("apiKey", apiKey);
} }
public void setName(String name) { public void setHostTag(String hostTag) {
this.name = name; set("hostTag", hostTag);
} }
} }
/*
* Copyright 2012-2017 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.metrics.export.ganglia;
import io.micrometer.core.instrument.Clock;
import io.micrometer.core.instrument.util.HierarchicalNameMapper;
import io.micrometer.ganglia.GangliaConfig;
import io.micrometer.ganglia.GangliaMeterRegistry;
import org.springframework.boot.actuate.autoconfigure.metrics.export.MetricsExporter;
import org.springframework.boot.actuate.autoconfigure.metrics.export.StringToDurationConverter;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
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.context.annotation.Import;
/**
* Configuration for exporting metrics to Ganglia.
*
* @author Jon Schneider
* @since 2.0.0
*/
@Configuration
@ConditionalOnClass(GangliaMeterRegistry.class)
@Import(StringToDurationConverter.class)
@EnableConfigurationProperties(GangliaProperties.class)
public class GangliaExportConfiguration {
@Bean
@ConditionalOnProperty(value = "metrics.ganglia.enabled", matchIfMissing = true)
public MetricsExporter gangliaExporter(GangliaConfig config,
HierarchicalNameMapper nameMapper, Clock clock) {
return () -> new GangliaMeterRegistry(config, nameMapper, clock);
}
@Bean
@ConditionalOnMissingBean
public Clock clock() {
return Clock.SYSTEM;
}
@Bean
@ConditionalOnMissingBean
public HierarchicalNameMapper hierarchicalNameMapper() {
return HierarchicalNameMapper.DEFAULT;
}
}
...@@ -14,56 +14,65 @@ ...@@ -14,56 +14,65 @@
* limitations under the License. * limitations under the License.
*/ */
package org.springframework.boot.actuate.autoconfigure.metrics; package org.springframework.boot.actuate.autoconfigure.metrics.export.ganglia;
import java.util.EnumSet; import java.time.Duration;
import java.util.HashSet; import java.util.concurrent.TimeUnit;
import java.util.Set;
import org.springframework.boot.actuate.metrics.web.servlet.MetricsFilter; import info.ganglia.gmetric4j.gmetric.GMetric;
import org.springframework.boot.actuate.metrics.web.servlet.MetricsFilterSubmission; import io.micrometer.ganglia.GangliaConfig;
import org.springframework.boot.actuate.autoconfigure.metrics.export.RegistryProperties;
import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.boot.context.properties.ConfigurationProperties;
/** /**
* Configuration properties for the {@link MetricsFilter}. * {@link ConfigurationProperties} for configuring Ganglia metrics export.
* *
* @author Sebastian Kirsch * @author Jon Schneider
* @author Phillip Webb
* @since 2.0.0 * @since 2.0.0
*/ */
@ConfigurationProperties(prefix = "management.metrics.filter") @ConfigurationProperties(prefix = "metrics.ganglia")
public class MetricFilterProperties { public class GangliaProperties extends RegistryProperties implements GangliaConfig {
/** @Override
* Submissions that should be made to the gauge. public String prefix() {
*/ return "metrics.ganglia";
private Set<MetricsFilterSubmission> gaugeSubmissions; }
/** public void setStep(Duration step) {
* Submissions that should be made to the counter. set("step", step);
*/ }
private Set<MetricsFilterSubmission> counterSubmissions;
public void setRateUnits(TimeUnit rateUnits) {
public MetricFilterProperties() { set("rateUnits", rateUnits);
this.gaugeSubmissions = new HashSet<>(EnumSet.of(MetricsFilterSubmission.MERGED)); }
this.counterSubmissions = new HashSet<>(
EnumSet.of(MetricsFilterSubmission.MERGED)); public void setDurationUnits(TimeUnit durationUnits) {
set("durationUnits", durationUnits);
}
public void setProtocolVersion(String protocolVersion) {
set("protocolVersion", protocolVersion);
}
public void setAddressingMode(GMetric.UDPAddressingMode addressingMode) {
set("addressingMode", addressingMode);
} }
public Set<MetricsFilterSubmission> getGaugeSubmissions() { public void setTtl(Integer ttl) {
return this.gaugeSubmissions; set("ttl", ttl);
} }
public void setGaugeSubmissions(Set<MetricsFilterSubmission> gaugeSubmissions) { public void setHost(String host) {
this.gaugeSubmissions = gaugeSubmissions; set("host", host);
} }
public Set<MetricsFilterSubmission> getCounterSubmissions() { public void setPort(Integer port) {
return this.counterSubmissions; set("port", port);
} }
public void setCounterSubmissions(Set<MetricsFilterSubmission> counterSubmissions) { public void setEnabled(Boolean enabled) {
this.counterSubmissions = counterSubmissions; set("enabled", enabled);
} }
} }
/*
* Copyright 2012-2017 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.metrics.export.graphite;
import io.micrometer.core.instrument.Clock;
import io.micrometer.core.instrument.util.HierarchicalNameMapper;
import io.micrometer.graphite.GraphiteConfig;
import io.micrometer.graphite.GraphiteMeterRegistry;
import org.springframework.boot.actuate.autoconfigure.metrics.export.MetricsExporter;
import org.springframework.boot.actuate.autoconfigure.metrics.export.StringToDurationConverter;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
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.context.annotation.Import;
/**
* Configuration for exporting metrics to Graphite.
*
* @author Jon Schneider
* @since 2.0.0
*/
@Configuration
@ConditionalOnClass(GraphiteMeterRegistry.class)
@Import(StringToDurationConverter.class)
@EnableConfigurationProperties(GraphiteProperties.class)
public class GraphiteExportConfiguration {
@Bean
@ConditionalOnProperty(value = "metrics.graphite.enabled", matchIfMissing = true)
public MetricsExporter graphiteExporter(GraphiteConfig config,
HierarchicalNameMapper nameMapper, Clock clock) {
return () -> new GraphiteMeterRegistry(config, nameMapper, clock);
}
@Bean
@ConditionalOnMissingBean
public Clock clock() {
return Clock.SYSTEM;
}
@Bean
@ConditionalOnMissingBean
public HierarchicalNameMapper hierarchicalNameMapper() {
return HierarchicalNameMapper.DEFAULT;
}
}
/*
* Copyright 2012-2017 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.metrics.export.graphite;
import java.time.Duration;
import java.util.concurrent.TimeUnit;
import io.micrometer.graphite.GraphiteConfig;
import org.springframework.boot.actuate.autoconfigure.metrics.export.RegistryProperties;
import org.springframework.boot.context.properties.ConfigurationProperties;
/**
* {@link ConfigurationProperties} for configuring Graphite metrics export.
*
* @author Jon Schneider
* @since 2.0.0
*/
@ConfigurationProperties(prefix = "metrics.graphite")
public class GraphiteProperties extends RegistryProperties implements GraphiteConfig {
@Override
public String prefix() {
return "metrics.graphite";
}
public void setStep(Duration step) {
set("step", step);
}
public void setRateUnits(TimeUnit rateUnits) {
set("rateUnits", rateUnits);
}
public void setDurationUnits(TimeUnit durationUnits) {
set("durationUnits", durationUnits);
}
public void setHost(String host) {
set("host", host);
}
public void setPort(Integer port) {
set("port", port);
}
public void setEnabled(Boolean enabled) {
set("enabled", enabled);
}
}
/*
* Copyright 2012-2017 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.metrics.export.influx;
import io.micrometer.core.instrument.Clock;
import io.micrometer.influx.InfluxConfig;
import io.micrometer.influx.InfluxMeterRegistry;
import org.springframework.boot.actuate.autoconfigure.metrics.export.MetricsExporter;
import org.springframework.boot.actuate.autoconfigure.metrics.export.StringToDurationConverter;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
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.context.annotation.Import;
/**
* Configuration for exporting metrics to Influx.
*
* @author Jon Schneider
* @since 2.0.0
*/
@Configuration
@ConditionalOnClass(InfluxMeterRegistry.class)
@Import(StringToDurationConverter.class)
@EnableConfigurationProperties(InfluxProperties.class)
public class InfluxExportConfiguration {
@Bean
@ConditionalOnProperty(value = "metrics.influx.enabled", matchIfMissing = true)
public MetricsExporter influxExporter(InfluxConfig config, Clock clock) {
return () -> new InfluxMeterRegistry(config, clock);
}
@Bean
@ConditionalOnMissingBean
public Clock clock() {
return Clock.SYSTEM;
}
}
...@@ -14,57 +14,54 @@ ...@@ -14,57 +14,54 @@
* limitations under the License. * limitations under the License.
*/ */
package org.springframework.boot.actuate.metrics.writer; package org.springframework.boot.actuate.autoconfigure.metrics.export.influx;
import java.util.ArrayList; import io.micrometer.influx.InfluxConfig;
import java.util.Collections; import io.micrometer.influx.InfluxConsistency;
import java.util.Iterator;
import java.util.List;
import org.springframework.boot.actuate.metrics.Metric; import org.springframework.boot.actuate.autoconfigure.metrics.export.StepRegistryProperties;
import org.springframework.boot.context.properties.ConfigurationProperties;
/** /**
* Composite implementation of {@link MetricWriter} that just sends its input to all of * {@link ConfigurationProperties} for configuring Influx metrics export.
* the delegates that have been registered.
* *
* @author Dave Syer * @author Jon Schneider
* @since 2.0.0
*/ */
public class CompositeMetricWriter implements MetricWriter, Iterable<MetricWriter> { @ConfigurationProperties(prefix = "metrics.influx")
public class InfluxProperties extends StepRegistryProperties implements InfluxConfig {
private final List<MetricWriter> writers = new ArrayList<>(); @Override
public String prefix() {
return "metrics.influx";
}
public CompositeMetricWriter(MetricWriter... writers) { public void setDb(String db) {
Collections.addAll(this.writers, writers); set("db", db);
} }
public CompositeMetricWriter(List<MetricWriter> writers) { public void setConsistency(InfluxConsistency consistency) {
this.writers.addAll(writers); set("consistency", consistency);
} }
@Override public void setUserName(String userName) {
public Iterator<MetricWriter> iterator() { set("userName", userName);
return this.writers.iterator();
} }
@Override public void setPassword(String password) {
public void increment(Delta<?> delta) { set("password", password);
for (MetricWriter writer : this.writers) {
writer.increment(delta);
}
} }
@Override public void setRetentionPolicy(String retentionPolicy) {
public void set(Metric<?> value) { set("retentionPolicy", retentionPolicy);
for (MetricWriter writer : this.writers) {
writer.set(value);
}
} }
@Override public void setUri(String uri) {
public void reset(String metricName) { set("uri", uri);
for (MetricWriter writer : this.writers) { }
writer.reset(metricName);
} public void setCompressed(Boolean compressed) {
set("compressed", compressed);
} }
} }
...@@ -14,38 +14,45 @@ ...@@ -14,38 +14,45 @@
* limitations under the License. * limitations under the License.
*/ */
package org.springframework.boot.actuate.autoconfigure.metrics; package org.springframework.boot.actuate.autoconfigure.metrics.export.jmx;
import org.springframework.beans.factory.annotation.Qualifier; import io.micrometer.core.instrument.Clock;
import org.springframework.boot.actuate.metrics.writer.MessageChannelMetricWriter; import io.micrometer.core.instrument.util.HierarchicalNameMapper;
import org.springframework.boot.autoconfigure.AutoConfigureBefore; import io.micrometer.jmx.JmxMeterRegistry;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean; import org.springframework.boot.actuate.autoconfigure.metrics.export.MetricsExporter;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; 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.Bean;
import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Configuration;
import org.springframework.messaging.MessageChannel;
/** /**
* {@link EnableAutoConfiguration Auto-configuration} for writing metrics to a * Configuration for exporting metrics to JMX.
* {@link MessageChannel}.
* *
* @author Dave Syer * @author Jon Schneider
* @since 2.0.0 * @since 2.0.0
*/ */
@Configuration @Configuration
@ConditionalOnClass(MessageChannel.class) @ConditionalOnClass(JmxMeterRegistry.class)
@ConditionalOnBean(name = "metricsChannel") public class JmxExportConfiguration {
@AutoConfigureBefore(MetricRepositoryAutoConfiguration.class)
public class MetricsChannelAutoConfiguration { @Bean
@ConditionalOnProperty(value = "metrics.jmx.enabled", matchIfMissing = true)
public MetricsExporter jmxExporter(HierarchicalNameMapper nameMapper, Clock clock) {
return () -> new JmxMeterRegistry(nameMapper, clock);
}
@Bean
@ConditionalOnMissingBean
public Clock clock() {
return Clock.SYSTEM;
}
@Bean @Bean
@ExportMetricWriter
@ConditionalOnMissingBean @ConditionalOnMissingBean
public MessageChannelMetricWriter messageChannelMetricWriter( public HierarchicalNameMapper hierarchicalNameMapper() {
@Qualifier("metricsChannel") MessageChannel channel) { return HierarchicalNameMapper.DEFAULT;
return new MessageChannelMetricWriter(channel);
} }
} }
...@@ -14,67 +14,62 @@ ...@@ -14,67 +14,62 @@
* limitations under the License. * limitations under the License.
*/ */
package org.springframework.boot.actuate.autoconfigure.metrics; package org.springframework.boot.actuate.autoconfigure.metrics.export.prometheus;
import com.codahale.metrics.MetricRegistry; import io.micrometer.core.instrument.Clock;
import io.micrometer.prometheus.PrometheusConfig;
import io.micrometer.prometheus.PrometheusMeterRegistry;
import io.prometheus.client.CollectorRegistry;
import org.springframework.beans.factory.ObjectProvider; import org.springframework.boot.actuate.autoconfigure.metrics.export.MetricsExporter;
import org.springframework.boot.actuate.metrics.CounterService; import org.springframework.boot.actuate.autoconfigure.web.ManagementContextConfiguration;
import org.springframework.boot.actuate.metrics.GaugeService; import org.springframework.boot.actuate.metrics.export.prometheus.PrometheusScrapeEndpoint;
import org.springframework.boot.actuate.metrics.dropwizard.DropwizardMetricServices;
import org.springframework.boot.actuate.metrics.dropwizard.ReservoirFactory;
import org.springframework.boot.actuate.metrics.reader.MetricReaderPublicMetrics;
import org.springframework.boot.actuate.metrics.reader.MetricRegistryMetricReader;
import org.springframework.boot.autoconfigure.AutoConfigureBefore;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; 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.Bean;
import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Configuration;
/** /**
* {@link EnableAutoConfiguration Auto-configuration} for Dropwizard-based metrics. * Configuration for exporting metrics to Prometheus.
* *
* @author Dave Syer
* @since 2.0.0 * @since 2.0.0
* @author Jon Schneider
*/ */
@Configuration @Configuration
@ConditionalOnClass(MetricRegistry.class) @ConditionalOnClass(PrometheusMeterRegistry.class)
@AutoConfigureBefore(MetricRepositoryAutoConfiguration.class) @EnableConfigurationProperties(PrometheusProperties.class)
public class MetricsDropwizardAutoConfiguration { public class PrometheusExportConfiguration {
private final ReservoirFactory reservoirFactory; @Bean
@ConditionalOnProperty(value = "metrics.prometheus.enabled", matchIfMissing = true)
public MetricsDropwizardAutoConfiguration( public MetricsExporter prometheusExporter(PrometheusConfig config,
ObjectProvider<ReservoirFactory> reservoirFactory) { CollectorRegistry collectorRegistry, Clock clock) {
this.reservoirFactory = reservoirFactory.getIfAvailable(); return () -> new PrometheusMeterRegistry(config, collectorRegistry, clock);
} }
@Bean @Bean
@ConditionalOnMissingBean @ConditionalOnMissingBean
public MetricRegistry metricRegistry() { public CollectorRegistry collectorRegistry() {
return new MetricRegistry(); return new CollectorRegistry(true);
} }
@Bean @Bean
@ConditionalOnMissingBean({ DropwizardMetricServices.class, CounterService.class, @ConditionalOnMissingBean
GaugeService.class }) public Clock clock() {
public DropwizardMetricServices dropwizardMetricServices( return Clock.SYSTEM;
MetricRegistry metricRegistry) {
if (this.reservoirFactory == null) {
return new DropwizardMetricServices(metricRegistry);
}
else {
return new DropwizardMetricServices(metricRegistry, this.reservoirFactory);
}
} }
@Bean @ManagementContextConfiguration
public MetricReaderPublicMetrics dropwizardPublicMetrics( public static class PrometheusScrapeEndpointConfiguration {
MetricRegistry metricRegistry) {
MetricRegistryMetricReader reader = new MetricRegistryMetricReader( @Bean
metricRegistry); public PrometheusScrapeEndpoint prometheusEndpoint(
return new MetricReaderPublicMetrics(reader); CollectorRegistry collectorRegistry) {
return new PrometheusScrapeEndpoint(collectorRegistry);
}
} }
} }
/*
* Copyright 2012-2017 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.metrics.export.prometheus;
import io.micrometer.prometheus.PrometheusConfig;
import org.springframework.boot.actuate.autoconfigure.metrics.export.RegistryProperties;
import org.springframework.boot.context.properties.ConfigurationProperties;
/**
* {@link ConfigurationProperties} for configuring metrics export to Prometheus.
*
* @author Jon Schneider
* @since 2.0.0
*/
@ConfigurationProperties(prefix = "metrics.prometheus")
public class PrometheusProperties extends RegistryProperties implements PrometheusConfig {
private boolean enabled = true;
public boolean isEnabled() {
return this.enabled;
}
public void setEnabled(boolean enabled) {
this.enabled = enabled;
}
public void setDescriptions(Boolean descriptions) {
set("descriptions", descriptions);
}
@Override
public String prefix() {
return "metrics.prometheus";
}
}
...@@ -14,39 +14,39 @@ ...@@ -14,39 +14,39 @@
* limitations under the License. * limitations under the License.
*/ */
package sample.metrics.opentsdb; package org.springframework.boot.actuate.autoconfigure.metrics.export.simple;
import org.springframework.boot.SpringApplication; import io.micrometer.core.instrument.Clock;
import org.springframework.boot.actuate.autoconfigure.metrics.ExportMetricWriter; import io.micrometer.core.instrument.simple.SimpleMeterRegistry;
import org.springframework.boot.actuate.metrics.opentsdb.DefaultOpenTsdbNamingStrategy;
import org.springframework.boot.actuate.metrics.opentsdb.OpenTsdbGaugeWriter; import org.springframework.boot.actuate.autoconfigure.metrics.export.MetricsExporter;
import org.springframework.boot.actuate.metrics.writer.GaugeWriter; import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.boot.context.properties.EnableConfigurationProperties; import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@SpringBootApplication /**
@EnableConfigurationProperties(HelloWorldProperties.class) * Configuration for exporting metrics to a {@link SimpleMeterRegistry}.
public class SampleOpenTsdbExportApplication { *
* @author Jon Schneider
* @since 2.0.0
*/
@Configuration
@EnableConfigurationProperties(SimpleProperties.class)
public class SimpleExportConfiguration {
@Bean @Bean
@ConfigurationProperties("metrics.export") @ConditionalOnProperty(value = "metrics.simple.enabled", matchIfMissing = true)
@ExportMetricWriter @ConditionalOnMissingBean(MetricsExporter.class)
public GaugeWriter openTsdbMetricWriter() { public MetricsExporter simpleExporter(Clock clock) {
OpenTsdbGaugeWriter writer = new OpenTsdbGaugeWriter(); return () -> new SimpleMeterRegistry(clock);
writer.setNamingStrategy(namingStrategy());
return writer;
} }
@Bean @Bean
@ConfigurationProperties("metrics.names") @ConditionalOnMissingBean
public DefaultOpenTsdbNamingStrategy namingStrategy() { public Clock clock() {
return new DefaultOpenTsdbNamingStrategy(); return Clock.SYSTEM;
}
public static void main(String[] args) throws Exception {
SpringApplication.run(SampleOpenTsdbExportApplication.class, args);
} }
} }
/* /*
* Copyright 2012-2016 the original author or authors. * Copyright 2012-2017 the original author or authors.
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
...@@ -14,21 +14,30 @@ ...@@ -14,21 +14,30 @@
* limitations under the License. * limitations under the License.
*/ */
package sample.metrics.opentsdb; package org.springframework.boot.actuate.autoconfigure.metrics.export.simple;
import io.micrometer.core.instrument.simple.SimpleMeterRegistry;
import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.boot.context.properties.ConfigurationProperties;
@ConfigurationProperties(prefix = "service", ignoreUnknownFields = false) /**
public class HelloWorldProperties { * {@link ConfigurationProperties} for configuring metrics export to a
* {@link SimpleMeterRegistry}.
*
* @author Jon Schneider
* @since 2.0.0
*/
@ConfigurationProperties(prefix = "metrics.simple")
public class SimpleProperties {
private String name = "World"; private boolean enabled = true;
public String getName() { public boolean isEnabled() {
return this.name; return this.enabled;
} }
public void setName(String name) { public void setEnabled(boolean enabled) {
this.name = name; this.enabled = enabled;
} }
} }
/*
* Copyright 2012-2017 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.metrics.reactive.server;
import io.micrometer.core.instrument.MeterRegistry;
import org.springframework.boot.actuate.autoconfigure.metrics.MetricsProperties;
import org.springframework.boot.actuate.metrics.web.reactive.server.DefaultWebFluxTagsProvider;
import org.springframework.boot.actuate.metrics.web.reactive.server.MetricsWebFilter;
import org.springframework.boot.actuate.metrics.web.reactive.server.WebFluxTagsProvider;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
/**
* Configures instrumentation of Spring Webflux MVC annotation-based programming model
* request mappings.
*
* @author Jon Schneider
* @since 2.0.0
*/
@ConditionalOnWebApplication(type = ConditionalOnWebApplication.Type.REACTIVE)
@Configuration
public class WebFluxMetricsConfiguration {
@Bean
@ConditionalOnMissingBean(WebFluxTagsProvider.class)
public DefaultWebFluxTagsProvider webfluxTagConfigurer() {
return new DefaultWebFluxTagsProvider();
}
@Bean
public MetricsWebFilter webfluxMetrics(MeterRegistry registry,
WebFluxTagsProvider tagConfigurer, MetricsProperties properties) {
return new MetricsWebFilter(registry, tagConfigurer,
properties.getWeb().getServer().getRequestsMetricName());
}
}
/*
* Copyright 2012-2017 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.metrics.web.client;
import io.micrometer.core.instrument.MeterRegistry;
import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.boot.actuate.autoconfigure.metrics.MetricsProperties;
import org.springframework.boot.actuate.metrics.web.client.DefaultRestTemplateExchangeTagsProvider;
import org.springframework.boot.actuate.metrics.web.client.MetricsRestTemplateCustomizer;
import org.springframework.boot.actuate.metrics.web.client.RestTemplateExchangeTagsProvider;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.client.RestTemplate;
/**
* Configuration for {@link RestTemplate}-related metrics.
*
* @author Jon Schneider
* @author Phillip Webb
* @since 2.0.0
*/
@Configuration
@ConditionalOnClass(name = "org.springframework.web.client.RestTemplate")
public class RestTemplateMetricsConfiguration {
@Bean
@ConditionalOnMissingBean(RestTemplateExchangeTagsProvider.class)
public DefaultRestTemplateExchangeTagsProvider restTemplateTagConfigurer() {
return new DefaultRestTemplateExchangeTagsProvider();
}
@Bean
public MetricsRestTemplateCustomizer metricsRestTemplateCustomizer(
MeterRegistry meterRegistry,
RestTemplateExchangeTagsProvider restTemplateTagConfigurer,
MetricsProperties properties) {
return new MetricsRestTemplateCustomizer(meterRegistry, restTemplateTagConfigurer,
properties.getWeb().getClient().getRequestsMetricName(),
properties.getWeb().getClient().isRecordRequestPercentiles());
}
@Bean
public static BeanPostProcessor restTemplateInterceptorPostProcessor(
ApplicationContext applicationContext) {
return new MetricsInterceptorPostProcessor(applicationContext);
}
/**
* {@link BeanPostProcessor} to apply {@link MetricsRestTemplateCustomizer} to any
* directly registered {@link RestTemplate} beans.
*/
private static class MetricsInterceptorPostProcessor implements BeanPostProcessor {
private final ApplicationContext applicationContext;
private MetricsRestTemplateCustomizer customizer;
MetricsInterceptorPostProcessor(ApplicationContext applicationContext) {
this.applicationContext = applicationContext;
}
@Override
public Object postProcessBeforeInitialization(Object bean, String beanName) {
return bean;
}
@Override
public Object postProcessAfterInitialization(Object bean, String beanName) {
if (bean instanceof RestTemplate) {
geCustomizer().customize((RestTemplate) bean);
}
return bean;
}
private MetricsRestTemplateCustomizer geCustomizer() {
if (this.customizer == null) {
this.customizer = this.applicationContext
.getBean(MetricsRestTemplateCustomizer.class);
}
return this.customizer;
}
}
}
/*
* Copyright 2012-2017 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.metrics.web.servlet;
import io.micrometer.core.instrument.MeterRegistry;
import org.springframework.boot.actuate.autoconfigure.metrics.MetricsProperties;
import org.springframework.boot.actuate.metrics.web.servlet.DefaultWebMvcTagsProvider;
import org.springframework.boot.actuate.metrics.web.servlet.MetricsHandlerInterceptor;
import org.springframework.boot.actuate.metrics.web.servlet.WebMvcMetrics;
import org.springframework.boot.actuate.metrics.web.servlet.WebMvcTagsProvider;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.DispatcherServlet;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
/**
* Configures instrumentation of Spring Web MVC servlet-based request mappings.
*
* @author Jon Schneider
* @since 2.0.0
*/
@Configuration
@ConditionalOnWebApplication(type = ConditionalOnWebApplication.Type.SERVLET)
@ConditionalOnClass(DispatcherServlet.class)
@EnableConfigurationProperties(MetricsProperties.class)
public class WebMvcMetricsConfiguration {
@Bean
@ConditionalOnMissingBean(WebMvcTagsProvider.class)
public DefaultWebMvcTagsProvider webmvcTagConfigurer() {
return new DefaultWebMvcTagsProvider();
}
@Bean
public WebMvcMetrics controllerMetrics(MeterRegistry registry,
MetricsProperties properties, WebMvcTagsProvider configurer) {
return new WebMvcMetrics(registry, configurer,
properties.getWeb().getServer().getRequestsMetricName(),
properties.getWeb().getServer().isAutoTimeRequests(),
properties.getWeb().getServer().isRecordRequestPercentiles());
}
@Bean
public MetricsHandlerInterceptor webMetricsInterceptor(
WebMvcMetrics controllerMetrics) {
return new MetricsHandlerInterceptor(controllerMetrics);
}
@Configuration
public class MetricsServletRequestInterceptorConfiguration
implements WebMvcConfigurer {
private final MetricsHandlerInterceptor handlerInterceptor;
public MetricsServletRequestInterceptorConfiguration(
MetricsHandlerInterceptor handlerInterceptor) {
this.handlerInterceptor = handlerInterceptor;
}
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(this.handlerInterceptor);
}
}
}
...@@ -39,33 +39,6 @@ ...@@ -39,33 +39,6 @@
"vcap_services" "vcap_services"
] ]
}, },
{
"name": "endpoints.metrics.filter.counter-submissions",
"type": "java.util.Set<org.springframework.boot.actuate.metrics.web.servlet.MetricsFilterSubmission>",
"description": "Submissions that should be made to the counter.",
"deprecation": {
"replacement": "management.metrics.filter.counter-submissions",
"level": "error"
}
},
{
"name": "endpoints.metrics.filter.enabled",
"type": "java.lang.Boolean",
"description": "Enable the metrics servlet filter.",
"deprecation": {
"replacement": "management.metrics.filter.enabled",
"level": "error"
}
},
{
"name": "endpoints.metrics.filter.gauge-submissions",
"type": "java.util.Set<org.springframework.boot.actuate.metrics.web.servlet.MetricsFilterSubmission>",
"description": "Submissions that should be made to the gauge.",
"deprecation": {
"replacement": "management.metrics.filter.gauge-submissions",
"level": "error"
}
},
{ {
"name": "endpoints.trace.filter.enabled", "name": "endpoints.trace.filter.enabled",
"type": "java.lang.Boolean", "type": "java.lang.Boolean",
...@@ -205,12 +178,6 @@ ...@@ -205,12 +178,6 @@
"name": "management.info.git.mode", "name": "management.info.git.mode",
"defaultValue": "simple" "defaultValue": "simple"
}, },
{
"name": "management.metrics.filter.enabled",
"type": "java.lang.Boolean",
"description": "Enable the metrics servlet filter.",
"defaultValue": true
},
{ {
"name": "management.trace.filter.enabled", "name": "management.trace.filter.enabled",
"type": "java.lang.Boolean", "type": "java.lang.Boolean",
...@@ -790,33 +757,6 @@ ...@@ -790,33 +757,6 @@
"level": "error" "level": "error"
} }
}, },
{
"name": "endpoints.metrics.id",
"type": "java.lang.String",
"description": "Endpoint identifier. With HTTP monitoring the identifier of the endpoint is mapped\n to a URL (e.g. 'foo' is mapped to '/foo').",
"deprecation": {
"reason": "Endpoint identifier is no longer customizable.",
"level": "error"
}
},
{
"name": "endpoints.metrics.path",
"type": "java.lang.String",
"description": "Endpoint URL path.",
"deprecation": {
"reason": "Endpoint path is no longer customizable.",
"level": "error"
}
},
{
"name": "endpoints.metrics.sensitive",
"type": "java.lang.Boolean",
"description": "Mark if the endpoint exposes sensitive information.",
"deprecation": {
"reason": "Endpoint sensitive flag is no longer customizable as Spring Boot no longer provides a customizable security auto-configuration\n. Create or adapt your security configuration accordingly.",
"level": "error"
}
},
{ {
"name": "endpoints.sensitive", "name": "endpoints.sensitive",
"type": "java.lang.Boolean", "type": "java.lang.Boolean",
......
...@@ -3,7 +3,6 @@ org.springframework.boot.actuate.autoconfigure.amqp.RabbitHealthIndicatorAutoCon ...@@ -3,7 +3,6 @@ org.springframework.boot.actuate.autoconfigure.amqp.RabbitHealthIndicatorAutoCon
org.springframework.boot.actuate.autoconfigure.audit.AuditAutoConfiguration,\ org.springframework.boot.actuate.autoconfigure.audit.AuditAutoConfiguration,\
org.springframework.boot.actuate.autoconfigure.audit.AuditEventsEndpointAutoConfiguration,\ org.springframework.boot.actuate.autoconfigure.audit.AuditEventsEndpointAutoConfiguration,\
org.springframework.boot.actuate.autoconfigure.beans.BeansEndpointAutoConfiguration,\ org.springframework.boot.actuate.autoconfigure.beans.BeansEndpointAutoConfiguration,\
org.springframework.boot.actuate.autoconfigure.cache.CacheStatisticsAutoConfiguration,\
org.springframework.boot.actuate.autoconfigure.cassandra.CassandraHealthIndicatorAutoConfiguration,\ org.springframework.boot.actuate.autoconfigure.cassandra.CassandraHealthIndicatorAutoConfiguration,\
org.springframework.boot.actuate.autoconfigure.cloudfoundry.CloudFoundryActuatorAutoConfiguration,\ org.springframework.boot.actuate.autoconfigure.cloudfoundry.CloudFoundryActuatorAutoConfiguration,\
org.springframework.boot.actuate.autoconfigure.condition.AutoConfigurationReportEndpointAutoConfiguration,\ org.springframework.boot.actuate.autoconfigure.condition.AutoConfigurationReportEndpointAutoConfiguration,\
...@@ -26,13 +25,7 @@ org.springframework.boot.actuate.autoconfigure.liquibase.LiquibaseEndpointAutoCo ...@@ -26,13 +25,7 @@ org.springframework.boot.actuate.autoconfigure.liquibase.LiquibaseEndpointAutoCo
org.springframework.boot.actuate.autoconfigure.logging.LoggersEndpointAutoConfiguration,\ org.springframework.boot.actuate.autoconfigure.logging.LoggersEndpointAutoConfiguration,\
org.springframework.boot.actuate.autoconfigure.mail.MailHealthIndicatorAutoConfiguration,\ org.springframework.boot.actuate.autoconfigure.mail.MailHealthIndicatorAutoConfiguration,\
org.springframework.boot.actuate.autoconfigure.management.ThreadDumpEndpointAutoConfiguration,\ org.springframework.boot.actuate.autoconfigure.management.ThreadDumpEndpointAutoConfiguration,\
org.springframework.boot.actuate.autoconfigure.metrics.MetricExportAutoConfiguration,\ org.springframework.boot.actuate.autoconfigure.metrics.MetricsAutoConfiguration,\
org.springframework.boot.actuate.autoconfigure.metrics.MetricFilterAutoConfiguration,\
org.springframework.boot.actuate.autoconfigure.metrics.MetricRepositoryAutoConfiguration,\
org.springframework.boot.actuate.autoconfigure.metrics.MetricsChannelAutoConfiguration,\
org.springframework.boot.actuate.autoconfigure.metrics.MetricsDropwizardAutoConfiguration,\
org.springframework.boot.actuate.autoconfigure.metrics.MetricsEndpointAutoConfiguration,\
org.springframework.boot.actuate.autoconfigure.metrics.PublicMetricsAutoConfiguration,\
org.springframework.boot.actuate.autoconfigure.mongo.MongoHealthIndicatorAutoConfiguration,\ org.springframework.boot.actuate.autoconfigure.mongo.MongoHealthIndicatorAutoConfiguration,\
org.springframework.boot.actuate.autoconfigure.neo4j.Neo4jHealthIndicatorAutoConfiguration,\ org.springframework.boot.actuate.autoconfigure.neo4j.Neo4jHealthIndicatorAutoConfiguration,\
org.springframework.boot.actuate.autoconfigure.redis.RedisHealthIndicatorAutoConfiguration,\ org.springframework.boot.actuate.autoconfigure.redis.RedisHealthIndicatorAutoConfiguration,\
......
...@@ -28,7 +28,6 @@ import org.springframework.boot.actuate.autoconfigure.env.EnvironmentEndpointAut ...@@ -28,7 +28,6 @@ import org.springframework.boot.actuate.autoconfigure.env.EnvironmentEndpointAut
import org.springframework.boot.actuate.autoconfigure.health.HealthEndpointAutoConfiguration; import org.springframework.boot.actuate.autoconfigure.health.HealthEndpointAutoConfiguration;
import org.springframework.boot.actuate.autoconfigure.info.InfoEndpointAutoConfiguration; import org.springframework.boot.actuate.autoconfigure.info.InfoEndpointAutoConfiguration;
import org.springframework.boot.actuate.autoconfigure.management.ThreadDumpEndpointAutoConfiguration; import org.springframework.boot.actuate.autoconfigure.management.ThreadDumpEndpointAutoConfiguration;
import org.springframework.boot.actuate.autoconfigure.metrics.MetricsEndpointAutoConfiguration;
import org.springframework.boot.actuate.autoconfigure.trace.TraceEndpointAutoConfiguration; import org.springframework.boot.actuate.autoconfigure.trace.TraceEndpointAutoConfiguration;
import org.springframework.boot.actuate.autoconfigure.web.servlet.RequestMappingEndpointAutoConfiguration; import org.springframework.boot.actuate.autoconfigure.web.servlet.RequestMappingEndpointAutoConfiguration;
...@@ -50,7 +49,6 @@ final class EndpointAutoConfigurationClasses { ...@@ -50,7 +49,6 @@ final class EndpointAutoConfigurationClasses {
all.add(HealthEndpointAutoConfiguration.class); all.add(HealthEndpointAutoConfiguration.class);
all.add(InfoEndpointAutoConfiguration.class); all.add(InfoEndpointAutoConfiguration.class);
all.add(ThreadDumpEndpointAutoConfiguration.class); all.add(ThreadDumpEndpointAutoConfiguration.class);
all.add(MetricsEndpointAutoConfiguration.class);
all.add(TraceEndpointAutoConfiguration.class); all.add(TraceEndpointAutoConfiguration.class);
all.add(RequestMappingEndpointAutoConfiguration.class); all.add(RequestMappingEndpointAutoConfiguration.class);
ALL = all.toArray(new Class<?>[] {}); ALL = all.toArray(new Class<?>[] {});
......
...@@ -55,7 +55,7 @@ public class JmxEndpointIntegrationTests { ...@@ -55,7 +55,7 @@ public class JmxEndpointIntegrationTests {
MBeanServer mBeanServer = context.getBean(MBeanServer.class); MBeanServer mBeanServer = context.getBean(MBeanServer.class);
checkEndpointMBeans(mBeanServer, checkEndpointMBeans(mBeanServer,
new String[] { "autoconfig", "beans", "configprops", "env", "health", new String[] { "autoconfig", "beans", "configprops", "env", "health",
"info", "mappings", "metrics", "status", "threaddump", "info", "mappings", "status", "threaddump",
"trace" }, "trace" },
new String[] { "shutdown" }); new String[] { "shutdown" });
}); });
...@@ -68,7 +68,7 @@ public class JmxEndpointIntegrationTests { ...@@ -68,7 +68,7 @@ public class JmxEndpointIntegrationTests {
MBeanServer mBeanServer = context.getBean(MBeanServer.class); MBeanServer mBeanServer = context.getBean(MBeanServer.class);
checkEndpointMBeans(mBeanServer, new String[0], checkEndpointMBeans(mBeanServer, new String[0],
new String[] { "autoconfig", "beans", "configprops", "env", new String[] { "autoconfig", "beans", "configprops", "env",
"health", "mappings", "metrics", "shutdown", "health", "mappings", "shutdown",
"threaddump", "trace" }); "threaddump", "trace" });
}); });
...@@ -81,7 +81,7 @@ public class JmxEndpointIntegrationTests { ...@@ -81,7 +81,7 @@ public class JmxEndpointIntegrationTests {
MBeanServer mBeanServer = context.getBean(MBeanServer.class); MBeanServer mBeanServer = context.getBean(MBeanServer.class);
checkEndpointMBeans(mBeanServer, new String[] { "beans" }, checkEndpointMBeans(mBeanServer, new String[] { "beans" },
new String[] { "autoconfig", "configprops", "env", "health", new String[] { "autoconfig", "configprops", "env", "health",
"mappings", "metrics", "shutdown", "mappings", "shutdown",
"threaddump", "trace" }); "threaddump", "trace" });
}); });
} }
......
...@@ -71,7 +71,6 @@ public class WebMvcEndpointExposureIntegrationTests { ...@@ -71,7 +71,6 @@ public class WebMvcEndpointExposureIntegrationTests {
assertThat(isExposed(mvc, HttpMethod.GET, "health")).isFalse(); assertThat(isExposed(mvc, HttpMethod.GET, "health")).isFalse();
assertThat(isExposed(mvc, HttpMethod.GET, "info")).isTrue(); assertThat(isExposed(mvc, HttpMethod.GET, "info")).isTrue();
assertThat(isExposed(mvc, HttpMethod.GET, "mappings")).isFalse(); assertThat(isExposed(mvc, HttpMethod.GET, "mappings")).isFalse();
assertThat(isExposed(mvc, HttpMethod.GET, "metrics")).isFalse();
assertThat(isExposed(mvc, HttpMethod.POST, "shutdown")).isFalse(); assertThat(isExposed(mvc, HttpMethod.POST, "shutdown")).isFalse();
assertThat(isExposed(mvc, HttpMethod.GET, "status")).isTrue(); assertThat(isExposed(mvc, HttpMethod.GET, "status")).isTrue();
assertThat(isExposed(mvc, HttpMethod.GET, "threaddump")).isFalse(); assertThat(isExposed(mvc, HttpMethod.GET, "threaddump")).isFalse();
...@@ -92,7 +91,6 @@ public class WebMvcEndpointExposureIntegrationTests { ...@@ -92,7 +91,6 @@ public class WebMvcEndpointExposureIntegrationTests {
assertThat(isExposed(mvc, HttpMethod.GET, "health")).isTrue(); assertThat(isExposed(mvc, HttpMethod.GET, "health")).isTrue();
assertThat(isExposed(mvc, HttpMethod.GET, "info")).isTrue(); assertThat(isExposed(mvc, HttpMethod.GET, "info")).isTrue();
assertThat(isExposed(mvc, HttpMethod.GET, "mappings")).isTrue(); assertThat(isExposed(mvc, HttpMethod.GET, "mappings")).isTrue();
assertThat(isExposed(mvc, HttpMethod.GET, "metrics")).isTrue();
assertThat(isExposed(mvc, HttpMethod.POST, "shutdown")).isFalse(); assertThat(isExposed(mvc, HttpMethod.POST, "shutdown")).isFalse();
assertThat(isExposed(mvc, HttpMethod.GET, "status")).isTrue(); assertThat(isExposed(mvc, HttpMethod.GET, "status")).isTrue();
assertThat(isExposed(mvc, HttpMethod.GET, "threaddump")).isTrue(); assertThat(isExposed(mvc, HttpMethod.GET, "threaddump")).isTrue();
...@@ -114,7 +112,6 @@ public class WebMvcEndpointExposureIntegrationTests { ...@@ -114,7 +112,6 @@ public class WebMvcEndpointExposureIntegrationTests {
assertThat(isExposed(mvc, HttpMethod.GET, "health")).isFalse(); assertThat(isExposed(mvc, HttpMethod.GET, "health")).isFalse();
assertThat(isExposed(mvc, HttpMethod.GET, "info")).isTrue(); assertThat(isExposed(mvc, HttpMethod.GET, "info")).isTrue();
assertThat(isExposed(mvc, HttpMethod.GET, "mappings")).isFalse(); assertThat(isExposed(mvc, HttpMethod.GET, "mappings")).isFalse();
assertThat(isExposed(mvc, HttpMethod.GET, "metrics")).isFalse();
assertThat(isExposed(mvc, HttpMethod.POST, "shutdown")).isFalse(); assertThat(isExposed(mvc, HttpMethod.POST, "shutdown")).isFalse();
assertThat(isExposed(mvc, HttpMethod.GET, "status")).isTrue(); assertThat(isExposed(mvc, HttpMethod.GET, "status")).isTrue();
assertThat(isExposed(mvc, HttpMethod.GET, "threaddump")).isFalse(); assertThat(isExposed(mvc, HttpMethod.GET, "threaddump")).isFalse();
......
...@@ -14,51 +14,43 @@ ...@@ -14,51 +14,43 @@
* limitations under the License. * limitations under the License.
*/ */
package org.springframework.boot.actuate.metrics; package org.springframework.boot.actuate.autoconfigure.metrics;
import java.util.Iterator;
import io.micrometer.core.instrument.MeterRegistry;
import org.junit.Test; import org.junit.Test;
import org.springframework.boot.web.embedded.tomcat.TomcatServletWebServerFactory; import org.springframework.boot.autoconfigure.AutoConfigurations;
import org.springframework.boot.web.servlet.context.AnnotationConfigServletWebServerApplicationContext; import org.springframework.boot.context.annotation.UserConfigurations;
import org.springframework.boot.test.context.runner.ApplicationContextRunner;
import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThat;
/** /**
* Tests for {@link TomcatPublicMetrics} * Tests for applying {@link MeterRegistryConfigurer MeterRegistryConfigurers}.
* *
* @author Johannes Edmeier * @author Jon Schneider
* @author Phillip Webb * @author Andy Wilkinson
*/ */
public class TomcatPublicMetricsTests { public class MeterRegistryConfigurerTests {
@Test @Test
public void tomcatMetrics() throws Exception { public void commonTagsAreAppliedToAutoConfiguredBinders() {
try (AnnotationConfigServletWebServerApplicationContext context = new AnnotationConfigServletWebServerApplicationContext( new ApplicationContextRunner()
Config.class)) { .withConfiguration(AutoConfigurations.of(MetricsAutoConfiguration.class))
TomcatPublicMetrics tomcatMetrics = context .withConfiguration(
.getBean(TomcatPublicMetrics.class); UserConfigurations.of(MeterRegistryConfigurerConfiguration.class))
Iterator<Metric<?>> metrics = tomcatMetrics.metrics().iterator(); .withPropertyValues("metrics.use-global-registry=false")
assertThat(metrics.next().getName()).isEqualTo("httpsessions.max"); .run((context) -> assertThat(context.getBean(MeterRegistry.class)
assertThat(metrics.next().getName()).isEqualTo("httpsessions.active"); .find("jvm.memory.used").tags("region", "us-east-1").gauge())
assertThat(metrics.hasNext()).isFalse(); .isPresent());
}
} }
@Configuration static class MeterRegistryConfigurerConfiguration {
static class Config {
@Bean
public TomcatServletWebServerFactory webServerFactory() {
return new TomcatServletWebServerFactory(0);
}
@Bean @Bean
public TomcatPublicMetrics metrics() { public MeterRegistryConfigurer registryConfigurer() {
return new TomcatPublicMetrics(); return (registry) -> registry.config().commonTags("region", "us-east-1");
} }
} }
......
/*
* Copyright 2012-2017 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.metrics;
import java.util.Map;
import org.junit.After;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.ExpectedException;
import org.mockito.Mockito;
import org.springframework.beans.factory.NoSuchBeanDefinitionException;
import org.springframework.boot.actuate.metrics.GaugeService;
import org.springframework.boot.actuate.metrics.Metric;
import org.springframework.boot.actuate.metrics.export.MetricCopyExporter;
import org.springframework.boot.actuate.metrics.export.MetricExporters;
import org.springframework.boot.actuate.metrics.reader.MetricsEndpointMetricReader;
import org.springframework.boot.actuate.metrics.statsd.StatsdMetricWriter;
import org.springframework.boot.actuate.metrics.writer.GaugeWriter;
import org.springframework.boot.actuate.metrics.writer.MetricWriter;
import org.springframework.boot.autoconfigure.context.PropertyPlaceholderAutoConfiguration;
import org.springframework.boot.test.util.TestPropertyValues;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.integration.channel.FixedSubscriberChannel;
import org.springframework.messaging.SubscribableChannel;
import org.springframework.scheduling.annotation.SchedulingConfigurer;
import org.springframework.test.util.ReflectionTestUtils;
import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.atLeastOnce;
import static org.mockito.Mockito.verify;
/**
* Tests for {@link MetricExportAutoConfiguration}.
*
* @author Phillip Webb
* @author Dave Syer
* @author Simon Buettner
*/
public class MetricExportAutoConfigurationTests {
@Rule
public ExpectedException thrown = ExpectedException.none();
private AnnotationConfigApplicationContext context;
@After
public void after() {
if (this.context != null) {
this.context.close();
}
}
@Test
public void metricsFlushAutomatically() throws Exception {
this.context = new AnnotationConfigApplicationContext(WriterConfig.class,
MetricRepositoryAutoConfiguration.class,
MetricExportAutoConfiguration.class,
PropertyPlaceholderAutoConfiguration.class);
GaugeService gaugeService = this.context.getBean(GaugeService.class);
assertThat(gaugeService).isNotNull();
gaugeService.submit("foo", 2.7);
MetricExporters flusher = this.context.getBean(MetricExporters.class);
flusher.close(); // this will be called by Spring on shutdown
MetricWriter writer = this.context.getBean("writer", MetricWriter.class);
verify(writer, atLeastOnce()).set(any(Metric.class));
}
@Test
public void defaultExporterWhenMessageChannelAvailable() throws Exception {
this.context = new AnnotationConfigApplicationContext(
MessageChannelConfiguration.class,
MetricRepositoryAutoConfiguration.class,
MetricsChannelAutoConfiguration.class,
MetricExportAutoConfiguration.class,
PropertyPlaceholderAutoConfiguration.class);
MetricExporters exporter = this.context.getBean(MetricExporters.class);
assertThat(exporter).isNotNull();
assertThat(exporter.getExporters()).containsKey("messageChannelMetricWriter");
}
@Test
public void provideAdditionalWriter() {
this.context = new AnnotationConfigApplicationContext(WriterConfig.class,
MetricRepositoryAutoConfiguration.class,
MetricExportAutoConfiguration.class,
PropertyPlaceholderAutoConfiguration.class);
GaugeService gaugeService = this.context.getBean(GaugeService.class);
assertThat(gaugeService).isNotNull();
gaugeService.submit("foo", 2.7);
MetricExporters exporters = this.context.getBean(MetricExporters.class);
MetricCopyExporter exporter = (MetricCopyExporter) exporters.getExporters()
.get("writer");
exporter.setIgnoreTimestamps(true);
exporter.export();
MetricWriter writer = this.context.getBean("writer", MetricWriter.class);
Mockito.verify(writer, Mockito.atLeastOnce()).set(any(Metric.class));
}
@Test
public void exportMetricsEndpoint() {
this.context = new AnnotationConfigApplicationContext(WriterConfig.class,
MetricEndpointConfiguration.class, MetricExportAutoConfiguration.class,
PropertyPlaceholderAutoConfiguration.class);
MetricExporters exporters = this.context.getBean(MetricExporters.class);
MetricCopyExporter exporter = (MetricCopyExporter) exporters.getExporters()
.get("writer");
exporter.setIgnoreTimestamps(true);
exporter.export();
MetricsEndpointMetricReader reader = this.context.getBean("endpointReader",
MetricsEndpointMetricReader.class);
Mockito.verify(reader, Mockito.atLeastOnce()).findAll();
}
@Test
public void statsdMissingHost() throws Exception {
this.context = new AnnotationConfigApplicationContext();
this.context.register(WriterConfig.class, MetricEndpointConfiguration.class,
MetricExportAutoConfiguration.class,
PropertyPlaceholderAutoConfiguration.class);
this.context.refresh();
this.thrown.expect(NoSuchBeanDefinitionException.class);
this.context.getBean(StatsdMetricWriter.class);
}
@SuppressWarnings("unchecked")
@Test
public void statsdWithHost() throws Exception {
this.context = new AnnotationConfigApplicationContext();
TestPropertyValues.of("spring.metrics.export.statsd.host=localhost")
.applyTo(this.context);
this.context.register(MetricEndpointConfiguration.class,
MetricExportAutoConfiguration.class,
PropertyPlaceholderAutoConfiguration.class);
this.context.refresh();
StatsdMetricWriter statsdWriter = this.context.getBean(StatsdMetricWriter.class);
assertThat(statsdWriter).isNotNull();
SchedulingConfigurer schedulingConfigurer = this.context
.getBean(SchedulingConfigurer.class);
Map<String, GaugeWriter> exporters = (Map<String, GaugeWriter>) ReflectionTestUtils
.getField(schedulingConfigurer, "writers");
assertThat(exporters).containsValue(statsdWriter);
}
@Configuration
public static class MessageChannelConfiguration {
@Bean
public SubscribableChannel metricsChannel() {
return new FixedSubscriberChannel((message) -> {
});
}
}
@Configuration
public static class WriterConfig {
@Bean
@ExportMetricWriter
public MetricWriter writer() {
return Mockito.mock(MetricWriter.class);
}
}
@Configuration
public static class MetricEndpointConfiguration {
@Bean
@ExportMetricReader
public MetricsEndpointMetricReader endpointReader() {
return Mockito.mock(MetricsEndpointMetricReader.class);
}
}
}
/*
* Copyright 2012-2017 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.metrics;
import com.codahale.metrics.Gauge;
import com.codahale.metrics.MetricRegistry;
import org.junit.After;
import org.junit.Test;
import org.springframework.boot.actuate.metrics.CounterService;
import org.springframework.boot.actuate.metrics.GaugeService;
import org.springframework.boot.actuate.metrics.buffer.BufferCounterService;
import org.springframework.boot.actuate.metrics.buffer.BufferGaugeService;
import org.springframework.boot.actuate.metrics.dropwizard.DropwizardMetricServices;
import org.springframework.boot.actuate.metrics.reader.MetricReader;
import org.springframework.boot.actuate.metrics.reader.PrefixMetricReader;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.Mockito.mock;
/**
* Tests for {@link MetricRepositoryAutoConfiguration}.
*
* @author Phillip Webb
* @author Dave Syer
*/
public class MetricRepositoryAutoConfigurationTests {
private AnnotationConfigApplicationContext context;
@After
public void after() {
if (this.context != null) {
this.context.close();
}
}
@Test
public void createServices() throws Exception {
this.context = new AnnotationConfigApplicationContext(
MetricRepositoryAutoConfiguration.class);
GaugeService gaugeService = this.context.getBean(BufferGaugeService.class);
assertThat(gaugeService).isNotNull();
assertThat(this.context.getBean(BufferCounterService.class)).isNotNull();
assertThat(this.context.getBean(PrefixMetricReader.class)).isNotNull();
gaugeService.submit("foo", 2.7);
MetricReader bean = this.context.getBean(MetricReader.class);
assertThat(bean.findOne("gauge.foo").getValue()).isEqualTo(2.7);
}
@Test
public void dropwizardInstalledIfPresent() {
this.context = new AnnotationConfigApplicationContext(
MetricsDropwizardAutoConfiguration.class,
MetricRepositoryAutoConfiguration.class);
GaugeService gaugeService = this.context.getBean(GaugeService.class);
assertThat(gaugeService).isNotNull();
gaugeService.submit("foo", 2.7);
DropwizardMetricServices exporter = this.context
.getBean(DropwizardMetricServices.class);
assertThat(exporter).isEqualTo(gaugeService);
MetricRegistry registry = this.context.getBean(MetricRegistry.class);
@SuppressWarnings("unchecked")
Gauge<Double> gauge = (Gauge<Double>) registry.getMetrics().get("gauge.foo");
assertThat(gauge.getValue()).isEqualTo(new Double(2.7));
}
@Test
public void skipsIfBeansExist() throws Exception {
this.context = new AnnotationConfigApplicationContext(Config.class,
MetricRepositoryAutoConfiguration.class);
assertThat(this.context.getBeansOfType(BufferGaugeService.class)).isEmpty();
assertThat(this.context.getBeansOfType(BufferCounterService.class)).isEmpty();
}
@Configuration
public static class Config {
@Bean
public GaugeService gaugeService() {
return mock(GaugeService.class);
}
@Bean
public CounterService counterService() {
return mock(CounterService.class);
}
}
}
/*
* Copyright 2012-2017 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.metrics;
import java.util.Collections;
import java.util.Map;
import java.util.Set;
import io.micrometer.core.instrument.MeterRegistry;
import io.micrometer.core.instrument.Statistic;
import io.micrometer.core.instrument.binder.JvmMemoryMetrics;
import io.micrometer.core.instrument.binder.LogbackMetrics;
import io.micrometer.core.instrument.binder.MeterBinder;
import io.micrometer.core.instrument.simple.SimpleMeterRegistry;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.ImportAutoConfiguration;
import org.springframework.boot.autoconfigure.http.HttpMessageConvertersAutoConfiguration;
import org.springframework.boot.autoconfigure.jackson.JacksonAutoConfiguration;
import org.springframework.boot.autoconfigure.web.servlet.DispatcherServletAutoConfiguration;
import org.springframework.boot.autoconfigure.web.servlet.ServletWebServerFactoryAutoConfiguration;
import org.springframework.boot.autoconfigure.web.servlet.WebMvcAutoConfiguration;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.test.context.SpringBootTest.WebEnvironment;
import org.springframework.boot.test.web.client.TestRestTemplate;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
import org.springframework.http.HttpMethod;
import org.springframework.http.MediaType;
import org.springframework.test.context.TestPropertySource;
import org.springframework.test.context.junit4.SpringRunner;
import org.springframework.test.web.client.MockRestServiceServer;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.RestTemplate;
import static org.assertj.core.api.Assertions.assertThat;
import static org.springframework.test.web.client.ExpectedCount.once;
import static org.springframework.test.web.client.match.MockRestRequestMatchers.method;
import static org.springframework.test.web.client.match.MockRestRequestMatchers.requestTo;
import static org.springframework.test.web.client.response.MockRestResponseCreators.withSuccess;
/**
* Tests for {@link MetricsAutoConfiguration}.
*
* @author Jon Schneider
*/
@RunWith(SpringRunner.class)
@SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT, classes = MetricsAutoConfigurationTests.MetricsApp.class)
@TestPropertySource(properties = "metrics.use-global-registry=false")
public class MetricsAutoConfigurationTests {
@Autowired
private ApplicationContext context;
@Autowired
private RestTemplate external;
@Autowired
private TestRestTemplate loopback;
@Autowired
private MeterRegistry registry;
@SuppressWarnings("unchecked")
@Test
public void restTemplateIsInstrumented() {
MockRestServiceServer server = MockRestServiceServer.bindTo(this.external)
.build();
server.expect(once(), requestTo("/api/external"))
.andExpect(method(HttpMethod.GET)).andRespond(withSuccess(
"{\"message\": \"hello\"}", MediaType.APPLICATION_JSON));
assertThat(this.external.getForObject("/api/external", Map.class))
.containsKey("message");
assertThat(this.registry.find("http.client.requests").value(Statistic.Count, 1.0)
.timer()).isPresent();
}
@Test
public void requestMappingIsInstrumented() {
this.loopback.getForObject("/api/people", Set.class);
assertThat(this.registry.find("http.server.requests").value(Statistic.Count, 1.0)
.timer()).isPresent();
}
@Test
public void automaticallyRegisteredBinders() {
assertThat(this.context.getBeansOfType(MeterBinder.class).values())
.hasAtLeastOneElementOfType(LogbackMetrics.class)
.hasAtLeastOneElementOfType(JvmMemoryMetrics.class);
}
@Configuration
@ImportAutoConfiguration({ MetricsAutoConfiguration.class,
JacksonAutoConfiguration.class, HttpMessageConvertersAutoConfiguration.class,
WebMvcAutoConfiguration.class, DispatcherServletAutoConfiguration.class,
ServletWebServerFactoryAutoConfiguration.class })
@Import(PersonController.class)
static class MetricsApp {
@Bean
public MeterRegistry registry() {
return new SimpleMeterRegistry();
}
@Bean
public RestTemplate restTemplate() {
return new RestTemplate();
}
}
@RestController
static class PersonController {
@GetMapping("/api/people")
Set<String> personName() {
return Collections.singleton("Jon");
}
}
}
...@@ -16,36 +16,32 @@ ...@@ -16,36 +16,32 @@
package org.springframework.boot.actuate.autoconfigure.metrics; package org.springframework.boot.actuate.autoconfigure.metrics;
import io.micrometer.core.instrument.composite.CompositeMeterRegistry;
import io.micrometer.graphite.GraphiteMeterRegistry;
import io.micrometer.prometheus.PrometheusMeterRegistry;
import org.junit.Test; import org.junit.Test;
import org.springframework.boot.actuate.metrics.MetricsEndpoint;
import org.springframework.boot.autoconfigure.AutoConfigurations; import org.springframework.boot.autoconfigure.AutoConfigurations;
import org.springframework.boot.test.context.runner.ApplicationContextRunner; import org.springframework.boot.test.context.runner.ApplicationContextRunner;
import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThat;
/** /**
* Tests for {@link MetricsEndpointAutoConfiguration}. * Tests for {@link MetricsAutoConfiguration} creating a {@link CompositeMeterRegistry}.
* *
* @author Phillip Webb * @author Jon Schneider
*/ */
public class MetricsEndpointAutoConfigurationTests { public class MetricsConfigurationCompositeTests {
private final ApplicationContextRunner contextRunner = new ApplicationContextRunner()
.withConfiguration(
AutoConfigurations.of(MetricsEndpointAutoConfiguration.class));
@Test
public void runShouldHaveEndpointBean() {
this.contextRunner.run(
(context) -> assertThat(context).hasSingleBean(MetricsEndpoint.class));
}
@Test @Test
public void runWhenEnabledPropertyIsFalseShouldNotHaveEndpointBean() public void compositeContainsImplementationsOnClasspath() {
throws Exception { new ApplicationContextRunner()
this.contextRunner.withPropertyValues("endpoints.metrics.enabled:false").run( .withConfiguration(AutoConfigurations.of(MetricsAutoConfiguration.class))
(context) -> assertThat(context).doesNotHaveBean(MetricsEndpoint.class)); .withPropertyValues("metrics.use-global-registry=false")
.run((context) -> assertThat(
context.getBean(CompositeMeterRegistry.class).getRegistries())
.hasAtLeastOneElementOfType(PrometheusMeterRegistry.class)
.hasAtLeastOneElementOfType(GraphiteMeterRegistry.class));
} }
} }
/*
* Copyright 2012-2017 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.metrics;
import com.codahale.metrics.Reservoir;
import com.codahale.metrics.UniformReservoir;
import org.junit.After;
import org.junit.Test;
import org.springframework.boot.actuate.metrics.dropwizard.DropwizardMetricServices;
import org.springframework.boot.actuate.metrics.dropwizard.ReservoirFactory;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.test.util.ReflectionTestUtils;
import static org.assertj.core.api.Assertions.assertThat;
/**
* Tests for {@link MetricsDropwizardAutoConfiguration}.
*
* @author Lucas Saldanha
*/
public class MetricsDropwizardAutoConfigurationTests {
private AnnotationConfigApplicationContext context;
@After
public void after() {
if (this.context != null) {
this.context.close();
}
}
@Test
public void dropwizardWithoutCustomReservoirConfigured() {
this.context = new AnnotationConfigApplicationContext(
MetricsDropwizardAutoConfiguration.class);
DropwizardMetricServices dropwizardMetricServices = this.context
.getBean(DropwizardMetricServices.class);
ReservoirFactory reservoirFactory = (ReservoirFactory) ReflectionTestUtils
.getField(dropwizardMetricServices, "reservoirFactory");
assertThat(reservoirFactory.getReservoir("test")).isNull();
}
@Test
public void dropwizardWithCustomReservoirConfigured() {
this.context = new AnnotationConfigApplicationContext(
MetricsDropwizardAutoConfiguration.class, Config.class);
DropwizardMetricServices dropwizardMetricServices = this.context
.getBean(DropwizardMetricServices.class);
ReservoirFactory reservoirFactory = (ReservoirFactory) ReflectionTestUtils
.getField(dropwizardMetricServices, "reservoirFactory");
assertThat(reservoirFactory.getReservoir("test"))
.isInstanceOf(UniformReservoir.class);
}
@Configuration
static class Config {
@Bean
public ReservoirFactory reservoirFactory() {
return new UniformReservoirFactory();
}
}
private static class UniformReservoirFactory implements ReservoirFactory {
@Override
public Reservoir getReservoir(String name) {
return new UniformReservoir();
}
}
}
...@@ -14,47 +14,43 @@ ...@@ -14,47 +14,43 @@
* limitations under the License. * limitations under the License.
*/ */
package org.springframework.boot.actuate.metrics.integration; package org.springframework.boot.actuate.autoconfigure.metrics.export.simple;
import io.micrometer.core.instrument.composite.CompositeMeterRegistry;
import io.micrometer.core.instrument.simple.SimpleMeterRegistry;
import org.junit.Test; import org.junit.Test;
import org.junit.runner.RunWith; import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.actuate.autoconfigure.metrics.MetricsAutoConfiguration;
import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.boot.autoconfigure.AutoConfigurations;
import org.springframework.boot.actuate.autoconfigure.metrics.PublicMetricsAutoConfiguration; import org.springframework.boot.test.context.runner.ApplicationContextRunner;
import org.springframework.boot.actuate.metrics.reader.MetricReaderPublicMetrics;
import org.springframework.boot.autoconfigure.integration.IntegrationAutoConfiguration;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
import org.springframework.test.annotation.DirtiesContext;
import org.springframework.test.context.junit4.SpringRunner; import org.springframework.test.context.junit4.SpringRunner;
import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThat;
/** /**
* Tests for {@link SpringIntegrationMetricReader}. * Tests for {@link SimpleExportConfiguration}.
* *
* @author Artem Bilan * @author Jon Schneider
*/ */
@RunWith(SpringRunner.class) @RunWith(SpringRunner.class)
@SpringBootTest("spring.jmx.enabled=false") public class SimpleExportConfigurationTests {
@DirtiesContext
public class SpringIntegrationMetricReaderNoJmxTests {
@Autowired
@Qualifier("springIntegrationPublicMetrics")
private MetricReaderPublicMetrics integrationMetricReader;
@Test @Test
public void test() { public void simpleMeterRegistryIsInTheCompositeWhenNoOtherRegistryIs() {
assertThat(this.integrationMetricReader.metrics().size() > 0).isTrue(); new ApplicationContextRunner()
} .withPropertyValues("metrics.atlas.enabled=false",
"metrics.datadog.enabled=false", "metrics.ganglia.enabled=false",
@Configuration "metrics.graphite.enabled=false", "metrics.influx.enabled=false",
@Import({ IntegrationAutoConfiguration.class, PublicMetricsAutoConfiguration.class }) "metrics.jmx.enabled=false", "metrics.prometheus.enabled=false")
protected static class TestConfiguration { .withConfiguration(AutoConfigurations.of(MetricsAutoConfiguration.class))
.run((context) -> {
CompositeMeterRegistry meterRegistry = context
.getBean(CompositeMeterRegistry.class);
assertThat(meterRegistry.getRegistries()).hasSize(1);
assertThat(meterRegistry.getRegistries())
.hasOnlyElementsOfType(SimpleMeterRegistry.class);
});
} }
} }
...@@ -32,33 +32,28 @@ ...@@ -32,33 +32,28 @@
<optional>true</optional> <optional>true</optional>
</dependency> </dependency>
<dependency> <dependency>
<groupId>com.github.ben-manes.caffeine</groupId> <groupId>com.sun.mail</groupId>
<artifactId>caffeine</artifactId> <artifactId>javax.mail</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>com.hazelcast</groupId>
<artifactId>hazelcast</artifactId>
<optional>true</optional> <optional>true</optional>
</dependency> </dependency>
<dependency> <dependency>
<groupId>com.hazelcast</groupId> <groupId>com.zaxxer</groupId>
<artifactId>hazelcast-spring</artifactId> <artifactId>HikariCP</artifactId>
<optional>true</optional> <optional>true</optional>
</dependency> </dependency>
<dependency> <dependency>
<groupId>com.sun.mail</groupId> <groupId>io.lettuce</groupId>
<artifactId>javax.mail</artifactId> <artifactId>lettuce-core</artifactId>
<optional>true</optional> <optional>true</optional>
</dependency> </dependency>
<dependency> <dependency>
<groupId>com.timgroup</groupId> <groupId>io.micrometer</groupId>
<artifactId>java-statsd-client</artifactId> <artifactId>micrometer-core</artifactId>
<optional>true</optional> <optional>true</optional>
</dependency> </dependency>
<dependency> <dependency>
<groupId>io.lettuce</groupId> <groupId>io.micrometer</groupId>
<artifactId>lettuce-core</artifactId> <artifactId>micrometer-prometheus-starter</artifactId>
<optional>true</optional> <optional>true</optional>
</dependency> </dependency>
<dependency> <dependency>
...@@ -72,13 +67,13 @@ ...@@ -72,13 +67,13 @@
<optional>true</optional> <optional>true</optional>
</dependency> </dependency>
<dependency> <dependency>
<groupId>net.sf.ehcache</groupId> <groupId>org.apache.tomcat.embed</groupId>
<artifactId>ehcache</artifactId> <artifactId>tomcat-embed-core</artifactId>
<optional>true</optional> <optional>true</optional>
</dependency> </dependency>
<dependency> <dependency>
<groupId>org.apache.tomcat.embed</groupId> <groupId>org.aspectj</groupId>
<artifactId>tomcat-embed-core</artifactId> <artifactId>aspectjweaver</artifactId>
<optional>true</optional> <optional>true</optional>
</dependency> </dependency>
<dependency> <dependency>
......
/*
* Copyright 2012-2017 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.cache;
import java.lang.management.ManagementFactory;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import javax.management.AttributeNotFoundException;
import javax.management.InstanceNotFoundException;
import javax.management.MBeanException;
import javax.management.MBeanServer;
import javax.management.MalformedObjectNameException;
import javax.management.ObjectName;
import javax.management.ReflectionException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.cache.Cache;
import org.springframework.cache.CacheManager;
/**
* Base {@link CacheStatisticsProvider} implementation that uses JMX to retrieve the cache
* statistics.
*
* @param <C> The cache type
* @author Stephane Nicoll
* @since 1.3.0
*/
public abstract class AbstractJmxCacheStatisticsProvider<C extends Cache>
implements CacheStatisticsProvider<C> {
private static final Logger logger = LoggerFactory
.getLogger(AbstractJmxCacheStatisticsProvider.class);
private MBeanServer mBeanServer;
private final Map<String, ObjectNameWrapper> caches = new ConcurrentHashMap<>();
@Override
public CacheStatistics getCacheStatistics(CacheManager cacheManager, C cache) {
try {
ObjectName objectName = internalGetObjectName(cache);
return (objectName == null ? null : getCacheStatistics(objectName));
}
catch (MalformedObjectNameException ex) {
throw new IllegalStateException(ex);
}
}
/**
* Return the {@link ObjectName} of the MBean that is managing the specified cache or
* {@code null} if none is found.
* @param cache the cache to handle
* @return the object name of the cache statistics MBean
* @throws MalformedObjectNameException if the {@link ObjectName} for that cache is
* invalid
*/
protected abstract ObjectName getObjectName(C cache)
throws MalformedObjectNameException;
/**
* Return the current {@link CacheStatistics} snapshot from the MBean identified by
* the specified {@link ObjectName}.
* @param objectName the object name of the cache statistics MBean
* @return the current cache statistics
*/
protected abstract CacheStatistics getCacheStatistics(ObjectName objectName);
private ObjectName internalGetObjectName(C cache)
throws MalformedObjectNameException {
String cacheName = cache.getName();
ObjectNameWrapper value = this.caches.get(cacheName);
if (value != null) {
return value.objectName;
}
ObjectName objectName = getObjectName(cache);
this.caches.put(cacheName, new ObjectNameWrapper(objectName));
return objectName;
}
protected MBeanServer getMBeanServer() {
if (this.mBeanServer == null) {
this.mBeanServer = ManagementFactory.getPlatformMBeanServer();
}
return this.mBeanServer;
}
protected <T> T getAttribute(ObjectName objectName, String attributeName,
Class<T> type) {
try {
Object attribute = getMBeanServer().getAttribute(objectName, attributeName);
return type.cast(attribute);
}
catch (MBeanException | ReflectionException ex) {
throw new IllegalStateException(ex);
}
catch (AttributeNotFoundException ex) {
throw new IllegalStateException("Unexpected: MBean with name '" + objectName
+ "' " + "does not expose attribute with name " + attributeName, ex);
}
catch (InstanceNotFoundException ex) {
logger.warn("Cache statistics are no longer available", ex);
return null;
}
}
private static class ObjectNameWrapper {
private final ObjectName objectName;
ObjectNameWrapper(ObjectName objectName) {
this.objectName = objectName;
}
}
}
/*
* Copyright 2012-2017 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.cache;
import org.springframework.cache.Cache;
import org.springframework.cache.CacheManager;
/**
* Provide a {@link CacheStatistics} based on a {@link Cache}.
*
* @param <C> The {@link Cache} type
* @author Stephane Nicoll
* @author Phillip Webb
* @since 1.3.0
*/
@FunctionalInterface
public interface CacheStatisticsProvider<C extends Cache> {
/**
* Return the current {@link CacheStatistics} snapshot for the specified {@link Cache}
* or {@code null} if the given cache could not be handled.
* @param cacheManager the {@link CacheManager} handling this cache
* @param cache the cache to handle
* @return the current cache statistics or {@code null}
*/
CacheStatistics getCacheStatistics(CacheManager cacheManager, C cache);
}
/*
* Copyright 2012-2017 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.
*/
/**
* Actuator support for cache statistics.
*/
package org.springframework.boot.actuate.cache;
This diff is collapsed.
This diff is collapsed.
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment