Replace Boot's own metrics with support for Micrometer
Closes gh-9970
This commit is contained in:
parent
306c8d0ae2
commit
c2958c27ab
|
@ -33,6 +33,11 @@
|
||||||
<groupId>com.fasterxml.jackson.core</groupId>
|
<groupId>com.fasterxml.jackson.core</groupId>
|
||||||
<artifactId>jackson-databind</artifactId>
|
<artifactId>jackson-databind</artifactId>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>io.micrometer</groupId>
|
||||||
|
<artifactId>micrometer-core</artifactId>
|
||||||
|
<optional>true</optional>
|
||||||
|
</dependency>
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.springframework</groupId>
|
<groupId>org.springframework</groupId>
|
||||||
<artifactId>spring-core</artifactId>
|
<artifactId>spring-core</artifactId>
|
||||||
|
@ -87,6 +92,41 @@
|
||||||
<artifactId>lettuce-core</artifactId>
|
<artifactId>lettuce-core</artifactId>
|
||||||
<optional>true</optional>
|
<optional>true</optional>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>io.micrometer</groupId>
|
||||||
|
<artifactId>micrometer-atlas-starter</artifactId>
|
||||||
|
<optional>true</optional>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>io.micrometer</groupId>
|
||||||
|
<artifactId>micrometer-datadog-starter</artifactId>
|
||||||
|
<optional>true</optional>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>io.micrometer</groupId>
|
||||||
|
<artifactId>micrometer-ganglia-starter</artifactId>
|
||||||
|
<optional>true</optional>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>io.micrometer</groupId>
|
||||||
|
<artifactId>micrometer-graphite-starter</artifactId>
|
||||||
|
<optional>true</optional>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>io.micrometer</groupId>
|
||||||
|
<artifactId>micrometer-influx-starter</artifactId>
|
||||||
|
<optional>true</optional>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>io.micrometer</groupId>
|
||||||
|
<artifactId>micrometer-jmx-starter</artifactId>
|
||||||
|
<optional>true</optional>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>io.micrometer</groupId>
|
||||||
|
<artifactId>micrometer-prometheus-starter</artifactId>
|
||||||
|
<optional>true</optional>
|
||||||
|
</dependency>
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>io.searchbox</groupId>
|
<groupId>io.searchbox</groupId>
|
||||||
<artifactId>jest</artifactId>
|
<artifactId>jest</artifactId>
|
||||||
|
@ -143,6 +183,11 @@
|
||||||
<artifactId>tomcat-jdbc</artifactId>
|
<artifactId>tomcat-jdbc</artifactId>
|
||||||
<optional>true</optional>
|
<optional>true</optional>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.aspectj</groupId>
|
||||||
|
<artifactId>aspectjweaver</artifactId>
|
||||||
|
<optional>true</optional>
|
||||||
|
</dependency>
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.elasticsearch</groupId>
|
<groupId>org.elasticsearch</groupId>
|
||||||
<artifactId>elasticsearch</artifactId>
|
<artifactId>elasticsearch</artifactId>
|
||||||
|
@ -283,6 +328,11 @@
|
||||||
<artifactId>spring-boot-test</artifactId>
|
<artifactId>spring-boot-test</artifactId>
|
||||||
<scope>test</scope>
|
<scope>test</scope>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.springframework.boot</groupId>
|
||||||
|
<artifactId>spring-boot-test-autoconfigure</artifactId>
|
||||||
|
<scope>test</scope>
|
||||||
|
</dependency>
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.springframework.boot</groupId>
|
<groupId>org.springframework.boot</groupId>
|
||||||
<artifactId>spring-boot-test-support</artifactId>
|
<artifactId>spring-boot-test-support</artifactId>
|
||||||
|
|
|
@ -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<Cache> noOpCacheStatisticsProvider() {
|
|
||||||
return (cacheManager, cache) -> {
|
|
||||||
if (cacheManager instanceof NoOpCacheManager) {
|
|
||||||
return NO_OP_STATS;
|
|
||||||
}
|
|
||||||
return 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;
|
|
|
@ -36,9 +36,9 @@ import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
|
||||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
|
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
|
||||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
|
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
|
||||||
import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration;
|
import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration;
|
||||||
|
import org.springframework.boot.jdbc.metadata.CompositeDataSourcePoolMetadataProvider;
|
||||||
import org.springframework.boot.jdbc.metadata.DataSourcePoolMetadata;
|
import org.springframework.boot.jdbc.metadata.DataSourcePoolMetadata;
|
||||||
import org.springframework.boot.jdbc.metadata.DataSourcePoolMetadataProvider;
|
import org.springframework.boot.jdbc.metadata.DataSourcePoolMetadataProvider;
|
||||||
import org.springframework.boot.jdbc.metadata.DataSourcePoolMetadataProviders;
|
|
||||||
import org.springframework.context.annotation.Bean;
|
import org.springframework.context.annotation.Bean;
|
||||||
import org.springframework.context.annotation.Configuration;
|
import org.springframework.context.annotation.Configuration;
|
||||||
import org.springframework.jdbc.core.JdbcTemplate;
|
import org.springframework.jdbc.core.JdbcTemplate;
|
||||||
|
@ -94,7 +94,7 @@ public class DataSourceHealthIndicatorAutoConfiguration extends
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void afterPropertiesSet() throws Exception {
|
public void afterPropertiesSet() throws Exception {
|
||||||
this.poolMetadataProvider = new DataSourcePoolMetadataProviders(
|
this.poolMetadataProvider = new CompositeDataSourcePoolMetadataProvider(
|
||||||
this.metadataProviders);
|
this.metadataProviders);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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 {
|
|
||||||
|
|
||||||
}
|
|
|
@ -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 {
|
|
||||||
|
|
||||||
}
|
|
|
@ -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 {
|
|
||||||
|
|
||||||
}
|
|
|
@ -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();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -14,7 +14,28 @@
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/**
|
|
||||||
* Auto-configuration for actuator metrics concerns.
|
|
||||||
*/
|
|
||||||
package org.springframework.boot.actuate.autoconfigure.metrics;
|
package org.springframework.boot.actuate.autoconfigure.metrics;
|
||||||
|
|
||||||
|
import io.micrometer.core.instrument.Meter;
|
||||||
|
import io.micrometer.core.instrument.MeterRegistry;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Callback interface that can be used to customize auto-configured {@link MeterRegistry
|
||||||
|
* MeterRegistries}.
|
||||||
|
* <p>
|
||||||
|
* Configurers are guaranteed to be applied before any {@link Meter} is registered with
|
||||||
|
* the registry.
|
||||||
|
*
|
||||||
|
* @author Jon Schneider
|
||||||
|
* @since 2.0.0
|
||||||
|
*/
|
||||||
|
@FunctionalInterface
|
||||||
|
public interface MeterRegistryConfigurer {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Configure the given {@code registry}.
|
||||||
|
* @param registry the registry to configure
|
||||||
|
*/
|
||||||
|
void configureRegistry(MeterRegistry registry);
|
||||||
|
|
||||||
|
}
|
|
@ -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<MetricReader> readers;
|
|
||||||
|
|
||||||
private final Map<String, GaugeWriter> writers;
|
|
||||||
|
|
||||||
private final Map<String, Exporter> exporters;
|
|
||||||
|
|
||||||
public MetricExportAutoConfiguration(MetricExportProperties properties,
|
|
||||||
ObjectProvider<MetricsEndpointMetricReader> endpointReader,
|
|
||||||
@ExportMetricReader ObjectProvider<List<MetricReader>> readers,
|
|
||||||
@ExportMetricWriter ObjectProvider<Map<String, GaugeWriter>> writers,
|
|
||||||
ObjectProvider<Map<String, Exporter>> exporters) {
|
|
||||||
this.endpointReader = endpointReader.getIfAvailable();
|
|
||||||
this.readers = readers.getIfAvailable();
|
|
||||||
this.writers = writers.getIfAvailable();
|
|
||||||
this.exporters = exporters.getIfAvailable();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Bean
|
|
||||||
@ConditionalOnMissingBean(name = "metricWritersMetricExporter")
|
|
||||||
public SchedulingConfigurer metricWritersMetricExporter(
|
|
||||||
MetricExportProperties properties) {
|
|
||||||
Map<String, GaugeWriter> writers = new HashMap<>();
|
|
||||||
MetricReader reader = this.endpointReader;
|
|
||||||
if (reader == null && !CollectionUtils.isEmpty(this.readers)) {
|
|
||||||
reader = new CompositeMetricReader(
|
|
||||||
this.readers.toArray(new MetricReader[this.readers.size()]));
|
|
||||||
}
|
|
||||||
if (reader == null && CollectionUtils.isEmpty(this.exporters)) {
|
|
||||||
return new NoOpSchedulingConfigurer();
|
|
||||||
}
|
|
||||||
MetricExporters exporters = new MetricExporters(properties);
|
|
||||||
if (reader != null) {
|
|
||||||
if (!CollectionUtils.isEmpty(this.writers)) {
|
|
||||||
writers.putAll(this.writers);
|
|
||||||
}
|
|
||||||
exporters.setReader(reader);
|
|
||||||
exporters.setWriters(writers);
|
|
||||||
}
|
|
||||||
exporters.setExporters(this.exporters == null
|
|
||||||
? Collections.<String, Exporter>emptyMap() : this.exporters);
|
|
||||||
return exporters;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Configuration
|
|
||||||
static class StatsdConfiguration {
|
|
||||||
|
|
||||||
@Bean
|
|
||||||
@ExportMetricWriter
|
|
||||||
@ConditionalOnMissingBean
|
|
||||||
@ConditionalOnProperty(prefix = "spring.metrics.export.statsd", name = "host")
|
|
||||||
public StatsdMetricWriter statsdMetricWriter(MetricExportProperties properties) {
|
|
||||||
MetricExportProperties.Statsd statsdProperties = properties.getStatsd();
|
|
||||||
return new StatsdMetricWriter(statsdProperties.getPrefix(),
|
|
||||||
statsdProperties.getHost(), statsdProperties.getPort());
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
@Configuration
|
|
||||||
protected static class MetricExportPropertiesConfiguration {
|
|
||||||
|
|
||||||
@Value("${spring.application.name:application}.${random.value:0000}")
|
|
||||||
private String prefix = "";
|
|
||||||
|
|
||||||
private String aggregateKeyPattern = "k.d";
|
|
||||||
|
|
||||||
@Bean(name = "spring.metrics.export-org.springframework.boot.actuate.metrics.export.MetricExportProperties")
|
|
||||||
@ConditionalOnMissingBean
|
|
||||||
public MetricExportProperties metricExportProperties() {
|
|
||||||
MetricExportProperties export = new MetricExportProperties();
|
|
||||||
export.getRedis().setPrefix("spring.metrics"
|
|
||||||
+ (this.prefix.length() > 0 ? "." : "") + this.prefix);
|
|
||||||
export.getAggregate().setPrefix(this.prefix);
|
|
||||||
export.getAggregate().setKeyPattern(this.aggregateKeyPattern);
|
|
||||||
return export;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
private static class NoOpSchedulingConfigurer implements SchedulingConfigurer {
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void configureTasks(ScheduledTaskRegistrar taskRegistrar) {
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -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());
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -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<MetricsFilterSubmission> gaugeSubmissions;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Submissions that should be made to the counter.
|
|
||||||
*/
|
|
||||||
private Set<MetricsFilterSubmission> counterSubmissions;
|
|
||||||
|
|
||||||
public MetricFilterProperties() {
|
|
||||||
this.gaugeSubmissions = new HashSet<>(EnumSet.of(MetricsFilterSubmission.MERGED));
|
|
||||||
this.counterSubmissions = new HashSet<>(
|
|
||||||
EnumSet.of(MetricsFilterSubmission.MERGED));
|
|
||||||
}
|
|
||||||
|
|
||||||
public Set<MetricsFilterSubmission> getGaugeSubmissions() {
|
|
||||||
return this.gaugeSubmissions;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setGaugeSubmissions(Set<MetricsFilterSubmission> gaugeSubmissions) {
|
|
||||||
this.gaugeSubmissions = gaugeSubmissions;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Set<MetricsFilterSubmission> getCounterSubmissions() {
|
|
||||||
return this.counterSubmissions;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setCounterSubmissions(Set<MetricsFilterSubmission> counterSubmissions) {
|
|
||||||
this.counterSubmissions = counterSubmissions;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -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.
|
|
||||||
* <p>
|
|
||||||
* In general, even if metric data needs to be stored and analysed remotely, it is
|
|
||||||
* recommended to use in-memory storage to buffer metric updates locally as is done by the
|
|
||||||
* default {@link CounterBuffers} and {@link GaugeBuffers}. The values can be exported
|
|
||||||
* (e.g. on a periodic basis) using an {@link Exporter}, most implementations of which
|
|
||||||
* have optimizations for sending data to remote repositories.
|
|
||||||
* <p>
|
|
||||||
* If Spring Messaging is on the classpath and a {@link MessageChannel} called
|
|
||||||
* "metricsChannel" is also available, all metric update events are published additionally
|
|
||||||
* as messages on that channel. Additional analysis or actions can be taken by clients
|
|
||||||
* subscribing to that channel.
|
|
||||||
* <p>
|
|
||||||
* In addition if Dropwizard's metrics library is on the classpath a
|
|
||||||
* {@link MetricRegistry} will be created and the default counter and gauge services will
|
|
||||||
* switch to using it instead of the default repository. Users can create "special"
|
|
||||||
* Dropwizard metrics by prefixing their metric names with the appropriate type (e.g.
|
|
||||||
* "histogram.*", "meter.*". "timer.*") and sending them to the {@code GaugeService} or
|
|
||||||
* {@code CounterService}.
|
|
||||||
* <p>
|
|
||||||
* By default all metric updates go to all {@link MetricWriter} instances in the
|
|
||||||
* application context via a {@link MetricCopyExporter} firing every 5 seconds (disable
|
|
||||||
* this by setting {@code spring.metrics.export.enabled=false}).
|
|
||||||
*
|
|
||||||
* @see GaugeService
|
|
||||||
* @see CounterService
|
|
||||||
* @see MetricWriter
|
|
||||||
* @see InMemoryMetricRepository
|
|
||||||
* @see Exporter
|
|
||||||
* @author Dave Syer
|
|
||||||
* @since 2.0.0
|
|
||||||
*/
|
|
||||||
@Configuration
|
|
||||||
public class MetricRepositoryAutoConfiguration {
|
|
||||||
|
|
||||||
@Configuration
|
|
||||||
@ConditionalOnMissingBean(GaugeService.class)
|
|
||||||
static class FastMetricServicesConfiguration {
|
|
||||||
|
|
||||||
@Bean
|
|
||||||
@ConditionalOnMissingBean
|
|
||||||
public CounterBuffers counterBuffers() {
|
|
||||||
return new CounterBuffers();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Bean
|
|
||||||
@ConditionalOnMissingBean
|
|
||||||
public GaugeBuffers gaugeBuffers() {
|
|
||||||
return new GaugeBuffers();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Bean
|
|
||||||
@ExportMetricReader
|
|
||||||
@ConditionalOnMissingBean
|
|
||||||
public BufferMetricReader actuatorMetricReader(CounterBuffers counters,
|
|
||||||
GaugeBuffers gauges) {
|
|
||||||
return new BufferMetricReader(counters, gauges);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Bean
|
|
||||||
@ConditionalOnMissingBean(CounterService.class)
|
|
||||||
public BufferCounterService counterService(CounterBuffers writer) {
|
|
||||||
return new BufferCounterService(writer);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Bean
|
|
||||||
@ConditionalOnMissingBean(GaugeService.class)
|
|
||||||
public BufferGaugeService gaugeService(GaugeBuffers writer) {
|
|
||||||
return new BufferGaugeService(writer);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -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<Collection<MetricsExporter>> 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<Collection<MeterRegistryConfigurer>> configurers,
|
||||||
|
MetricsProperties config, Collection<MeterBinder> binders) {
|
||||||
|
configurers.getIfAvailable(Collections::emptyList)
|
||||||
|
.forEach((configurer) -> configurer.configureRegistry(registry));
|
||||||
|
binders.forEach((binder) -> binder.bindTo(registry));
|
||||||
|
if (config.isUseGlobalRegistry()) {
|
||||||
|
Metrics.addRegistry(registry);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -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> 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);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -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<List<PublicMetrics>> publicMetrics) {
|
|
||||||
return metricsEndpoint(publicMetrics.getIfAvailable(Collections::emptyList));
|
|
||||||
}
|
|
||||||
|
|
||||||
private MetricsEndpoint metricsEndpoint(List<PublicMetrics> publicMetrics) {
|
|
||||||
return new MetricsEndpoint(sort(publicMetrics));
|
|
||||||
}
|
|
||||||
|
|
||||||
private List<PublicMetrics> sort(List<PublicMetrics> publicMetrics) {
|
|
||||||
List<PublicMetrics> sorted = new ArrayList<>(publicMetrics);
|
|
||||||
Collections.sort(sorted, AnnotationAwareOrderComparator.INSTANCE);
|
|
||||||
return sorted;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -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;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -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<MetricReader> metricReaders;
|
|
||||||
|
|
||||||
public PublicMetricsAutoConfiguration(
|
|
||||||
@ExportMetricReader ObjectProvider<List<MetricReader>> 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<String, CacheManager> cacheManagers,
|
|
||||||
Collection<CacheStatisticsProvider<?>> 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));
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -14,23 +14,25 @@
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package org.springframework.boot.actuate.metrics.writer;
|
package org.springframework.boot.actuate.autoconfigure.metrics.export;
|
||||||
|
|
||||||
import org.springframework.boot.actuate.metrics.Metric;
|
import io.micrometer.core.instrument.MeterRegistry;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Writer for gauge values (simple metric with a number value).
|
* A {@code MetricsExporter} can be used to export metrics, typically to an external
|
||||||
|
* server running as a separate process.
|
||||||
*
|
*
|
||||||
* @author Dave Syer
|
* @author Jon Schneider
|
||||||
* @since 1.3.0
|
* @author Andy Wilkinson
|
||||||
|
* @since 2.0.0
|
||||||
*/
|
*/
|
||||||
@FunctionalInterface
|
@FunctionalInterface
|
||||||
public interface GaugeWriter {
|
public interface MetricsExporter {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Set the value of a metric.
|
* Returns the {@link MeterRegistry} used to register metrics with the exporter.
|
||||||
* @param value the value
|
* @return the meter registry
|
||||||
*/
|
*/
|
||||||
void set(Metric<?> value);
|
MeterRegistry registry();
|
||||||
|
|
||||||
}
|
}
|
|
@ -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");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
|
@ -14,21 +14,31 @@
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package sample.metrics.redis;
|
package org.springframework.boot.actuate.autoconfigure.metrics.export;
|
||||||
|
|
||||||
|
import java.util.Properties;
|
||||||
|
|
||||||
import org.springframework.boot.context.properties.ConfigurationProperties;
|
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||||
|
|
||||||
@ConfigurationProperties(prefix = "service", ignoreUnknownFields = false)
|
/**
|
||||||
public class HelloWorldProperties {
|
* Base {@link ConfigurationProperties} class for configuring a metrics registry.
|
||||||
|
*
|
||||||
|
* @author Jon Schneider
|
||||||
|
* @author Andy Wilkinson
|
||||||
|
* @since 2.0.0
|
||||||
|
*/
|
||||||
|
public abstract class RegistryProperties {
|
||||||
|
|
||||||
private String name = "World";
|
private final Properties properties = new Properties();
|
||||||
|
|
||||||
public String getName() {
|
protected abstract String prefix();
|
||||||
return this.name;
|
|
||||||
|
public String get(String key) {
|
||||||
|
return this.properties.getProperty(key);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setName(String name) {
|
protected void set(String key, Object value) {
|
||||||
this.name = name;
|
this.properties.put(prefix() + "." + key, value.toString());
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
|
@ -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);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -14,24 +14,26 @@
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package org.springframework.boot.actuate.metrics.web.servlet;
|
package org.springframework.boot.actuate.autoconfigure.metrics.export;
|
||||||
|
|
||||||
|
import java.time.Duration;
|
||||||
|
|
||||||
|
import org.springframework.boot.context.properties.ConfigurationPropertiesBinding;
|
||||||
|
import org.springframework.core.convert.converter.Converter;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Submission types that can be made by the {@link MetricsFilter}.
|
* A {@link Converter} to create a {@link Duration} from a {@link String}.
|
||||||
*
|
*
|
||||||
* @author Phillip Webb
|
* @author Jon Schneider
|
||||||
|
* @author Andy Wilkinson
|
||||||
* @since 2.0.0
|
* @since 2.0.0
|
||||||
*/
|
*/
|
||||||
public enum MetricsFilterSubmission {
|
@ConfigurationPropertiesBinding
|
||||||
|
public class StringToDurationConverter implements Converter<String, Duration> {
|
||||||
|
|
||||||
/**
|
@Override
|
||||||
* Merge all HTTP methods into a single submission.
|
public Duration convert(String source) {
|
||||||
*/
|
return Duration.parse(source);
|
||||||
MERGED,
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Group submissions by the HTTP method of the request.
|
|
||||||
*/
|
|
||||||
PER_HTTP_METHOD
|
|
||||||
|
|
||||||
}
|
}
|
|
@ -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;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -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);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -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;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -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);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -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;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -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);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -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;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -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);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -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;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -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);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -14,38 +14,45 @@
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package org.springframework.boot.actuate.autoconfigure.metrics;
|
package org.springframework.boot.actuate.autoconfigure.metrics.export.jmx;
|
||||||
|
|
||||||
import org.springframework.beans.factory.annotation.Qualifier;
|
import io.micrometer.core.instrument.Clock;
|
||||||
import org.springframework.boot.actuate.metrics.writer.MessageChannelMetricWriter;
|
import io.micrometer.core.instrument.util.HierarchicalNameMapper;
|
||||||
import org.springframework.boot.autoconfigure.AutoConfigureBefore;
|
import io.micrometer.jmx.JmxMeterRegistry;
|
||||||
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
|
|
||||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
|
import org.springframework.boot.actuate.autoconfigure.metrics.export.MetricsExporter;
|
||||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
|
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
|
||||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
|
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
|
||||||
|
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
|
||||||
import org.springframework.context.annotation.Bean;
|
import org.springframework.context.annotation.Bean;
|
||||||
import org.springframework.context.annotation.Configuration;
|
import org.springframework.context.annotation.Configuration;
|
||||||
import org.springframework.messaging.MessageChannel;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* {@link EnableAutoConfiguration Auto-configuration} for writing metrics to a
|
* Configuration for exporting metrics to JMX.
|
||||||
* {@link MessageChannel}.
|
|
||||||
*
|
*
|
||||||
* @author Dave Syer
|
* @author Jon Schneider
|
||||||
* @since 2.0.0
|
* @since 2.0.0
|
||||||
*/
|
*/
|
||||||
@Configuration
|
@Configuration
|
||||||
@ConditionalOnClass(MessageChannel.class)
|
@ConditionalOnClass(JmxMeterRegistry.class)
|
||||||
@ConditionalOnBean(name = "metricsChannel")
|
public class JmxExportConfiguration {
|
||||||
@AutoConfigureBefore(MetricRepositoryAutoConfiguration.class)
|
|
||||||
public class MetricsChannelAutoConfiguration {
|
@Bean
|
||||||
|
@ConditionalOnProperty(value = "metrics.jmx.enabled", matchIfMissing = true)
|
||||||
|
public MetricsExporter jmxExporter(HierarchicalNameMapper nameMapper, Clock clock) {
|
||||||
|
return () -> new JmxMeterRegistry(nameMapper, clock);
|
||||||
|
}
|
||||||
|
|
||||||
@Bean
|
@Bean
|
||||||
@ExportMetricWriter
|
|
||||||
@ConditionalOnMissingBean
|
@ConditionalOnMissingBean
|
||||||
public MessageChannelMetricWriter messageChannelMetricWriter(
|
public Clock clock() {
|
||||||
@Qualifier("metricsChannel") MessageChannel channel) {
|
return Clock.SYSTEM;
|
||||||
return new MessageChannelMetricWriter(channel);
|
}
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
@ConditionalOnMissingBean
|
||||||
|
public HierarchicalNameMapper hierarchicalNameMapper() {
|
||||||
|
return HierarchicalNameMapper.DEFAULT;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
|
@ -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);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -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";
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -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;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -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");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
|
@ -14,21 +14,30 @@
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package sample.metrics.opentsdb;
|
package org.springframework.boot.actuate.autoconfigure.metrics.export.simple;
|
||||||
|
|
||||||
|
import io.micrometer.core.instrument.simple.SimpleMeterRegistry;
|
||||||
|
|
||||||
import org.springframework.boot.context.properties.ConfigurationProperties;
|
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||||
|
|
||||||
@ConfigurationProperties(prefix = "service", ignoreUnknownFields = false)
|
/**
|
||||||
public class HelloWorldProperties {
|
* {@link ConfigurationProperties} for configuring metrics export to a
|
||||||
|
* {@link SimpleMeterRegistry}.
|
||||||
|
*
|
||||||
|
* @author Jon Schneider
|
||||||
|
* @since 2.0.0
|
||||||
|
*/
|
||||||
|
@ConfigurationProperties(prefix = "metrics.simple")
|
||||||
|
public class SimpleProperties {
|
||||||
|
|
||||||
private String name = "World";
|
private boolean enabled = true;
|
||||||
|
|
||||||
public String getName() {
|
public boolean isEnabled() {
|
||||||
return this.name;
|
return this.enabled;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setName(String name) {
|
public void setEnabled(boolean enabled) {
|
||||||
this.name = name;
|
this.enabled = enabled;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
|
@ -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());
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -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;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -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);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -39,33 +39,6 @@
|
||||||
"vcap_services"
|
"vcap_services"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
|
||||||
"name": "endpoints.metrics.filter.counter-submissions",
|
|
||||||
"type": "java.util.Set<org.springframework.boot.actuate.metrics.web.servlet.MetricsFilterSubmission>",
|
|
||||||
"description": "Submissions that should be made to the counter.",
|
|
||||||
"deprecation": {
|
|
||||||
"replacement": "management.metrics.filter.counter-submissions",
|
|
||||||
"level": "error"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "endpoints.metrics.filter.enabled",
|
|
||||||
"type": "java.lang.Boolean",
|
|
||||||
"description": "Enable the metrics servlet filter.",
|
|
||||||
"deprecation": {
|
|
||||||
"replacement": "management.metrics.filter.enabled",
|
|
||||||
"level": "error"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "endpoints.metrics.filter.gauge-submissions",
|
|
||||||
"type": "java.util.Set<org.springframework.boot.actuate.metrics.web.servlet.MetricsFilterSubmission>",
|
|
||||||
"description": "Submissions that should be made to the gauge.",
|
|
||||||
"deprecation": {
|
|
||||||
"replacement": "management.metrics.filter.gauge-submissions",
|
|
||||||
"level": "error"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"name": "endpoints.trace.filter.enabled",
|
"name": "endpoints.trace.filter.enabled",
|
||||||
"type": "java.lang.Boolean",
|
"type": "java.lang.Boolean",
|
||||||
|
@ -205,12 +178,6 @@
|
||||||
"name": "management.info.git.mode",
|
"name": "management.info.git.mode",
|
||||||
"defaultValue": "simple"
|
"defaultValue": "simple"
|
||||||
},
|
},
|
||||||
{
|
|
||||||
"name": "management.metrics.filter.enabled",
|
|
||||||
"type": "java.lang.Boolean",
|
|
||||||
"description": "Enable the metrics servlet filter.",
|
|
||||||
"defaultValue": true
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"name": "management.trace.filter.enabled",
|
"name": "management.trace.filter.enabled",
|
||||||
"type": "java.lang.Boolean",
|
"type": "java.lang.Boolean",
|
||||||
|
@ -790,33 +757,6 @@
|
||||||
"level": "error"
|
"level": "error"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
|
||||||
"name": "endpoints.metrics.id",
|
|
||||||
"type": "java.lang.String",
|
|
||||||
"description": "Endpoint identifier. With HTTP monitoring the identifier of the endpoint is mapped\n to a URL (e.g. 'foo' is mapped to '/foo').",
|
|
||||||
"deprecation": {
|
|
||||||
"reason": "Endpoint identifier is no longer customizable.",
|
|
||||||
"level": "error"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "endpoints.metrics.path",
|
|
||||||
"type": "java.lang.String",
|
|
||||||
"description": "Endpoint URL path.",
|
|
||||||
"deprecation": {
|
|
||||||
"reason": "Endpoint path is no longer customizable.",
|
|
||||||
"level": "error"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "endpoints.metrics.sensitive",
|
|
||||||
"type": "java.lang.Boolean",
|
|
||||||
"description": "Mark if the endpoint exposes sensitive information.",
|
|
||||||
"deprecation": {
|
|
||||||
"reason": "Endpoint sensitive flag is no longer customizable as Spring Boot no longer provides a customizable security auto-configuration\n. Create or adapt your security configuration accordingly.",
|
|
||||||
"level": "error"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"name": "endpoints.sensitive",
|
"name": "endpoints.sensitive",
|
||||||
"type": "java.lang.Boolean",
|
"type": "java.lang.Boolean",
|
||||||
|
|
|
@ -3,7 +3,6 @@ org.springframework.boot.actuate.autoconfigure.amqp.RabbitHealthIndicatorAutoCon
|
||||||
org.springframework.boot.actuate.autoconfigure.audit.AuditAutoConfiguration,\
|
org.springframework.boot.actuate.autoconfigure.audit.AuditAutoConfiguration,\
|
||||||
org.springframework.boot.actuate.autoconfigure.audit.AuditEventsEndpointAutoConfiguration,\
|
org.springframework.boot.actuate.autoconfigure.audit.AuditEventsEndpointAutoConfiguration,\
|
||||||
org.springframework.boot.actuate.autoconfigure.beans.BeansEndpointAutoConfiguration,\
|
org.springframework.boot.actuate.autoconfigure.beans.BeansEndpointAutoConfiguration,\
|
||||||
org.springframework.boot.actuate.autoconfigure.cache.CacheStatisticsAutoConfiguration,\
|
|
||||||
org.springframework.boot.actuate.autoconfigure.cassandra.CassandraHealthIndicatorAutoConfiguration,\
|
org.springframework.boot.actuate.autoconfigure.cassandra.CassandraHealthIndicatorAutoConfiguration,\
|
||||||
org.springframework.boot.actuate.autoconfigure.cloudfoundry.CloudFoundryActuatorAutoConfiguration,\
|
org.springframework.boot.actuate.autoconfigure.cloudfoundry.CloudFoundryActuatorAutoConfiguration,\
|
||||||
org.springframework.boot.actuate.autoconfigure.condition.AutoConfigurationReportEndpointAutoConfiguration,\
|
org.springframework.boot.actuate.autoconfigure.condition.AutoConfigurationReportEndpointAutoConfiguration,\
|
||||||
|
@ -26,13 +25,7 @@ org.springframework.boot.actuate.autoconfigure.liquibase.LiquibaseEndpointAutoCo
|
||||||
org.springframework.boot.actuate.autoconfigure.logging.LoggersEndpointAutoConfiguration,\
|
org.springframework.boot.actuate.autoconfigure.logging.LoggersEndpointAutoConfiguration,\
|
||||||
org.springframework.boot.actuate.autoconfigure.mail.MailHealthIndicatorAutoConfiguration,\
|
org.springframework.boot.actuate.autoconfigure.mail.MailHealthIndicatorAutoConfiguration,\
|
||||||
org.springframework.boot.actuate.autoconfigure.management.ThreadDumpEndpointAutoConfiguration,\
|
org.springframework.boot.actuate.autoconfigure.management.ThreadDumpEndpointAutoConfiguration,\
|
||||||
org.springframework.boot.actuate.autoconfigure.metrics.MetricExportAutoConfiguration,\
|
org.springframework.boot.actuate.autoconfigure.metrics.MetricsAutoConfiguration,\
|
||||||
org.springframework.boot.actuate.autoconfigure.metrics.MetricFilterAutoConfiguration,\
|
|
||||||
org.springframework.boot.actuate.autoconfigure.metrics.MetricRepositoryAutoConfiguration,\
|
|
||||||
org.springframework.boot.actuate.autoconfigure.metrics.MetricsChannelAutoConfiguration,\
|
|
||||||
org.springframework.boot.actuate.autoconfigure.metrics.MetricsDropwizardAutoConfiguration,\
|
|
||||||
org.springframework.boot.actuate.autoconfigure.metrics.MetricsEndpointAutoConfiguration,\
|
|
||||||
org.springframework.boot.actuate.autoconfigure.metrics.PublicMetricsAutoConfiguration,\
|
|
||||||
org.springframework.boot.actuate.autoconfigure.mongo.MongoHealthIndicatorAutoConfiguration,\
|
org.springframework.boot.actuate.autoconfigure.mongo.MongoHealthIndicatorAutoConfiguration,\
|
||||||
org.springframework.boot.actuate.autoconfigure.neo4j.Neo4jHealthIndicatorAutoConfiguration,\
|
org.springframework.boot.actuate.autoconfigure.neo4j.Neo4jHealthIndicatorAutoConfiguration,\
|
||||||
org.springframework.boot.actuate.autoconfigure.redis.RedisHealthIndicatorAutoConfiguration,\
|
org.springframework.boot.actuate.autoconfigure.redis.RedisHealthIndicatorAutoConfiguration,\
|
||||||
|
|
|
@ -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<Object, Object> 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;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -28,7 +28,6 @@ import org.springframework.boot.actuate.autoconfigure.env.EnvironmentEndpointAut
|
||||||
import org.springframework.boot.actuate.autoconfigure.health.HealthEndpointAutoConfiguration;
|
import org.springframework.boot.actuate.autoconfigure.health.HealthEndpointAutoConfiguration;
|
||||||
import org.springframework.boot.actuate.autoconfigure.info.InfoEndpointAutoConfiguration;
|
import org.springframework.boot.actuate.autoconfigure.info.InfoEndpointAutoConfiguration;
|
||||||
import org.springframework.boot.actuate.autoconfigure.management.ThreadDumpEndpointAutoConfiguration;
|
import org.springframework.boot.actuate.autoconfigure.management.ThreadDumpEndpointAutoConfiguration;
|
||||||
import org.springframework.boot.actuate.autoconfigure.metrics.MetricsEndpointAutoConfiguration;
|
|
||||||
import org.springframework.boot.actuate.autoconfigure.trace.TraceEndpointAutoConfiguration;
|
import org.springframework.boot.actuate.autoconfigure.trace.TraceEndpointAutoConfiguration;
|
||||||
import org.springframework.boot.actuate.autoconfigure.web.servlet.RequestMappingEndpointAutoConfiguration;
|
import org.springframework.boot.actuate.autoconfigure.web.servlet.RequestMappingEndpointAutoConfiguration;
|
||||||
|
|
||||||
|
@ -50,7 +49,6 @@ final class EndpointAutoConfigurationClasses {
|
||||||
all.add(HealthEndpointAutoConfiguration.class);
|
all.add(HealthEndpointAutoConfiguration.class);
|
||||||
all.add(InfoEndpointAutoConfiguration.class);
|
all.add(InfoEndpointAutoConfiguration.class);
|
||||||
all.add(ThreadDumpEndpointAutoConfiguration.class);
|
all.add(ThreadDumpEndpointAutoConfiguration.class);
|
||||||
all.add(MetricsEndpointAutoConfiguration.class);
|
|
||||||
all.add(TraceEndpointAutoConfiguration.class);
|
all.add(TraceEndpointAutoConfiguration.class);
|
||||||
all.add(RequestMappingEndpointAutoConfiguration.class);
|
all.add(RequestMappingEndpointAutoConfiguration.class);
|
||||||
ALL = all.toArray(new Class<?>[] {});
|
ALL = all.toArray(new Class<?>[] {});
|
||||||
|
|
|
@ -55,7 +55,7 @@ public class JmxEndpointIntegrationTests {
|
||||||
MBeanServer mBeanServer = context.getBean(MBeanServer.class);
|
MBeanServer mBeanServer = context.getBean(MBeanServer.class);
|
||||||
checkEndpointMBeans(mBeanServer,
|
checkEndpointMBeans(mBeanServer,
|
||||||
new String[] { "autoconfig", "beans", "configprops", "env", "health",
|
new String[] { "autoconfig", "beans", "configprops", "env", "health",
|
||||||
"info", "mappings", "metrics", "status", "threaddump",
|
"info", "mappings", "status", "threaddump",
|
||||||
"trace" },
|
"trace" },
|
||||||
new String[] { "shutdown" });
|
new String[] { "shutdown" });
|
||||||
});
|
});
|
||||||
|
@ -68,7 +68,7 @@ public class JmxEndpointIntegrationTests {
|
||||||
MBeanServer mBeanServer = context.getBean(MBeanServer.class);
|
MBeanServer mBeanServer = context.getBean(MBeanServer.class);
|
||||||
checkEndpointMBeans(mBeanServer, new String[0],
|
checkEndpointMBeans(mBeanServer, new String[0],
|
||||||
new String[] { "autoconfig", "beans", "configprops", "env",
|
new String[] { "autoconfig", "beans", "configprops", "env",
|
||||||
"health", "mappings", "metrics", "shutdown",
|
"health", "mappings", "shutdown",
|
||||||
"threaddump", "trace" });
|
"threaddump", "trace" });
|
||||||
|
|
||||||
});
|
});
|
||||||
|
@ -81,7 +81,7 @@ public class JmxEndpointIntegrationTests {
|
||||||
MBeanServer mBeanServer = context.getBean(MBeanServer.class);
|
MBeanServer mBeanServer = context.getBean(MBeanServer.class);
|
||||||
checkEndpointMBeans(mBeanServer, new String[] { "beans" },
|
checkEndpointMBeans(mBeanServer, new String[] { "beans" },
|
||||||
new String[] { "autoconfig", "configprops", "env", "health",
|
new String[] { "autoconfig", "configprops", "env", "health",
|
||||||
"mappings", "metrics", "shutdown",
|
"mappings", "shutdown",
|
||||||
"threaddump", "trace" });
|
"threaddump", "trace" });
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
|
@ -71,7 +71,6 @@ public class WebMvcEndpointExposureIntegrationTests {
|
||||||
assertThat(isExposed(mvc, HttpMethod.GET, "health")).isFalse();
|
assertThat(isExposed(mvc, HttpMethod.GET, "health")).isFalse();
|
||||||
assertThat(isExposed(mvc, HttpMethod.GET, "info")).isTrue();
|
assertThat(isExposed(mvc, HttpMethod.GET, "info")).isTrue();
|
||||||
assertThat(isExposed(mvc, HttpMethod.GET, "mappings")).isFalse();
|
assertThat(isExposed(mvc, HttpMethod.GET, "mappings")).isFalse();
|
||||||
assertThat(isExposed(mvc, HttpMethod.GET, "metrics")).isFalse();
|
|
||||||
assertThat(isExposed(mvc, HttpMethod.POST, "shutdown")).isFalse();
|
assertThat(isExposed(mvc, HttpMethod.POST, "shutdown")).isFalse();
|
||||||
assertThat(isExposed(mvc, HttpMethod.GET, "status")).isTrue();
|
assertThat(isExposed(mvc, HttpMethod.GET, "status")).isTrue();
|
||||||
assertThat(isExposed(mvc, HttpMethod.GET, "threaddump")).isFalse();
|
assertThat(isExposed(mvc, HttpMethod.GET, "threaddump")).isFalse();
|
||||||
|
@ -92,7 +91,6 @@ public class WebMvcEndpointExposureIntegrationTests {
|
||||||
assertThat(isExposed(mvc, HttpMethod.GET, "health")).isTrue();
|
assertThat(isExposed(mvc, HttpMethod.GET, "health")).isTrue();
|
||||||
assertThat(isExposed(mvc, HttpMethod.GET, "info")).isTrue();
|
assertThat(isExposed(mvc, HttpMethod.GET, "info")).isTrue();
|
||||||
assertThat(isExposed(mvc, HttpMethod.GET, "mappings")).isTrue();
|
assertThat(isExposed(mvc, HttpMethod.GET, "mappings")).isTrue();
|
||||||
assertThat(isExposed(mvc, HttpMethod.GET, "metrics")).isTrue();
|
|
||||||
assertThat(isExposed(mvc, HttpMethod.POST, "shutdown")).isFalse();
|
assertThat(isExposed(mvc, HttpMethod.POST, "shutdown")).isFalse();
|
||||||
assertThat(isExposed(mvc, HttpMethod.GET, "status")).isTrue();
|
assertThat(isExposed(mvc, HttpMethod.GET, "status")).isTrue();
|
||||||
assertThat(isExposed(mvc, HttpMethod.GET, "threaddump")).isTrue();
|
assertThat(isExposed(mvc, HttpMethod.GET, "threaddump")).isTrue();
|
||||||
|
@ -114,7 +112,6 @@ public class WebMvcEndpointExposureIntegrationTests {
|
||||||
assertThat(isExposed(mvc, HttpMethod.GET, "health")).isFalse();
|
assertThat(isExposed(mvc, HttpMethod.GET, "health")).isFalse();
|
||||||
assertThat(isExposed(mvc, HttpMethod.GET, "info")).isTrue();
|
assertThat(isExposed(mvc, HttpMethod.GET, "info")).isTrue();
|
||||||
assertThat(isExposed(mvc, HttpMethod.GET, "mappings")).isFalse();
|
assertThat(isExposed(mvc, HttpMethod.GET, "mappings")).isFalse();
|
||||||
assertThat(isExposed(mvc, HttpMethod.GET, "metrics")).isFalse();
|
|
||||||
assertThat(isExposed(mvc, HttpMethod.POST, "shutdown")).isFalse();
|
assertThat(isExposed(mvc, HttpMethod.POST, "shutdown")).isFalse();
|
||||||
assertThat(isExposed(mvc, HttpMethod.GET, "status")).isTrue();
|
assertThat(isExposed(mvc, HttpMethod.GET, "status")).isTrue();
|
||||||
assertThat(isExposed(mvc, HttpMethod.GET, "threaddump")).isFalse();
|
assertThat(isExposed(mvc, HttpMethod.GET, "threaddump")).isFalse();
|
||||||
|
|
|
@ -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");
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -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<String, GaugeWriter> exporters = (Map<String, GaugeWriter>) ReflectionTestUtils
|
|
||||||
.getField(schedulingConfigurer, "writers");
|
|
||||||
assertThat(exporters).containsValue(statsdWriter);
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
@Configuration
|
|
||||||
public static class MessageChannelConfiguration {
|
|
||||||
|
|
||||||
@Bean
|
|
||||||
public SubscribableChannel metricsChannel() {
|
|
||||||
return new FixedSubscriberChannel((message) -> {
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
@Configuration
|
|
||||||
public static class WriterConfig {
|
|
||||||
|
|
||||||
@Bean
|
|
||||||
@ExportMetricWriter
|
|
||||||
public MetricWriter writer() {
|
|
||||||
return Mockito.mock(MetricWriter.class);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
@Configuration
|
|
||||||
public static class MetricEndpointConfiguration {
|
|
||||||
|
|
||||||
@Bean
|
|
||||||
@ExportMetricReader
|
|
||||||
public MetricsEndpointMetricReader endpointReader() {
|
|
||||||
return Mockito.mock(MetricsEndpointMetricReader.class);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -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<ResponseEntity<String>> create() {
|
|
||||||
final DeferredResult<ResponseEntity<String>> 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<ResponseEntity<String>> createFailure() {
|
|
||||||
final DeferredResult<ResponseEntity<String>> 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());
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -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<Double> gauge = (Gauge<Double>) registry.getMetrics().get("gauge.foo");
|
|
||||||
assertThat(gauge.getValue()).isEqualTo(new Double(2.7));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void skipsIfBeansExist() throws Exception {
|
|
||||||
this.context = new AnnotationConfigApplicationContext(Config.class,
|
|
||||||
MetricRepositoryAutoConfiguration.class);
|
|
||||||
assertThat(this.context.getBeansOfType(BufferGaugeService.class)).isEmpty();
|
|
||||||
assertThat(this.context.getBeansOfType(BufferCounterService.class)).isEmpty();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Configuration
|
|
||||||
public static class Config {
|
|
||||||
|
|
||||||
@Bean
|
|
||||||
public GaugeService gaugeService() {
|
|
||||||
return mock(GaugeService.class);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Bean
|
|
||||||
public CounterService counterService() {
|
|
||||||
return mock(CounterService.class);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -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<String> personName() {
|
||||||
|
return Collections.singleton("Jon");
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -16,36 +16,32 @@
|
||||||
|
|
||||||
package org.springframework.boot.actuate.autoconfigure.metrics;
|
package org.springframework.boot.actuate.autoconfigure.metrics;
|
||||||
|
|
||||||
|
import io.micrometer.core.instrument.composite.CompositeMeterRegistry;
|
||||||
|
import io.micrometer.graphite.GraphiteMeterRegistry;
|
||||||
|
import io.micrometer.prometheus.PrometheusMeterRegistry;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
|
|
||||||
import org.springframework.boot.actuate.metrics.MetricsEndpoint;
|
|
||||||
import org.springframework.boot.autoconfigure.AutoConfigurations;
|
import org.springframework.boot.autoconfigure.AutoConfigurations;
|
||||||
import org.springframework.boot.test.context.runner.ApplicationContextRunner;
|
import org.springframework.boot.test.context.runner.ApplicationContextRunner;
|
||||||
|
|
||||||
import static org.assertj.core.api.Assertions.assertThat;
|
import static org.assertj.core.api.Assertions.assertThat;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Tests for {@link MetricsEndpointAutoConfiguration}.
|
* Tests for {@link MetricsAutoConfiguration} creating a {@link CompositeMeterRegistry}.
|
||||||
*
|
*
|
||||||
* @author Phillip Webb
|
* @author Jon Schneider
|
||||||
*/
|
*/
|
||||||
public class MetricsEndpointAutoConfigurationTests {
|
public class MetricsConfigurationCompositeTests {
|
||||||
|
|
||||||
private final ApplicationContextRunner contextRunner = new ApplicationContextRunner()
|
|
||||||
.withConfiguration(
|
|
||||||
AutoConfigurations.of(MetricsEndpointAutoConfiguration.class));
|
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void runShouldHaveEndpointBean() {
|
public void compositeContainsImplementationsOnClasspath() {
|
||||||
this.contextRunner.run(
|
new ApplicationContextRunner()
|
||||||
(context) -> assertThat(context).hasSingleBean(MetricsEndpoint.class));
|
.withConfiguration(AutoConfigurations.of(MetricsAutoConfiguration.class))
|
||||||
}
|
.withPropertyValues("metrics.use-global-registry=false")
|
||||||
|
.run((context) -> assertThat(
|
||||||
@Test
|
context.getBean(CompositeMeterRegistry.class).getRegistries())
|
||||||
public void runWhenEnabledPropertyIsFalseShouldNotHaveEndpointBean()
|
.hasAtLeastOneElementOfType(PrometheusMeterRegistry.class)
|
||||||
throws Exception {
|
.hasAtLeastOneElementOfType(GraphiteMeterRegistry.class));
|
||||||
this.contextRunner.withPropertyValues("endpoints.metrics.enabled:false").run(
|
|
||||||
(context) -> assertThat(context).doesNotHaveBean(MetricsEndpoint.class));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
|
@ -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();
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -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<Metric<?>> 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<Metric<?>> 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<Metric<?>> 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<Void>) (connection) -> null);
|
|
||||||
Collection<Metric<?>> 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<Metric<?>> 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<Metric<?>> 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<Metric<?>> 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<Metric<?>> 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<Metric<?>> metrics = bean.metrics();
|
|
||||||
assertMetrics(metrics, "cache.books.size", "cache.second_speakers.size",
|
|
||||||
"cache.first_speakers.size", "cache.users.size");
|
|
||||||
}
|
|
||||||
|
|
||||||
private void assertHasMetric(Collection<Metric<?>> 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<Metric<?>> metrics, String... keys) {
|
|
||||||
Map<String, Number> 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");
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -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);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -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 {
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -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);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -31,29 +31,14 @@
|
||||||
<artifactId>jackson-databind</artifactId>
|
<artifactId>jackson-databind</artifactId>
|
||||||
<optional>true</optional>
|
<optional>true</optional>
|
||||||
</dependency>
|
</dependency>
|
||||||
<dependency>
|
|
||||||
<groupId>com.github.ben-manes.caffeine</groupId>
|
|
||||||
<artifactId>caffeine</artifactId>
|
|
||||||
<optional>true</optional>
|
|
||||||
</dependency>
|
|
||||||
<dependency>
|
|
||||||
<groupId>com.hazelcast</groupId>
|
|
||||||
<artifactId>hazelcast</artifactId>
|
|
||||||
<optional>true</optional>
|
|
||||||
</dependency>
|
|
||||||
<dependency>
|
|
||||||
<groupId>com.hazelcast</groupId>
|
|
||||||
<artifactId>hazelcast-spring</artifactId>
|
|
||||||
<optional>true</optional>
|
|
||||||
</dependency>
|
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>com.sun.mail</groupId>
|
<groupId>com.sun.mail</groupId>
|
||||||
<artifactId>javax.mail</artifactId>
|
<artifactId>javax.mail</artifactId>
|
||||||
<optional>true</optional>
|
<optional>true</optional>
|
||||||
</dependency>
|
</dependency>
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>com.timgroup</groupId>
|
<groupId>com.zaxxer</groupId>
|
||||||
<artifactId>java-statsd-client</artifactId>
|
<artifactId>HikariCP</artifactId>
|
||||||
<optional>true</optional>
|
<optional>true</optional>
|
||||||
</dependency>
|
</dependency>
|
||||||
<dependency>
|
<dependency>
|
||||||
|
@ -61,6 +46,16 @@
|
||||||
<artifactId>lettuce-core</artifactId>
|
<artifactId>lettuce-core</artifactId>
|
||||||
<optional>true</optional>
|
<optional>true</optional>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>io.micrometer</groupId>
|
||||||
|
<artifactId>micrometer-core</artifactId>
|
||||||
|
<optional>true</optional>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>io.micrometer</groupId>
|
||||||
|
<artifactId>micrometer-prometheus-starter</artifactId>
|
||||||
|
<optional>true</optional>
|
||||||
|
</dependency>
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>io.searchbox</groupId>
|
<groupId>io.searchbox</groupId>
|
||||||
<artifactId>jest</artifactId>
|
<artifactId>jest</artifactId>
|
||||||
|
@ -72,13 +67,13 @@
|
||||||
<optional>true</optional>
|
<optional>true</optional>
|
||||||
</dependency>
|
</dependency>
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>net.sf.ehcache</groupId>
|
<groupId>org.apache.tomcat.embed</groupId>
|
||||||
<artifactId>ehcache</artifactId>
|
<artifactId>tomcat-embed-core</artifactId>
|
||||||
<optional>true</optional>
|
<optional>true</optional>
|
||||||
</dependency>
|
</dependency>
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.apache.tomcat.embed</groupId>
|
<groupId>org.aspectj</groupId>
|
||||||
<artifactId>tomcat-embed-core</artifactId>
|
<artifactId>aspectjweaver</artifactId>
|
||||||
<optional>true</optional>
|
<optional>true</optional>
|
||||||
</dependency>
|
</dependency>
|
||||||
<dependency>
|
<dependency>
|
||||||
|
|
|
@ -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 <C> The cache type
|
|
||||||
* @author Stephane Nicoll
|
|
||||||
* @since 1.3.0
|
|
||||||
*/
|
|
||||||
public abstract class AbstractJmxCacheStatisticsProvider<C extends Cache>
|
|
||||||
implements CacheStatisticsProvider<C> {
|
|
||||||
|
|
||||||
private static final Logger logger = LoggerFactory
|
|
||||||
.getLogger(AbstractJmxCacheStatisticsProvider.class);
|
|
||||||
|
|
||||||
private MBeanServer mBeanServer;
|
|
||||||
|
|
||||||
private final Map<String, ObjectNameWrapper> caches = new ConcurrentHashMap<>();
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public CacheStatistics getCacheStatistics(CacheManager cacheManager, C cache) {
|
|
||||||
try {
|
|
||||||
ObjectName objectName = internalGetObjectName(cache);
|
|
||||||
return (objectName == null ? null : getCacheStatistics(objectName));
|
|
||||||
}
|
|
||||||
catch (MalformedObjectNameException ex) {
|
|
||||||
throw new IllegalStateException(ex);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Return the {@link ObjectName} of the MBean that is managing the specified cache or
|
|
||||||
* {@code null} if none is found.
|
|
||||||
* @param cache the cache to handle
|
|
||||||
* @return the object name of the cache statistics MBean
|
|
||||||
* @throws MalformedObjectNameException if the {@link ObjectName} for that cache is
|
|
||||||
* invalid
|
|
||||||
*/
|
|
||||||
protected abstract ObjectName getObjectName(C cache)
|
|
||||||
throws MalformedObjectNameException;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Return the current {@link CacheStatistics} snapshot from the MBean identified by
|
|
||||||
* the specified {@link ObjectName}.
|
|
||||||
* @param objectName the object name of the cache statistics MBean
|
|
||||||
* @return the current cache statistics
|
|
||||||
*/
|
|
||||||
protected abstract CacheStatistics getCacheStatistics(ObjectName objectName);
|
|
||||||
|
|
||||||
private ObjectName internalGetObjectName(C cache)
|
|
||||||
throws MalformedObjectNameException {
|
|
||||||
String cacheName = cache.getName();
|
|
||||||
ObjectNameWrapper value = this.caches.get(cacheName);
|
|
||||||
if (value != null) {
|
|
||||||
return value.objectName;
|
|
||||||
}
|
|
||||||
ObjectName objectName = getObjectName(cache);
|
|
||||||
this.caches.put(cacheName, new ObjectNameWrapper(objectName));
|
|
||||||
return objectName;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected MBeanServer getMBeanServer() {
|
|
||||||
if (this.mBeanServer == null) {
|
|
||||||
this.mBeanServer = ManagementFactory.getPlatformMBeanServer();
|
|
||||||
}
|
|
||||||
return this.mBeanServer;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected <T> T getAttribute(ObjectName objectName, String attributeName,
|
|
||||||
Class<T> type) {
|
|
||||||
try {
|
|
||||||
Object attribute = getMBeanServer().getAttribute(objectName, attributeName);
|
|
||||||
return type.cast(attribute);
|
|
||||||
}
|
|
||||||
catch (MBeanException | ReflectionException ex) {
|
|
||||||
throw new IllegalStateException(ex);
|
|
||||||
}
|
|
||||||
catch (AttributeNotFoundException ex) {
|
|
||||||
throw new IllegalStateException("Unexpected: MBean with name '" + objectName
|
|
||||||
+ "' " + "does not expose attribute with name " + attributeName, ex);
|
|
||||||
}
|
|
||||||
catch (InstanceNotFoundException ex) {
|
|
||||||
logger.warn("Cache statistics are no longer available", ex);
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private static class ObjectNameWrapper {
|
|
||||||
|
|
||||||
private final ObjectName objectName;
|
|
||||||
|
|
||||||
ObjectNameWrapper(ObjectName objectName) {
|
|
||||||
this.objectName = objectName;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -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<String, CacheManager> cacheManagers;
|
|
||||||
|
|
||||||
private final Collection<CacheStatisticsProvider<?>> statisticsProviders;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Create a new {@link CachePublicMetrics} instance.
|
|
||||||
* @param cacheManagers the cache managers
|
|
||||||
* @param statisticsProviders the statistics providers
|
|
||||||
*/
|
|
||||||
public CachePublicMetrics(Map<String, CacheManager> cacheManagers,
|
|
||||||
Collection<CacheStatisticsProvider<?>> statisticsProviders) {
|
|
||||||
this.cacheManagers = cacheManagers;
|
|
||||||
this.statisticsProviders = statisticsProviders;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Collection<Metric<?>> metrics() {
|
|
||||||
Collection<Metric<?>> metrics = new HashSet<>();
|
|
||||||
for (Map.Entry<String, List<CacheManagerBean>> entry : getCacheManagerBeans()
|
|
||||||
.entrySet()) {
|
|
||||||
addMetrics(metrics, entry.getKey(), entry.getValue());
|
|
||||||
}
|
|
||||||
return metrics;
|
|
||||||
}
|
|
||||||
|
|
||||||
private MultiValueMap<String, CacheManagerBean> getCacheManagerBeans() {
|
|
||||||
MultiValueMap<String, CacheManagerBean> cacheManagerNamesByCacheName = new LinkedMultiValueMap<>();
|
|
||||||
for (Map.Entry<String, CacheManager> 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<Metric<?>> metrics, String cacheName,
|
|
||||||
List<CacheManagerBean> 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;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -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<Metric<?>> 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%.
|
|
||||||
* <p>
|
|
||||||
* 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%.
|
|
||||||
* <p>
|
|
||||||
* 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();
|
|
||||||
|
|
||||||
}
|
|
|
@ -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 <C> The {@link Cache} type
|
|
||||||
* @author Stephane Nicoll
|
|
||||||
* @author Phillip Webb
|
|
||||||
* @since 1.3.0
|
|
||||||
*/
|
|
||||||
@FunctionalInterface
|
|
||||||
public interface CacheStatisticsProvider<C extends Cache> {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Return the current {@link CacheStatistics} snapshot for the specified {@link Cache}
|
|
||||||
* or {@code null} if the given cache could not be handled.
|
|
||||||
* @param cacheManager the {@link CacheManager} handling this cache
|
|
||||||
* @param cache the cache to handle
|
|
||||||
* @return the current cache statistics or {@code null}
|
|
||||||
*/
|
|
||||||
CacheStatistics getCacheStatistics(CacheManager cacheManager, C cache);
|
|
||||||
|
|
||||||
}
|
|
|
@ -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<CaffeineCache> {
|
|
||||||
|
|
||||||
@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;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -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<ConcurrentMapCache> {
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public CacheStatistics getCacheStatistics(CacheManager cacheManager,
|
|
||||||
ConcurrentMapCache cache) {
|
|
||||||
DefaultCacheStatistics statistics = new DefaultCacheStatistics();
|
|
||||||
statistics.setSize((long) cache.getNativeCache().size());
|
|
||||||
return statistics;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -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<Metric<?>> toMetrics(String prefix) {
|
|
||||||
Collection<Metric<?>> 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 <T extends Number> void addMetric(Collection<Metric<?>> metrics, String name,
|
|
||||||
T value) {
|
|
||||||
if (value != null) {
|
|
||||||
metrics.add(new Metric<>(name, value));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -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<EhCacheCache> {
|
|
||||||
|
|
||||||
@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);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -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<HazelcastCache> {
|
|
||||||
|
|
||||||
@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;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -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<SpringCache> {
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected ObjectName getObjectName(SpringCache cache)
|
|
||||||
throws MalformedObjectNameException {
|
|
||||||
ObjectName name = new ObjectName(
|
|
||||||
"org.infinispan:component=Statistics,type=Cache,name=\"" + cache.getName()
|
|
||||||
+ "(local)\",*");
|
|
||||||
Set<ObjectInstance> 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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -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<JCacheCache> {
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected ObjectName getObjectName(JCacheCache cache)
|
|
||||||
throws MalformedObjectNameException {
|
|
||||||
ObjectName name = new ObjectName(
|
|
||||||
"javax.cache:type=CacheStatistics,Cache=" + cache.getName() + ",*");
|
|
||||||
Set<ObjectInstance> 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;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -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;
|
|
|
@ -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);
|
|
||||||
|
|
||||||
}
|
|
|
@ -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<DataSourcePoolMetadataProvider> providers;
|
|
||||||
|
|
||||||
private final Map<String, DataSourcePoolMetadata> metadataByPrefix = new HashMap<>();
|
|
||||||
|
|
||||||
@PostConstruct
|
|
||||||
public void initialize() {
|
|
||||||
DataSource primaryDataSource = getPrimaryDataSource();
|
|
||||||
DataSourcePoolMetadataProvider provider = new DataSourcePoolMetadataProviders(
|
|
||||||
this.providers);
|
|
||||||
for (Map.Entry<String, DataSource> 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<Metric<?>> metrics() {
|
|
||||||
Set<Metric<?>> metrics = new LinkedHashSet<>();
|
|
||||||
for (Map.Entry<String, DataSourcePoolMetadata> 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 <T extends Number> void addMetric(Set<Metric<?>> 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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -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);
|
|
||||||
|
|
||||||
}
|
|
|
@ -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 <T> the value type
|
|
||||||
* @author Dave Syer
|
|
||||||
*/
|
|
||||||
public class Metric<T extends Number> {
|
|
||||||
|
|
||||||
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<Long> increment(int amount) {
|
|
||||||
return new Metric<>(this.getName(),
|
|
||||||
Long.valueOf(this.getValue().longValue() + amount));
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Create a new {@link Metric} with a different value.
|
|
||||||
* @param <S> the metric value type
|
|
||||||
* @param value the value of the new metric
|
|
||||||
* @return a new {@link Metric} instance
|
|
||||||
*/
|
|
||||||
public <S extends Number> Metric<S> 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);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -16,93 +16,100 @@
|
||||||
|
|
||||||
package org.springframework.boot.actuate.metrics;
|
package org.springframework.boot.actuate.metrics;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.LinkedHashMap;
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.function.Predicate;
|
import java.util.stream.Collectors;
|
||||||
import java.util.regex.Pattern;
|
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.Endpoint;
|
||||||
import org.springframework.boot.actuate.endpoint.annotation.ReadOperation;
|
import org.springframework.boot.actuate.endpoint.annotation.ReadOperation;
|
||||||
import org.springframework.boot.actuate.endpoint.annotation.Selector;
|
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
|
* @since 2.0.0
|
||||||
*/
|
*/
|
||||||
@Endpoint(id = "metrics")
|
@Endpoint(id = "metrics")
|
||||||
public class MetricsEndpoint {
|
public class MetricsEndpoint {
|
||||||
|
|
||||||
private final List<PublicMetrics> publicMetrics;
|
private final MeterRegistry registry;
|
||||||
|
|
||||||
/**
|
public MetricsEndpoint(MeterRegistry registry) {
|
||||||
* Create a new {@link MetricsEndpoint} instance.
|
this.registry = registry;
|
||||||
* @param publicMetrics the metrics to expose
|
|
||||||
*/
|
|
||||||
public MetricsEndpoint(PublicMetrics publicMetrics) {
|
|
||||||
this(Collections.singleton(publicMetrics));
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 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> publicMetrics) {
|
|
||||||
Assert.notNull(publicMetrics, "PublicMetrics must not be null");
|
|
||||||
this.publicMetrics = new ArrayList<>(publicMetrics);
|
|
||||||
AnnotationAwareOrderComparator.sort(this.publicMetrics);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void registerPublicMetrics(PublicMetrics metrics) {
|
|
||||||
this.publicMetrics.add(metrics);
|
|
||||||
AnnotationAwareOrderComparator.sort(this.publicMetrics);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void unregisterPublicMetrics(PublicMetrics metrics) {
|
|
||||||
this.publicMetrics.remove(metrics);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@ReadOperation
|
@ReadOperation
|
||||||
public Map<String, Object> metrics(String pattern) {
|
public Map<String, List<String>> listNames() {
|
||||||
return metrics(StringUtils.hasText(pattern)
|
return Collections.singletonMap("names", this.registry.getMeters().stream()
|
||||||
? Pattern.compile(pattern).asPredicate() : (name) -> true);
|
.map(this::getMeterIdName).collect(Collectors.toList()));
|
||||||
|
}
|
||||||
|
|
||||||
|
private String getMeterIdName(Meter meter) {
|
||||||
|
return meter.getId().getName();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ReadOperation
|
@ReadOperation
|
||||||
public Map<String, Object> metricNamed(@Selector String requiredName) {
|
public Map<String, Collection<MeasurementSample>> metric(
|
||||||
Map<String, Object> metrics = metrics((name) -> name.equals(requiredName));
|
@Selector String requiredMetricName) {
|
||||||
if (metrics.isEmpty()) {
|
return this.registry.find(requiredMetricName).meters().stream()
|
||||||
return null;
|
.collect(Collectors.toMap(this::getHierarchicalName, this::getSamples));
|
||||||
}
|
|
||||||
return metrics;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private Map<String, Object> metrics(Predicate<String> namePredicate) {
|
private List<MeasurementSample> getSamples(Meter meter) {
|
||||||
Map<String, Object> result = new LinkedHashMap<>();
|
return stream(meter.measure()).map(this::getSample).collect(Collectors.toList());
|
||||||
List<PublicMetrics> 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());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private MeasurementSample getSample(Measurement measurement) {
|
||||||
|
return new MeasurementSample(measurement.getStatistic(), measurement.getValue());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private String getHierarchicalName(Meter meter) {
|
||||||
|
return HierarchicalNameMapper.DEFAULT.toHierarchicalName(meter.getId());
|
||||||
}
|
}
|
||||||
catch (Exception ex) {
|
|
||||||
// Could not evaluate metrics
|
private <T> Stream<T> stream(Iterable<T> measure) {
|
||||||
|
return StreamSupport.stream(measure.spliterator(), false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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 + '}';
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -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<Metric<?>> metrics() {
|
|
||||||
Collection<Metric<?>> result = new LinkedHashSet<>();
|
|
||||||
addBasicMetrics(result);
|
|
||||||
addManagementMetrics(result);
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Add basic system metrics.
|
|
||||||
* @param result the result
|
|
||||||
*/
|
|
||||||
protected void addBasicMetrics(Collection<Metric<?>> 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<Metric<?>> 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<Metric<?>> 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<Metric<?>> 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<Long> newMemoryMetric(String name, long bytes) {
|
|
||||||
return new Metric<>(name, bytes / 1024);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Add thread metrics.
|
|
||||||
* @param result the result
|
|
||||||
*/
|
|
||||||
protected void addThreadMetrics(Collection<Metric<?>> 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<Metric<?>> 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<Metric<?>> result) {
|
|
||||||
List<GarbageCollectorMXBean> 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();
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -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<Metric<?>> 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<Metric<?>> metrics(Manager manager) {
|
|
||||||
List<Metric<?>> 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<Metric<?>> metrics, String name, Integer value) {
|
|
||||||
metrics.add(new Metric<>(name, value));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void setApplicationContext(ApplicationContext applicationContext)
|
|
||||||
throws BeansException {
|
|
||||||
this.applicationContext = applicationContext;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -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:
|
|
||||||
* <ul>
|
|
||||||
* <li>"d" means "discard" this key segment (useful for global prefixes like system
|
|
||||||
* identifiers, or aggregate keys a.k.a. physical identifiers)</li>
|
|
||||||
* <li>"k" means "keep" it with no change (useful for logical identifiers like app
|
|
||||||
* names)</li>
|
|
||||||
* </ul>
|
|
||||||
* 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<Metric<?>> 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<String> 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<Number>(name, metric.getValue(),
|
|
||||||
metric.getTimestamp());
|
|
||||||
}
|
|
||||||
else if (key.contains("counter.")) {
|
|
||||||
// accumulate all values
|
|
||||||
aggregate = new Metric<Number>(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<Number>(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();
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -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;
|
|
|
@ -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 <T> the value type
|
|
||||||
* @author Dave Syer
|
|
||||||
* @author Phillip Webb
|
|
||||||
*/
|
|
||||||
abstract class Buffer<T extends Number> {
|
|
||||||
|
|
||||||
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();
|
|
||||||
|
|
||||||
}
|
|
|
@ -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<String, String> 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;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -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<String, String> 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;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -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<String> 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<Metric<?>> findAll() {
|
|
||||||
return findAll(BufferMetricReader.ALL);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Iterable<Metric<?>> findAll(String prefix) {
|
|
||||||
return findAll(Pattern.compile(prefix + ".*").asPredicate());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public long count() {
|
|
||||||
return this.counterBuffers.count() + this.gaugeBuffers.count();
|
|
||||||
}
|
|
||||||
|
|
||||||
private Iterable<Metric<?>> findAll(Predicate<String> predicate) {
|
|
||||||
final List<Metric<?>> metrics = new ArrayList<>();
|
|
||||||
collectMetrics(this.gaugeBuffers, predicate, metrics);
|
|
||||||
collectMetrics(this.counterBuffers, predicate, metrics);
|
|
||||||
return metrics;
|
|
||||||
}
|
|
||||||
|
|
||||||
private <T extends Number, B extends Buffer<T>> void collectMetrics(
|
|
||||||
Buffers<B> buffers, Predicate<String> predicate,
|
|
||||||
final List<Metric<?>> metrics) {
|
|
||||||
buffers.forEach(predicate, (name, value) -> metrics.add(asMetric(name, value)));
|
|
||||||
}
|
|
||||||
|
|
||||||
private <T extends Number> Metric<T> asMetric(String name, Buffer<T> buffer) {
|
|
||||||
return new Metric<>(name, buffer.getValue(), new Date(buffer.getTimestamp()));
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -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 <B> The buffer type
|
|
||||||
* @author Dave Syer
|
|
||||||
* @author Phillip Webb
|
|
||||||
*/
|
|
||||||
abstract class Buffers<B extends Buffer<?>> {
|
|
||||||
|
|
||||||
private final ConcurrentHashMap<String, B> buffers = new ConcurrentHashMap<>();
|
|
||||||
|
|
||||||
public void forEach(final Predicate<String> predicate,
|
|
||||||
BiConsumer<String, B> 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<B> consumer) {
|
|
||||||
B buffer = this.buffers.get(name);
|
|
||||||
if (buffer == null) {
|
|
||||||
buffer = this.buffers.computeIfAbsent(name, (k) -> createBuffer());
|
|
||||||
}
|
|
||||||
consumer.accept(buffer);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected abstract B createBuffer();
|
|
||||||
|
|
||||||
}
|
|
|
@ -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<Long> {
|
|
||||||
|
|
||||||
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();
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -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<Double> {
|
|
||||||
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -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<GaugeBuffer> {
|
|
||||||
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -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;
|
|
|
@ -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.
|
|
||||||
* <ul>
|
|
||||||
* <li>Updates to {@link #increment(String)} with names in "meter.*" are treated as
|
|
||||||
* {@link Meter} events</li>
|
|
||||||
* <li>Other deltas are treated as simple {@link Counter} values</li>
|
|
||||||
* <li>Inputs to {@link #submit(String, double)} with names in "histogram.*" are treated
|
|
||||||
* as {@link Histogram} updates</li>
|
|
||||||
* <li>Inputs to {@link #submit(String, double)} with names in "timer.*" are treated as
|
|
||||||
* {@link Timer} updates</li>
|
|
||||||
* <li>Other metrics are treated as simple {@link Gauge} values (single valued
|
|
||||||
* measurements of type double)</li>
|
|
||||||
* </ul>
|
|
||||||
*
|
|
||||||
* @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<String, SimpleGauge> gauges = new ConcurrentHashMap<>();
|
|
||||||
|
|
||||||
private final ConcurrentHashMap<String, String> 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 extends Metric> T register(String name, MetricRegistrar<T> 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<Double> {
|
|
||||||
|
|
||||||
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<T extends Metric> {
|
|
||||||
|
|
||||||
private final Class<T> type;
|
|
||||||
|
|
||||||
@SuppressWarnings("unchecked")
|
|
||||||
MetricRegistrar() {
|
|
||||||
this.type = (Class<T>) 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<Timer> {
|
|
||||||
|
|
||||||
@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<Histogram> {
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected Histogram register(MetricRegistry registry, String name) {
|
|
||||||
return registry.histogram(name);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected Histogram createForReservoir(Reservoir reservoir) {
|
|
||||||
return new Histogram(reservoir);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -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);
|
|
||||||
|
|
||||||
}
|
|
|
@ -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;
|
|
|
@ -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<Metric<?>> 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<Number>(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<String> 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<Metric<?>> values);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get the next group of metrics to write.
|
|
||||||
* @param group the group name to write
|
|
||||||
* @return some metrics to write
|
|
||||||
*/
|
|
||||||
protected abstract Iterable<Metric<?>> next(String group);
|
|
||||||
|
|
||||||
}
|
|
|
@ -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();
|
|
||||||
|
|
||||||
}
|
|
|
@ -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<String, Long> 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<Metric<?>> 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<Metric<?>> 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<Metric<?>> {
|
|
||||||
|
|
||||||
private final MetricReader reader;
|
|
||||||
|
|
||||||
PatternMatchingIterable(MetricReader reader) {
|
|
||||||
this.reader = reader;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Iterator<Metric<?>> iterator() {
|
|
||||||
return new PatternMatchingIterator(this.reader.findAll().iterator());
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
private class PatternMatchingIterator implements Iterator<Metric<?>> {
|
|
||||||
|
|
||||||
private Metric<?> buffer = null;
|
|
||||||
|
|
||||||
private Iterator<Metric<?>> iterator;
|
|
||||||
|
|
||||||
PatternMatchingIterator(Iterator<Metric<?>> 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;
|
|
||||||
}
|
|
||||||
|
|
||||||
};
|
|
||||||
|
|
||||||
}
|
|
|
@ -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<String, SpecificTriggerProperties> 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<String, SpecificTriggerProperties> 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<String, SpecificTriggerProperties> 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;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -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<String, GaugeWriter> writers = new HashMap<>();
|
|
||||||
|
|
||||||
private final MetricExportProperties properties;
|
|
||||||
|
|
||||||
private final Map<String, Exporter> exporters = new HashMap<>();
|
|
||||||
|
|
||||||
private final Set<String> closeables = new HashSet<>();
|
|
||||||
|
|
||||||
public MetricExporters(MetricExportProperties properties) {
|
|
||||||
this.properties = properties;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setReader(MetricReader reader) {
|
|
||||||
this.reader = reader;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setWriters(Map<String, GaugeWriter> writers) {
|
|
||||||
this.writers.putAll(writers);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setExporters(Map<String, Exporter> exporters) {
|
|
||||||
this.exporters.putAll(exporters);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void configureTasks(ScheduledTaskRegistrar taskRegistrar) {
|
|
||||||
for (Entry<String, Exporter> 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<String, GaugeWriter> 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<String, Exporter> 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();
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -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<String, Long> counts = new ConcurrentHashMap<>();
|
|
||||||
|
|
||||||
private Set<String> 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<String> groups) {
|
|
||||||
this.groups = groups;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected Iterable<String> groups() {
|
|
||||||
if ((this.reader instanceof MultiMetricRepository) && this.groups.isEmpty()) {
|
|
||||||
return ((MultiMetricRepository) this.reader).groups();
|
|
||||||
}
|
|
||||||
return this.groups;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected Iterable<Metric<?>> next(String group) {
|
|
||||||
return this.reader.findAll(group);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void write(String group, Collection<Metric<?>> 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());
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -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<Metric<?>> next(String group) {
|
|
||||||
RichGauge rich = this.reader.findOne(group);
|
|
||||||
Collection<Metric<?>> metrics = new ArrayList<>();
|
|
||||||
metrics.add(new Metric<Number>(group + MIN, rich.getMin()));
|
|
||||||
metrics.add(new Metric<Number>(group + MAX, rich.getMax()));
|
|
||||||
metrics.add(new Metric<Number>(group + COUNT, rich.getCount()));
|
|
||||||
metrics.add(new Metric<Number>(group + VALUE, rich.getValue()));
|
|
||||||
metrics.add(new Metric<Number>(group + AVG, rich.getAverage()));
|
|
||||||
metrics.add(new Metric<Number>(group + ALPHA, rich.getAlpha()));
|
|
||||||
return metrics;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected Iterable<String> groups() {
|
|
||||||
Collection<String> names = new HashSet<>();
|
|
||||||
for (RichGauge rich : this.reader.findAll()) {
|
|
||||||
names.add(rich.getName());
|
|
||||||
}
|
|
||||||
return names;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void write(String group, Collection<Metric<?>> values) {
|
|
||||||
this.writer.set(group, values);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -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;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -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;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue