diff --git a/spring-boot-actuator-autoconfigure/pom.xml b/spring-boot-actuator-autoconfigure/pom.xml index e4d4f108feb4..784ef3805a97 100644 --- a/spring-boot-actuator-autoconfigure/pom.xml +++ b/spring-boot-actuator-autoconfigure/pom.xml @@ -33,6 +33,11 @@ com.fasterxml.jackson.core jackson-databind + + io.micrometer + micrometer-core + true + org.springframework spring-core @@ -87,6 +92,41 @@ lettuce-core true + + io.micrometer + micrometer-atlas-starter + true + + + io.micrometer + micrometer-datadog-starter + true + + + io.micrometer + micrometer-ganglia-starter + true + + + io.micrometer + micrometer-graphite-starter + true + + + io.micrometer + micrometer-influx-starter + true + + + io.micrometer + micrometer-jmx-starter + true + + + io.micrometer + micrometer-prometheus-starter + true + io.searchbox jest @@ -143,6 +183,11 @@ tomcat-jdbc true + + org.aspectj + aspectjweaver + true + org.elasticsearch elasticsearch @@ -283,6 +328,11 @@ spring-boot-test test + + org.springframework.boot + spring-boot-test-autoconfigure + test + org.springframework.boot spring-boot-test-support diff --git a/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/cache/CacheStatisticsAutoConfiguration.java b/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/cache/CacheStatisticsAutoConfiguration.java deleted file mode 100644 index af59da37f6d3..000000000000 --- a/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/cache/CacheStatisticsAutoConfiguration.java +++ /dev/null @@ -1,150 +0,0 @@ -/* - * 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 noOpCacheStatisticsProvider() { - return (cacheManager, cache) -> { - if (cacheManager instanceof NoOpCacheManager) { - return NO_OP_STATS; - } - return null; - }; - } - - } - -} diff --git a/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/cache/package-info.java b/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/cache/package-info.java deleted file mode 100644 index e1350cafb1a8..000000000000 --- a/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/cache/package-info.java +++ /dev/null @@ -1,20 +0,0 @@ -/* - * 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; diff --git a/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/jdbc/DataSourceHealthIndicatorAutoConfiguration.java b/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/jdbc/DataSourceHealthIndicatorAutoConfiguration.java index 631bb77cdc15..e80938c862f8 100644 --- a/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/jdbc/DataSourceHealthIndicatorAutoConfiguration.java +++ b/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/jdbc/DataSourceHealthIndicatorAutoConfiguration.java @@ -36,9 +36,9 @@ import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; 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.DataSourcePoolMetadataProvider; -import org.springframework.boot.jdbc.metadata.DataSourcePoolMetadataProviders; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.jdbc.core.JdbcTemplate; @@ -94,7 +94,7 @@ private Map filterDataSources( @Override public void afterPropertiesSet() throws Exception { - this.poolMetadataProvider = new DataSourcePoolMetadataProviders( + this.poolMetadataProvider = new CompositeDataSourcePoolMetadataProvider( this.metadataProviders); } diff --git a/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/metrics/ActuatorMetricWriter.java b/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/metrics/ActuatorMetricWriter.java deleted file mode 100644 index 052f5a01dcc8..000000000000 --- a/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/metrics/ActuatorMetricWriter.java +++ /dev/null @@ -1,43 +0,0 @@ -/* - * 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 { - -} diff --git a/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/metrics/ExportMetricReader.java b/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/metrics/ExportMetricReader.java deleted file mode 100644 index 71138f0a0576..000000000000 --- a/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/metrics/ExportMetricReader.java +++ /dev/null @@ -1,43 +0,0 @@ -/* - * 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 { - -} diff --git a/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/metrics/ExportMetricWriter.java b/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/metrics/ExportMetricWriter.java deleted file mode 100644 index a5079b9c19c6..000000000000 --- a/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/metrics/ExportMetricWriter.java +++ /dev/null @@ -1,43 +0,0 @@ -/* - * 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 { - -} diff --git a/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/metrics/MeterBindersConfiguration.java b/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/metrics/MeterBindersConfiguration.java new file mode 100644 index 000000000000..38d709faedd9 --- /dev/null +++ b/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/metrics/MeterBindersConfiguration.java @@ -0,0 +1,56 @@ +/* + * 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 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.binder.UptimeMetrics; + +import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; +import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +/** + * Configuration for various {@link MeterBinder MeterBinders}. + * + * @author Jon Schneider + */ +@Configuration +class MeterBindersConfiguration { + + @Bean + @ConditionalOnMissingBean(JvmMemoryMetrics.class) + public JvmMemoryMetrics jvmMemoryMetrics() { + return new JvmMemoryMetrics(); + } + + @Bean + @ConditionalOnMissingBean(LogbackMetrics.class) + @ConditionalOnClass(name = "ch.qos.logback.classic.Logger") + public LogbackMetrics logbackMetrics() { + return new LogbackMetrics(); + } + + @Bean + @ConditionalOnMissingBean(UptimeMetrics.class) + public UptimeMetrics uptimeMetrics() { + return new UptimeMetrics(); + } + +} diff --git a/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/metrics/package-info.java b/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/metrics/MeterRegistryConfigurer.java similarity index 54% rename from spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/metrics/package-info.java rename to spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/metrics/MeterRegistryConfigurer.java index 7dbdea918f2b..a12b27f636c2 100644 --- a/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/metrics/package-info.java +++ b/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/metrics/MeterRegistryConfigurer.java @@ -14,7 +14,28 @@ * 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}. + *

+ * 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); + +} diff --git a/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/metrics/MetricExportAutoConfiguration.java b/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/metrics/MetricExportAutoConfiguration.java deleted file mode 100644 index 277adb058091..000000000000 --- a/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/metrics/MetricExportAutoConfiguration.java +++ /dev/null @@ -1,147 +0,0 @@ -/* - * 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 readers; - - private final Map writers; - - private final Map exporters; - - public MetricExportAutoConfiguration(MetricExportProperties properties, - ObjectProvider endpointReader, - @ExportMetricReader ObjectProvider> readers, - @ExportMetricWriter ObjectProvider> writers, - ObjectProvider> 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 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.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) { - } - - } - -} diff --git a/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/metrics/MetricFilterAutoConfiguration.java b/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/metrics/MetricFilterAutoConfiguration.java deleted file mode 100644 index 236b70edd050..000000000000 --- a/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/metrics/MetricFilterAutoConfiguration.java +++ /dev/null @@ -1,75 +0,0 @@ -/* - * 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 javax.servlet.Servlet; -import javax.servlet.ServletRegistration; - -import org.springframework.boot.actuate.metrics.CounterService; -import org.springframework.boot.actuate.metrics.GaugeService; -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.ConditionalOnProperty; -import org.springframework.boot.context.properties.EnableConfigurationProperties; -import org.springframework.context.annotation.Bean; -import org.springframework.context.annotation.Configuration; -import org.springframework.web.filter.OncePerRequestFilter; -import org.springframework.web.servlet.HandlerMapping; - -/** - * {@link EnableAutoConfiguration Auto-configuration} that records Servlet interactions - * with a {@link CounterService} and {@link GaugeService}. - * - * @author Dave Syer - * @author Phillip Webb - * @author Andy Wilkinson - * @author Sebastian Kirsch - * @since 2.0.0 - */ -@Configuration -@ConditionalOnBean({ CounterService.class, GaugeService.class }) -@ConditionalOnClass({ Servlet.class, ServletRegistration.class, - OncePerRequestFilter.class, HandlerMapping.class }) -@AutoConfigureAfter(MetricRepositoryAutoConfiguration.class) -@ConditionalOnProperty(prefix = "management.metrics.filter", name = "enabled", matchIfMissing = true) -@EnableConfigurationProperties({ MetricFilterProperties.class }) -public class MetricFilterAutoConfiguration { - - private final CounterService counterService; - - private final GaugeService gaugeService; - - private final MetricFilterProperties properties; - - public MetricFilterAutoConfiguration(CounterService counterService, - GaugeService gaugeService, MetricFilterProperties properties) { - this.counterService = counterService; - this.gaugeService = gaugeService; - this.properties = properties; - } - - @Bean - public MetricsFilter metricsFilter() { - return new MetricsFilter(this.counterService, this.gaugeService, - this.properties.getCounterSubmissions(), - this.properties.getGaugeSubmissions()); - } - -} diff --git a/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/metrics/MetricFilterProperties.java b/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/metrics/MetricFilterProperties.java deleted file mode 100644 index ad9244c75cd9..000000000000 --- a/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/metrics/MetricFilterProperties.java +++ /dev/null @@ -1,69 +0,0 @@ -/* - * 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.EnumSet; -import java.util.HashSet; -import java.util.Set; - -import org.springframework.boot.actuate.metrics.web.servlet.MetricsFilter; -import org.springframework.boot.actuate.metrics.web.servlet.MetricsFilterSubmission; -import org.springframework.boot.context.properties.ConfigurationProperties; - -/** - * Configuration properties for the {@link MetricsFilter}. - * - * @author Sebastian Kirsch - * @author Phillip Webb - * @since 2.0.0 - */ -@ConfigurationProperties(prefix = "management.metrics.filter") -public class MetricFilterProperties { - - /** - * Submissions that should be made to the gauge. - */ - private Set gaugeSubmissions; - - /** - * Submissions that should be made to the counter. - */ - private Set counterSubmissions; - - public MetricFilterProperties() { - this.gaugeSubmissions = new HashSet<>(EnumSet.of(MetricsFilterSubmission.MERGED)); - this.counterSubmissions = new HashSet<>( - EnumSet.of(MetricsFilterSubmission.MERGED)); - } - - public Set getGaugeSubmissions() { - return this.gaugeSubmissions; - } - - public void setGaugeSubmissions(Set gaugeSubmissions) { - this.gaugeSubmissions = gaugeSubmissions; - } - - public Set getCounterSubmissions() { - return this.counterSubmissions; - } - - public void setCounterSubmissions(Set counterSubmissions) { - this.counterSubmissions = counterSubmissions; - } - -} diff --git a/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/metrics/MetricRepositoryAutoConfiguration.java b/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/metrics/MetricRepositoryAutoConfiguration.java deleted file mode 100644 index c237ccf6ef10..000000000000 --- a/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/metrics/MetricRepositoryAutoConfiguration.java +++ /dev/null @@ -1,114 +0,0 @@ -/* - * 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. - *

- * 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. - *

- * 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. - *

- * 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}. - *

- * 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); - } - - } - -} diff --git a/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/metrics/MetricsAutoConfiguration.java b/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/metrics/MetricsAutoConfiguration.java new file mode 100644 index 000000000000..069feee08dc8 --- /dev/null +++ b/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/metrics/MetricsAutoConfiguration.java @@ -0,0 +1,128 @@ +/* + * 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.Collection; +import java.util.Collections; + +import io.micrometer.core.annotation.Timed; +import io.micrometer.core.instrument.MeterRegistry; +import io.micrometer.core.instrument.Metrics; +import io.micrometer.core.instrument.binder.MeterBinder; +import io.micrometer.core.instrument.composite.CompositeMeterRegistry; + +import org.springframework.beans.factory.ObjectProvider; +import org.springframework.boot.actuate.autoconfigure.endpoint.condition.ConditionalOnEnabledEndpoint; +import org.springframework.boot.actuate.autoconfigure.metrics.export.MetricsExporter; +import org.springframework.boot.actuate.autoconfigure.metrics.export.atlas.AtlasExportConfiguration; +import org.springframework.boot.actuate.autoconfigure.metrics.export.datadog.DatadogExportConfiguration; +import org.springframework.boot.actuate.autoconfigure.metrics.export.ganglia.GangliaExportConfiguration; +import org.springframework.boot.actuate.autoconfigure.metrics.export.graphite.GraphiteExportConfiguration; +import org.springframework.boot.actuate.autoconfigure.metrics.export.influx.InfluxExportConfiguration; +import org.springframework.boot.actuate.autoconfigure.metrics.export.jmx.JmxExportConfiguration; +import org.springframework.boot.actuate.autoconfigure.metrics.export.prometheus.PrometheusExportConfiguration; +import org.springframework.boot.actuate.autoconfigure.metrics.export.simple.SimpleExportConfiguration; +import org.springframework.boot.actuate.autoconfigure.metrics.reactive.server.WebFluxMetricsConfiguration; +import org.springframework.boot.actuate.autoconfigure.metrics.web.client.RestTemplateMetricsConfiguration; +import org.springframework.boot.actuate.autoconfigure.metrics.web.servlet.WebMvcMetricsConfiguration; +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.condition.ConditionalOnBean; +import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; +import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; +import org.springframework.boot.autoconfigure.condition.SearchStrategy; +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; +import org.springframework.integration.config.EnableIntegrationManagement; +import org.springframework.integration.support.management.IntegrationManagementConfigurer; + +/** + * {@link EnableAutoConfiguration Auto-configuration} for Micrometer-based metrics. + * + * @since 2.0.0 + * @author Jon Schneider + */ +@Configuration +@ConditionalOnClass(Timed.class) +@EnableConfigurationProperties(MetricsProperties.class) +@Import({ MeterBindersConfiguration.class, WebMvcMetricsConfiguration.class, + WebFluxMetricsConfiguration.class, RestTemplateMetricsConfiguration.class, + AtlasExportConfiguration.class, DatadogExportConfiguration.class, + GangliaExportConfiguration.class, GraphiteExportConfiguration.class, + InfluxExportConfiguration.class, JmxExportConfiguration.class, + PrometheusExportConfiguration.class, SimpleExportConfiguration.class }) +public class MetricsAutoConfiguration { + + @Bean + @ConditionalOnMissingBean(MeterRegistry.class) + public CompositeMeterRegistry compositeMeterRegistry( + ObjectProvider> exporters) { + CompositeMeterRegistry composite = new CompositeMeterRegistry(); + exporters.getIfAvailable(Collections::emptyList).stream() + .map(MetricsExporter::registry).forEach(composite::add); + return composite; + } + + @Bean + @ConditionalOnBean(MeterRegistry.class) + @ConditionalOnMissingBean + @ConditionalOnEnabledEndpoint + public MetricsEndpoint metricsEndpoint(MeterRegistry registry) { + return new MetricsEndpoint(registry); + } + + @Configuration + @ConditionalOnClass(EnableIntegrationManagement.class) + static class MetricsIntegrationConfiguration { + + @Bean(name = IntegrationManagementConfigurer.MANAGEMENT_CONFIGURER_NAME) + @ConditionalOnMissingBean(value = IntegrationManagementConfigurer.class, name = IntegrationManagementConfigurer.MANAGEMENT_CONFIGURER_NAME, search = SearchStrategy.CURRENT) + public IntegrationManagementConfigurer integrationManagementConfigurer() { + IntegrationManagementConfigurer configurer = new IntegrationManagementConfigurer(); + configurer.setDefaultCountsEnabled(true); + configurer.setDefaultStatsEnabled(true); + return configurer; + } + + @Bean + public SpringIntegrationMetrics springIntegrationMetrics( + IntegrationManagementConfigurer configurer) { + return new SpringIntegrationMetrics(configurer); + } + + } + + @Configuration + static class MeterRegistryConfigurationSupport { + + MeterRegistryConfigurationSupport(MeterRegistry registry, + ObjectProvider> configurers, + MetricsProperties config, Collection binders) { + configurers.getIfAvailable(Collections::emptyList) + .forEach((configurer) -> configurer.configureRegistry(registry)); + binders.forEach((binder) -> binder.bindTo(registry)); + if (config.isUseGlobalRegistry()) { + Metrics.addRegistry(registry); + } + } + + } + +} diff --git a/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/metrics/MetricsDropwizardAutoConfiguration.java b/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/metrics/MetricsDropwizardAutoConfiguration.java deleted file mode 100644 index 82e69dfd3438..000000000000 --- a/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/metrics/MetricsDropwizardAutoConfiguration.java +++ /dev/null @@ -1,80 +0,0 @@ -/* - * 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.beans.factory.ObjectProvider; -import org.springframework.boot.actuate.metrics.CounterService; -import org.springframework.boot.actuate.metrics.GaugeService; -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.ConditionalOnMissingBean; -import org.springframework.context.annotation.Bean; -import org.springframework.context.annotation.Configuration; - -/** - * {@link EnableAutoConfiguration Auto-configuration} for Dropwizard-based metrics. - * - * @author Dave Syer - * @since 2.0.0 - */ -@Configuration -@ConditionalOnClass(MetricRegistry.class) -@AutoConfigureBefore(MetricRepositoryAutoConfiguration.class) -public class MetricsDropwizardAutoConfiguration { - - private final ReservoirFactory reservoirFactory; - - public MetricsDropwizardAutoConfiguration( - ObjectProvider reservoirFactory) { - this.reservoirFactory = reservoirFactory.getIfAvailable(); - } - - @Bean - @ConditionalOnMissingBean - public MetricRegistry metricRegistry() { - return new MetricRegistry(); - } - - @Bean - @ConditionalOnMissingBean({ DropwizardMetricServices.class, CounterService.class, - GaugeService.class }) - public DropwizardMetricServices dropwizardMetricServices( - MetricRegistry metricRegistry) { - if (this.reservoirFactory == null) { - return new DropwizardMetricServices(metricRegistry); - } - else { - return new DropwizardMetricServices(metricRegistry, this.reservoirFactory); - } - } - - @Bean - public MetricReaderPublicMetrics dropwizardPublicMetrics( - MetricRegistry metricRegistry) { - MetricRegistryMetricReader reader = new MetricRegistryMetricReader( - metricRegistry); - return new MetricReaderPublicMetrics(reader); - } - -} diff --git a/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/metrics/MetricsEndpointAutoConfiguration.java b/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/metrics/MetricsEndpointAutoConfiguration.java deleted file mode 100644 index 97323fae0953..000000000000 --- a/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/metrics/MetricsEndpointAutoConfiguration.java +++ /dev/null @@ -1,62 +0,0 @@ -/* - * 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.ArrayList; -import java.util.Collections; -import java.util.List; - -import org.springframework.beans.factory.ObjectProvider; -import org.springframework.boot.actuate.autoconfigure.endpoint.condition.ConditionalOnEnabledEndpoint; -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.context.annotation.Bean; -import org.springframework.context.annotation.Configuration; -import org.springframework.core.annotation.AnnotationAwareOrderComparator; - -/** - * {@link EnableAutoConfiguration Auto-configuration} for the {@link MetricsEndpoint}. - * - * @author Phillip Webb - * @since 2.0.0 - */ -@Configuration -@AutoConfigureAfter(PublicMetricsAutoConfiguration.class) -public class MetricsEndpointAutoConfiguration { - - @Bean - @ConditionalOnMissingBean - @ConditionalOnEnabledEndpoint - public MetricsEndpoint metricsEndpoint( - ObjectProvider> publicMetrics) { - return metricsEndpoint(publicMetrics.getIfAvailable(Collections::emptyList)); - } - - private MetricsEndpoint metricsEndpoint(List publicMetrics) { - return new MetricsEndpoint(sort(publicMetrics)); - } - - private List sort(List publicMetrics) { - List sorted = new ArrayList<>(publicMetrics); - Collections.sort(sorted, AnnotationAwareOrderComparator.INSTANCE); - return sorted; - } - -} diff --git a/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/metrics/MetricsProperties.java b/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/metrics/MetricsProperties.java new file mode 100644 index 000000000000..c59500f4997f --- /dev/null +++ b/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/metrics/MetricsProperties.java @@ -0,0 +1,154 @@ +/* + * 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; + } + + } + + } + +} diff --git a/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/metrics/PublicMetricsAutoConfiguration.java b/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/metrics/PublicMetricsAutoConfiguration.java deleted file mode 100644 index c66c08d7b1b4..000000000000 --- a/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/metrics/PublicMetricsAutoConfiguration.java +++ /dev/null @@ -1,167 +0,0 @@ -/* - * 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.Collection; -import java.util.List; -import java.util.Map; - -import javax.servlet.Servlet; -import javax.sql.DataSource; - -import org.apache.catalina.startup.Tomcat; - -import org.springframework.beans.factory.ObjectProvider; -import org.springframework.boot.actuate.autoconfigure.cache.CacheStatisticsAutoConfiguration; -import org.springframework.boot.actuate.cache.CachePublicMetrics; -import org.springframework.boot.actuate.cache.CacheStatisticsProvider; -import org.springframework.boot.actuate.metrics.DataSourcePublicMetrics; -import org.springframework.boot.actuate.metrics.PublicMetrics; -import org.springframework.boot.actuate.metrics.SystemPublicMetrics; -import org.springframework.boot.actuate.metrics.TomcatPublicMetrics; -import org.springframework.boot.actuate.metrics.integration.SpringIntegrationMetricReader; -import org.springframework.boot.actuate.metrics.reader.CompositeMetricReader; -import org.springframework.boot.actuate.metrics.reader.MetricReader; -import org.springframework.boot.actuate.metrics.reader.MetricReaderPublicMetrics; -import org.springframework.boot.actuate.metrics.rich.RichGaugeReader; -import org.springframework.boot.actuate.metrics.rich.RichGaugeReaderPublicMetrics; -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.boot.autoconfigure.condition.ConditionalOnMissingBean; -import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication; -import org.springframework.boot.autoconfigure.condition.SearchStrategy; -import org.springframework.boot.autoconfigure.integration.IntegrationAutoConfiguration; -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.Configuration; -import org.springframework.integration.config.EnableIntegrationManagement; -import org.springframework.integration.support.management.IntegrationManagementConfigurer; - -/** - * {@link EnableAutoConfiguration Auto-configuration} for {@link PublicMetrics}. - * - * @author Stephane Nicoll - * @author Phillip Webb - * @author Johannes Edmeier - * @author Artem Bilan - * @since 2.0.0 - */ -@Configuration -@AutoConfigureAfter({ DataSourceAutoConfiguration.class, CacheAutoConfiguration.class, - MetricRepositoryAutoConfiguration.class, CacheStatisticsAutoConfiguration.class, - IntegrationAutoConfiguration.class }) -public class PublicMetricsAutoConfiguration { - - private final List metricReaders; - - public PublicMetricsAutoConfiguration( - @ExportMetricReader ObjectProvider> metricReaders) { - this.metricReaders = metricReaders.getIfAvailable(); - } - - @Bean - public SystemPublicMetrics systemPublicMetrics() { - return new SystemPublicMetrics(); - } - - @Bean - public MetricReaderPublicMetrics metricReaderPublicMetrics() { - return new MetricReaderPublicMetrics( - new CompositeMetricReader(this.metricReaders == null ? new MetricReader[0] - : this.metricReaders - .toArray(new MetricReader[this.metricReaders.size()]))); - } - - @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 cacheManagers, - Collection> statisticsProviders) { - return new CachePublicMetrics(cacheManagers, statisticsProviders); - } - - } - - @Configuration - @ConditionalOnClass(EnableIntegrationManagement.class) - static class IntegrationMetricsConfiguration { - - @Bean(name = IntegrationManagementConfigurer.MANAGEMENT_CONFIGURER_NAME) - @ConditionalOnMissingBean(value = IntegrationManagementConfigurer.class, name = IntegrationManagementConfigurer.MANAGEMENT_CONFIGURER_NAME, search = SearchStrategy.CURRENT) - public IntegrationManagementConfigurer managementConfigurer() { - IntegrationManagementConfigurer configurer = new IntegrationManagementConfigurer(); - configurer.setDefaultCountsEnabled(true); - configurer.setDefaultStatsEnabled(true); - return configurer; - } - - @Bean - @ConditionalOnMissingBean(name = "springIntegrationPublicMetrics") - public MetricReaderPublicMetrics springIntegrationPublicMetrics( - IntegrationManagementConfigurer managementConfigurer) { - return new MetricReaderPublicMetrics( - new SpringIntegrationMetricReader(managementConfigurer)); - } - - } - -} diff --git a/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/metrics/writer/GaugeWriter.java b/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/metrics/export/MetricsExporter.java similarity index 57% rename from spring-boot-actuator/src/main/java/org/springframework/boot/actuate/metrics/writer/GaugeWriter.java rename to spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/metrics/export/MetricsExporter.java index 5cfa2d67d0b8..a2f280bf31cc 100644 --- a/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/metrics/writer/GaugeWriter.java +++ b/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/metrics/export/MetricsExporter.java @@ -14,23 +14,25 @@ * 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 - * @since 1.3.0 + * @author Jon Schneider + * @author Andy Wilkinson + * @since 2.0.0 */ @FunctionalInterface -public interface GaugeWriter { +public interface MetricsExporter { /** - * Set the value of a metric. - * @param value the value + * Returns the {@link MeterRegistry} used to register metrics with the exporter. + * @return the meter registry */ - void set(Metric value); + MeterRegistry registry(); } diff --git a/spring-boot-samples/spring-boot-sample-metrics-redis/src/main/java/sample/metrics/redis/HelloWorldProperties.java b/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/metrics/export/RegistryProperties.java similarity index 50% rename from spring-boot-samples/spring-boot-sample-metrics-redis/src/main/java/sample/metrics/redis/HelloWorldProperties.java rename to spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/metrics/export/RegistryProperties.java index 650ec958ddc0..f2c36c57df82 100644 --- a/spring-boot-samples/spring-boot-sample-metrics-redis/src/main/java/sample/metrics/redis/HelloWorldProperties.java +++ b/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/metrics/export/RegistryProperties.java @@ -1,5 +1,5 @@ /* - * 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"); * you may not use this file except in compliance with the License. @@ -14,21 +14,31 @@ * 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; -@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() { - return this.name; + public String get(String key) { + return this.properties.getProperty(key); } - public void setName(String name) { - this.name = name; + protected void set(String key, Object value) { + this.properties.put(prefix() + "." + key, value.toString()); } } diff --git a/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/metrics/export/StepRegistryProperties.java b/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/metrics/export/StepRegistryProperties.java new file mode 100644 index 000000000000..bce50de53ae5 --- /dev/null +++ b/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/metrics/export/StepRegistryProperties.java @@ -0,0 +1,58 @@ +/* + * 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; + +import java.time.Duration; + +import io.micrometer.core.instrument.spectator.step.StepRegistryConfig; + +/** + * Specialization of {@link RegistryProperties} for configuring a metrics registry that + * pushes aggregated metrics on a regular interval. + * + * @author Jon Schneider + * @author Andy Wilkinson + * @since 2.0.0 + */ +public abstract class StepRegistryProperties extends RegistryProperties + implements StepRegistryConfig { + + public void setStep(Duration step) { + set("step", step); + } + + public void setEnabled(Boolean enabled) { + set("enabled", enabled); + } + + public void setBatchSize(Integer batchSize) { + set("batchSize", batchSize); + } + + public void setConnectTimeout(Duration connectTimeout) { + set("connectTimeout", connectTimeout); + } + + public void setReadTimeout(Duration readTimeout) { + set("readTimeout", readTimeout); + } + + public void setNumThreads(Integer numThreads) { + set("numThreads", numThreads); + } + +} diff --git a/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/metrics/web/servlet/MetricsFilterSubmission.java b/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/metrics/export/StringToDurationConverter.java similarity index 53% rename from spring-boot-actuator/src/main/java/org/springframework/boot/actuate/metrics/web/servlet/MetricsFilterSubmission.java rename to spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/metrics/export/StringToDurationConverter.java index 846ab20aba44..c82fbed14010 100644 --- a/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/metrics/web/servlet/MetricsFilterSubmission.java +++ b/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/metrics/export/StringToDurationConverter.java @@ -14,24 +14,26 @@ * 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 */ -public enum MetricsFilterSubmission { - - /** - * Merge all HTTP methods into a single submission. - */ - MERGED, +@ConfigurationPropertiesBinding +public class StringToDurationConverter implements Converter { - /** - * Group submissions by the HTTP method of the request. - */ - PER_HTTP_METHOD + @Override + public Duration convert(String source) { + return Duration.parse(source); + } } diff --git a/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/metrics/export/atlas/AtlasExportConfiguration.java b/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/metrics/export/atlas/AtlasExportConfiguration.java new file mode 100644 index 000000000000..0f359223f300 --- /dev/null +++ b/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/metrics/export/atlas/AtlasExportConfiguration.java @@ -0,0 +1,58 @@ +/* + * 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 com.netflix.spectator.atlas.AtlasConfig; +import io.micrometer.atlas.AtlasMeterRegistry; +import io.micrometer.core.instrument.Clock; + +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 Atlas. + * + * @author Jon Schneider + * @author Andy Wilkinson + * @since 2.0.0 + */ +@Configuration +@ConditionalOnClass(AtlasMeterRegistry.class) +@Import(StringToDurationConverter.class) +@EnableConfigurationProperties(AtlasProperties.class) +public class AtlasExportConfiguration { + + @Bean + @ConditionalOnProperty(value = "metrics.atlas.enabled", matchIfMissing = true) + public MetricsExporter atlasExporter(AtlasConfig config, Clock clock) { + return () -> new AtlasMeterRegistry(config, clock); + } + + @Bean + @ConditionalOnMissingBean + public Clock clock() { + return Clock.SYSTEM; + } + +} diff --git a/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/metrics/export/atlas/AtlasProperties.java b/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/metrics/export/atlas/AtlasProperties.java new file mode 100644 index 000000000000..4ed63746c526 --- /dev/null +++ b/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/metrics/export/atlas/AtlasProperties.java @@ -0,0 +1,92 @@ +/* + * 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); + } + +} diff --git a/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/metrics/export/datadog/DatadogExportConfiguration.java b/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/metrics/export/datadog/DatadogExportConfiguration.java new file mode 100644 index 000000000000..eb29576195a0 --- /dev/null +++ b/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/metrics/export/datadog/DatadogExportConfiguration.java @@ -0,0 +1,57 @@ +/* + * 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; + } + +} diff --git a/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/metrics/export/datadog/DatadogProperties.java b/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/metrics/export/datadog/DatadogProperties.java new file mode 100644 index 000000000000..444dc0ae42d3 --- /dev/null +++ b/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/metrics/export/datadog/DatadogProperties.java @@ -0,0 +1,50 @@ +/* + * 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.datadog.DatadogConfig; + +import org.springframework.boot.actuate.autoconfigure.metrics.export.StepRegistryProperties; +import org.springframework.boot.context.properties.ConfigurationProperties; + +/** + * {@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() { + set("apiKey", "dummyKey"); // FIXME otherwise tests fail + } + + public void setApiKey(String apiKey) { + set("apiKey", apiKey); + } + + public void setHostTag(String hostTag) { + set("hostTag", hostTag); + } + +} diff --git a/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/metrics/export/ganglia/GangliaExportConfiguration.java b/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/metrics/export/ganglia/GangliaExportConfiguration.java new file mode 100644 index 000000000000..88f5fedd4399 --- /dev/null +++ b/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/metrics/export/ganglia/GangliaExportConfiguration.java @@ -0,0 +1,65 @@ +/* + * 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; + } + +} diff --git a/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/metrics/export/ganglia/GangliaProperties.java b/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/metrics/export/ganglia/GangliaProperties.java new file mode 100644 index 000000000000..b3691d309f6d --- /dev/null +++ b/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/metrics/export/ganglia/GangliaProperties.java @@ -0,0 +1,78 @@ +/* + * 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 java.time.Duration; +import java.util.concurrent.TimeUnit; + +import info.ganglia.gmetric4j.gmetric.GMetric; +import io.micrometer.ganglia.GangliaConfig; + +import org.springframework.boot.actuate.autoconfigure.metrics.export.RegistryProperties; +import org.springframework.boot.context.properties.ConfigurationProperties; + +/** + * {@link ConfigurationProperties} for configuring Ganglia metrics export. + * + * @author Jon Schneider + * @since 2.0.0 + */ +@ConfigurationProperties(prefix = "metrics.ganglia") +public class GangliaProperties extends RegistryProperties implements GangliaConfig { + + @Override + public String prefix() { + return "metrics.ganglia"; + } + + 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 setProtocolVersion(String protocolVersion) { + set("protocolVersion", protocolVersion); + } + + public void setAddressingMode(GMetric.UDPAddressingMode addressingMode) { + set("addressingMode", addressingMode); + } + + public void setTtl(Integer ttl) { + set("ttl", ttl); + } + + public void setHost(String host) { + set("host", host); + } + + public void setPort(Integer port) { + set("port", port); + } + + public void setEnabled(Boolean enabled) { + set("enabled", enabled); + } + +} diff --git a/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/metrics/export/graphite/GraphiteExportConfiguration.java b/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/metrics/export/graphite/GraphiteExportConfiguration.java new file mode 100644 index 000000000000..bdd5d0a15ac3 --- /dev/null +++ b/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/metrics/export/graphite/GraphiteExportConfiguration.java @@ -0,0 +1,65 @@ +/* + * 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; + } + +} diff --git a/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/metrics/export/graphite/GraphiteProperties.java b/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/metrics/export/graphite/GraphiteProperties.java new file mode 100644 index 000000000000..f1fce4f70e4a --- /dev/null +++ b/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/metrics/export/graphite/GraphiteProperties.java @@ -0,0 +1,65 @@ +/* + * 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); + } + +} diff --git a/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/metrics/export/influx/InfluxExportConfiguration.java b/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/metrics/export/influx/InfluxExportConfiguration.java new file mode 100644 index 000000000000..62319355f578 --- /dev/null +++ b/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/metrics/export/influx/InfluxExportConfiguration.java @@ -0,0 +1,57 @@ +/* + * 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; + } + +} diff --git a/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/metrics/export/influx/InfluxProperties.java b/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/metrics/export/influx/InfluxProperties.java new file mode 100644 index 000000000000..b5153682d0c6 --- /dev/null +++ b/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/metrics/export/influx/InfluxProperties.java @@ -0,0 +1,67 @@ +/* + * 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.influx.InfluxConfig; +import io.micrometer.influx.InfluxConsistency; + +import org.springframework.boot.actuate.autoconfigure.metrics.export.StepRegistryProperties; +import org.springframework.boot.context.properties.ConfigurationProperties; + +/** + * {@link ConfigurationProperties} for configuring Influx metrics export. + * + * @author Jon Schneider + * @since 2.0.0 + */ +@ConfigurationProperties(prefix = "metrics.influx") +public class InfluxProperties extends StepRegistryProperties implements InfluxConfig { + + @Override + public String prefix() { + return "metrics.influx"; + } + + public void setDb(String db) { + set("db", db); + } + + public void setConsistency(InfluxConsistency consistency) { + set("consistency", consistency); + } + + public void setUserName(String userName) { + set("userName", userName); + } + + public void setPassword(String password) { + set("password", password); + } + + public void setRetentionPolicy(String retentionPolicy) { + set("retentionPolicy", retentionPolicy); + } + + public void setUri(String uri) { + set("uri", uri); + } + + public void setCompressed(Boolean compressed) { + set("compressed", compressed); + } + +} diff --git a/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/metrics/MetricsChannelAutoConfiguration.java b/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/metrics/export/jmx/JmxExportConfiguration.java similarity index 52% rename from spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/metrics/MetricsChannelAutoConfiguration.java rename to spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/metrics/export/jmx/JmxExportConfiguration.java index f5415bc61c55..cbaba8ec2041 100644 --- a/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/metrics/MetricsChannelAutoConfiguration.java +++ b/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/metrics/export/jmx/JmxExportConfiguration.java @@ -14,38 +14,45 @@ * 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 org.springframework.boot.actuate.metrics.writer.MessageChannelMetricWriter; -import org.springframework.boot.autoconfigure.AutoConfigureBefore; -import org.springframework.boot.autoconfigure.EnableAutoConfiguration; -import org.springframework.boot.autoconfigure.condition.ConditionalOnBean; +import io.micrometer.core.instrument.Clock; +import io.micrometer.core.instrument.util.HierarchicalNameMapper; +import io.micrometer.jmx.JmxMeterRegistry; + +import org.springframework.boot.actuate.autoconfigure.metrics.export.MetricsExporter; import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; 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.Configuration; -import org.springframework.messaging.MessageChannel; /** - * {@link EnableAutoConfiguration Auto-configuration} for writing metrics to a - * {@link MessageChannel}. + * Configuration for exporting metrics to JMX. * - * @author Dave Syer + * @author Jon Schneider * @since 2.0.0 */ @Configuration -@ConditionalOnClass(MessageChannel.class) -@ConditionalOnBean(name = "metricsChannel") -@AutoConfigureBefore(MetricRepositoryAutoConfiguration.class) -public class MetricsChannelAutoConfiguration { +@ConditionalOnClass(JmxMeterRegistry.class) +public class JmxExportConfiguration { + + @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 - @ExportMetricWriter @ConditionalOnMissingBean - public MessageChannelMetricWriter messageChannelMetricWriter( - @Qualifier("metricsChannel") MessageChannel channel) { - return new MessageChannelMetricWriter(channel); + public HierarchicalNameMapper hierarchicalNameMapper() { + return HierarchicalNameMapper.DEFAULT; } } diff --git a/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/metrics/export/prometheus/PrometheusExportConfiguration.java b/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/metrics/export/prometheus/PrometheusExportConfiguration.java new file mode 100644 index 000000000000..adcc3cae4b40 --- /dev/null +++ b/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/metrics/export/prometheus/PrometheusExportConfiguration.java @@ -0,0 +1,75 @@ +/* + * 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.core.instrument.Clock; +import io.micrometer.prometheus.PrometheusConfig; +import io.micrometer.prometheus.PrometheusMeterRegistry; +import io.prometheus.client.CollectorRegistry; + +import org.springframework.boot.actuate.autoconfigure.metrics.export.MetricsExporter; +import org.springframework.boot.actuate.autoconfigure.web.ManagementContextConfiguration; +import org.springframework.boot.actuate.metrics.export.prometheus.PrometheusScrapeEndpoint; +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; + +/** + * Configuration for exporting metrics to Prometheus. + * + * @since 2.0.0 + * @author Jon Schneider + */ +@Configuration +@ConditionalOnClass(PrometheusMeterRegistry.class) +@EnableConfigurationProperties(PrometheusProperties.class) +public class PrometheusExportConfiguration { + + @Bean + @ConditionalOnProperty(value = "metrics.prometheus.enabled", matchIfMissing = true) + public MetricsExporter prometheusExporter(PrometheusConfig config, + CollectorRegistry collectorRegistry, Clock clock) { + return () -> new PrometheusMeterRegistry(config, collectorRegistry, clock); + } + + @Bean + @ConditionalOnMissingBean + public CollectorRegistry collectorRegistry() { + return new CollectorRegistry(true); + } + + @Bean + @ConditionalOnMissingBean + public Clock clock() { + return Clock.SYSTEM; + } + + @ManagementContextConfiguration + public static class PrometheusScrapeEndpointConfiguration { + + @Bean + public PrometheusScrapeEndpoint prometheusEndpoint( + CollectorRegistry collectorRegistry) { + return new PrometheusScrapeEndpoint(collectorRegistry); + } + + } + +} diff --git a/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/metrics/export/prometheus/PrometheusProperties.java b/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/metrics/export/prometheus/PrometheusProperties.java new file mode 100644 index 000000000000..c82cab294e1e --- /dev/null +++ b/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/metrics/export/prometheus/PrometheusProperties.java @@ -0,0 +1,52 @@ +/* + * 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"; + } + +} diff --git a/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/metrics/export/simple/SimpleExportConfiguration.java b/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/metrics/export/simple/SimpleExportConfiguration.java new file mode 100644 index 000000000000..6b9fdc153691 --- /dev/null +++ b/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/metrics/export/simple/SimpleExportConfiguration.java @@ -0,0 +1,52 @@ +/* + * 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.simple; + +import io.micrometer.core.instrument.Clock; +import io.micrometer.core.instrument.simple.SimpleMeterRegistry; + +import org.springframework.boot.actuate.autoconfigure.metrics.export.MetricsExporter; +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; + +/** + * Configuration for exporting metrics to a {@link SimpleMeterRegistry}. + * + * @author Jon Schneider + * @since 2.0.0 + */ +@Configuration +@EnableConfigurationProperties(SimpleProperties.class) +public class SimpleExportConfiguration { + + @Bean + @ConditionalOnProperty(value = "metrics.simple.enabled", matchIfMissing = true) + @ConditionalOnMissingBean(MetricsExporter.class) + public MetricsExporter simpleExporter(Clock clock) { + return () -> new SimpleMeterRegistry(clock); + } + + @Bean + @ConditionalOnMissingBean + public Clock clock() { + return Clock.SYSTEM; + } + +} diff --git a/spring-boot-samples/spring-boot-sample-metrics-opentsdb/src/main/java/sample/metrics/opentsdb/HelloWorldProperties.java b/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/metrics/export/simple/SimpleProperties.java similarity index 52% rename from spring-boot-samples/spring-boot-sample-metrics-opentsdb/src/main/java/sample/metrics/opentsdb/HelloWorldProperties.java rename to spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/metrics/export/simple/SimpleProperties.java index a89b89f969b3..d9fb942b1534 100644 --- a/spring-boot-samples/spring-boot-sample-metrics-opentsdb/src/main/java/sample/metrics/opentsdb/HelloWorldProperties.java +++ b/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/metrics/export/simple/SimpleProperties.java @@ -1,5 +1,5 @@ /* - * 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"); * you may not use this file except in compliance with the License. @@ -14,21 +14,30 @@ * 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; -@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() { - return this.name; + public boolean isEnabled() { + return this.enabled; } - public void setName(String name) { - this.name = name; + public void setEnabled(boolean enabled) { + this.enabled = enabled; } } diff --git a/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/metrics/reactive/server/WebFluxMetricsConfiguration.java b/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/metrics/reactive/server/WebFluxMetricsConfiguration.java new file mode 100644 index 000000000000..53d859a42f69 --- /dev/null +++ b/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/metrics/reactive/server/WebFluxMetricsConfiguration.java @@ -0,0 +1,54 @@ +/* + * 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()); + } + +} diff --git a/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/metrics/web/client/RestTemplateMetricsConfiguration.java b/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/metrics/web/client/RestTemplateMetricsConfiguration.java new file mode 100644 index 000000000000..386d296b286e --- /dev/null +++ b/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/metrics/web/client/RestTemplateMetricsConfiguration.java @@ -0,0 +1,103 @@ +/* + * 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; + } + + } + +} diff --git a/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/metrics/web/servlet/WebMvcMetricsConfiguration.java b/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/metrics/web/servlet/WebMvcMetricsConfiguration.java new file mode 100644 index 000000000000..1965a53c60fe --- /dev/null +++ b/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/metrics/web/servlet/WebMvcMetricsConfiguration.java @@ -0,0 +1,87 @@ +/* + * 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); + } + + } + +} diff --git a/spring-boot-actuator-autoconfigure/src/main/resources/META-INF/additional-spring-configuration-metadata.json b/spring-boot-actuator-autoconfigure/src/main/resources/META-INF/additional-spring-configuration-metadata.json index 4c0ceedcfcf7..4bfe6a21f753 100644 --- a/spring-boot-actuator-autoconfigure/src/main/resources/META-INF/additional-spring-configuration-metadata.json +++ b/spring-boot-actuator-autoconfigure/src/main/resources/META-INF/additional-spring-configuration-metadata.json @@ -39,33 +39,6 @@ "vcap_services" ] }, - { - "name": "endpoints.metrics.filter.counter-submissions", - "type": "java.util.Set", - "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", - "description": "Submissions that should be made to the gauge.", - "deprecation": { - "replacement": "management.metrics.filter.gauge-submissions", - "level": "error" - } - }, { "name": "endpoints.trace.filter.enabled", "type": "java.lang.Boolean", @@ -205,12 +178,6 @@ "name": "management.info.git.mode", "defaultValue": "simple" }, - { - "name": "management.metrics.filter.enabled", - "type": "java.lang.Boolean", - "description": "Enable the metrics servlet filter.", - "defaultValue": true - }, { "name": "management.trace.filter.enabled", "type": "java.lang.Boolean", @@ -790,33 +757,6 @@ "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", "type": "java.lang.Boolean", diff --git a/spring-boot-actuator-autoconfigure/src/main/resources/META-INF/spring.factories b/spring-boot-actuator-autoconfigure/src/main/resources/META-INF/spring.factories index f3f69dd7016c..3d365c5bb98a 100644 --- a/spring-boot-actuator-autoconfigure/src/main/resources/META-INF/spring.factories +++ b/spring-boot-actuator-autoconfigure/src/main/resources/META-INF/spring.factories @@ -3,7 +3,6 @@ org.springframework.boot.actuate.autoconfigure.amqp.RabbitHealthIndicatorAutoCon org.springframework.boot.actuate.autoconfigure.audit.AuditAutoConfiguration,\ org.springframework.boot.actuate.autoconfigure.audit.AuditEventsEndpointAutoConfiguration,\ 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.cloudfoundry.CloudFoundryActuatorAutoConfiguration,\ org.springframework.boot.actuate.autoconfigure.condition.AutoConfigurationReportEndpointAutoConfiguration,\ @@ -26,13 +25,7 @@ org.springframework.boot.actuate.autoconfigure.liquibase.LiquibaseEndpointAutoCo org.springframework.boot.actuate.autoconfigure.logging.LoggersEndpointAutoConfiguration,\ org.springframework.boot.actuate.autoconfigure.mail.MailHealthIndicatorAutoConfiguration,\ org.springframework.boot.actuate.autoconfigure.management.ThreadDumpEndpointAutoConfiguration,\ -org.springframework.boot.actuate.autoconfigure.metrics.MetricExportAutoConfiguration,\ -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.metrics.MetricsAutoConfiguration,\ org.springframework.boot.actuate.autoconfigure.mongo.MongoHealthIndicatorAutoConfiguration,\ org.springframework.boot.actuate.autoconfigure.neo4j.Neo4jHealthIndicatorAutoConfiguration,\ org.springframework.boot.actuate.autoconfigure.redis.RedisHealthIndicatorAutoConfiguration,\ diff --git a/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/cache/CacheStatisticsAutoConfigurationTests.java b/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/cache/CacheStatisticsAutoConfigurationTests.java deleted file mode 100644 index 9a3593286ada..000000000000 --- a/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/cache/CacheStatisticsAutoConfigurationTests.java +++ /dev/null @@ -1,314 +0,0 @@ -/* - * 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 java.io.IOException; -import java.io.InputStream; -import java.util.Arrays; - -import javax.cache.Caching; -import javax.cache.configuration.MutableConfiguration; - -import com.github.benmanes.caffeine.cache.Caffeine; -import com.hazelcast.cache.HazelcastCachingProvider; -import com.hazelcast.config.Config; -import com.hazelcast.config.XmlConfigBuilder; -import com.hazelcast.core.Hazelcast; -import com.hazelcast.core.HazelcastInstance; -import com.hazelcast.spring.cache.HazelcastCacheManager; -import org.infinispan.manager.DefaultCacheManager; -import org.infinispan.manager.EmbeddedCacheManager; -import org.infinispan.spring.provider.SpringEmbeddedCacheManager; -import org.junit.After; -import org.junit.Test; - -import org.springframework.boot.actuate.cache.CacheStatistics; -import org.springframework.boot.actuate.cache.CacheStatisticsProvider; -import org.springframework.cache.Cache; -import org.springframework.cache.CacheManager; -import org.springframework.cache.caffeine.CaffeineCacheManager; -import org.springframework.cache.concurrent.ConcurrentMapCacheManager; -import org.springframework.cache.ehcache.EhCacheCacheManager; -import org.springframework.cache.ehcache.EhCacheManagerUtils; -import org.springframework.cache.jcache.JCacheCacheManager; -import org.springframework.cache.support.NoOpCacheManager; -import org.springframework.context.annotation.AnnotationConfigApplicationContext; -import org.springframework.context.annotation.Bean; -import org.springframework.context.annotation.Configuration; -import org.springframework.core.io.ClassPathResource; -import org.springframework.core.io.Resource; -import org.springframework.util.Assert; - -import static org.assertj.core.api.Assertions.assertThat; -import static org.assertj.core.api.Assertions.offset; - -/** - * Tests for {@link CacheStatisticsAutoConfiguration}. - * - * @author Stephane Nicoll - * @author EddĂș MelĂ©ndez - * @author Raja Kolli - */ -@SuppressWarnings({ "rawtypes", "unchecked" }) -public class CacheStatisticsAutoConfigurationTests { - - private AnnotationConfigApplicationContext context; - - private CacheManager cacheManager; - - @After - public void after() { - if (this.context != null) { - this.context.close(); - } - } - - @Test - public void basicJCacheCacheStatistics() { - load(JCacheCacheConfig.class); - CacheStatisticsProvider provider = this.context - .getBean("jCacheCacheStatisticsProvider", CacheStatisticsProvider.class); - doTestCoreStatistics(provider, false); - } - - @Test - public void basicEhCacheCacheStatistics() { - load(EhCacheConfig.class); - CacheStatisticsProvider provider = this.context - .getBean("ehCacheCacheStatisticsProvider", CacheStatisticsProvider.class); - doTestCoreStatistics(provider, true); - } - - @Test - public void basicHazelcastCacheStatistics() { - load(HazelcastConfig.class); - CacheStatisticsProvider provider = this.context.getBean( - "hazelcastCacheStatisticsProvider", CacheStatisticsProvider.class); - doTestCoreStatistics(provider, true); - } - - @Test - public void basicInfinispanCacheStatistics() { - load(InfinispanConfig.class); - CacheStatisticsProvider provider = this.context.getBean( - "infinispanCacheStatisticsProvider", CacheStatisticsProvider.class); - doTestCoreStatistics(provider, true); - } - - @Test - public void baseCaffeineCacheStatistics() { - load(CaffeineCacheConfig.class); - CacheStatisticsProvider provider = this.context.getBean( - "caffeineCacheStatisticsProvider", CacheStatisticsProvider.class); - doTestCoreStatistics(provider, true); - } - - @Test - public void concurrentMapCacheStatistics() { - load(ConcurrentMapConfig.class); - CacheStatisticsProvider provider = this.context.getBean( - "concurrentMapCacheStatisticsProvider", CacheStatisticsProvider.class); - Cache books = getCache("books"); - CacheStatistics cacheStatistics = provider.getCacheStatistics(this.cacheManager, - books); - assertCoreStatistics(cacheStatistics, 0L, null, null); - getOrCreate(books, "a", "b", "b", "a", "a"); - CacheStatistics updatedCacheStatistics = provider - .getCacheStatistics(this.cacheManager, books); - assertCoreStatistics(updatedCacheStatistics, 2L, null, null); - } - - @Test - public void noOpCacheStatistics() { - load(NoOpCacheConfig.class); - CacheStatisticsProvider provider = this.context - .getBean("noOpCacheStatisticsProvider", CacheStatisticsProvider.class); - Cache books = getCache("books"); - CacheStatistics cacheStatistics = provider.getCacheStatistics(this.cacheManager, - books); - assertCoreStatistics(cacheStatistics, null, null, null); - getOrCreate(books, "a", "b", "b", "a", "a"); - CacheStatistics updatedCacheStatistics = provider - .getCacheStatistics(this.cacheManager, books); - assertCoreStatistics(updatedCacheStatistics, null, null, null); - } - - private void doTestCoreStatistics(CacheStatisticsProvider provider, - boolean supportSize) { - Cache books = getCache("books"); - CacheStatistics cacheStatistics = provider.getCacheStatistics(this.cacheManager, - books); - assertCoreStatistics(cacheStatistics, (supportSize ? 0L : null), null, null); - getOrCreate(books, "a", "b", "b", "a", "a", "a"); - CacheStatistics updatedCacheStatistics = provider - .getCacheStatistics(this.cacheManager, books); - assertCoreStatistics(updatedCacheStatistics, (supportSize ? 2L : null), 0.66D, - 0.33D); - } - - private void assertCoreStatistics(CacheStatistics metrics, Long size, Double hitRatio, - Double missRatio) { - assertThat(metrics).isNotNull(); - assertThat(metrics.getSize()).isEqualTo(size); - checkRatio("Wrong hit ratio for metrics " + metrics, hitRatio, - metrics.getHitRatio()); - checkRatio("Wrong miss ratio for metrics " + metrics, missRatio, - metrics.getMissRatio()); - } - - private void checkRatio(String message, Double expected, Double actual) { - if (expected == null || actual == null) { - assertThat(actual).as(message).isEqualTo(expected); - } - else { - assertThat(actual).as(message).isEqualTo(expected, offset(0.01D)); - } - } - - private void getOrCreate(Cache cache, String... ids) { - for (String id : ids) { - Cache.ValueWrapper wrapper = cache.get(id); - if (wrapper == null) { - cache.put(id, id); - } - } - } - - private Cache getCache(String cacheName) { - Cache cache = this.cacheManager.getCache(cacheName); - Assert.notNull(cache, "No cache with name '" + cacheName + "' found."); - return cache; - } - - private void load(Class... config) { - this.context = new AnnotationConfigApplicationContext(); - if (config.length > 0) { - this.context.register(config); - } - this.context.register(CacheStatisticsAutoConfiguration.class); - this.context.refresh(); - this.cacheManager = this.context.getBean(CacheManager.class); - } - - @Configuration - static class JCacheCacheConfig { - - @Bean - public JCacheCacheManager cacheManager() { - javax.cache.CacheManager cacheManager = jCacheCacheManager(); - return new JCacheCacheManager(cacheManager); - } - - @Bean - public javax.cache.CacheManager jCacheCacheManager() { - javax.cache.CacheManager cacheManager = Caching - .getCachingProvider(HazelcastCachingProvider.class.getName()) - .getCacheManager(); - MutableConfiguration config = new MutableConfiguration<>(); - config.setStatisticsEnabled(true); - cacheManager.createCache("books", config); - cacheManager.createCache("speakers", config); - return cacheManager; - } - - } - - @Configuration - static class EhCacheConfig { - - @Bean - public EhCacheCacheManager cacheManager() { - return new EhCacheCacheManager(ehCacheCacheManager()); - } - - @Bean - public net.sf.ehcache.CacheManager ehCacheCacheManager() { - return EhCacheManagerUtils - .buildCacheManager(new ClassPathResource("cache/test-ehcache.xml")); - } - - } - - @Configuration - static class HazelcastConfig { - - @Bean - public HazelcastCacheManager cacheManager() throws IOException { - return new HazelcastCacheManager(hazelcastInstance()); - } - - @Bean - public HazelcastInstance hazelcastInstance() throws IOException { - Resource resource = new ClassPathResource("cache/test-hazelcast.xml"); - Config cfg = new XmlConfigBuilder(resource.getURL()).build(); - return Hazelcast.newHazelcastInstance(cfg); - } - - } - - @Configuration - static class InfinispanConfig { - - @Bean - public SpringEmbeddedCacheManager cacheManager() throws IOException { - return new SpringEmbeddedCacheManager(embeddedCacheManager()); - } - - @Bean - public EmbeddedCacheManager embeddedCacheManager() throws IOException { - Resource resource = new ClassPathResource("cache/test-infinispan.xml"); - try (InputStream in = resource.getInputStream()) { - return new DefaultCacheManager(in); - } - } - - } - - @Configuration - static class ConcurrentMapConfig { - - @Bean - public ConcurrentMapCacheManager cacheManager() { - return new ConcurrentMapCacheManager("books", "speakers"); - } - - } - - @Configuration - static class NoOpCacheConfig { - - @Bean - public NoOpCacheManager cacheManager() { - return new NoOpCacheManager(); - } - - } - - @Configuration - static class CaffeineCacheConfig { - - @Bean - public CaffeineCacheManager cacheManager() { - CaffeineCacheManager cacheManager = new CaffeineCacheManager(); - cacheManager.setCaffeine(Caffeine.newBuilder().recordStats()); - cacheManager.setCacheNames(Arrays.asList("books", "speaker")); - return cacheManager; - } - - } - -} diff --git a/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/integrationtest/EndpointAutoConfigurationClasses.java b/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/integrationtest/EndpointAutoConfigurationClasses.java index 621d3bea075e..3f1d697e8408 100644 --- a/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/integrationtest/EndpointAutoConfigurationClasses.java +++ b/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/integrationtest/EndpointAutoConfigurationClasses.java @@ -28,7 +28,6 @@ import org.springframework.boot.actuate.autoconfigure.health.HealthEndpointAutoConfiguration; import org.springframework.boot.actuate.autoconfigure.info.InfoEndpointAutoConfiguration; 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.web.servlet.RequestMappingEndpointAutoConfiguration; @@ -50,7 +49,6 @@ final class EndpointAutoConfigurationClasses { all.add(HealthEndpointAutoConfiguration.class); all.add(InfoEndpointAutoConfiguration.class); all.add(ThreadDumpEndpointAutoConfiguration.class); - all.add(MetricsEndpointAutoConfiguration.class); all.add(TraceEndpointAutoConfiguration.class); all.add(RequestMappingEndpointAutoConfiguration.class); ALL = all.toArray(new Class[] {}); diff --git a/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/integrationtest/JmxEndpointIntegrationTests.java b/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/integrationtest/JmxEndpointIntegrationTests.java index 093d0813b090..7ee3366e3bcc 100644 --- a/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/integrationtest/JmxEndpointIntegrationTests.java +++ b/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/integrationtest/JmxEndpointIntegrationTests.java @@ -55,7 +55,7 @@ public void jmxEndpointsAreExposed() { MBeanServer mBeanServer = context.getBean(MBeanServer.class); checkEndpointMBeans(mBeanServer, new String[] { "autoconfig", "beans", "configprops", "env", "health", - "info", "mappings", "metrics", "status", "threaddump", + "info", "mappings", "status", "threaddump", "trace" }, new String[] { "shutdown" }); }); @@ -68,7 +68,7 @@ public void jmxEndpointsCanBeDisabled() { MBeanServer mBeanServer = context.getBean(MBeanServer.class); checkEndpointMBeans(mBeanServer, new String[0], new String[] { "autoconfig", "beans", "configprops", "env", - "health", "mappings", "metrics", "shutdown", + "health", "mappings", "shutdown", "threaddump", "trace" }); }); @@ -81,7 +81,7 @@ public void singleJmxEndpointCanBeEnabled() { MBeanServer mBeanServer = context.getBean(MBeanServer.class); checkEndpointMBeans(mBeanServer, new String[] { "beans" }, new String[] { "autoconfig", "configprops", "env", "health", - "mappings", "metrics", "shutdown", + "mappings", "shutdown", "threaddump", "trace" }); }); } diff --git a/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/integrationtest/WebMvcEndpointExposureIntegrationTests.java b/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/integrationtest/WebMvcEndpointExposureIntegrationTests.java index 49ba15ab60d3..5790bd476b87 100644 --- a/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/integrationtest/WebMvcEndpointExposureIntegrationTests.java +++ b/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/integrationtest/WebMvcEndpointExposureIntegrationTests.java @@ -71,7 +71,6 @@ public void webEndpointsAreDisabledByDefault() { assertThat(isExposed(mvc, HttpMethod.GET, "health")).isFalse(); assertThat(isExposed(mvc, HttpMethod.GET, "info")).isTrue(); 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.GET, "status")).isTrue(); assertThat(isExposed(mvc, HttpMethod.GET, "threaddump")).isFalse(); @@ -92,7 +91,6 @@ public void webEndpointsCanBeEnabled() { assertThat(isExposed(mvc, HttpMethod.GET, "health")).isTrue(); assertThat(isExposed(mvc, HttpMethod.GET, "info")).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.GET, "status")).isTrue(); assertThat(isExposed(mvc, HttpMethod.GET, "threaddump")).isTrue(); @@ -114,7 +112,6 @@ public void singleWebEndpointCanBeEnabled() { assertThat(isExposed(mvc, HttpMethod.GET, "health")).isFalse(); assertThat(isExposed(mvc, HttpMethod.GET, "info")).isTrue(); 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.GET, "status")).isTrue(); assertThat(isExposed(mvc, HttpMethod.GET, "threaddump")).isFalse(); diff --git a/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/metrics/MeterRegistryConfigurerTests.java b/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/metrics/MeterRegistryConfigurerTests.java new file mode 100644 index 000000000000..3c21c63cbffc --- /dev/null +++ b/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/metrics/MeterRegistryConfigurerTests.java @@ -0,0 +1,58 @@ +/* + * 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 io.micrometer.core.instrument.MeterRegistry; +import org.junit.Test; + +import org.springframework.boot.autoconfigure.AutoConfigurations; +import org.springframework.boot.context.annotation.UserConfigurations; +import org.springframework.boot.test.context.runner.ApplicationContextRunner; +import org.springframework.context.annotation.Bean; + +import static org.assertj.core.api.Assertions.assertThat; + +/** + * Tests for applying {@link MeterRegistryConfigurer MeterRegistryConfigurers}. + * + * @author Jon Schneider + * @author Andy Wilkinson + */ +public class MeterRegistryConfigurerTests { + + @Test + public void commonTagsAreAppliedToAutoConfiguredBinders() { + new ApplicationContextRunner() + .withConfiguration(AutoConfigurations.of(MetricsAutoConfiguration.class)) + .withConfiguration( + UserConfigurations.of(MeterRegistryConfigurerConfiguration.class)) + .withPropertyValues("metrics.use-global-registry=false") + .run((context) -> assertThat(context.getBean(MeterRegistry.class) + .find("jvm.memory.used").tags("region", "us-east-1").gauge()) + .isPresent()); + } + + static class MeterRegistryConfigurerConfiguration { + + @Bean + public MeterRegistryConfigurer registryConfigurer() { + return (registry) -> registry.config().commonTags("region", "us-east-1"); + } + + } + +} diff --git a/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/metrics/MetricExportAutoConfigurationTests.java b/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/metrics/MetricExportAutoConfigurationTests.java deleted file mode 100644 index 9d5bdb614fe6..000000000000 --- a/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/metrics/MetricExportAutoConfigurationTests.java +++ /dev/null @@ -1,197 +0,0 @@ -/* - * 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 exporters = (Map) 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); - } - - } - -} diff --git a/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/metrics/MetricFilterAutoConfigurationTests.java b/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/metrics/MetricFilterAutoConfigurationTests.java deleted file mode 100644 index ecffe6854a74..000000000000 --- a/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/metrics/MetricFilterAutoConfigurationTests.java +++ /dev/null @@ -1,561 +0,0 @@ -/* - * 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.io.IOException; -import java.util.concurrent.CountDownLatch; - -import javax.servlet.Filter; -import javax.servlet.FilterChain; -import javax.servlet.ServletException; -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; - -import org.junit.Test; - -import org.springframework.boot.actuate.metrics.CounterService; -import org.springframework.boot.actuate.metrics.GaugeService; -import org.springframework.boot.actuate.metrics.web.servlet.MetricsFilter; -import org.springframework.boot.actuate.metrics.web.servlet.MetricsFilterSubmission; -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.core.annotation.Order; -import org.springframework.http.HttpStatus; -import org.springframework.http.ResponseEntity; -import org.springframework.mock.web.MockHttpServletRequest; -import org.springframework.mock.web.MockHttpServletResponse; -import org.springframework.stereotype.Component; -import org.springframework.test.web.servlet.MockMvc; -import org.springframework.test.web.servlet.MvcResult; -import org.springframework.test.web.servlet.setup.MockMvcBuilders; -import org.springframework.web.bind.annotation.PathVariable; -import org.springframework.web.bind.annotation.RequestMapping; -import org.springframework.web.bind.annotation.ResponseBody; -import org.springframework.web.bind.annotation.ResponseStatus; -import org.springframework.web.bind.annotation.RestController; -import org.springframework.web.context.request.async.DeferredResult; -import org.springframework.web.filter.OncePerRequestFilter; -import org.springframework.web.util.NestedServletException; - -import static org.assertj.core.api.Assertions.assertThat; -import static org.hamcrest.Matchers.is; -import static org.hamcrest.Matchers.notNullValue; -import static org.hamcrest.Matchers.nullValue; -import static org.junit.Assert.fail; -import static org.mockito.ArgumentMatchers.anyDouble; -import static org.mockito.ArgumentMatchers.anyString; -import static org.mockito.ArgumentMatchers.eq; -import static org.mockito.BDDMockito.willAnswer; -import static org.mockito.BDDMockito.willThrow; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.never; -import static org.mockito.Mockito.times; -import static org.mockito.Mockito.verify; -import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.asyncDispatch; -import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; -import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post; -import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.request; -import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; - -/** - * Tests for {@link MetricFilterAutoConfiguration}. - * - * @author Phillip Webb - * @author Andy Wilkinson - * @author Stephane Nicoll - */ -public class MetricFilterAutoConfigurationTests { - - @Test - public void defaultMetricFilterAutoConfigurationProperties() { - MetricFilterProperties properties = new MetricFilterProperties(); - assertThat(properties.getGaugeSubmissions()) - .containsExactly(MetricsFilterSubmission.MERGED); - assertThat(properties.getCounterSubmissions()) - .containsExactly(MetricsFilterSubmission.MERGED); - } - - @Test - public void recordsHttpInteractions() throws Exception { - AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext( - Config.class, MetricFilterAutoConfiguration.class); - try { - Filter filter = context.getBean(Filter.class); - MockHttpServletRequest request = new MockHttpServletRequest("GET", - "/test/path"); - MockHttpServletResponse response = new MockHttpServletResponse(); - FilterChain chain = mock(FilterChain.class); - willAnswer((invocation) -> { - response.setStatus(200); - return null; - }).given(chain).doFilter(request, response); - filter.doFilter(request, response, chain); - verify(context.getBean(CounterService.class)) - .increment("status.200.test.path"); - verify(context.getBean(GaugeService.class)).submit(eq("response.test.path"), - anyDouble()); - } - finally { - context.close(); - } - } - - @Test - public void recordsHttpInteractionsWithTemplateVariable() throws Exception { - AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext( - Config.class, MetricFilterAutoConfiguration.class); - Filter filter = context.getBean(Filter.class); - MockMvc mvc = MockMvcBuilders.standaloneSetup(new MetricFilterTestController()) - .addFilter(filter).build(); - mvc.perform(get("/templateVarTest/foo")).andExpect(status().isOk()); - verify(context.getBean(CounterService.class)) - .increment("status.200.templateVarTest.someVariable"); - verify(context.getBean(GaugeService.class)) - .submit(eq("response.templateVarTest.someVariable"), anyDouble()); - context.close(); - } - - @Test - public void recordsHttpInteractionsWithRegexTemplateVariable() throws Exception { - AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext( - Config.class, MetricFilterAutoConfiguration.class); - Filter filter = context.getBean(Filter.class); - MockMvc mvc = MockMvcBuilders.standaloneSetup(new MetricFilterTestController()) - .addFilter(filter).build(); - mvc.perform(get("/templateVarRegexTest/foo")).andExpect(status().isOk()); - verify(context.getBean(CounterService.class)) - .increment("status.200.templateVarRegexTest.someVariable"); - verify(context.getBean(GaugeService.class)) - .submit(eq("response.templateVarRegexTest.someVariable"), anyDouble()); - context.close(); - } - - @Test - public void recordsHttpInteractionsWithWildcardMapping() throws Exception { - AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext( - Config.class, MetricFilterAutoConfiguration.class); - Filter filter = context.getBean(Filter.class); - MockMvc mvc = MockMvcBuilders.standaloneSetup(new MetricFilterTestController()) - .addFilter(filter).build(); - mvc.perform(get("/wildcardMapping/foo")).andExpect(status().isOk()); - verify(context.getBean(CounterService.class)) - .increment("status.200.wildcardMapping.star"); - verify(context.getBean(GaugeService.class)) - .submit(eq("response.wildcardMapping.star"), anyDouble()); - context.close(); - } - - @Test - public void recordsHttpInteractionsWithDoubleWildcardMapping() throws Exception { - AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext( - Config.class, MetricFilterAutoConfiguration.class); - Filter filter = context.getBean(Filter.class); - MockMvc mvc = MockMvcBuilders.standaloneSetup(new MetricFilterTestController()) - .addFilter(filter).build(); - mvc.perform(get("/doubleWildcardMapping/foo/bar/baz")).andExpect(status().isOk()); - verify(context.getBean(CounterService.class)) - .increment("status.200.doubleWildcardMapping.star-star.baz"); - verify(context.getBean(GaugeService.class)) - .submit(eq("response.doubleWildcardMapping.star-star.baz"), anyDouble()); - context.close(); - } - - @Test - public void recordsKnown404HttpInteractionsAsSingleMetricWithPathAndTemplateVariable() - throws Exception { - AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext( - Config.class, MetricFilterAutoConfiguration.class); - Filter filter = context.getBean(Filter.class); - MockMvc mvc = MockMvcBuilders.standaloneSetup(new MetricFilterTestController()) - .addFilter(filter).build(); - mvc.perform(get("/knownPath/foo")).andExpect(status().isNotFound()); - verify(context.getBean(CounterService.class)) - .increment("status.404.knownPath.someVariable"); - verify(context.getBean(GaugeService.class)) - .submit(eq("response.knownPath.someVariable"), anyDouble()); - context.close(); - } - - @Test - public void records404HttpInteractionsAsSingleMetric() throws Exception { - AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext( - Config.class, MetricFilterAutoConfiguration.class); - Filter filter = context.getBean(Filter.class); - MockMvc mvc = MockMvcBuilders.standaloneSetup(new MetricFilterTestController()) - .addFilter(filter).build(); - mvc.perform(get("/unknownPath/1")).andExpect(status().isNotFound()); - mvc.perform(get("/unknownPath/2")).andExpect(status().isNotFound()); - verify(context.getBean(CounterService.class), times(2)) - .increment("status.404.unmapped"); - verify(context.getBean(GaugeService.class), times(2)) - .submit(eq("response.unmapped"), anyDouble()); - context.close(); - } - - @Test - public void records302HttpInteractionsAsSingleMetric() throws Exception { - AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext( - Config.class, MetricFilterAutoConfiguration.class, RedirectFilter.class); - MetricsFilter filter = context.getBean(MetricsFilter.class); - MockMvc mvc = MockMvcBuilders.standaloneSetup(new MetricFilterTestController()) - .addFilter(filter).addFilter(context.getBean(RedirectFilter.class)) - .build(); - mvc.perform(get("/unknownPath/1")).andExpect(status().is3xxRedirection()); - mvc.perform(get("/unknownPath/2")).andExpect(status().is3xxRedirection()); - verify(context.getBean(CounterService.class), times(2)) - .increment("status.302.unmapped"); - verify(context.getBean(GaugeService.class), times(2)) - .submit(eq("response.unmapped"), anyDouble()); - context.close(); - } - - @Test - public void skipsFilterIfMissingServices() throws Exception { - AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext( - MetricFilterAutoConfiguration.class); - assertThat(context.getBeansOfType(Filter.class).size()).isEqualTo(0); - context.close(); - } - - @Test - public void skipsFilterIfPropertyDisabled() throws Exception { - AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(); - TestPropertyValues.of("management.metrics.filter.enabled:false").applyTo(context); - context.register(Config.class, MetricFilterAutoConfiguration.class); - context.refresh(); - assertThat(context.getBeansOfType(Filter.class).size()).isEqualTo(0); - context.close(); - } - - @Test - public void controllerMethodThatThrowsUnhandledException() throws Exception { - AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext( - Config.class, MetricFilterAutoConfiguration.class); - Filter filter = context.getBean(Filter.class); - MockMvc mvc = MockMvcBuilders.standaloneSetup(new MetricFilterTestController()) - .addFilter(filter).build(); - try { - mvc.perform(get("/unhandledException")) - .andExpect(status().isInternalServerError()); - } - catch (NestedServletException ex) { - // Expected - } - verify(context.getBean(CounterService.class)) - .increment("status.500.unhandledException"); - verify(context.getBean(GaugeService.class)) - .submit(eq("response.unhandledException"), anyDouble()); - context.close(); - } - - @Test - public void gaugeServiceThatThrows() throws Exception { - AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext( - Config.class, MetricFilterAutoConfiguration.class); - GaugeService gaugeService = context.getBean(GaugeService.class); - willThrow(new IllegalStateException()).given(gaugeService).submit(anyString(), - anyDouble()); - Filter filter = context.getBean(Filter.class); - MockMvc mvc = MockMvcBuilders.standaloneSetup(new MetricFilterTestController()) - .addFilter(filter).build(); - mvc.perform(get("/templateVarTest/foo")).andExpect(status().isOk()); - verify(context.getBean(CounterService.class)) - .increment("status.200.templateVarTest.someVariable"); - verify(context.getBean(GaugeService.class)) - .submit(eq("response.templateVarTest.someVariable"), anyDouble()); - context.close(); - } - - @Test - public void correctlyRecordsMetricsForDeferredResultResponse() throws Exception { - AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext( - Config.class, MetricFilterAutoConfiguration.class); - MetricsFilter filter = context.getBean(MetricsFilter.class); - CountDownLatch latch = new CountDownLatch(1); - MockMvc mvc = MockMvcBuilders - .standaloneSetup(new MetricFilterTestController(latch)).addFilter(filter) - .build(); - String attributeName = MetricsFilter.class.getName() + ".StopWatch"; - MvcResult result = mvc.perform(post("/create")).andExpect(status().isOk()) - .andExpect(request().asyncStarted()) - .andExpect(request().attribute(attributeName, is(notNullValue()))) - .andReturn(); - latch.countDown(); - mvc.perform(asyncDispatch(result)).andExpect(status().isCreated()) - .andExpect(request().attribute(attributeName, is(nullValue()))); - verify(context.getBean(CounterService.class)).increment("status.201.create"); - context.close(); - } - - @Test - public void correctlyRecordsMetricsForFailedDeferredResultResponse() - throws Exception { - AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext( - Config.class, MetricFilterAutoConfiguration.class); - MetricsFilter filter = context.getBean(MetricsFilter.class); - CountDownLatch latch = new CountDownLatch(1); - MockMvc mvc = MockMvcBuilders - .standaloneSetup(new MetricFilterTestController(latch)).addFilter(filter) - .build(); - String attributeName = MetricsFilter.class.getName() + ".StopWatch"; - MvcResult result = mvc.perform(post("/createFailure")).andExpect(status().isOk()) - .andExpect(request().asyncStarted()) - .andExpect(request().attribute(attributeName, is(notNullValue()))) - .andReturn(); - latch.countDown(); - try { - mvc.perform(asyncDispatch(result)); - fail(); - } - catch (Exception ex) { - assertThat(result.getRequest().getAttribute(attributeName)).isNull(); - verify(context.getBean(CounterService.class)) - .increment("status.500.createFailure"); - } - finally { - context.close(); - } - } - - @Test - public void records5xxxHttpInteractionsAsSingleMetric() throws Exception { - AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext( - Config.class, MetricFilterAutoConfiguration.class, - ServiceUnavailableFilter.class); - MetricsFilter filter = context.getBean(MetricsFilter.class); - MockMvc mvc = MockMvcBuilders.standaloneSetup(new MetricFilterTestController()) - .addFilter(filter) - .addFilter(context.getBean(ServiceUnavailableFilter.class)).build(); - mvc.perform(get("/unknownPath/1")).andExpect(status().isServiceUnavailable()); - mvc.perform(get("/unknownPath/2")).andExpect(status().isServiceUnavailable()); - verify(context.getBean(CounterService.class), times(2)) - .increment("status.503.unmapped"); - verify(context.getBean(GaugeService.class), times(2)) - .submit(eq("response.unmapped"), anyDouble()); - context.close(); - } - - @Test - public void additionallyRecordsMetricsWithHttpMethodNameIfConfigured() - throws Exception { - AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(); - context.register(Config.class, MetricFilterAutoConfiguration.class); - TestPropertyValues - .of("management.metrics.filter.gauge-submissions=merged,per-http-method", - "management.metrics.filter.counter-submissions=merged,per-http-method") - .applyTo(context); - context.refresh(); - Filter filter = context.getBean(Filter.class); - MockHttpServletRequest request = new MockHttpServletRequest("PUT", "/test/path"); - MockHttpServletResponse response = new MockHttpServletResponse(); - FilterChain chain = mock(FilterChain.class); - willAnswer((invocation) -> { - response.setStatus(200); - return null; - }).given(chain).doFilter(request, response); - filter.doFilter(request, response, chain); - verify(context.getBean(GaugeService.class)).submit(eq("response.test.path"), - anyDouble()); - verify(context.getBean(GaugeService.class)).submit(eq("response.PUT.test.path"), - anyDouble()); - verify(context.getBean(CounterService.class)) - .increment(eq("status.200.test.path")); - verify(context.getBean(CounterService.class)) - .increment(eq("status.PUT.200.test.path")); - context.close(); - } - - @Test - public void doesNotRecordRolledUpMetricsIfConfigured() throws Exception { - AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(); - context.register(Config.class, MetricFilterAutoConfiguration.class); - TestPropertyValues - .of("management.metrics.filter.gauge-submissions=", - "management.metrics.filter.counter-submissions=") - .applyTo(context); - context.refresh(); - Filter filter = context.getBean(Filter.class); - MockHttpServletRequest request = new MockHttpServletRequest("PUT", "/test/path"); - MockHttpServletResponse response = new MockHttpServletResponse(); - FilterChain chain = mock(FilterChain.class); - willAnswer((invocation) -> { - response.setStatus(200); - return null; - }).given(chain).doFilter(request, response); - filter.doFilter(request, response, chain); - verify(context.getBean(GaugeService.class), never()).submit(anyString(), - anyDouble()); - verify(context.getBean(CounterService.class), never()).increment(anyString()); - context.close(); - } - - @Test - public void whenExceptionIsThrownResponseStatusIsUsedWhenResponseHasBeenCommitted() - throws Exception { - AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(); - try { - context.register(Config.class, MetricFilterAutoConfiguration.class); - context.refresh(); - Filter filter = context.getBean(Filter.class); - MockHttpServletRequest request = new MockHttpServletRequest("GET", - "/test/path"); - MockHttpServletResponse response = new MockHttpServletResponse(); - FilterChain chain = mock(FilterChain.class); - willAnswer((invocation) -> { - response.setStatus(200); - response.setCommitted(true); - throw new IOException(); - }).given(chain).doFilter(request, response); - try { - filter.doFilter(request, response, chain); - fail(); - } - catch (IOException ex) { - // Continue - } - verify(context.getBean(CounterService.class)) - .increment(eq("status.200.test.path")); - } - finally { - context.close(); - } - } - - @Configuration - public static class Config { - - @Bean - public CounterService counterService() { - return mock(CounterService.class); - } - - @Bean - public GaugeService gaugeService() { - return mock(GaugeService.class); - } - - } - - @RestController - class MetricFilterTestController { - - private final CountDownLatch latch; - - MetricFilterTestController() { - this(null); - } - - MetricFilterTestController(CountDownLatch latch) { - this.latch = latch; - } - - @RequestMapping("templateVarTest/{someVariable}") - public String testTemplateVariableResolution(@PathVariable String someVariable) { - return someVariable; - } - - @RequestMapping("wildcardMapping/*") - public String testWildcardMapping() { - return "wildcard"; - } - - @RequestMapping("doubleWildcardMapping/**/baz") - public String testDoubleWildcardMapping() { - return "doubleWildcard"; - } - - @RequestMapping("templateVarRegexTest/{someVariable:[a-z]+}") - public String testTemplateVariableRegexResolution( - @PathVariable String someVariable) { - return someVariable; - } - - @RequestMapping("knownPath/{someVariable}") - @ResponseStatus(HttpStatus.NOT_FOUND) - @ResponseBody - public String testKnownPathWith404Response(@PathVariable String someVariable) { - return someVariable; - } - - @ResponseBody - @RequestMapping("unhandledException") - public String testException() { - throw new RuntimeException(); - } - - @RequestMapping("create") - public DeferredResult> create() { - final DeferredResult> result = new DeferredResult<>(); - new Thread(() -> { - try { - MetricFilterTestController.this.latch.await(); - result.setResult(new ResponseEntity<>("Done", HttpStatus.CREATED)); - } - catch (InterruptedException ex) { - } - }).start(); - return result; - } - - @RequestMapping("createFailure") - public DeferredResult> createFailure() { - final DeferredResult> result = new DeferredResult<>(); - new Thread(() -> { - try { - MetricFilterTestController.this.latch.await(); - result.setErrorResult(new Exception("It failed")); - } - catch (InterruptedException ex) { - } - }).start(); - return result; - } - - } - - @Component - @Order(0) - public static class RedirectFilter extends OncePerRequestFilter { - - @Override - protected void doFilterInternal(HttpServletRequest request, - HttpServletResponse response, FilterChain chain) - throws ServletException, IOException { - // send redirect before filter chain is executed, like Spring Security sending - // us back to a login page - response.sendRedirect("http://example.com"); - } - - } - - @Component - @Order(0) - public static class ServiceUnavailableFilter extends OncePerRequestFilter { - - @Override - protected void doFilterInternal(HttpServletRequest request, - HttpServletResponse response, FilterChain chain) - throws ServletException, IOException { - - response.sendError(HttpStatus.SERVICE_UNAVAILABLE.value()); - } - - } - -} diff --git a/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/metrics/MetricRepositoryAutoConfigurationTests.java b/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/metrics/MetricRepositoryAutoConfigurationTests.java deleted file mode 100644 index 54ba51d663bc..000000000000 --- a/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/metrics/MetricRepositoryAutoConfigurationTests.java +++ /dev/null @@ -1,108 +0,0 @@ -/* - * 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 gauge = (Gauge) 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); - } - - } - -} diff --git a/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/metrics/MetricsAutoConfigurationTests.java b/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/metrics/MetricsAutoConfigurationTests.java new file mode 100644 index 000000000000..a988cb9cd358 --- /dev/null +++ b/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/metrics/MetricsAutoConfigurationTests.java @@ -0,0 +1,141 @@ +/* + * 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 personName() { + return Collections.singleton("Jon"); + } + + } + +} diff --git a/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/metrics/MetricsEndpointAutoConfigurationTests.java b/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/metrics/MetricsConfigurationCompositeTests.java similarity index 53% rename from spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/metrics/MetricsEndpointAutoConfigurationTests.java rename to spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/metrics/MetricsConfigurationCompositeTests.java index e54a557fd142..60c5d2ead81d 100644 --- a/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/metrics/MetricsEndpointAutoConfigurationTests.java +++ b/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/metrics/MetricsConfigurationCompositeTests.java @@ -16,36 +16,32 @@ 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.springframework.boot.actuate.metrics.MetricsEndpoint; import org.springframework.boot.autoconfigure.AutoConfigurations; import org.springframework.boot.test.context.runner.ApplicationContextRunner; 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 { - - private final ApplicationContextRunner contextRunner = new ApplicationContextRunner() - .withConfiguration( - AutoConfigurations.of(MetricsEndpointAutoConfiguration.class)); - - @Test - public void runShouldHaveEndpointBean() { - this.contextRunner.run( - (context) -> assertThat(context).hasSingleBean(MetricsEndpoint.class)); - } +public class MetricsConfigurationCompositeTests { @Test - public void runWhenEnabledPropertyIsFalseShouldNotHaveEndpointBean() - throws Exception { - this.contextRunner.withPropertyValues("endpoints.metrics.enabled:false").run( - (context) -> assertThat(context).doesNotHaveBean(MetricsEndpoint.class)); + public void compositeContainsImplementationsOnClasspath() { + new ApplicationContextRunner() + .withConfiguration(AutoConfigurations.of(MetricsAutoConfiguration.class)) + .withPropertyValues("metrics.use-global-registry=false") + .run((context) -> assertThat( + context.getBean(CompositeMeterRegistry.class).getRegistries()) + .hasAtLeastOneElementOfType(PrometheusMeterRegistry.class) + .hasAtLeastOneElementOfType(GraphiteMeterRegistry.class)); } } diff --git a/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/metrics/MetricsDropwizardAutoConfigurationTests.java b/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/metrics/MetricsDropwizardAutoConfigurationTests.java deleted file mode 100644 index 6a9d39f3444f..000000000000 --- a/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/metrics/MetricsDropwizardAutoConfigurationTests.java +++ /dev/null @@ -1,91 +0,0 @@ -/* - * 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(); - } - - } - -} diff --git a/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/metrics/PublicMetricsAutoConfigurationTests.java b/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/metrics/PublicMetricsAutoConfigurationTests.java deleted file mode 100644 index 49be63615570..000000000000 --- a/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/metrics/PublicMetricsAutoConfigurationTests.java +++ /dev/null @@ -1,382 +0,0 @@ -/* - * 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.sql.SQLException; -import java.util.Collection; -import java.util.Collections; -import java.util.HashMap; -import java.util.Map; - -import javax.sql.DataSource; - -import com.zaxxer.hikari.HikariDataSource; -import org.apache.commons.dbcp2.BasicDataSource; -import org.junit.After; -import org.junit.Test; - -import org.springframework.boot.actuate.autoconfigure.cache.CacheStatisticsAutoConfiguration; -import org.springframework.boot.actuate.autoconfigure.web.servlet.MockServletWebServerFactory; -import org.springframework.boot.actuate.cache.CachePublicMetrics; -import org.springframework.boot.actuate.metrics.DataSourcePublicMetrics; -import org.springframework.boot.actuate.metrics.Metric; -import org.springframework.boot.actuate.metrics.PublicMetrics; -import org.springframework.boot.actuate.metrics.SystemPublicMetrics; -import org.springframework.boot.actuate.metrics.TomcatPublicMetrics; -import org.springframework.boot.actuate.metrics.reader.MetricReaderPublicMetrics; -import org.springframework.boot.actuate.metrics.rich.RichGauge; -import org.springframework.boot.actuate.metrics.rich.RichGaugeReader; -import org.springframework.boot.actuate.metrics.rich.RichGaugeReaderPublicMetrics; -import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration; -import org.springframework.boot.autoconfigure.jdbc.metadata.DataSourcePoolMetadataProvidersConfiguration; -import org.springframework.boot.jdbc.DataSourceBuilder; -import org.springframework.boot.web.embedded.tomcat.TomcatServletWebServerFactory; -import org.springframework.boot.web.servlet.context.AnnotationConfigServletWebServerApplicationContext; -import org.springframework.cache.CacheManager; -import org.springframework.cache.concurrent.ConcurrentMapCacheManager; -import org.springframework.context.ConfigurableApplicationContext; -import org.springframework.context.annotation.AnnotationConfigApplicationContext; -import org.springframework.context.annotation.Bean; -import org.springframework.context.annotation.Configuration; -import org.springframework.context.annotation.Primary; -import org.springframework.core.annotation.Order; -import org.springframework.jdbc.core.ConnectionCallback; -import org.springframework.jdbc.core.JdbcTemplate; - -import static org.assertj.core.api.Assertions.assertThat; -import static org.junit.Assert.fail; -import static org.mockito.BDDMockito.given; -import static org.mockito.Mockito.mock; - -/** - * Tests for {@link PublicMetricsAutoConfiguration}. - * - * @author Stephane Nicoll - * @author Dave Syer - * @author Phillip Webb - */ -public class PublicMetricsAutoConfigurationTests { - - private ConfigurableApplicationContext context; - - @After - public void after() { - if (this.context != null) { - this.context.close(); - } - } - - @Test - public void systemPublicMetrics() throws Exception { - load(); - assertThat(this.context.getBeansOfType(SystemPublicMetrics.class)).hasSize(1); - } - - @Test - public void metricReaderPublicMetrics() throws Exception { - load(); - assertThat(this.context.getBeansOfType(MetricReaderPublicMetrics.class)) - .hasSize(2); - } - - @Test - public void richGaugePublicMetrics() { - AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext( - RichGaugeReaderConfig.class, MetricRepositoryAutoConfiguration.class, - PublicMetricsAutoConfiguration.class); - RichGaugeReader richGaugeReader = context.getBean(RichGaugeReader.class); - assertThat(richGaugeReader).isNotNull(); - given(richGaugeReader.findAll()) - .willReturn(Collections.singletonList(new RichGauge("bar", 3.7d))); - RichGaugeReaderPublicMetrics publicMetrics = context - .getBean(RichGaugeReaderPublicMetrics.class); - assertThat(publicMetrics).isNotNull(); - Collection> metrics = publicMetrics.metrics(); - assertThat(metrics).isNotNull(); - assertThat(6).isEqualTo(metrics.size()); - assertHasMetric(metrics, new Metric<>("bar.val", 3.7d)); - assertHasMetric(metrics, new Metric<>("bar.avg", 3.7d)); - assertHasMetric(metrics, new Metric<>("bar.min", 3.7d)); - assertHasMetric(metrics, new Metric<>("bar.max", 3.7d)); - assertHasMetric(metrics, new Metric<>("bar.alpha", -1.d)); - assertHasMetric(metrics, new Metric<>("bar.count", 1L)); - context.close(); - } - - @Test - public void noDataSource() { - load(); - assertThat(this.context.getBeansOfType(DataSourcePublicMetrics.class)).isEmpty(); - } - - @Test - public void autoDataSource() throws SQLException { - load(DataSourceAutoConfiguration.class); - PublicMetrics bean = this.context.getBean(DataSourcePublicMetrics.class); - this.context.getBean(DataSource.class).getConnection().close(); - Collection> metrics = bean.metrics(); - assertMetrics(metrics, "datasource.primary.active", "datasource.primary.usage"); - } - - @Test - public void multipleDataSources() { - load(MultipleDataSourcesConfig.class); - PublicMetrics bean = this.context.getBean(DataSourcePublicMetrics.class); - Collection> metrics = bean.metrics(); - assertMetrics(metrics, "datasource.tomcat.active", "datasource.tomcat.usage", - "datasource.commonsDbcp.active", "datasource.commonsDbcp.usage"); - // Hikari won't work unless a first connection has been retrieved - JdbcTemplate jdbcTemplate = new JdbcTemplate( - this.context.getBean("hikariDS", DataSource.class)); - jdbcTemplate.execute((ConnectionCallback) (connection) -> null); - Collection> anotherMetrics = bean.metrics(); - assertMetrics(anotherMetrics, "datasource.tomcat.active", - "datasource.tomcat.usage", "datasource.hikariDS.active", - "datasource.hikariDS.usage", "datasource.commonsDbcp.active", - "datasource.commonsDbcp.usage"); - } - - @Test - public void multipleDataSourcesWithPrimary() { - load(MultipleDataSourcesWithPrimaryConfig.class); - PublicMetrics bean = this.context.getBean(DataSourcePublicMetrics.class); - Collection> metrics = bean.metrics(); - assertMetrics(metrics, "datasource.primary.active", "datasource.primary.usage", - "datasource.commonsDbcp.active", "datasource.commonsDbcp.usage"); - } - - @Test - public void multipleDataSourcesWithCustomPrimary() { - load(MultipleDataSourcesWithCustomPrimaryConfig.class); - PublicMetrics bean = this.context.getBean(DataSourcePublicMetrics.class); - Collection> metrics = bean.metrics(); - assertMetrics(metrics, "datasource.primary.active", "datasource.primary.usage", - "datasource.dataSource.active", "datasource.dataSource.usage"); - } - - @Test - public void customPrefix() { - load(MultipleDataSourcesWithPrimaryConfig.class, - CustomDataSourcePublicMetrics.class); - PublicMetrics bean = this.context.getBean(DataSourcePublicMetrics.class); - Collection> metrics = bean.metrics(); - assertMetrics(metrics, "ds.first.active", "ds.first.usage", "ds.second.active", - "ds.second.usage"); - } - - @Test - public void tomcatMetrics() throws Exception { - loadWeb(TomcatConfiguration.class); - assertThat(this.context.getBeansOfType(TomcatPublicMetrics.class)).hasSize(1); - } - - @Test - public void noCacheMetrics() { - load(); - assertThat(this.context.getBeansOfType(CachePublicMetrics.class)).isEmpty(); - } - - @Test - public void autoCacheManager() { - load(CacheConfiguration.class); - CachePublicMetrics bean = this.context.getBean(CachePublicMetrics.class); - Collection> metrics = bean.metrics(); - assertMetrics(metrics, "cache.books.size", "cache.speakers.size"); - } - - @Test - public void multipleCacheManagers() { - load(MultipleCacheConfiguration.class); - CachePublicMetrics bean = this.context.getBean(CachePublicMetrics.class); - Collection> metrics = bean.metrics(); - assertMetrics(metrics, "cache.books.size", "cache.second_speakers.size", - "cache.first_speakers.size", "cache.users.size"); - } - - private void assertHasMetric(Collection> metrics, Metric metric) { - for (Metric m : metrics) { - if (m.getValue().equals(metric.getValue()) - && m.getName().equals(metric.getName())) { - return; - } - } - fail("Metric " + metric.toString() + " not found in " + metrics.toString()); - } - - private void assertMetrics(Collection> metrics, String... keys) { - Map content = new HashMap<>(); - for (Metric metric : metrics) { - content.put(metric.getName(), metric.getValue()); - } - for (String key : keys) { - assertThat(content).containsKey(key); - } - } - - private void loadWeb(Class... config) { - AnnotationConfigServletWebServerApplicationContext context = new AnnotationConfigServletWebServerApplicationContext(); - if (config.length > 0) { - context.register(config); - } - context.register(DataSourcePoolMetadataProvidersConfiguration.class, - CacheStatisticsAutoConfiguration.class, - PublicMetricsAutoConfiguration.class, MockServletWebServerFactory.class); - context.refresh(); - this.context = context; - } - - private void load(Class... config) { - AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(); - if (config.length > 0) { - context.register(config); - } - context.register(DataSourcePoolMetadataProvidersConfiguration.class, - CacheStatisticsAutoConfiguration.class, - PublicMetricsAutoConfiguration.class); - context.refresh(); - this.context = context; - } - - @Configuration - static class MultipleDataSourcesConfig { - - @Bean - public DataSource tomcatDataSource() { - return InitializedBuilder.create() - .type(org.apache.tomcat.jdbc.pool.DataSource.class).build(); - } - - @Bean - public DataSource hikariDS() { - return InitializedBuilder.create().type(HikariDataSource.class).build(); - } - - @Bean - public DataSource commonsDbcpDataSource() { - return InitializedBuilder.create().type(BasicDataSource.class).build(); - } - - } - - @Configuration - static class MultipleDataSourcesWithPrimaryConfig { - - @Bean - @Primary - public DataSource myDataSource() { - return InitializedBuilder.create() - .type(org.apache.tomcat.jdbc.pool.DataSource.class).build(); - } - - @Bean - public DataSource commonsDbcpDataSource() { - return InitializedBuilder.create().type(BasicDataSource.class).build(); - } - - } - - @Configuration - static class MultipleDataSourcesWithCustomPrimaryConfig { - - @Bean - @Primary - public DataSource myDataSource() { - return InitializedBuilder.create() - .type(org.apache.tomcat.jdbc.pool.DataSource.class).build(); - } - - @Bean - public DataSource dataSource() { - return InitializedBuilder.create().type(BasicDataSource.class).build(); - } - - } - - @Configuration - static class CustomDataSourcePublicMetrics { - - @Bean - public DataSourcePublicMetrics myDataSourcePublicMetrics() { - return new DataSourcePublicMetrics() { - @Override - protected String createPrefix(String dataSourceName, - DataSource dataSource, boolean primary) { - return (primary ? "ds.first." : "ds.second"); - } - }; - } - - } - - @Configuration - static class RichGaugeReaderConfig { - - @Bean - public RichGaugeReader richGaugeReader() { - return mock(RichGaugeReader.class); - } - - } - - @Configuration - static class TomcatConfiguration { - - @Bean - public TomcatServletWebServerFactory webServerFactory() { - return new TomcatServletWebServerFactory(0); - } - - } - - @Configuration - static class CacheConfiguration { - - @Bean - public CacheManager cacheManager() { - return new ConcurrentMapCacheManager("books", "speakers"); - } - - } - - @Configuration - static class MultipleCacheConfiguration { - - @Bean - @Order(1) - public CacheManager first() { - return new ConcurrentMapCacheManager("books", "speakers"); - } - - @Bean - @Order(2) - public CacheManager second() { - return new ConcurrentMapCacheManager("users", "speakers"); - } - - } - - private static class InitializedBuilder { - - public static DataSourceBuilder create() { - return DataSourceBuilder.create() - .driverClassName("org.hsqldb.jdbc.JDBCDriver") - .url("jdbc:hsqldb:mem:test").username("sa"); - } - - } - -} diff --git a/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/metrics/export/simple/SimpleExportConfigurationTests.java b/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/metrics/export/simple/SimpleExportConfigurationTests.java new file mode 100644 index 000000000000..b789df54681f --- /dev/null +++ b/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/metrics/export/simple/SimpleExportConfigurationTests.java @@ -0,0 +1,56 @@ +/* + * 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.simple; + +import io.micrometer.core.instrument.composite.CompositeMeterRegistry; +import io.micrometer.core.instrument.simple.SimpleMeterRegistry; +import org.junit.Test; +import org.junit.runner.RunWith; + +import org.springframework.boot.actuate.autoconfigure.metrics.MetricsAutoConfiguration; +import org.springframework.boot.autoconfigure.AutoConfigurations; +import org.springframework.boot.test.context.runner.ApplicationContextRunner; +import org.springframework.test.context.junit4.SpringRunner; + +import static org.assertj.core.api.Assertions.assertThat; + +/** + * Tests for {@link SimpleExportConfiguration}. + * + * @author Jon Schneider + */ +@RunWith(SpringRunner.class) +public class SimpleExportConfigurationTests { + + @Test + public void simpleMeterRegistryIsInTheCompositeWhenNoOtherRegistryIs() { + new ApplicationContextRunner() + .withPropertyValues("metrics.atlas.enabled=false", + "metrics.datadog.enabled=false", "metrics.ganglia.enabled=false", + "metrics.graphite.enabled=false", "metrics.influx.enabled=false", + "metrics.jmx.enabled=false", "metrics.prometheus.enabled=false") + .withConfiguration(AutoConfigurations.of(MetricsAutoConfiguration.class)) + .run((context) -> { + CompositeMeterRegistry meterRegistry = context + .getBean(CompositeMeterRegistry.class); + assertThat(meterRegistry.getRegistries()).hasSize(1); + assertThat(meterRegistry.getRegistries()) + .hasOnlyElementsOfType(SimpleMeterRegistry.class); + }); + } + +} diff --git a/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/metrics/integration/SpringIntegrationMetricReaderNoJmxTests.java b/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/metrics/integration/SpringIntegrationMetricReaderNoJmxTests.java deleted file mode 100644 index 3b0400796e02..000000000000 --- a/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/metrics/integration/SpringIntegrationMetricReaderNoJmxTests.java +++ /dev/null @@ -1,60 +0,0 @@ -/* - * 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.metrics.integration; - -import org.junit.Test; -import org.junit.runner.RunWith; - -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.beans.factory.annotation.Qualifier; -import org.springframework.boot.actuate.autoconfigure.metrics.PublicMetricsAutoConfiguration; -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 static org.assertj.core.api.Assertions.assertThat; - -/** - * Tests for {@link SpringIntegrationMetricReader}. - * - * @author Artem Bilan - */ -@RunWith(SpringRunner.class) -@SpringBootTest("spring.jmx.enabled=false") -@DirtiesContext -public class SpringIntegrationMetricReaderNoJmxTests { - - @Autowired - @Qualifier("springIntegrationPublicMetrics") - private MetricReaderPublicMetrics integrationMetricReader; - - @Test - public void test() { - assertThat(this.integrationMetricReader.metrics().size() > 0).isTrue(); - } - - @Configuration - @Import({ IntegrationAutoConfiguration.class, PublicMetricsAutoConfiguration.class }) - protected static class TestConfiguration { - - } - -} diff --git a/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/metrics/integration/SpringIntegrationMetricReaderTests.java b/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/metrics/integration/SpringIntegrationMetricReaderTests.java deleted file mode 100644 index 46e443dfdd38..000000000000 --- a/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/metrics/integration/SpringIntegrationMetricReaderTests.java +++ /dev/null @@ -1,66 +0,0 @@ -/* - * 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.metrics.integration; - -import org.junit.Test; -import org.junit.runner.RunWith; - -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.boot.autoconfigure.integration.IntegrationAutoConfiguration; -import org.springframework.boot.autoconfigure.jmx.JmxAutoConfiguration; -import org.springframework.boot.test.context.SpringBootTest; -import org.springframework.context.annotation.Bean; -import org.springframework.context.annotation.Configuration; -import org.springframework.context.annotation.Import; -import org.springframework.integration.support.management.IntegrationManagementConfigurer; -import org.springframework.test.annotation.DirtiesContext; -import org.springframework.test.context.junit4.SpringRunner; - -import static org.assertj.core.api.Assertions.assertThat; - -/** - * Tests for {@link SpringIntegrationMetricReader}. - * - * @author Dave Syer - * @author Artem Bilan - */ -@RunWith(SpringRunner.class) -@SpringBootTest("spring.jmx.enabled=true") -@DirtiesContext -public class SpringIntegrationMetricReaderTests { - - @Autowired - private SpringIntegrationMetricReader reader; - - @Test - public void test() { - assertThat(this.reader.count() > 0).isTrue(); - } - - @Configuration - @Import({ JmxAutoConfiguration.class, IntegrationAutoConfiguration.class }) - protected static class TestConfiguration { - - @Bean - public SpringIntegrationMetricReader reader( - IntegrationManagementConfigurer managementConfigurer) { - return new SpringIntegrationMetricReader(managementConfigurer); - } - - } - -} diff --git a/spring-boot-actuator/pom.xml b/spring-boot-actuator/pom.xml index 30fd37cc3d4a..2e69117a8a16 100644 --- a/spring-boot-actuator/pom.xml +++ b/spring-boot-actuator/pom.xml @@ -32,33 +32,28 @@ true - com.github.ben-manes.caffeine - caffeine - true - - - com.hazelcast - hazelcast + com.sun.mail + javax.mail true - com.hazelcast - hazelcast-spring + com.zaxxer + HikariCP true - com.sun.mail - javax.mail + io.lettuce + lettuce-core true - com.timgroup - java-statsd-client + io.micrometer + micrometer-core true - io.lettuce - lettuce-core + io.micrometer + micrometer-prometheus-starter true @@ -72,13 +67,13 @@ true - net.sf.ehcache - ehcache + org.apache.tomcat.embed + tomcat-embed-core true - org.apache.tomcat.embed - tomcat-embed-core + org.aspectj + aspectjweaver true diff --git a/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/cache/AbstractJmxCacheStatisticsProvider.java b/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/cache/AbstractJmxCacheStatisticsProvider.java deleted file mode 100644 index d922a8ef1121..000000000000 --- a/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/cache/AbstractJmxCacheStatisticsProvider.java +++ /dev/null @@ -1,133 +0,0 @@ -/* - * 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 The cache type - * @author Stephane Nicoll - * @since 1.3.0 - */ -public abstract class AbstractJmxCacheStatisticsProvider - implements CacheStatisticsProvider { - - private static final Logger logger = LoggerFactory - .getLogger(AbstractJmxCacheStatisticsProvider.class); - - private MBeanServer mBeanServer; - - private final Map 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 getAttribute(ObjectName objectName, String attributeName, - Class 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; - } - - } - -} diff --git a/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/cache/CachePublicMetrics.java b/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/cache/CachePublicMetrics.java deleted file mode 100644 index 4b7fd074d419..000000000000 --- a/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/cache/CachePublicMetrics.java +++ /dev/null @@ -1,160 +0,0 @@ -/* - * 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.util.Collection; -import java.util.HashSet; -import java.util.List; -import java.util.Map; - -import org.springframework.boot.actuate.metrics.Metric; -import org.springframework.boot.actuate.metrics.PublicMetrics; -import org.springframework.cache.Cache; -import org.springframework.cache.CacheManager; -import org.springframework.cache.transaction.TransactionAwareCacheDecorator; -import org.springframework.core.ResolvableType; -import org.springframework.util.ClassUtils; -import org.springframework.util.LinkedMultiValueMap; -import org.springframework.util.MultiValueMap; - -/** - * A {@link PublicMetrics} implementation that provides cache statistics. - * - * @author Stephane Nicoll - * @since 2.0.0 - */ -public class CachePublicMetrics implements PublicMetrics { - - private final Map cacheManagers; - - private final Collection> statisticsProviders; - - /** - * Create a new {@link CachePublicMetrics} instance. - * @param cacheManagers the cache managers - * @param statisticsProviders the statistics providers - */ - public CachePublicMetrics(Map cacheManagers, - Collection> statisticsProviders) { - this.cacheManagers = cacheManagers; - this.statisticsProviders = statisticsProviders; - } - - @Override - public Collection> metrics() { - Collection> metrics = new HashSet<>(); - for (Map.Entry> entry : getCacheManagerBeans() - .entrySet()) { - addMetrics(metrics, entry.getKey(), entry.getValue()); - } - return metrics; - } - - private MultiValueMap getCacheManagerBeans() { - MultiValueMap cacheManagerNamesByCacheName = new LinkedMultiValueMap<>(); - for (Map.Entry entry : this.cacheManagers.entrySet()) { - for (String cacheName : entry.getValue().getCacheNames()) { - cacheManagerNamesByCacheName.add(cacheName, - new CacheManagerBean(entry.getKey(), entry.getValue())); - } - } - return cacheManagerNamesByCacheName; - } - - private void addMetrics(Collection> metrics, String cacheName, - List cacheManagerBeans) { - for (CacheManagerBean cacheManagerBean : cacheManagerBeans) { - CacheManager cacheManager = cacheManagerBean.getCacheManager(); - Cache cache = unwrapIfNecessary(cacheManager.getCache(cacheName)); - CacheStatistics statistics = getCacheStatistics(cache, cacheManager); - if (statistics != null) { - String prefix = cacheName; - if (cacheManagerBeans.size() > 1) { - prefix = cacheManagerBean.getBeanName() + "_" + prefix; - } - prefix = "cache." + prefix + (prefix.endsWith(".") ? "" : "."); - metrics.addAll(statistics.toMetrics(prefix)); - } - } - } - - private Cache unwrapIfNecessary(Cache cache) { - if (ClassUtils.isPresent( - "org.springframework.cache.transaction.TransactionAwareCacheDecorator", - getClass().getClassLoader())) { - return TransactionAwareCacheDecoratorHandler.unwrapIfNecessary(cache); - } - return cache; - } - - @SuppressWarnings({ "rawtypes", "unchecked" }) - private CacheStatistics getCacheStatistics(Cache cache, CacheManager cacheManager) { - if (this.statisticsProviders != null) { - for (CacheStatisticsProvider provider : this.statisticsProviders) { - Class cacheType = ResolvableType - .forClass(CacheStatisticsProvider.class, provider.getClass()) - .resolveGeneric(); - if (cacheType.isInstance(cache)) { - CacheStatistics statistics = provider.getCacheStatistics(cacheManager, - cache); - if (statistics != null) { - return statistics; - } - } - } - } - return null; - } - - private static class CacheManagerBean { - - private final String beanName; - - private final CacheManager cacheManager; - - CacheManagerBean(String beanName, CacheManager cacheManager) { - this.beanName = beanName; - this.cacheManager = cacheManager; - } - - public String getBeanName() { - return this.beanName; - } - - public CacheManager getCacheManager() { - return this.cacheManager; - } - - } - - private static class TransactionAwareCacheDecoratorHandler { - - private static Cache unwrapIfNecessary(Cache cache) { - try { - if (cache instanceof TransactionAwareCacheDecorator) { - return ((TransactionAwareCacheDecorator) cache).getTargetCache(); - } - } - catch (NoClassDefFoundError ex) { - // Ignore - } - return cache; - } - - } - -} diff --git a/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/cache/CacheStatistics.java b/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/cache/CacheStatistics.java deleted file mode 100644 index f6cc96e1f988..000000000000 --- a/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/cache/CacheStatistics.java +++ /dev/null @@ -1,66 +0,0 @@ -/* - * Copyright 2012-2015 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.boot.actuate.cache; - -import java.util.Collection; - -import org.springframework.boot.actuate.metrics.Metric; - -/** - * Snapshot of the statistics of a given cache. {@code CacheStatistics} instances have a - * very short life as it represents the statistics of a cache at one particular point in - * time. - * - * @author Stephane Nicoll - * @since 1.3.0 - */ -public interface CacheStatistics { - - /** - * Generate the relevant {@link Metric} instances based on the specified prefix. - * @param prefix the metrics prefix (ends with '.') - * @return the metrics corresponding to this instance - */ - Collection> toMetrics(String prefix); - - /** - * Return the size of the cache or {@code null} if that information is not available. - * @return the size of the cache or {@code null} - */ - Long getSize(); - - /** - * Return the ratio of cache requests which were hits as a value between 0 and 1 where - * 0 means that the hit ratio is 0% and 1 means it is 100%. - *

- * This may also return {@code null} if the cache-specifics statistics does not - * provide the necessary information - * @return the hit ratio or {@code null} - */ - Double getHitRatio(); - - /** - * Return the ratio of cache requests which were misses as value between 0 and 1 where - * 0 means that the miss ratio is 0% and 1 means it is 100%. - *

- * This may also return {@code null} if the cache-specifics statistics does not - * provide the necessary information - * @return the miss ratio or {@code null} - */ - Double getMissRatio(); - -} diff --git a/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/cache/CacheStatisticsProvider.java b/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/cache/CacheStatisticsProvider.java deleted file mode 100644 index 2aebddea6c92..000000000000 --- a/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/cache/CacheStatisticsProvider.java +++ /dev/null @@ -1,42 +0,0 @@ -/* - * 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 The {@link Cache} type - * @author Stephane Nicoll - * @author Phillip Webb - * @since 1.3.0 - */ -@FunctionalInterface -public interface CacheStatisticsProvider { - - /** - * 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); - -} diff --git a/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/cache/CaffeineCacheStatisticsProvider.java b/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/cache/CaffeineCacheStatisticsProvider.java deleted file mode 100644 index dc6e77ffe8e3..000000000000 --- a/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/cache/CaffeineCacheStatisticsProvider.java +++ /dev/null @@ -1,46 +0,0 @@ -/* - * Copyright 2012-2016 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 com.github.benmanes.caffeine.cache.stats.CacheStats; - -import org.springframework.cache.CacheManager; -import org.springframework.cache.caffeine.CaffeineCache; - -/** - * {@link CacheStatisticsProvider} implementation for Caffeine. - * - * @author EddĂș MelĂ©ndez - * @since 1.4.0 - */ -public class CaffeineCacheStatisticsProvider - implements CacheStatisticsProvider { - - @Override - public CacheStatistics getCacheStatistics(CacheManager cacheManager, - CaffeineCache cache) { - DefaultCacheStatistics statistics = new DefaultCacheStatistics(); - statistics.setSize(cache.getNativeCache().estimatedSize()); - CacheStats caffeineStatistics = cache.getNativeCache().stats(); - if (caffeineStatistics.requestCount() > 0) { - statistics.setHitRatio(caffeineStatistics.hitRate()); - statistics.setMissRatio(caffeineStatistics.missRate()); - } - return statistics; - } - -} diff --git a/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/cache/ConcurrentMapCacheStatisticsProvider.java b/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/cache/ConcurrentMapCacheStatisticsProvider.java deleted file mode 100644 index 7adee9999fa5..000000000000 --- a/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/cache/ConcurrentMapCacheStatisticsProvider.java +++ /dev/null @@ -1,39 +0,0 @@ -/* - * Copyright 2012-2015 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.boot.actuate.cache; - -import org.springframework.cache.CacheManager; -import org.springframework.cache.concurrent.ConcurrentMapCache; - -/** - * {@link CacheStatisticsProvider} implementation for {@link ConcurrentMapCache}. - * - * @author Stephane Nicoll - * @since 1.3.0 - */ -public class ConcurrentMapCacheStatisticsProvider - implements CacheStatisticsProvider { - - @Override - public CacheStatistics getCacheStatistics(CacheManager cacheManager, - ConcurrentMapCache cache) { - DefaultCacheStatistics statistics = new DefaultCacheStatistics(); - statistics.setSize((long) cache.getNativeCache().size()); - return statistics; - } - -} diff --git a/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/cache/DefaultCacheStatistics.java b/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/cache/DefaultCacheStatistics.java deleted file mode 100644 index 091ab34ccb55..000000000000 --- a/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/cache/DefaultCacheStatistics.java +++ /dev/null @@ -1,90 +0,0 @@ -/* - * 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.util.ArrayList; -import java.util.Collection; - -import org.springframework.boot.actuate.metrics.Metric; - -/** - * A default {@link CacheStatistics} implementation. - * - * @author Stephane Nicoll - * @since 1.3.0 - */ -public class DefaultCacheStatistics implements CacheStatistics { - - private Long size; - - private Double hitRatio; - - private Double missRatio; - - @Override - public Collection> toMetrics(String prefix) { - Collection> result = new ArrayList<>(); - addMetric(result, prefix + "size", getSize()); - addMetric(result, prefix + "hit.ratio", getHitRatio()); - addMetric(result, prefix + "miss.ratio", getMissRatio()); - return result; - } - - public void setGetCacheCounts(long hitCount, long missCount) { - long total = hitCount + missCount; - if (total > 0) { - double hitRatio = hitCount / (double) total; - setHitRatio(hitRatio); - setMissRatio(1 - hitRatio); - } - } - - @Override - public Long getSize() { - return this.size; - } - - public void setSize(Long size) { - this.size = size; - } - - @Override - public Double getHitRatio() { - return this.hitRatio; - } - - public void setHitRatio(Double hitRatio) { - this.hitRatio = hitRatio; - } - - @Override - public Double getMissRatio() { - return this.missRatio; - } - - public void setMissRatio(Double missRatio) { - this.missRatio = missRatio; - } - - private void addMetric(Collection> metrics, String name, - T value) { - if (value != null) { - metrics.add(new Metric<>(name, value)); - } - } - -} diff --git a/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/cache/EhCacheStatisticsProvider.java b/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/cache/EhCacheStatisticsProvider.java deleted file mode 100644 index 98eae49faed1..000000000000 --- a/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/cache/EhCacheStatisticsProvider.java +++ /dev/null @@ -1,55 +0,0 @@ -/* - * Copyright 2012-2016 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 net.sf.ehcache.statistics.StatisticsGateway; - -import org.springframework.cache.CacheManager; -import org.springframework.cache.ehcache.EhCacheCache; - -/** - * {@link CacheStatisticsProvider} implementation for EhCache. - * - * @author Stephane Nicoll - * @since 1.3.0 - */ -public class EhCacheStatisticsProvider implements CacheStatisticsProvider { - - @Override - public CacheStatistics getCacheStatistics(CacheManager cacheManager, - EhCacheCache cache) { - DefaultCacheStatistics statistics = new DefaultCacheStatistics(); - StatisticsGateway ehCacheStatistics = cache.getNativeCache().getStatistics(); - statistics.setSize(ehCacheStatistics.getSize()); - double hitRatio = cacheHitRatio(ehCacheStatistics); - if (!Double.isNaN(hitRatio)) { - // ratio is calculated 'racily' and can drift marginally above unity, - // so we cap it here - double sanitizedHitRatio = (hitRatio > 1 ? 1 : hitRatio); - statistics.setHitRatio(sanitizedHitRatio); - statistics.setMissRatio(1 - sanitizedHitRatio); - } - return statistics; - } - - private double cacheHitRatio(StatisticsGateway stats) { - long hitCount = stats.cacheHitCount(); - long missCount = stats.cacheMissCount(); - return ((double) hitCount) / (hitCount + missCount); - } - -} diff --git a/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/cache/HazelcastCacheStatisticsProvider.java b/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/cache/HazelcastCacheStatisticsProvider.java deleted file mode 100644 index d445f9d6bdf9..000000000000 --- a/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/cache/HazelcastCacheStatisticsProvider.java +++ /dev/null @@ -1,46 +0,0 @@ -/* - * Copyright 2012-2015 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.boot.actuate.cache; - -import com.hazelcast.core.IMap; -import com.hazelcast.monitor.LocalMapStats; -import com.hazelcast.spring.cache.HazelcastCache; - -import org.springframework.cache.CacheManager; - -/** - * {@link CacheStatisticsProvider} implementation for Hazelcast. - * - * @author Stephane Nicoll - * @since 1.3.0 - */ -public class HazelcastCacheStatisticsProvider - implements CacheStatisticsProvider { - - @Override - public CacheStatistics getCacheStatistics(CacheManager cacheManager, - HazelcastCache cache) { - DefaultCacheStatistics statistics = new DefaultCacheStatistics(); - LocalMapStats mapStatistics = ((IMap) cache.getNativeCache()) - .getLocalMapStats(); - statistics.setSize(mapStatistics.getOwnedEntryCount()); - statistics.setGetCacheCounts(mapStatistics.getHits(), - mapStatistics.getGetOperationCount() - mapStatistics.getHits()); - return statistics; - } - -} diff --git a/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/cache/InfinispanCacheStatisticsProvider.java b/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/cache/InfinispanCacheStatisticsProvider.java deleted file mode 100644 index 2a424ae2b522..000000000000 --- a/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/cache/InfinispanCacheStatisticsProvider.java +++ /dev/null @@ -1,73 +0,0 @@ -/* - * Copyright 2012-2015 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.boot.actuate.cache; - -import java.util.Set; - -import javax.management.MalformedObjectNameException; -import javax.management.ObjectInstance; -import javax.management.ObjectName; - -import org.infinispan.spring.provider.SpringCache; - -/** - * {@link CacheStatisticsProvider} implementation for Infinispan. - * - * @author Stephane Nicoll - * @since 1.3.0 - */ -public class InfinispanCacheStatisticsProvider - extends AbstractJmxCacheStatisticsProvider { - - @Override - protected ObjectName getObjectName(SpringCache cache) - throws MalformedObjectNameException { - ObjectName name = new ObjectName( - "org.infinispan:component=Statistics,type=Cache,name=\"" + cache.getName() - + "(local)\",*"); - Set instances = getMBeanServer().queryMBeans(name, null); - if (instances.size() == 1) { - return instances.iterator().next().getObjectName(); - } - // None or more than one - return null; - } - - @Override - protected CacheStatistics getCacheStatistics(ObjectName objectName) { - DefaultCacheStatistics statistics = new DefaultCacheStatistics(); - Integer size = getAttribute(objectName, "numberOfEntries", Integer.class); - if (size != null) { - statistics.setSize((long) size); - if (size > 0) { - // Let's initialize the stats if we have some data - initializeStats(objectName, statistics); - } - } - return statistics; - } - - private void initializeStats(ObjectName objectName, - DefaultCacheStatistics statistics) { - Double hitRatio = getAttribute(objectName, "hitRatio", Double.class); - if ((hitRatio != null)) { - statistics.setHitRatio(hitRatio); - statistics.setMissRatio(1 - hitRatio); - } - } - -} diff --git a/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/cache/JCacheCacheStatisticsProvider.java b/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/cache/JCacheCacheStatisticsProvider.java deleted file mode 100644 index 02cc62ae1738..000000000000 --- a/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/cache/JCacheCacheStatisticsProvider.java +++ /dev/null @@ -1,63 +0,0 @@ -/* - * Copyright 2012-2015 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.boot.actuate.cache; - -import java.util.Set; - -import javax.management.MalformedObjectNameException; -import javax.management.ObjectInstance; -import javax.management.ObjectName; - -import org.springframework.cache.jcache.JCacheCache; - -/** - * {@link CacheStatisticsProvider} implementation for a JSR-107 compliant cache. - * - * @author Stephane Nicoll - * @since 1.3.0 - */ -public class JCacheCacheStatisticsProvider - extends AbstractJmxCacheStatisticsProvider { - - @Override - protected ObjectName getObjectName(JCacheCache cache) - throws MalformedObjectNameException { - ObjectName name = new ObjectName( - "javax.cache:type=CacheStatistics,Cache=" + cache.getName() + ",*"); - Set instances = getMBeanServer().queryMBeans(name, null); - if (instances.size() == 1) { - return instances.iterator().next().getObjectName(); - } - // None or more than one - return null; - } - - @Override - protected CacheStatistics getCacheStatistics(ObjectName objectName) { - DefaultCacheStatistics statistics = new DefaultCacheStatistics(); - Float hitPercentage = getAttribute(objectName, "CacheHitPercentage", Float.class); - Float missPercentage = getAttribute(objectName, "CacheMissPercentage", - Float.class); - if ((hitPercentage != null && missPercentage != null) - && (hitPercentage > 0 || missPercentage > 0)) { - statistics.setHitRatio(hitPercentage / (double) 100); - statistics.setMissRatio(missPercentage / (double) 100); - } - return statistics; - } - -} diff --git a/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/cache/package-info.java b/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/cache/package-info.java deleted file mode 100644 index 922292e85713..000000000000 --- a/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/cache/package-info.java +++ /dev/null @@ -1,20 +0,0 @@ -/* - * 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; diff --git a/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/metrics/CounterService.java b/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/metrics/CounterService.java deleted file mode 100644 index aae0921f912c..000000000000 --- a/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/metrics/CounterService.java +++ /dev/null @@ -1,44 +0,0 @@ -/* - * Copyright 2012-2013 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.boot.actuate.metrics; - -/** - * A service that can be used to increment, decrement and reset a named counter value. - * - * @author Dave Syer - */ -public interface CounterService { - - /** - * Increment the specified counter by 1. - * @param metricName the name of the counter - */ - void increment(String metricName); - - /** - * Decrement the specified counter by 1. - * @param metricName the name of the counter - */ - void decrement(String metricName); - - /** - * Reset the specified counter. - * @param metricName the name of the counter - */ - void reset(String metricName); - -} diff --git a/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/metrics/DataSourcePublicMetrics.java b/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/metrics/DataSourcePublicMetrics.java deleted file mode 100644 index ade95e797560..000000000000 --- a/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/metrics/DataSourcePublicMetrics.java +++ /dev/null @@ -1,127 +0,0 @@ -/* - * 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.metrics; - -import java.util.Collection; -import java.util.HashMap; -import java.util.LinkedHashSet; -import java.util.Map; -import java.util.Set; - -import javax.annotation.PostConstruct; -import javax.sql.DataSource; - -import org.springframework.beans.factory.NoSuchBeanDefinitionException; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.boot.jdbc.metadata.DataSourcePoolMetadata; -import org.springframework.boot.jdbc.metadata.DataSourcePoolMetadataProvider; -import org.springframework.boot.jdbc.metadata.DataSourcePoolMetadataProviders; -import org.springframework.context.ApplicationContext; -import org.springframework.context.annotation.Primary; - -/** - * A {@link PublicMetrics} implementation that provides data source usage statistics. - * - * @author Stephane Nicoll - * @since 2.0.0 - */ -public class DataSourcePublicMetrics implements PublicMetrics { - - private static final String DATASOURCE_SUFFIX = "dataSource"; - - @Autowired - private ApplicationContext applicationContext; - - @Autowired - private Collection providers; - - private final Map metadataByPrefix = new HashMap<>(); - - @PostConstruct - public void initialize() { - DataSource primaryDataSource = getPrimaryDataSource(); - DataSourcePoolMetadataProvider provider = new DataSourcePoolMetadataProviders( - this.providers); - for (Map.Entry entry : this.applicationContext - .getBeansOfType(DataSource.class).entrySet()) { - String beanName = entry.getKey(); - DataSource bean = entry.getValue(); - String prefix = createPrefix(beanName, bean, bean.equals(primaryDataSource)); - DataSourcePoolMetadata poolMetadata = provider - .getDataSourcePoolMetadata(bean); - if (poolMetadata != null) { - this.metadataByPrefix.put(prefix, poolMetadata); - } - } - } - - @Override - public Collection> metrics() { - Set> metrics = new LinkedHashSet<>(); - for (Map.Entry entry : this.metadataByPrefix - .entrySet()) { - String prefix = entry.getKey(); - prefix = (prefix.endsWith(".") ? prefix : prefix + "."); - DataSourcePoolMetadata metadata = entry.getValue(); - addMetric(metrics, prefix + "active", metadata.getActive()); - addMetric(metrics, prefix + "usage", metadata.getUsage()); - } - return metrics; - } - - private void addMetric(Set> metrics, String name, - T value) { - if (value != null) { - metrics.add(new Metric<>(name, value)); - } - } - - /** - * Create the prefix to use for the metrics to associate with the given - * {@link DataSource}. - * @param name the name of the data source bean - * @param dataSource the data source to configure - * @param primary if this data source is the primary data source - * @return a prefix for the given data source - */ - protected String createPrefix(String name, DataSource dataSource, boolean primary) { - if (primary) { - return "datasource.primary"; - } - if (name.length() > DATASOURCE_SUFFIX.length() - && name.toLowerCase().endsWith(DATASOURCE_SUFFIX.toLowerCase())) { - name = name.substring(0, name.length() - DATASOURCE_SUFFIX.length()); - } - return "datasource." + name; - } - - /** - * Attempt to locate the primary {@link DataSource} (i.e. either the only data source - * available or the one amongst the candidates marked as {@link Primary}). Return - * {@code null} if there no primary data source could be found. - * @return the primary datasource - */ - private DataSource getPrimaryDataSource() { - try { - return this.applicationContext.getBean(DataSource.class); - } - catch (NoSuchBeanDefinitionException ex) { - return null; - } - } - -} diff --git a/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/metrics/GaugeService.java b/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/metrics/GaugeService.java deleted file mode 100644 index eaa00fb48d3c..000000000000 --- a/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/metrics/GaugeService.java +++ /dev/null @@ -1,40 +0,0 @@ -/* - * 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.metrics; - -/** - * A service that can be used to submit a named double value for storage and analysis. Any - * statistics or analysis that needs to be carried out is best left for other concerns, - * but ultimately they are under control of the implementation of this service. For - * instance, the value submitted here could be a method execution timing result, and it - * would go to a backend that keeps a histogram of recent values for comparison purposes. - * Or it could be a simple measurement of a sensor value (like a temperature reading) to - * be passed on to a monitoring system in its raw form. - * - * @author Dave Syer - */ -@FunctionalInterface -public interface GaugeService { - - /** - * Set the specified gauge value. - * @param metricName the name of the gauge to set - * @param value the value of the gauge - */ - void submit(String metricName, double value); - -} diff --git a/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/metrics/Metric.java b/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/metrics/Metric.java deleted file mode 100644 index 19d61ac1784d..000000000000 --- a/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/metrics/Metric.java +++ /dev/null @@ -1,137 +0,0 @@ -/* - * 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.metrics; - -import java.util.Date; - -import org.springframework.util.Assert; -import org.springframework.util.ObjectUtils; - -/** - * Immutable class that can be used to hold any arbitrary system measurement value (a - * named numeric value with a timestamp). For example a metric might record the number of - * active connections to a server, or the temperature of a meeting room. - * - * @param the value type - * @author Dave Syer - */ -public class Metric { - - private final String name; - - private final T value; - - private final Date timestamp; - - /** - * Create a new {@link Metric} instance for the current time. - * @param name the name of the metric - * @param value the value of the metric - */ - public Metric(String name, T value) { - this(name, value, new Date()); - } - - /** - * Create a new {@link Metric} instance. - * @param name the name of the metric - * @param value the value of the metric - * @param timestamp the timestamp for the metric - */ - public Metric(String name, T value, Date timestamp) { - Assert.notNull(name, "Name must not be null"); - this.name = name; - this.value = value; - this.timestamp = timestamp; - } - - /** - * Returns the name of the metric. - * @return the name - */ - public String getName() { - return this.name; - } - - /** - * Returns the value of the metric. - * @return the value - */ - public T getValue() { - return this.value; - } - - public Date getTimestamp() { - return this.timestamp; - } - - @Override - public String toString() { - return "Metric [name=" + this.name + ", value=" + this.value + ", timestamp=" - + this.timestamp + "]"; - } - - /** - * Create a new {@link Metric} with an incremented value. - * @param amount the amount that the new metric will differ from this one - * @return a new {@link Metric} instance - */ - public Metric increment(int amount) { - return new Metric<>(this.getName(), - Long.valueOf(this.getValue().longValue() + amount)); - } - - /** - * Create a new {@link Metric} with a different value. - * @param the metric value type - * @param value the value of the new metric - * @return a new {@link Metric} instance - */ - public Metric set(S value) { - return new Metric<>(this.getName(), value); - } - - @Override - public int hashCode() { - final int prime = 31; - int result = 1; - result = prime * result + ObjectUtils.nullSafeHashCode(this.name); - result = prime * result + ObjectUtils.nullSafeHashCode(this.timestamp); - result = prime * result + ObjectUtils.nullSafeHashCode(this.value); - return result; - } - - @Override - public boolean equals(Object obj) { - if (this == obj) { - return true; - } - if (obj == null) { - return false; - } - if (obj instanceof Metric) { - Metric other = (Metric) obj; - boolean rtn = true; - rtn = rtn && ObjectUtils.nullSafeEquals(this.name, other.name); - rtn = rtn && ObjectUtils.nullSafeEquals(this.timestamp, other.timestamp); - rtn = rtn && ObjectUtils.nullSafeEquals(this.value, other.value); - return rtn; - } - return super.equals(obj); - } - -} diff --git a/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/metrics/MetricsEndpoint.java b/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/metrics/MetricsEndpoint.java index 143fb2b5719f..6572573482c6 100644 --- a/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/metrics/MetricsEndpoint.java +++ b/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/metrics/MetricsEndpoint.java @@ -16,93 +16,100 @@ package org.springframework.boot.actuate.metrics; -import java.util.ArrayList; import java.util.Collection; import java.util.Collections; -import java.util.LinkedHashMap; import java.util.List; import java.util.Map; -import java.util.function.Predicate; -import java.util.regex.Pattern; +import java.util.stream.Collectors; +import java.util.stream.Stream; +import java.util.stream.StreamSupport; + +import io.micrometer.core.instrument.Measurement; +import io.micrometer.core.instrument.Meter; +import io.micrometer.core.instrument.MeterRegistry; +import io.micrometer.core.instrument.Statistic; +import io.micrometer.core.instrument.util.HierarchicalNameMapper; import org.springframework.boot.actuate.endpoint.annotation.Endpoint; import org.springframework.boot.actuate.endpoint.annotation.ReadOperation; import org.springframework.boot.actuate.endpoint.annotation.Selector; -import org.springframework.core.annotation.AnnotationAwareOrderComparator; -import org.springframework.util.Assert; -import org.springframework.util.StringUtils; /** - * {@link Endpoint} to expose a collection of {@link PublicMetrics}. + * An {@link Endpoint} for exposing the metrics held by a {@link MeterRegistry}. * - * @author Dave Syer + * @author Jon Schneider * @since 2.0.0 */ @Endpoint(id = "metrics") public class MetricsEndpoint { - private final List publicMetrics; + private final MeterRegistry registry; - /** - * Create a new {@link MetricsEndpoint} instance. - * @param publicMetrics the metrics to expose - */ - public MetricsEndpoint(PublicMetrics publicMetrics) { - this(Collections.singleton(publicMetrics)); + public MetricsEndpoint(MeterRegistry registry) { + this.registry = registry; } - /** - * Create a new {@link MetricsEndpoint} instance. - * @param publicMetrics the metrics to expose. The collection will be sorted using the - * {@link AnnotationAwareOrderComparator}. - */ - public MetricsEndpoint(Collection publicMetrics) { - Assert.notNull(publicMetrics, "PublicMetrics must not be null"); - this.publicMetrics = new ArrayList<>(publicMetrics); - AnnotationAwareOrderComparator.sort(this.publicMetrics); + @ReadOperation + public Map> listNames() { + return Collections.singletonMap("names", this.registry.getMeters().stream() + .map(this::getMeterIdName).collect(Collectors.toList())); } - public void registerPublicMetrics(PublicMetrics metrics) { - this.publicMetrics.add(metrics); - AnnotationAwareOrderComparator.sort(this.publicMetrics); + private String getMeterIdName(Meter meter) { + return meter.getId().getName(); } - public void unregisterPublicMetrics(PublicMetrics metrics) { - this.publicMetrics.remove(metrics); + @ReadOperation + public Map> metric( + @Selector String requiredMetricName) { + return this.registry.find(requiredMetricName).meters().stream() + .collect(Collectors.toMap(this::getHierarchicalName, this::getSamples)); } - @ReadOperation - public Map metrics(String pattern) { - return metrics(StringUtils.hasText(pattern) - ? Pattern.compile(pattern).asPredicate() : (name) -> true); + private List getSamples(Meter meter) { + return stream(meter.measure()).map(this::getSample).collect(Collectors.toList()); } - @ReadOperation - public Map metricNamed(@Selector String requiredName) { - Map metrics = metrics((name) -> name.equals(requiredName)); - if (metrics.isEmpty()) { - return null; - } - return metrics; + private MeasurementSample getSample(Measurement measurement) { + return new MeasurementSample(measurement.getStatistic(), measurement.getValue()); + } + + private String getHierarchicalName(Meter meter) { + return HierarchicalNameMapper.DEFAULT.toHierarchicalName(meter.getId()); + } + + private Stream stream(Iterable measure) { + return StreamSupport.stream(measure.spliterator(), false); } - private Map metrics(Predicate namePredicate) { - Map result = new LinkedHashMap<>(); - List metrics = new ArrayList<>(this.publicMetrics); - for (PublicMetrics publicMetric : metrics) { - try { - for (Metric metric : publicMetric.metrics()) { - if (namePredicate.test(metric.getName()) - && metric.getValue() != null) { - result.put(metric.getName(), metric.getValue()); - } - } - } - catch (Exception ex) { - // Could not evaluate metrics - } + /** + * A measurement sample combining a {@link Statistic statistic} and a value. + */ + static class MeasurementSample { + + private final Statistic statistic; + + private final Double value; + + MeasurementSample(Statistic statistic, Double value) { + this.statistic = statistic; + this.value = value; } - return result; + + public Statistic getStatistic() { + return this.statistic; + } + + public Double getValue() { + return this.value; + } + + @Override + public String toString() { + return "MeasurementSample{" + "statistic=" + this.statistic + ", value=" + + this.value + '}'; + } + } } diff --git a/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/metrics/SystemPublicMetrics.java b/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/metrics/SystemPublicMetrics.java deleted file mode 100644 index 848915f7e645..000000000000 --- a/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/metrics/SystemPublicMetrics.java +++ /dev/null @@ -1,193 +0,0 @@ -/* - * 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.metrics; - -import java.lang.management.ClassLoadingMXBean; -import java.lang.management.GarbageCollectorMXBean; -import java.lang.management.ManagementFactory; -import java.lang.management.MemoryUsage; -import java.lang.management.ThreadMXBean; -import java.util.Collection; -import java.util.LinkedHashSet; -import java.util.List; - -import org.springframework.core.Ordered; -import org.springframework.util.StringUtils; - -/** - * A {@link PublicMetrics} implementation that provides various system-related metrics. - * - * @author Dave Syer - * @author Christian Dupuis - * @author Stephane Nicoll - * @author Johannes Edmeier - * @since 2.0.0 - */ -public class SystemPublicMetrics implements PublicMetrics, Ordered { - - private long timestamp; - - public SystemPublicMetrics() { - this.timestamp = System.currentTimeMillis(); - } - - @Override - public int getOrder() { - return Ordered.HIGHEST_PRECEDENCE + 10; - } - - @Override - public Collection> metrics() { - Collection> result = new LinkedHashSet<>(); - addBasicMetrics(result); - addManagementMetrics(result); - return result; - } - - /** - * Add basic system metrics. - * @param result the result - */ - protected void addBasicMetrics(Collection> result) { - // NOTE: ManagementFactory must not be used here since it fails on GAE - Runtime runtime = Runtime.getRuntime(); - result.add(newMemoryMetric("mem", - runtime.totalMemory() + getTotalNonHeapMemoryIfPossible())); - result.add(newMemoryMetric("mem.free", runtime.freeMemory())); - result.add(new Metric<>("processors", runtime.availableProcessors())); - result.add(new Metric<>("instance.uptime", - System.currentTimeMillis() - this.timestamp)); - } - - private long getTotalNonHeapMemoryIfPossible() { - try { - return ManagementFactory.getMemoryMXBean().getNonHeapMemoryUsage().getUsed(); - } - catch (Throwable ex) { - return 0; - } - } - - /** - * Add metrics from ManagementFactory if possible. Note that ManagementFactory is not - * available on Google App Engine. - * @param result the result - */ - private void addManagementMetrics(Collection> result) { - try { - // Add JVM up time in ms - result.add(new Metric<>("uptime", - ManagementFactory.getRuntimeMXBean().getUptime())); - result.add(new Metric<>("systemload.average", - ManagementFactory.getOperatingSystemMXBean().getSystemLoadAverage())); - addHeapMetrics(result); - addNonHeapMetrics(result); - addThreadMetrics(result); - addClassLoadingMetrics(result); - addGarbageCollectionMetrics(result); - } - catch (NoClassDefFoundError ex) { - // Expected on Google App Engine - } - } - - /** - * Add JVM heap metrics. - * @param result the result - */ - protected void addHeapMetrics(Collection> result) { - MemoryUsage memoryUsage = ManagementFactory.getMemoryMXBean() - .getHeapMemoryUsage(); - result.add(newMemoryMetric("heap.committed", memoryUsage.getCommitted())); - result.add(newMemoryMetric("heap.init", memoryUsage.getInit())); - result.add(newMemoryMetric("heap.used", memoryUsage.getUsed())); - result.add(newMemoryMetric("heap", memoryUsage.getMax())); - } - - /** - * Add JVM non-heap metrics. - * @param result the result - */ - private void addNonHeapMetrics(Collection> result) { - MemoryUsage memoryUsage = ManagementFactory.getMemoryMXBean() - .getNonHeapMemoryUsage(); - result.add(newMemoryMetric("nonheap.committed", memoryUsage.getCommitted())); - result.add(newMemoryMetric("nonheap.init", memoryUsage.getInit())); - result.add(newMemoryMetric("nonheap.used", memoryUsage.getUsed())); - result.add(newMemoryMetric("nonheap", memoryUsage.getMax())); - } - - private Metric newMemoryMetric(String name, long bytes) { - return new Metric<>(name, bytes / 1024); - } - - /** - * Add thread metrics. - * @param result the result - */ - protected void addThreadMetrics(Collection> result) { - ThreadMXBean threadMxBean = ManagementFactory.getThreadMXBean(); - result.add( - new Metric<>("threads.peak", (long) threadMxBean.getPeakThreadCount())); - result.add(new Metric<>("threads.daemon", - (long) threadMxBean.getDaemonThreadCount())); - result.add(new Metric<>("threads.totalStarted", - threadMxBean.getTotalStartedThreadCount())); - result.add(new Metric<>("threads", (long) threadMxBean.getThreadCount())); - } - - /** - * Add class loading metrics. - * @param result the result - */ - protected void addClassLoadingMetrics(Collection> result) { - ClassLoadingMXBean classLoadingMxBean = ManagementFactory.getClassLoadingMXBean(); - result.add( - new Metric<>("classes", (long) classLoadingMxBean.getLoadedClassCount())); - result.add(new Metric<>("classes.loaded", - classLoadingMxBean.getTotalLoadedClassCount())); - result.add(new Metric<>("classes.unloaded", - classLoadingMxBean.getUnloadedClassCount())); - } - - /** - * Add garbage collection metrics. - * @param result the result - */ - protected void addGarbageCollectionMetrics(Collection> result) { - List garbageCollectorMxBeans = ManagementFactory - .getGarbageCollectorMXBeans(); - for (GarbageCollectorMXBean garbageCollectorMXBean : garbageCollectorMxBeans) { - String name = beautifyGcName(garbageCollectorMXBean.getName()); - result.add(new Metric<>("gc." + name + ".count", - garbageCollectorMXBean.getCollectionCount())); - result.add(new Metric<>("gc." + name + ".time", - garbageCollectorMXBean.getCollectionTime())); - } - } - - /** - * Turn GC names like 'PS Scavenge' or 'PS MarkSweep' into something that is more - * metrics friendly. - * @param name the source name - * @return a metric friendly name - */ - private String beautifyGcName(String name) { - return StringUtils.replace(name, " ", "_").toLowerCase(); - } - -} diff --git a/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/metrics/TomcatPublicMetrics.java b/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/metrics/TomcatPublicMetrics.java deleted file mode 100644 index 9c9b1264be6b..000000000000 --- a/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/metrics/TomcatPublicMetrics.java +++ /dev/null @@ -1,96 +0,0 @@ -/* - * 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.metrics; - -import java.util.ArrayList; -import java.util.Collection; -import java.util.Collections; -import java.util.List; - -import org.apache.catalina.Container; -import org.apache.catalina.Context; -import org.apache.catalina.Manager; -import org.apache.catalina.session.ManagerBase; - -import org.springframework.beans.BeansException; -import org.springframework.boot.web.embedded.tomcat.TomcatWebServer; -import org.springframework.boot.web.server.WebServer; -import org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext; -import org.springframework.context.ApplicationContext; -import org.springframework.context.ApplicationContextAware; - -/** - * A {@link PublicMetrics} implementation that provides Tomcat statistics. - * - * @author Johannes Edmeier - * @author Phillip Webb - * @since 2.0.0 - */ -public class TomcatPublicMetrics implements PublicMetrics, ApplicationContextAware { - - private ApplicationContext applicationContext; - - @Override - public Collection> metrics() { - if (this.applicationContext instanceof ServletWebServerApplicationContext) { - Manager manager = getManager( - (ServletWebServerApplicationContext) this.applicationContext); - if (manager != null) { - return metrics(manager); - } - } - return Collections.emptySet(); - } - - private Manager getManager(ServletWebServerApplicationContext applicationContext) { - WebServer webServer = applicationContext.getWebServer(); - if (webServer instanceof TomcatWebServer) { - return getManager((TomcatWebServer) webServer); - } - return null; - } - - private Manager getManager(TomcatWebServer webServer) { - for (Container container : webServer.getTomcat().getHost().findChildren()) { - if (container instanceof Context) { - return ((Context) container).getManager(); - } - } - return null; - } - - private Collection> metrics(Manager manager) { - List> metrics = new ArrayList<>(2); - if (manager instanceof ManagerBase) { - addMetric(metrics, "httpsessions.max", - ((ManagerBase) manager).getMaxActiveSessions()); - } - addMetric(metrics, "httpsessions.active", manager.getActiveSessions()); - return metrics; - } - - private void addMetric(List> metrics, String name, Integer value) { - metrics.add(new Metric<>(name, value)); - } - - @Override - public void setApplicationContext(ApplicationContext applicationContext) - throws BeansException { - this.applicationContext = applicationContext; - } - -} diff --git a/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/metrics/aggregate/AggregateMetricReader.java b/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/metrics/aggregate/AggregateMetricReader.java deleted file mode 100644 index 96a3ec368040..000000000000 --- a/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/metrics/aggregate/AggregateMetricReader.java +++ /dev/null @@ -1,158 +0,0 @@ -/* - * 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.metrics.aggregate; - -import java.util.HashSet; -import java.util.Set; - -import org.springframework.boot.actuate.metrics.Metric; -import org.springframework.boot.actuate.metrics.reader.MetricReader; -import org.springframework.boot.actuate.metrics.repository.InMemoryMetricRepository; -import org.springframework.util.StringUtils; - -/** - * A metric reader that aggregates values from a source reader, normally one that has been - * collecting data from many sources in the same form (like a scaled-out application). The - * source has metrics with names in the form {@code *.*.counter.**} and - * {@code *.*.[anything].**}, and the result has metric names in the form - * {@code aggregate.count.**} and {@code aggregate.[anything].**}. Counters are summed and - * anything else (i.e. gauges) are aggregated by choosing the most recent value. - * - * @author Dave Syer - * @since 1.3.0 - */ -public class AggregateMetricReader implements MetricReader { - - private MetricReader source; - - private String keyPattern = "d.d"; - - private String prefix = "aggregate."; - - public AggregateMetricReader(MetricReader source) { - this.source = source; - } - - /** - * Pattern that tells the aggregator what to do with the keys from the source - * repository. The keys in the source repository are assumed to be period separated, - * and the pattern is in the same format, e.g. "d.d.k.d". The pattern segments are - * matched against the source keys and a rule is applied: - *

    - *
  • "d" means "discard" this key segment (useful for global prefixes like system - * identifiers, or aggregate keys a.k.a. physical identifiers)
  • - *
  • "k" means "keep" it with no change (useful for logical identifiers like app - * names)
  • - *
- * Default is "d.d" (we assume there is a global prefix of length 2). - * @param keyPattern the keyPattern to set - */ - public void setKeyPattern(String keyPattern) { - this.keyPattern = keyPattern; - } - - /** - * Prefix to apply to all output metrics. A period will be appended if not present in - * the provided value. - * @param prefix the prefix to use (default "aggregator.") - */ - public void setPrefix(String prefix) { - if (StringUtils.hasText(prefix) && !prefix.endsWith(".")) { - prefix = prefix + "."; - } - this.prefix = prefix; - } - - @Override - public Metric findOne(String metricName) { - if (!metricName.startsWith(this.prefix)) { - return null; - } - InMemoryMetricRepository result = new InMemoryMetricRepository(); - String baseName = metricName.substring(this.prefix.length()); - for (Metric metric : this.source.findAll()) { - String name = getSourceKey(metric.getName()); - if (baseName.equals(name)) { - update(result, name, metric); - } - } - return result.findOne(metricName); - } - - @Override - public Iterable> findAll() { - InMemoryMetricRepository result = new InMemoryMetricRepository(); - for (Metric metric : this.source.findAll()) { - String key = getSourceKey(metric.getName()); - if (key != null) { - update(result, key, metric); - } - } - return result.findAll(); - } - - @Override - public long count() { - Set names = new HashSet<>(); - for (Metric metric : this.source.findAll()) { - String name = getSourceKey(metric.getName()); - if (name != null) { - names.add(name); - } - } - return names.size(); - } - - private void update(InMemoryMetricRepository result, String key, Metric metric) { - String name = this.prefix + key; - Metric aggregate = result.findOne(name); - if (aggregate == null) { - aggregate = new Metric(name, metric.getValue(), - metric.getTimestamp()); - } - else if (key.contains("counter.")) { - // accumulate all values - aggregate = new Metric(name, - metric.increment(aggregate.getValue().intValue()).getValue(), - metric.getTimestamp()); - } - else if (aggregate.getTimestamp().before(metric.getTimestamp())) { - // sort by timestamp and only take the latest - aggregate = new Metric(name, metric.getValue(), - metric.getTimestamp()); - } - result.set(aggregate); - } - - private String getSourceKey(String name) { - String[] keys = StringUtils.delimitedListToStringArray(name, "."); - String[] patterns = StringUtils.delimitedListToStringArray(this.keyPattern, "."); - StringBuilder builder = new StringBuilder(); - for (int i = 0; i < patterns.length; i++) { - if ("k".equals(patterns[i])) { - builder.append(builder.length() > 0 ? "." : ""); - builder.append(keys[i]); - } - } - for (int i = patterns.length; i < keys.length; i++) { - builder.append(builder.length() > 0 ? "." : ""); - builder.append(keys[i]); - } - return builder.toString(); - } - -} diff --git a/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/metrics/aggregate/package-info.java b/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/metrics/aggregate/package-info.java deleted file mode 100644 index 85e328bc6ec9..000000000000 --- a/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/metrics/aggregate/package-info.java +++ /dev/null @@ -1,20 +0,0 @@ -/* - * 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. - */ - -/** - * Classes for aggregation metrics. - */ -package org.springframework.boot.actuate.metrics.aggregate; diff --git a/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/metrics/buffer/Buffer.java b/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/metrics/buffer/Buffer.java deleted file mode 100644 index 708fbf89d743..000000000000 --- a/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/metrics/buffer/Buffer.java +++ /dev/null @@ -1,48 +0,0 @@ -/* - * Copyright 2012-2016 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.boot.actuate.metrics.buffer; - -/** - * Base class for a mutable buffer containing a timestamp and a value. - * - * @param the value type - * @author Dave Syer - * @author Phillip Webb - */ -abstract class Buffer { - - private volatile long timestamp; - - Buffer(long timestamp) { - this.timestamp = timestamp; - } - - public long getTimestamp() { - return this.timestamp; - } - - public void setTimestamp(long timestamp) { - this.timestamp = timestamp; - } - - /** - * Returns the buffer value. - * @return the value of the buffer - */ - public abstract T getValue(); - -} diff --git a/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/metrics/buffer/BufferCounterService.java b/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/metrics/buffer/BufferCounterService.java deleted file mode 100644 index 0ffa20dc85ab..000000000000 --- a/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/metrics/buffer/BufferCounterService.java +++ /dev/null @@ -1,71 +0,0 @@ -/* - * 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.metrics.buffer; - -import java.util.concurrent.ConcurrentHashMap; - -import org.springframework.boot.actuate.metrics.CounterService; - -/** - * Fast implementation of {@link CounterService} using {@link CounterBuffers}. - * - * @author Dave Syer - * @since 1.3.0 - */ -public class BufferCounterService implements CounterService { - - private final ConcurrentHashMap names = new ConcurrentHashMap<>(); - - private final CounterBuffers buffers; - - /** - * Create a {@link BufferCounterService} instance. - * @param buffers the underlying buffers used to store metrics - */ - public BufferCounterService(CounterBuffers buffers) { - this.buffers = buffers; - } - - @Override - public void increment(String metricName) { - this.buffers.increment(wrap(metricName), 1L); - } - - @Override - public void decrement(String metricName) { - this.buffers.increment(wrap(metricName), -1L); - } - - @Override - public void reset(String metricName) { - this.buffers.reset(wrap(metricName)); - } - - private String wrap(String metricName) { - String cached = this.names.get(metricName); - if (cached != null) { - return cached; - } - if (metricName.startsWith("counter") || metricName.startsWith("meter")) { - return metricName; - } - String name = "counter." + metricName; - this.names.put(metricName, name); - return name; - } - -} diff --git a/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/metrics/buffer/BufferGaugeService.java b/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/metrics/buffer/BufferGaugeService.java deleted file mode 100644 index d928cbd31f4d..000000000000 --- a/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/metrics/buffer/BufferGaugeService.java +++ /dev/null @@ -1,62 +0,0 @@ -/* - * 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.metrics.buffer; - -import java.util.concurrent.ConcurrentHashMap; - -import org.springframework.boot.actuate.metrics.GaugeService; - -/** - * Fast implementation of {@link GaugeService} using {@link GaugeBuffers}. - * - * @author Dave Syer - * @since 1.3.0 - */ -public class BufferGaugeService implements GaugeService { - - private final ConcurrentHashMap names = new ConcurrentHashMap<>(); - - private final GaugeBuffers buffers; - - /** - * Create a {@link BufferGaugeService} instance. - * @param buffers the underlying buffers used to store metrics - */ - public BufferGaugeService(GaugeBuffers buffers) { - this.buffers = buffers; - } - - @Override - public void submit(String metricName, double value) { - this.buffers.set(wrap(metricName), value); - } - - private String wrap(String metricName) { - String cached = this.names.get(metricName); - if (cached != null) { - return cached; - } - if (metricName.startsWith("gauge") || metricName.startsWith("histogram") - || metricName.startsWith("timer")) { - return metricName; - } - String name = "gauge." + metricName; - this.names.put(metricName, name); - return name; - } - -} diff --git a/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/metrics/buffer/BufferMetricReader.java b/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/metrics/buffer/BufferMetricReader.java deleted file mode 100644 index ae0070fafe89..000000000000 --- a/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/metrics/buffer/BufferMetricReader.java +++ /dev/null @@ -1,90 +0,0 @@ -/* - * 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.metrics.buffer; - -import java.util.ArrayList; -import java.util.Date; -import java.util.List; -import java.util.function.Predicate; -import java.util.regex.Pattern; - -import org.springframework.boot.actuate.metrics.Metric; -import org.springframework.boot.actuate.metrics.reader.MetricReader; -import org.springframework.boot.actuate.metrics.reader.PrefixMetricReader; - -/** - * {@link MetricReader} implementation using {@link CounterBuffers} and - * {@link GaugeBuffers}. - * - * @author Dave Syer - * @since 1.3.0 - */ -public class BufferMetricReader implements MetricReader, PrefixMetricReader { - - private static final Predicate ALL = Pattern.compile(".*").asPredicate(); - - private final CounterBuffers counterBuffers; - - private final GaugeBuffers gaugeBuffers; - - public BufferMetricReader(CounterBuffers counterBuffers, GaugeBuffers gaugeBuffers) { - this.counterBuffers = counterBuffers; - this.gaugeBuffers = gaugeBuffers; - } - - @Override - public Metric findOne(String name) { - Buffer buffer = this.counterBuffers.find(name); - if (buffer == null) { - buffer = this.gaugeBuffers.find(name); - } - return (buffer == null ? null : asMetric(name, buffer)); - } - - @Override - public Iterable> findAll() { - return findAll(BufferMetricReader.ALL); - } - - @Override - public Iterable> findAll(String prefix) { - return findAll(Pattern.compile(prefix + ".*").asPredicate()); - } - - @Override - public long count() { - return this.counterBuffers.count() + this.gaugeBuffers.count(); - } - - private Iterable> findAll(Predicate predicate) { - final List> metrics = new ArrayList<>(); - collectMetrics(this.gaugeBuffers, predicate, metrics); - collectMetrics(this.counterBuffers, predicate, metrics); - return metrics; - } - - private > void collectMetrics( - Buffers buffers, Predicate predicate, - final List> metrics) { - buffers.forEach(predicate, (name, value) -> metrics.add(asMetric(name, value))); - } - - private Metric asMetric(String name, Buffer buffer) { - return new Metric<>(name, buffer.getValue(), new Date(buffer.getTimestamp())); - } - -} diff --git a/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/metrics/buffer/Buffers.java b/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/metrics/buffer/Buffers.java deleted file mode 100644 index f7bad60d514a..000000000000 --- a/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/metrics/buffer/Buffers.java +++ /dev/null @@ -1,62 +0,0 @@ -/* - * 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.metrics.buffer; - -import java.util.concurrent.ConcurrentHashMap; -import java.util.function.BiConsumer; -import java.util.function.Consumer; -import java.util.function.Predicate; - -/** - * Base class used to manage a map of {@link Buffer} objects. - * - * @param The buffer type - * @author Dave Syer - * @author Phillip Webb - */ -abstract class Buffers> { - - private final ConcurrentHashMap buffers = new ConcurrentHashMap<>(); - - public void forEach(final Predicate predicate, - BiConsumer consumer) { - this.buffers.forEach((name, value) -> { - if (predicate.test(name)) { - consumer.accept(name, value); - } - }); - } - - public B find(String name) { - return this.buffers.get(name); - } - - public int count() { - return this.buffers.size(); - } - - protected final void doWith(String name, Consumer consumer) { - B buffer = this.buffers.get(name); - if (buffer == null) { - buffer = this.buffers.computeIfAbsent(name, (k) -> createBuffer()); - } - consumer.accept(buffer); - } - - protected abstract B createBuffer(); - -} diff --git a/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/metrics/buffer/CounterBuffer.java b/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/metrics/buffer/CounterBuffer.java deleted file mode 100644 index 2d34e68acb4b..000000000000 --- a/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/metrics/buffer/CounterBuffer.java +++ /dev/null @@ -1,49 +0,0 @@ -/* - * Copyright 2012-2016 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.boot.actuate.metrics.buffer; - -import java.util.concurrent.atomic.LongAdder; - -/** - * Mutable buffer containing a long adder (Java 8) and a timestamp. - * - * @author Dave Syer - * @since 1.3.0 - */ -public class CounterBuffer extends Buffer { - - private final LongAdder adder; - - public CounterBuffer(long timestamp) { - super(timestamp); - this.adder = new LongAdder(); - } - - public void add(long delta) { - this.adder.add(delta); - } - - public void reset() { - this.adder.reset(); - } - - @Override - public Long getValue() { - return this.adder.sum(); - } - -} diff --git a/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/metrics/buffer/GaugeBuffer.java b/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/metrics/buffer/GaugeBuffer.java deleted file mode 100644 index fa5f6568bc1e..000000000000 --- a/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/metrics/buffer/GaugeBuffer.java +++ /dev/null @@ -1,43 +0,0 @@ -/* - * Copyright 2012-2015 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.boot.actuate.metrics.buffer; - -/** - * Mutable buffer containing a double value and a timestamp. - * - * @author Dave Syer - * @since 1.3.0 - */ -public class GaugeBuffer extends Buffer { - - private volatile double value; - - public GaugeBuffer(long timestamp) { - super(timestamp); - this.value = 0; - } - - @Override - public Double getValue() { - return this.value; - } - - public void setValue(double value) { - this.value = value; - } - -} diff --git a/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/metrics/buffer/GaugeBuffers.java b/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/metrics/buffer/GaugeBuffers.java deleted file mode 100644 index 6ed46231858a..000000000000 --- a/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/metrics/buffer/GaugeBuffers.java +++ /dev/null @@ -1,39 +0,0 @@ -/* - * 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.metrics.buffer; - -/** - * Fast writes to in-memory metrics store using {@link GaugeBuffer}. - * - * @author Dave Syer - * @since 1.3.0 - */ -public class GaugeBuffers extends Buffers { - - public void set(String name, double value) { - doWith(name, (buffer) -> { - buffer.setTimestamp(System.currentTimeMillis()); - buffer.setValue(value); - }); - } - - @Override - protected GaugeBuffer createBuffer() { - return new GaugeBuffer(0L); - } - -} diff --git a/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/metrics/buffer/package-info.java b/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/metrics/buffer/package-info.java deleted file mode 100644 index b7090d885b41..000000000000 --- a/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/metrics/buffer/package-info.java +++ /dev/null @@ -1,20 +0,0 @@ -/* - * Copyright 2012-2015 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -/** - * Metrics buffering support. - */ -package org.springframework.boot.actuate.metrics.buffer; diff --git a/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/metrics/dropwizard/DropwizardMetricServices.java b/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/metrics/dropwizard/DropwizardMetricServices.java deleted file mode 100644 index 64d11a5605f8..000000000000 --- a/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/metrics/dropwizard/DropwizardMetricServices.java +++ /dev/null @@ -1,279 +0,0 @@ -/* - * 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.metrics.dropwizard; - -import java.util.concurrent.ConcurrentHashMap; -import java.util.concurrent.ConcurrentMap; -import java.util.concurrent.TimeUnit; - -import com.codahale.metrics.Counter; -import com.codahale.metrics.Gauge; -import com.codahale.metrics.Histogram; -import com.codahale.metrics.Meter; -import com.codahale.metrics.Metric; -import com.codahale.metrics.MetricRegistry; -import com.codahale.metrics.Reservoir; -import com.codahale.metrics.Timer; - -import org.springframework.boot.actuate.metrics.CounterService; -import org.springframework.boot.actuate.metrics.GaugeService; -import org.springframework.core.ResolvableType; -import org.springframework.util.Assert; - -/** - * A {@link GaugeService} and {@link CounterService} that sends data to a Dropwizard - * {@link MetricRegistry} based on a naming convention. - *
    - *
  • Updates to {@link #increment(String)} with names in "meter.*" are treated as - * {@link Meter} events
  • - *
  • Other deltas are treated as simple {@link Counter} values
  • - *
  • Inputs to {@link #submit(String, double)} with names in "histogram.*" are treated - * as {@link Histogram} updates
  • - *
  • Inputs to {@link #submit(String, double)} with names in "timer.*" are treated as - * {@link Timer} updates
  • - *
  • Other metrics are treated as simple {@link Gauge} values (single valued - * measurements of type double)
  • - *
- * - * @author Dave Syer - * @author Jay Anderson - * @author Andy Wilkinson - */ -public class DropwizardMetricServices implements CounterService, GaugeService { - - private final MetricRegistry registry; - - private final ReservoirFactory reservoirFactory; - - private final ConcurrentMap gauges = new ConcurrentHashMap<>(); - - private final ConcurrentHashMap names = new ConcurrentHashMap<>(); - - /** - * Create a new {@link DropwizardMetricServices} instance. - * @param registry the underlying metric registry - */ - public DropwizardMetricServices(MetricRegistry registry) { - this(registry, null); - } - - /** - * Create a new {@link DropwizardMetricServices} instance. - * @param registry the underlying metric registry - * @param reservoirFactory the factory that instantiates the {@link Reservoir} that - * will be used on Timers and Histograms - */ - public DropwizardMetricServices(MetricRegistry registry, - ReservoirFactory reservoirFactory) { - this.registry = registry; - this.reservoirFactory = (reservoirFactory == null ? ReservoirFactory.NONE - : reservoirFactory); - } - - @Override - public void increment(String name) { - incrementInternal(name, 1L); - } - - @Override - public void decrement(String name) { - incrementInternal(name, -1L); - } - - private void incrementInternal(String name, long value) { - if (name.startsWith("meter")) { - Meter meter = this.registry.meter(name); - meter.mark(value); - } - else { - name = wrapCounterName(name); - Counter counter = this.registry.counter(name); - counter.inc(value); - } - } - - @Override - public void submit(String name, double value) { - if (name.startsWith("histogram")) { - submitHistogram(name, value); - } - else if (name.startsWith("timer")) { - submitTimer(name, value); - } - else { - name = wrapGaugeName(name); - setGaugeValue(name, value); - } - } - - private void submitTimer(String name, double value) { - long longValue = (long) value; - Timer metric = register(name, new TimerMetricRegistrar()); - metric.update(longValue, TimeUnit.MILLISECONDS); - } - - private void submitHistogram(String name, double value) { - long longValue = (long) value; - Histogram metric = register(name, new HistogramMetricRegistrar()); - metric.update(longValue); - } - - @SuppressWarnings("unchecked") - private T register(String name, MetricRegistrar registrar) { - Reservoir reservoir = this.reservoirFactory.getReservoir(name); - if (reservoir == null) { - return registrar.register(this.registry, name); - } - Metric metric = this.registry.getMetrics().get(name); - if (metric != null) { - registrar.checkExisting(metric); - return (T) metric; - } - try { - return this.registry.register(name, registrar.createForReservoir(reservoir)); - } - catch (IllegalArgumentException ex) { - Metric added = this.registry.getMetrics().get(name); - registrar.checkExisting(added); - return (T) added; - } - } - - private void setGaugeValue(String name, double value) { - // NOTE: Dropwizard provides no way to do this atomically - SimpleGauge gauge = this.gauges.get(name); - if (gauge == null) { - SimpleGauge newGauge = new SimpleGauge(value); - gauge = this.gauges.putIfAbsent(name, newGauge); - if (gauge == null) { - this.registry.register(name, newGauge); - return; - } - } - gauge.setValue(value); - } - - private String wrapGaugeName(String metricName) { - return wrapName(metricName, "gauge."); - } - - private String wrapCounterName(String metricName) { - return wrapName(metricName, "counter."); - } - - private String wrapName(String metricName, String prefix) { - String cached = this.names.get(metricName); - if (cached != null) { - return cached; - } - if (metricName.startsWith(prefix)) { - return metricName; - } - String name = prefix + metricName; - this.names.put(metricName, name); - return name; - } - - @Override - public void reset(String name) { - if (!name.startsWith("meter")) { - name = wrapCounterName(name); - } - this.registry.remove(name); - } - - /** - * Simple {@link Gauge} implementation to {@literal double} value. - */ - private final static class SimpleGauge implements Gauge { - - private volatile double value; - - private SimpleGauge(double value) { - this.value = value; - } - - @Override - public Double getValue() { - return this.value; - } - - public void setValue(double value) { - this.value = value; - } - - } - - /** - * Strategy used to register metrics. - */ - private static abstract class MetricRegistrar { - - private final Class type; - - @SuppressWarnings("unchecked") - MetricRegistrar() { - this.type = (Class) ResolvableType - .forClass(MetricRegistrar.class, getClass()).resolveGeneric(); - } - - public void checkExisting(Metric metric) { - Assert.isInstanceOf(this.type, metric, - "Different metric type already registered"); - } - - protected abstract T register(MetricRegistry registry, String name); - - protected abstract T createForReservoir(Reservoir reservoir); - - } - - /** - * {@link MetricRegistrar} for {@link Timer} metrics. - */ - private static class TimerMetricRegistrar extends MetricRegistrar { - - @Override - protected Timer register(MetricRegistry registry, String name) { - return registry.timer(name); - } - - @Override - protected Timer createForReservoir(Reservoir reservoir) { - return new Timer(reservoir); - } - - } - - /** - * {@link MetricRegistrar} for {@link Histogram} metrics. - */ - private static class HistogramMetricRegistrar extends MetricRegistrar { - - @Override - protected Histogram register(MetricRegistry registry, String name) { - return registry.histogram(name); - } - - @Override - protected Histogram createForReservoir(Reservoir reservoir) { - return new Histogram(reservoir); - } - - } - -} diff --git a/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/metrics/dropwizard/ReservoirFactory.java b/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/metrics/dropwizard/ReservoirFactory.java deleted file mode 100644 index b3a20b6edfbf..000000000000 --- a/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/metrics/dropwizard/ReservoirFactory.java +++ /dev/null @@ -1,45 +0,0 @@ -/* - * 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.metrics.dropwizard; - -import com.codahale.metrics.Reservoir; - -/** - * Factory interface that can be used by {@link DropwizardMetricServices} to create a - * custom {@link Reservoir}. - * - * @author Lucas Saldanha - * @author Phillip Webb - * @since 1.5.0 - */ -@FunctionalInterface -public interface ReservoirFactory { - - /** - * Default empty {@link ReservoirFactory} implementation. - */ - ReservoirFactory NONE = (name) -> null; - - /** - * Return the {@link Reservoir} instance to use or {@code null} if a custom reservoir - * is not needed. - * @param name the name of the metric - * @return a reservoir instance or {@code null} - */ - Reservoir getReservoir(String name); - -} diff --git a/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/metrics/dropwizard/package-info.java b/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/metrics/dropwizard/package-info.java deleted file mode 100644 index 14864c15e2d4..000000000000 --- a/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/metrics/dropwizard/package-info.java +++ /dev/null @@ -1,20 +0,0 @@ -/* - * Copyright 2012-2015 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -/** - * Metrics integration with Dropwizard Metrics. - */ -package org.springframework.boot.actuate.metrics.dropwizard; diff --git a/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/metrics/export/AbstractMetricExporter.java b/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/metrics/export/AbstractMetricExporter.java deleted file mode 100644 index 9a93e2b0f7f7..000000000000 --- a/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/metrics/export/AbstractMetricExporter.java +++ /dev/null @@ -1,184 +0,0 @@ -/* - * 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.metrics.export; - -import java.io.Closeable; -import java.io.Flushable; -import java.io.IOException; -import java.util.ArrayList; -import java.util.Collection; -import java.util.Collections; -import java.util.Date; -import java.util.concurrent.atomic.AtomicBoolean; - -import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; - -import org.springframework.boot.actuate.metrics.Metric; -import org.springframework.util.StringUtils; - -/** - * Base class for metric exporters that have common features, principally a prefix for - * exported metrics and filtering by timestamp (so only new values are included in the - * export). - * - * @author Dave Syer - * @since 1.3.0 - */ -public abstract class AbstractMetricExporter implements Exporter, Closeable, Flushable { - - private static final Log logger = LogFactory.getLog(AbstractMetricExporter.class); - - private final String prefix; - - private Date earliestTimestamp = new Date(); - - private boolean ignoreTimestamps = false; - - private boolean sendLatest = true; - - private volatile AtomicBoolean processing = new AtomicBoolean(false); - - private Date latestTimestamp = new Date(0L); - - public AbstractMetricExporter(String prefix) { - this.prefix = (!StringUtils.hasText(prefix) ? "" - : (prefix.endsWith(".") ? prefix : prefix + ".")); - } - - /** - * The earliest time for which data will be exported. - * @param earliestTimestamp the timestamp to set - */ - public void setEarliestTimestamp(Date earliestTimestamp) { - this.earliestTimestamp = earliestTimestamp; - } - - /** - * Ignore timestamps (export all metrics). - * @param ignoreTimestamps the flag to set - */ - public void setIgnoreTimestamps(boolean ignoreTimestamps) { - this.ignoreTimestamps = ignoreTimestamps; - } - - /** - * Send only the data that changed since the last export. - * @param sendLatest the flag to set - */ - public void setSendLatest(boolean sendLatest) { - this.sendLatest = sendLatest; - } - - @Override - public void export() { - if (this.processing.compareAndSet(false, true)) { - long latestTimestamp = System.currentTimeMillis(); - try { - exportGroups(); - } - catch (Exception ex) { - logger.warn("Could not write to MetricWriter: " + ex.getClass() + ": " - + ex.getMessage()); - } - finally { - this.latestTimestamp = new Date(latestTimestamp); - flushQuietly(); - this.processing.set(false); - } - } - } - - private void exportGroups() { - for (String group : groups()) { - Collection> values = new ArrayList<>(); - for (Metric metric : next(group)) { - Date timestamp = metric.getTimestamp(); - if (canExportTimestamp(timestamp)) { - values.add(getPrefixedMetric(metric)); - } - } - if (!values.isEmpty()) { - write(group, values); - } - } - } - - private Metric getPrefixedMetric(Metric metric) { - String name = this.prefix + metric.getName(); - return new Metric(name, metric.getValue(), metric.getTimestamp()); - } - - private boolean canExportTimestamp(Date timestamp) { - if (this.ignoreTimestamps) { - return true; - } - if (this.earliestTimestamp.after(timestamp)) { - return false; - } - if (this.sendLatest && this.latestTimestamp.after(timestamp)) { - return false; - } - return true; - } - - private void flushQuietly() { - try { - flush(); - } - catch (Exception ex) { - logger.warn("Could not flush MetricWriter: " + ex.getClass() + ": " - + ex.getMessage()); - } - } - - @Override - public void close() throws IOException { - export(); - flushQuietly(); - } - - @Override - public void flush() { - } - - /** - * Generate a group of metrics to iterate over in the form of a set of Strings (e.g. - * prefixes). If the metrics to be exported partition into groups identified by a - * String, subclasses should override this method. Otherwise the default should be - * fine (iteration over all metrics). - * @return groups of metrics to iterate over (default singleton empty string) - */ - protected Iterable groups() { - return Collections.singleton(""); - } - - /** - * Write the values associated with a group. - * @param group the group to write - * @param values the values to write - */ - protected abstract void write(String group, Collection> values); - - /** - * Get the next group of metrics to write. - * @param group the group name to write - * @return some metrics to write - */ - protected abstract Iterable> next(String group); - -} diff --git a/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/metrics/export/Exporter.java b/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/metrics/export/Exporter.java deleted file mode 100644 index 067197b19ada..000000000000 --- a/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/metrics/export/Exporter.java +++ /dev/null @@ -1,38 +0,0 @@ -/* - * 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.metrics.export; - -/** - * Generic interface for metric exports. As you scale up metric collection you will often - * need to buffer metric data locally and export it periodically (e.g. for aggregation - * across a cluster), so this is the marker interface for those operations. The trigger of - * an export operation might be periodic or event driven, but it remains outside the scope - * of this interface. You might for instance create an instance of an Exporter and trigger - * it using a {@code @Scheduled} annotation in a Spring ApplicationContext. - * - * @author Dave Syer - * @since 1.3.0 - */ -@FunctionalInterface -public interface Exporter { - - /** - * Export metric data. - */ - void export(); - -} diff --git a/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/metrics/export/MetricCopyExporter.java b/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/metrics/export/MetricCopyExporter.java deleted file mode 100644 index 5eec2cdff568..000000000000 --- a/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/metrics/export/MetricCopyExporter.java +++ /dev/null @@ -1,244 +0,0 @@ -/* - * 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.metrics.export; - -import java.io.Flushable; -import java.lang.reflect.Method; -import java.util.Collection; -import java.util.Iterator; -import java.util.concurrent.ConcurrentHashMap; -import java.util.concurrent.ConcurrentMap; - -import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; - -import org.springframework.boot.actuate.metrics.Metric; -import org.springframework.boot.actuate.metrics.reader.MetricReader; -import org.springframework.boot.actuate.metrics.writer.CompositeMetricWriter; -import org.springframework.boot.actuate.metrics.writer.CounterWriter; -import org.springframework.boot.actuate.metrics.writer.Delta; -import org.springframework.boot.actuate.metrics.writer.GaugeWriter; -import org.springframework.boot.actuate.metrics.writer.MetricWriter; -import org.springframework.util.ClassUtils; -import org.springframework.util.ObjectUtils; -import org.springframework.util.PatternMatchUtils; -import org.springframework.util.ReflectionUtils; - -/** - * {@link Exporter} that "exports" by copying metric data from a source - * {@link MetricReader} to a destination {@link MetricWriter}. Actually the output writer - * can be a {@link GaugeWriter}, in which case all metrics are simply output as their - * current value. If the output writer is also a {@link CounterWriter} then metrics whose - * names begin with "counter." are special: instead of writing them out as simple gauges - * the writer will increment the counter value. This involves the exporter storing the - * previous value of the counter so the delta can be computed. For best results with the - * counters, do not use the exporter concurrently in multiple threads (normally it will - * only be used periodically and sequentially, even if it is in a background thread, and - * this is fine). - * - * @author Dave Syer - * @since 1.3.0 - */ -public class MetricCopyExporter extends AbstractMetricExporter { - - private static final Log logger = LogFactory.getLog(MetricCopyExporter.class); - - private final MetricReader reader; - - private final GaugeWriter writer; - - private final CounterWriter counter; - - private ConcurrentMap counts = new ConcurrentHashMap<>(); - - private String[] includes = new String[0]; - - private String[] excludes = new String[0]; - - /** - * Create a new {@link MetricCopyExporter} instance. - * @param reader the metric reader - * @param writer the metric writer - */ - public MetricCopyExporter(MetricReader reader, GaugeWriter writer) { - this(reader, writer, ""); - } - - /** - * Create a new {@link MetricCopyExporter} instance. - * @param reader the metric reader - * @param writer the metric writer - * @param prefix the name prefix - */ - public MetricCopyExporter(MetricReader reader, GaugeWriter writer, String prefix) { - super(prefix); - this.reader = reader; - this.writer = writer; - if (writer instanceof CounterWriter) { - this.counter = (CounterWriter) writer; - } - else { - this.counter = null; - } - } - - /** - * Set the include patterns used to filter metrics. - * @param includes the include patterns - */ - public void setIncludes(String... includes) { - if (includes != null) { - this.includes = includes; - } - } - - /** - * Set the exclude patterns used to filter metrics. - * @param excludes the exclude patterns - */ - public void setExcludes(String... excludes) { - if (excludes != null) { - this.excludes = excludes; - } - } - - @Override - protected Iterable> next(String group) { - if (ObjectUtils.isEmpty(this.includes) && ObjectUtils.isEmpty(this.excludes)) { - return this.reader.findAll(); - } - return new PatternMatchingIterable(MetricCopyExporter.this.reader); - } - - @Override - protected void write(String group, Collection> values) { - for (Metric value : values) { - if (value.getName().startsWith("counter.") && this.counter != null) { - this.counter.increment(calculateDelta(value)); - } - else { - this.writer.set(value); - } - } - } - - private Delta calculateDelta(Metric value) { - long delta = value.getValue().longValue(); - Long old = this.counts.replace(value.getName(), delta); - if (old != null) { - delta = delta - old; - } - else { - this.counts.putIfAbsent(value.getName(), delta); - } - return new Delta<>(value.getName(), delta, value.getTimestamp()); - } - - @Override - public void flush() { - flush(this.writer); - } - - private void flush(GaugeWriter writer) { - if (writer instanceof CompositeMetricWriter) { - for (MetricWriter child : (CompositeMetricWriter) writer) { - flush(child); - } - } - try { - if (ClassUtils.isPresent("java.io.Flushable", null)) { - if (writer instanceof Flushable) { - ((Flushable) writer).flush(); - return; - } - } - Method method = ReflectionUtils.findMethod(writer.getClass(), "flush"); - if (method != null) { - ReflectionUtils.invokeMethod(method, writer); - } - } - catch (Exception ex) { - logger.warn("Could not flush MetricWriter: " + ex.getClass() + ": " - + ex.getMessage()); - } - } - - private class PatternMatchingIterable implements Iterable> { - - private final MetricReader reader; - - PatternMatchingIterable(MetricReader reader) { - this.reader = reader; - } - - @Override - public Iterator> iterator() { - return new PatternMatchingIterator(this.reader.findAll().iterator()); - } - - } - - private class PatternMatchingIterator implements Iterator> { - - private Metric buffer = null; - - private Iterator> iterator; - - PatternMatchingIterator(Iterator> iterator) { - this.iterator = iterator; - } - - @Override - public boolean hasNext() { - if (this.buffer != null) { - return true; - } - this.buffer = findNext(); - return this.buffer != null; - } - - private Metric findNext() { - while (this.iterator.hasNext()) { - Metric metric = this.iterator.next(); - if (isMatch(metric)) { - return metric; - } - } - return null; - } - - private boolean isMatch(Metric metric) { - String[] includes = MetricCopyExporter.this.includes; - String[] excludes = MetricCopyExporter.this.excludes; - String name = metric.getName(); - if (ObjectUtils.isEmpty(includes) - || PatternMatchUtils.simpleMatch(includes, name)) { - return !PatternMatchUtils.simpleMatch(excludes, name); - } - return false; - } - - @Override - public Metric next() { - Metric metric = this.buffer; - this.buffer = null; - return metric; - } - - }; - -} diff --git a/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/metrics/export/MetricExportProperties.java b/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/metrics/export/MetricExportProperties.java deleted file mode 100644 index 39222ea745c9..000000000000 --- a/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/metrics/export/MetricExportProperties.java +++ /dev/null @@ -1,265 +0,0 @@ -/* - * 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.metrics.export; - -import java.util.LinkedHashMap; -import java.util.Map; -import java.util.Map.Entry; - -import javax.annotation.PostConstruct; - -import org.springframework.boot.context.properties.ConfigurationProperties; -import org.springframework.util.PatternMatchUtils; - -/** - * Configuration properties for metrics export. - * - * @author Dave Syer - * @author Simon Buettner - * @since 1.3.0 - */ -@ConfigurationProperties(prefix = "spring.metrics.export") -public class MetricExportProperties extends TriggerProperties { - - /** - * Specific trigger properties per MetricWriter bean name. - */ - private Map triggers = new LinkedHashMap<>(); - - private Aggregate aggregate = new Aggregate(); - - private Redis redis = new Redis(); - - private Statsd statsd = new Statsd(); - - @PostConstruct - public void setUpDefaults() { - TriggerProperties defaults = this; - for (Entry entry : this.triggers.entrySet()) { - String key = entry.getKey(); - SpecificTriggerProperties value = entry.getValue(); - if (value.getNames() == null || value.getNames().length == 0) { - value.setNames(new String[] { key }); - } - } - if (defaults.isSendLatest() == null) { - defaults.setSendLatest(true); - } - if (defaults.getDelayMillis() == null) { - defaults.setDelayMillis(5000); - } - for (TriggerProperties value : this.triggers.values()) { - if (value.isSendLatest() == null) { - value.setSendLatest(defaults.isSendLatest()); - } - if (value.getDelayMillis() == null) { - value.setDelayMillis(defaults.getDelayMillis()); - } - } - } - - /** - * Configuration for triggers on individual named writers. Each value can individually - * specify a name pattern explicitly, or else the map key will be used if the name is - * not set. - * @return the writers - */ - public Map getTriggers() { - return this.triggers; - } - - public Aggregate getAggregate() { - return this.aggregate; - } - - public void setAggregate(Aggregate aggregate) { - this.aggregate = aggregate; - } - - public Redis getRedis() { - return this.redis; - } - - public void setRedis(Redis redis) { - this.redis = redis; - } - - public Statsd getStatsd() { - return this.statsd; - } - - public void setStatsd(Statsd statsd) { - this.statsd = statsd; - } - - /** - * Find a matching trigger configuration. - * @param name the bean name to match - * @return a matching configuration if there is one - */ - public TriggerProperties findTrigger(String name) { - for (SpecificTriggerProperties value : this.triggers.values()) { - if (PatternMatchUtils.simpleMatch(value.getNames(), name)) { - return value; - } - } - return this; - } - - /** - * Aggregate properties. - */ - public static class Aggregate { - - /** - * Prefix for global repository if active. Should be unique for this JVM, but most - * useful if it also has the form "a.b" where "a" is unique to this logical - * process (this application) and "b" is unique to this physical process. If you - * set spring.application.name elsewhere, then the default will be in the right - * form. - */ - private String prefix = ""; - - /** - * Pattern that tells the aggregator what to do with the keys from the source - * repository. The keys in the source repository are assumed to be period - * separated, and the pattern is in the same format, e.g. "d.d.k.d". Here "d" - * means "discard" and "k" means "keep" the key segment in the corresponding - * position in the source. - */ - private String keyPattern = ""; - - public String getPrefix() { - return this.prefix; - } - - public void setPrefix(String prefix) { - this.prefix = prefix; - } - - public String getKeyPattern() { - return this.keyPattern; - } - - public void setKeyPattern(String keyPattern) { - this.keyPattern = keyPattern; - } - - } - - /** - * Redis properties. - */ - public static class Redis { - - /** - * Prefix for redis repository if active. Should be globally unique across all - * processes sharing the same repository. - */ - private String prefix = "spring.metrics"; - - /** - * Key for redis repository export (if active). Should be globally unique for a - * system sharing a redis repository across multiple processes. - */ - private String key = "keys.spring.metrics"; - - public String getPrefix() { - return this.prefix; - } - - public void setPrefix(String prefix) { - this.prefix = prefix; - } - - public String getKey() { - return this.key; - } - - public void setKey(String key) { - this.key = key; - } - - public String getAggregatePrefix() { - // The common case including a standalone aggregator would have a prefix that - // starts with the end of the key, so strip that bit off and call it the - // aggregate prefix. - if (this.key.startsWith("keys.")) { - String candidate = this.key.substring("keys.".length()); - if (this.prefix.startsWith(candidate)) { - return candidate; - } - return candidate; - } - // If the user went off piste, choose something that is safe (not empty) but - // not the whole prefix (on the assumption that it contains dimension keys) - if (this.prefix.contains(".") - && this.prefix.indexOf(".") < this.prefix.length() - 1) { - return this.prefix.substring(this.prefix.indexOf(".") + 1); - } - return this.prefix; - } - - } - - /** - * Statsd properties. - */ - public static class Statsd { - - /** - * Host of a statsd server to receive exported metrics. - */ - private String host; - - /** - * Port of a statsd server to receive exported metrics. - */ - private int port = 8125; - - /** - * Prefix for statsd exported metrics. - */ - private String prefix; - - public String getHost() { - return this.host; - } - - public void setHost(String host) { - this.host = host; - } - - public int getPort() { - return this.port; - } - - public void setPort(int port) { - this.port = port; - } - - public String getPrefix() { - return this.prefix; - } - - public void setPrefix(String prefix) { - this.prefix = prefix; - } - - } - -} diff --git a/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/metrics/export/MetricExporters.java b/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/metrics/export/MetricExporters.java deleted file mode 100644 index fb6d06e7d205..000000000000 --- a/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/metrics/export/MetricExporters.java +++ /dev/null @@ -1,134 +0,0 @@ -/* - * 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.metrics.export; - -import java.io.Closeable; -import java.io.IOException; -import java.util.HashMap; -import java.util.HashSet; -import java.util.Map; -import java.util.Map.Entry; -import java.util.Set; - -import org.springframework.boot.actuate.metrics.reader.MetricReader; -import org.springframework.boot.actuate.metrics.writer.GaugeWriter; -import org.springframework.scheduling.annotation.SchedulingConfigurer; -import org.springframework.scheduling.config.IntervalTask; -import org.springframework.scheduling.config.ScheduledTaskRegistrar; - -/** - * {@link SchedulingConfigurer} to handle metrics {@link MetricCopyExporter export}. - * - * @author Dave Syer - * @since 1.3.0 - */ -public class MetricExporters implements SchedulingConfigurer, Closeable { - - private MetricReader reader; - - private Map writers = new HashMap<>(); - - private final MetricExportProperties properties; - - private final Map exporters = new HashMap<>(); - - private final Set closeables = new HashSet<>(); - - public MetricExporters(MetricExportProperties properties) { - this.properties = properties; - } - - public void setReader(MetricReader reader) { - this.reader = reader; - } - - public void setWriters(Map writers) { - this.writers.putAll(writers); - } - - public void setExporters(Map exporters) { - this.exporters.putAll(exporters); - } - - @Override - public void configureTasks(ScheduledTaskRegistrar taskRegistrar) { - for (Entry entry : this.exporters.entrySet()) { - String name = entry.getKey(); - Exporter exporter = entry.getValue(); - TriggerProperties trigger = this.properties.findTrigger(name); - if (trigger != null) { - ExportRunner runner = new ExportRunner(exporter); - IntervalTask task = new IntervalTask(runner, trigger.getDelayMillis(), - trigger.getDelayMillis()); - taskRegistrar.addFixedDelayTask(task); - } - } - for (Entry entry : this.writers.entrySet()) { - String name = entry.getKey(); - GaugeWriter writer = entry.getValue(); - TriggerProperties trigger = this.properties.findTrigger(name); - if (trigger != null) { - MetricCopyExporter exporter = getExporter(writer, trigger); - this.exporters.put(name, exporter); - this.closeables.add(name); - ExportRunner runner = new ExportRunner(exporter); - IntervalTask task = new IntervalTask(runner, trigger.getDelayMillis(), - trigger.getDelayMillis()); - taskRegistrar.addFixedDelayTask(task); - } - } - } - - private MetricCopyExporter getExporter(GaugeWriter writer, - TriggerProperties trigger) { - MetricCopyExporter exporter = new MetricCopyExporter(this.reader, writer); - exporter.setIncludes(trigger.getIncludes()); - exporter.setExcludes(trigger.getExcludes()); - exporter.setSendLatest(trigger.isSendLatest()); - return exporter; - } - - public Map getExporters() { - return this.exporters; - } - - @Override - public void close() throws IOException { - for (String name : this.closeables) { - Exporter exporter = this.exporters.get(name); - if (exporter instanceof Closeable) { - ((Closeable) exporter).close(); - } - } - } - - private static class ExportRunner implements Runnable { - - private final Exporter exporter; - - ExportRunner(Exporter exporter) { - this.exporter = exporter; - } - - @Override - public void run() { - this.exporter.export(); - } - - } - -} diff --git a/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/metrics/export/PrefixMetricGroupExporter.java b/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/metrics/export/PrefixMetricGroupExporter.java deleted file mode 100644 index 90e8a9ebe77d..000000000000 --- a/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/metrics/export/PrefixMetricGroupExporter.java +++ /dev/null @@ -1,118 +0,0 @@ -/* - * 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.metrics.export; - -import java.util.Collection; -import java.util.HashSet; -import java.util.Set; -import java.util.concurrent.ConcurrentHashMap; -import java.util.concurrent.ConcurrentMap; - -import org.springframework.boot.actuate.metrics.Metric; -import org.springframework.boot.actuate.metrics.reader.PrefixMetricReader; -import org.springframework.boot.actuate.metrics.repository.MultiMetricRepository; -import org.springframework.boot.actuate.metrics.writer.Delta; -import org.springframework.boot.actuate.metrics.writer.PrefixMetricWriter; - -/** - * A convenient exporter for a group of metrics from a {@link PrefixMetricReader}. Exports - * all metrics whose name starts with a prefix (or all metrics if the prefix is empty). - * - * @author Dave Syer - * @since 1.3.0 - */ -public class PrefixMetricGroupExporter extends AbstractMetricExporter { - - private final PrefixMetricReader reader; - - private final PrefixMetricWriter writer; - - private ConcurrentMap counts = new ConcurrentHashMap<>(); - - private Set groups = new HashSet<>(); - - /** - * Create a new exporter for metrics to a writer based on an empty prefix for the - * metric names. - * @param reader a reader as the source of metrics - * @param writer the writer to send the metrics to - */ - public PrefixMetricGroupExporter(PrefixMetricReader reader, - PrefixMetricWriter writer) { - this(reader, writer, ""); - } - - /** - * Create a new exporter for metrics to a writer based on a prefix for the metric - * names. - * @param reader a reader as the source of metrics - * @param writer the writer to send the metrics to - * @param prefix the prefix for metrics to export - */ - public PrefixMetricGroupExporter(PrefixMetricReader reader, PrefixMetricWriter writer, - String prefix) { - super(prefix); - this.reader = reader; - this.writer = writer; - } - - /** - * The groups to export. - * @param groups the groups to set - */ - public void setGroups(Set groups) { - this.groups = groups; - } - - @Override - protected Iterable groups() { - if ((this.reader instanceof MultiMetricRepository) && this.groups.isEmpty()) { - return ((MultiMetricRepository) this.reader).groups(); - } - return this.groups; - } - - @Override - protected Iterable> next(String group) { - return this.reader.findAll(group); - } - - @Override - protected void write(String group, Collection> values) { - if (group.contains("counter.")) { - for (Metric value : values) { - this.writer.increment(group, calculateDelta(value)); - } - } - else { - this.writer.set(group, values); - } - } - - private Delta calculateDelta(Metric value) { - long delta = value.getValue().longValue(); - Long old = this.counts.replace(value.getName(), delta); - if (old != null) { - delta = delta - old; - } - else { - this.counts.putIfAbsent(value.getName(), delta); - } - return new Delta<>(value.getName(), delta, value.getTimestamp()); - } - -} diff --git a/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/metrics/export/RichGaugeExporter.java b/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/metrics/export/RichGaugeExporter.java deleted file mode 100644 index f996bac0f1f0..000000000000 --- a/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/metrics/export/RichGaugeExporter.java +++ /dev/null @@ -1,98 +0,0 @@ -/* - * 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.metrics.export; - -import java.util.ArrayList; -import java.util.Collection; -import java.util.HashSet; - -import org.springframework.boot.actuate.metrics.Metric; -import org.springframework.boot.actuate.metrics.repository.MultiMetricRepository; -import org.springframework.boot.actuate.metrics.rich.RichGauge; -import org.springframework.boot.actuate.metrics.rich.RichGaugeReader; -import org.springframework.boot.actuate.metrics.writer.MetricWriter; -import org.springframework.boot.actuate.metrics.writer.PrefixMetricWriter; - -/** - * Exporter or converter for {@link RichGauge} data to a metric-based back end. Each gauge - * measurement is stored as a set of related metrics with a common prefix (the name of the - * gauge), and suffixes that describe the data. For example, a gauge called {@code foo} is - * stored as {@code[foo.min, foo.max. foo.val, foo.count, foo.avg, foo.alpha]}. If the - * {@link MetricWriter} provided is a {@link MultiMetricRepository} then the values for a - * gauge will be stored as a group, and hence will be retrievable from the repository in a - * single query (or optionally individually). - * - * @author Dave Syer - * @since 1.3.0 - */ -public class RichGaugeExporter extends AbstractMetricExporter { - - private static final String MIN = ".min"; - - private static final String MAX = ".max"; - - private static final String COUNT = ".count"; - - private static final String VALUE = ".val"; - - private static final String AVG = ".avg"; - - private static final String ALPHA = ".alpha"; - - private final RichGaugeReader reader; - - private final PrefixMetricWriter writer; - - public RichGaugeExporter(RichGaugeReader reader, PrefixMetricWriter writer) { - this(reader, writer, ""); - } - - public RichGaugeExporter(RichGaugeReader reader, PrefixMetricWriter writer, - String prefix) { - super(prefix); - this.reader = reader; - this.writer = writer; - } - - @Override - protected Iterable> next(String group) { - RichGauge rich = this.reader.findOne(group); - Collection> metrics = new ArrayList<>(); - metrics.add(new Metric(group + MIN, rich.getMin())); - metrics.add(new Metric(group + MAX, rich.getMax())); - metrics.add(new Metric(group + COUNT, rich.getCount())); - metrics.add(new Metric(group + VALUE, rich.getValue())); - metrics.add(new Metric(group + AVG, rich.getAverage())); - metrics.add(new Metric(group + ALPHA, rich.getAlpha())); - return metrics; - } - - @Override - protected Iterable groups() { - Collection names = new HashSet<>(); - for (RichGauge rich : this.reader.findAll()) { - names.add(rich.getName()); - } - return names; - } - - @Override - protected void write(String group, Collection> values) { - this.writer.set(group, values); - } - -} diff --git a/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/metrics/export/SpecificTriggerProperties.java b/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/metrics/export/SpecificTriggerProperties.java deleted file mode 100644 index 0b291dbb9ed2..000000000000 --- a/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/metrics/export/SpecificTriggerProperties.java +++ /dev/null @@ -1,40 +0,0 @@ -/* - * Copyright 2012-2015 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.boot.actuate.metrics.export; - -/** - * Trigger for specific names or patterns. - * - * @author Dave Syer - * @since 1.3.0 - */ -public class SpecificTriggerProperties extends TriggerProperties { - - /** - * Names (or patterns) for bean names that this configuration applies to. - */ - private String[] names; - - public String[] getNames() { - return this.names; - } - - public void setNames(String[] names) { - this.names = names; - } - -} diff --git a/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/metrics/export/TriggerProperties.java b/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/metrics/export/TriggerProperties.java deleted file mode 100644 index 513c0a0f377a..000000000000 --- a/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/metrics/export/TriggerProperties.java +++ /dev/null @@ -1,94 +0,0 @@ -/* - * Copyright 2012-2015 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.boot.actuate.metrics.export; - -/** - * Abstract base class for trigger properties. - * - * @author Dave Syer - * @since 1.3.0 - */ -public abstract class TriggerProperties { - - /** - * Delay in milliseconds between export ticks. Metrics are exported to external - * sources on a schedule with this delay. - */ - private Long delayMillis; - - /** - * Flag to enable metric export (assuming a MetricWriter is available). - */ - private boolean enabled = true; - - /** - * Flag to switch off any available optimizations based on not exporting unchanged - * metric values. - */ - private Boolean sendLatest; - - /** - * List of patterns for metric names to include. - */ - private String[] includes; - - /** - * List of patterns for metric names to exclude. Applied after the includes. - */ - private String[] excludes; - - public String[] getIncludes() { - return this.includes; - } - - public void setIncludes(String[] includes) { - this.includes = includes; - } - - public void setExcludes(String[] excludes) { - this.excludes = excludes; - } - - public String[] getExcludes() { - return this.excludes; - } - - public boolean isEnabled() { - return this.enabled; - } - - public void setEnabled(boolean enabled) { - this.enabled = enabled; - } - - public Long getDelayMillis() { - return this.delayMillis; - } - - public void setDelayMillis(long delayMillis) { - this.delayMillis = delayMillis; - } - - public Boolean isSendLatest() { - return this.sendLatest; - } - - public void setSendLatest(boolean sendLatest) { - this.sendLatest = sendLatest; - } - -} diff --git a/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/metrics/export/package-info.java b/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/metrics/export/package-info.java deleted file mode 100644 index 2a0ab81c8095..000000000000 --- a/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/metrics/export/package-info.java +++ /dev/null @@ -1,20 +0,0 @@ -/* - * Copyright 2012-2015 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -/** - * Metrics export support. - */ -package org.springframework.boot.actuate.metrics.export; diff --git a/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/metrics/export/prometheus/PrometheusScrapeEndpoint.java b/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/metrics/export/prometheus/PrometheusScrapeEndpoint.java new file mode 100644 index 000000000000..1d914e648e72 --- /dev/null +++ b/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/metrics/export/prometheus/PrometheusScrapeEndpoint.java @@ -0,0 +1,60 @@ +/* + * 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.metrics.export.prometheus; + +import java.io.IOException; +import java.io.StringWriter; +import java.io.Writer; + +import io.prometheus.client.CollectorRegistry; +import io.prometheus.client.exporter.common.TextFormat; + +import org.springframework.boot.actuate.endpoint.EndpointExposure; +import org.springframework.boot.actuate.endpoint.annotation.Endpoint; +import org.springframework.boot.actuate.endpoint.annotation.ReadOperation; + +/** + * {@link Endpoint} that outputs metrics in a format that can be scraped by the Prometheus + * server. + * + * @author Jon Schneider + * @since 2.0.0 + */ +@Endpoint(id = "prometheus", exposure = EndpointExposure.WEB) +public class PrometheusScrapeEndpoint { + + private final CollectorRegistry collectorRegistry; + + public PrometheusScrapeEndpoint(CollectorRegistry collectorRegistry) { + this.collectorRegistry = collectorRegistry; + } + + @ReadOperation(produces = TextFormat.CONTENT_TYPE_004) + public String scrape() { + try { + Writer writer = new StringWriter(); + TextFormat.write004(writer, this.collectorRegistry.metricFamilySamples()); + return writer.toString(); + } + catch (IOException e) { + // This actually never happens since StringWriter::write() doesn't throw any + // IOException + throw new RuntimeException("Writing metrics failed", e); + } + } + +} diff --git a/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/metrics/integration/SpringIntegrationMetricReader.java b/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/metrics/integration/SpringIntegrationMetricReader.java deleted file mode 100644 index cb3bffb186ae..000000000000 --- a/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/metrics/integration/SpringIntegrationMetricReader.java +++ /dev/null @@ -1,131 +0,0 @@ -/* - * 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.metrics.integration; - -import java.util.ArrayList; -import java.util.Collection; -import java.util.List; - -import org.springframework.boot.actuate.metrics.Metric; -import org.springframework.boot.actuate.metrics.reader.MetricReader; -import org.springframework.integration.support.management.IntegrationManagementConfigurer; -import org.springframework.integration.support.management.MessageChannelMetrics; -import org.springframework.integration.support.management.MessageHandlerMetrics; -import org.springframework.integration.support.management.MessageSourceMetrics; -import org.springframework.integration.support.management.PollableChannelManagement; -import org.springframework.integration.support.management.Statistics; - -/** - * A {@link MetricReader} for Spring Integration metrics (as provided by - * {@link IntegrationManagementConfigurer}). - * - * @author Dave Syer - * @author Artem Bilan - * @since 1.3.0 - */ -public class SpringIntegrationMetricReader implements MetricReader { - - private final IntegrationManagementConfigurer configurer; - - public SpringIntegrationMetricReader(IntegrationManagementConfigurer configurer) { - this.configurer = configurer; - } - - @Override - public Metric findOne(String metricName) { - return null; - } - - @Override - public Iterable> findAll() { - List> result = new ArrayList<>(); - String[] channelNames = this.configurer.getChannelNames(); - String[] handlerNames = this.configurer.getHandlerNames(); - String[] sourceNames = this.configurer.getSourceNames(); - addChannelMetrics(result, channelNames); - addHandlerMetrics(result, handlerNames); - addSourceMetrics(result, sourceNames); - result.add(new Metric<>("integration.handlerCount", handlerNames.length)); - result.add(new Metric<>("integration.channelCount", channelNames.length)); - result.add(new Metric<>("integration.sourceCount", sourceNames.length)); - return result; - } - - private void addChannelMetrics(List> result, String[] names) { - for (String name : names) { - addChannelMetrics(result, name, this.configurer.getChannelMetrics(name)); - } - } - - private void addChannelMetrics(List> result, String name, - MessageChannelMetrics metrics) { - String prefix = "integration.channel." + name; - result.addAll(getStatistics(prefix + ".errorRate", metrics.getErrorRate())); - result.add(new Metric<>(prefix + ".sendCount", metrics.getSendCountLong())); - result.addAll(getStatistics(prefix + ".sendRate", metrics.getSendRate())); - if (metrics instanceof PollableChannelManagement) { - result.add(new Metric<>(prefix + ".receiveCount", - ((PollableChannelManagement) metrics).getReceiveCountLong())); - } - } - - private void addHandlerMetrics(List> result, String[] names) { - for (String name : names) { - addHandlerMetrics(result, name, this.configurer.getHandlerMetrics(name)); - } - } - - private void addHandlerMetrics(List> result, String name, - MessageHandlerMetrics metrics) { - String prefix = "integration.handler." + name; - result.addAll(getStatistics(prefix + ".duration", metrics.getDuration())); - long activeCount = metrics.getActiveCountLong(); - result.add(new Metric<>(prefix + ".activeCount", activeCount)); - } - - private void addSourceMetrics(List> result, String[] names) { - for (String name : names) { - addSourceMetrics(result, name, this.configurer.getSourceMetrics(name)); - } - } - - private void addSourceMetrics(List> result, String name, - MessageSourceMetrics sourceMetrics) { - String prefix = "integration.source." + name; - result.add(new Metric<>(prefix + ".messageCount", - sourceMetrics.getMessageCountLong())); - } - - private Collection> getStatistics(String name, Statistics stats) { - List> metrics = new ArrayList<>(); - metrics.add(new Metric<>(name + ".mean", stats.getMean())); - metrics.add(new Metric<>(name + ".max", stats.getMax())); - metrics.add(new Metric<>(name + ".min", stats.getMin())); - metrics.add(new Metric<>(name + ".stdev", stats.getStandardDeviation())); - metrics.add(new Metric<>(name + ".count", stats.getCountLong())); - return metrics; - } - - @Override - public long count() { - int totalChannelCount = this.configurer.getChannelNames().length; - int totalHandlerCount = this.configurer.getHandlerNames().length; - int totalSourceCount = this.configurer.getSourceNames().length; - return totalChannelCount + totalHandlerCount + totalSourceCount; - } - -} diff --git a/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/metrics/integration/SpringIntegrationMetrics.java b/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/metrics/integration/SpringIntegrationMetrics.java new file mode 100644 index 000000000000..3fb677d1d1b6 --- /dev/null +++ b/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/metrics/integration/SpringIntegrationMetrics.java @@ -0,0 +1,201 @@ +/* + * 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.metrics.integration; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.concurrent.TimeUnit; + +import io.micrometer.core.instrument.Meter.Id; +import io.micrometer.core.instrument.MeterRegistry; +import io.micrometer.core.instrument.Tag; +import io.micrometer.core.instrument.Tags; +import io.micrometer.core.instrument.binder.MeterBinder; + +import org.springframework.beans.factory.SmartInitializingSingleton; +import org.springframework.integration.support.management.IntegrationManagementConfigurer; +import org.springframework.integration.support.management.MessageChannelMetrics; +import org.springframework.integration.support.management.MessageHandlerMetrics; +import org.springframework.integration.support.management.MessageSourceMetrics; +import org.springframework.integration.support.management.PollableChannelManagement; + +/** + * A {@link MeterBinder} for Spring Integration metrics. + * + * @author Jon Schneider + * @since 2.0.0 + */ +public class SpringIntegrationMetrics implements MeterBinder, SmartInitializingSingleton { + + private final Iterable tags; + + private Collection registries = new ArrayList<>(); + + private final IntegrationManagementConfigurer configurer; + + public SpringIntegrationMetrics(IntegrationManagementConfigurer configurer) { + this(configurer, Collections.emptyList()); + } + + public SpringIntegrationMetrics(IntegrationManagementConfigurer configurer, + Iterable tags) { + this.configurer = configurer; + this.tags = tags; + } + + @Override + public void bindTo(MeterRegistry registry) { + bindChannelNames(registry); + bindHandlerNames(registry); + bindSourceNames(registry); + this.registries.add(registry); + } + + private void bindChannelNames(MeterRegistry registry) { + Id id = registry.createId("spring.integration.channelNames", this.tags, + "The number of spring integration channels"); + registry.gauge(id, this.configurer, (c) -> c.getChannelNames().length); + } + + private void bindHandlerNames(MeterRegistry registry) { + Id id = registry.createId("spring.integration.handlerNames", this.tags, + "The number of spring integration handlers"); + registry.gauge(id, this.configurer, (c) -> c.getHandlerNames().length); + } + + private void bindSourceNames(MeterRegistry registry) { + Id id = registry.createId("spring.integration.sourceNames", this.tags, + "The number of spring integration sources"); + registry.gauge(id, this.configurer, (c) -> c.getSourceNames().length); + } + + private void addSourceMetrics(MeterRegistry registry) { + for (String sourceName : this.configurer.getSourceNames()) { + addSourceMetrics(registry, sourceName); + } + } + + private void addSourceMetrics(MeterRegistry registry, String sourceName) { + MessageSourceMetrics sourceMetrics = this.configurer.getSourceMetrics(sourceName); + Iterable tags = Tags.concat(this.tags, "source", sourceName); + Id id = registry.createId("spring.integration.source.messages", tags, + "The number of successful handler calls"); + registry.more().counter(id, sourceMetrics, MessageSourceMetrics::getMessageCount); + } + + private void addHandlerMetrics(MeterRegistry registry) { + for (String handler : this.configurer.getHandlerNames()) { + addHandlerMetrics(registry, handler); + } + } + + private void addHandlerMetrics(MeterRegistry registry, String handler) { + MessageHandlerMetrics handlerMetrics = this.configurer.getHandlerMetrics(handler); + // FIXME could use improvement to dynamically compute the handler name with its + // ID, which can change after + // creation as shown in the SpringIntegrationApplication sample. + Iterable tags = Tags.concat(this.tags, "handler", handler); + addDurationMax(registry, handlerMetrics, tags); + addDurationMin(registry, handlerMetrics, tags); + addDurationMean(registry, handlerMetrics, tags); + addActiveCount(registry, handlerMetrics, tags); + } + + private void addDurationMax(MeterRegistry registry, + MessageHandlerMetrics handlerMetrics, Iterable tags) { + Id id = registry.createId("spring.integration.handler.duration.max", tags, + "The maximum handler duration"); + registry.more().timeGauge(id, handlerMetrics, TimeUnit.MILLISECONDS, + MessageHandlerMetrics::getMaxDuration); + } + + private void addDurationMin(MeterRegistry registry, + MessageHandlerMetrics handlerMetrics, Iterable tags) { + Id id = registry.createId("spring.integration.handler.duration.min", tags, + "The minimum handler duration"); + registry.more().timeGauge(id, handlerMetrics, TimeUnit.MILLISECONDS, + MessageHandlerMetrics::getMinDuration); + } + + private void addDurationMean(MeterRegistry registry, + MessageHandlerMetrics handlerMetrics, Iterable tags) { + Id id = registry.createId("spring.integration.handler.duration.mean", tags, + "The mean handler duration"); + registry.more().timeGauge(id, handlerMetrics, TimeUnit.MILLISECONDS, + MessageHandlerMetrics::getMeanDuration); + } + + private void addActiveCount(MeterRegistry registry, + MessageHandlerMetrics handlerMetrics, Iterable tags) { + Id id = registry.createId("spring.integration.handler.activeCount", tags, + "The number of active handlers"); + registry.gauge(id, handlerMetrics, MessageHandlerMetrics::getActiveCount); + } + + private void addChannelMetrics(MeterRegistry registry) { + for (String channel : this.configurer.getChannelNames()) { + addChannelMetrics(registry, channel); + } + } + + private void addChannelMetrics(MeterRegistry registry, String channel) { + Iterable tags = Tags.concat(this.tags, "channel", channel); + MessageChannelMetrics channelMetrics = this.configurer.getChannelMetrics(channel); + addSendErrors(registry, tags, channelMetrics); + addChannelSends(registry, tags, channelMetrics); + if (channelMetrics instanceof PollableChannelManagement) { + addReceives(registry, tags, channelMetrics); + } + } + + private void addSendErrors(MeterRegistry registry, Iterable tags, + MessageChannelMetrics channelMetrics) { + Id id = registry.createId("spring.integration.channel.sendErrors", tags, + "The number of failed sends (either throwing an exception or " + + "rejected by the channel)"); + registry.more().counter(id, channelMetrics, + MessageChannelMetrics::getSendErrorCount); + } + + private void addReceives(MeterRegistry registry, Iterable tags, + MessageChannelMetrics channelMetrics) { + Id id = registry.createId("spring.integration.channel.receives", tags, + "The number of messages received"); + registry.more().counter(id, (PollableChannelManagement) channelMetrics, + PollableChannelManagement::getReceiveCount); + } + + private void addChannelSends(MeterRegistry registry, Iterable tags, + MessageChannelMetrics channelMetrics) { + Id id = registry.createId("spring.integration.channel.sends", tags, + "The number of successful sends"); + registry.more().counter(id, channelMetrics, MessageChannelMetrics::getSendCount); + } + + @Override + public void afterSingletonsInstantiated() { + // FIXME better would be to use a BeanPostProcessor + this.configurer.afterSingletonsInstantiated(); + this.registries.forEach((registry) -> { + addChannelMetrics(registry); + addHandlerMetrics(registry); + addSourceMetrics(registry); + }); + } + +} diff --git a/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/metrics/integration/package-info.java b/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/metrics/integration/package-info.java deleted file mode 100644 index bfa7a446fe0f..000000000000 --- a/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/metrics/integration/package-info.java +++ /dev/null @@ -1,20 +0,0 @@ -/* - * 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. - */ - -/** - * Spring Integration metrics support. - */ -package org.springframework.boot.actuate.metrics.integration; diff --git a/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/metrics/jdbc/DataSourcePoolMetrics.java b/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/metrics/jdbc/DataSourcePoolMetrics.java new file mode 100644 index 000000000000..f958006b528d --- /dev/null +++ b/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/metrics/jdbc/DataSourcePoolMetrics.java @@ -0,0 +1,122 @@ +/* + * 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.metrics.jdbc; + +import java.util.Collection; +import java.util.Map; +import java.util.function.Function; + +import javax.sql.DataSource; + +import io.micrometer.core.instrument.MeterRegistry; +import io.micrometer.core.instrument.Tag; +import io.micrometer.core.instrument.binder.MeterBinder; + +import org.springframework.boot.jdbc.metadata.CompositeDataSourcePoolMetadataProvider; +import org.springframework.boot.jdbc.metadata.DataSourcePoolMetadata; +import org.springframework.boot.jdbc.metadata.DataSourcePoolMetadataProvider; +import org.springframework.util.Assert; +import org.springframework.util.ConcurrentReferenceHashMap; + +/** + * A {@link MeterBinder} for a {@link DataSource}. + * + * @author Jon Schneider + * @author Phillip Webb + * @since 2.0.0 + */ +public class DataSourcePoolMetrics implements MeterBinder { + + private final DataSource dataSource; + + private final CachingDataSourcePoolMetadataProvider metadataProvider; + + private final String name; + + private final Iterable tags; + + public DataSourcePoolMetrics(DataSource dataSource, + Collection metadataProviders, String name, + Iterable tags) { + this(dataSource, new CompositeDataSourcePoolMetadataProvider(metadataProviders), + name, tags); + } + + public DataSourcePoolMetrics(DataSource dataSource, + DataSourcePoolMetadataProvider metadataProvider, String name, + Iterable tags) { + Assert.notNull(dataSource, "DataSource must not be null"); + Assert.notNull(metadataProvider, "MetadataProvider must not be null"); + this.dataSource = dataSource; + this.metadataProvider = new CachingDataSourcePoolMetadataProvider(dataSource, + metadataProvider); + this.name = name; + this.tags = tags; + } + + @Override + public void bindTo(MeterRegistry registry) { + if (this.metadataProvider.getDataSourcePoolMetadata(this.dataSource) != null) { + bindPoolMetadata(registry, "active", DataSourcePoolMetadata::getActive); + bindPoolMetadata(registry, "max", DataSourcePoolMetadata::getMax); + bindPoolMetadata(registry, "min", DataSourcePoolMetadata::getMin); + } + } + + private void bindPoolMetadata(MeterRegistry registry, String name, + Function function) { + bindDataSource(registry, name, this.metadataProvider.getValueFunction(function)); + } + + private void bindDataSource(MeterRegistry registry, String name, + Function function) { + if (function.apply(this.dataSource) != null) { + registry.gauge(this.name + "." + name + ".connections", this.tags, + this.dataSource, (m) -> function.apply(m).doubleValue()); + } + } + + private static class CachingDataSourcePoolMetadataProvider + implements DataSourcePoolMetadataProvider { + + private static final Map cache = new ConcurrentReferenceHashMap<>(); + + private final DataSourcePoolMetadataProvider metadataProvider; + + CachingDataSourcePoolMetadataProvider(DataSource dataSource, + DataSourcePoolMetadataProvider metadataProvider) { + this.metadataProvider = metadataProvider; + } + + public Function getValueFunction( + Function function) { + return (dataSource) -> function.apply(getDataSourcePoolMetadata(dataSource)); + } + + @Override + public DataSourcePoolMetadata getDataSourcePoolMetadata(DataSource dataSource) { + DataSourcePoolMetadata metadata = cache.get(dataSource); + if (metadata == null) { + metadata = this.metadataProvider.getDataSourcePoolMetadata(dataSource); + cache.put(dataSource, metadata); + } + return metadata; + } + + } + +} diff --git a/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/metrics/jmx/DefaultMetricNamingStrategy.java b/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/metrics/jmx/DefaultMetricNamingStrategy.java deleted file mode 100644 index 45ddd1e36d55..000000000000 --- a/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/metrics/jmx/DefaultMetricNamingStrategy.java +++ /dev/null @@ -1,65 +0,0 @@ -/* - * 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.metrics.jmx; - -import java.util.Hashtable; - -import javax.management.MalformedObjectNameException; -import javax.management.ObjectName; - -import org.springframework.jmx.export.naming.KeyNamingStrategy; -import org.springframework.jmx.export.naming.ObjectNamingStrategy; -import org.springframework.util.StringUtils; - -/** - * MBean naming strategy for metric keys. A metric name of {@code counter.foo.bar.spam} - * translates to an object name with {@code type=counter}, {@code name=foo} and - * {@code value=bar.spam}. This results in a more or less pleasing view with no tweaks in - * jconsole or jvisualvm. The domain is copied from the input key and the type in the - * input key is discarded. - * - * @author Dave Syer - * @since 1.3.0 - */ -public class DefaultMetricNamingStrategy implements ObjectNamingStrategy { - - private ObjectNamingStrategy namingStrategy = new KeyNamingStrategy(); - - @Override - public ObjectName getObjectName(Object managedBean, String beanKey) - throws MalformedObjectNameException { - ObjectName objectName = this.namingStrategy.getObjectName(managedBean, beanKey); - String domain = objectName.getDomain(); - Hashtable table = new Hashtable<>( - objectName.getKeyPropertyList()); - String name = objectName.getKeyProperty("name"); - if (name != null) { - table.remove("name"); - String[] parts = StringUtils.delimitedListToStringArray(name, "."); - table.put("type", parts[0]); - if (parts.length > 1) { - table.put(parts.length > 2 ? "name" : "value", parts[1]); - } - if (parts.length > 2) { - table.put("value", - name.substring(parts[0].length() + parts[1].length() + 2)); - } - } - return new ObjectName(domain, table); - } - -} diff --git a/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/metrics/jmx/JmxMetricWriter.java b/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/metrics/jmx/JmxMetricWriter.java deleted file mode 100644 index 9a0f271ea953..000000000000 --- a/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/metrics/jmx/JmxMetricWriter.java +++ /dev/null @@ -1,169 +0,0 @@ -/* - * 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.metrics.jmx; - -import java.util.Date; -import java.util.concurrent.ConcurrentHashMap; -import java.util.concurrent.ConcurrentMap; - -import javax.management.MalformedObjectNameException; -import javax.management.ObjectName; - -import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; - -import org.springframework.boot.actuate.metrics.Metric; -import org.springframework.boot.actuate.metrics.writer.Delta; -import org.springframework.boot.actuate.metrics.writer.MetricWriter; -import org.springframework.jmx.export.MBeanExporter; -import org.springframework.jmx.export.annotation.ManagedAttribute; -import org.springframework.jmx.export.annotation.ManagedOperation; -import org.springframework.jmx.export.annotation.ManagedResource; -import org.springframework.jmx.export.naming.ObjectNamingStrategy; - -/** - * A {@link MetricWriter} for MBeans. Each metric is registered as an individual MBean, so - * (for instance) it can be graphed and monitored. The object names are provided by an - * {@link ObjectNamingStrategy}, where the default is a - * {@link DefaultMetricNamingStrategy} which provides {@code type}, {@code name} and - * {@code value} keys by splitting up the metric name on periods. - * - * @author Dave Syer - * @since 1.3.0 - */ -@ManagedResource(description = "MetricWriter for pushing metrics to JMX MBeans.") -public class JmxMetricWriter implements MetricWriter { - - private static final Log logger = LogFactory.getLog(JmxMetricWriter.class); - - private final ConcurrentMap values = new ConcurrentHashMap<>(); - - private final MBeanExporter exporter; - - private ObjectNamingStrategy namingStrategy = new DefaultMetricNamingStrategy(); - - private String domain = "org.springframework.metrics"; - - public JmxMetricWriter(MBeanExporter exporter) { - this.exporter = exporter; - } - - public void setNamingStrategy(ObjectNamingStrategy namingStrategy) { - this.namingStrategy = namingStrategy; - } - - public void setDomain(String domain) { - this.domain = domain; - } - - @ManagedOperation - public void increment(String name, long value) { - increment(new Delta<>(name, value)); - } - - @Override - public void increment(Delta delta) { - MetricValue counter = getValue(delta.getName()); - counter.increment(delta.getValue().longValue()); - } - - @ManagedOperation - public void set(String name, double value) { - set(new Metric<>(name, value)); - } - - @Override - public void set(Metric value) { - MetricValue metric = getValue(value.getName()); - metric.setValue(value.getValue().doubleValue()); - } - - @Override - @ManagedOperation - public void reset(String name) { - MetricValue value = this.values.remove(name); - if (value != null) { - try { - // We can unregister the MBean, but if this writer is on the end of an - // Exporter the chances are it will be re-registered almost immediately. - this.exporter.unregisterManagedResource(getName(name, value)); - } - catch (MalformedObjectNameException ex) { - logger.warn("Could not unregister MBean for " + name); - } - } - } - - private MetricValue getValue(String name) { - MetricValue value = this.values.get(name); - if (value == null) { - value = new MetricValue(); - MetricValue oldValue = this.values.putIfAbsent(name, value); - if (oldValue != null) { - value = oldValue; - } - try { - this.exporter.registerManagedResource(value, getName(name, value)); - } - catch (Exception ex) { - // Could not register mbean, maybe just a race condition - } - } - return value; - } - - private ObjectName getName(String name, MetricValue value) - throws MalformedObjectNameException { - String key = String.format(this.domain + ":type=MetricValue,name=%s", name); - return this.namingStrategy.getObjectName(value, key); - } - - /** - * A single metric value. - */ - @ManagedResource - public static class MetricValue { - - private double value; - - private long lastUpdated = 0; - - public void setValue(double value) { - if (this.value != value) { - this.lastUpdated = System.currentTimeMillis(); - } - this.value = value; - } - - public void increment(long value) { - this.lastUpdated = System.currentTimeMillis(); - this.value += value; - } - - @ManagedAttribute - public double getValue() { - return this.value; - } - - @ManagedAttribute - public Date getLastUpdated() { - return new Date(this.lastUpdated); - } - - } - -} diff --git a/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/metrics/jmx/package-info.java b/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/metrics/jmx/package-info.java deleted file mode 100644 index af557de79930..000000000000 --- a/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/metrics/jmx/package-info.java +++ /dev/null @@ -1,20 +0,0 @@ -/* - * Copyright 2012-2015 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -/** - * Metrics integration with JMX. - */ -package org.springframework.boot.actuate.metrics.jmx; diff --git a/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/metrics/opentsdb/DefaultOpenTsdbNamingStrategy.java b/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/metrics/opentsdb/DefaultOpenTsdbNamingStrategy.java deleted file mode 100644 index abdd76fc99a8..000000000000 --- a/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/metrics/opentsdb/DefaultOpenTsdbNamingStrategy.java +++ /dev/null @@ -1,79 +0,0 @@ -/* - * 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.metrics.opentsdb; - -import java.util.HashMap; -import java.util.LinkedHashMap; -import java.util.Map; - -import org.springframework.util.ObjectUtils; - -/** - * A naming strategy that just passes through the metric name, together with tags from a - * set of static values. Open TSDB requires at least one tag, so tags are always added for - * you: the {@value #DOMAIN_KEY} key is added with a value "spring", and the - * {@value #PROCESS_KEY} key is added with a value equal to the object hash of "this" (the - * naming strategy). The "domain" value is a system identifier - it would be common to all - * processes in the same distributed system. In most cases this will be unique enough to - * allow aggregation of the underlying metrics in Open TSDB, but normally it is best to - * provide your own tags, including a prefix and process identifier if you know one - * (overwriting the default). - * - * @author Dave Syer - * @since 1.3.0 - */ -public class DefaultOpenTsdbNamingStrategy implements OpenTsdbNamingStrategy { - - /** - * The domain key. - */ - public static final String DOMAIN_KEY = "domain"; - - /** - * The process key. - */ - public static final String PROCESS_KEY = "process"; - - /** - * Tags to apply to every metric. Open TSDB requires at least one tag, so a "prefix" - * tag is added for you by default. - */ - private Map tags = new LinkedHashMap<>(); - - private Map cache = new HashMap<>(); - - public DefaultOpenTsdbNamingStrategy() { - this.tags.put(DOMAIN_KEY, "org.springframework.metrics"); - this.tags.put(PROCESS_KEY, ObjectUtils.getIdentityHexString(this)); - } - - public void setTags(Map staticTags) { - this.tags.putAll(staticTags); - } - - @Override - public OpenTsdbName getName(String name) { - if (this.cache.containsKey(name)) { - return this.cache.get(name); - } - OpenTsdbName value = new OpenTsdbName(name); - value.setTags(this.tags); - this.cache.put(name, value); - return value; - } - -} diff --git a/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/metrics/opentsdb/OpenTsdbData.java b/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/metrics/opentsdb/OpenTsdbData.java deleted file mode 100644 index df216ab96548..000000000000 --- a/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/metrics/opentsdb/OpenTsdbData.java +++ /dev/null @@ -1,85 +0,0 @@ -/* - * Copyright 2012-2015 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.boot.actuate.metrics.opentsdb; - -import java.util.Map; - -/** - * OpenTSDB Data. - * - * @author Dave Syer - * @since 1.3.0 - */ -public class OpenTsdbData { - - private OpenTsdbName name; - - private Long timestamp; - - private Number value; - - protected OpenTsdbData() { - this.name = new OpenTsdbName(); - } - - public OpenTsdbData(String metric, Number value) { - this(metric, value, System.currentTimeMillis()); - } - - public OpenTsdbData(String metric, Number value, Long timestamp) { - this(new OpenTsdbName(metric), value, timestamp); - } - - public OpenTsdbData(OpenTsdbName name, Number value, Long timestamp) { - this.name = name; - this.value = value; - this.timestamp = timestamp; - } - - public String getMetric() { - return this.name.getMetric(); - } - - public void setMetric(String metric) { - this.name.setMetric(metric); - } - - public Long getTimestamp() { - return this.timestamp; - } - - public void setTimestamp(Long timestamp) { - this.timestamp = timestamp; - } - - public Number getValue() { - return this.value; - } - - public void setValue(Number value) { - this.value = value; - } - - public Map getTags() { - return this.name.getTags(); - } - - public void setTags(Map tags) { - this.name.setTags(tags); - } - -} diff --git a/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/metrics/opentsdb/OpenTsdbGaugeWriter.java b/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/metrics/opentsdb/OpenTsdbGaugeWriter.java deleted file mode 100644 index 576ffa20f5a3..000000000000 --- a/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/metrics/opentsdb/OpenTsdbGaugeWriter.java +++ /dev/null @@ -1,169 +0,0 @@ -/* - * 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.metrics.opentsdb; - -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collections; -import java.util.List; -import java.util.Map; - -import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; - -import org.springframework.boot.actuate.metrics.Metric; -import org.springframework.boot.actuate.metrics.writer.GaugeWriter; -import org.springframework.http.HttpEntity; -import org.springframework.http.HttpHeaders; -import org.springframework.http.MediaType; -import org.springframework.http.ResponseEntity; -import org.springframework.http.client.SimpleClientHttpRequestFactory; -import org.springframework.scheduling.annotation.Scheduled; -import org.springframework.web.client.RestOperations; -import org.springframework.web.client.RestTemplate; - -/** - * A {@link GaugeWriter} for the Open TSDB database (version 2.0), writing metrics to the - * HTTP endpoint provided by the server. Data are buffered according to the - * {@link #setBufferSize(int) bufferSize} property, and only flushed automatically when - * the buffer size is reached. Users should either manually {@link #flush()} after writing - * a batch of data if that makes sense, or consider adding a {@link Scheduled Scheduled} - * task to flush periodically. - * - * @author Dave Syer - * @author Thomas Badie - * @since 1.3.0 - */ -public class OpenTsdbGaugeWriter implements GaugeWriter { - - private static final int DEFAULT_CONNECT_TIMEOUT = 10000; - - private static final int DEFAULT_READ_TIMEOUT = 30000; - - private static final Log logger = LogFactory.getLog(OpenTsdbGaugeWriter.class); - - private RestOperations restTemplate; - - /** - * URL for POSTing data. Defaults to http://localhost:4242/api/put. - */ - private String url = "http://localhost:4242/api/put"; - - /** - * Buffer size to fill before posting data to server. - */ - private int bufferSize = 64; - - /** - * The media type to use to serialize and accept responses from the server. Defaults - * to "application/json". - */ - private MediaType mediaType = MediaType.APPLICATION_JSON; - - private final List buffer = new ArrayList<>(this.bufferSize); - - private OpenTsdbNamingStrategy namingStrategy = new DefaultOpenTsdbNamingStrategy(); - - /** - * Creates a new {@code OpenTsdbGaugeWriter} with the default connect (10 seconds) and - * read (30 seconds) timeouts. - */ - public OpenTsdbGaugeWriter() { - this(DEFAULT_CONNECT_TIMEOUT, DEFAULT_READ_TIMEOUT); - } - - /** - * Creates a new {@code OpenTsdbGaugeWriter} with the given millisecond - * {@code connectTimeout} and {@code readTimeout}. - * @param connectTimeout the connect timeout in milliseconds - * @param readTimeout the read timeout in milliseconds - */ - public OpenTsdbGaugeWriter(int connectTimeout, int readTimeout) { - SimpleClientHttpRequestFactory requestFactory = new SimpleClientHttpRequestFactory(); - requestFactory.setConnectTimeout(connectTimeout); - requestFactory.setReadTimeout(readTimeout); - this.restTemplate = new RestTemplate(requestFactory); - } - - public RestOperations getRestTemplate() { - return this.restTemplate; - } - - public void setRestTemplate(RestOperations restTemplate) { - this.restTemplate = restTemplate; - } - - public void setUrl(String url) { - this.url = url; - } - - public void setBufferSize(int bufferSize) { - this.bufferSize = bufferSize; - } - - public void setMediaType(MediaType mediaType) { - this.mediaType = mediaType; - } - - public void setNamingStrategy(OpenTsdbNamingStrategy namingStrategy) { - this.namingStrategy = namingStrategy; - } - - @Override - public void set(Metric value) { - OpenTsdbData data = new OpenTsdbData(this.namingStrategy.getName(value.getName()), - value.getValue(), value.getTimestamp().getTime()); - synchronized (this.buffer) { - this.buffer.add(data); - if (this.buffer.size() >= this.bufferSize) { - flush(); - } - } - } - - /** - * Flush the buffer without waiting for it to fill any further. - */ - @SuppressWarnings("rawtypes") - public void flush() { - List snapshot = getBufferSnapshot(); - if (snapshot.isEmpty()) { - return; - } - HttpHeaders headers = new HttpHeaders(); - headers.setAccept(Arrays.asList(this.mediaType)); - headers.setContentType(this.mediaType); - ResponseEntity response = this.restTemplate.postForEntity(this.url, - new HttpEntity<>(snapshot, headers), Map.class); - if (!response.getStatusCode().is2xxSuccessful()) { - logger.warn("Cannot write metrics (discarded " + snapshot.size() - + " values): " + response.getBody()); - } - } - - private List getBufferSnapshot() { - synchronized (this.buffer) { - if (this.buffer.isEmpty()) { - return Collections.emptyList(); - } - List snapshot = new ArrayList<>(this.buffer); - this.buffer.clear(); - return snapshot; - } - } - -} diff --git a/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/metrics/opentsdb/OpenTsdbName.java b/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/metrics/opentsdb/OpenTsdbName.java deleted file mode 100644 index 682f69912f31..000000000000 --- a/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/metrics/opentsdb/OpenTsdbName.java +++ /dev/null @@ -1,61 +0,0 @@ -/* - * 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.metrics.opentsdb; - -import java.util.LinkedHashMap; -import java.util.Map; - -/** - * OpenTSDB Name. - * - * @author Dave Syer - * @since 1.3.0 - */ -public class OpenTsdbName { - - private String metric; - - private Map tags = new LinkedHashMap<>(); - - protected OpenTsdbName() { - } - - public OpenTsdbName(String metric) { - this.metric = metric; - } - - public String getMetric() { - return this.metric; - } - - public void setMetric(String metric) { - this.metric = metric; - } - - public Map getTags() { - return this.tags; - } - - public void setTags(Map tags) { - this.tags.putAll(tags); - } - - public void tag(String name, String value) { - this.tags.put(name, value); - } - -} diff --git a/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/metrics/opentsdb/OpenTsdbNamingStrategy.java b/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/metrics/opentsdb/OpenTsdbNamingStrategy.java deleted file mode 100644 index 82ab5e0a9fc8..000000000000 --- a/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/metrics/opentsdb/OpenTsdbNamingStrategy.java +++ /dev/null @@ -1,35 +0,0 @@ -/* - * 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.metrics.opentsdb; - -/** - * Strategy used to convert a metric name into an {@link OpenTsdbName}. - * - * @author Dave Syer - * @since 1.3.0 - */ -@FunctionalInterface -public interface OpenTsdbNamingStrategy { - - /** - * Convert the metric name into a {@link OpenTsdbName}. - * @param metricName the name of the metric - * @return an Open TSDB name - */ - OpenTsdbName getName(String metricName); - -} diff --git a/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/metrics/opentsdb/package-info.java b/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/metrics/opentsdb/package-info.java deleted file mode 100644 index 619a3f161c16..000000000000 --- a/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/metrics/opentsdb/package-info.java +++ /dev/null @@ -1,20 +0,0 @@ -/* - * Copyright 2012-2015 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -/** - * Metrics integration with OpenTSDB. - */ -package org.springframework.boot.actuate.metrics.opentsdb; diff --git a/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/metrics/package-info.java b/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/metrics/package-info.java deleted file mode 100644 index b220f18cd930..000000000000 --- a/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/metrics/package-info.java +++ /dev/null @@ -1,20 +0,0 @@ -/* - * Copyright 2012-2015 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -/** - * Core metrics classes. - */ -package org.springframework.boot.actuate.metrics; diff --git a/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/metrics/reader/CompositeMetricReader.java b/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/metrics/reader/CompositeMetricReader.java deleted file mode 100644 index 1a8cb8fa4f91..000000000000 --- a/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/metrics/reader/CompositeMetricReader.java +++ /dev/null @@ -1,71 +0,0 @@ -/* - * 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.metrics.reader; - -import java.util.ArrayList; -import java.util.Collections; -import java.util.List; - -import org.springframework.boot.actuate.metrics.Metric; - -/** - * Composite implementation of {@link MetricReader}. - * - * @author Dave Syer - */ -public class CompositeMetricReader implements MetricReader { - - private final List readers = new ArrayList<>(); - - public CompositeMetricReader(MetricReader... readers) { - Collections.addAll(this.readers, readers); - } - - @Override - public Metric findOne(String metricName) { - for (MetricReader delegate : this.readers) { - Metric value = delegate.findOne(metricName); - if (value != null) { - return value; - } - } - return null; - } - - @Override - public Iterable> findAll() { - List> values = new ArrayList<>((int) count()); - for (MetricReader delegate : this.readers) { - Iterable> all = delegate.findAll(); - for (Metric value : all) { - values.add(value); - } - } - return values; - } - - @Override - public long count() { - long count = 0; - for (MetricReader delegate : this.readers) { - count += delegate.count(); - - } - return count; - } - -} diff --git a/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/metrics/reader/MetricReader.java b/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/metrics/reader/MetricReader.java deleted file mode 100644 index e82eede79ecc..000000000000 --- a/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/metrics/reader/MetricReader.java +++ /dev/null @@ -1,48 +0,0 @@ -/* - * Copyright 2012-2013 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.boot.actuate.metrics.reader; - -import org.springframework.boot.actuate.metrics.Metric; - -/** - * A simple reader interface used to interrogate {@link Metric}s. - * - * @author Dave Syer - */ -public interface MetricReader { - - /** - * Find an instance of the metric with the given name (usually the latest recorded - * value). - * @param metricName the name of the metric to find - * @return a metric value or null if there are none with that name - */ - Metric findOne(String metricName); - - /** - * Find all the metrics known to this reader. - * @return all instances of metrics known to this reader - */ - Iterable> findAll(); - - /** - * The number of metrics known to this reader. - * @return the number of metrics - */ - long count(); - -} diff --git a/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/metrics/reader/MetricReaderPublicMetrics.java b/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/metrics/reader/MetricReaderPublicMetrics.java deleted file mode 100644 index 7013887110ff..000000000000 --- a/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/metrics/reader/MetricReaderPublicMetrics.java +++ /dev/null @@ -1,54 +0,0 @@ -/* - * 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.metrics.reader; - -import java.util.ArrayList; -import java.util.Collection; -import java.util.List; - -import org.springframework.boot.actuate.metrics.Metric; -import org.springframework.boot.actuate.metrics.PublicMetrics; -import org.springframework.util.Assert; - -/** - * {@link PublicMetrics} exposed from a {@link MetricReader}. - * - * @author Dave Syer - * @author Christian Dupuis - * @author Stephane Nicoll - * @author Phillip Webb - * @since 2.0.0 - */ -public class MetricReaderPublicMetrics implements PublicMetrics { - - private final MetricReader metricReader; - - public MetricReaderPublicMetrics(MetricReader metricReader) { - Assert.notNull(metricReader, "MetricReader must not be null"); - this.metricReader = metricReader; - } - - @Override - public Collection> metrics() { - List> result = new ArrayList<>(); - for (Metric metric : this.metricReader.findAll()) { - result.add(metric); - } - return result; - } - -} diff --git a/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/metrics/reader/MetricRegistryMetricReader.java b/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/metrics/reader/MetricRegistryMetricReader.java deleted file mode 100644 index 76a9da69883e..000000000000 --- a/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/metrics/reader/MetricRegistryMetricReader.java +++ /dev/null @@ -1,254 +0,0 @@ -/* - * 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.metrics.reader; - -import java.beans.PropertyDescriptor; -import java.util.HashSet; -import java.util.List; -import java.util.Map; -import java.util.Set; -import java.util.concurrent.ConcurrentHashMap; -import java.util.concurrent.TimeUnit; - -import com.codahale.metrics.Counter; -import com.codahale.metrics.Gauge; -import com.codahale.metrics.Histogram; -import com.codahale.metrics.Meter; -import com.codahale.metrics.MetricRegistry; -import com.codahale.metrics.MetricRegistryListener; -import com.codahale.metrics.Sampling; -import com.codahale.metrics.Timer; -import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; - -import org.springframework.beans.BeanUtils; -import org.springframework.beans.BeanWrapperImpl; -import org.springframework.boot.actuate.metrics.Metric; -import org.springframework.util.ClassUtils; -import org.springframework.util.LinkedMultiValueMap; -import org.springframework.util.MultiValueMap; -import org.springframework.util.StringUtils; - -/** - * A Spring Boot {@link MetricReader} that reads metrics from a Dropwizard - * {@link MetricRegistry}. Gauges and Counters are reflected as a single value. Timers, - * Meters and Histograms are expanded into sets of metrics containing all the properties - * of type Number. - * - * @author Dave Syer - * @author Andy Wilkinson - */ -public class MetricRegistryMetricReader implements MetricReader, MetricRegistryListener { - - private static final Log logger = LogFactory.getLog(MetricRegistryMetricReader.class); - - private static final Map, Set> numberKeys = new ConcurrentHashMap<>(); - - private final Object monitor = new Object(); - - private final Map names = new ConcurrentHashMap<>(); - - private final MultiValueMap reverse = new LinkedMultiValueMap<>(); - - private final MetricRegistry registry; - - public MetricRegistryMetricReader(MetricRegistry registry) { - this.registry = registry; - registry.addListener(this); - } - - @Override - public Metric findOne(String metricName) { - String name = this.names.get(metricName); - if (name == null) { - return null; - } - com.codahale.metrics.Metric metric = this.registry.getMetrics().get(name); - if (metric == null) { - return null; - } - if (metric instanceof Counter) { - Counter counter = (Counter) metric; - return new Metric(metricName, counter.getCount()); - } - if (metric instanceof Gauge) { - Object value = ((Gauge) metric).getValue(); - if (value instanceof Number) { - return new Metric<>(metricName, (Number) value); - } - if (logger.isDebugEnabled()) { - logger.debug("Ignoring gauge '" + name + "' (" + metric - + ") as its value is not a Number"); - } - return null; - } - if (metric instanceof Sampling) { - if (metricName.contains(".snapshot.")) { - Number value = getMetric(((Sampling) metric).getSnapshot(), metricName); - if (metric instanceof Timer) { - // convert back to MILLISEC - value = TimeUnit.MILLISECONDS.convert(value.longValue(), - TimeUnit.NANOSECONDS); - } - return new Metric<>(metricName, value); - } - } - return new Metric<>(metricName, getMetric(metric, metricName)); - } - - @Override - public Iterable> findAll() { - return () -> { - Set> metrics = new HashSet<>(); - for (String name : MetricRegistryMetricReader.this.names.keySet()) { - Metric metric = findOne(name); - if (metric != null) { - metrics.add(metric); - } - } - return metrics.iterator(); - }; - } - - @Override - public long count() { - return this.names.size(); - } - - @Override - public void onGaugeAdded(String name, Gauge gauge) { - this.names.put(name, name); - synchronized (this.monitor) { - this.reverse.add(name, name); - } - } - - @Override - public void onGaugeRemoved(String name) { - remove(name); - } - - @Override - public void onCounterAdded(String name, Counter counter) { - this.names.put(name, name); - synchronized (this.monitor) { - this.reverse.add(name, name); - } - } - - @Override - public void onCounterRemoved(String name) { - remove(name); - } - - @Override - public void onHistogramAdded(String name, Histogram histogram) { - for (String key : getNumberKeys(histogram)) { - String metricName = name + "." + key; - this.names.put(metricName, name); - synchronized (this.monitor) { - this.reverse.add(name, metricName); - } - } - for (String key : getNumberKeys(histogram.getSnapshot())) { - String metricName = name + ".snapshot." + key; - this.names.put(metricName, name); - synchronized (this.monitor) { - this.reverse.add(name, metricName); - } - } - } - - @Override - public void onHistogramRemoved(String name) { - remove(name); - } - - @Override - public void onMeterAdded(String name, Meter meter) { - for (String key : getNumberKeys(meter)) { - String metricName = name + "." + key; - this.names.put(metricName, name); - synchronized (this.monitor) { - this.reverse.add(name, metricName); - } - } - } - - @Override - public void onMeterRemoved(String name) { - remove(name); - } - - @Override - public void onTimerAdded(String name, Timer timer) { - for (String key : getNumberKeys(timer)) { - String metricName = name + "." + key; - this.names.put(metricName, name); - synchronized (this.monitor) { - this.reverse.add(name, metricName); - } - } - for (String key : getNumberKeys(timer.getSnapshot())) { - String metricName = name + ".snapshot." + key; - this.names.put(metricName, name); - synchronized (this.monitor) { - this.reverse.add(name, metricName); - } - } - } - - @Override - public void onTimerRemoved(String name) { - remove(name); - } - - private void remove(String name) { - List keys; - synchronized (this.monitor) { - keys = this.reverse.remove(name); - } - if (keys != null) { - for (String key : keys) { - this.names.remove(name + "." + key); - } - } - } - - private static Set getNumberKeys(Object metric) { - Set result = numberKeys.get(metric.getClass()); - if (result == null) { - result = new HashSet<>(); - } - if (result.isEmpty()) { - for (PropertyDescriptor descriptor : BeanUtils - .getPropertyDescriptors(metric.getClass())) { - if (ClassUtils.isAssignable(Number.class, descriptor.getPropertyType())) { - result.add(descriptor.getName()); - } - } - numberKeys.put(metric.getClass(), result); - } - return result; - } - - private static Number getMetric(Object metric, String metricName) { - String key = StringUtils.getFilenameExtension(metricName); - return (Number) new BeanWrapperImpl(metric).getPropertyValue(key); - } - -} diff --git a/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/metrics/reader/MetricsEndpointMetricReader.java b/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/metrics/reader/MetricsEndpointMetricReader.java deleted file mode 100644 index b9a3ffda99d3..000000000000 --- a/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/metrics/reader/MetricsEndpointMetricReader.java +++ /dev/null @@ -1,72 +0,0 @@ -/* - * 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.metrics.reader; - -import java.util.ArrayList; -import java.util.Date; -import java.util.List; -import java.util.Map; -import java.util.Map.Entry; - -import org.springframework.boot.actuate.metrics.Metric; -import org.springframework.boot.actuate.metrics.MetricsEndpoint; - -/** - * {@link MetricReader} that pulls all current values out of the {@link MetricsEndpoint}. - * No timestamp information is available, so there is no way to check if the values are - * recent, and they all come out with the default (current time). - * - * @author Dave Syer - * @since 2.0.0 - */ -public class MetricsEndpointMetricReader implements MetricReader { - - private final MetricsEndpoint endpoint; - - public MetricsEndpointMetricReader(MetricsEndpoint endpoint) { - this.endpoint = endpoint; - } - - @Override - public Metric findOne(String metricName) { - Metric metric = null; - Object value = this.endpoint.metrics(null).get(metricName); - if (value != null) { - metric = new Metric<>(metricName, (Number) value); - } - return metric; - } - - @Override - public Iterable> findAll() { - List> metrics = new ArrayList<>(); - Map values = this.endpoint.metrics(null); - Date timestamp = new Date(); - for (Entry entry : values.entrySet()) { - String name = entry.getKey(); - Object value = entry.getValue(); - metrics.add(new Metric<>(name, (Number) value, timestamp)); - } - return metrics; - } - - @Override - public long count() { - return this.endpoint.metrics(null).size(); - } - -} diff --git a/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/metrics/reader/PrefixMetricReader.java b/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/metrics/reader/PrefixMetricReader.java deleted file mode 100644 index 39c599bd9035..000000000000 --- a/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/metrics/reader/PrefixMetricReader.java +++ /dev/null @@ -1,36 +0,0 @@ -/* - * 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.metrics.reader; - -import org.springframework.boot.actuate.metrics.Metric; - -/** - * Interface for extracting metrics as a group whose name starts with a prefix. - * - * @author Dave Syer - */ -@FunctionalInterface -public interface PrefixMetricReader { - - /** - * Find all metrics whose name starts with the given prefix. - * @param prefix the prefix for metric names - * @return all metrics with names starting with the prefix - */ - Iterable> findAll(String prefix); - -} diff --git a/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/metrics/reader/package-info.java b/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/metrics/reader/package-info.java deleted file mode 100644 index 2c0a84ffd0b5..000000000000 --- a/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/metrics/reader/package-info.java +++ /dev/null @@ -1,22 +0,0 @@ -/* - * Copyright 2012-2015 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -/** - * Metrics readers. - * - * @see org.springframework.boot.actuate.metrics.reader.MetricReader - */ -package org.springframework.boot.actuate.metrics.reader; diff --git a/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/metrics/repository/InMemoryMetricRepository.java b/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/metrics/repository/InMemoryMetricRepository.java deleted file mode 100644 index 37f9e71a1532..000000000000 --- a/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/metrics/repository/InMemoryMetricRepository.java +++ /dev/null @@ -1,83 +0,0 @@ -/* - * 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.metrics.repository; - -import java.util.Date; -import java.util.concurrent.ConcurrentNavigableMap; - -import org.springframework.boot.actuate.metrics.Metric; -import org.springframework.boot.actuate.metrics.util.SimpleInMemoryRepository; -import org.springframework.boot.actuate.metrics.writer.Delta; - -/** - * {@link MetricRepository} implementation that stores metrics in memory. - * - * @author Dave Syer - * @author Stephane Nicoll - */ -public class InMemoryMetricRepository implements MetricRepository { - - private final SimpleInMemoryRepository> metrics = new SimpleInMemoryRepository<>(); - - public void setValues(ConcurrentNavigableMap> values) { - this.metrics.setValues(values); - } - - @Override - public void increment(Delta delta) { - final String metricName = delta.getName(); - final int amount = delta.getValue().intValue(); - final Date timestamp = delta.getTimestamp(); - this.metrics.update(metricName, (current) -> { - if (current != null) { - return new Metric<>(metricName, current.increment(amount).getValue(), - timestamp); - } - return new Metric<>(metricName, (long) amount, timestamp); - }); - } - - @Override - public void set(Metric value) { - this.metrics.set(value.getName(), value); - } - - @Override - public long count() { - return this.metrics.count(); - } - - @Override - public void reset(String metricName) { - this.metrics.remove(metricName); - } - - @Override - public Metric findOne(String metricName) { - return this.metrics.findOne(metricName); - } - - @Override - public Iterable> findAll() { - return this.metrics.findAll(); - } - - public Iterable> findAllWithPrefix(String prefix) { - return this.metrics.findAllWithPrefix(prefix); - } - -} diff --git a/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/metrics/repository/InMemoryMultiMetricRepository.java b/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/metrics/repository/InMemoryMultiMetricRepository.java deleted file mode 100644 index ed82b152d57d..000000000000 --- a/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/metrics/repository/InMemoryMultiMetricRepository.java +++ /dev/null @@ -1,111 +0,0 @@ -/* - * 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.metrics.repository; - -import java.util.Collection; -import java.util.Collections; -import java.util.HashSet; - -import org.springframework.boot.actuate.metrics.Metric; -import org.springframework.boot.actuate.metrics.writer.Delta; -import org.springframework.util.Assert; - -/** - * {@link MultiMetricRepository} implementation backed by a - * {@link InMemoryMetricRepository}. - * - * @author Stephane Nicoll - * @since 1.5.0 - */ -public class InMemoryMultiMetricRepository implements MultiMetricRepository { - - private final InMemoryMetricRepository repository; - - private final Collection groups = new HashSet<>(); - - /** - * Create a new {@link InMemoryMetricRepository} backed by a new - * {@link InMemoryMetricRepository}. - */ - public InMemoryMultiMetricRepository() { - this(new InMemoryMetricRepository()); - } - - /** - * Create a new {@link InMemoryMetricRepository} backed by the specified - * {@link InMemoryMetricRepository}. - * @param repository the backing repository - */ - public InMemoryMultiMetricRepository(InMemoryMetricRepository repository) { - Assert.notNull(repository, "Repository must not be null"); - this.repository = repository; - } - - @Override - public void set(String group, Collection> values) { - String prefix = group; - if (!prefix.endsWith(".")) { - prefix = prefix + "."; - } - for (Metric metric : values) { - if (!metric.getName().startsWith(prefix)) { - metric = new Metric(prefix + metric.getName(), metric.getValue(), - metric.getTimestamp()); - } - this.repository.set(metric); - } - this.groups.add(group); - } - - @Override - public void increment(String group, Delta delta) { - String prefix = group; - if (!prefix.endsWith(".")) { - prefix = prefix + "."; - } - if (!delta.getName().startsWith(prefix)) { - delta = new Delta(prefix + delta.getName(), delta.getValue(), - delta.getTimestamp()); - } - this.repository.increment(delta); - this.groups.add(group); - } - - @Override - public Iterable groups() { - return Collections.unmodifiableCollection(this.groups); - } - - @Override - public long countGroups() { - return this.groups.size(); - } - - @Override - public void reset(String group) { - for (Metric metric : findAll(group)) { - this.repository.reset(metric.getName()); - } - this.groups.remove(group); - } - - @Override - public Iterable> findAll(String metricNamePrefix) { - return this.repository.findAllWithPrefix(metricNamePrefix); - } - -} diff --git a/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/metrics/repository/MetricRepository.java b/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/metrics/repository/MetricRepository.java deleted file mode 100644 index ff69d6d712fe..000000000000 --- a/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/metrics/repository/MetricRepository.java +++ /dev/null @@ -1,29 +0,0 @@ -/* - * Copyright 2012-2013 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.boot.actuate.metrics.repository; - -import org.springframework.boot.actuate.metrics.reader.MetricReader; -import org.springframework.boot.actuate.metrics.writer.MetricWriter; - -/** - * Convenient combination of reader and writer concerns. - * - * @author Dave Syer - */ -public interface MetricRepository extends MetricReader, MetricWriter { - -} diff --git a/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/metrics/repository/MultiMetricRepository.java b/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/metrics/repository/MultiMetricRepository.java deleted file mode 100644 index c80a2576e066..000000000000 --- a/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/metrics/repository/MultiMetricRepository.java +++ /dev/null @@ -1,42 +0,0 @@ -/* - * Copyright 2012-2014 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.boot.actuate.metrics.repository; - -import org.springframework.boot.actuate.metrics.reader.PrefixMetricReader; -import org.springframework.boot.actuate.metrics.writer.PrefixMetricWriter; - -/** - * A repository for metrics that allows efficient storage and retrieval of groups of - * metrics with a common name prefix (their group name). - * - * @author Dave Syer - */ -public interface MultiMetricRepository extends PrefixMetricReader, PrefixMetricWriter { - - /** - * The names of all the groups known to this repository. - * @return all available group names - */ - Iterable groups(); - - /** - * The number of groups available in this repository. - * @return the number of groups - */ - long countGroups(); - -} diff --git a/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/metrics/repository/package-info.java b/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/metrics/repository/package-info.java deleted file mode 100644 index 57a4bdcbfd7e..000000000000 --- a/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/metrics/repository/package-info.java +++ /dev/null @@ -1,22 +0,0 @@ -/* - * Copyright 2012-2015 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -/** - * Metrics repository support. - * - * @see org.springframework.boot.actuate.metrics.repository.MetricRepository - */ -package org.springframework.boot.actuate.metrics.repository; diff --git a/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/metrics/repository/redis/RedisMetricRepository.java b/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/metrics/repository/redis/RedisMetricRepository.java deleted file mode 100644 index be77aec59a71..000000000000 --- a/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/metrics/repository/redis/RedisMetricRepository.java +++ /dev/null @@ -1,198 +0,0 @@ -/* - * 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.metrics.repository.redis; - -import java.util.ArrayList; -import java.util.Date; -import java.util.Iterator; -import java.util.List; -import java.util.Set; - -import org.springframework.boot.actuate.metrics.Metric; -import org.springframework.boot.actuate.metrics.repository.MetricRepository; -import org.springframework.boot.actuate.metrics.writer.Delta; -import org.springframework.data.redis.connection.RedisConnectionFactory; -import org.springframework.data.redis.core.BoundZSetOperations; -import org.springframework.data.redis.core.RedisOperations; -import org.springframework.util.Assert; - -/** - * A {@link MetricRepository} implementation for a redis backend. Metric values are stored - * as zset values plus a regular hash value for the timestamp, both against a key composed - * of the metric name prefixed with a constant (default "spring.metrics."). If you have - * multiple metrics repositories all point at the same instance of Redis, it may be useful - * to change the prefix to be unique (but not if you want them to contribute to the same - * metrics). - * - * @author Dave Syer - */ -public class RedisMetricRepository implements MetricRepository { - - private static final String DEFAULT_METRICS_PREFIX = "spring.metrics."; - - private static final String DEFAULT_KEY = "keys.spring.metrics"; - - private String prefix = DEFAULT_METRICS_PREFIX; - - private String key = DEFAULT_KEY; - - private BoundZSetOperations zSetOperations; - - private final RedisOperations redisOperations; - - /** - * Create a RedisMetricRepository with a default prefix to apply to all metric names. - * If multiple repositories share a redis instance they will feed into the same global - * metrics. - * @param redisConnectionFactory the redis connection factory - */ - public RedisMetricRepository(RedisConnectionFactory redisConnectionFactory) { - this(redisConnectionFactory, null); - } - - /** - * Create a RedisMetricRepository with a prefix to apply to all metric names (ideally - * unique to this repository or to a logical repository contributed to by multiple - * instances, where they all see the same values). Recommended constructor for general - * purpose use. - * @param redisConnectionFactory the redis connection factory - * @param prefix the prefix to set for all metrics keys - */ - public RedisMetricRepository(RedisConnectionFactory redisConnectionFactory, - String prefix) { - this(redisConnectionFactory, prefix, null); - } - - /** - * Allows user to set the prefix and key to use to store the index of other keys. The - * redis store will hold a zset under the key just so the metric names can be - * enumerated. Read operations, especially {@link #findAll()} and {@link #count()}, - * will only be accurate if the key is unique to the prefix of this repository. - * @param redisConnectionFactory the redis connection factory - * @param prefix the prefix to set for all metrics keys - * @param key the key to set - */ - public RedisMetricRepository(RedisConnectionFactory redisConnectionFactory, - String prefix, String key) { - if (prefix == null) { - prefix = DEFAULT_METRICS_PREFIX; - if (key == null) { - key = DEFAULT_KEY; - } - } - else if (key == null) { - key = "keys." + prefix; - } - Assert.notNull(redisConnectionFactory, "RedisConnectionFactory must not be null"); - this.redisOperations = RedisUtils.stringTemplate(redisConnectionFactory); - if (!prefix.endsWith(".")) { - prefix = prefix + "."; - } - this.prefix = prefix; - if (key.endsWith(".")) { - key = key.substring(0, key.length() - 1); - } - this.key = key; - this.zSetOperations = this.redisOperations.boundZSetOps(this.key); - } - - @Override - public Metric findOne(String metricName) { - String redisKey = keyFor(metricName); - String raw = this.redisOperations.opsForValue().get(redisKey); - return deserialize(redisKey, raw, this.zSetOperations.score(redisKey)); - } - - @Override - public Iterable> findAll() { - - // This set is sorted - Set keys = this.zSetOperations.range(0, -1); - Iterator keysIt = keys.iterator(); - - List> result = new ArrayList<>(keys.size()); - List values = this.redisOperations.opsForValue().multiGet(keys); - for (String v : values) { - String key = keysIt.next(); - Metric value = deserialize(key, v, this.zSetOperations.score(key)); - if (value != null) { - result.add(value); - } - } - return result; - - } - - @Override - public long count() { - return this.zSetOperations.size(); - } - - @Override - public void increment(Delta delta) { - String name = delta.getName(); - String key = keyFor(name); - trackMembership(key); - double value = this.zSetOperations.incrementScore(key, - delta.getValue().doubleValue()); - String raw = serialize(new Metric<>(name, value, delta.getTimestamp())); - this.redisOperations.opsForValue().set(key, raw); - } - - @Override - public void set(Metric value) { - String name = value.getName(); - String key = keyFor(name); - trackMembership(key); - this.zSetOperations.add(key, value.getValue().doubleValue()); - String raw = serialize(value); - this.redisOperations.opsForValue().set(key, raw); - } - - @Override - public void reset(String metricName) { - String key = keyFor(metricName); - if (this.zSetOperations.remove(key) == 1) { - this.redisOperations.delete(key); - } - } - - private Metric deserialize(String redisKey, String v, Double value) { - if (redisKey == null || v == null || !redisKey.startsWith(this.prefix)) { - return null; - } - Date timestamp = new Date(Long.valueOf(v)); - return new Metric<>(nameFor(redisKey), value, timestamp); - } - - private String serialize(Metric entity) { - return String.valueOf(entity.getTimestamp().getTime()); - } - - private String keyFor(String name) { - return this.prefix + name; - } - - private String nameFor(String redisKey) { - return redisKey.substring(this.prefix.length()); - } - - private void trackMembership(String redisKey) { - this.zSetOperations.incrementScore(redisKey, 0.0D); - } - -} diff --git a/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/metrics/repository/redis/RedisMultiMetricRepository.java b/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/metrics/repository/redis/RedisMultiMetricRepository.java deleted file mode 100644 index aeeadee27180..000000000000 --- a/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/metrics/repository/redis/RedisMultiMetricRepository.java +++ /dev/null @@ -1,169 +0,0 @@ -/* - * 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.metrics.repository.redis; - -import java.util.ArrayList; -import java.util.Collection; -import java.util.Date; -import java.util.Iterator; -import java.util.List; -import java.util.Set; - -import org.springframework.boot.actuate.metrics.Metric; -import org.springframework.boot.actuate.metrics.repository.MultiMetricRepository; -import org.springframework.boot.actuate.metrics.writer.Delta; -import org.springframework.data.redis.connection.RedisConnectionFactory; -import org.springframework.data.redis.core.BoundZSetOperations; -import org.springframework.data.redis.core.RedisOperations; -import org.springframework.util.Assert; - -/** - * {@link MultiMetricRepository} implementation backed by a redis store. Metric values are - * stored as zset values and the timestamps as regular values, both against a key composed - * of the group name prefixed with a constant prefix (default "spring.groups."). The group - * names are stored as a zset under "keys." + {@code [prefix]}. - * - * @author Dave Syer - */ -public class RedisMultiMetricRepository implements MultiMetricRepository { - - private static final String DEFAULT_METRICS_PREFIX = "spring.groups."; - - private final String prefix; - - private final String keys; - - private final BoundZSetOperations zSetOperations; - - private final RedisOperations redisOperations; - - public RedisMultiMetricRepository(RedisConnectionFactory redisConnectionFactory) { - this(redisConnectionFactory, DEFAULT_METRICS_PREFIX); - } - - public RedisMultiMetricRepository(RedisConnectionFactory redisConnectionFactory, - String prefix) { - Assert.notNull(redisConnectionFactory, "RedisConnectionFactory must not be null"); - this.redisOperations = RedisUtils.stringTemplate(redisConnectionFactory); - if (!prefix.endsWith(".")) { - prefix = prefix + "."; - } - this.prefix = prefix; - this.keys = "keys." + this.prefix.substring(0, prefix.length() - 1); - this.zSetOperations = this.redisOperations.boundZSetOps(this.keys); - } - - @Override - public Iterable> findAll(String group) { - - BoundZSetOperations zSetOperations = this.redisOperations - .boundZSetOps(keyFor(group)); - - Set keys = zSetOperations.range(0, -1); - Iterator keysIt = keys.iterator(); - - List> result = new ArrayList<>(keys.size()); - List values = this.redisOperations.opsForValue().multiGet(keys); - for (String v : values) { - String key = keysIt.next(); - result.add(deserialize(group, key, v, zSetOperations.score(key))); - } - return result; - - } - - @Override - public void set(String group, Collection> values) { - String groupKey = keyFor(group); - trackMembership(groupKey); - BoundZSetOperations zSetOperations = this.redisOperations - .boundZSetOps(groupKey); - for (Metric metric : values) { - String raw = serialize(metric); - String key = keyFor(metric.getName()); - zSetOperations.add(key, metric.getValue().doubleValue()); - this.redisOperations.opsForValue().set(key, raw); - } - } - - @Override - public void increment(String group, Delta delta) { - String groupKey = keyFor(group); - trackMembership(groupKey); - BoundZSetOperations zSetOperations = this.redisOperations - .boundZSetOps(groupKey); - String key = keyFor(delta.getName()); - double value = zSetOperations.incrementScore(key, delta.getValue().doubleValue()); - String raw = serialize( - new Metric<>(delta.getName(), value, delta.getTimestamp())); - this.redisOperations.opsForValue().set(key, raw); - } - - @Override - public Iterable groups() { - Set range = this.zSetOperations.range(0, -1); - Collection result = new ArrayList<>(); - for (String key : range) { - result.add(key.substring(this.prefix.length())); - } - return result; - } - - @Override - public long countGroups() { - return this.zSetOperations.size(); - } - - @Override - public void reset(String group) { - String groupKey = keyFor(group); - if (this.redisOperations.hasKey(groupKey)) { - BoundZSetOperations zSetOperations = this.redisOperations - .boundZSetOps(groupKey); - Set keys = zSetOperations.range(0, -1); - for (String key : keys) { - this.redisOperations.delete(key); - } - this.redisOperations.delete(groupKey); - } - this.zSetOperations.remove(groupKey); - } - - private Metric deserialize(String group, String redisKey, String v, Double value) { - Date timestamp = new Date(Long.valueOf(v)); - return new Metric<>(nameFor(redisKey), value, timestamp); - } - - private String serialize(Metric entity) { - return String.valueOf(entity.getTimestamp().getTime()); - } - - private String keyFor(String name) { - return this.prefix + name; - } - - private String nameFor(String redisKey) { - Assert.state(redisKey != null && redisKey.startsWith(this.prefix), - "Invalid key does not start with prefix: " + redisKey); - return redisKey.substring(this.prefix.length()); - } - - private void trackMembership(String redisKey) { - this.zSetOperations.incrementScore(redisKey, 0.0D); - } - -} diff --git a/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/metrics/repository/redis/RedisUtils.java b/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/metrics/repository/redis/RedisUtils.java deleted file mode 100644 index 44ddfe5518b4..000000000000 --- a/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/metrics/repository/redis/RedisUtils.java +++ /dev/null @@ -1,55 +0,0 @@ -/* - * 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.metrics.repository.redis; - -import org.springframework.data.redis.connection.RedisConnectionFactory; -import org.springframework.data.redis.core.RedisOperations; -import org.springframework.data.redis.core.RedisTemplate; -import org.springframework.data.redis.core.StringRedisTemplate; -import org.springframework.data.redis.serializer.GenericToStringSerializer; -import org.springframework.data.redis.serializer.StringRedisSerializer; - -/** - * General Utils for working with Redis. - * - * @author Luke Taylor - */ -final class RedisUtils { - - private RedisUtils() { - } - - static RedisTemplate createRedisTemplate( - RedisConnectionFactory connectionFactory, Class valueClass) { - RedisTemplate redisTemplate = new RedisTemplate<>(); - redisTemplate.setKeySerializer(new StringRedisSerializer()); - redisTemplate.setValueSerializer(new GenericToStringSerializer<>(valueClass)); - - // avoids proxy - redisTemplate.setExposeConnection(true); - - redisTemplate.setConnectionFactory(connectionFactory); - redisTemplate.afterPropertiesSet(); - return redisTemplate; - } - - static RedisOperations stringTemplate( - RedisConnectionFactory redisConnectionFactory) { - return new StringRedisTemplate(redisConnectionFactory); - } - -} diff --git a/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/metrics/repository/redis/package-info.java b/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/metrics/repository/redis/package-info.java deleted file mode 100644 index 2062da27fabd..000000000000 --- a/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/metrics/repository/redis/package-info.java +++ /dev/null @@ -1,22 +0,0 @@ -/* - * Copyright 2012-2015 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -/** - * Metrics Redis repository support. - * - * @see org.springframework.boot.actuate.metrics.repository.redis.RedisMetricRepository - */ -package org.springframework.boot.actuate.metrics.repository.redis; diff --git a/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/metrics/rich/InMemoryRichGaugeRepository.java b/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/metrics/rich/InMemoryRichGaugeRepository.java deleted file mode 100644 index 6ecc54eb10c1..000000000000 --- a/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/metrics/rich/InMemoryRichGaugeRepository.java +++ /dev/null @@ -1,82 +0,0 @@ -/* - * 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.metrics.rich; - -import org.springframework.boot.actuate.metrics.Metric; -import org.springframework.boot.actuate.metrics.util.SimpleInMemoryRepository; -import org.springframework.boot.actuate.metrics.writer.Delta; -import org.springframework.boot.actuate.metrics.writer.MetricWriter; - -/** - * In memory implementation of {@link MetricWriter} and {@link RichGaugeReader}. When you - * {@link MetricWriter#set(Metric) set} or {@link MetricWriter#increment(Delta) increment} - * a metric value it is used to update a {@link RichGauge}. Gauge values can then be read - * out using the reader operations. - * - * @author Dave Syer - * @author Andy Wilkinson - */ -public class InMemoryRichGaugeRepository implements RichGaugeRepository { - - private final SimpleInMemoryRepository repository = new SimpleInMemoryRepository<>(); - - @Override - public void increment(Delta delta) { - this.repository.update(delta.getName(), (current) -> { - double value = ((Number) delta.getValue()).doubleValue(); - if (current == null) { - return new RichGauge(delta.getName(), value); - } - current.set(current.getValue() + value); - return current; - }); - } - - @Override - public void set(Metric metric) { - final String name = metric.getName(); - final double value = metric.getValue().doubleValue(); - this.repository.update(name, (current) -> { - if (current == null) { - return new RichGauge(name, value); - } - current.set(value); - return current; - }); - } - - @Override - public void reset(String metricName) { - this.repository.remove(metricName); - } - - @Override - public RichGauge findOne(String metricName) { - return this.repository.findOne(metricName); - } - - @Override - public Iterable findAll() { - return this.repository.findAll(); - } - - @Override - public long count() { - return this.repository.count(); - } - -} diff --git a/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/metrics/rich/MultiMetricRichGaugeReader.java b/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/metrics/rich/MultiMetricRichGaugeReader.java deleted file mode 100644 index 66abf8b96e5b..000000000000 --- a/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/metrics/rich/MultiMetricRichGaugeReader.java +++ /dev/null @@ -1,88 +0,0 @@ -/* - * 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.metrics.rich; - -import java.util.ArrayList; -import java.util.List; - -import org.springframework.boot.actuate.metrics.Metric; -import org.springframework.boot.actuate.metrics.repository.MultiMetricRepository; - -/** - * A {@link RichGaugeReader} that works by reading metric values from a - * {@link MultiMetricRepository} where the group name is the RichGauge name. The format - * used matches that in the RichGaugeExporter, so this reader can be used on a store that - * has been populated using that exporter. - * - * @author Dave Syer - * @since 1.1.0 - */ -public class MultiMetricRichGaugeReader implements RichGaugeReader { - - private final MultiMetricRepository repository; - - public MultiMetricRichGaugeReader(MultiMetricRepository repository) { - this.repository = repository; - } - - @Override - public RichGauge findOne(String name) { - Iterable> metrics = this.repository.findAll(name); - double value = 0; - double average = 0.; - double alpha = -1.; - double min = 0.; - double max = 0.; - long count = 0; - for (Metric metric : metrics) { - if (metric.getName().endsWith(RichGauge.VAL)) { - value = metric.getValue().doubleValue(); - } - else if (metric.getName().endsWith(RichGauge.ALPHA)) { - alpha = metric.getValue().doubleValue(); - } - else if (metric.getName().endsWith(RichGauge.AVG)) { - average = metric.getValue().doubleValue(); - } - else if (metric.getName().endsWith(RichGauge.MIN)) { - min = metric.getValue().doubleValue(); - } - else if (metric.getName().endsWith(RichGauge.MAX)) { - max = metric.getValue().doubleValue(); - } - else if (metric.getName().endsWith(RichGauge.COUNT)) { - count = metric.getValue().longValue(); - } - } - return new RichGauge(name, value, alpha, average, max, min, count); - } - - @Override - public Iterable findAll() { - List result = new ArrayList<>(); - for (String name : this.repository.groups()) { - result.add(findOne(name)); - } - return result; - } - - @Override - public long count() { - return this.repository.countGroups(); - } - -} diff --git a/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/metrics/rich/RichGauge.java b/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/metrics/rich/RichGauge.java deleted file mode 100644 index 564f44dea762..000000000000 --- a/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/metrics/rich/RichGauge.java +++ /dev/null @@ -1,238 +0,0 @@ -/* - * Copyright 2012-2014 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.boot.actuate.metrics.rich; - -import org.springframework.util.Assert; - -/** - * A gauge which stores the maximum, minimum and average in addition to the current value. - *

- * The value of the average will depend on whether a weight ('alpha') is set for the - * gauge. If it is unset, the average will contain a simple arithmetic mean. If a weight - * is set, an exponential moving average will be calculated as defined in this - * NIST - * document. - * - * @author Luke Taylor - */ -public final class RichGauge { - - /** - * The suffix for count gauges. - */ - public static final String COUNT = ".count"; - - /** - * The suffix for max gauges. - */ - public static final String MAX = ".max"; - - /** - * The suffix for min gauges. - */ - public static final String MIN = ".min"; - - /** - * The suffix for average value gauges. - */ - public static final String AVG = ".avg"; - - /** - * The suffix for alpha gauges. - */ - public static final String ALPHA = ".alpha"; - - /** - * The suffix for value gauges. - */ - public static final String VAL = ".val"; - - private final String name; - - private double value; - - private double average; - - private double max; - - private double min; - - private long count; - - private double alpha; - - /** - * Creates an "empty" gauge. The average, max and min will be zero, but this initial - * value will not be included after the first value has been set on the gauge. - * @param name the name under which the gauge will be stored. - */ - public RichGauge(String name) { - this(name, 0.0); - this.count = 0; - } - - public RichGauge(String name, double value) { - Assert.notNull(name, "The gauge name cannot be null or empty"); - this.name = name; - this.value = value; - this.average = this.value; - this.min = this.value; - this.max = this.value; - this.alpha = -1.0; - this.count = 1; - } - - public RichGauge(String name, double value, double alpha, double mean, double max, - double min, long count) { - this.name = name; - this.value = value; - this.alpha = alpha; - this.average = mean; - this.max = max; - this.min = min; - this.count = count; - } - - /** - * Return the name of the gauge. - * @return the name - */ - public String getName() { - return this.name; - } - - /** - * Return the current value of the gauge. - * @return the value - */ - public double getValue() { - return this.value; - } - - /** - * Return either an exponential weighted moving average or a simple mean, - * respectively, depending on whether the weight 'alpha' has been set for this gauge. - * @return The average over all the accumulated values - */ - public double getAverage() { - return this.average; - } - - /** - * Return the maximum value of the gauge. - * @return the maximum value - */ - public double getMax() { - return this.max; - } - - /** - * Return the minimum value of the gauge. - * @return the minimum value - */ - public double getMin() { - return this.min; - } - - /** - * Return the number of times the value has been set. - * @return the value set count - */ - public long getCount() { - return this.count; - } - - /** - * Return the smoothing constant value. - * @return the alpha smoothing value - */ - public double getAlpha() { - return this.alpha; - } - - public RichGauge setAlpha(double alpha) { - Assert.isTrue(alpha == -1 || (alpha > 0.0 && alpha < 1.0), - "Smoothing constant must be between 0 and 1, or -1 to use arithmetic mean"); - this.alpha = alpha; - return this; - } - - RichGauge set(double value) { - if (this.count == 0) { - this.max = value; - this.min = value; - } - else if (value > this.max) { - this.max = value; - } - else if (value < this.min) { - this.min = value; - } - - if (this.alpha > 0.0 && this.count > 0) { - this.average = this.alpha * this.value + (1 - this.alpha) * this.average; - } - else { - double sum = this.average * this.count; - sum += value; - this.average = sum / (this.count + 1); - } - this.count++; - this.value = value; - return this; - } - - RichGauge reset() { - this.value = 0.0; - this.max = 0.0; - this.min = 0.0; - this.average = 0.0; - this.count = 0; - return this; - } - - @Override - public boolean equals(Object o) { - if (this == o) { - return true; - } - if (o == null || getClass() != o.getClass()) { - return false; - } - - RichGauge richGauge = (RichGauge) o; - - if (!this.name.equals(richGauge.name)) { - return false; - } - - return true; - } - - @Override - public int hashCode() { - return this.name.hashCode(); - } - - @Override - public String toString() { - return "Gauge [name = " + this.name + ", value = " + this.value + ", alpha = " - + this.alpha + ", average = " + this.average + ", max = " + this.max - + ", min = " + this.min + ", count = " + this.count + "]"; - } - -} diff --git a/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/metrics/rich/RichGaugeReader.java b/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/metrics/rich/RichGaugeReader.java deleted file mode 100644 index 34d608b759b0..000000000000 --- a/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/metrics/rich/RichGaugeReader.java +++ /dev/null @@ -1,45 +0,0 @@ -/* - * Copyright 2012-2013 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.boot.actuate.metrics.rich; - -/** - * A basic set of read operations for {@link RichGauge} instances. - * - * @author Dave Syer - */ -public interface RichGaugeReader { - - /** - * Find a single instance of a rich gauge by name. - * @param name the name of the gauge - * @return a rich gauge value - */ - RichGauge findOne(String name); - - /** - * Find all instances of rich gauge known to this reader. - * @return all instances known to this reader - */ - Iterable findAll(); - - /** - * Return the number of gauge values available. - * @return the number of values - */ - long count(); - -} diff --git a/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/metrics/rich/RichGaugeReaderPublicMetrics.java b/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/metrics/rich/RichGaugeReaderPublicMetrics.java deleted file mode 100644 index 54fef7fdc920..000000000000 --- a/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/metrics/rich/RichGaugeReaderPublicMetrics.java +++ /dev/null @@ -1,62 +0,0 @@ -/* - * 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.metrics.rich; - -import java.util.ArrayList; -import java.util.Collection; -import java.util.List; - -import org.springframework.boot.actuate.metrics.Metric; -import org.springframework.boot.actuate.metrics.PublicMetrics; -import org.springframework.util.Assert; - -/** - * {@link PublicMetrics} exposed from a {@link RichGaugeReader}. - * - * @author Johannes Edmeier - * @since 2.0.0 - */ -public class RichGaugeReaderPublicMetrics implements PublicMetrics { - - private final RichGaugeReader richGaugeReader; - - public RichGaugeReaderPublicMetrics(RichGaugeReader richGaugeReader) { - Assert.notNull(richGaugeReader, "RichGaugeReader must not be null"); - this.richGaugeReader = richGaugeReader; - } - - @Override - public Collection> metrics() { - List> result = new ArrayList<>(); - for (RichGauge richGauge : this.richGaugeReader.findAll()) { - result.addAll(convert(richGauge)); - } - return result; - } - - private List> convert(RichGauge gauge) { - List> result = new ArrayList<>(6); - result.add(new Metric<>(gauge.getName() + RichGauge.AVG, gauge.getAverage())); - result.add(new Metric<>(gauge.getName() + RichGauge.VAL, gauge.getValue())); - result.add(new Metric<>(gauge.getName() + RichGauge.MIN, gauge.getMin())); - result.add(new Metric<>(gauge.getName() + RichGauge.MAX, gauge.getMax())); - result.add(new Metric<>(gauge.getName() + RichGauge.ALPHA, gauge.getAlpha())); - result.add(new Metric<>(gauge.getName() + RichGauge.COUNT, gauge.getCount())); - return result; - } - -} diff --git a/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/metrics/rich/RichGaugeRepository.java b/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/metrics/rich/RichGaugeRepository.java deleted file mode 100644 index 00da0108e470..000000000000 --- a/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/metrics/rich/RichGaugeRepository.java +++ /dev/null @@ -1,28 +0,0 @@ -/* - * Copyright 2012-2013 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.boot.actuate.metrics.rich; - -import org.springframework.boot.actuate.metrics.writer.MetricWriter; - -/** - * Convenient combination of reader and writer concerns for {@link RichGauge} instances. - * - * @author Dave Syer - */ -public interface RichGaugeRepository extends RichGaugeReader, MetricWriter { - -} diff --git a/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/metrics/rich/package-info.java b/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/metrics/rich/package-info.java deleted file mode 100644 index 85e92b4db21e..000000000000 --- a/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/metrics/rich/package-info.java +++ /dev/null @@ -1,22 +0,0 @@ -/* - * Copyright 2012-2015 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -/** - * Metrics rich gauge support. - * - * @see org.springframework.boot.actuate.metrics.rich.RichGauge - */ -package org.springframework.boot.actuate.metrics.rich; diff --git a/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/metrics/statsd/StatsdMetricWriter.java b/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/metrics/statsd/StatsdMetricWriter.java deleted file mode 100644 index d4cc7703bde1..000000000000 --- a/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/metrics/statsd/StatsdMetricWriter.java +++ /dev/null @@ -1,142 +0,0 @@ -/* - * 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.metrics.statsd; - -import java.io.Closeable; - -import com.timgroup.statsd.NonBlockingStatsDClient; -import com.timgroup.statsd.StatsDClient; -import com.timgroup.statsd.StatsDClientErrorHandler; -import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; - -import org.springframework.boot.actuate.metrics.Metric; -import org.springframework.boot.actuate.metrics.writer.Delta; -import org.springframework.boot.actuate.metrics.writer.MetricWriter; -import org.springframework.util.Assert; -import org.springframework.util.StringUtils; - -/** - * A {@link MetricWriter} that pushes data to statsd. Statsd has the concept of counters - * and gauges, but only supports gauges with data type Long, so values will be truncated - * towards zero. Metrics whose name contains "timer." (but not "gauge." or "counter.") - * will be treated as execution times (in statsd terms). Anything incremented is treated - * as a counter, and anything with a snapshot value in {@link #set(Metric)} is treated as - * a gauge. - * - * @author Dave Syer - * @author OdĂ­n del RĂ­o - * @since 1.3.0 - */ -public class StatsdMetricWriter implements MetricWriter, Closeable { - - private static final Log logger = LogFactory.getLog(StatsdMetricWriter.class); - - private final StatsDClient client; - - /** - * Create a new writer instance with the given parameters. - * @param host the hostname for the statsd server - * @param port the port for the statsd server - */ - public StatsdMetricWriter(String host, int port) { - this(null, host, port); - } - - /** - * Create a new writer with the given parameters. - * @param prefix the prefix to apply to all metric names (can be null) - * @param host the hostname for the statsd server - * @param port the port for the statsd server - */ - public StatsdMetricWriter(String prefix, String host, int port) { - this(new NonBlockingStatsDClient(trimPrefix(prefix), host, port, - new LoggingStatsdErrorHandler())); - } - - /** - * Create a new writer with the given client. - * @param client StatsD client to write metrics with - */ - public StatsdMetricWriter(StatsDClient client) { - Assert.notNull(client, "Client must not be null"); - this.client = client; - } - - private static String trimPrefix(String prefix) { - String trimmedPrefix = StringUtils.hasText(prefix) ? prefix : null; - while (trimmedPrefix != null && trimmedPrefix.endsWith(".")) { - trimmedPrefix = trimmedPrefix.substring(0, trimmedPrefix.length() - 1); - } - - return trimmedPrefix; - } - - @Override - public void increment(Delta delta) { - this.client.count(sanitizeMetricName(delta.getName()), - delta.getValue().longValue()); - } - - @Override - public void set(Metric value) { - String name = sanitizeMetricName(value.getName()); - if (name.contains("timer.") && !name.contains("gauge.") - && !name.contains("counter.")) { - this.client.recordExecutionTime(name, value.getValue().longValue()); - } - else { - if (name.contains("counter.")) { - this.client.count(name, value.getValue().longValue()); - } - else { - this.client.gauge(name, value.getValue().doubleValue()); - } - } - } - - @Override - public void reset(String name) { - // Not implemented - } - - @Override - public void close() { - this.client.stop(); - } - - /** - * Sanitize the metric name if necessary. - * @param name the metric name - * @return the sanitized metric name - */ - private String sanitizeMetricName(String name) { - return name.replace(":", "-"); - } - - private static final class LoggingStatsdErrorHandler - implements StatsDClientErrorHandler { - - @Override - public void handle(Exception e) { - logger.debug("Failed to write metric. Exception: " + e.getClass() - + ", message: " + e.getMessage()); - } - - } - -} diff --git a/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/metrics/statsd/package-info.java b/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/metrics/statsd/package-info.java deleted file mode 100644 index 5346aec9c68e..000000000000 --- a/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/metrics/statsd/package-info.java +++ /dev/null @@ -1,20 +0,0 @@ -/* - * Copyright 2012-2015 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -/** - * Metrics integration with Statsd. - */ -package org.springframework.boot.actuate.metrics.statsd; diff --git a/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/metrics/util/SimpleInMemoryRepository.java b/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/metrics/util/SimpleInMemoryRepository.java deleted file mode 100644 index 69f536111dee..000000000000 --- a/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/metrics/util/SimpleInMemoryRepository.java +++ /dev/null @@ -1,117 +0,0 @@ -/* - * 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.metrics.util; - -import java.util.ArrayList; -import java.util.NavigableMap; -import java.util.concurrent.ConcurrentHashMap; -import java.util.concurrent.ConcurrentMap; -import java.util.concurrent.ConcurrentNavigableMap; -import java.util.concurrent.ConcurrentSkipListMap; - -/** - * Repository utility that stores stuff in memory with period-separated String keys. - * - * @param the type to store - * @author Dave Syer - * @author Andy Wilkinson - */ -public class SimpleInMemoryRepository { - - private ConcurrentNavigableMap values = new ConcurrentSkipListMap<>(); - - private final ConcurrentMap locks = new ConcurrentHashMap<>(); - - public T update(String name, Callback callback) { - Object lock = getLock(name); - synchronized (lock) { - T current = this.values.get(name); - T value = callback.modify(current); - this.values.put(name, value); - return value; - } - } - - private Object getLock(String name) { - Object lock = this.locks.get(name); - if (lock == null) { - Object newLock = new Object(); - lock = this.locks.putIfAbsent(name, newLock); - if (lock == null) { - lock = newLock; - } - } - return lock; - } - - public void set(String name, T value) { - this.values.put(name, value); - } - - public long count() { - return this.values.size(); - } - - public void remove(String name) { - this.values.remove(name); - } - - public T findOne(String name) { - return this.values.get(name); - } - - public Iterable findAll() { - return new ArrayList<>(this.values.values()); - } - - public Iterable findAllWithPrefix(String prefix) { - if (prefix.endsWith(".*")) { - prefix = prefix.substring(0, prefix.length() - 1); - } - if (!prefix.endsWith(".")) { - prefix = prefix + "."; - } - return new ArrayList<>( - this.values.subMap(prefix, false, prefix + "~", true).values()); - } - - public void setValues(ConcurrentNavigableMap values) { - this.values = values; - } - - protected NavigableMap getValues() { - return this.values; - } - - /** - * Callback used to update a value. - * - * @param the value type - */ - @FunctionalInterface - public interface Callback { - - /** - * Modify an existing value. - * @param current the value to modify - * @return the updated value - */ - T modify(T current); - - } - -} diff --git a/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/metrics/util/package-info.java b/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/metrics/util/package-info.java deleted file mode 100644 index 67297dd3c8c3..000000000000 --- a/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/metrics/util/package-info.java +++ /dev/null @@ -1,22 +0,0 @@ -/* - * Copyright 2012-2015 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -/** - * Metrics utility classes. - * - * @see org.springframework.boot.actuate.metrics.rich.RichGauge - */ -package org.springframework.boot.actuate.metrics.util; diff --git a/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/metrics/web/client/DefaultRestTemplateExchangeTagsProvider.java b/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/metrics/web/client/DefaultRestTemplateExchangeTagsProvider.java new file mode 100644 index 000000000000..d7a4231e5fe3 --- /dev/null +++ b/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/metrics/web/client/DefaultRestTemplateExchangeTagsProvider.java @@ -0,0 +1,47 @@ +/* + * 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.metrics.web.client; + +import java.util.Arrays; + +import io.micrometer.core.instrument.Tag; + +import org.springframework.http.HttpRequest; +import org.springframework.http.client.ClientHttpResponse; +import org.springframework.util.StringUtils; + +/** + * Default implementation of {@link RestTemplateExchangeTagsProvider}. + * + * @author Jon Schneider + * @since 2.0.0 + */ +public class DefaultRestTemplateExchangeTagsProvider + implements RestTemplateExchangeTagsProvider { + + @Override + public Iterable getTags(String urlTemplate, HttpRequest request, + ClientHttpResponse response) { + Tag uriTag = StringUtils.hasText(urlTemplate) + ? RestTemplateExchangeTags.uri(urlTemplate) + : RestTemplateExchangeTags.uri(request); + return Arrays.asList(RestTemplateExchangeTags.method(request), uriTag, + RestTemplateExchangeTags.status(response), + RestTemplateExchangeTags.clientName(request)); + } + +} diff --git a/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/metrics/web/client/MetricsClientHttpRequestInterceptor.java b/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/metrics/web/client/MetricsClientHttpRequestInterceptor.java new file mode 100644 index 000000000000..a476aef97009 --- /dev/null +++ b/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/metrics/web/client/MetricsClientHttpRequestInterceptor.java @@ -0,0 +1,110 @@ +/* + * 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.metrics.web.client; + +import java.io.IOException; +import java.net.URI; +import java.util.Map; +import java.util.concurrent.TimeUnit; + +import io.micrometer.core.instrument.MeterRegistry; +import io.micrometer.core.instrument.Timer; +import io.micrometer.core.instrument.stats.hist.Histogram; + +import org.springframework.core.NamedThreadLocal; +import org.springframework.http.HttpRequest; +import org.springframework.http.client.ClientHttpRequestExecution; +import org.springframework.http.client.ClientHttpRequestInterceptor; +import org.springframework.http.client.ClientHttpResponse; +import org.springframework.web.util.UriTemplateHandler; + +/** + * {@link ClientHttpRequestInterceptor} applied via a + * {@link MetricsRestTemplateCustomizer} to record metrics. + * + * @author Jon Schneider + * @author Phillip Webb + * @since 2.0.0 + */ +class MetricsClientHttpRequestInterceptor implements ClientHttpRequestInterceptor { + + private static final ThreadLocal urlTemplate = new NamedThreadLocal<>( + "Rest Template URL Template"); + + private final MeterRegistry meterRegistry; + + private final RestTemplateExchangeTagsProvider tagProvider; + + private final String metricName; + + private final boolean recordPercentiles; + + MetricsClientHttpRequestInterceptor(MeterRegistry meterRegistry, + RestTemplateExchangeTagsProvider tagProvider, String metricName, + boolean recordPercentiles) { + this.tagProvider = tagProvider; + this.meterRegistry = meterRegistry; + this.metricName = metricName; + this.recordPercentiles = recordPercentiles; + } + + @Override + public ClientHttpResponse intercept(HttpRequest request, byte[] body, + ClientHttpRequestExecution execution) throws IOException { + long startTime = System.nanoTime(); + ClientHttpResponse response = null; + try { + response = execution.execute(request, body); + return response; + } + finally { + getTimeBuilder(request, response).register(this.meterRegistry) + .record(System.nanoTime() - startTime, TimeUnit.NANOSECONDS); + urlTemplate.remove(); + } + } + + UriTemplateHandler createUriTemplateHandler(UriTemplateHandler delegate) { + return new UriTemplateHandler() { + + @Override + public URI expand(String url, Map arguments) { + urlTemplate.set(url); + return delegate.expand(url, arguments); + } + + @Override + public URI expand(String url, Object... arguments) { + urlTemplate.set(url); + return delegate.expand(url, arguments); + } + + }; + } + + private Timer.Builder getTimeBuilder(HttpRequest request, + ClientHttpResponse response) { + Timer.Builder builder = Timer.builder(this.metricName) + .tags(this.tagProvider.getTags(urlTemplate.get(), request, response)) + .description("Timer of RestTemplate operation"); + if (this.recordPercentiles) { + builder = builder.histogram(Histogram.percentiles()); + } + return builder; + } + +} diff --git a/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/metrics/web/client/MetricsRestTemplateCustomizer.java b/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/metrics/web/client/MetricsRestTemplateCustomizer.java new file mode 100644 index 000000000000..74831e9011f3 --- /dev/null +++ b/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/metrics/web/client/MetricsRestTemplateCustomizer.java @@ -0,0 +1,68 @@ +/* + * 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.metrics.web.client; + +import java.util.ArrayList; +import java.util.List; + +import io.micrometer.core.instrument.MeterRegistry; + +import org.springframework.boot.web.client.RestTemplateCustomizer; +import org.springframework.http.client.ClientHttpRequestInterceptor; +import org.springframework.web.client.RestTemplate; +import org.springframework.web.util.UriTemplateHandler; + +/** + * {@link RestTemplateCustomizer} that configures the {@link RestTemplate} to record + * request metrics. + * + * @author Andy Wilkinson + * @author Phillip Webb + * @since 2.0.0 + */ +public class MetricsRestTemplateCustomizer implements RestTemplateCustomizer { + + private final MetricsClientHttpRequestInterceptor interceptor; + + /** + * Creates a new {@code MetricsRestTemplateInterceptor} that will record metrics using + * the given {@code meterRegistry} with tags provided by the given + * {@code tagProvider}. + * @param meterRegistry the meter registry + * @param tagProvider the tag provider + * @param metricName the name of the recorded metric + * @param recordPercentiles whether percentile histogram buckets should be recorded + */ + public MetricsRestTemplateCustomizer(MeterRegistry meterRegistry, + RestTemplateExchangeTagsProvider tagProvider, String metricName, + boolean recordPercentiles) { + this.interceptor = new MetricsClientHttpRequestInterceptor(meterRegistry, + tagProvider, metricName, recordPercentiles); + } + + @Override + public void customize(RestTemplate restTemplate) { + UriTemplateHandler templateHandler = restTemplate.getUriTemplateHandler(); + templateHandler = this.interceptor.createUriTemplateHandler(templateHandler); + restTemplate.setUriTemplateHandler(templateHandler); + List interceptors = new ArrayList<>(); + interceptors.add(this.interceptor); + interceptors.addAll(restTemplate.getInterceptors()); + restTemplate.setInterceptors(interceptors); + } + +} diff --git a/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/metrics/web/client/RestTemplateExchangeTags.java b/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/metrics/web/client/RestTemplateExchangeTags.java new file mode 100644 index 000000000000..8fd908f93f61 --- /dev/null +++ b/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/metrics/web/client/RestTemplateExchangeTags.java @@ -0,0 +1,111 @@ +/* + * 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.metrics.web.client; + +import java.io.IOException; +import java.net.URI; + +import io.micrometer.core.instrument.Tag; + +import org.springframework.http.HttpRequest; +import org.springframework.http.client.ClientHttpResponse; +import org.springframework.util.StringUtils; +import org.springframework.web.client.RestTemplate; + +/** + * Factory methods for creating {@link Tag Tags} related to a request-response exchange + * performed by a {@link RestTemplate}. + * + * @author Andy Wilkinson + * @author Jon Schneider + * @since 2.0.0 + */ +public final class RestTemplateExchangeTags { + + private RestTemplateExchangeTags() { + } + + /** + * Creates a {@code method} {@code Tag} for the {@link HttpRequest#getMethod() method} + * of the given {@code request}. + * @param request the request + * @return the method tag + */ + public static Tag method(HttpRequest request) { + return Tag.of("method", request.getMethod().name()); + } + + /** + * Creates a {@code uri} {@code Tag} for the URI of the given {@code request}. + * @param request the request + * @return the uri tag + */ + public static Tag uri(HttpRequest request) { + return Tag.of("uri", stripUri(request.getURI().toString())); + } + + /** + * Creates a {@code uri} {@code Tag} from the given {@code uriTemplate}. + * @param uriTemplate the template + * @return the uri tag + */ + public static Tag uri(String uriTemplate) { + String uri = StringUtils.hasText(uriTemplate) ? uriTemplate : "none"; + return Tag.of("uri", stripUri(uri)); + } + + private static String stripUri(String uri) { + return uri.replaceAll("^https?://[^/]+/", ""); + } + + /** + * Creates a {@code status} {@code Tag} derived from the + * {@link ClientHttpResponse#getRawStatusCode() status} of the given {@code response}. + * @param response the response + * @return the status tag + */ + public static Tag status(ClientHttpResponse response) { + return Tag.of("status", getStatusMessage(response)); + } + + private static String getStatusMessage(ClientHttpResponse response) { + try { + if (response == null) { + return "CLIENT_ERROR"; + } + return String.valueOf(response.getRawStatusCode()); + } + catch (IOException ex) { + return "IO_ERROR"; + } + } + + /** + * Create a {@code clientName} {@code Tag} derived from the {@link URI#getHost host} + * of the {@link HttpRequest#getURI() URI} of the given {@code request}. + * @param request the request + * @return the clientName tag + */ + public static Tag clientName(HttpRequest request) { + String host = request.getURI().getHost(); + if (host == null) { + host = "none"; + } + return Tag.of("clientName", host); + } + +} diff --git a/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/metrics/web/client/RestTemplateExchangeTagsProvider.java b/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/metrics/web/client/RestTemplateExchangeTagsProvider.java new file mode 100644 index 000000000000..085f8868ee61 --- /dev/null +++ b/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/metrics/web/client/RestTemplateExchangeTagsProvider.java @@ -0,0 +1,46 @@ +/* + * 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.metrics.web.client; + +import io.micrometer.core.instrument.Tag; + +import org.springframework.http.HttpRequest; +import org.springframework.http.client.ClientHttpResponse; +import org.springframework.web.client.RestTemplate; + +/** + * Provides {@link Tag Tags} for an exchange performed by a {@link RestTemplate}. + * + * @author Jon Schneider + * @author Andy Wilkinson + * @since 2.0.0 + */ +@FunctionalInterface +public interface RestTemplateExchangeTagsProvider { + + /** + * Provides the tags to be associated with metrics that are recorded for the given + * {@code request} and {@code response} exchange. + * @param urlTemplate the source URl template, if available + * @param request the request + * @param response the response (may be {@code null} if the exchange failed) + * @return the tags + */ + Iterable getTags(String urlTemplate, HttpRequest request, + ClientHttpResponse response); + +} diff --git a/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/metrics/buffer/CounterBuffers.java b/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/metrics/web/reactive/server/DefaultWebFluxTagsProvider.java similarity index 50% rename from spring-boot-actuator/src/main/java/org/springframework/boot/actuate/metrics/buffer/CounterBuffers.java rename to spring-boot-actuator/src/main/java/org/springframework/boot/actuate/metrics/web/reactive/server/DefaultWebFluxTagsProvider.java index 7381fde6c52c..94b02b938ac4 100644 --- a/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/metrics/buffer/CounterBuffers.java +++ b/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/metrics/web/reactive/server/DefaultWebFluxTagsProvider.java @@ -14,33 +14,28 @@ * limitations under the License. */ -package org.springframework.boot.actuate.metrics.buffer; +package org.springframework.boot.actuate.metrics.web.reactive.server; + +import java.util.Arrays; + +import io.micrometer.core.instrument.Tag; + +import org.springframework.web.server.ServerWebExchange; /** - * Fast writes to in-memory metrics store using {@link CounterBuffer}. + * Default implementation of {@link WebFluxTagsProvider}. * - * @author Dave Syer - * @since 1.3.0 + * @author Jon Schneider + * @author Andy Wilkinson + * @since 2.0.0 */ -public class CounterBuffers extends Buffers { - - public void increment(String name, long delta) { - doWith(name, (buffer) -> { - buffer.setTimestamp(System.currentTimeMillis()); - buffer.add(delta); - }); - } - - public void reset(String name) { - doWith(name, (buffer) -> { - buffer.setTimestamp(System.currentTimeMillis()); - buffer.reset(); - }); - } +public class DefaultWebFluxTagsProvider implements WebFluxTagsProvider { @Override - protected CounterBuffer createBuffer() { - return new CounterBuffer(0); + public Iterable httpRequestTags(ServerWebExchange exchange, + Throwable exception) { + return Arrays.asList(WebFluxTags.method(exchange), WebFluxTags.uri(exchange), + WebFluxTags.exception(exception), WebFluxTags.status(exchange)); } } diff --git a/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/metrics/web/reactive/server/MetricsWebFilter.java b/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/metrics/web/reactive/server/MetricsWebFilter.java new file mode 100644 index 000000000000..b4f2d6a84b3c --- /dev/null +++ b/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/metrics/web/reactive/server/MetricsWebFilter.java @@ -0,0 +1,75 @@ +/* + * 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.metrics.web.reactive.server; + +import java.util.concurrent.TimeUnit; + +import io.micrometer.core.instrument.MeterRegistry; +import io.micrometer.core.instrument.Tag; +import org.reactivestreams.Publisher; +import reactor.core.publisher.Mono; + +import org.springframework.web.server.ServerWebExchange; +import org.springframework.web.server.WebFilter; +import org.springframework.web.server.WebFilterChain; + +/** + * Intercepts incoming HTTP requests modeled with the Webflux annotation-based programming + * model. + * + * @author Jon Schneider + * @since 2.0.0 + */ +public class MetricsWebFilter implements WebFilter { + + private final MeterRegistry registry; + + private final WebFluxTagsProvider tagsProvider; + + private final String metricName; + + public MetricsWebFilter(MeterRegistry registry, WebFluxTagsProvider tagsProvider, + String metricName) { + this.registry = registry; + this.tagsProvider = tagsProvider; + this.metricName = metricName; + } + + @Override + public Mono filter(ServerWebExchange exchange, WebFilterChain chain) { + return chain.filter(exchange).compose((call) -> filter(exchange, call)); + } + + private Publisher filter(ServerWebExchange exchange, Mono call) { + long start = System.nanoTime(); + return call.doOnSuccess((done) -> success(exchange, start)) + .doOnError((cause) -> error(exchange, start, cause)); + } + + private void success(ServerWebExchange exchange, long start) { + Iterable tags = this.tagsProvider.httpRequestTags(exchange, null); + this.registry.timer(this.metricName, tags).record(System.nanoTime() - start, + TimeUnit.NANOSECONDS); + } + + private void error(ServerWebExchange exchange, long start, Throwable cause) { + Iterable tags = this.tagsProvider.httpRequestTags(exchange, cause); + this.registry.timer(this.metricName, tags).record(System.nanoTime() - start, + TimeUnit.NANOSECONDS); + } + +} diff --git a/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/metrics/web/reactive/server/RouterFunctionMetrics.java b/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/metrics/web/reactive/server/RouterFunctionMetrics.java new file mode 100644 index 000000000000..e8f3615a1e77 --- /dev/null +++ b/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/metrics/web/reactive/server/RouterFunctionMetrics.java @@ -0,0 +1,111 @@ +/* + * 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.metrics.web.reactive.server; + +import java.util.Arrays; +import java.util.Collection; +import java.util.Collections; +import java.util.concurrent.TimeUnit; +import java.util.function.BiFunction; + +import io.micrometer.core.instrument.MeterRegistry; +import io.micrometer.core.instrument.Tag; +import io.micrometer.core.instrument.Tags; + +import org.springframework.web.reactive.function.server.HandlerFilterFunction; +import org.springframework.web.reactive.function.server.RouterFunction; +import org.springframework.web.reactive.function.server.ServerRequest; +import org.springframework.web.reactive.function.server.ServerResponse; + +/** + * Support class for WebFlux {@link RouterFunction}-related metrics. + * + * @author Jon Schneider + * @since 2.0.0 + */ +public class RouterFunctionMetrics { + + private final MeterRegistry registry; + + private BiFunction> defaultTags = ( + ServerRequest request, ServerResponse response) -> response != null + ? Arrays.asList(method(request), status(response)) + : Collections.singletonList(method(request)); + + public RouterFunctionMetrics(MeterRegistry registry) { + this.registry = registry; + } + + /** + * Configures the default tags. + * @param defaultTags Generate a list of tags to apply to the timer. + * {@code ServerResponse} may be null. + * @return {@code this} for further configuration + */ + public RouterFunctionMetrics defaultTags( + BiFunction> defaultTags) { + this.defaultTags = defaultTags; + return this; + } + + public HandlerFilterFunction timer(String name) { + return timer(name, Collections.emptyList()); + } + + public HandlerFilterFunction timer(String name, + String... tags) { + return timer(name, Tags.zip(tags)); + } + + public HandlerFilterFunction timer(String name, + Iterable tags) { + return (request, next) -> { + final long start = System.nanoTime(); + return next.handle(request).doOnSuccess(response -> { + Iterable allTags = Tags.concat(tags, + this.defaultTags.apply(request, response)); + this.registry.timer(name, allTags).record(System.nanoTime() - start, + TimeUnit.NANOSECONDS); + }).doOnError(error -> { + // FIXME how do we get the response under an error condition? + Iterable allTags = Tags.concat(tags, + this.defaultTags.apply(request, null)); + this.registry.timer(name, allTags).record(System.nanoTime() - start, + TimeUnit.NANOSECONDS); + }); + }; + } + + /** + * Creates a {@code method} tag from the method of the given {@code request}. + * @param request The HTTP request. + * @return A "method" tag whose value is a capitalized method (e.g. GET). + */ + public static Tag method(ServerRequest request) { + return Tag.of("method", request.method().toString()); + } + + /** + * Creates a {@code status} tag from the status of the given {@code response}. + * @param response The HTTP response. + * @return A "status" tag whose value is the numeric status code. + */ + public static Tag status(ServerResponse response) { + return Tag.of("status", response.statusCode().toString()); + } + +} diff --git a/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/metrics/web/reactive/server/WebFluxTags.java b/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/metrics/web/reactive/server/WebFluxTags.java new file mode 100644 index 000000000000..0ebc3e1561ce --- /dev/null +++ b/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/metrics/web/reactive/server/WebFluxTags.java @@ -0,0 +1,96 @@ +/* + * 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.metrics.web.reactive.server; + +import io.micrometer.core.instrument.Tag; + +import org.springframework.http.HttpStatus; +import org.springframework.util.StringUtils; +import org.springframework.web.reactive.HandlerMapping; +import org.springframework.web.server.ServerWebExchange; +import org.springframework.web.util.pattern.PathPattern; + +/** + * Factory methods for {@link Tag Tags} associated with a request-response exchange that + * is handled by WebFlux. + * + * @author Jon Schneider + * @author Andy Wilkinson + * @since 2.0.0 + */ +public final class WebFluxTags { + + private WebFluxTags() { + } + + /** + * Creates a {@code method} tag based on the + * {@link org.springframework.http.server.reactive.ServerHttpRequest#getMethod() + * method} of the {@link ServerWebExchange#getRequest()} request of the given + * {@code exchange}. + * @param exchange the exchange + * @return the method tag whose value is a capitalized method (e.g. GET). + */ + public static Tag method(ServerWebExchange exchange) { + return Tag.of("method", exchange.getRequest().getMethod().toString()); + } + + /** + * Creates a {@code method} tag based on the response status of the given + * {@code exchange}. + * @param exchange the exchange + * @return the "status" tag derived from the response status + */ + public static Tag status(ServerWebExchange exchange) { + HttpStatus status = exchange.getResponse().getStatusCode(); + if (status == null) { + status = HttpStatus.OK; + } + return Tag.of("status", status.toString()); + } + + /** + * Creates a {@code uri} tag based on the URI of the given {@code exchange}. Uses the + * {@link HandlerMapping#BEST_MATCHING_PATTERN_ATTRIBUTE} best matching pattern. + * @param exchange the exchange + * @return the uri tag derived from the exchange + */ + public static Tag uri(ServerWebExchange exchange) { + PathPattern pathPattern = exchange.getAttributeOrDefault( + HandlerMapping.BEST_MATCHING_PATTERN_ATTRIBUTE, null); + String uri = pathPattern == null ? exchange.getRequest().getURI().toString() + : pathPattern.getPatternString(); + if (!StringUtils.hasText(uri)) { + uri = "/"; + } + return Tag.of("uri", uri.isEmpty() ? "root" : uri); + } + + /** + * Creates an {@code exception} tag based on the {@link Class#getSimpleName() simple + * name} of the class of the given {@code exception}. + * @param exception the exception, may be {@code null} + * @return the exception tag derived from the exception + */ + public static Tag exception(Throwable exception) { + if (exception != null) { + return Tag.of("exception", exception.getClass().getSimpleName()); + } + return Tag.of("exception", "none"); + } + +} diff --git a/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/metrics/PublicMetrics.java b/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/metrics/web/reactive/server/WebFluxTagsProvider.java similarity index 51% rename from spring-boot-actuator/src/main/java/org/springframework/boot/actuate/metrics/PublicMetrics.java rename to spring-boot-actuator/src/main/java/org/springframework/boot/actuate/metrics/web/reactive/server/WebFluxTagsProvider.java index 1b90d4a273b5..942991696683 100644 --- a/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/metrics/PublicMetrics.java +++ b/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/metrics/web/reactive/server/WebFluxTagsProvider.java @@ -14,27 +14,28 @@ * limitations under the License. */ -package org.springframework.boot.actuate.metrics; +package org.springframework.boot.actuate.metrics.web.reactive.server; -import java.util.Collection; +import io.micrometer.core.instrument.Tag; + +import org.springframework.web.server.ServerWebExchange; /** - * Interface to expose specific {@link Metric}s via a {@link MetricsEndpoint}. - * Implementations should take care that the metrics they provide have unique names in the - * application context, but they shouldn't have to care about global uniqueness in the JVM - * or across a distributed system. + * Provides {@link Tag Tags} for WebFlux-based request handling. * - * @author Dave Syer + * @author Jon Schneider + * @author Andy Wilkinson * @since 2.0.0 - * @see SystemPublicMetrics SystemPublicMetrics for an example implementation */ @FunctionalInterface -public interface PublicMetrics { +public interface WebFluxTagsProvider { /** - * Return an indication of current state through metrics. - * @return the public metrics + * Provides tags to be associated with metrics for the given {@code exchange}. + * @param exchange the exchange + * @param ex the current exception (may be {@code null} + * @return tags to associate with metrics for the request and response exchange */ - Collection> metrics(); + Iterable httpRequestTags(ServerWebExchange exchange, Throwable ex); } diff --git a/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/metrics/web/servlet/DefaultWebMvcTagsProvider.java b/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/metrics/web/servlet/DefaultWebMvcTagsProvider.java new file mode 100644 index 000000000000..495cbe4403f9 --- /dev/null +++ b/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/metrics/web/servlet/DefaultWebMvcTagsProvider.java @@ -0,0 +1,59 @@ +/* + * 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.metrics.web.servlet; + +import java.util.Arrays; + +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +import io.micrometer.core.instrument.Tag; + +/** + * Default implementation of {@link WebMvcTagsProvider}. + * + * @author Jon Schneider + * @since 2.0.0 + */ +public class DefaultWebMvcTagsProvider implements WebMvcTagsProvider { + + /** + * Supplies default tags to long task timers. + * @param request The HTTP request. + * @param handler The request method that is responsible for handling the request. + * @return A set of tags added to every Spring MVC HTTP request + */ + @Override + public Iterable httpLongRequestTags(HttpServletRequest request, Object handler) { + return Arrays.asList(WebMvcTags.method(request), WebMvcTags.uri(request)); + } + + /** + * Supplies default tags to the Web MVC server programming model. + * @param request The HTTP request. + * @param response The HTTP response. + * @param ex The current exception, if any + * @return A set of tags added to every Spring MVC HTTP request. + */ + @Override + public Iterable httpRequestTags(HttpServletRequest request, + HttpServletResponse response, Throwable ex) { + return Arrays.asList(WebMvcTags.method(request), WebMvcTags.uri(request), + WebMvcTags.exception(ex), WebMvcTags.status(response)); + } + +} diff --git a/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/metrics/web/servlet/MetricsFilter.java b/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/metrics/web/servlet/MetricsFilter.java deleted file mode 100644 index 978172589191..000000000000 --- a/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/metrics/web/servlet/MetricsFilter.java +++ /dev/null @@ -1,271 +0,0 @@ -/* - * 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.metrics.web.servlet; - -import java.io.IOException; -import java.util.Collections; -import java.util.LinkedHashSet; -import java.util.Set; -import java.util.regex.Pattern; - -import javax.servlet.FilterChain; -import javax.servlet.ServletException; -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; - -import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; - -import org.springframework.boot.actuate.metrics.CounterService; -import org.springframework.boot.actuate.metrics.GaugeService; -import org.springframework.core.Ordered; -import org.springframework.core.annotation.Order; -import org.springframework.http.HttpStatus; -import org.springframework.http.HttpStatus.Series; -import org.springframework.util.StopWatch; -import org.springframework.web.filter.OncePerRequestFilter; -import org.springframework.web.servlet.HandlerMapping; -import org.springframework.web.util.UrlPathHelper; - -/** - * Filter that counts requests and measures processing times. - * - * @author Dave Syer - * @author Andy Wilkinson - * @since 2.0.0 - */ -@Order(Ordered.HIGHEST_PRECEDENCE) -public final class MetricsFilter extends OncePerRequestFilter { - - private static final String ATTRIBUTE_STOP_WATCH = MetricsFilter.class.getName() - + ".StopWatch"; - - private static final int UNDEFINED_HTTP_STATUS = 999; - - private static final String UNKNOWN_PATH_SUFFIX = "/unmapped"; - - private static final Log logger = LogFactory.getLog(MetricsFilter.class); - - private final CounterService counterService; - - private final GaugeService gaugeService; - - private final Set counterSubmissions; - - private final Set gaugeSubmissions; - - private static final Set STATUS_REPLACERS; - - static { - Set replacements = new LinkedHashSet<>(); - replacements.add(new PatternReplacer("\\{(.+?)(?::.+)?\\}", 0, "-$1-")); - replacements.add(new PatternReplacer("**", Pattern.LITERAL, "-star-star-")); - replacements.add(new PatternReplacer("*", Pattern.LITERAL, "-star-")); - replacements.add(new PatternReplacer("/-", Pattern.LITERAL, "/")); - replacements.add(new PatternReplacer("-/", Pattern.LITERAL, "/")); - STATUS_REPLACERS = Collections.unmodifiableSet(replacements); - } - - private static final Set KEY_REPLACERS; - - static { - Set replacements = new LinkedHashSet<>(); - replacements.add(new PatternReplacer("/", Pattern.LITERAL, ".")); - replacements.add(new PatternReplacer("..", Pattern.LITERAL, ".")); - KEY_REPLACERS = Collections.unmodifiableSet(replacements); - } - - public MetricsFilter(CounterService counterService, GaugeService gaugeService, - Set counterSubmissions, - Set gaugeSubmissions) { - this.counterService = counterService; - this.gaugeService = gaugeService; - this.counterSubmissions = counterSubmissions; - this.gaugeSubmissions = gaugeSubmissions; - } - - @Override - protected boolean shouldNotFilterAsyncDispatch() { - return false; - } - - @Override - protected void doFilterInternal(HttpServletRequest request, - HttpServletResponse response, FilterChain chain) - throws ServletException, IOException { - StopWatch stopWatch = createStopWatchIfNecessary(request); - String path = new UrlPathHelper().getPathWithinApplication(request); - int status = HttpStatus.INTERNAL_SERVER_ERROR.value(); - try { - chain.doFilter(request, response); - status = getStatus(response); - } - finally { - if (!request.isAsyncStarted()) { - if (response.isCommitted()) { - status = getStatus(response); - } - stopWatch.stop(); - request.removeAttribute(ATTRIBUTE_STOP_WATCH); - recordMetrics(request, path, status, stopWatch.getTotalTimeMillis()); - } - } - } - - private StopWatch createStopWatchIfNecessary(HttpServletRequest request) { - StopWatch stopWatch = (StopWatch) request.getAttribute(ATTRIBUTE_STOP_WATCH); - if (stopWatch == null) { - stopWatch = new StopWatch(); - stopWatch.start(); - request.setAttribute(ATTRIBUTE_STOP_WATCH, stopWatch); - } - return stopWatch; - } - - private int getStatus(HttpServletResponse response) { - try { - return response.getStatus(); - } - catch (Exception ex) { - return UNDEFINED_HTTP_STATUS; - } - } - - private void recordMetrics(HttpServletRequest request, String path, int status, - long time) { - String suffix = determineMetricNameSuffix(request, path, status); - submitMetrics(MetricsFilterSubmission.MERGED, request, status, time, suffix); - submitMetrics(MetricsFilterSubmission.PER_HTTP_METHOD, request, status, time, - suffix); - } - - private String determineMetricNameSuffix(HttpServletRequest request, String path, - int status) { - Object bestMatchingPattern = request - .getAttribute(HandlerMapping.BEST_MATCHING_PATTERN_ATTRIBUTE); - if (bestMatchingPattern != null) { - return fixSpecialCharacters(bestMatchingPattern.toString()); - } - Series series = getSeries(status); - if (Series.CLIENT_ERROR.equals(series) || Series.SERVER_ERROR.equals(series) - || Series.REDIRECTION.equals(series)) { - return UNKNOWN_PATH_SUFFIX; - } - return path; - } - - private String fixSpecialCharacters(String value) { - String result = value; - for (PatternReplacer replacer : STATUS_REPLACERS) { - result = replacer.apply(result); - } - if (result.endsWith("-")) { - result = result.substring(0, result.length() - 1); - } - if (result.startsWith("-")) { - result = result.substring(1); - } - return result; - } - - private Series getSeries(int status) { - try { - return HttpStatus.valueOf(status).series(); - } - catch (Exception ex) { - return null; - } - } - - private void submitMetrics(MetricsFilterSubmission submission, - HttpServletRequest request, int status, long time, String suffix) { - String prefix = ""; - if (submission == MetricsFilterSubmission.PER_HTTP_METHOD) { - prefix = request.getMethod() + "."; - } - if (shouldSubmitToGauge(submission)) { - submitToGauge(getKey("response." + prefix + suffix), time); - } - if (shouldSubmitToCounter(submission)) { - incrementCounter(getKey("status." + prefix + status + suffix)); - } - } - - private boolean shouldSubmitToGauge(MetricsFilterSubmission submission) { - return shouldSubmit(this.gaugeSubmissions, submission); - } - - private boolean shouldSubmitToCounter(MetricsFilterSubmission submission) { - return shouldSubmit(this.counterSubmissions, submission); - } - - private boolean shouldSubmit(Set submissions, - MetricsFilterSubmission submission) { - return submissions != null && submissions.contains(submission); - } - - private String getKey(String string) { - // graphite compatible metric names - String key = string; - for (PatternReplacer replacer : KEY_REPLACERS) { - key = replacer.apply(key); - } - if (key.endsWith(".")) { - key = key + "root"; - } - if (key.startsWith("_")) { - key = key.substring(1); - } - return key; - } - - private void submitToGauge(String metricName, double value) { - try { - this.gaugeService.submit(metricName, value); - } - catch (Exception ex) { - logger.warn("Unable to submit gauge metric '" + metricName + "'", ex); - } - } - - private void incrementCounter(String metricName) { - try { - this.counterService.increment(metricName); - } - catch (Exception ex) { - logger.warn("Unable to submit counter metric '" + metricName + "'", ex); - } - } - - private static class PatternReplacer { - - private final Pattern pattern; - - private final String replacement; - - PatternReplacer(String regex, int flags, String replacement) { - this.pattern = Pattern.compile(regex, flags); - this.replacement = replacement; - } - - public String apply(String input) { - return this.pattern.matcher(input).replaceAll(this.replacement); - } - - } - -} diff --git a/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/metrics/web/servlet/MetricsHandlerInterceptor.java b/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/metrics/web/servlet/MetricsHandlerInterceptor.java new file mode 100644 index 000000000000..00b7a41d9c95 --- /dev/null +++ b/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/metrics/web/servlet/MetricsHandlerInterceptor.java @@ -0,0 +1,52 @@ +/* + * 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.metrics.web.servlet; + +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +import org.springframework.web.servlet.handler.HandlerInterceptorAdapter; + +/** + * Intercepts incoming HTTP requests and records metrics about execution time and results. + * + * @author Jon Schneider + * @since 2.0.0 + */ +public class MetricsHandlerInterceptor extends HandlerInterceptorAdapter { + + private final WebMvcMetrics webMvcMetrics; + + public MetricsHandlerInterceptor(WebMvcMetrics webMvcMetrics) { + this.webMvcMetrics = webMvcMetrics; + } + + @Override + public boolean preHandle(HttpServletRequest request, HttpServletResponse response, + Object handler) throws Exception { + this.webMvcMetrics.preHandle(request, handler); + return super.preHandle(request, response, handler); + } + + @Override + public void afterCompletion(HttpServletRequest request, HttpServletResponse response, + Object handler, Exception ex) throws Exception { + this.webMvcMetrics.record(request, response, ex); + super.afterCompletion(request, response, handler, ex); + } + +} diff --git a/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/metrics/web/servlet/WebMvcMetrics.java b/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/metrics/web/servlet/WebMvcMetrics.java new file mode 100644 index 000000000000..7ecd231b159c --- /dev/null +++ b/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/metrics/web/servlet/WebMvcMetrics.java @@ -0,0 +1,305 @@ +/* + * 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.metrics.web.servlet; + +import java.lang.reflect.AnnotatedElement; +import java.lang.reflect.Method; +import java.util.Collections; +import java.util.IdentityHashMap; +import java.util.Map; +import java.util.Set; +import java.util.concurrent.TimeUnit; +import java.util.function.Supplier; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +import io.micrometer.core.annotation.Timed; +import io.micrometer.core.instrument.LongTaskTimer; +import io.micrometer.core.instrument.MeterRegistry; +import io.micrometer.core.instrument.Tag; +import io.micrometer.core.instrument.Tags; +import io.micrometer.core.instrument.Timer; +import io.micrometer.core.instrument.stats.hist.Histogram; +import io.micrometer.core.instrument.stats.quantile.WindowSketchQuantiles; +import io.micrometer.core.instrument.util.AnnotationUtils; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +import org.springframework.util.ObjectUtils; +import org.springframework.web.context.request.RequestAttributes; +import org.springframework.web.context.request.RequestContextHolder; +import org.springframework.web.method.HandlerMethod; + +/** + * Support class for Spring MVC metrics. + * + * @author Jon Schneider + * @since 2.0.0 + */ +public class WebMvcMetrics { + + private static final String TIMING_REQUEST_ATTRIBUTE = "micrometer.requestStartTime"; + + private static final String HANDLER_REQUEST_ATTRIBUTE = "micrometer.requestHandler"; + + private static final String EXCEPTION_ATTRIBUTE = "micrometer.requestException"; + + private static final Log logger = LogFactory.getLog(WebMvcMetrics.class); + + private final Map longTaskTimerIds = Collections + .synchronizedMap(new IdentityHashMap<>()); + + private final MeterRegistry registry; + + private final WebMvcTagsProvider tagsProvider; + + private final String metricName; + + private final boolean autoTimeRequests; + + private final boolean recordAsPercentiles; + + public WebMvcMetrics(MeterRegistry registry, WebMvcTagsProvider tagsProvider, + String metricName, boolean autoTimeRequests, boolean recordAsPercentiles) { + this.registry = registry; + this.tagsProvider = tagsProvider; + this.metricName = metricName; + this.autoTimeRequests = autoTimeRequests; + this.recordAsPercentiles = recordAsPercentiles; + } + + void tagWithException(Throwable exception) { + RequestAttributes attributes = RequestContextHolder.getRequestAttributes(); + attributes.setAttribute(EXCEPTION_ATTRIBUTE, exception, + RequestAttributes.SCOPE_REQUEST); + } + + void preHandle(HttpServletRequest request, Object handler) { + request.setAttribute(TIMING_REQUEST_ATTRIBUTE, System.nanoTime()); + request.setAttribute(HANDLER_REQUEST_ATTRIBUTE, handler); + longTaskTimed(handler).forEach((config) -> { + if (config.getName() == null) { + logWarning(request, handler); + return; + } + this.longTaskTimerIds.put(request, + longTaskTimer(config, request, handler).start()); + }); + } + + private void logWarning(HttpServletRequest request, Object handler) { + if (handler instanceof HandlerMethod) { + logger.warn("Unable to perform metrics timing on " + + ((HandlerMethod) handler).getShortLogMessage() + + ": @Timed annotation must have a value used to name the metric"); + return; + } + logger.warn("Unable to perform metrics timing for request " + + request.getRequestURI() + + ": @Timed annotation must have a value used to name the metric"); + } + + void record(HttpServletRequest request, HttpServletResponse response, Throwable ex) { + Object handler = request.getAttribute(HANDLER_REQUEST_ATTRIBUTE); + Long startTime = (Long) request.getAttribute(TIMING_REQUEST_ATTRIBUTE); + long endTime = System.nanoTime(); + completeLongTimerTasks(request, handler); + Throwable thrown = (ex != null ? ex + : (Throwable) request.getAttribute(EXCEPTION_ATTRIBUTE)); + recordTimerTasks(request, response, handler, startTime, endTime, thrown); + } + + private void completeLongTimerTasks(HttpServletRequest request, Object handler) { + longTaskTimed(handler) + .forEach((config) -> completeLongTimerTask(request, handler, config)); + } + + private void completeLongTimerTask(HttpServletRequest request, Object handler, + TimerConfig config) { + if (config.getName() != null) { + longTaskTimer(config, request, handler) + .stop(this.longTaskTimerIds.remove(request)); + } + } + + private void recordTimerTasks(HttpServletRequest request, + HttpServletResponse response, Object handler, Long startTime, long endTime, + Throwable thrown) { + // record Timer values + timed(handler).forEach((config) -> { + Timer.Builder builder = getTimerBuilder(request, response, thrown, config); + long amount = endTime - startTime; + builder.register(this.registry).record(amount, TimeUnit.NANOSECONDS); + }); + } + + private Timer.Builder getTimerBuilder(HttpServletRequest request, + HttpServletResponse response, Throwable thrown, TimerConfig config) { + Timer.Builder builder = Timer.builder(config.getName()) + .tags(this.tagsProvider.httpRequestTags(request, response, thrown)) + .tags(config.getExtraTags()).description("Timer of servlet request"); + if (config.getQuantiles().length > 0) { + WindowSketchQuantiles quantiles = WindowSketchQuantiles + .quantiles(config.getQuantiles()).create(); + builder = builder.quantiles(quantiles); + } + if (config.isPercentiles()) { + builder = builder.histogram(Histogram.percentilesTime()); + } + return builder; + } + + private LongTaskTimer longTaskTimer(TimerConfig config, HttpServletRequest request, + Object handler) { + Iterable tags = Tags.concat( + this.tagsProvider.httpLongRequestTags(request, handler), + config.getExtraTags()); + return this.registry.more().longTaskTimer(this.registry.createId(config.getName(), + tags, "Timer of long servlet request")); + } + + private Set longTaskTimed(Object handler) { + if (handler instanceof HandlerMethod) { + return longTaskTimed((HandlerMethod) handler); + } + return Collections.emptySet(); + } + + private Set longTaskTimed(HandlerMethod handler) { + Set timed = getLongTaskAnnotationConfig(handler.getMethod()); + if (timed.isEmpty()) { + return getLongTaskAnnotationConfig(handler.getBeanType()); + } + return timed; + } + + private Set timed(Object handler) { + if (handler instanceof HandlerMethod) { + return timed((HandlerMethod) handler); + } + return Collections.emptySet(); + } + + private Set timed(HandlerMethod handler) { + Set config = getNonLongTaskAnnotationConfig(handler.getMethod()); + if (config.isEmpty()) { + config = getNonLongTaskAnnotationConfig(handler.getBeanType()); + if (config.isEmpty() && this.autoTimeRequests) { + return Collections.singleton(new TimerConfig(getServerRequestName(), + this.recordAsPercentiles)); + } + } + return config; + } + + private Set getNonLongTaskAnnotationConfig(AnnotatedElement element) { + return findTimedAnnotations(element).filter((t) -> !t.longTask()) + .map(this::fromAnnotation).collect(Collectors.toSet()); + } + + private Set getLongTaskAnnotationConfig(AnnotatedElement element) { + return findTimedAnnotations(element).filter(Timed::longTask) + .map(this::fromAnnotation).collect(Collectors.toSet()); + } + + private Stream findTimedAnnotations(AnnotatedElement element) { + if (element instanceof Class) { + return AnnotationUtils.findTimed((Class) element); + } + return AnnotationUtils.findTimed((Method) element); + } + + private TimerConfig fromAnnotation(Timed timed) { + return new TimerConfig(timed, this::getServerRequestName); + } + + private String getServerRequestName() { + return this.metricName; + } + + private static class TimerConfig { + + private final String name; + + private final Iterable extraTags; + + private final double[] quantiles; + + private final boolean percentiles; + + TimerConfig(String name, boolean percentiles) { + this.name = name; + this.extraTags = Collections.emptyList(); + this.quantiles = new double[0]; + this.percentiles = percentiles; + } + + TimerConfig(Timed timed, Supplier name) { + this.name = buildName(timed, name); + this.extraTags = Tags.zip(timed.extraTags()); + this.quantiles = timed.quantiles(); + this.percentiles = timed.percentiles(); + } + + private String buildName(Timed timed, Supplier name) { + if (timed.longTask() && timed.value().isEmpty()) { + // the user MUST name long task timers, we don't lump them in with regular + // timers with the same name + return null; + } + return (timed.value().isEmpty() ? name.get() : timed.value()); + } + + public String getName() { + return this.name; + } + + public Iterable getExtraTags() { + return this.extraTags; + } + + public double[] getQuantiles() { + return this.quantiles; + } + + public boolean isPercentiles() { + return this.percentiles; + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + TimerConfig other = (TimerConfig) o; + return ObjectUtils.nullSafeEquals(this.name, other.name); + } + + @Override + public int hashCode() { + return ObjectUtils.nullSafeHashCode(this.name); + } + + } + +} diff --git a/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/metrics/web/servlet/WebMvcTags.java b/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/metrics/web/servlet/WebMvcTags.java new file mode 100644 index 000000000000..174fb0b20dba --- /dev/null +++ b/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/metrics/web/servlet/WebMvcTags.java @@ -0,0 +1,92 @@ +/* + * 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.metrics.web.servlet; + +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +import io.micrometer.core.instrument.Tag; + +import org.springframework.util.StringUtils; +import org.springframework.web.servlet.HandlerMapping; + +/** + * Factory methods for {@link Tag Tags} associated with a request-response exchange that + * is handled by Spring MVC. + * + * @author Jon Schneider + * @author Andy Wilkinson + * @since 2.0.0 + */ +public final class WebMvcTags { + + private WebMvcTags() { + } + + /** + * Creates a {@code method} tag based on the {@link HttpServletRequest#getMethod() + * method} of the given {@code request}. + * @param request the request + * @return the method tag whose value is a capitalized method (e.g. GET). + */ + public static Tag method(HttpServletRequest request) { + return Tag.of("method", request.getMethod()); + } + + /** + * Creates a {@code method} tag based on the status of the given {@code response}. + * @param response the HTTP response + * @return the status tag derived from the status of the response + */ + public static Tag status(HttpServletResponse response) { + return Tag.of("status", ((Integer) response.getStatus()).toString()); + } + + /** + * Creates a {@code uri} tag based on the URI of the given {@code request}. Uses the + * {@link HandlerMapping#BEST_MATCHING_PATTERN_ATTRIBUTE} best matching pattern if + * available, falling back to the request's {@link HttpServletRequest#getPathInfo() + * path info} if necessary. + * @param request the request + * @return the uri tag derived from the request + */ + public static Tag uri(HttpServletRequest request) { + String uri = (String) request + .getAttribute(HandlerMapping.BEST_MATCHING_PATTERN_ATTRIBUTE); + if (uri == null) { + uri = request.getPathInfo(); + } + if (!StringUtils.hasText(uri)) { + uri = "/"; + } + return Tag.of("uri", uri.isEmpty() ? "root" : uri); + } + + /** + * Creates a {@code exception} tag based on the {@link Class#getSimpleName() simple + * name} of the class of the given {@code exception}. + * @param exception the exception, may be {@code null} + * @return the exception tag derived from the exception + */ + public static Tag exception(Throwable exception) { + if (exception != null) { + return Tag.of("exception", exception.getClass().getSimpleName()); + } + return Tag.of("exception", "None"); + } + +} diff --git a/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/metrics/web/servlet/WebMvcTagsProvider.java b/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/metrics/web/servlet/WebMvcTagsProvider.java new file mode 100644 index 000000000000..a4d612057ae3 --- /dev/null +++ b/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/metrics/web/servlet/WebMvcTagsProvider.java @@ -0,0 +1,53 @@ +/* + * 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.metrics.web.servlet; + +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +import io.micrometer.core.instrument.LongTaskTimer; +import io.micrometer.core.instrument.Tag; + +/** + * Provides {@link Tag Tags} for Spring MVC-based request handling. + * + * @author Jon Schneider + * @author Andy Wilkinson + * @since 2.0.0 + */ +public interface WebMvcTagsProvider { + + /** + * Provides tags to be used by {@link LongTaskTimer long task timers}. + * @param request the HTTP request + * @param handler the handler for the request + * @return tags to associate with metrics recorded for the request + */ + Iterable httpLongRequestTags(HttpServletRequest request, Object handler); + + /** + * Provides tags to be associated with metrics for the given {@code request} and + * {@code response} exchange. + * @param request the request + * @param response the response + * @param ex the current exception, if any + * @return tags to associate with metrics for the request and response exchange + */ + Iterable httpRequestTags(HttpServletRequest request, + HttpServletResponse response, Throwable ex); + +} diff --git a/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/metrics/web/servlet/package-info.java b/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/metrics/web/servlet/package-info.java deleted file mode 100644 index 2acf65125819..000000000000 --- a/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/metrics/web/servlet/package-info.java +++ /dev/null @@ -1,20 +0,0 @@ -/* - * 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. - */ - -/** - * Metrics support for Servlet-based web applications. - */ -package org.springframework.boot.actuate.metrics.web.servlet; diff --git a/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/metrics/writer/CompositeMetricWriter.java b/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/metrics/writer/CompositeMetricWriter.java deleted file mode 100644 index 18ef8f6ad9aa..000000000000 --- a/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/metrics/writer/CompositeMetricWriter.java +++ /dev/null @@ -1,70 +0,0 @@ -/* - * 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.metrics.writer; - -import java.util.ArrayList; -import java.util.Collections; -import java.util.Iterator; -import java.util.List; - -import org.springframework.boot.actuate.metrics.Metric; - -/** - * Composite implementation of {@link MetricWriter} that just sends its input to all of - * the delegates that have been registered. - * - * @author Dave Syer - */ -public class CompositeMetricWriter implements MetricWriter, Iterable { - - private final List writers = new ArrayList<>(); - - public CompositeMetricWriter(MetricWriter... writers) { - Collections.addAll(this.writers, writers); - } - - public CompositeMetricWriter(List writers) { - this.writers.addAll(writers); - } - - @Override - public Iterator iterator() { - return this.writers.iterator(); - } - - @Override - public void increment(Delta delta) { - for (MetricWriter writer : this.writers) { - writer.increment(delta); - } - } - - @Override - public void set(Metric value) { - for (MetricWriter writer : this.writers) { - writer.set(value); - } - } - - @Override - public void reset(String metricName) { - for (MetricWriter writer : this.writers) { - writer.reset(metricName); - } - } - -} diff --git a/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/metrics/writer/CounterWriter.java b/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/metrics/writer/CounterWriter.java deleted file mode 100644 index 1f88829e840a..000000000000 --- a/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/metrics/writer/CounterWriter.java +++ /dev/null @@ -1,43 +0,0 @@ -/* - * Copyright 2012-2015 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.boot.actuate.metrics.writer; - -/** - * Simple writer for counters (metrics that increment). - * - * @author Dave Syer - * @since 1.3.0 - */ -public interface CounterWriter { - - /** - * Increment the value of a metric (or decrement if the delta is negative). The name - * of the delta is the name of the metric to increment. - * @param delta the amount to increment by - */ - void increment(Delta delta); - - /** - * Reset the value of a metric, usually to zero value. Implementations can discard the - * old values if desired, but may choose not to. This operation is optional (some - * implementations may not be able to fulfill the contract, in which case they should - * simply do nothing). - * @param metricName the name to reset - */ - void reset(String metricName); - -} diff --git a/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/metrics/writer/DefaultCounterService.java b/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/metrics/writer/DefaultCounterService.java deleted file mode 100644 index 2151705f76c8..000000000000 --- a/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/metrics/writer/DefaultCounterService.java +++ /dev/null @@ -1,70 +0,0 @@ -/* - * 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.metrics.writer; - -import java.util.concurrent.ConcurrentHashMap; - -import org.springframework.boot.actuate.metrics.CounterService; - -/** - * Default implementation of {@link CounterService}. - * - * @author Dave Syer - */ -public class DefaultCounterService implements CounterService { - - private final MetricWriter writer; - - private final ConcurrentHashMap names = new ConcurrentHashMap<>(); - - /** - * Create a {@link DefaultCounterService} instance. - * @param writer the underlying writer used to manage metrics - */ - public DefaultCounterService(MetricWriter writer) { - this.writer = writer; - } - - @Override - public void increment(String metricName) { - this.writer.increment(new Delta<>(wrap(metricName), 1L)); - } - - @Override - public void decrement(String metricName) { - this.writer.increment(new Delta<>(wrap(metricName), -1L)); - } - - @Override - public void reset(String metricName) { - this.writer.reset(wrap(metricName)); - } - - private String wrap(String metricName) { - String cached = this.names.get(metricName); - if (cached != null) { - return cached; - } - if (metricName.startsWith("counter.") || metricName.startsWith("meter.")) { - return metricName; - } - String name = "counter." + metricName; - this.names.put(metricName, name); - return name; - } - -} diff --git a/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/metrics/writer/DefaultGaugeService.java b/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/metrics/writer/DefaultGaugeService.java deleted file mode 100644 index daecc2b97beb..000000000000 --- a/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/metrics/writer/DefaultGaugeService.java +++ /dev/null @@ -1,62 +0,0 @@ -/* - * 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.metrics.writer; - -import java.util.concurrent.ConcurrentHashMap; - -import org.springframework.boot.actuate.metrics.GaugeService; -import org.springframework.boot.actuate.metrics.Metric; - -/** - * Default implementation of {@link GaugeService}. - * - * @author Dave Syer - */ -public class DefaultGaugeService implements GaugeService { - - private final MetricWriter writer; - - private final ConcurrentHashMap names = new ConcurrentHashMap<>(); - - /** - * Create a {@link DefaultGaugeService} instance. - * @param writer the underlying writer used to manage metrics - */ - public DefaultGaugeService(MetricWriter writer) { - this.writer = writer; - } - - @Override - public void submit(String metricName, double value) { - this.writer.set(new Metric<>(wrap(metricName), value)); - } - - private String wrap(String metricName) { - String cached = this.names.get(metricName); - if (cached != null) { - return cached; - } - if (metricName.startsWith("gauge") || metricName.startsWith("histogram") - || metricName.startsWith("timer")) { - return metricName; - } - String name = "gauge." + metricName; - this.names.put(metricName, name); - return name; - } - -} diff --git a/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/metrics/writer/Delta.java b/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/metrics/writer/Delta.java deleted file mode 100644 index 679fb2fe2336..000000000000 --- a/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/metrics/writer/Delta.java +++ /dev/null @@ -1,39 +0,0 @@ -/* - * Copyright 2012-2015 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.boot.actuate.metrics.writer; - -import java.util.Date; - -import org.springframework.boot.actuate.metrics.Metric; - -/** - * A value object representing an increment in a metric value (usually a counter). - * - * @param the value type - * @author Dave Syer - */ -public class Delta extends Metric { - - public Delta(String name, T value, Date timestamp) { - super(name, value, timestamp); - } - - public Delta(String name, T value) { - super(name, value); - } - -} diff --git a/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/metrics/writer/MessageChannelMetricWriter.java b/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/metrics/writer/MessageChannelMetricWriter.java deleted file mode 100644 index 924667582dff..000000000000 --- a/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/metrics/writer/MessageChannelMetricWriter.java +++ /dev/null @@ -1,53 +0,0 @@ -/* - * Copyright 2012-2015 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.boot.actuate.metrics.writer; - -import org.springframework.boot.actuate.metrics.Metric; -import org.springframework.messaging.MessageChannel; - -/** - * A {@link MetricWriter} that publishes the metric updates on a {@link MessageChannel}. - * The messages have the writer input ({@link Delta} or {@link Metric}) as payload, and - * carry an additional header "metricName" with the name of the metric in it. - * - * @author Dave Syer - * @see MetricWriterMessageHandler - */ -public class MessageChannelMetricWriter implements MetricWriter { - - private final MessageChannel channel; - - public MessageChannelMetricWriter(MessageChannel channel) { - this.channel = channel; - } - - @Override - public void increment(Delta delta) { - this.channel.send(MetricMessage.forIncrement(delta)); - } - - @Override - public void set(Metric value) { - this.channel.send(MetricMessage.forSet(value)); - } - - @Override - public void reset(String metricName) { - this.channel.send(MetricMessage.forReset(metricName)); - } - -} diff --git a/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/metrics/writer/MetricMessage.java b/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/metrics/writer/MetricMessage.java deleted file mode 100644 index acbbca8d4407..000000000000 --- a/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/metrics/writer/MetricMessage.java +++ /dev/null @@ -1,70 +0,0 @@ -/* - * Copyright 2012-2015 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.boot.actuate.metrics.writer; - -import org.springframework.boot.actuate.metrics.Metric; -import org.springframework.messaging.Message; -import org.springframework.messaging.support.MessageBuilder; - -/** - * A metric message sent via Spring Integration. - * - * @author Phillip Webb - */ -class MetricMessage { - - private static final String METRIC_NAME = "metricName"; - - private static final String DELETE = "delete"; - - private final Message message; - - MetricMessage(Message message) { - this.message = message; - } - - public boolean isReset() { - return DELETE.equals(getPayload()); - } - - public Object getPayload() { - return this.message.getPayload(); - } - - public String getMetricName() { - return this.message.getHeaders().get(METRIC_NAME, String.class); - } - - public static Message forIncrement(Delta delta) { - return forPayload(delta.getName(), delta); - } - - public static Message forSet(Metric value) { - return forPayload(value.getName(), value); - } - - public static Message forReset(String metricName) { - return forPayload(metricName, DELETE); - } - - private static Message forPayload(String metricName, Object payload) { - MessageBuilder builder = MessageBuilder.withPayload(payload); - builder.setHeader(METRIC_NAME, metricName); - return builder.build(); - } - -} diff --git a/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/metrics/writer/MetricWriter.java b/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/metrics/writer/MetricWriter.java deleted file mode 100644 index 5756b36b9b7b..000000000000 --- a/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/metrics/writer/MetricWriter.java +++ /dev/null @@ -1,30 +0,0 @@ -/* - * Copyright 2012-2015 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.boot.actuate.metrics.writer; - -import org.springframework.boot.actuate.metrics.Metric; - -/** - * Basic strategy for write operations on {@link Metric} data. - * - * @author Dave Syer - * @see GaugeWriter - * @see CounterWriter - */ -public interface MetricWriter extends GaugeWriter, CounterWriter { - -} diff --git a/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/metrics/writer/MetricWriterMessageHandler.java b/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/metrics/writer/MetricWriterMessageHandler.java deleted file mode 100644 index 212c6abf002b..000000000000 --- a/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/metrics/writer/MetricWriterMessageHandler.java +++ /dev/null @@ -1,70 +0,0 @@ -/* - * Copyright 2012-2015 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.boot.actuate.metrics.writer; - -import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; - -import org.springframework.boot.actuate.metrics.Metric; -import org.springframework.messaging.Message; -import org.springframework.messaging.MessageHandler; -import org.springframework.messaging.MessagingException; - -/** - * A {@link MessageHandler} that updates {@link Metric} values through a - * {@link MetricWriter}. - * - * @author Dave Syer - * @see MessageChannelMetricWriter - */ -public final class MetricWriterMessageHandler implements MessageHandler { - - private static final Log logger = LogFactory.getLog(MetricWriterMessageHandler.class); - - private final MetricWriter observer; - - public MetricWriterMessageHandler(MetricWriter observer) { - this.observer = observer; - } - - @Override - public void handleMessage(Message message) throws MessagingException { - handleMessage(new MetricMessage(message)); - } - - private void handleMessage(MetricMessage message) { - Object payload = message.getPayload(); - if (message.isReset()) { - this.observer.reset(message.getMetricName()); - } - else if (payload instanceof Delta) { - Delta value = (Delta) payload; - this.observer.increment(value); - } - else if (payload instanceof Metric) { - Metric value = (Metric) payload; - this.observer.set(value); - } - else { - if (logger.isWarnEnabled()) { - logger.warn("Unsupported metric payload " - + (payload == null ? "null" : payload.getClass().getName())); - } - } - } - -} diff --git a/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/metrics/writer/PrefixMetricWriter.java b/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/metrics/writer/PrefixMetricWriter.java deleted file mode 100644 index 4619e6a49db6..000000000000 --- a/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/metrics/writer/PrefixMetricWriter.java +++ /dev/null @@ -1,54 +0,0 @@ -/* - * Copyright 2012-2015 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.boot.actuate.metrics.writer; - -import java.util.Collection; - -import org.springframework.boot.actuate.metrics.Metric; - -/** - * A writer for metrics that allows efficient storage of groups of metrics with a common - * name prefix (their group name). - * - * @author Dave Syer - * @since 1.1.0 - */ -public interface PrefixMetricWriter { - - /** - * Save some metric values and associate them with a group name. - * @param group the name of the group - * @param values the metric values to save - */ - void set(String group, Collection> values); - - /** - * Increment the value of a metric (or decrement if the delta is negative). The name - * of the metric to increment is {@code group + "." + delta.name}. - * @param group the name of the group - * @param delta the amount to increment by - */ - void increment(String group, Delta delta); - - /** - * Rest the values of all metrics in the group. Implementations may choose to discard - * the old values. - * @param group reset the whole group - */ - void reset(String group); - -} diff --git a/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/metrics/writer/package-info.java b/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/metrics/writer/package-info.java deleted file mode 100644 index 2753bddf998b..000000000000 --- a/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/metrics/writer/package-info.java +++ /dev/null @@ -1,20 +0,0 @@ -/* - * Copyright 2012-2015 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -/** - * Support for writing metrics. - */ -package org.springframework.boot.actuate.metrics.writer; diff --git a/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/cache/CachePublicMetricsTests.java b/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/cache/CachePublicMetricsTests.java deleted file mode 100644 index 391297d644b7..000000000000 --- a/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/cache/CachePublicMetricsTests.java +++ /dev/null @@ -1,110 +0,0 @@ -/* - * 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.util.Arrays; -import java.util.Collection; -import java.util.Collections; -import java.util.HashMap; -import java.util.Map; - -import org.junit.Before; -import org.junit.Test; - -import org.springframework.boot.actuate.metrics.Metric; -import org.springframework.cache.CacheManager; -import org.springframework.cache.concurrent.ConcurrentMapCache; -import org.springframework.cache.concurrent.ConcurrentMapCacheManager; -import org.springframework.cache.support.SimpleCacheManager; -import org.springframework.cache.transaction.TransactionAwareCacheDecorator; - -import static org.assertj.core.api.Assertions.assertThat; -import static org.assertj.core.api.Assertions.entry; - -/** - * Tests for {@link CachePublicMetrics} - * - * @author Stephane Nicoll - */ -public class CachePublicMetricsTests { - - private Map cacheManagers = new HashMap<>(); - - @Before - public void setup() { - this.cacheManagers.put("cacheManager", - new ConcurrentMapCacheManager("foo", "bar")); - } - - @Test - public void cacheMetricsWithMatchingProvider() { - CachePublicMetrics cpm = new CachePublicMetrics(this.cacheManagers, - providers(new ConcurrentMapCacheStatisticsProvider())); - Map metrics = metrics(cpm); - assertThat(metrics).containsOnly(entry("cache.foo.size", 0L), - entry("cache.bar.size", 0L)); - } - - @Test - public void cacheMetricsWithNoMatchingProvider() { - CachePublicMetrics cpm = new CachePublicMetrics(this.cacheManagers, - providers(new CaffeineCacheStatisticsProvider())); - Map metrics = metrics(cpm); - assertThat(metrics).isEmpty(); - } - - @Test - public void cacheMetricsWithMultipleCacheManagers() { - this.cacheManagers.put("anotherCacheManager", - new ConcurrentMapCacheManager("foo")); - CachePublicMetrics cpm = new CachePublicMetrics(this.cacheManagers, - providers(new ConcurrentMapCacheStatisticsProvider())); - Map metrics = metrics(cpm); - assertThat(metrics).containsOnly(entry("cache.cacheManager_foo.size", 0L), - entry("cache.bar.size", 0L), - entry("cache.anotherCacheManager_foo.size", 0L)); - } - - @Test - public void cacheMetricsWithTransactionAwareCacheDecorator() { - SimpleCacheManager cacheManager = new SimpleCacheManager(); - cacheManager.setCaches(Collections.singletonList( - new TransactionAwareCacheDecorator(new ConcurrentMapCache("foo")))); - cacheManager.afterPropertiesSet(); - this.cacheManagers.put("cacheManager", cacheManager); - CachePublicMetrics cpm = new CachePublicMetrics(this.cacheManagers, - providers(new ConcurrentMapCacheStatisticsProvider())); - Map metrics = metrics(cpm); - assertThat(metrics).containsOnly(entry("cache.foo.size", 0L)); - } - - private Map metrics(CachePublicMetrics cpm) { - Collection> metrics = cpm.metrics(); - assertThat(metrics).isNotNull(); - Map result = new HashMap<>(); - for (Metric metric : metrics) { - result.put(metric.getName(), metric.getValue()); - } - return result; - } - - private Collection> providers( - CacheStatisticsProvider... providers) { - return Arrays.asList(providers); - } - -} diff --git a/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/metrics/Iterables.java b/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/metrics/Iterables.java deleted file mode 100644 index 6f9562365b95..000000000000 --- a/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/metrics/Iterables.java +++ /dev/null @@ -1,38 +0,0 @@ -/* - * 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.metrics; - -import java.util.ArrayList; -import java.util.Collection; - -/** - * @author Dave Syer - */ -public abstract class Iterables { - - public static Collection collection(Iterable iterable) { - if (iterable instanceof Collection) { - return (Collection) iterable; - } - ArrayList list = new ArrayList<>(); - for (T t : iterable) { - list.add(t); - } - return list; - } - -} diff --git a/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/metrics/MetricsEndpointMvcIntegrationTests.java b/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/metrics/MetricsEndpointMvcIntegrationTests.java deleted file mode 100644 index b05f329f9eaa..000000000000 --- a/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/metrics/MetricsEndpointMvcIntegrationTests.java +++ /dev/null @@ -1,110 +0,0 @@ -/* - * 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.metrics; - -import java.util.Arrays; - -import org.junit.Test; -import org.junit.runner.RunWith; - -import org.springframework.boot.actuate.endpoint.web.test.WebEndpointRunners; -import org.springframework.context.annotation.Bean; -import org.springframework.context.annotation.Configuration; -import org.springframework.test.web.reactive.server.WebTestClient; - -/** - * Integration tests for {@link MetricsEndpoint} when exposed via Spring MVC - * - * @author Andy Wilkinson - * @author Sergei Egorov - */ -@RunWith(WebEndpointRunners.class) -public class MetricsEndpointMvcIntegrationTests { - - private static WebTestClient client; - - @Test - public void home() { - client.get().uri("/application/metrics").exchange().expectStatus().isOk() - .expectBody().jsonPath("foo").isEqualTo(1); - } - - @Test - public void specificMetric() { - client.get().uri("/application/metrics/foo").exchange().expectStatus().isOk() - .expectBody().jsonPath("foo").isEqualTo(1); - } - - @Test - public void specificMetricWithDot() throws Exception { - client.get().uri("/application/metrics/group2.a").exchange().expectStatus().isOk() - .expectBody().jsonPath("$.length()").isEqualTo(1).jsonPath("['group2.a']") - .isEqualTo("1"); - } - - @Test - public void specificMetricWithNameThatCouldBeMistakenForAPathExtension() { - client.get().uri("/application/metrics/bar.png").exchange().expectStatus().isOk() - .expectBody().jsonPath("['bar.png']").isEqualTo(1); - } - - @Test - public void specificMetricThatDoesNotExist() throws Exception { - client.get().uri("/application/metrics/bar").exchange().expectStatus() - .isNotFound(); - } - - @Test - public void regexAll() throws Exception { - client.get().uri("/application/metrics?pattern=.*").exchange().expectStatus() - .isOk().expectBody().jsonPath("$.length()").isEqualTo(6).jsonPath("foo") - .isEqualTo(1).jsonPath("['bar.png']").isEqualTo(1) - .jsonPath("['group1.a']").isEqualTo(1).jsonPath("['group1.b']") - .isEqualTo(1).jsonPath("['group2.a']").isEqualTo(1).jsonPath("group2_a") - .isEqualTo(1); - } - - @Test - public void regexGroupDot() throws Exception { - client.get().uri("/application/metrics?pattern=group%5B0-9%5D%5C..*").exchange() - .expectStatus().isOk().expectBody().jsonPath("$.length()").isEqualTo(3) - .jsonPath("['group1.a']").isEqualTo(1).jsonPath("['group1.b']") - .isEqualTo(1).jsonPath("['group2.a']").isEqualTo(1); - } - - @Test - public void regexGroup1() throws Exception { - client.get().uri("/application/metrics?pattern=group1%5C..*").exchange() - .expectStatus().isOk().expectBody().jsonPath("['group1.a']").isEqualTo(1) - .jsonPath("['group1.b']").isEqualTo(1).jsonPath("$.length()") - .isEqualTo(2); - } - - @Configuration - static class TestConfiguration { - - @Bean - public MetricsEndpoint endpoint() { - return new MetricsEndpoint(() -> Arrays.asList(new Metric<>("foo", 1), - new Metric<>("bar.png", 1), new Metric<>("group1.a", 1), - new Metric<>("group1.b", 1), new Metric<>("group2.a", 1), - new Metric<>("group2_a", 1), new Metric("baz", null))); - } - - } - -} diff --git a/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/metrics/MetricsEndpointTests.java b/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/metrics/MetricsEndpointTests.java deleted file mode 100644 index 3a6f7e8f119c..000000000000 --- a/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/metrics/MetricsEndpointTests.java +++ /dev/null @@ -1,126 +0,0 @@ -/* - * 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.metrics; - -import java.util.Arrays; -import java.util.Collection; -import java.util.Collections; -import java.util.List; -import java.util.Map; - -import org.junit.Test; - -import org.springframework.core.Ordered; - -import static org.assertj.core.api.Assertions.assertThat; - -/** - * Tests for {@link MetricsEndpoint}. - * - * @author Phillip Webb - * @author Andy Wilkinson - */ -public class MetricsEndpointTests { - - private Metric metric1 = new Metric<>("a", 1); - - private Metric metric2 = new Metric<>("b", 2); - - private Metric metric3 = new Metric<>("c", 3); - - @Test - public void basicMetrics() throws Exception { - MetricsEndpoint endpoint = new MetricsEndpoint( - Collections.singletonList(() -> Arrays.asList(new Metric("a", 5), - new Metric("b", 4)))); - Map metrics = endpoint.metrics(null); - assertThat(metrics.get("a")).isEqualTo(5); - assertThat(metrics.get("b")).isEqualTo(4); - } - - @Test - public void metricsOrderingIsReflectedInOutput() { - List publicMetrics = Arrays.asList( - new TestPublicMetrics(2, this.metric2, this.metric2, this.metric3), - new TestPublicMetrics(1, this.metric1)); - Map metrics = new MetricsEndpoint(publicMetrics).metrics(null); - assertThat(metrics.keySet()).containsExactly("a", "b", "c"); - assertThat(metrics).hasSize(3); - } - - @Test - public void singleSelectedMetric() { - List publicMetrics = Arrays.asList( - new TestPublicMetrics(2, this.metric2, this.metric2, this.metric3), - new TestPublicMetrics(1, this.metric1)); - Map selected = new MetricsEndpoint(publicMetrics) - .metricNamed("a"); - assertThat(selected).hasSize(1); - assertThat(selected).containsEntry("a", 1); - } - - @Test - public void multipleSelectedMetrics() { - List publicMetrics = Arrays.asList( - new TestPublicMetrics(2, this.metric2, this.metric2, this.metric3), - new TestPublicMetrics(1, this.metric1)); - Map selected = new MetricsEndpoint(publicMetrics).metrics("[ab]"); - assertThat(selected).hasSize(2); - assertThat(selected).containsEntry("a", 1); - assertThat(selected).containsEntry("b", 2); - } - - @Test - public void noMetricMatchingName() { - List publicMetrics = Arrays.asList( - new TestPublicMetrics(2, this.metric2, this.metric2, this.metric3), - new TestPublicMetrics(1, this.metric1)); - assertThat(new MetricsEndpoint(publicMetrics).metricNamed("z")).isNull(); - } - - @Test - public void noMetricMatchingPattern() { - List publicMetrics = Arrays.asList( - new TestPublicMetrics(2, this.metric2, this.metric2, this.metric3), - new TestPublicMetrics(1, this.metric1)); - assertThat(new MetricsEndpoint(publicMetrics).metrics("[z]")).isEmpty(); - } - - private static class TestPublicMetrics implements PublicMetrics, Ordered { - - private final int order; - - private final List> metrics; - - TestPublicMetrics(int order, Metric... metrics) { - this.order = order; - this.metrics = Arrays.asList(metrics); - } - - @Override - public int getOrder() { - return this.order; - } - - @Override - public Collection> metrics() { - return this.metrics; - } - - } - -} diff --git a/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/metrics/MetricsEndpointWebIntegrationTests.java b/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/metrics/MetricsEndpointWebIntegrationTests.java new file mode 100644 index 000000000000..d39e0ab4088e --- /dev/null +++ b/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/metrics/MetricsEndpointWebIntegrationTests.java @@ -0,0 +1,91 @@ +/* + * 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.metrics; + +import java.io.IOException; +import java.util.List; +import java.util.Map; + +import com.fasterxml.jackson.databind.ObjectMapper; +import io.micrometer.core.instrument.MeterRegistry; +import io.micrometer.core.instrument.binder.JvmMemoryMetrics; +import io.micrometer.core.instrument.simple.SimpleMeterRegistry; +import org.junit.Test; +import org.junit.runner.RunWith; + +import org.springframework.boot.actuate.endpoint.web.test.WebEndpointRunners; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.test.web.reactive.server.WebTestClient; + +import static org.assertj.core.api.Assertions.assertThat; + +/** + * Web integration tests for {@link MetricsEndpoint}. + * + * @author Jon Schneider + * @author Andy Wilkinson + */ +@RunWith(WebEndpointRunners.class) +public class MetricsEndpointWebIntegrationTests { + + private static WebTestClient client; + + private final ObjectMapper mapper = new ObjectMapper(); + + @SuppressWarnings("unchecked") + @Test + public void listNames() throws IOException { + String responseBody = MetricsEndpointWebIntegrationTests.client.get() + .uri("/application/metrics").exchange().expectStatus().isOk() + .expectBody(String.class).returnResult().getResponseBody(); + Map> names = this.mapper.readValue(responseBody, Map.class); + assertThat(names.get("names")).contains("jvm.memory.used"); + } + + @Test + public void selectByName() throws IOException { + MetricsEndpointWebIntegrationTests.client.get() + .uri("/application/metrics/jvm.memory.used").exchange().expectStatus() + .isOk().expectBody() + .jsonPath("['jvm_memory_used.area.nonheap.id.Compressed Class Space']") + .exists().jsonPath("['jvm_memory_used.area.heap.id.PS Old Gen']"); + } + + @Configuration + static class TestConfiguration { + + @Bean + public MeterRegistry registry() { + return new SimpleMeterRegistry(); + } + + @Bean + public MetricsEndpoint metricsEndpoint(MeterRegistry meterRegistry) { + return new MetricsEndpoint(meterRegistry); + } + + @Bean + public JvmMemoryMetrics jvmMemoryMetrics(MeterRegistry meterRegistry) { + JvmMemoryMetrics memoryMetrics = new JvmMemoryMetrics(); + memoryMetrics.bindTo(meterRegistry); + return memoryMetrics; + } + + } + +} diff --git a/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/metrics/SystemPublicMetricsTests.java b/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/metrics/SystemPublicMetricsTests.java deleted file mode 100644 index ee11f6f14057..000000000000 --- a/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/metrics/SystemPublicMetricsTests.java +++ /dev/null @@ -1,62 +0,0 @@ -/* - * 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.metrics; - -import java.util.HashMap; -import java.util.Map; - -import org.junit.Test; - -import static org.assertj.core.api.Assertions.assertThat; - -/** - * Tests for {@link SystemPublicMetrics} - * - * @author Stephane Nicoll - */ -public class SystemPublicMetricsTests { - - @Test - public void testSystemMetrics() throws Exception { - SystemPublicMetrics publicMetrics = new SystemPublicMetrics(); - Map> results = new HashMap<>(); - for (Metric metric : publicMetrics.metrics()) { - results.put(metric.getName(), metric); - } - assertThat(results).containsKey("mem"); - assertThat(results).containsKey("mem.free"); - assertThat(results).containsKey("processors"); - assertThat(results).containsKey("uptime"); - assertThat(results).containsKey("systemload.average"); - assertThat(results).containsKey("heap.committed"); - assertThat(results).containsKey("heap.init"); - assertThat(results).containsKey("heap.used"); - assertThat(results).containsKey("heap"); - assertThat(results).containsKey("nonheap.committed"); - assertThat(results).containsKey("nonheap.init"); - assertThat(results).containsKey("nonheap.used"); - assertThat(results).containsKey("nonheap"); - assertThat(results).containsKey("threads.peak"); - assertThat(results).containsKey("threads.daemon"); - assertThat(results).containsKey("threads.totalStarted"); - assertThat(results).containsKey("threads"); - assertThat(results).containsKey("classes.loaded"); - assertThat(results).containsKey("classes.unloaded"); - assertThat(results).containsKey("classes"); - } - -} diff --git a/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/metrics/TomcatPublicMetricsTests.java b/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/metrics/TomcatPublicMetricsTests.java deleted file mode 100644 index 5c634e1c329c..000000000000 --- a/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/metrics/TomcatPublicMetricsTests.java +++ /dev/null @@ -1,66 +0,0 @@ -/* - * 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.metrics; - -import java.util.Iterator; - -import org.junit.Test; - -import org.springframework.boot.web.embedded.tomcat.TomcatServletWebServerFactory; -import org.springframework.boot.web.servlet.context.AnnotationConfigServletWebServerApplicationContext; -import org.springframework.context.annotation.Bean; -import org.springframework.context.annotation.Configuration; - -import static org.assertj.core.api.Assertions.assertThat; - -/** - * Tests for {@link TomcatPublicMetrics} - * - * @author Johannes Edmeier - * @author Phillip Webb - */ -public class TomcatPublicMetricsTests { - - @Test - public void tomcatMetrics() throws Exception { - try (AnnotationConfigServletWebServerApplicationContext context = new AnnotationConfigServletWebServerApplicationContext( - Config.class)) { - TomcatPublicMetrics tomcatMetrics = context - .getBean(TomcatPublicMetrics.class); - Iterator> metrics = tomcatMetrics.metrics().iterator(); - assertThat(metrics.next().getName()).isEqualTo("httpsessions.max"); - assertThat(metrics.next().getName()).isEqualTo("httpsessions.active"); - assertThat(metrics.hasNext()).isFalse(); - } - } - - @Configuration - static class Config { - - @Bean - public TomcatServletWebServerFactory webServerFactory() { - return new TomcatServletWebServerFactory(0); - } - - @Bean - public TomcatPublicMetrics metrics() { - return new TomcatPublicMetrics(); - } - - } - -} diff --git a/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/metrics/aggregate/AggregateMetricReaderTests.java b/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/metrics/aggregate/AggregateMetricReaderTests.java deleted file mode 100644 index 870fd9d4abc2..000000000000 --- a/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/metrics/aggregate/AggregateMetricReaderTests.java +++ /dev/null @@ -1,114 +0,0 @@ -/* - * 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.metrics.aggregate; - -import java.util.Date; - -import org.junit.Test; - -import org.springframework.boot.actuate.metrics.Metric; -import org.springframework.boot.actuate.metrics.repository.InMemoryMetricRepository; -import org.springframework.boot.actuate.metrics.writer.Delta; - -import static org.assertj.core.api.Assertions.assertThat; - -/** - * Tests for {@link AggregateMetricReader}. - * - * @author Dave Syer - */ -public class AggregateMetricReaderTests { - - private InMemoryMetricRepository source = new InMemoryMetricRepository(); - - private AggregateMetricReader reader = new AggregateMetricReader(this.source); - - @Test - public void writeAndReadDefaults() { - this.source.set(new Metric<>("foo.bar.spam", 2.3)); - assertThat(this.reader.findOne("aggregate.spam").getValue()).isEqualTo(2.3); - } - - @Test - public void defaultKeyPattern() { - this.source.set(new Metric<>("foo.bar.spam.bucket.wham", 2.3)); - assertThat(this.reader.findOne("aggregate.spam.bucket.wham").getValue()) - .isEqualTo(2.3); - } - - @Test - public void addKeyPattern() { - this.source.set(new Metric<>("foo.bar.spam.bucket.wham", 2.3)); - this.reader.setKeyPattern("d.d.k.d"); - assertThat(this.reader.findOne("aggregate.spam.wham").getValue()).isEqualTo(2.3); - } - - @Test - public void addPrefix() { - this.source.set(new Metric<>("foo.bar.spam.bucket.wham", 2.3)); - this.source.set(new Metric<>("off.bar.spam.bucket.wham", 2.4)); - this.reader.setPrefix("www"); - this.reader.setKeyPattern("k.d.k.d"); - assertThat(this.reader.findOne("www.foo.spam.wham").getValue()).isEqualTo(2.3); - assertThat(this.reader.count()).isEqualTo(2); - } - - @Test - public void writeAndReadExtraLong() { - this.source.set(new Metric<>("blee.foo.bar.spam", 2.3)); - this.reader.setKeyPattern("d.d.d.k"); - assertThat(this.reader.findOne("aggregate.spam").getValue()).isEqualTo(2.3); - } - - @Test - public void writeAndReadLatestValue() { - this.source.set(new Metric<>("foo.bar.spam", 2.3, new Date(100L))); - this.source.set(new Metric<>("oof.rab.spam", 2.4, new Date(0L))); - assertThat(this.reader.findOne("aggregate.spam").getValue()).isEqualTo(2.3); - } - - @Test - public void onlyPrefixed() { - this.source.set(new Metric<>("foo.bar.spam", 2.3)); - assertThat(this.reader.findOne("spam")).isNull(); - } - - @Test - public void incrementCounter() { - this.source.increment(new Delta<>("foo.bar.counter.spam", 2L)); - this.source.increment(new Delta<>("oof.rab.counter.spam", 3L)); - assertThat(this.reader.findOne("aggregate.counter.spam").getValue()) - .isEqualTo(5L); - } - - @Test - public void countGauges() { - this.source.set(new Metric<>("foo.bar.spam", 2.3)); - this.source.set(new Metric<>("oof.rab.spam", 2.4)); - assertThat(this.reader.count()).isEqualTo(1); - } - - @Test - public void countGaugesAndCounters() { - this.source.set(new Metric<>("foo.bar.spam", 2.3)); - this.source.set(new Metric<>("oof.rab.spam", 2.4)); - this.source.increment(new Delta<>("foo.bar.counter.spam", 2L)); - this.source.increment(new Delta<>("oof.rab.counter.spam", 3L)); - assertThat(this.reader.count()).isEqualTo(2); - } - -} diff --git a/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/metrics/buffer/BufferGaugeServiceSpeedTests.java b/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/metrics/buffer/BufferGaugeServiceSpeedTests.java deleted file mode 100644 index 7aa995718a83..000000000000 --- a/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/metrics/buffer/BufferGaugeServiceSpeedTests.java +++ /dev/null @@ -1,141 +0,0 @@ -/* - * 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.metrics.buffer; - -import java.io.FileNotFoundException; -import java.io.PrintWriter; -import java.util.Collection; -import java.util.HashSet; -import java.util.Random; -import java.util.concurrent.ExecutorService; -import java.util.concurrent.Executors; -import java.util.concurrent.Future; -import java.util.concurrent.atomic.DoubleAdder; -import java.util.concurrent.atomic.LongAdder; -import java.util.regex.Pattern; - -import org.junit.AfterClass; -import org.junit.BeforeClass; -import org.junit.experimental.theories.DataPoints; -import org.junit.experimental.theories.Theories; -import org.junit.experimental.theories.Theory; -import org.junit.runner.RunWith; - -import org.springframework.boot.actuate.metrics.GaugeService; -import org.springframework.util.StopWatch; - -import static org.assertj.core.api.Assertions.assertThat; - -/** - * Speed tests for {@link BufferGaugeService}. - * - * @author Dave Syer - */ -@RunWith(Theories.class) -public class BufferGaugeServiceSpeedTests { - - @DataPoints - public static String[] values = new String[10]; - - public static String[] names = new String[] { "foo", "bar", "spam", "bucket" }; - - public static String[] sample = new String[1000]; - - private GaugeBuffers gauges = new GaugeBuffers(); - - private GaugeService service = new BufferGaugeService(this.gauges); - - private BufferMetricReader reader = new BufferMetricReader(new CounterBuffers(), - this.gauges); - - private static int threadCount = 2; - - private static final int number = Boolean.getBoolean("performance.test") ? 10000000 - : 1000000; - - private static StopWatch watch = new StopWatch("count"); - - private static int count; - - private static PrintWriter err; - - @BeforeClass - public static void prime() throws FileNotFoundException { - err = new NullPrintWriter(); - Random random = new Random(); - for (int i = 0; i < 1000; i++) { - sample[i] = names[random.nextInt(names.length)]; - } - } - - @AfterClass - public static void washup() { - System.err.println(watch); - } - - @Theory - public void raw(String input) throws Exception { - iterate("writeRaw"); - double rate = number / watch.getLastTaskTimeMillis() * 1000; - System.err.println("Rate(" + count + ")=" + rate + ", " + watch); - watch.start("readRaw" + count); - for (String name : names) { - this.gauges.forEach(Pattern.compile(name).asPredicate(), - (key, value) -> err.println(key + "=" + value)); - } - DoubleAdder total = new DoubleAdder(); - this.gauges.forEach(Pattern.compile(".*").asPredicate(), - (name, value) -> total.add(value.getValue())); - watch.stop(); - System.err.println("Read(" + count + ")=" + watch.getLastTaskTimeMillis() + "ms"); - assertThat(number * threadCount < total.longValue()).isTrue(); - } - - @Theory - public void reader(String input) throws Exception { - iterate("writeReader"); - double rate = number / watch.getLastTaskTimeMillis() * 1000; - System.err.println("Rate(" + count + ")=" + rate + ", " + watch); - watch.start("readReader" + count); - this.reader.findAll().forEach((metric) -> err.println(metric)); - LongAdder total = new LongAdder(); - this.reader.findAll().forEach((value) -> total.add(value.getValue().intValue())); - watch.stop(); - System.err.println("Read(" + count + ")=" + watch.getLastTaskTimeMillis() + "ms"); - assertThat(0 < total.longValue()).isTrue(); - } - - private void iterate(String taskName) throws Exception { - watch.start(taskName + count++); - ExecutorService pool = Executors.newFixedThreadPool(threadCount); - Runnable task = () -> { - for (int i = 0; i < number; i++) { - String name = sample[i % sample.length]; - BufferGaugeServiceSpeedTests.this.service.submit(name, count + i); - } - }; - Collection> futures = new HashSet<>(); - for (int i = 0; i < threadCount; i++) { - futures.add(pool.submit(task)); - } - for (Future future : futures) { - future.get(); - } - watch.stop(); - } - -} diff --git a/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/metrics/buffer/BufferMetricReaderTests.java b/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/metrics/buffer/BufferMetricReaderTests.java deleted file mode 100644 index 1e3ce1cd38dc..000000000000 --- a/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/metrics/buffer/BufferMetricReaderTests.java +++ /dev/null @@ -1,58 +0,0 @@ -/* - * Copyright 2012-2016 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.boot.actuate.metrics.buffer; - -import org.junit.Test; - -import static org.assertj.core.api.Assertions.assertThat; - -/** - * Tests for {@link BufferMetricReader}. - * - * @author Dave Syer - */ -public class BufferMetricReaderTests { - - private CounterBuffers counters = new CounterBuffers(); - - private GaugeBuffers gauges = new GaugeBuffers(); - - private BufferMetricReader reader = new BufferMetricReader(this.counters, - this.gauges); - - @Test - public void countReflectsNumberOfMetrics() { - this.gauges.set("foo", 1); - this.counters.increment("bar", 2); - assertThat(this.reader.count()).isEqualTo(2); - } - - @Test - public void findGauge() { - this.gauges.set("foo", 1); - assertThat(this.reader.findOne("foo")).isNotNull(); - assertThat(this.reader.count()).isEqualTo(1); - } - - @Test - public void findCounter() { - this.counters.increment("foo", 1); - assertThat(this.reader.findOne("foo")).isNotNull(); - assertThat(this.reader.count()).isEqualTo(1); - } - -} diff --git a/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/metrics/buffer/CounterBuffersTests.java b/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/metrics/buffer/CounterBuffersTests.java deleted file mode 100644 index 72b4a0f07f5e..000000000000 --- a/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/metrics/buffer/CounterBuffersTests.java +++ /dev/null @@ -1,54 +0,0 @@ -/* - * 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.metrics.buffer; - -import org.junit.Test; - -import static org.assertj.core.api.Assertions.assertThat; - -/** - * Tests for {@link CounterBuffers}. - * - * @author Dave Syer - */ -public class CounterBuffersTests { - - private CounterBuffers buffers = new CounterBuffers(); - - private long value; - - @Test - public void inAndOut() { - this.buffers.increment("foo", 2); - this.buffers.doWith("foo", - (buffer) -> CounterBuffersTests.this.value = buffer.getValue()); - assertThat(this.value).isEqualTo(2); - } - - @Test - public void getNonExistent() { - this.buffers.doWith("foo", - (buffer) -> CounterBuffersTests.this.value = buffer.getValue()); - assertThat(this.value).isEqualTo(0); - } - - @Test - public void findNonExistent() { - assertThat(this.buffers.find("foo")).isNull(); - } - -} diff --git a/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/metrics/buffer/CounterServiceSpeedTests.java b/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/metrics/buffer/CounterServiceSpeedTests.java deleted file mode 100644 index e3e93546495e..000000000000 --- a/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/metrics/buffer/CounterServiceSpeedTests.java +++ /dev/null @@ -1,140 +0,0 @@ -/* - * 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.metrics.buffer; - -import java.io.FileNotFoundException; -import java.io.PrintWriter; -import java.util.Collection; -import java.util.HashSet; -import java.util.Random; -import java.util.concurrent.ExecutorService; -import java.util.concurrent.Executors; -import java.util.concurrent.Future; -import java.util.concurrent.atomic.LongAdder; -import java.util.regex.Pattern; - -import org.junit.AfterClass; -import org.junit.BeforeClass; -import org.junit.experimental.theories.DataPoints; -import org.junit.experimental.theories.Theories; -import org.junit.experimental.theories.Theory; -import org.junit.runner.RunWith; - -import org.springframework.boot.actuate.metrics.CounterService; -import org.springframework.util.StopWatch; - -import static org.assertj.core.api.Assertions.assertThat; - -/** - * Speed tests for {@link CounterService}. - * - * @author Dave Syer - */ -@RunWith(Theories.class) -public class CounterServiceSpeedTests { - - @DataPoints - public static String[] values = new String[10]; - - public static String[] names = new String[] { "foo", "bar", "spam", "bucket" }; - - public static String[] sample = new String[1000]; - - private CounterBuffers counters = new CounterBuffers(); - - private CounterService service = new BufferCounterService(this.counters); - - private BufferMetricReader reader = new BufferMetricReader(this.counters, - new GaugeBuffers()); - - private static int threadCount = 2; - - private static final int number = Boolean.getBoolean("performance.test") ? 10000000 - : 1000000; - - private static StopWatch watch = new StopWatch("count"); - - private static int count; - - private static PrintWriter err; - - @BeforeClass - public static void prime() throws FileNotFoundException { - err = new NullPrintWriter(); - Random random = new Random(); - for (int i = 0; i < 1000; i++) { - sample[i] = names[random.nextInt(names.length)]; - } - } - - @AfterClass - public static void washup() { - System.err.println(watch); - } - - @Theory - public void raw(String input) throws Exception { - iterate("writeRaw"); - double rate = number / watch.getLastTaskTimeMillis() * 1000; - System.err.println("Rate(" + count + ")=" + rate + ", " + watch); - watch.start("readRaw" + count); - for (String name : names) { - this.counters.forEach(Pattern.compile(name).asPredicate(), - (key, value) -> err.println(key + "=" + value)); - } - LongAdder total = new LongAdder(); - this.counters.forEach(Pattern.compile(".*").asPredicate(), - (name, value) -> total.add(value.getValue())); - watch.stop(); - System.err.println("Read(" + count + ")=" + watch.getLastTaskTimeMillis() + "ms"); - assertThat(total.longValue()).isEqualTo(number * threadCount); - } - - @Theory - public void reader(String input) throws Exception { - iterate("writeReader"); - double rate = number / watch.getLastTaskTimeMillis() * 1000; - System.err.println("Rate(" + count + ")=" + rate + ", " + watch); - watch.start("readReader" + count); - this.reader.findAll().forEach(err::println); - LongAdder total = new LongAdder(); - this.reader.findAll().forEach((value) -> total.add(value.getValue().intValue())); - watch.stop(); - System.err.println("Read(" + count + ")=" + watch.getLastTaskTimeMillis() + "ms"); - assertThat(total.longValue()).isEqualTo(number * threadCount); - } - - private void iterate(String taskName) throws Exception { - watch.start(taskName + count++); - ExecutorService pool = Executors.newFixedThreadPool(threadCount); - Runnable task = () -> { - for (int i = 0; i < number; i++) { - String name = sample[i % sample.length]; - CounterServiceSpeedTests.this.service.increment(name); - } - }; - Collection> futures = new HashSet<>(); - for (int i = 0; i < threadCount; i++) { - futures.add(pool.submit(task)); - } - for (Future future : futures) { - future.get(); - } - watch.stop(); - } - -} diff --git a/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/metrics/buffer/DefaultCounterServiceSpeedTests.java b/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/metrics/buffer/DefaultCounterServiceSpeedTests.java deleted file mode 100644 index d4e6b37f812b..000000000000 --- a/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/metrics/buffer/DefaultCounterServiceSpeedTests.java +++ /dev/null @@ -1,113 +0,0 @@ -/* - * 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.metrics.buffer; - -import java.io.FileNotFoundException; -import java.io.PrintWriter; -import java.util.Collection; -import java.util.HashSet; -import java.util.Random; -import java.util.concurrent.ExecutorService; -import java.util.concurrent.Executors; -import java.util.concurrent.Future; -import java.util.concurrent.atomic.LongAdder; - -import org.junit.BeforeClass; -import org.junit.experimental.theories.DataPoints; -import org.junit.experimental.theories.Theories; -import org.junit.experimental.theories.Theory; -import org.junit.runner.RunWith; - -import org.springframework.boot.actuate.metrics.CounterService; -import org.springframework.boot.actuate.metrics.reader.MetricReader; -import org.springframework.boot.actuate.metrics.repository.InMemoryMetricRepository; -import org.springframework.boot.actuate.metrics.writer.DefaultCounterService; -import org.springframework.util.StopWatch; - -import static org.assertj.core.api.Assertions.assertThat; - -/** - * Speed tests for {@link DefaultCounterService}. - * - * @author Dave Syer - */ -@RunWith(Theories.class) -public class DefaultCounterServiceSpeedTests { - - @DataPoints - public static String[] values = new String[10]; - - public static String[] names = new String[] { "foo", "bar", "spam", "bucket" }; - - public static String[] sample = new String[1000]; - - private InMemoryMetricRepository repository = new InMemoryMetricRepository(); - - private CounterService counterService = new DefaultCounterService(this.repository); - - private MetricReader reader = this.repository; - - private static int threadCount = 2; - - private static final int number = Boolean.getBoolean("performance.test") ? 2000000 - : 1000000; - - private static int count; - - private static StopWatch watch = new StopWatch("count"); - - private static PrintWriter err; - - @BeforeClass - public static void prime() throws FileNotFoundException { - err = new NullPrintWriter(); - final Random random = new Random(); - for (int i = 0; i < 1000; i++) { - sample[i] = names[random.nextInt(names.length)]; - } - } - - @Theory - public void counters(String input) throws Exception { - watch.start("counters" + count++); - ExecutorService pool = Executors.newFixedThreadPool(threadCount); - Runnable task = () -> { - for (int i = 0; i < number; i++) { - String name = sample[i % sample.length]; - DefaultCounterServiceSpeedTests.this.counterService.increment(name); - } - }; - Collection> futures = new HashSet<>(); - for (int i = 0; i < threadCount; i++) { - futures.add(pool.submit(task)); - } - for (Future future : futures) { - future.get(); - } - watch.stop(); - double rate = number / watch.getLastTaskTimeMillis() * 1000; - System.err.println("Counters rate(" + count + ")=" + rate + ", " + watch); - watch.start("read" + count); - this.reader.findAll().forEach(err::println); - LongAdder total = new LongAdder(); - this.reader.findAll().forEach((value) -> total.add(value.getValue().intValue())); - watch.stop(); - System.err.println("Read(" + count + ")=" + watch.getLastTaskTimeMillis() + "ms"); - assertThat(total.longValue()).isEqualTo(number * threadCount); - } - -} diff --git a/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/metrics/buffer/DefaultGaugeServiceSpeedTests.java b/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/metrics/buffer/DefaultGaugeServiceSpeedTests.java deleted file mode 100644 index b8d8398d9dfb..000000000000 --- a/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/metrics/buffer/DefaultGaugeServiceSpeedTests.java +++ /dev/null @@ -1,113 +0,0 @@ -/* - * 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.metrics.buffer; - -import java.io.FileNotFoundException; -import java.io.PrintWriter; -import java.util.Collection; -import java.util.HashSet; -import java.util.Random; -import java.util.concurrent.ExecutorService; -import java.util.concurrent.Executors; -import java.util.concurrent.Future; -import java.util.concurrent.atomic.LongAdder; - -import org.junit.BeforeClass; -import org.junit.experimental.theories.DataPoints; -import org.junit.experimental.theories.Theories; -import org.junit.experimental.theories.Theory; -import org.junit.runner.RunWith; - -import org.springframework.boot.actuate.metrics.GaugeService; -import org.springframework.boot.actuate.metrics.reader.MetricReader; -import org.springframework.boot.actuate.metrics.repository.InMemoryMetricRepository; -import org.springframework.boot.actuate.metrics.writer.DefaultGaugeService; -import org.springframework.util.StopWatch; - -import static org.assertj.core.api.Assertions.assertThat; - -/** - * Speed tests for {@link DefaultGaugeService}. - * - * @author Dave Syer - */ -@RunWith(Theories.class) -public class DefaultGaugeServiceSpeedTests { - - @DataPoints - public static String[] values = new String[10]; - - public static String[] names = new String[] { "foo", "bar", "spam", "bucket" }; - - public static String[] sample = new String[1000]; - - private InMemoryMetricRepository repository = new InMemoryMetricRepository(); - - private GaugeService gaugeService = new DefaultGaugeService(this.repository); - - private MetricReader reader = this.repository; - - private static int threadCount = 2; - - private static final int number = Boolean.getBoolean("performance.test") ? 5000000 - : 1000000; - - private static int count; - - private static StopWatch watch = new StopWatch("count"); - - private static PrintWriter err; - - @BeforeClass - public static void prime() throws FileNotFoundException { - err = new NullPrintWriter(); - Random random = new Random(); - for (int i = 0; i < 1000; i++) { - sample[i] = names[random.nextInt(names.length)]; - } - } - - @Theory - public void gauges(String input) throws Exception { - watch.start("gauges" + count++); - ExecutorService pool = Executors.newFixedThreadPool(threadCount); - Runnable task = () -> { - for (int i = 0; i < number; i++) { - String name = sample[i % sample.length]; - DefaultGaugeServiceSpeedTests.this.gaugeService.submit(name, count + i); - } - }; - Collection> futures = new HashSet<>(); - for (int i = 0; i < threadCount; i++) { - futures.add(pool.submit(task)); - } - for (Future future : futures) { - future.get(); - } - watch.stop(); - double rate = number / watch.getLastTaskTimeMillis() * 1000; - System.err.println("Gauges rate(" + count + ")=" + rate + ", " + watch); - watch.start("read" + count); - this.reader.findAll().forEach(err::println); - LongAdder total = new LongAdder(); - this.reader.findAll().forEach((value) -> total.add(value.getValue().intValue())); - watch.stop(); - System.err.println("Read(" + count + ")=" + watch.getLastTaskTimeMillis() + "ms"); - assertThat(0 < total.longValue()).isTrue(); - } - -} diff --git a/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/metrics/buffer/DropwizardCounterServiceSpeedTests.java b/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/metrics/buffer/DropwizardCounterServiceSpeedTests.java deleted file mode 100644 index bb1c9588612f..000000000000 --- a/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/metrics/buffer/DropwizardCounterServiceSpeedTests.java +++ /dev/null @@ -1,115 +0,0 @@ -/* - * 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.metrics.buffer; - -import java.io.FileNotFoundException; -import java.io.PrintWriter; -import java.util.Collection; -import java.util.HashSet; -import java.util.Random; -import java.util.concurrent.ExecutorService; -import java.util.concurrent.Executors; -import java.util.concurrent.Future; -import java.util.concurrent.atomic.LongAdder; - -import com.codahale.metrics.MetricRegistry; -import org.junit.BeforeClass; -import org.junit.experimental.theories.DataPoints; -import org.junit.experimental.theories.Theories; -import org.junit.experimental.theories.Theory; -import org.junit.runner.RunWith; - -import org.springframework.boot.actuate.metrics.CounterService; -import org.springframework.boot.actuate.metrics.dropwizard.DropwizardMetricServices; -import org.springframework.boot.actuate.metrics.reader.MetricReader; -import org.springframework.boot.actuate.metrics.reader.MetricRegistryMetricReader; -import org.springframework.util.StopWatch; - -import static org.assertj.core.api.Assertions.assertThat; - -/** - * Speeds tests for {@link DropwizardMetricServices DropwizardMetricServices'} - * {@link CounterService}. - * - * @author Dave Syer - */ -@RunWith(Theories.class) -public class DropwizardCounterServiceSpeedTests { - - @DataPoints - public static String[] values = new String[10]; - - public static String[] names = new String[] { "foo", "bar", "spam", "bucket" }; - - public static String[] sample = new String[1000]; - - private MetricRegistry registry = new MetricRegistry(); - - private CounterService counterService = new DropwizardMetricServices(this.registry); - - private MetricReader reader = new MetricRegistryMetricReader(this.registry); - - private static int threadCount = 2; - - private static final int number = Boolean.getBoolean("performance.test") ? 10000000 - : 1000000; - - private static int count; - - private static StopWatch watch = new StopWatch("count"); - - private static PrintWriter err; - - @BeforeClass - public static void prime() throws FileNotFoundException { - err = new NullPrintWriter(); - Random random = new Random(); - for (int i = 0; i < 1000; i++) { - sample[i] = names[random.nextInt(names.length)]; - } - } - - @Theory - public void counters(String input) throws Exception { - watch.start("counters" + count++); - ExecutorService pool = Executors.newFixedThreadPool(threadCount); - Runnable task = () -> { - for (int i = 0; i < number; i++) { - String name = sample[i % sample.length]; - DropwizardCounterServiceSpeedTests.this.counterService.increment(name); - } - }; - Collection> futures = new HashSet<>(); - for (int i = 0; i < threadCount; i++) { - futures.add(pool.submit(task)); - } - for (Future future : futures) { - future.get(); - } - watch.stop(); - double rate = number / watch.getLastTaskTimeMillis() * 1000; - System.err.println("Counters rate(" + count + ")=" + rate + ", " + watch); - watch.start("read" + count); - this.reader.findAll().forEach(err::println); - LongAdder total = new LongAdder(); - this.reader.findAll().forEach((value) -> total.add(value.getValue().intValue())); - watch.stop(); - System.err.println("Read(" + count + ")=" + watch.getLastTaskTimeMillis() + "ms"); - assertThat(total.longValue()).isEqualTo(number * threadCount); - } - -} diff --git a/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/metrics/buffer/NullPrintWriter.java b/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/metrics/buffer/NullPrintWriter.java deleted file mode 100644 index 39cc92f8b6c6..000000000000 --- a/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/metrics/buffer/NullPrintWriter.java +++ /dev/null @@ -1,39 +0,0 @@ -/* - * Copyright 2012-2015 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.boot.actuate.metrics.buffer; - -import java.io.File; -import java.io.FileNotFoundException; -import java.io.PrintWriter; - -/** - * A {@link PrintWriter} that writes to {@code NUL} on Windows and {@code /dev/null} on - * all other platforms. - * - * @author Andy Wilkinson - */ -public class NullPrintWriter extends PrintWriter { - - public NullPrintWriter() throws FileNotFoundException { - super(isWindows() ? "NUL" : "/dev/null"); - } - - private static boolean isWindows() { - return File.separatorChar == '\\'; - } - -} diff --git a/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/metrics/dropwizard/DropwizardMetricServicesTests.java b/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/metrics/dropwizard/DropwizardMetricServicesTests.java deleted file mode 100644 index 83485f2aa3b7..000000000000 --- a/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/metrics/dropwizard/DropwizardMetricServicesTests.java +++ /dev/null @@ -1,196 +0,0 @@ -/* - * 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.metrics.dropwizard; - -import java.util.ArrayList; -import java.util.List; - -import com.codahale.metrics.Gauge; -import com.codahale.metrics.Histogram; -import com.codahale.metrics.MetricRegistry; -import com.codahale.metrics.Timer; -import com.codahale.metrics.UniformReservoir; -import org.junit.Before; -import org.junit.Test; -import org.mockito.Mock; -import org.mockito.MockitoAnnotations; - -import org.springframework.test.util.ReflectionTestUtils; - -import static org.assertj.core.api.Assertions.assertThat; -import static org.mockito.ArgumentMatchers.anyString; -import static org.mockito.BDDMockito.given; - -/** - * Tests for {@link DropwizardMetricServices}. - * - * @author Dave Syer - * @author Lucas Saldanha - */ -public class DropwizardMetricServicesTests { - - private MetricRegistry registry = new MetricRegistry(); - - @Mock - private ReservoirFactory reservoirFactory; - - private DropwizardMetricServices writer; - - @Before - public void setup() { - MockitoAnnotations.initMocks(this); - this.writer = new DropwizardMetricServices(this.registry, this.reservoirFactory); - } - - @Test - public void incrementCounter() { - this.writer.increment("foo"); - this.writer.increment("foo"); - this.writer.increment("foo"); - assertThat(this.registry.counter("counter.foo").getCount()).isEqualTo(3); - } - - @Test - public void updatePredefinedMeter() { - this.writer.increment("meter.foo"); - this.writer.increment("meter.foo"); - this.writer.increment("meter.foo"); - assertThat(this.registry.meter("meter.foo").getCount()).isEqualTo(3); - } - - @Test - public void updatePredefinedCounter() { - this.writer.increment("counter.foo"); - this.writer.increment("counter.foo"); - this.writer.increment("counter.foo"); - assertThat(this.registry.counter("counter.foo").getCount()).isEqualTo(3); - } - - @Test - public void setGauge() { - this.writer.submit("foo", 2.1); - @SuppressWarnings("unchecked") - Gauge gauge = (Gauge) this.registry.getMetrics().get("gauge.foo"); - assertThat(gauge.getValue()).isEqualTo(new Double(2.1)); - this.writer.submit("foo", 2.3); - assertThat(gauge.getValue()).isEqualTo(new Double(2.3)); - } - - @Test - public void setPredefinedTimer() { - this.writer.submit("timer.foo", 200); - this.writer.submit("timer.foo", 300); - assertThat(this.registry.timer("timer.foo").getCount()).isEqualTo(2); - } - - @Test - public void setCustomReservoirTimer() { - given(this.reservoirFactory.getReservoir(anyString())) - .willReturn(new UniformReservoir()); - this.writer.submit("timer.foo", 200); - this.writer.submit("timer.foo", 300); - assertThat(this.registry.timer("timer.foo").getCount()).isEqualTo(2); - Timer timer = (Timer) this.registry.getMetrics().get("timer.foo"); - Histogram histogram = (Histogram) ReflectionTestUtils.getField(timer, - "histogram"); - assertThat(ReflectionTestUtils.getField(histogram, "reservoir").getClass() - .equals(UniformReservoir.class)).isTrue(); - } - - @Test - public void setPredefinedHistogram() { - this.writer.submit("histogram.foo", 2.1); - this.writer.submit("histogram.foo", 2.3); - assertThat(this.registry.histogram("histogram.foo").getCount()).isEqualTo(2); - } - - @Test - public void setCustomReservoirHistogram() { - given(this.reservoirFactory.getReservoir(anyString())) - .willReturn(new UniformReservoir()); - this.writer.submit("histogram.foo", 2.1); - this.writer.submit("histogram.foo", 2.3); - assertThat(this.registry.histogram("histogram.foo").getCount()).isEqualTo(2); - assertThat(ReflectionTestUtils - .getField(this.registry.getMetrics().get("histogram.foo"), "reservoir") - .getClass().equals(UniformReservoir.class)).isTrue(); - } - - /** - * Test the case where a given writer is used amongst several threads where each - * thread is updating the same set of metrics. This would be an example case of the - * writer being used with the MetricsFilter handling several requests/sec to the same - * URL. - * @throws Exception if an error occurs - */ - @Test - public void testParallelism() throws Exception { - List threads = new ArrayList<>(); - ThreadGroup group = new ThreadGroup("threads"); - for (int i = 0; i < 10; i++) { - WriterThread thread = new WriterThread(group, i, this.writer); - threads.add(thread); - thread.start(); - } - - while (group.activeCount() > 0) { - Thread.sleep(1000); - } - - for (WriterThread thread : threads) { - assertThat(thread.isFailed()) - .as("expected thread caused unexpected exception").isFalse(); - } - } - - public static class WriterThread extends Thread { - - private int index; - - private boolean failed; - - private DropwizardMetricServices writer; - - public WriterThread(ThreadGroup group, int index, - DropwizardMetricServices writer) { - super(group, "Writer-" + index); - this.index = index; - this.writer = writer; - } - - public boolean isFailed() { - return this.failed; - } - - @Override - public void run() { - for (int i = 0; i < 10000; i++) { - try { - this.writer.submit("timer.test.service", this.index); - this.writer.submit("histogram.test.service", this.index); - this.writer.submit("gauge.test.service", this.index); - } - catch (IllegalArgumentException ex) { - this.failed = true; - throw ex; - } - } - } - - } - -} diff --git a/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/metrics/export/MetricCopyExporterTests.java b/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/metrics/export/MetricCopyExporterTests.java deleted file mode 100644 index 40b61f6bbebe..000000000000 --- a/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/metrics/export/MetricCopyExporterTests.java +++ /dev/null @@ -1,135 +0,0 @@ -/* - * 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.metrics.export; - -import java.util.Date; - -import org.junit.Test; - -import org.springframework.boot.actuate.metrics.Metric; -import org.springframework.boot.actuate.metrics.repository.InMemoryMetricRepository; -import org.springframework.boot.actuate.metrics.writer.Delta; -import org.springframework.boot.actuate.metrics.writer.GaugeWriter; - -import static org.assertj.core.api.Assertions.assertThat; - -/** - * Tests for {@link MetricCopyExporter}. - * - * @author Dave Syer - */ -public class MetricCopyExporterTests { - - private final InMemoryMetricRepository writer = new InMemoryMetricRepository(); - - private final InMemoryMetricRepository reader = new InMemoryMetricRepository(); - - private final MetricCopyExporter exporter = new MetricCopyExporter(this.reader, - this.writer); - - @Test - public void export() { - this.reader.set(new Metric("foo", 2.3)); - this.exporter.export(); - assertThat(this.writer.count()).isEqualTo(1); - } - - @Test - public void counter() { - this.reader.increment(new Delta("counter.foo", 2)); - this.exporter.export(); - assertThat(this.writer.count()).isEqualTo(1); - this.reader.increment(new Delta("counter.foo", 3)); - this.exporter.export(); - this.exporter.flush(); - assertThat(this.writer.findOne("counter.foo").getValue()).isEqualTo(5L); - } - - @Test - public void counterWithGaugeWriter() throws Exception { - SimpleGaugeWriter writer = new SimpleGaugeWriter(); - try (MetricCopyExporter customExporter = new MetricCopyExporter(this.reader, - writer)) { - this.reader.increment(new Delta("counter.foo", 2)); - customExporter.export(); - this.reader.increment(new Delta("counter.foo", 3)); - customExporter.export(); - customExporter.flush(); - assertThat(writer.getValue().getValue()).isEqualTo(5L); - } - } - - @Test - public void exportIncludes() { - this.exporter.setIncludes("*"); - this.reader.set(new Metric("foo", 2.3)); - this.exporter.export(); - assertThat(this.writer.count()).isEqualTo(1); - } - - @Test - public void exportExcludesWithIncludes() { - this.exporter.setIncludes("*"); - this.exporter.setExcludes("foo"); - this.reader.set(new Metric("foo", 2.3)); - this.reader.set(new Metric("bar", 2.4)); - this.exporter.export(); - assertThat(this.writer.count()).isEqualTo(1); - } - - @Test - public void exportExcludesDefaultIncludes() { - this.exporter.setExcludes("foo"); - this.reader.set(new Metric("foo", 2.3)); - this.reader.set(new Metric("bar", 2.4)); - this.exporter.export(); - assertThat(this.writer.count()).isEqualTo(1); - } - - @Test - public void timestamp() { - this.reader.set(new Metric("foo", 2.3)); - this.exporter.setEarliestTimestamp(new Date(System.currentTimeMillis() + 10000)); - this.exporter.export(); - assertThat(this.writer.count()).isEqualTo(0); - } - - @Test - public void ignoreTimestamp() { - this.reader.set(new Metric("foo", 2.3)); - this.exporter.setIgnoreTimestamps(true); - this.exporter.setEarliestTimestamp(new Date(System.currentTimeMillis() + 10000)); - this.exporter.export(); - assertThat(this.writer.count()).isEqualTo(1); - } - - private static class SimpleGaugeWriter implements GaugeWriter { - - private Metric value; - - @Override - public void set(Metric value) { - this.value = value; - } - - public Metric getValue() { - return this.value; - } - - } - -} diff --git a/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/metrics/export/MetricExportersTests.java b/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/metrics/export/MetricExportersTests.java deleted file mode 100644 index 200a46439280..000000000000 --- a/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/metrics/export/MetricExportersTests.java +++ /dev/null @@ -1,83 +0,0 @@ -/* - * 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.metrics.export; - -import java.util.Collections; -import java.util.LinkedHashMap; -import java.util.Map; - -import org.junit.Test; - -import org.springframework.boot.actuate.metrics.reader.MetricReader; -import org.springframework.boot.actuate.metrics.writer.GaugeWriter; -import org.springframework.boot.actuate.metrics.writer.MetricWriter; -import org.springframework.scheduling.config.ScheduledTaskRegistrar; - -import static org.assertj.core.api.Assertions.assertThat; -import static org.mockito.Mockito.mock; - -/** - * Tests for {@link MetricExporters}. - * - * @author Dave Syer - */ -public class MetricExportersTests { - - private MetricExporters exporters; - - private MetricExportProperties export = new MetricExportProperties(); - - private Map writers = new LinkedHashMap<>(); - - private MetricReader reader = mock(MetricReader.class); - - private MetricWriter writer = mock(MetricWriter.class); - - @Test - public void emptyWriters() { - this.exporters = new MetricExporters(this.export); - this.exporters.setReader(this.reader); - this.exporters.setWriters(this.writers); - this.exporters.configureTasks(new ScheduledTaskRegistrar()); - assertThat(this.exporters.getExporters()).isNotNull(); - assertThat(this.exporters.getExporters()).isEmpty(); - } - - @Test - public void oneWriter() { - this.export.setUpDefaults(); - this.writers.put("foo", this.writer); - this.exporters = new MetricExporters(this.export); - this.exporters.setReader(this.reader); - this.exporters.setWriters(this.writers); - this.exporters.configureTasks(new ScheduledTaskRegistrar()); - assertThat(this.exporters.getExporters()).isNotNull(); - assertThat(this.exporters.getExporters()).hasSize(1); - } - - @Test - public void exporter() { - this.export.setUpDefaults(); - this.exporters = new MetricExporters(this.export); - this.exporters.setExporters(Collections.singletonMap("foo", - new MetricCopyExporter(this.reader, this.writer))); - this.exporters.configureTasks(new ScheduledTaskRegistrar()); - assertThat(this.exporters.getExporters()).isNotNull(); - assertThat(this.exporters.getExporters()).hasSize(1); - } - -} diff --git a/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/metrics/export/PrefixMetricGroupExporterTests.java b/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/metrics/export/PrefixMetricGroupExporterTests.java deleted file mode 100644 index 164801ca64af..000000000000 --- a/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/metrics/export/PrefixMetricGroupExporterTests.java +++ /dev/null @@ -1,94 +0,0 @@ -/* - * 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.metrics.export; - -import java.util.Arrays; -import java.util.Collections; - -import org.junit.Test; - -import org.springframework.boot.actuate.metrics.Iterables; -import org.springframework.boot.actuate.metrics.Metric; -import org.springframework.boot.actuate.metrics.repository.InMemoryMultiMetricRepository; -import org.springframework.boot.actuate.metrics.writer.Delta; - -import static org.assertj.core.api.Assertions.assertThat; - -/** - * Tests for {@link PrefixMetricGroupExporter}. - * - * @author Dave Syer - */ -public class PrefixMetricGroupExporterTests { - - private final InMemoryMultiMetricRepository reader = new InMemoryMultiMetricRepository(); - - private final InMemoryMultiMetricRepository writer = new InMemoryMultiMetricRepository(); - - private final PrefixMetricGroupExporter exporter = new PrefixMetricGroupExporter( - this.reader, this.writer); - - @Test - public void prefixedMetricsCopied() { - this.reader.set("foo", Arrays.>asList(new Metric("bar", 2.3), - new Metric("spam", 1.3))); - this.exporter.setGroups(Collections.singleton("foo")); - this.exporter.export(); - assertThat(Iterables.collection(this.writer.groups())).hasSize(1); - } - - @Test - public void countersIncremented() { - this.writer.increment("counter.foo", new Delta<>("bar", 1L)); - this.reader.set("counter", Collections - .>singletonList(new Metric("counter.foo.bar", 1))); - this.exporter.setGroups(Collections.singleton("counter.foo")); - this.exporter.export(); - assertThat(this.writer.findAll("counter.foo").iterator().next().getValue()) - .isEqualTo(2L); - } - - @Test - public void unprefixedMetricsNotCopied() { - this.reader.set("foo", Arrays.>asList( - new Metric("foo.bar", 2.3), new Metric("foo.spam", 1.3))); - this.exporter.setGroups(Collections.singleton("bar")); - this.exporter.export(); - assertThat(Iterables.collection(this.writer.groups())).isEmpty(); - } - - @Test - public void multiMetricGroupsCopiedAsDefault() { - this.reader.set("foo", Arrays.>asList(new Metric("bar", 2.3), - new Metric("spam", 1.3))); - this.exporter.export(); - assertThat(this.writer.countGroups()).isEqualTo(1); - assertThat(Iterables.collection(this.writer.findAll("foo"))).hasSize(2); - } - - @Test - public void onlyPrefixedMetricsCopied() { - this.reader.set("foo", Arrays.>asList( - new Metric("foo.bar", 2.3), new Metric("foo.spam", 1.3))); - this.reader.set("foobar", Collections - .>singletonList(new Metric("foobar.spam", 1.3))); - this.exporter.setGroups(Collections.singleton("foo")); - this.exporter.export(); - assertThat(Iterables.collection(this.writer.groups())).hasSize(1); - } - -} diff --git a/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/metrics/export/RichGaugeExporterTests.java b/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/metrics/export/RichGaugeExporterTests.java deleted file mode 100644 index d77b10ea9345..000000000000 --- a/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/metrics/export/RichGaugeExporterTests.java +++ /dev/null @@ -1,49 +0,0 @@ -/* - * Copyright 2012-2016 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.boot.actuate.metrics.export; - -import org.junit.Test; - -import org.springframework.boot.actuate.metrics.Iterables; -import org.springframework.boot.actuate.metrics.Metric; -import org.springframework.boot.actuate.metrics.repository.InMemoryMultiMetricRepository; -import org.springframework.boot.actuate.metrics.rich.InMemoryRichGaugeRepository; - -import static org.assertj.core.api.Assertions.assertThat; - -/** - * Tests for {@link RichGaugeExporter}. - * - * @author Dave Syer - */ -public class RichGaugeExporterTests { - - private final InMemoryRichGaugeRepository reader = new InMemoryRichGaugeRepository(); - - private final InMemoryMultiMetricRepository writer = new InMemoryMultiMetricRepository(); - - private final RichGaugeExporter exporter = new RichGaugeExporter(this.reader, - this.writer); - - @Test - public void prefixedMetricsCopied() { - this.reader.set(new Metric("foo", 2.3)); - this.exporter.export(); - assertThat(Iterables.collection(this.writer.groups())).hasSize(1); - } - -} diff --git a/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/metrics/export/prometheus/PrometheusScrapeEndpointIntegrationTests.java b/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/metrics/export/prometheus/PrometheusScrapeEndpointIntegrationTests.java new file mode 100644 index 000000000000..3ad537e10cee --- /dev/null +++ b/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/metrics/export/prometheus/PrometheusScrapeEndpointIntegrationTests.java @@ -0,0 +1,71 @@ +/* + * 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.metrics.export.prometheus; + +import io.micrometer.core.instrument.Clock; +import io.micrometer.core.instrument.MeterRegistry; +import io.micrometer.prometheus.PrometheusMeterRegistry; +import io.prometheus.client.CollectorRegistry; +import io.prometheus.client.exporter.common.TextFormat; +import org.junit.Test; +import org.junit.runner.RunWith; + +import org.springframework.boot.actuate.endpoint.web.test.WebEndpointRunners; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.http.MediaType; +import org.springframework.test.web.reactive.server.WebTestClient; + +/** + * Tests for {@link PrometheusScrapeEndpoint}. + * + * @author Jon Schneider + */ +@RunWith(WebEndpointRunners.class) +public class PrometheusScrapeEndpointIntegrationTests { + + private static WebTestClient client; + + @Test + public void scrapeHasContentTypeText004() { + client.get().uri("/application/prometheus").exchange().expectStatus().isOk() + .expectHeader() + .contentType(MediaType.parseMediaType(TextFormat.CONTENT_TYPE_004)); + } + + @Configuration + static class TestConfiguration { + + @Bean + public PrometheusScrapeEndpoint prometheusScrapeEndpoint( + CollectorRegistry collectorRegistry) { + return new PrometheusScrapeEndpoint(collectorRegistry); + } + + @Bean + public CollectorRegistry collectorRegistry() { + return new CollectorRegistry(true); + } + + @Bean + public MeterRegistry registry(CollectorRegistry registry) { + return new PrometheusMeterRegistry((k) -> null, registry, Clock.SYSTEM); + } + + } + +} diff --git a/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/metrics/integration/SpringIntegrationMetricsIntegrationTests.java b/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/metrics/integration/SpringIntegrationMetricsIntegrationTests.java new file mode 100644 index 000000000000..abae472f3406 --- /dev/null +++ b/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/metrics/integration/SpringIntegrationMetricsIntegrationTests.java @@ -0,0 +1,138 @@ +/* + * 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.metrics.integration; + +import java.io.IOException; +import java.util.Map; + +import com.fasterxml.jackson.databind.ObjectMapper; +import io.micrometer.core.instrument.MeterRegistry; +import io.micrometer.core.instrument.Statistic; +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.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.integration.annotation.Gateway; +import org.springframework.integration.annotation.IntegrationComponentScan; +import org.springframework.integration.annotation.MessagingGateway; +import org.springframework.integration.config.EnableIntegration; +import org.springframework.integration.dsl.IntegrationFlow; +import org.springframework.integration.support.management.IntegrationManagementConfigurer; +import org.springframework.test.context.junit4.SpringRunner; + +import static org.assertj.core.api.Assertions.assertThat; + +/** + * Integration tests for {@link SpringIntegrationMetrics}. + * + * @author Jon Schneider + * @author Andy Wilkinson + */ +@RunWith(SpringRunner.class) +public class SpringIntegrationMetricsIntegrationTests { + + @Autowired + TestSpringIntegrationApplication.TempConverter converter; + + @Autowired + MeterRegistry registry; + + @Test + public void springIntegrationMetrics() { + this.converter.fahrenheitToCelcius(68.0); + assertThat(this.registry.find("spring.integration.channel.sends") + .tags("channel", "convert.input").value(Statistic.Count, 1).meter()) + .isPresent(); + assertThat(this.registry.find("spring.integration.handler.duration.min").meter()) + .isPresent(); + assertThat(this.registry.find("spring.integration.sourceNames").meter()) + .isPresent(); + } + + @Configuration + @EnableIntegration + @IntegrationComponentScan + public static class TestSpringIntegrationApplication { + + @Bean + MeterRegistry meterRegistry() { + return new SimpleMeterRegistry(); + } + + @Bean + public IntegrationManagementConfigurer integrationManagementConfigurer() { + IntegrationManagementConfigurer configurer = new IntegrationManagementConfigurer(); + configurer.setDefaultCountsEnabled(true); + configurer.setDefaultStatsEnabled(true); + return configurer; + } + + @Bean + public SpringIntegrationMetrics springIntegrationMetrics( + IntegrationManagementConfigurer configurer, MeterRegistry registry) { + SpringIntegrationMetrics springIntegrationMetrics = new SpringIntegrationMetrics( + configurer); + springIntegrationMetrics.bindTo(registry); + return springIntegrationMetrics; + } + + @Bean + public IntegrationFlow convert() { + return (f) -> f + .transform((payload) -> "{\"fahrenheit\":" + payload + "}", + (e) -> e.id("toJson")) + .handle(String.class, this::fahrenheitToCelcius, + (e) -> e.id("temperatureConverter")) + .transform(this::extractResult, e -> e.id("toResponse")); + } + + private double extractResult(String json) { + try { + return (double) new ObjectMapper().readValue(json, Map.class) + .get("celcius"); + } + catch (IOException ex) { + throw new RuntimeException(ex); + } + } + + private String fahrenheitToCelcius(String payload, Map headers) { + try { + double fahrenheit = (double) new ObjectMapper() + .readValue(payload, Map.class).get("fahrenheit"); + double celcius = (fahrenheit - 32) * (5.0 / 9.0); + return "{\"celcius\":" + celcius + "}"; + } + catch (Exception ex) { + throw new RuntimeException(ex); + } + } + + @MessagingGateway + public interface TempConverter { + + @Gateway(requestChannel = "convert.input") + double fahrenheitToCelcius(double fahren); + + } + + } + +} diff --git a/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/metrics/jdbc/DataSourcePoolMetricsTests.java b/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/metrics/jdbc/DataSourcePoolMetricsTests.java new file mode 100644 index 000000000000..520ac737c6b0 --- /dev/null +++ b/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/metrics/jdbc/DataSourcePoolMetricsTests.java @@ -0,0 +1,83 @@ +/* + * 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.metrics.jdbc; + +import java.sql.SQLException; +import java.util.Collection; +import java.util.Collections; + +import javax.sql.DataSource; + +import io.micrometer.core.instrument.MeterRegistry; +import io.micrometer.core.instrument.simple.SimpleMeterRegistry; +import org.junit.Test; + +import org.springframework.boot.autoconfigure.AutoConfigurations; +import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration; +import org.springframework.boot.jdbc.metadata.DataSourcePoolMetadataProvider; +import org.springframework.boot.test.context.runner.ApplicationContextRunner; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +import static org.assertj.core.api.Assertions.assertThat; + +/** + * Tests for {@link DataSourcePoolMetrics}. + * + * @author Jon Schneider + * @author Andy Wilkinson + */ +public class DataSourcePoolMetricsTests { + + @Test + public void dataSourceIsInstrumented() throws SQLException, InterruptedException { + new ApplicationContextRunner() + .withUserConfiguration(DataSourceConfig.class, MetricsApp.class) + .withConfiguration( + AutoConfigurations.of(DataSourceAutoConfiguration.class)) + .withPropertyValues("spring.datasource.generate-unique-name=true", + "metrics.use-global-registry=false") + .run((context) -> { + context.getBean(DataSource.class).getConnection().getMetaData(); + assertThat(context.getBean(MeterRegistry.class) + .find("data.source.max.connections").meter()).isPresent(); + }); + } + + @Configuration + static class MetricsApp { + + @Bean + MeterRegistry registry() { + return new SimpleMeterRegistry(); + } + + } + + @Configuration + static class DataSourceConfig { + + DataSourceConfig(DataSource dataSource, + Collection metadataProviders, + MeterRegistry registry) { + new DataSourcePoolMetrics(dataSource, metadataProviders, "data.source", + Collections.emptyList()).bindTo(registry); + } + + } + +} diff --git a/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/metrics/jmx/DefaultMetricNamingStrategyTests.java b/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/metrics/jmx/DefaultMetricNamingStrategyTests.java deleted file mode 100644 index a9b09f16abbf..000000000000 --- a/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/metrics/jmx/DefaultMetricNamingStrategyTests.java +++ /dev/null @@ -1,71 +0,0 @@ -/* - * Copyright 2012-2016 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.boot.actuate.metrics.jmx; - -import javax.management.ObjectName; - -import org.junit.Test; - -import static org.assertj.core.api.Assertions.assertThat; - -/** - * Tests for {@link DefaultMetricNamingStrategy}. - * - * @author Dave Syer - */ -public class DefaultMetricNamingStrategyTests { - - private DefaultMetricNamingStrategy strategy = new DefaultMetricNamingStrategy(); - - @Test - public void simpleName() throws Exception { - ObjectName name = this.strategy.getObjectName(null, - "domain:type=MetricValue,name=foo"); - assertThat(name.getDomain()).isEqualTo("domain"); - assertThat(name.getKeyProperty("type")).isEqualTo("foo"); - } - - @Test - public void onePeriod() throws Exception { - ObjectName name = this.strategy.getObjectName(null, - "domain:type=MetricValue,name=foo.bar"); - assertThat(name.getDomain()).isEqualTo("domain"); - assertThat(name.getKeyProperty("type")).isEqualTo("foo"); - assertThat(name.getKeyProperty("value")).isEqualTo("bar"); - } - - @Test - public void twoPeriods() throws Exception { - ObjectName name = this.strategy.getObjectName(null, - "domain:type=MetricValue,name=foo.bar.spam"); - assertThat(name.getDomain()).isEqualTo("domain"); - assertThat(name.getKeyProperty("type")).isEqualTo("foo"); - assertThat(name.getKeyProperty("name")).isEqualTo("bar"); - assertThat(name.getKeyProperty("value")).isEqualTo("spam"); - } - - @Test - public void threePeriods() throws Exception { - ObjectName name = this.strategy.getObjectName(null, - "domain:type=MetricValue,name=foo.bar.spam.bucket"); - assertThat(name.getDomain()).isEqualTo("domain"); - assertThat(name.getKeyProperty("type")).isEqualTo("foo"); - assertThat(name.getKeyProperty("name")).isEqualTo("bar"); - assertThat(name.getKeyProperty("value")).isEqualTo("spam.bucket"); - } - -} diff --git a/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/metrics/opentsdb/OpenTsdbGaugeWriterTests.java b/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/metrics/opentsdb/OpenTsdbGaugeWriterTests.java deleted file mode 100644 index 45d8a44dd9cd..000000000000 --- a/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/metrics/opentsdb/OpenTsdbGaugeWriterTests.java +++ /dev/null @@ -1,81 +0,0 @@ -/* - * 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.metrics.opentsdb; - -import java.util.Collections; -import java.util.Map; - -import org.junit.Before; -import org.junit.Test; - -import org.springframework.boot.actuate.metrics.Metric; -import org.springframework.http.HttpStatus; -import org.springframework.http.ResponseEntity; -import org.springframework.web.client.RestOperations; - -import static org.mockito.ArgumentMatchers.any; -import static org.mockito.ArgumentMatchers.anyString; -import static org.mockito.BDDMockito.given; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.verify; - -/** - * Tests for {@link OpenTsdbGaugeWriter}. - * - * @author Dave Syer - */ -public class OpenTsdbGaugeWriterTests { - - private OpenTsdbGaugeWriter writer; - - private RestOperations restTemplate = mock(RestOperations.class); - - @Before - public void init() { - this.writer = new OpenTsdbGaugeWriter(); - this.writer.setRestTemplate(this.restTemplate); - } - - @Test - public void postSuccessfullyOnFlush() { - this.writer.set(new Metric<>("foo", 2.4)); - given(this.restTemplate.postForEntity(anyString(), any(Object.class), anyMap())) - .willReturn(emptyResponse()); - this.writer.flush(); - verify(this.restTemplate).postForEntity(anyString(), any(Object.class), anyMap()); - } - - @Test - public void flushAutomatically() { - given(this.restTemplate.postForEntity(anyString(), any(Object.class), anyMap())) - .willReturn(emptyResponse()); - this.writer.setBufferSize(0); - this.writer.set(new Metric<>("foo", 2.4)); - verify(this.restTemplate).postForEntity(anyString(), any(Object.class), anyMap()); - } - - @SuppressWarnings("rawtypes") - private ResponseEntity emptyResponse() { - return new ResponseEntity<>(Collections.emptyMap(), HttpStatus.OK); - } - - @SuppressWarnings({ "rawtypes", "unchecked" }) - private Class anyMap() { - return any(Class.class); - } - -} diff --git a/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/metrics/reader/MetricReaderPublicMetricsTests.java b/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/metrics/reader/MetricReaderPublicMetricsTests.java deleted file mode 100644 index 0f88334fcfcd..000000000000 --- a/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/metrics/reader/MetricReaderPublicMetricsTests.java +++ /dev/null @@ -1,48 +0,0 @@ -/* - * 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.metrics.reader; - -import java.util.ArrayList; -import java.util.List; - -import org.junit.Test; - -import org.springframework.boot.actuate.metrics.Metric; - -import static org.assertj.core.api.Assertions.assertThat; -import static org.mockito.BDDMockito.given; -import static org.mockito.Mockito.mock; - -/** - * Tests for {@link MetricReaderPublicMetrics}. - * - * @author Phillip Webb - */ -public class MetricReaderPublicMetricsTests { - - @Test - public void exposesMetrics() { - List> metrics = new ArrayList<>(); - metrics.add(mock(Metric.class)); - metrics.add(mock(Metric.class)); - MetricReader reader = mock(MetricReader.class); - given(reader.findAll()).willReturn(metrics); - MetricReaderPublicMetrics publicMetrics = new MetricReaderPublicMetrics(reader); - assertThat(publicMetrics.metrics()).isEqualTo(metrics); - } - -} diff --git a/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/metrics/reader/MetricRegistryMetricReaderTests.java b/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/metrics/reader/MetricRegistryMetricReaderTests.java deleted file mode 100644 index c69eee736f00..000000000000 --- a/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/metrics/reader/MetricRegistryMetricReaderTests.java +++ /dev/null @@ -1,60 +0,0 @@ -/* - * 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.metrics.reader; - -import java.util.HashSet; -import java.util.Set; - -import com.codahale.metrics.Gauge; -import com.codahale.metrics.MetricRegistry; -import org.junit.Test; - -import org.springframework.boot.actuate.metrics.Metric; - -import static org.assertj.core.api.Assertions.assertThat; - -/** - * Tests for {@link MetricRegistryMetricReader}. - * - * @author Andy Wilkinson - */ -public class MetricRegistryMetricReaderTests { - - private final MetricRegistry metricRegistry = new MetricRegistry(); - - private final MetricRegistryMetricReader metricReader = new MetricRegistryMetricReader( - this.metricRegistry); - - @Test - public void nonNumberGaugesAreTolerated() { - this.metricRegistry.register("test", (Gauge>) HashSet::new); - assertThat(this.metricReader.findOne("test")).isNull(); - this.metricRegistry.remove("test"); - assertThat(this.metricReader.findOne("test")).isNull(); - } - - @Test - @SuppressWarnings("unchecked") - public void numberGauge() { - this.metricRegistry.register("test", (Gauge) () -> Integer.valueOf(5)); - Metric metric = (Metric) this.metricReader.findOne("test"); - assertThat(metric.getValue()).isEqualTo(Integer.valueOf(5)); - this.metricRegistry.remove("test"); - assertThat(this.metricReader.findOne("test")).isNull(); - } - -} diff --git a/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/metrics/repository/InMemoryMetricRepositoryTests.java b/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/metrics/repository/InMemoryMetricRepositoryTests.java deleted file mode 100644 index 25cf51063b61..000000000000 --- a/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/metrics/repository/InMemoryMetricRepositoryTests.java +++ /dev/null @@ -1,52 +0,0 @@ -/* - * 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.metrics.repository; - -import java.util.Date; - -import org.junit.Test; - -import org.springframework.boot.actuate.metrics.Metric; -import org.springframework.boot.actuate.metrics.writer.Delta; - -import static org.assertj.core.api.Assertions.assertThat; -import static org.assertj.core.api.Assertions.offset; - -/** - * Tests for {@link InMemoryMetricRepository}. - * - * @author Dave Syer - */ -public class InMemoryMetricRepositoryTests { - - private final InMemoryMetricRepository repository = new InMemoryMetricRepository(); - - @Test - public void increment() { - this.repository.increment(new Delta<>("foo", 1, new Date())); - assertThat(this.repository.findOne("foo").getValue().doubleValue()).isEqualTo(1.0, - offset(0.01)); - } - - @Test - public void set() { - this.repository.set(new Metric<>("foo", 2.5, new Date())); - assertThat(this.repository.findOne("foo").getValue().doubleValue()).isEqualTo(2.5, - offset(0.01)); - } - -} diff --git a/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/metrics/repository/InMemoryMultiMetricRepositoryTests.java b/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/metrics/repository/InMemoryMultiMetricRepositoryTests.java deleted file mode 100644 index 501691c6bc5c..000000000000 --- a/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/metrics/repository/InMemoryMultiMetricRepositoryTests.java +++ /dev/null @@ -1,101 +0,0 @@ -/* - * 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.metrics.repository; - -import java.util.HashMap; -import java.util.HashSet; -import java.util.Map; -import java.util.Set; - -import org.junit.Test; - -import org.springframework.boot.actuate.metrics.Metric; -import org.springframework.boot.actuate.metrics.writer.Delta; - -import static org.assertj.core.api.Assertions.assertThat; - -/** - * Tests for {@link InMemoryMultiMetricRepository}. - * - * @author Dave Syer - */ -public class InMemoryMultiMetricRepositoryTests { - - private final InMemoryMultiMetricRepository repository = new InMemoryMultiMetricRepository(); - - @Test - public void registeredPrefixCounted() { - this.repository.increment("foo", new Delta("bar", 1)); - this.repository.increment("foo", new Delta("bar", 1)); - this.repository.increment("foo", new Delta("spam", 1)); - Set names = new HashSet<>(); - for (Metric metric : this.repository.findAll("foo")) { - names.add(metric.getName()); - } - assertThat(names).hasSize(2); - assertThat(names.contains("foo.bar")).isTrue(); - } - - @Test - public void prefixWithWildcard() { - this.repository.increment("foo", new Delta("bar", 1)); - Set names = new HashSet<>(); - for (Metric metric : this.repository.findAll("foo.*")) { - names.add(metric.getName()); - } - assertThat(names).hasSize(1); - assertThat(names.contains("foo.bar")).isTrue(); - } - - @Test - public void prefixWithPeriod() { - this.repository.increment("foo", new Delta("bar", 1)); - Set names = new HashSet<>(); - for (Metric metric : this.repository.findAll("foo.")) { - names.add(metric.getName()); - } - assertThat(names).hasSize(1); - assertThat(names.contains("foo.bar")).isTrue(); - } - - @Test - public void onlyRegisteredPrefixCounted() { - this.repository.increment("foo", new Delta("bar", 1)); - this.repository.increment("foobar", new Delta("spam", 1)); - Set names = new HashSet<>(); - for (Metric metric : this.repository.findAll("foo")) { - names.add(metric.getName()); - } - assertThat(names).hasSize(1); - assertThat(names.contains("foo.bar")).isTrue(); - } - - @Test - public void incrementGroup() { - this.repository.increment("foo", new Delta("foo.bar", 1)); - this.repository.increment("foo", new Delta("foo.bar", 2)); - this.repository.increment("foo", new Delta("foo.spam", 1)); - Map> metrics = new HashMap<>(); - for (Metric metric : this.repository.findAll("foo")) { - metrics.put(metric.getName(), metric); - } - assertThat(metrics).hasSize(2); - assertThat(metrics).containsKeys("foo.bar", "foo.spam"); - assertThat(metrics.get("foo.bar").getValue()).isEqualTo(3L); - } - -} diff --git a/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/metrics/repository/redis/RedisMetricRepositoryTests.java b/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/metrics/repository/redis/RedisMetricRepositoryTests.java deleted file mode 100644 index a281adbed6f5..000000000000 --- a/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/metrics/repository/redis/RedisMetricRepositoryTests.java +++ /dev/null @@ -1,108 +0,0 @@ -/* - * 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.metrics.repository.redis; - -import org.junit.After; -import org.junit.Before; -import org.junit.Rule; -import org.junit.Test; - -import org.springframework.boot.actuate.metrics.Iterables; -import org.springframework.boot.actuate.metrics.Metric; -import org.springframework.boot.actuate.metrics.writer.Delta; -import org.springframework.boot.testsupport.rule.RedisTestServer; -import org.springframework.data.redis.core.StringRedisTemplate; - -import static org.assertj.core.api.Assertions.assertThat; -import static org.assertj.core.api.Assertions.offset; - -/** - * Tests for {@link RedisMetricRepository}. - * - * @author Dave Syer - */ -public class RedisMetricRepositoryTests { - - @Rule - public RedisTestServer redis = new RedisTestServer(); - - private RedisMetricRepository repository; - - private String prefix; - - @Before - public void init() { - this.prefix = "spring.test." + System.currentTimeMillis(); - this.repository = new RedisMetricRepository(this.redis.getConnectionFactory(), - this.prefix); - } - - @After - public void clear() { - assertThat(new StringRedisTemplate(this.redis.getConnectionFactory()) - .opsForValue().get(this.prefix + ".foo")).isNotNull(); - this.repository.reset("foo"); - this.repository.reset("bar"); - assertThat(new StringRedisTemplate(this.redis.getConnectionFactory()) - .opsForValue().get(this.prefix + ".foo")).isNull(); - } - - @Test - public void setAndGet() { - this.repository.set(new Metric("foo", 12.3)); - Metric metric = this.repository.findOne("foo"); - assertThat(metric.getName()).isEqualTo("foo"); - assertThat(metric.getValue().doubleValue()).isEqualTo(12.3, offset(0.01)); - } - - @Test - public void incrementAndGet() { - this.repository.increment(new Delta<>("foo", 3L)); - assertThat(this.repository.findOne("foo").getValue().longValue()).isEqualTo(3); - } - - @Test - public void setIncrementAndGet() { - this.repository.set(new Metric("foo", 12.3)); - this.repository.increment(new Delta<>("foo", 3L)); - Metric metric = this.repository.findOne("foo"); - assertThat(metric.getName()).isEqualTo("foo"); - assertThat(metric.getValue().doubleValue()).isEqualTo(15.3, offset(0.01)); - } - - @Test - public void findAll() { - this.repository.increment(new Delta<>("foo", 3L)); - this.repository.set(new Metric("bar", 12.3)); - assertThat(Iterables.collection(this.repository.findAll())).hasSize(2); - } - - @Test - public void findOneWithAll() { - this.repository.increment(new Delta<>("foo", 3L)); - Metric metric = this.repository.findAll().iterator().next(); - assertThat(metric.getName()).isEqualTo("foo"); - } - - @Test - public void count() { - this.repository.increment(new Delta<>("foo", 3L)); - this.repository.set(new Metric("bar", 12.3)); - assertThat(this.repository.count()).isEqualTo(2); - } - -} diff --git a/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/metrics/repository/redis/RedisMultiMetricRepositoryTests.java b/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/metrics/repository/redis/RedisMultiMetricRepositoryTests.java deleted file mode 100644 index f4537dcd4a6e..000000000000 --- a/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/metrics/repository/redis/RedisMultiMetricRepositoryTests.java +++ /dev/null @@ -1,146 +0,0 @@ -/* - * 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.metrics.repository.redis; - -import java.util.Arrays; -import java.util.Collection; -import java.util.HashSet; -import java.util.List; -import java.util.Set; - -import org.junit.After; -import org.junit.Before; -import org.junit.Rule; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.junit.runners.Parameterized; -import org.junit.runners.Parameterized.Parameter; -import org.junit.runners.Parameterized.Parameters; - -import org.springframework.boot.actuate.metrics.Iterables; -import org.springframework.boot.actuate.metrics.Metric; -import org.springframework.boot.actuate.metrics.writer.Delta; -import org.springframework.boot.testsupport.rule.RedisTestServer; -import org.springframework.data.redis.core.StringRedisTemplate; - -import static org.assertj.core.api.Assertions.assertThat; - -/** - * Tests for {@link RedisMultiMetricRepository}. - * - * @author Dave Syer - */ -@RunWith(Parameterized.class) -public class RedisMultiMetricRepositoryTests { - - @Rule - public RedisTestServer redis = new RedisTestServer(); - - private RedisMultiMetricRepository repository; - - @Parameter(0) - public String prefix; - - @Parameters - public static List parameters() { - return Arrays.asList(new Object[] { null }, new Object[] { "test" }); - } - - @Before - public void init() { - if (this.prefix == null) { - this.prefix = "spring.groups"; - this.repository = new RedisMultiMetricRepository( - this.redis.getConnectionFactory()); - } - else { - this.repository = new RedisMultiMetricRepository( - this.redis.getConnectionFactory(), this.prefix); - } - } - - @After - public void clear() { - assertThat(new StringRedisTemplate(this.redis.getConnectionFactory()).opsForZSet() - .size("keys." + this.prefix)).isGreaterThan(0); - this.repository.reset("foo"); - this.repository.reset("bar"); - assertThat(new StringRedisTemplate(this.redis.getConnectionFactory()) - .opsForValue().get(this.prefix + ".foo")).isNull(); - assertThat(new StringRedisTemplate(this.redis.getConnectionFactory()) - .opsForValue().get(this.prefix + ".bar")).isNull(); - } - - @Test - public void setAndGet() { - this.repository.set("foo", - Arrays.>asList(new Metric("foo.bar", 12.3))); - this.repository.set("foo", - Arrays.>asList(new Metric("foo.bar", 15.3))); - assertThat(Iterables.collection(this.repository.findAll("foo")).iterator().next() - .getValue()).isEqualTo(15.3); - } - - @Test - public void setAndGetMultiple() { - this.repository.set("foo", - Arrays.>asList(new Metric("foo.val", 12.3), - new Metric("foo.bar", 11.3))); - assertThat(Iterables.collection(this.repository.findAll("foo"))).hasSize(2); - } - - @Test - public void groups() { - this.repository.set("foo", - Arrays.>asList(new Metric("foo.val", 12.3), - new Metric("foo.bar", 11.3))); - this.repository.set("bar", - Arrays.>asList(new Metric("bar.val", 12.3), - new Metric("bar.foo", 11.3))); - Collection groups = Iterables.collection(this.repository.groups()); - assertThat(groups).hasSize(2).contains("foo"); - } - - @Test - public void count() { - this.repository.set("foo", - Arrays.>asList(new Metric("foo.val", 12.3), - new Metric("foo.bar", 11.3))); - this.repository.set("bar", - Arrays.>asList(new Metric("bar.val", 12.3), - new Metric("bar.foo", 11.3))); - assertThat(this.repository.countGroups()).isEqualTo(2); - } - - @Test - public void increment() { - this.repository.increment("foo", new Delta("foo.bar", 1)); - this.repository.increment("foo", new Delta("foo.bar", 2)); - this.repository.increment("foo", new Delta("foo.spam", 1)); - Metric bar = null; - Set names = new HashSet<>(); - for (Metric metric : this.repository.findAll("foo")) { - names.add(metric.getName()); - if (metric.getName().equals("foo.bar")) { - bar = metric; - } - } - assertThat(names).hasSize(2).contains("foo.bar"); - assertThat(bar.getValue()).isEqualTo(3d); - } - -} diff --git a/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/metrics/rich/InMemoryRichGaugeRepositoryTests.java b/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/metrics/rich/InMemoryRichGaugeRepositoryTests.java deleted file mode 100644 index 6af63795f8c0..000000000000 --- a/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/metrics/rich/InMemoryRichGaugeRepositoryTests.java +++ /dev/null @@ -1,60 +0,0 @@ -/* - * 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.metrics.rich; - -import org.junit.Test; - -import org.springframework.boot.actuate.metrics.Metric; -import org.springframework.boot.actuate.metrics.writer.Delta; - -import static org.assertj.core.api.Assertions.assertThat; -import static org.assertj.core.api.Assertions.offset; - -/** - * Tests for {@link InMemoryRichGaugeRepository}. - * - * @author Dave Syer - * @author Andy Wilkinson - */ -public class InMemoryRichGaugeRepositoryTests { - - private final InMemoryRichGaugeRepository repository = new InMemoryRichGaugeRepository(); - - @Test - public void writeAndRead() { - this.repository.set(new Metric<>("foo", 1d)); - this.repository.set(new Metric<>("foo", 2d)); - assertThat(this.repository.findOne("foo").getCount()).isEqualTo(2L); - assertThat(this.repository.findOne("foo").getValue()).isEqualTo(2d, offset(0.01)); - } - - @Test - public void incrementExisting() { - this.repository.set(new Metric<>("foo", 1d)); - this.repository.increment(new Delta<>("foo", 2d)); - assertThat(this.repository.findOne("foo").getCount()).isEqualTo(2L); - assertThat(this.repository.findOne("foo").getValue()).isEqualTo(3d, offset(0.01)); - } - - @Test - public void incrementNew() { - this.repository.increment(new Delta<>("foo", 2d)); - assertThat(this.repository.findOne("foo").getCount()).isEqualTo(1L); - assertThat(this.repository.findOne("foo").getValue()).isEqualTo(2d, offset(0.01)); - } - -} diff --git a/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/metrics/rich/MultiMetricRichGaugeReaderTests.java b/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/metrics/rich/MultiMetricRichGaugeReaderTests.java deleted file mode 100644 index df4b7bcd557e..000000000000 --- a/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/metrics/rich/MultiMetricRichGaugeReaderTests.java +++ /dev/null @@ -1,68 +0,0 @@ -/* - * 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.metrics.rich; - -import org.junit.Test; - -import org.springframework.boot.actuate.metrics.Metric; -import org.springframework.boot.actuate.metrics.export.RichGaugeExporter; -import org.springframework.boot.actuate.metrics.repository.InMemoryMultiMetricRepository; - -import static org.assertj.core.api.Assertions.assertThat; - -/** - * Tests for {@link MultiMetricRichGaugeReader}. - * - * @author Dave Syer - */ -public class MultiMetricRichGaugeReaderTests { - - private InMemoryMultiMetricRepository repository = new InMemoryMultiMetricRepository(); - - private MultiMetricRichGaugeReader reader = new MultiMetricRichGaugeReader( - this.repository); - - private InMemoryRichGaugeRepository data = new InMemoryRichGaugeRepository(); - - private RichGaugeExporter exporter = new RichGaugeExporter(this.data, - this.repository); - - @Test - public void countOne() { - this.data.set(new Metric<>("foo", 1)); - this.data.set(new Metric<>("foo", 1)); - this.exporter.export(); - // Check the exporter worked - assertThat(this.repository.countGroups()).isEqualTo(1); - assertThat(this.reader.count()).isEqualTo(1); - RichGauge one = this.reader.findOne("foo"); - assertThat(one).isNotNull(); - assertThat(one.getCount()).isEqualTo(2); - } - - @Test - public void countTwo() { - this.data.set(new Metric<>("foo", 1)); - this.data.set(new Metric<>("bar", 1)); - this.exporter.export(); - assertThat(this.reader.count()).isEqualTo(2); - RichGauge one = this.reader.findOne("foo"); - assertThat(one).isNotNull(); - assertThat(one.getCount()).isEqualTo(1); - } - -} diff --git a/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/metrics/rich/RichGaugeReaderPublicMetricsTests.java b/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/metrics/rich/RichGaugeReaderPublicMetricsTests.java deleted file mode 100644 index f42ba5a0b7b3..000000000000 --- a/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/metrics/rich/RichGaugeReaderPublicMetricsTests.java +++ /dev/null @@ -1,69 +0,0 @@ -/* - * 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.metrics.rich; - -import java.util.Date; -import java.util.HashMap; -import java.util.Map; - -import org.junit.Test; - -import org.springframework.boot.actuate.metrics.Metric; - -import static org.assertj.core.api.Assertions.assertThat; - -/** - * Tests for {@link RichGaugeReaderPublicMetrics}. - * - * @author Johannes Edmeier - */ -public class RichGaugeReaderPublicMetricsTests { - - @Test - public void testMetrics() throws Exception { - InMemoryRichGaugeRepository repository = new InMemoryRichGaugeRepository(); - - repository.set(new Metric<>("a", 0.d, new Date())); - repository.set(new Metric<>("a", 0.5d, new Date())); - - RichGaugeReaderPublicMetrics metrics = new RichGaugeReaderPublicMetrics( - repository); - - Map> results = new HashMap<>(); - for (Metric metric : metrics.metrics()) { - results.put(metric.getName(), metric); - } - assertThat(results.containsKey("a.val")).isTrue(); - assertThat(results.get("a.val").getValue().doubleValue()).isEqualTo(0.5d); - - assertThat(results.containsKey("a.avg")).isTrue(); - assertThat(results.get("a.avg").getValue().doubleValue()).isEqualTo(0.25d); - - assertThat(results.containsKey("a.min")).isTrue(); - assertThat(results.get("a.min").getValue().doubleValue()).isEqualTo(0.0d); - - assertThat(results.containsKey("a.max")).isTrue(); - assertThat(results.get("a.max").getValue().doubleValue()).isEqualTo(0.5d); - - assertThat(results.containsKey("a.count")).isTrue(); - assertThat(results.get("a.count").getValue().longValue()).isEqualTo(2L); - - assertThat(results.containsKey("a.alpha")).isTrue(); - assertThat(results.get("a.alpha").getValue().doubleValue()).isEqualTo(-1.d); - } - -} diff --git a/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/metrics/statsd/StatsdMetricWriterTests.java b/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/metrics/statsd/StatsdMetricWriterTests.java deleted file mode 100644 index 936d4586f86a..000000000000 --- a/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/metrics/statsd/StatsdMetricWriterTests.java +++ /dev/null @@ -1,167 +0,0 @@ -/* - * 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.metrics.statsd; - -import java.net.DatagramPacket; -import java.net.DatagramSocket; -import java.net.SocketException; -import java.nio.charset.Charset; -import java.util.ArrayList; -import java.util.List; - -import org.junit.After; -import org.junit.Test; - -import org.springframework.boot.actuate.metrics.Metric; -import org.springframework.boot.actuate.metrics.writer.Delta; - -import static org.assertj.core.api.Assertions.assertThat; - -/** - * Tests for {@link StatsdMetricWriter}. - * - * @author Dave Syer - * @author OdĂ­n del RĂ­o - */ -public class StatsdMetricWriterTests { - - private DummyStatsDServer server = new DummyStatsDServer(0); - - private StatsdMetricWriter writer = new StatsdMetricWriter("me", "localhost", - this.server.getPort()); - - @After - public void close() { - this.server.stop(); - this.writer.close(); - } - - @Test - public void increment() { - this.writer.increment(new Delta<>("counter.foo", 3L)); - this.server.waitForMessage(); - assertThat(this.server.messagesReceived().get(0)).isEqualTo("me.counter.foo:3|c"); - } - - @Test - public void setLongMetric() throws Exception { - this.writer.set(new Metric<>("gauge.foo", 3L)); - this.server.waitForMessage(); - assertThat(this.server.messagesReceived().get(0)).isEqualTo("me.gauge.foo:3|g"); - } - - @Test - public void setDoubleMetric() throws Exception { - this.writer.set(new Metric<>("gauge.foo", 3.7)); - this.server.waitForMessage(); - // Doubles are truncated - assertThat(this.server.messagesReceived().get(0)).isEqualTo("me.gauge.foo:3.7|g"); - } - - @Test - public void setTimerMetric() throws Exception { - this.writer.set(new Metric<>("timer.foo", 37L)); - this.server.waitForMessage(); - assertThat(this.server.messagesReceived().get(0)).isEqualTo("me.timer.foo:37|ms"); - } - - @Test - public void nullPrefix() throws Exception { - this.writer = new StatsdMetricWriter("localhost", this.server.getPort()); - this.writer.set(new Metric<>("gauge.foo", 3L)); - this.server.waitForMessage(); - assertThat(this.server.messagesReceived().get(0)).isEqualTo("gauge.foo:3|g"); - } - - @Test - public void periodPrefix() throws Exception { - this.writer = new StatsdMetricWriter("my.", "localhost", this.server.getPort()); - this.writer.set(new Metric<>("gauge.foo", 3L)); - this.server.waitForMessage(); - assertThat(this.server.messagesReceived().get(0)).isEqualTo("my.gauge.foo:3|g"); - } - - @Test - public void incrementMetricWithInvalidCharsInName() throws Exception { - this.writer.increment(new Delta<>("counter.fo:o", 3L)); - this.server.waitForMessage(); - assertThat(this.server.messagesReceived().get(0)) - .isEqualTo("me.counter.fo-o:3|c"); - } - - @Test - public void setMetricWithInvalidCharsInName() throws Exception { - this.writer.set(new Metric<>("gauge.f:o:o", 3L)); - this.server.waitForMessage(); - assertThat(this.server.messagesReceived().get(0)).isEqualTo("me.gauge.f-o-o:3|g"); - } - - private static final class DummyStatsDServer implements Runnable { - - private final List messagesReceived = new ArrayList<>(); - - private final DatagramSocket server; - - DummyStatsDServer(int port) { - try { - this.server = new DatagramSocket(port); - } - catch (SocketException ex) { - throw new IllegalStateException(ex); - } - new Thread(this).start(); - } - - int getPort() { - return this.server.getLocalPort(); - } - - public void stop() { - this.server.close(); - } - - @Override - public void run() { - try { - DatagramPacket packet = new DatagramPacket(new byte[256], 256); - this.server.receive(packet); - this.messagesReceived.add( - new String(packet.getData(), Charset.forName("UTF-8")).trim()); - } - catch (Exception ex) { - // Ignore - } - } - - public void waitForMessage() { - while (this.messagesReceived.isEmpty()) { - try { - Thread.sleep(50L); - } - catch (InterruptedException e) { - Thread.currentThread().interrupt(); - } - } - } - - public List messagesReceived() { - return new ArrayList<>(this.messagesReceived); - } - - } - -} diff --git a/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/metrics/util/SimpleInMemoryRepositoryTests.java b/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/metrics/util/SimpleInMemoryRepositoryTests.java deleted file mode 100755 index dd80db3f3fe1..000000000000 --- a/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/metrics/util/SimpleInMemoryRepositoryTests.java +++ /dev/null @@ -1,116 +0,0 @@ -/* - * 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.metrics.util; - -import java.util.ArrayList; -import java.util.Collection; -import java.util.Iterator; -import java.util.List; -import java.util.concurrent.Callable; -import java.util.concurrent.Executors; -import java.util.concurrent.Future; -import java.util.concurrent.TimeUnit; - -import org.junit.Test; - -import static org.assertj.core.api.Assertions.assertThat; - -/** - * Tests for {@link SimpleInMemoryRepository}. - * - * @author Dave Syer - */ -public class SimpleInMemoryRepositoryTests { - - private final SimpleInMemoryRepository repository = new SimpleInMemoryRepository<>(); - - @Test - public void setAndGet() { - this.repository.set("foo", "bar"); - assertThat(this.repository.findOne("foo")).isEqualTo("bar"); - } - - @Test - public void updateExisting() { - this.repository.set("foo", "spam"); - this.repository.update("foo", (current) -> "bar"); - assertThat(this.repository.findOne("foo")).isEqualTo("bar"); - } - - @Test - public void updateNonexistent() { - this.repository.update("foo", (current) -> "bar"); - assertThat(this.repository.findOne("foo")).isEqualTo("bar"); - } - - @Test - public void findWithPrefix() { - this.repository.set("foo", "bar"); - this.repository.set("foo.bar", "one"); - this.repository.set("foo.min", "two"); - this.repository.set("foo.max", "three"); - assertThat(((Collection) this.repository.findAllWithPrefix("foo"))).hasSize(3); - } - - @Test - public void patternsAcceptedForRegisteredPrefix() { - this.repository.set("foo.bar", "spam"); - Iterator iterator = this.repository.findAllWithPrefix("foo.*").iterator(); - assertThat(iterator.next()).isEqualTo("spam"); - assertThat(iterator.hasNext()).isFalse(); - } - - @Test - public void updateConcurrent() throws Exception { - SimpleInMemoryRepository repository = new SimpleInMemoryRepository<>(); - Collection> tasks = new ArrayList<>(); - for (int i = 0; i < 1000; i++) { - tasks.add(new RepositoryUpdate(repository, 1)); - tasks.add(new RepositoryUpdate(repository, -1)); - } - List> all = Executors.newFixedThreadPool(10).invokeAll(tasks); - for (Future future : all) { - assertThat(future.get(1, TimeUnit.SECONDS)).isTrue(); - } - assertThat(repository.findOne("foo")).isEqualTo(0); - } - - private static class RepositoryUpdate implements Callable { - - private final SimpleInMemoryRepository repository; - - private final int delta; - - RepositoryUpdate(SimpleInMemoryRepository repository, int delta) { - this.repository = repository; - this.delta = delta; - } - - @Override - public Boolean call() throws Exception { - this.repository.update("foo", (current) -> { - if (current == null) { - return RepositoryUpdate.this.delta; - } - return current + RepositoryUpdate.this.delta; - }); - return true; - } - - } - -} diff --git a/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/metrics/web/client/MetricsRestTemplateCustomizerTests.java b/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/metrics/web/client/MetricsRestTemplateCustomizerTests.java new file mode 100644 index 000000000000..b9ddf038e83b --- /dev/null +++ b/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/metrics/web/client/MetricsRestTemplateCustomizerTests.java @@ -0,0 +1,69 @@ +/* + * 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.metrics.web.client; + +import java.util.stream.StreamSupport; + +import io.micrometer.core.instrument.MeterRegistry; +import io.micrometer.core.instrument.Statistic; +import io.micrometer.core.instrument.Tag; +import io.micrometer.core.instrument.simple.SimpleMeterRegistry; +import org.junit.Test; + +import org.springframework.http.HttpMethod; +import org.springframework.http.MediaType; +import org.springframework.test.web.client.MockRestServiceServer; +import org.springframework.test.web.client.match.MockRestRequestMatchers; +import org.springframework.test.web.client.response.MockRestResponseCreators; +import org.springframework.web.client.RestTemplate; + +import static org.assertj.core.api.Assertions.assertThat; + +/** + * Tests for {@link MetricsRestTemplateCustomizer}. + * + * @author Jon Schneider + */ +public class MetricsRestTemplateCustomizerTests { + + @Test + public void interceptRestTemplate() { + MeterRegistry registry = new SimpleMeterRegistry(); + RestTemplate restTemplate = new RestTemplate(); + MetricsRestTemplateCustomizer customizer = new MetricsRestTemplateCustomizer( + registry, new DefaultRestTemplateExchangeTagsProvider(), + "http.client.requests", true); + customizer.customize(restTemplate); + MockRestServiceServer mockServer = MockRestServiceServer + .createServer(restTemplate); + mockServer.expect(MockRestRequestMatchers.requestTo("/test/123")) + .andExpect(MockRestRequestMatchers.method(HttpMethod.GET)) + .andRespond(MockRestResponseCreators.withSuccess("OK", + MediaType.APPLICATION_JSON)); + String result = restTemplate.getForObject("/test/{id}", String.class, 123); + assertThat(registry.find("http.client.requests") + .tags("method", "GET", "uri", "/test/{id}", "status", "200") + .value(Statistic.Count, 1.0).timer()).isPresent(); + assertThat(registry.find("http.client.requests").meters() + .stream().flatMap((m) -> StreamSupport + .stream(m.getId().getTags().spliterator(), false)) + .map(Tag::getKey)).contains("bucket"); + assertThat(result).isEqualTo("OK"); + mockServer.verify(); + } + +} diff --git a/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/metrics/web/servlet/MetricsHandlerInterceptorAutoTimedTests.java b/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/metrics/web/servlet/MetricsHandlerInterceptorAutoTimedTests.java new file mode 100644 index 000000000000..ea1584376bb7 --- /dev/null +++ b/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/metrics/web/servlet/MetricsHandlerInterceptorAutoTimedTests.java @@ -0,0 +1,122 @@ +/* + * 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.metrics.web.servlet; + +import io.micrometer.core.instrument.MeterRegistry; +import io.micrometer.core.instrument.simple.SimpleMeterRegistry; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.Import; +import org.springframework.test.context.junit4.SpringRunner; +import org.springframework.test.context.web.WebAppConfiguration; +import org.springframework.test.web.servlet.MockMvc; +import org.springframework.test.web.servlet.setup.MockMvcBuilders; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; +import org.springframework.web.context.WebApplicationContext; +import org.springframework.web.servlet.config.annotation.EnableWebMvc; +import org.springframework.web.servlet.config.annotation.InterceptorRegistry; +import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; + +/** + * Tests for {@link MetricsHandlerInterceptor} with auto-timed server requests. + * + * @author Jon Schneider + */ +@RunWith(SpringRunner.class) +@WebAppConfiguration +public class MetricsHandlerInterceptorAutoTimedTests { + + @Autowired + private MeterRegistry registry; + + @Autowired + private WebApplicationContext context; + + private MockMvc mvc; + + @Before + public void setupMockMvc() { + this.mvc = MockMvcBuilders.webAppContextSetup(this.context).build(); + } + + @Test + public void metricsCanBeAutoTimed() throws Exception { + this.mvc.perform(get("/api/10")).andExpect(status().isOk()); + assertThat( + this.registry.find("http.server.requests").tags("status", "200").timer()) + .hasValueSatisfying((t) -> assertThat(t.count()).isEqualTo(1)); + } + + @Configuration + @EnableWebMvc + @Import(Controller.class) + static class TestConfiguration { + + @Bean + MeterRegistry meterRegistry() { + return new SimpleMeterRegistry(); + } + + @Bean + WebMvcMetrics webMvcMetrics(MeterRegistry meterRegistry) { + return new WebMvcMetrics(meterRegistry, new DefaultWebMvcTagsProvider(), + "http.server.requests", true, true); + } + + @Configuration + static class HandlerInterceptorConfiguration implements WebMvcConfigurer { + + private final WebMvcMetrics webMvcMetrics; + + HandlerInterceptorConfiguration(WebMvcMetrics webMvcMetrics) { + this.webMvcMetrics = webMvcMetrics; + } + + @Override + public void addInterceptors(InterceptorRegistry registry) { + registry.addInterceptor( + new MetricsHandlerInterceptor(this.webMvcMetrics)); + } + + } + + } + + @RestController + @RequestMapping("/api") + static class Controller { + + @GetMapping("/{id}") + public String successful(@PathVariable Long id) { + return id.toString(); + } + + } + +} diff --git a/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/metrics/web/servlet/MetricsHandlerInterceptorTests.java b/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/metrics/web/servlet/MetricsHandlerInterceptorTests.java new file mode 100644 index 000000000000..c108ebe1b73d --- /dev/null +++ b/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/metrics/web/servlet/MetricsHandlerInterceptorTests.java @@ -0,0 +1,297 @@ +/* + * 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.metrics.web.servlet; + +import java.util.concurrent.Callable; +import java.util.concurrent.CountDownLatch; +import java.util.stream.StreamSupport; + +import javax.servlet.http.HttpServletRequest; + +import io.micrometer.core.annotation.Timed; +import io.micrometer.core.instrument.MeterRegistry; +import io.micrometer.core.instrument.Statistic; +import io.micrometer.core.instrument.Tag; +import io.micrometer.core.instrument.simple.SimpleMeterRegistry; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.Import; +import org.springframework.http.HttpStatus; +import org.springframework.test.context.junit4.SpringRunner; +import org.springframework.test.context.web.WebAppConfiguration; +import org.springframework.test.web.servlet.MockMvc; +import org.springframework.test.web.servlet.MvcResult; +import org.springframework.test.web.servlet.setup.MockMvcBuilders; +import org.springframework.web.bind.annotation.ExceptionHandler; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.ResponseStatus; +import org.springframework.web.bind.annotation.RestController; +import org.springframework.web.context.WebApplicationContext; +import org.springframework.web.servlet.ModelAndView; +import org.springframework.web.servlet.config.annotation.EnableWebMvc; +import org.springframework.web.servlet.config.annotation.InterceptorRegistry; +import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatCode; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.asyncDispatch; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.request; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; + +/** + * Tests for {@link MetricsHandlerInterceptor}. + * + * @author Jon Schneider + */ +@RunWith(SpringRunner.class) +@WebAppConfiguration +public class MetricsHandlerInterceptorTests { + + private static final CountDownLatch longRequestCountDown = new CountDownLatch(1); + + @Autowired + private MeterRegistry registry; + + @Autowired + private WebApplicationContext context; + + private MockMvc mvc; + + @Before + public void setupMockMvc() { + this.mvc = MockMvcBuilders.webAppContextSetup(this.context).build(); + } + + @Test + public void timedMethod() throws Exception { + this.mvc.perform(get("/api/c1/10")).andExpect(status().isOk()); + assertThat(this.registry.find("http.server.requests") + .tags("status", "200", "uri", "/api/c1/{id}", "public", "true") + .value(Statistic.Count, 1.0).timer()).isPresent(); + } + + @Test + public void untimedMethod() throws Exception { + this.mvc.perform(get("/api/c1/untimed/10")).andExpect(status().isOk()); + assertThat(this.registry.find("http.server.requests") + .tags("uri", "/api/c1/untimed/10").timer()).isEmpty(); + } + + @Test + public void timedControllerClass() throws Exception { + this.mvc.perform(get("/api/c2/10")).andExpect(status().isOk()); + assertThat( + this.registry.find("http.server.requests").tags("status", "200").timer()) + .hasValueSatisfying((t) -> assertThat(t.count()).isEqualTo(1)); + } + + @Test + public void badClientRequest() throws Exception { + this.mvc.perform(get("/api/c1/oops")).andExpect(status().is4xxClientError()); + assertThat( + this.registry.find("http.server.requests").tags("status", "400").timer()) + .hasValueSatisfying((t) -> assertThat(t.count()).isEqualTo(1)); + } + + @Test + public void unhandledError() throws Exception { + assertThatCode(() -> this.mvc.perform(get("/api/c1/unhandledError/10")) + .andExpect(status().isOk())).hasCauseInstanceOf(RuntimeException.class); + assertThat(this.registry.find("http.server.requests") + .tags("exception", "RuntimeException").value(Statistic.Count, 1.0) + .timer()).isPresent(); + } + + @Test + public void longRunningRequest() throws Exception { + MvcResult result = this.mvc.perform(get("/api/c1/long/10")) + .andExpect(request().asyncStarted()).andReturn(); + + // while the mapping is running, it contributes to the activeTasks count + assertThat(this.registry.find("my.long.request").tags("region", "test") + .value(Statistic.Count, 1.0).longTaskTimer()).isPresent(); + + // once the mapping completes, we can gather information about status, etc. + longRequestCountDown.countDown(); + + this.mvc.perform(asyncDispatch(result)).andExpect(status().isOk()); + + assertThat(this.registry.find("http.server.requests").tags("status", "200") + .value(Statistic.Count, 1.0).timer()).isPresent(); + } + + @Test + public void endpointThrowsError() throws Exception { + this.mvc.perform(get("/api/c1/error/10")).andExpect(status().is4xxClientError()); + assertThat(this.registry.find("http.server.requests").tags("status", "422") + .value(Statistic.Count, 1.0).timer()).isPresent(); + } + + @Test + public void regexBasedRequestMapping() throws Exception { + this.mvc.perform(get("/api/c1/regex/.abc")).andExpect(status().isOk()); + assertThat(this.registry.find("http.server.requests") + .tags("uri", "/api/c1/regex/{id:\\.[a-z]+}").value(Statistic.Count, 1.0) + .timer()).isPresent(); + } + + @Test + public void recordQuantiles() throws Exception { + this.mvc.perform(get("/api/c1/quantiles/10")).andExpect(status().isOk()); + + assertThat(this.registry.find("http.server.requests").tags("quantile", "0.5") + .gauge()).isNotEmpty(); + assertThat(this.registry.find("http.server.requests").tags("quantile", "0.95") + .gauge()).isNotEmpty(); + } + + @Test + public void recordPercentiles() throws Exception { + this.mvc.perform(get("/api/c1/percentiles/10")).andExpect(status().isOk()); + + assertThat(this.registry.find("http.server.requests").meters() + .stream().flatMap((m) -> StreamSupport + .stream(m.getId().getTags().spliterator(), false)) + .map(Tag::getKey)).contains("bucket"); + } + + @Configuration + @EnableWebMvc + @Import({ Controller1.class, Controller2.class }) + static class TestConfiguration { + + @Bean + MeterRegistry meterRegistry() { + return new SimpleMeterRegistry(); + } + + @Bean + WebMvcMetrics webMvcMetrics(MeterRegistry meterRegistry) { + return new WebMvcMetrics(meterRegistry, new DefaultWebMvcTagsProvider(), + "http.server.requests", true, true); + } + + @Configuration + static class HandlerInterceptorConfiguration implements WebMvcConfigurer { + + private final WebMvcMetrics webMvcMetrics; + + HandlerInterceptorConfiguration(WebMvcMetrics webMvcMetrics) { + this.webMvcMetrics = webMvcMetrics; + } + + @Override + public void addInterceptors(InterceptorRegistry registry) { + registry.addInterceptor( + new MetricsHandlerInterceptor(this.webMvcMetrics)); + } + + } + + } + + @RestController + @RequestMapping("/api/c1") + static class Controller1 { + + @Timed(extraTags = { "public", "true" }) + @GetMapping("/{id}") + public String successfulWithExtraTags(@PathVariable Long id) { + return id.toString(); + } + + @Timed // contains dimensions for status, etc. that can't be known until after the + // response is sent + @Timed(value = "my.long.request", extraTags = { "region", + "test" }, longTask = true) // in progress metric + @GetMapping("/long/{id}") + public Callable takesLongTimeToSatisfy(@PathVariable Long id) { + return () -> { + try { + longRequestCountDown.await(); + } + catch (InterruptedException e) { + throw new RuntimeException(e); + } + return id.toString(); + }; + } + + @GetMapping("/untimed/{id}") + public String successfulButUntimed(@PathVariable Long id) { + return id.toString(); + } + + @Timed + @GetMapping("/error/{id}") + public String alwaysThrowsException(@PathVariable Long id) { + throw new IllegalStateException("Boom on $id!"); + } + + @Timed + @GetMapping("/unhandledError/{id}") + public String alwaysThrowsUnhandledException(@PathVariable Long id) { + throw new RuntimeException("Boom on $id!"); + } + + @Timed + @GetMapping("/regex/{id:\\.[a-z]+}") + public String successfulRegex(@PathVariable String id) { + return id; + } + + @Timed(quantiles = { 0.5, 0.95 }) + @GetMapping("/quantiles/{id}") + public String quantiles(@PathVariable String id) { + return id; + } + + @Timed(percentiles = true) + @GetMapping("/percentiles/{id}") + public String percentiles(@PathVariable String id) { + return id; + } + + @ExceptionHandler(IllegalStateException.class) + @ResponseStatus(HttpStatus.UNPROCESSABLE_ENTITY) + ModelAndView defaultErrorHandler(HttpServletRequest request, Exception e) { + return new ModelAndView("myerror"); + } + + } + + @RestController + @Timed + @RequestMapping("/api/c2") + static class Controller2 { + + @GetMapping("/{id}") + public String successful(@PathVariable Long id) { + return id.toString(); + } + + } + +} diff --git a/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/metrics/web/servlet/WebMvcMetricsIntegrationTests.java b/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/metrics/web/servlet/WebMvcMetricsIntegrationTests.java new file mode 100644 index 000000000000..9e86287ad68d --- /dev/null +++ b/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/metrics/web/servlet/WebMvcMetricsIntegrationTests.java @@ -0,0 +1,174 @@ +/* + * 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.metrics.web.servlet; + +import io.micrometer.core.annotation.Timed; +import io.micrometer.core.instrument.MeterRegistry; +import io.micrometer.core.instrument.Statistic; +import io.micrometer.core.instrument.simple.SimpleMeterRegistry; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.test.context.junit4.SpringRunner; +import org.springframework.test.context.web.WebAppConfiguration; +import org.springframework.test.web.servlet.MockMvc; +import org.springframework.test.web.servlet.setup.MockMvcBuilders; +import org.springframework.web.bind.annotation.ControllerAdvice; +import org.springframework.web.bind.annotation.ExceptionHandler; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; +import org.springframework.web.context.WebApplicationContext; +import org.springframework.web.servlet.config.annotation.EnableWebMvc; +import org.springframework.web.servlet.config.annotation.InterceptorRegistry; +import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatCode; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; + +/** + * Integration tests for {@link WebMvcMetrics}. + * + * @author Jon Schneider + */ +@RunWith(SpringRunner.class) +@WebAppConfiguration +public class WebMvcMetricsIntegrationTests { + + @Autowired + private WebApplicationContext context; + + @Autowired + private SimpleMeterRegistry registry; + + private MockMvc mvc; + + @Before + public void setupMockMvc() { + this.mvc = MockMvcBuilders.webAppContextSetup(this.context).build(); + } + + @Test + public void handledExceptionIsRecordedInMetricTag() throws Exception { + this.mvc.perform(get("/api/handledError")).andExpect(status().is5xxServerError()); + assertThat(this.registry.find("http.server.requests") + .tags("exception", "Exception1").value(Statistic.Count, 1.0).timer()) + .isPresent(); + } + + @Test + public void rethrownExceptionIsRecordedInMetricTag() throws Exception { + assertThatCode(() -> this.mvc.perform(get("/api/rethrownError")) + .andExpect(status().is5xxServerError())); + assertThat(this.registry.find("http.server.requests") + .tags("exception", "Exception2").value(Statistic.Count, 1.0).timer()) + .isPresent(); + } + + @Configuration + static class TestConfiguration { + + @Bean + WebMvcMetrics webMvcMetrics(MeterRegistry meterRegistry) { + return new WebMvcMetrics(meterRegistry, new DefaultWebMvcTagsProvider(), + "http.server.requests", true, true); + } + + @Bean + MeterRegistry registry() { + return new SimpleMeterRegistry(); + } + + @RestController + @RequestMapping("/api") + @Timed + static class Controller1 { + + @Bean + public CustomExceptionHandler controllerAdvice() { + return new CustomExceptionHandler(); + } + + @GetMapping("/handledError") + public String handledError() { + throw new Exception1(); + } + + @GetMapping("/rethrownError") + public String rethrownError() { + throw new Exception2(); + } + + } + + @Configuration + @EnableWebMvc + static class HandlerInterceptorConfiguration implements WebMvcConfigurer { + + private final WebMvcMetrics webMvcMetrics; + + HandlerInterceptorConfiguration(WebMvcMetrics webMvcMetrics) { + this.webMvcMetrics = webMvcMetrics; + } + + @Override + public void addInterceptors(InterceptorRegistry registry) { + registry.addInterceptor( + new MetricsHandlerInterceptor(this.webMvcMetrics)); + } + + } + + } + + static class Exception1 extends RuntimeException { + + } + + static class Exception2 extends RuntimeException { + + } + + @ControllerAdvice + static class CustomExceptionHandler { + + @Autowired + WebMvcMetrics metrics; + + @ExceptionHandler + ResponseEntity handleError(Exception1 ex) throws Throwable { + this.metrics.tagWithException(ex); + return new ResponseEntity<>("this is a custom exception body", + HttpStatus.INTERNAL_SERVER_ERROR); + } + + @ExceptionHandler + ResponseEntity rethrowError(Exception2 ex) throws Throwable { + throw ex; + } + + } + +} diff --git a/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/metrics/writer/DefaultCounterServiceTests.java b/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/metrics/writer/DefaultCounterServiceTests.java deleted file mode 100644 index 0aa4ada20216..000000000000 --- a/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/metrics/writer/DefaultCounterServiceTests.java +++ /dev/null @@ -1,87 +0,0 @@ -/* - * Copyright 2012-2016 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.boot.actuate.metrics.writer; - -import org.junit.Before; -import org.junit.Test; -import org.mockito.ArgumentCaptor; -import org.mockito.Captor; -import org.mockito.MockitoAnnotations; - -import static org.assertj.core.api.Assertions.assertThat; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.verify; - -/** - * Tests for {@link DefaultCounterService}. - * - * @author Dave Syer - */ -public class DefaultCounterServiceTests { - - private final MetricWriter repository = mock(MetricWriter.class); - - private final DefaultCounterService service = new DefaultCounterService( - this.repository); - - @Captor - private ArgumentCaptor> captor; - - @Before - public void setup() { - MockitoAnnotations.initMocks(this); - } - - @Test - public void incrementWithExistingCounter() { - this.service.increment("counter.foo"); - verify(this.repository).increment(this.captor.capture()); - assertThat(this.captor.getValue().getName()).isEqualTo("counter.foo"); - assertThat(this.captor.getValue().getValue()).isEqualTo(1L); - } - - @Test - public void incrementWithExistingNearCounter() { - this.service.increment("counter-foo"); - verify(this.repository).increment(this.captor.capture()); - assertThat(this.captor.getValue().getName()).isEqualTo("counter.counter-foo"); - assertThat(this.captor.getValue().getValue()).isEqualTo(1L); - } - - @Test - public void incrementPrependsCounter() { - this.service.increment("foo"); - verify(this.repository).increment(this.captor.capture()); - assertThat(this.captor.getValue().getName()).isEqualTo("counter.foo"); - assertThat(this.captor.getValue().getValue()).isEqualTo(1L); - } - - @Test - public void decrementPrependsCounter() { - this.service.decrement("foo"); - verify(this.repository).increment(this.captor.capture()); - assertThat(this.captor.getValue().getName()).isEqualTo("counter.foo"); - assertThat(this.captor.getValue().getValue()).isEqualTo(-1L); - } - - @Test - public void resetResetsCounter() throws Exception { - this.service.reset("foo"); - verify(this.repository).reset("counter.foo"); - } - -} diff --git a/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/metrics/writer/DefaultGaugeServiceTests.java b/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/metrics/writer/DefaultGaugeServiceTests.java deleted file mode 100644 index d9d874023cad..000000000000 --- a/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/metrics/writer/DefaultGaugeServiceTests.java +++ /dev/null @@ -1,49 +0,0 @@ -/* - * Copyright 2012-2016 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.boot.actuate.metrics.writer; - -import org.junit.Test; -import org.mockito.ArgumentCaptor; - -import org.springframework.boot.actuate.metrics.Metric; - -import static org.assertj.core.api.Assertions.assertThat; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.verify; - -/** - * Tests for {@link DefaultGaugeService}. - * - * @author Dave Syer - */ -public class DefaultGaugeServiceTests { - - private final MetricWriter repository = mock(MetricWriter.class); - - private final DefaultGaugeService service = new DefaultGaugeService(this.repository); - - @Test - public void setPrependsGauge() { - this.service.submit("foo", 2.3); - @SuppressWarnings("rawtypes") - ArgumentCaptor captor = ArgumentCaptor.forClass(Metric.class); - verify(this.repository).set(captor.capture()); - assertThat(captor.getValue().getName()).isEqualTo("gauge.foo"); - assertThat(captor.getValue().getValue()).isEqualTo(2.3); - } - -} diff --git a/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/metrics/writer/MessageChannelMetricWriterTests.java b/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/metrics/writer/MessageChannelMetricWriterTests.java deleted file mode 100644 index 2f42dee70741..000000000000 --- a/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/metrics/writer/MessageChannelMetricWriterTests.java +++ /dev/null @@ -1,82 +0,0 @@ -/* - * 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.metrics.writer; - -import org.junit.Before; -import org.junit.Test; -import org.mockito.Mock; -import org.mockito.MockitoAnnotations; - -import org.springframework.boot.actuate.metrics.Metric; -import org.springframework.messaging.Message; -import org.springframework.messaging.MessageChannel; - -import static org.mockito.ArgumentMatchers.any; -import static org.mockito.BDDMockito.given; -import static org.mockito.Mockito.verify; - -/** - * Tests for {@link MessageChannelMetricWriter} and {@link MetricWriterMessageHandler}. - * - * @author Dave Syer - */ -public class MessageChannelMetricWriterTests { - - @Mock - private MessageChannel channel; - - @Mock - private MetricWriter observer; - - private MessageChannelMetricWriter writer; - - private MetricWriterMessageHandler handler; - - @Before - public void setup() { - MockitoAnnotations.initMocks(this); - given(this.channel.send(any(Message.class))).willAnswer((invocation) -> { - MessageChannelMetricWriterTests.this.handler - .handleMessage(invocation.getArgument(0)); - return true; - }); - this.writer = new MessageChannelMetricWriter(this.channel); - this.handler = new MetricWriterMessageHandler(this.observer); - } - - @Test - public void messageSentOnAdd() { - this.writer.increment(new Delta<>("foo", 1)); - verify(this.channel).send(any(Message.class)); - verify(this.observer).increment(any(Delta.class)); - } - - @Test - public void messageSentOnSet() { - this.writer.set(new Metric<>("foo", 1d)); - verify(this.channel).send(any(Message.class)); - verify(this.observer).set(any(Metric.class)); - } - - @Test - public void messageSentOnReset() throws Exception { - this.writer.reset("foo"); - verify(this.channel).send(any(Message.class)); - verify(this.observer).reset("foo"); - } - -} diff --git a/spring-boot-dependencies/pom.xml b/spring-boot-dependencies/pom.xml index 0ba18697d1f2..411ed77fe23e 100644 --- a/spring-boot-dependencies/pom.xml +++ b/spring-boot-dependencies/pom.xml @@ -132,6 +132,7 @@ 1.2.3 1.16.18 2.1.1 + 0.11.0.RELEASE 6.2.1.jre8 2.9.0 1.5.0 @@ -834,6 +835,46 @@ lettuce-core ${lettuce.version} + + io.micrometer + micrometer-core + ${micrometer.version} + + + io.micrometer + micrometer-atlas-starter + ${micrometer.version} + + + io.micrometer + micrometer-datadog-starter + ${micrometer.version} + + + io.micrometer + micrometer-influx-starter + ${micrometer.version} + + + io.micrometer + micrometer-ganglia-starter + ${micrometer.version} + + + io.micrometer + micrometer-graphite-starter + ${micrometer.version} + + + io.micrometer + micrometer-jmx-starter + ${micrometer.version} + + + io.micrometer + micrometer-prometheus-starter + ${micrometer.version} + io.netty netty-bom diff --git a/spring-boot-docs/src/main/asciidoc/appendix-application-properties.adoc b/spring-boot-docs/src/main/asciidoc/appendix-application-properties.adoc index b29cd04682cd..412172fdfc2b 100644 --- a/spring-boot-docs/src/main/asciidoc/appendix-application-properties.adoc +++ b/spring-boot-docs/src/main/asciidoc/appendix-application-properties.adoc @@ -1161,6 +1161,11 @@ content into your application; rather pick only the properties that you need. endpoints.metrics.jmx.enabled= # Expose the metrics endpoint as a JMX MBean. endpoints.metrics.web.enabled= # Expose the metrics endpoint as a Web endpoint. + # PROMETHEUS ENDPOINT ({sc-spring-boot-actuator}/metrics/export/prometheus/PrometheusScrapeEndpoint.{sc-ext}[PrometheusScrapeEndpoint]) + endpoints.prometheus.cache.time-to-live=0 # Maximum time in milliseconds that a response can be cached. + endpoints.prometheus.enabled= # Enable the metrics endpoint. + endpoints.prometheus.web.enabled= # Expose the metrics endpoint as a Web endpoint. + # SHUTDOWN ENDPOINT ({sc-spring-boot-actuator}/context/ShutdownEndpoint.{sc-ext}[ShutdownEndpoint]) endpoints.shutdown.cache.time-to-live=0 # Maximum time in milliseconds that a response can be cached. endpoints.shutdown.enabled=false # Enable the shutdown endpoint. @@ -1257,29 +1262,18 @@ content into your application; rather pick only the properties that you need. management.jolokia.enabled=false # Enable Jolokia. management.jolokia.path=/jolokia # Path at which Jolokia will be available. - # METRICS FILTER ({sc-spring-boot-actuator}/autoconfigure/metrics/MetricFilterProperties.{sc-ext}[MetricFilterProperties]) - management.metrics.filter.counter-submissions= # Submissions that should be made to the counter. - management.metrics.filter.enabled=true # Enable the metrics servlet filter. - management.metrics.filter.gauge-submissions= # Submissions that should be made to the gauge. - # TRACING ({sc-spring-boot-actuator}/trace/TraceProperties.{sc-ext}[TraceProperties]) management.trace.filter.enabled=true # Enable the trace servlet filter. management.trace.include=request-headers,response-headers,cookies,errors # Items to be included in the trace. - # METRICS EXPORT ({sc-spring-boot-actuator}/metrics/export/MetricExportProperties.{sc-ext}[MetricExportProperties]) - spring.metrics.export.aggregate.key-pattern= # Pattern that tells the aggregator what to do with the keys from the source repository. - spring.metrics.export.aggregate.prefix= # Prefix for global repository if active. - spring.metrics.export.delay-millis=5000 # Delay in milliseconds between export ticks. Metrics are exported to external sources on a schedule with this delay. - spring.metrics.export.enabled=true # Flag to enable metric export (assuming a MetricWriter is available). - spring.metrics.export.excludes= # List of patterns for metric names to exclude. Applied after the includes. - spring.metrics.export.includes= # List of patterns for metric names to include. - spring.metrics.export.redis.key=keys.spring.metrics # Key for redis repository export (if active). - spring.metrics.export.redis.prefix=spring.metrics # Prefix for redis repository if active. - spring.metrics.export.send-latest= # Flag to switch off any available optimizations based on not exporting unchanged metric values. - spring.metrics.export.statsd.host= # Host of a statsd server to receive exported metrics. - spring.metrics.export.statsd.port=8125 # Port of a statsd server to receive exported metrics. - spring.metrics.export.statsd.prefix= # Prefix for statsd exported metrics. - spring.metrics.export.triggers.*= # Specific trigger properties per MetricWriter bean name. + # METRICS + spring.metrics.use-global-registry=true # Whether or not auto-configured MeterRegistry implementations should be bound to the global static registry on Metrics + spring.metrics.web.client.record-request-percentiles=false # Whether or not instrumented requests record percentiles histogram buckets by default. + spring.metrics.web.client.requests-metric-name=http.client.requests # Name of the metric for sent requests. + spring.metrics.web.server.auto-time-requests=true Whether or not requests handled by Spring MVC or WebFlux should be automatically timed. + spring.metrics.web.server.record-request-percentiles=false # Whether or not instrumented requests record percentiles histogram buckets by default. + spring.metrics.web.server.requests-metric-name=http.server.requests # Name of the metric for received requests. + # ---------------------------------------- diff --git a/spring-boot-docs/src/main/asciidoc/production-ready-features.adoc b/spring-boot-docs/src/main/asciidoc/production-ready-features.adoc index 8ef9ab1a59c7..9d00851486aa 100644 --- a/spring-boot-docs/src/main/asciidoc/production-ready-features.adoc +++ b/spring-boot-docs/src/main/asciidoc/production-ready-features.adoc @@ -133,6 +133,10 @@ following additional endpoints can also be used: |Returns the contents of the logfile (if `logging.file` or `logging.path` properties have been set). Supports the use of the HTTP `Range` header to retrieve part of the log file's content. + +|`prometheus` +|Exposes metrics in a format that can be scraped by a Prometheus server. + |=== [[production-ready-endpoints-security]] @@ -851,433 +855,162 @@ logger (and use the default configuration instead). [[production-ready-metrics]] == Metrics -Spring Boot Actuator includes a metrics service with '`gauge`' and '`counter`' support. -A '`gauge`' records a single value; and a '`counter`' records a delta (an increment or -decrement). Spring Boot Actuator also provides a -{sc-spring-boot-actuator}/endpoint/PublicMetrics.{sc-ext}[`PublicMetrics`] interface that -you can implement to expose metrics that you cannot record via one of those two -mechanisms. Look at {sc-spring-boot-actuator}/endpoint/SystemPublicMetrics.{sc-ext}[`SystemPublicMetrics`] -for an example. - -Metrics for all HTTP requests are automatically recorded, so if you hit the `metrics` -endpoint you should see a response similar to this: - -[source,json,indent=0] ----- - { - "counter.status.200.root": 20, - "counter.status.200.metrics": 3, - "counter.status.200.star-star": 5, - "counter.status.401.root": 4, - "gauge.response.star-star": 6, - "gauge.response.root": 2, - "gauge.response.metrics": 3, - "classes": 5808, - "classes.loaded": 5808, - "classes.unloaded": 0, - "heap": 3728384, - "heap.committed": 986624, - "heap.init": 262144, - "heap.used": 52765, - "nonheap": 0, - "nonheap.committed": 77568, - "nonheap.init": 2496, - "nonheap.used": 75826, - "mem": 986624, - "mem.free": 933858, - "processors": 8, - "threads": 15, - "threads.daemon": 11, - "threads.peak": 15, - "threads.totalStarted": 42, - "uptime": 494836, - "instance.uptime": 489782, - "datasource.primary.active": 5, - "datasource.primary.usage": 0.25 - } ----- - -Here we can see basic `memory`, `heap`, `class loading`, `processor` and `thread pool` -information along with some HTTP metrics. In this instance the `root` ('`/`') and `/metrics` -URLs have returned `HTTP 200` responses `20` and `3` times respectively. It also appears -that the `root` URL returned `HTTP 401` (unauthorized) `4` times. The double asterisks (`star-star`) -comes from a request matched by Spring MVC as `+/**+` (normally a static resource). - -The `gauge` shows the last response time for a request. So the last request to `root` took -`2ms` to respond and the last to `/metrics` took `3ms`. - -NOTE: In this example we are actually accessing the endpoint over HTTP using the -`/metrics` URL, this explains why `metrics` appears in the response. - - - -[[production-ready-system-metrics]] -=== System metrics -The following system metrics are exposed by Spring Boot: - -* The total system memory in KB (`mem`) -* The amount of free memory in KB (`mem.free`) -* The number of processors (`processors`) -* The system uptime in milliseconds (`uptime`) -* The application context uptime in milliseconds (`instance.uptime`) -* The average system load (`systemload.average`) -* Heap information in KB (`heap`, `heap.committed`, `heap.init`, `heap.used`) -* Thread information (`threads`, `thread.peak`, `thread.daemon`) -* Class load information (`classes`, `classes.loaded`, `classes.unloaded`) -* Garbage collection information (`gc.xxx.count`, `gc.xxx.time`) - - - -[[production-ready-datasource-metrics]] -=== DataSource metrics -The following metrics are exposed for each supported `DataSource` defined in your -application: - -* The number of active connections (`datasource.xxx.active`) -* The current usage of the connection pool (`datasource.xxx.usage`). - -All data source metrics share the `datasource.` prefix. The prefix is further qualified -for each data source: - -* If the data source is the primary data source (that is either the only available data - source or the one flagged `@Primary` amongst the existing ones), the prefix is - `datasource.primary`. -* If the data source bean name ends with `DataSource`, the prefix is the name of the bean - without `DataSource` (i.e. `datasource.batch` for `batchDataSource`). -* In all other cases, the name of the bean is used. - -It is possible to override part or all of those defaults by registering a bean with a -customized version of `DataSourcePublicMetrics`. By default, Spring Boot provides metadata -for all supported data sources; you can add additional `DataSourcePoolMetadataProvider` -beans if your favorite data source isn't supported out of the box. See -`DataSourcePoolMetadataProvidersConfiguration` for examples. - - - -[[production-ready-datasource-cache]] -=== Cache metrics -The following metrics are exposed for each supported cache defined in your application: - -* The current size of the cache (`cache.xxx.size`) -* Hit ratio (`cache.xxx.hit.ratio`) -* Miss ratio (`cache.xxx.miss.ratio`) - -NOTE: Cache providers do not expose the hit/miss ratio in a consistent way. While some -expose an **aggregated** value (i.e. the hit ratio since the last time the stats were -cleared), others expose a **temporal** value (i.e. the hit ratio of the last second). -Check your caching provider documentation for more details. - -If two different cache managers happen to define the same cache, the name of the cache -is prefixed by the name of the `CacheManager` bean. - -It is possible to override part or all of those defaults by registering a bean with a -customized version of `CachePublicMetrics`. By default, Spring Boot provides cache -statistics for EhCache, Hazelcast, Infinispan, JCache and Caffeine. You can add additional -`CacheStatisticsProvider` beans if your favorite caching library isn't supported out of -the box. See `CacheStatisticsAutoConfiguration` for examples. - - -[[production-ready-session-metrics]] -=== Tomcat session metrics -If you are using Tomcat as your embedded servlet container, session metrics will -automatically be exposed. The `httpsessions.active` and `httpsessions.max` keys provide -the number of active and maximum sessions. - - - -[[production-ready-recording-metrics]] -=== Recording your own metrics -To record your own metrics inject a -{sc-spring-boot-actuator}/metrics/CounterService.{sc-ext}[`CounterService`] and/or -{sc-spring-boot-actuator}/metrics/GaugeService.{sc-ext}[`GaugeService`] into -your bean. The `CounterService` exposes `increment`, `decrement` and `reset` methods; the -`GaugeService` provides a `submit` method. - -Here is a simple example that counts the number of times that a method is invoked: - -[source,java,indent=0] ----- - import org.springframework.beans.factory.annotation.Autowired; - import org.springframework.boot.actuate.metrics.CounterService; - import org.springframework.stereotype.Service; - - @Service - public class MyService { - - private final CounterService counterService; - - @Autowired - public MyService(CounterService counterService) { - this.counterService = counterService; - } - - public void exampleMethod() { - this.counterService.increment("services.system.myservice.invoked"); - } +Spring Boot Actuator provides dependency management and auto-configuration for +https://micrometer.io[Micrometer], an application metrics facade that supports numerous +monitoring systems: - } ----- +- https://github.com/Netflix/atlas[Atlas] +- https://www.datadoghq.com[Datadog] +- http://ganglia.sourceforge.net[Ganglia] +- https://graphiteapp.org[Graphite] +- https://www.influxdata.com[Influx] +- https://prometheus.io[Prometheus] -TIP: You can use any string as a metric name but you should follow guidelines of your chosen -store/graphing technology. Some good guidelines for Graphite are available on -http://matt.aimonetti.net/posts/2013/06/26/practical-guide-to-graphite-monitoring/[Matt Aimonetti's Blog]. +Micrometer provides a separate module for each supported monitoring system. Depending on +one (or more) of these modules is sufficient to get started with Micrometer in your +Spring Boot application. To learn more about Micrometer's capabilities, please refer to +its https://micrometer.io/docs[reference documentation]. -[[production-ready-public-metrics]] -=== Adding your own public metrics -To add additional metrics that are computed every time the metrics endpoint is invoked, -simply register additional `PublicMetrics` implementation bean(s). By default, all such -beans are gathered by the endpoint. You can easily change that by defining your own -`MetricsEndpoint`. +[[production-ready-metrics-spring-mvc]] +=== Spring MVC metrics +Auto-configuration will enable the instrumentation of requests handled by Spring MVC. +When `metrics.web.server.auto-time-requests` is `true`, this instrumentation will occur +for all requests. Alternatively, when set to `false`, instrumentation can be enabled +by adding `@Timed` to a request-handling method. +Metrics will, by default, be generated with the name `http.server.requests`. The name +can be customized using the `metrics.web.server.requests-metrics-name` property. -[[production-ready-metric-writers]] -=== Metric writers, exporters and aggregation -Spring Boot provides a couple of implementations of a marker interface called `Exporter` -which can be used to copy metric readings from the in-memory buffers to a place where they -can be analyzed and displayed. Indeed, if you provide a `@Bean` that implements the -`MetricWriter` interface (or `GaugeWriter` for simple use cases) and mark it -`@ExportMetricWriter`, then it will automatically be hooked up to an `Exporter` and fed -metric updates every 5 seconds (configured via `spring.metrics.export.delay-millis`). -In addition, any `MetricReader` that you define and mark as `@ExportMetricReader` will -have its values exported by the default exporter. -NOTE: This feature is enabling scheduling in your application (`@EnableScheduling`) which -can be a problem if you run an integration test as your own scheduled tasks will start. -You can disable this behaviour by setting `spring.metrics.export.enabled` to `false`. +[[production-ready-metrics-spring-mvc-tags]] +==== Spring MVC metric tags +Spring MVC-related metrics will, by default, be tagged with the following: -The default exporter is a `MetricCopyExporter` which tries to optimize itself by not -copying values that haven't changed since it was last called (the optimization can be -switched off using a flag `spring.metrics.export.send-latest`). Note also that the -Dropwizard `MetricRegistry` has no support for timestamps, so the optimization is not -available if you are using Dropwizard metrics (all metrics will be copied on every tick). +- Request's method, +- Request's URI (templated if possible) +- Simple class name of any exception that was thrown while handling the request +- Response's status -The default values for the export trigger (`delay-millis`, `includes`, `excludes` -and `send-latest`) can be set as `spring.metrics.export.\*`. Individual -values for specific `MetricWriters` can be set as -`spring.metrics.export.triggers..*` where `` is a bean name (or pattern for -matching bean names). +To customize the tags, provide a `@Bean` that implements `WebMvcTagsProvider`. -WARNING: The automatic export of metrics is disabled if you switch off the default -`MetricRepository` (e.g. by using Dropwizard metrics). You can get back the same -functionality be declaring a bean of your own of type `MetricReader` and declaring it to -be `@ExportMetricReader`. +[[production-ready-metrics-web-flux]] +=== WebFlux metrics +Auto-configuration will enable the instrumentation of all requests handled by WebFlux +controllers. A helper class, `RouterFunctionMetrics`, is also provided that can be +used to instrument applications using WebFlux's funtional programming model. +Metrics will, by default, be generated with the name `http.server.requests`. The name +can be customized using the `metrics.web.server.requests-metrics-name` property. -[[production-ready-metric-writers-export-to-redis]] -==== Example: Export to Redis -If you provide a `@Bean` of type `RedisMetricRepository` and mark it `@ExportMetricWriter` -the metrics are exported to a Redis cache for aggregation. The `RedisMetricRepository` has -two important parameters to configure it for this purpose: `prefix` and `key` (passed into -its constructor). It is best to use a prefix that is unique to the application instance -(e.g. using a random value and maybe the logical name of the application to make it -possible to correlate with other instances of the same application). The "`key`" is used -to keep a global index of all metric names, so it should be unique "`globally`", whatever -that means for your system (e.g. two instances of the same system could share a Redis cache -if they have distinct keys). -Example: -[source,java,indent=0] ----- - @Bean - @ExportMetricWriter - MetricWriter metricWriter(MetricExportProperties export) { - return new RedisMetricRepository(connectionFactory, - export.getRedis().getPrefix(), export.getRedis().getKey()); - } ----- +[[production-ready-metrics-web-flux-tags]] +==== WebFlux metric tags +WebFlux-related metrics for the annotation-based programming model will, by default, +be tagged with the following: -.application.properties -[source,properties,indent=0] ----- - spring.metrics.export.redis.prefix: metrics.mysystem.${spring.application.name:application}.${random.value:0000} - spring.metrics.export.redis.key: keys.metrics.mysystem ----- +- Request's method, +- Request's URI (templated if possible) +- Simple class name of any exception that was thrown while handling the request +- Response's status -The prefix is constructed with the application name and id at the end, so it can easily be used -to identify a group of processes with the same logical name later. +To customize the tags, provide a `@Bean` that implements `WebFluxTagsProvider`. -NOTE: It's important to set both the `key` and the `prefix`. The key is used for all -repository operations, and can be shared by multiple repositories. If multiple -repositories share a key (like in the case where you need to aggregate across them), then -you normally have a read-only "`master`" repository that has a short, but identifiable, -prefix (like "`metrics.mysystem`"), and many write-only repositories with prefixes that -start with the master prefix (like `metrics.mysystem.*` in the example above). It is -efficient to read all the keys from a "`master`" repository like that, but inefficient to -read a subset with a longer prefix (e.g. using one of the writing repositories). +Metrics for the functional programming model will, by default, be tagged with the +following: -TIP: The example above uses `MetricExportProperties` to inject and extract the key and -prefix. This is provided to you as a convenience by Spring Boot, configured with sensible -defaults. There is nothing to stop you using your own values as long as they follow the -recommendations. +- Request's method, +- Request's URI (templated if possible) +- Response's status +To customize the tags, use the `defaultTags` method on the `RouterFunctionMetrics` +instance that you are using. -[[production-ready-metric-writers-export-to-open-tsdb]] -==== Example: Export to Open TSDB -If you provide a `@Bean` of type `OpenTsdbGaugeWriter` and mark it -`@ExportMetricWriter` metrics are exported to http://opentsdb.net/[Open TSDB] for -aggregation. The `OpenTsdbGaugeWriter` has a `url` property that you need to set -to the Open TSDB "`/put`" endpoint, e.g. `http://localhost:4242/api/put`). It also has a -`namingStrategy` that you can customize or configure to make the metrics match the data -structure you need on the server. By default it just passes through the metric name as an -Open TSDB metric name, and adds the tags "`domain`" (with value -"`org.springframework.metrics`") and "`process`" (with the value equal to the object hash -of the naming strategy). Thus, after running the application and generating some metrics -you can inspect the metrics in the TSD UI (http://localhost:4242 by default). -Example: +[[production-ready-metrics-rest-template]] +=== RestTemplate metrics +Auto-configuration will customize the auto-configured `RestTemplate` to enable the +instrumentation of its requests. `MetricsRestTemplateCustomizer` can be used to +customize your own `RestTemplate` instances. -[source,indent=0] ----- - curl localhost:4242/api/query?start=1h-ago&m=max:counter.status.200.root - [ - { - "metric": "counter.status.200.root", - "tags": { - "domain": "org.springframework.metrics", - "process": "b968a76" - }, - "aggregateTags": [], - "dps": { - "1430492872": 2, - "1430492875": 6 - } - } - ] ----- +Metrics will, by default, be generated with the name `http.client.requests`. The name +can be customized using the `metrics.web.client.requests-metrics-name` property. -[[production-ready-metric-writers-export-to-statsd]] -==== Example: Export to Statsd -To export metrics to Statsd, make sure first that you have added -`com.timgroup:java-statsd-client` as a dependency of your project (Spring Boot -provides a dependency management for it). Then add a `spring.metrics.export.statsd.host` -value to your `application.properties` file. Connections will be opened to port `8125` -unless a `spring.metrics.export.statsd.port` override is provided. You can use -`spring.metrics.export.statsd.prefix` if you want a custom prefix. +[[production-ready-metrics-rest-template-tags]] +==== RestTemplate metric tags +Metrics generated by an instrumeted `RestTemplate` will, by default, be tagged with +the following: -Alternatively, you can provide a `@Bean` of type `StatsdMetricWriter` and mark it -`@ExportMetricWriter`: - -[source,java,indent=0] ----- - @Value("${spring.application.name:application}.${random.value:0000}") - private String prefix = "metrics"; - - @Bean - @ExportMetricWriter - MetricWriter metricWriter() { - return new StatsdMetricWriter(prefix, "localhost", 8125); - } ----- +- Request's method +- Request's URI (templated if possible) +- Response's status +- Request URI's host -[[production-ready-metric-writers-export-to-jmx]] -==== Example: Export to JMX -If you provide a `@Bean` of type `JmxMetricWriter` marked `@ExportMetricWriter` the metrics are exported as MBeans to -the local server (the `MBeanExporter` is provided by Spring Boot JMX auto-configuration as -long as it is switched on). Metrics can then be inspected, graphed, alerted etc. using any -tool that understands JMX (e.g. JConsole or JVisualVM). +[[production-ready-metrics-integration]] +Auto-configuration will enable binding of a number of Spring Integration-related +metrics: -Example: - -[source,java,indent=0] ----- - @Bean - @ExportMetricWriter - MetricWriter metricWriter(MBeanExporter exporter) { - return new JmxMetricWriter(exporter); - } ----- - -Each metric is exported as an individual MBean. The format for the `ObjectNames` is given -by an `ObjectNamingStrategy` which can be injected into the `JmxMetricWriter` (the default -breaks up the metric name and tags the first two period-separated sections in a way that -should make the metrics group nicely in JVisualVM or JConsole). - - - -[[production-ready-metric-aggregation]] -=== Aggregating metrics from multiple sources -There is an `AggregateMetricReader` that you can use to consolidate metrics from different -physical sources. Sources for the same logical metric just need to publish them with a -period-separated prefix, and the reader will aggregate (by truncating the metric names, -and dropping the prefix). Counters are summed and everything else (i.e. gauges) take their -most recent value. - -This is very useful if multiple application instances are feeding to a central (e.g. -Redis) repository and you want to display the results. Particularly recommended in -conjunction with a `MetricReaderPublicMetrics` for hooking up to the results to the -"`/metrics`" endpoint. +.General metrics +|=== +| Metric | Description -Example: +| `spring.integration.channelNames` +| Number of Spring Integration channels -[source,java,indent=0] ----- - @Autowired - private MetricExportProperties export; +| `spring.integration.handlerNames` +| Number of Spring Integration handlers - @Bean - public PublicMetrics metricsAggregate() { - return new MetricReaderPublicMetrics(aggregatesMetricReader()); - } +| `spring.integration.sourceNames` +| Number of Spring Integration sources +|=== - private MetricReader globalMetricsForAggregation() { - return new RedisMetricRepository(this.connectionFactory, - this.export.getRedis().getAggregatePrefix(), this.export.getRedis().getKey()); - } +.Channel metrics +|=== +| Metric | Description - private MetricReader aggregatesMetricReader() { - AggregateMetricReader repository = new AggregateMetricReader( - globalMetricsForAggregation()); - return repository; - } ----- +| `spring.integration.channel.receives` +| Number of receives -NOTE: The example above uses `MetricExportProperties` to inject and extract the key and -prefix. This is provided to you as a convenience by Spring Boot, and the defaults will be -sensible. They are set up in `MetricExportAutoConfiguration`. +| `spring.integration.channel.sendErrors` +| Number of failed sends -NOTE: The `MetricReaders` above are not `@Beans` and are not marked as -`@ExportMetricReader` because they are just collecting and analyzing data from other -repositories, and don't want to export their values. +| `spring.integration.channel.sends` +| Number of successful sends +|=== +.Handler metrics +|=== +| Metric | Description +| `spring.integration.handler.duration.max` +| Maximum handler duration in milliseconds -[[production-ready-dropwizard-metrics]] -=== Dropwizard Metrics -A default `MetricRegistry` Spring bean will be created when you declare a dependency to -the `io.dropwizard.metrics:metrics-core` library; you can also register you own `@Bean` -instance if you need customizations. Users of the -https://dropwizard.github.io/metrics/[Dropwizard '`Metrics`' library] will find that -Spring Boot metrics are automatically published to `com.codahale.metrics.MetricRegistry`. -Metrics from the `MetricRegistry` are also automatically exposed via the `/metrics` -endpoint +| `spring.integration.handler.duration.min` +| Minimum handler duration in milliseconds -When Dropwizard metrics are in use, the default `CounterService` and `GaugeService` are -replaced with a `DropwizardMetricServices`, which is a wrapper around the `MetricRegistry` -(so you can `@Autowired` one of those services and use it as normal). You can also create -"`special`" Dropwizard metrics by prefixing your metric names with the appropriate type -(i.e. `+timer.*+`, `+histogram.*+` for gauges, and `+meter.*+` for counters). +| `spring.integration.handler.duration.mean` +| Mean handler duration in milliseconds +| `spring.integration.handler.activeCount` +| Number of active handlers +|=== +.Source metrics +|=== +| Metric | Description -[[production-ready-metrics-message-channel-integration]] -=== Message channel integration -If a `MessageChannel` bean called `metricsChannel` exists, then a `MetricWriter` will be -created that writes metrics to that channel. Each message sent to the channel will contain -a {dc-spring-boot-actuator}/metrics/writer/Delta.{dc-ext}[`Delta`] or -{dc-spring-boot-actuator}/metrics/Metric.{dc-ext}[`Metric`] payload and have a `metricName` -header. The writer is automatically hooked up to an exporter (as for all writers), so all -metric values will appear on the channel, and additional analysis or actions can be taken -by subscribers (it's up to you to provide the channel and any subscribers you need). +| `spring.integration.source.messages` +| Number of successful source calls +|=== diff --git a/spring-boot-parent/src/checkstyle/checkstyle.xml b/spring-boot-parent/src/checkstyle/checkstyle.xml index f0f7a1303407..8acb0a5eb81c 100644 --- a/spring-boot-parent/src/checkstyle/checkstyle.xml +++ b/spring-boot-parent/src/checkstyle/checkstyle.xml @@ -70,7 +70,7 @@ + value="org.assertj.core.api.Assertions.*, org.junit.Assert.*, org.junit.Assume.*, org.junit.internal.matchers.ThrowableMessageMatcher.*, org.hamcrest.CoreMatchers.*, org.hamcrest.Matchers.*, org.springframework.boot.configurationprocessor.ConfigurationMetadataMatchers.*, org.springframework.boot.configurationprocessor.TestCompiler.*, org.springframework.boot.test.autoconfigure.AutoConfigurationImportedCondition.*, org.mockito.Mockito.*, org.mockito.BDDMockito.*, org.mockito.ArgumentMatchers.*, org.mockito.Matchers.*, org.springframework.restdocs.mockmvc.MockMvcRestDocumentation.*, org.springframework.restdocs.hypermedia.HypermediaDocumentation.*, org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*, org.springframework.test.web.servlet.result.MockMvcResultMatchers.*, org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.*, org.springframework.security.test.web.servlet.setup.SecurityMockMvcConfigurers.*, org.springframework.hateoas.mvc.ControllerLinkBuilder.linkTo, org.springframework.test.web.client.ExpectedCount.*, org.springframework.test.web.client.match.MockRestRequestMatchers.*, org.springframework.test.web.client.response.MockRestResponseCreators.*" /> diff --git a/spring-boot-samples/pom.xml b/spring-boot-samples/pom.xml index 48f9c578a7b6..78dfbb838e14 100644 --- a/spring-boot-samples/pom.xml +++ b/spring-boot-samples/pom.xml @@ -62,9 +62,6 @@ spring-boot-sample-junit-jupiter spring-boot-sample-liquibase spring-boot-sample-logback - spring-boot-sample-metrics-dropwizard - spring-boot-sample-metrics-opentsdb - spring-boot-sample-metrics-redis spring-boot-sample-parent-context spring-boot-sample-profile spring-boot-sample-property-validation diff --git a/spring-boot-samples/spring-boot-sample-actuator-noweb/src/test/java/sample/actuator/noweb/SampleActuatorNoWebApplicationTests.java b/spring-boot-samples/spring-boot-sample-actuator-noweb/src/test/java/sample/actuator/noweb/SampleActuatorNoWebApplicationTests.java index 0b624a9074af..d52ffb1f974e 100644 --- a/spring-boot-samples/spring-boot-sample-actuator-noweb/src/test/java/sample/actuator/noweb/SampleActuatorNoWebApplicationTests.java +++ b/spring-boot-samples/spring-boot-sample-actuator-noweb/src/test/java/sample/actuator/noweb/SampleActuatorNoWebApplicationTests.java @@ -19,14 +19,9 @@ import org.junit.Test; import org.junit.runner.RunWith; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.boot.actuate.metrics.MetricsEndpoint; import org.springframework.boot.test.context.SpringBootTest; -import org.springframework.test.annotation.DirtiesContext; import org.springframework.test.context.junit4.SpringRunner; -import static org.assertj.core.api.Assertions.assertThat; - /** * Basic integration tests for service demo application. * @@ -34,15 +29,11 @@ */ @RunWith(SpringRunner.class) @SpringBootTest -@DirtiesContext public class SampleActuatorNoWebApplicationTests { - @Autowired - private MetricsEndpoint endpoint; - @Test - public void endpointsExist() throws Exception { - assertThat(this.endpoint).isNotNull(); + public void contextLoads() { + } } diff --git a/spring-boot-samples/spring-boot-sample-actuator/src/test/java/sample/actuator/SampleActuatorApplicationTests.java b/spring-boot-samples/spring-boot-sample-actuator/src/test/java/sample/actuator/SampleActuatorApplicationTests.java index 4ffb5215237a..32d93b216276 100644 --- a/spring-boot-samples/spring-boot-sample-actuator/src/test/java/sample/actuator/SampleActuatorApplicationTests.java +++ b/spring-boot-samples/spring-boot-sample-actuator/src/test/java/sample/actuator/SampleActuatorApplicationTests.java @@ -89,6 +89,7 @@ public void testHome() throws Exception { assertThat(body.get("message")).isEqualTo("Hello Phil"); } + @SuppressWarnings("unchecked") @Test public void testMetrics() throws Exception { testHome(); // makes sure some requests have been made @@ -97,9 +98,10 @@ public void testMetrics() throws Exception { .withBasicAuth("user", getPassword()) .getForEntity("/application/metrics", Map.class); assertThat(entity.getStatusCode()).isEqualTo(HttpStatus.OK); - @SuppressWarnings("unchecked") Map body = entity.getBody(); - assertThat(body).containsKey("counter.status.200.root"); + assertThat(body).containsKey("names"); + assertThat((List) body.get("names")).contains("jvm.buffer.count"); + } @Test diff --git a/spring-boot-samples/spring-boot-sample-metrics-dropwizard/pom.xml b/spring-boot-samples/spring-boot-sample-metrics-dropwizard/pom.xml deleted file mode 100644 index 02ab992a4f95..000000000000 --- a/spring-boot-samples/spring-boot-sample-metrics-dropwizard/pom.xml +++ /dev/null @@ -1,57 +0,0 @@ - - - 4.0.0 - - - org.springframework.boot - spring-boot-samples - 2.0.0.BUILD-SNAPSHOT - - spring-boot-sample-metrics-dropwizard - Spring Boot Metrics Dropwizard Sample - Spring Boot Metrics Dropwizard Sample - http://projects.spring.io/spring-boot/ - - Pivotal Software, Inc. - http://www.spring.io - - - ${basedir}/../.. - - - - - org.springframework.boot - spring-boot-starter-actuator - - - org.springframework.boot - spring-boot-starter-web - - - io.dropwizard.metrics - metrics-core - - - - org.springframework.boot - spring-boot-configuration-processor - true - - - - org.springframework.boot - spring-boot-starter-test - test - - - - - - org.springframework.boot - spring-boot-maven-plugin - - - - diff --git a/spring-boot-samples/spring-boot-sample-metrics-dropwizard/src/main/java/sample/metrics/dropwizard/HelloWorldProperties.java b/spring-boot-samples/spring-boot-sample-metrics-dropwizard/src/main/java/sample/metrics/dropwizard/HelloWorldProperties.java deleted file mode 100644 index 0926914e96f9..000000000000 --- a/spring-boot-samples/spring-boot-sample-metrics-dropwizard/src/main/java/sample/metrics/dropwizard/HelloWorldProperties.java +++ /dev/null @@ -1,37 +0,0 @@ -/* - * Copyright 2012-2016 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 sample.metrics.dropwizard; - -import org.springframework.boot.context.properties.ConfigurationProperties; - -@ConfigurationProperties(prefix = "service", ignoreUnknownFields = false) -public class HelloWorldProperties { - - /** - * Name of the service. - */ - private String name = "World"; - - public String getName() { - return this.name; - } - - public void setName(String name) { - this.name = name; - } - -} diff --git a/spring-boot-samples/spring-boot-sample-metrics-dropwizard/src/main/java/sample/metrics/dropwizard/SampleController.java b/spring-boot-samples/spring-boot-sample-metrics-dropwizard/src/main/java/sample/metrics/dropwizard/SampleController.java deleted file mode 100644 index 184f28d65bdd..000000000000 --- a/spring-boot-samples/spring-boot-sample-metrics-dropwizard/src/main/java/sample/metrics/dropwizard/SampleController.java +++ /dev/null @@ -1,50 +0,0 @@ -/* - * Copyright 2012-2016 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 sample.metrics.dropwizard; - -import java.util.Collections; -import java.util.Map; - -import org.springframework.boot.actuate.metrics.GaugeService; -import org.springframework.context.annotation.Description; -import org.springframework.stereotype.Controller; -import org.springframework.web.bind.annotation.GetMapping; -import org.springframework.web.bind.annotation.ResponseBody; - -@Controller -@Description("A controller for handling requests for hello messages") -public class SampleController { - - private final HelloWorldProperties helloWorldProperties; - - private final GaugeService gauges; - - public SampleController(HelloWorldProperties helloWorldProperties, - GaugeService gauges) { - this.helloWorldProperties = helloWorldProperties; - this.gauges = gauges; - } - - @GetMapping("/") - @ResponseBody - public Map hello() { - this.gauges.submit("timer.test.value", Math.random() * 1000 + 1000); - return Collections.singletonMap("message", - "Hello " + this.helloWorldProperties.getName()); - } - -} diff --git a/spring-boot-samples/spring-boot-sample-metrics-dropwizard/src/main/java/sample/metrics/dropwizard/SampleDropwizardMetricsApplication.java b/spring-boot-samples/spring-boot-sample-metrics-dropwizard/src/main/java/sample/metrics/dropwizard/SampleDropwizardMetricsApplication.java deleted file mode 100644 index 362fde998554..000000000000 --- a/spring-boot-samples/spring-boot-sample-metrics-dropwizard/src/main/java/sample/metrics/dropwizard/SampleDropwizardMetricsApplication.java +++ /dev/null @@ -1,31 +0,0 @@ -/* - * Copyright 2012-2016 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 sample.metrics.dropwizard; - -import org.springframework.boot.SpringApplication; -import org.springframework.boot.autoconfigure.SpringBootApplication; -import org.springframework.boot.context.properties.EnableConfigurationProperties; - -@SpringBootApplication -@EnableConfigurationProperties(HelloWorldProperties.class) -public class SampleDropwizardMetricsApplication { - - public static void main(String[] args) throws Exception { - SpringApplication.run(SampleDropwizardMetricsApplication.class, args); - } - -} diff --git a/spring-boot-samples/spring-boot-sample-metrics-dropwizard/src/main/resources/application.properties b/spring-boot-samples/spring-boot-sample-metrics-dropwizard/src/main/resources/application.properties deleted file mode 100644 index 119a68aa4333..000000000000 --- a/spring-boot-samples/spring-boot-sample-metrics-dropwizard/src/main/resources/application.properties +++ /dev/null @@ -1,3 +0,0 @@ -management.security.enabled=false - -service.name=Phil diff --git a/spring-boot-samples/spring-boot-sample-metrics-dropwizard/src/test/java/sample/metrics/dropwizard/SampleDropwizardMetricsApplicationTests.java b/spring-boot-samples/spring-boot-sample-metrics-dropwizard/src/test/java/sample/metrics/dropwizard/SampleDropwizardMetricsApplicationTests.java deleted file mode 100644 index 1e5e02e53cdb..000000000000 --- a/spring-boot-samples/spring-boot-sample-metrics-dropwizard/src/test/java/sample/metrics/dropwizard/SampleDropwizardMetricsApplicationTests.java +++ /dev/null @@ -1,54 +0,0 @@ -/* - * Copyright 2012-2016 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 sample.metrics.dropwizard; - -import com.codahale.metrics.MetricRegistry; -import org.junit.Test; -import org.junit.runner.RunWith; - -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.boot.actuate.metrics.GaugeService; -import org.springframework.boot.test.context.SpringBootTest; -import org.springframework.boot.test.context.SpringBootTest.WebEnvironment; -import org.springframework.test.annotation.DirtiesContext; -import org.springframework.test.context.junit4.SpringRunner; - -import static org.assertj.core.api.Assertions.assertThat; - -/** - * Basic integration tests for {@link SampleDropwizardMetricsApplication}. - * - * @author Dave Syer - */ -@RunWith(SpringRunner.class) -@SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT) -@DirtiesContext -public class SampleDropwizardMetricsApplicationTests { - - @Autowired - private MetricRegistry registry; - - @Autowired - private GaugeService gauges; - - @Test - public void timerCreated() { - this.gauges.submit("timer.test", 1234); - assertThat(this.registry.getTimers().get("timer.test").getCount()).isEqualTo(1); - } - -} diff --git a/spring-boot-samples/spring-boot-sample-metrics-opentsdb/README.adoc b/spring-boot-samples/spring-boot-sample-metrics-opentsdb/README.adoc deleted file mode 100644 index 04abf268335e..000000000000 --- a/spring-boot-samples/spring-boot-sample-metrics-opentsdb/README.adoc +++ /dev/null @@ -1,29 +0,0 @@ -Spring Boot sample with Open TSDB export for metrics. - -Start opentsdb, e.g. with [Docker Compose]() - -[source,indent=0] ----- -$ docker-compose up ----- - -Run the app and ping the home page (http://localhost:8080) a few times. Go and look at -the result in the TSD UI, e.g. - -[source,indent=0] ----- - $ curl localhost:4242/api/query?start=1h-ago&m=max:counter.status.200.root - [ - { - "metric": "counter.status.200.root", - "tags": { - "prefix": "spring.b968a76" - }, - "aggregateTags": [], - "dps": { - "1430492872": 2, - "1430492875": 6 - } - } - ] ----- \ No newline at end of file diff --git a/spring-boot-samples/spring-boot-sample-metrics-opentsdb/docker-compose.yml b/spring-boot-samples/spring-boot-sample-metrics-opentsdb/docker-compose.yml deleted file mode 100644 index c9d453bd7c19..000000000000 --- a/spring-boot-samples/spring-boot-sample-metrics-opentsdb/docker-compose.yml +++ /dev/null @@ -1,4 +0,0 @@ -opentsdb: - image: lancope/opentsdb - ports: - - "4242:4242" diff --git a/spring-boot-samples/spring-boot-sample-metrics-opentsdb/pom.xml b/spring-boot-samples/spring-boot-sample-metrics-opentsdb/pom.xml deleted file mode 100644 index 05e4e74db2d8..000000000000 --- a/spring-boot-samples/spring-boot-sample-metrics-opentsdb/pom.xml +++ /dev/null @@ -1,52 +0,0 @@ - - - 4.0.0 - - - org.springframework.boot - spring-boot-samples - 2.0.0.BUILD-SNAPSHOT - - spring-boot-sample-metrics-opentsdb - Spring Boot Metrics OpenTSDB Sample - Spring Boot Metrics OpenTSDB Sample - http://projects.spring.io/spring-boot/ - - Pivotal Software, Inc. - http://www.spring.io - - - ${basedir}/../.. - - - - - org.springframework.boot - spring-boot-starter-actuator - - - org.springframework.boot - spring-boot-starter-web - - - - org.springframework.boot - spring-boot-configuration-processor - true - - - - org.springframework.boot - spring-boot-starter-test - test - - - - - - org.springframework.boot - spring-boot-maven-plugin - - - - diff --git a/spring-boot-samples/spring-boot-sample-metrics-opentsdb/src/main/java/sample/metrics/opentsdb/SampleController.java b/spring-boot-samples/spring-boot-sample-metrics-opentsdb/src/main/java/sample/metrics/opentsdb/SampleController.java deleted file mode 100644 index 4a64b4809756..000000000000 --- a/spring-boot-samples/spring-boot-sample-metrics-opentsdb/src/main/java/sample/metrics/opentsdb/SampleController.java +++ /dev/null @@ -1,60 +0,0 @@ -/* - * 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 sample.metrics.opentsdb; - -import java.util.Collections; -import java.util.Map; - -import org.hibernate.validator.constraints.NotBlank; - -import org.springframework.context.annotation.Description; -import org.springframework.stereotype.Controller; -import org.springframework.web.bind.annotation.GetMapping; -import org.springframework.web.bind.annotation.ResponseBody; - -@Controller -@Description("A controller for handling requests for hello messages") -public class SampleController { - - private final HelloWorldProperties helloWorldProperties; - - public SampleController(HelloWorldProperties helloWorldProperties) { - this.helloWorldProperties = helloWorldProperties; - } - - @GetMapping("/") - @ResponseBody - public Map hello() { - return Collections.singletonMap("message", - "Hello " + this.helloWorldProperties.getName()); - } - - protected static class Message { - - @NotBlank(message = "Message value cannot be empty") - private String value; - - public String getValue() { - return this.value; - } - - public void setValue(String value) { - this.value = value; - } - } - -} diff --git a/spring-boot-samples/spring-boot-sample-metrics-opentsdb/src/main/java/sample/metrics/opentsdb/SampleOpenTsdbExportApplication.java b/spring-boot-samples/spring-boot-sample-metrics-opentsdb/src/main/java/sample/metrics/opentsdb/SampleOpenTsdbExportApplication.java deleted file mode 100644 index 16ecf315b141..000000000000 --- a/spring-boot-samples/spring-boot-sample-metrics-opentsdb/src/main/java/sample/metrics/opentsdb/SampleOpenTsdbExportApplication.java +++ /dev/null @@ -1,52 +0,0 @@ -/* - * 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 sample.metrics.opentsdb; - -import org.springframework.boot.SpringApplication; -import org.springframework.boot.actuate.autoconfigure.metrics.ExportMetricWriter; -import org.springframework.boot.actuate.metrics.opentsdb.DefaultOpenTsdbNamingStrategy; -import org.springframework.boot.actuate.metrics.opentsdb.OpenTsdbGaugeWriter; -import org.springframework.boot.actuate.metrics.writer.GaugeWriter; -import org.springframework.boot.autoconfigure.SpringBootApplication; -import org.springframework.boot.context.properties.ConfigurationProperties; -import org.springframework.boot.context.properties.EnableConfigurationProperties; -import org.springframework.context.annotation.Bean; - -@SpringBootApplication -@EnableConfigurationProperties(HelloWorldProperties.class) -public class SampleOpenTsdbExportApplication { - - @Bean - @ConfigurationProperties("metrics.export") - @ExportMetricWriter - public GaugeWriter openTsdbMetricWriter() { - OpenTsdbGaugeWriter writer = new OpenTsdbGaugeWriter(); - writer.setNamingStrategy(namingStrategy()); - return writer; - } - - @Bean - @ConfigurationProperties("metrics.names") - public DefaultOpenTsdbNamingStrategy namingStrategy() { - return new DefaultOpenTsdbNamingStrategy(); - } - - public static void main(String[] args) throws Exception { - SpringApplication.run(SampleOpenTsdbExportApplication.class, args); - } - -} diff --git a/spring-boot-samples/spring-boot-sample-metrics-opentsdb/src/main/resources/application.properties b/spring-boot-samples/spring-boot-sample-metrics-opentsdb/src/main/resources/application.properties deleted file mode 100644 index e092b8e642f1..000000000000 --- a/spring-boot-samples/spring-boot-sample-metrics-opentsdb/src/main/resources/application.properties +++ /dev/null @@ -1,4 +0,0 @@ -management.security.enabled=false - -service.name=Phil -metrics.names.tags.process=${spring.application.name:application}:${random.value:0000} \ No newline at end of file diff --git a/spring-boot-samples/spring-boot-sample-metrics-opentsdb/src/test/java/sample/metrics/opentsdb/SampleOpenTsdbExportApplicationTests.java b/spring-boot-samples/spring-boot-sample-metrics-opentsdb/src/test/java/sample/metrics/opentsdb/SampleOpenTsdbExportApplicationTests.java deleted file mode 100644 index 83591718db84..000000000000 --- a/spring-boot-samples/spring-boot-sample-metrics-opentsdb/src/test/java/sample/metrics/opentsdb/SampleOpenTsdbExportApplicationTests.java +++ /dev/null @@ -1,42 +0,0 @@ -/* - * Copyright 2012-2016 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 sample.metrics.opentsdb; - -import org.junit.Test; -import org.junit.runner.RunWith; - -import org.springframework.boot.test.context.SpringBootTest; -import org.springframework.boot.test.context.SpringBootTest.WebEnvironment; -import org.springframework.test.annotation.DirtiesContext; -import org.springframework.test.context.junit4.SpringRunner; - -/** - * Basic integration tests for {@link SampleOpenTsdbExportApplication}. - * - * @author Dave Syer - */ -@RunWith(SpringRunner.class) -@SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT) -@DirtiesContext -public class SampleOpenTsdbExportApplicationTests { - - @Test - public void contextLoads() { - - } - -} diff --git a/spring-boot-samples/spring-boot-sample-metrics-redis/README.adoc b/spring-boot-samples/spring-boot-sample-metrics-redis/README.adoc deleted file mode 100644 index 6626e0c0c168..000000000000 --- a/spring-boot-samples/spring-boot-sample-metrics-redis/README.adoc +++ /dev/null @@ -1,46 +0,0 @@ -= Spring Boot sample with Redis export for metrics. - -Start redis, e.g. with [Docker Compose]() - -[source,indent=0] ----- - $ docker-compose up ----- - -Run the app and ping the home page (http://localhost:8080) a few times. Go and look at -the result in Redis, e.g. - -[source,indent=0] ----- - $ redis-cli - 127.0.0.1:6379> keys * - 1) "keys.spring.metrics" - 2) "spring.metrics.counter.status.200.root" - 3) "spring.metrics.gauge.response.root" - 127.0.0.1:6379> zrange keys.spring.metrics 0 0 WITHSCORES - 1) "spring.metrics.counter.status.200.root" - 2) "4" ----- - -There is also an `AggregateMetricReader` with public metrics in the application context, -and you can see the result in the "/metrics" (metrics with names in "aggregate.*"). -The way the Redis repository was set up (with a random key in the metric names) makes the -aggregates work across restarts of the same application, or across a scaled up application -running in multiple processes. E.g. - -[source,indent=0] ----- - $ curl localhost:8080/metrics - { - ... - "aggregate.application.counter.status.200.metrics": 12, - "aggregate.application.counter.status.200.root": 29, - "aggregate.application.gauge.response.metrics": 43, - "aggregate.application.gauge.response.root": 5, - "counter.status.200.root": 2, - "counter.status.200.metrics": 1, - "gauge.response.metrics": 43, - "gauge.response.root": 5 - } ----- - diff --git a/spring-boot-samples/spring-boot-sample-metrics-redis/docker-compose.yml b/spring-boot-samples/spring-boot-sample-metrics-redis/docker-compose.yml deleted file mode 100644 index e5a62d0ef497..000000000000 --- a/spring-boot-samples/spring-boot-sample-metrics-redis/docker-compose.yml +++ /dev/null @@ -1,4 +0,0 @@ -redis: - image: redis - ports: - - "6379:6379" diff --git a/spring-boot-samples/spring-boot-sample-metrics-redis/pom.xml b/spring-boot-samples/spring-boot-sample-metrics-redis/pom.xml deleted file mode 100644 index 6a7cfb1c857c..000000000000 --- a/spring-boot-samples/spring-boot-sample-metrics-redis/pom.xml +++ /dev/null @@ -1,57 +0,0 @@ - - - 4.0.0 - - - org.springframework.boot - spring-boot-samples - 2.0.0.BUILD-SNAPSHOT - - spring-boot-sample-metrics-redis - Spring Boot Metrics Redis Sample - Spring Boot Metrics Redis Sample - http://projects.spring.io/spring-boot/ - - Pivotal Software, Inc. - http://www.spring.io - - - ${basedir}/../.. - - - - - org.springframework.boot - spring-boot-starter-actuator - - - org.springframework.boot - spring-boot-starter-web - - - org.springframework.boot - spring-boot-starter-data-redis - - - - org.springframework.boot - spring-boot-configuration-processor - true - - - - org.springframework.boot - spring-boot-starter-test - test - - - - - - org.springframework.boot - spring-boot-maven-plugin - - - - diff --git a/spring-boot-samples/spring-boot-sample-metrics-redis/src/main/java/sample/metrics/redis/AggregateMetricsConfiguration.java b/spring-boot-samples/spring-boot-sample-metrics-redis/src/main/java/sample/metrics/redis/AggregateMetricsConfiguration.java deleted file mode 100644 index 7ce9da8dd6cf..000000000000 --- a/spring-boot-samples/spring-boot-sample-metrics-redis/src/main/java/sample/metrics/redis/AggregateMetricsConfiguration.java +++ /dev/null @@ -1,60 +0,0 @@ -/* - * 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 sample.metrics.redis; - -import org.springframework.boot.actuate.metrics.PublicMetrics; -import org.springframework.boot.actuate.metrics.aggregate.AggregateMetricReader; -import org.springframework.boot.actuate.metrics.export.MetricExportProperties; -import org.springframework.boot.actuate.metrics.reader.MetricReader; -import org.springframework.boot.actuate.metrics.reader.MetricReaderPublicMetrics; -import org.springframework.boot.actuate.metrics.repository.redis.RedisMetricRepository; -import org.springframework.context.annotation.Bean; -import org.springframework.context.annotation.Configuration; -import org.springframework.data.redis.connection.RedisConnectionFactory; - -@Configuration -public class AggregateMetricsConfiguration { - - private final MetricExportProperties export; - - private final RedisConnectionFactory connectionFactory; - - public AggregateMetricsConfiguration(MetricExportProperties export, - RedisConnectionFactory connectionFactory) { - this.export = export; - this.connectionFactory = connectionFactory; - } - - @Bean - public PublicMetrics metricsAggregate() { - return new MetricReaderPublicMetrics(aggregatesMetricReader()); - } - - private MetricReader globalMetricsForAggregation() { - return new RedisMetricRepository(this.connectionFactory, - this.export.getRedis().getAggregatePrefix(), - this.export.getRedis().getKey()); - } - - private MetricReader aggregatesMetricReader() { - AggregateMetricReader repository = new AggregateMetricReader( - globalMetricsForAggregation()); - repository.setKeyPattern(this.export.getAggregate().getKeyPattern()); - return repository; - } - -} diff --git a/spring-boot-samples/spring-boot-sample-metrics-redis/src/main/java/sample/metrics/redis/SampleController.java b/spring-boot-samples/spring-boot-sample-metrics-redis/src/main/java/sample/metrics/redis/SampleController.java deleted file mode 100644 index 0f7ef3ded658..000000000000 --- a/spring-boot-samples/spring-boot-sample-metrics-redis/src/main/java/sample/metrics/redis/SampleController.java +++ /dev/null @@ -1,60 +0,0 @@ -/* - * 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 sample.metrics.redis; - -import java.util.Collections; -import java.util.Map; - -import org.hibernate.validator.constraints.NotBlank; - -import org.springframework.context.annotation.Description; -import org.springframework.stereotype.Controller; -import org.springframework.web.bind.annotation.GetMapping; -import org.springframework.web.bind.annotation.ResponseBody; - -@Controller -@Description("A controller for handling requests for hello messages") -public class SampleController { - - private final HelloWorldProperties helloWorldProperties; - - public SampleController(HelloWorldProperties helloWorldProperties) { - this.helloWorldProperties = helloWorldProperties; - } - - @GetMapping("/") - @ResponseBody - public Map hello() { - return Collections.singletonMap("message", - "Hello " + this.helloWorldProperties.getName()); - } - - protected static class Message { - - @NotBlank(message = "Message value cannot be empty") - private String value; - - public String getValue() { - return this.value; - } - - public void setValue(String value) { - this.value = value; - } - } - -} diff --git a/spring-boot-samples/spring-boot-sample-metrics-redis/src/main/java/sample/metrics/redis/SampleRedisExportApplication.java b/spring-boot-samples/spring-boot-sample-metrics-redis/src/main/java/sample/metrics/redis/SampleRedisExportApplication.java deleted file mode 100644 index 1f6ddf666796..000000000000 --- a/spring-boot-samples/spring-boot-sample-metrics-redis/src/main/java/sample/metrics/redis/SampleRedisExportApplication.java +++ /dev/null @@ -1,58 +0,0 @@ -/* - * 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 sample.metrics.redis; - -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.beans.factory.annotation.Qualifier; -import org.springframework.boot.SpringApplication; -import org.springframework.boot.actuate.autoconfigure.metrics.ExportMetricWriter; -import org.springframework.boot.actuate.metrics.export.MetricExportProperties; -import org.springframework.boot.actuate.metrics.jmx.JmxMetricWriter; -import org.springframework.boot.actuate.metrics.repository.redis.RedisMetricRepository; -import org.springframework.boot.autoconfigure.SpringBootApplication; -import org.springframework.boot.context.properties.EnableConfigurationProperties; -import org.springframework.context.annotation.Bean; -import org.springframework.data.redis.connection.RedisConnectionFactory; -import org.springframework.jmx.export.MBeanExporter; - -@SpringBootApplication -@EnableConfigurationProperties(HelloWorldProperties.class) -public class SampleRedisExportApplication { - - @Autowired - private MetricExportProperties export; - - @Bean - @ExportMetricWriter - public RedisMetricRepository redisMetricWriter( - RedisConnectionFactory connectionFactory) { - return new RedisMetricRepository(connectionFactory, - this.export.getRedis().getPrefix(), this.export.getRedis().getKey()); - } - - @Bean - @ExportMetricWriter - public JmxMetricWriter jmxMetricWriter( - @Qualifier("mbeanExporter") MBeanExporter exporter) { - return new JmxMetricWriter(exporter); - } - - public static void main(String[] args) throws Exception { - SpringApplication.run(SampleRedisExportApplication.class, args); - } - -} diff --git a/spring-boot-samples/spring-boot-sample-metrics-redis/src/main/resources/application.properties b/spring-boot-samples/spring-boot-sample-metrics-redis/src/main/resources/application.properties deleted file mode 100644 index 0cf56ec17f8e..000000000000 --- a/spring-boot-samples/spring-boot-sample-metrics-redis/src/main/resources/application.properties +++ /dev/null @@ -1,9 +0,0 @@ -management.security.enabled=false - -service.name=Phil -spring.metrics.export.redis.prefix=metrics.sample.${spring.metrics.export.aggregate.prefix} -spring.metrics.export.redis.key=keys.metrics.sample -spring.metrics.export.aggregate.prefix=${random.value:0000}.${spring.application.name:application} -spring.metrics.export.aggregate.key-pattern=d -spring.jmx.default-domain=org.springframework.boot -spring.data.redis.repositories.enabled=false \ No newline at end of file diff --git a/spring-boot-samples/spring-boot-sample-metrics-redis/src/test/java/sample/metrics/redis/SampleRedisExportApplicationTests.java b/spring-boot-samples/spring-boot-sample-metrics-redis/src/test/java/sample/metrics/redis/SampleRedisExportApplicationTests.java deleted file mode 100644 index c8bd9c4952d4..000000000000 --- a/spring-boot-samples/spring-boot-sample-metrics-redis/src/test/java/sample/metrics/redis/SampleRedisExportApplicationTests.java +++ /dev/null @@ -1,41 +0,0 @@ -/* - * Copyright 2012-2016 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 sample.metrics.redis; - -import org.junit.Test; -import org.junit.runner.RunWith; - -import org.springframework.boot.test.context.SpringBootTest; -import org.springframework.boot.test.context.SpringBootTest.WebEnvironment; -import org.springframework.test.annotation.DirtiesContext; -import org.springframework.test.context.junit4.SpringRunner; - -/** - * Basic integration tests for {@link SampleRedisExportApplication}. - * - * @author Dave Syer - */ -@RunWith(SpringRunner.class) -@SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT, properties = "spring.jmx.enabled=true") -@DirtiesContext -public class SampleRedisExportApplicationTests { - - @Test - public void contextLoads() { - } - -} diff --git a/spring-boot-starters/spring-boot-starter-actuator/pom.xml b/spring-boot-starters/spring-boot-starter-actuator/pom.xml index 6d7df3916698..3414f656214e 100644 --- a/spring-boot-starters/spring-boot-starter-actuator/pom.xml +++ b/spring-boot-starters/spring-boot-starter-actuator/pom.xml @@ -1,5 +1,6 @@ - + 4.0.0 org.springframework.boot @@ -27,5 +28,9 @@ org.springframework.boot spring-boot-actuator-autoconfigure + + io.micrometer + micrometer-core + diff --git a/spring-boot-starters/spring-boot-starter-actuator/src/main/resources/META-INF/spring.provides b/spring-boot-starters/spring-boot-starter-actuator/src/main/resources/META-INF/spring.provides index 0b5f44060d69..75b44b39adf4 100644 --- a/spring-boot-starters/spring-boot-starter-actuator/src/main/resources/META-INF/spring.provides +++ b/spring-boot-starters/spring-boot-starter-actuator/src/main/resources/META-INF/spring.provides @@ -1 +1 @@ -provides: spring-boot-actuator \ No newline at end of file +provides: spring-boot-actuator,micrometer-core diff --git a/spring-boot/src/main/java/org/springframework/boot/jdbc/metadata/DataSourcePoolMetadataProviders.java b/spring-boot/src/main/java/org/springframework/boot/jdbc/metadata/CompositeDataSourcePoolMetadataProvider.java similarity index 84% rename from spring-boot/src/main/java/org/springframework/boot/jdbc/metadata/DataSourcePoolMetadataProviders.java rename to spring-boot/src/main/java/org/springframework/boot/jdbc/metadata/CompositeDataSourcePoolMetadataProvider.java index 2d59dfd978de..14b37eb2c9c6 100644 --- a/spring-boot/src/main/java/org/springframework/boot/jdbc/metadata/DataSourcePoolMetadataProviders.java +++ b/spring-boot/src/main/java/org/springframework/boot/jdbc/metadata/CompositeDataSourcePoolMetadataProvider.java @@ -30,20 +30,21 @@ * @author Stephane Nicoll * @since 2.0.0 */ -public class DataSourcePoolMetadataProviders implements DataSourcePoolMetadataProvider { +public class CompositeDataSourcePoolMetadataProvider + implements DataSourcePoolMetadataProvider { private final List providers; /** - * Create a {@link DataSourcePoolMetadataProviders} instance with an initial + * Create a {@link CompositeDataSourcePoolMetadataProvider} instance with an initial * collection of delegates to use. * @param providers the data source pool metadata providers */ - public DataSourcePoolMetadataProviders( + public CompositeDataSourcePoolMetadataProvider( Collection providers) { this.providers = (providers == null ? Collections.emptyList() - : new ArrayList<>(providers)); + : Collections.unmodifiableList(new ArrayList<>(providers))); } @Override diff --git a/spring-boot/src/test/java/org/springframework/boot/jdbc/metadata/DataSourcePoolMetadataProvidersTests.java b/spring-boot/src/test/java/org/springframework/boot/jdbc/metadata/CompositeDataSourcePoolMetadataProviderTests.java similarity index 90% rename from spring-boot/src/test/java/org/springframework/boot/jdbc/metadata/DataSourcePoolMetadataProvidersTests.java rename to spring-boot/src/test/java/org/springframework/boot/jdbc/metadata/CompositeDataSourcePoolMetadataProviderTests.java index 257ce3945b19..004c88db3210 100644 --- a/spring-boot/src/test/java/org/springframework/boot/jdbc/metadata/DataSourcePoolMetadataProvidersTests.java +++ b/spring-boot/src/test/java/org/springframework/boot/jdbc/metadata/CompositeDataSourcePoolMetadataProviderTests.java @@ -29,11 +29,11 @@ import static org.mockito.BDDMockito.given; /** - * Tests for {@link DataSourcePoolMetadataProviders}. + * Tests for {@link CompositeDataSourcePoolMetadataProvider}. * * @author Stephane Nicoll */ -public class DataSourcePoolMetadataProvidersTests { +public class CompositeDataSourcePoolMetadataProviderTests { @Mock private DataSourcePoolMetadataProvider firstProvider; @@ -67,7 +67,7 @@ public void setup() { @Test public void createWithProviders() { - DataSourcePoolMetadataProviders provider = new DataSourcePoolMetadataProviders( + CompositeDataSourcePoolMetadataProvider provider = new CompositeDataSourcePoolMetadataProvider( Arrays.asList(this.firstProvider, this.secondProvider)); assertThat(provider.getDataSourcePoolMetadata(this.firstDataSource)) .isSameAs(this.first);