diff --git a/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/autoconfigure/MetricFilterAutoConfiguration.java b/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/autoconfigure/MetricFilterAutoConfiguration.java index e15f26aee1..6bfb1b4f6b 100644 --- a/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/autoconfigure/MetricFilterAutoConfiguration.java +++ b/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/autoconfigure/MetricFilterAutoConfiguration.java @@ -22,8 +22,6 @@ import javax.servlet.Filter; import javax.servlet.FilterChain; import javax.servlet.Servlet; import javax.servlet.ServletException; -import javax.servlet.ServletRequest; -import javax.servlet.ServletResponse; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; @@ -39,7 +37,7 @@ import org.springframework.context.annotation.Configuration; import org.springframework.core.Ordered; import org.springframework.core.annotation.Order; import org.springframework.util.StopWatch; -import org.springframework.web.filter.GenericFilterBean; +import org.springframework.web.filter.OncePerRequestFilter; import org.springframework.web.util.UrlPathHelper; /** @@ -72,23 +70,20 @@ public class MetricFilterAutoConfiguration { * Filter that counts requests and measures processing times. */ @Order(Ordered.HIGHEST_PRECEDENCE) - private final class MetricsFilter extends GenericFilterBean { + private final class MetricsFilter extends OncePerRequestFilter { + /* + * (non-Javadoc) + * + * @see + * org.springframework.web.filter.OncePerRequestFilter#doFilterInternal(javax. + * servlet.http.HttpServletRequest, javax.servlet.http.HttpServletResponse, + * javax.servlet.FilterChain) + */ @Override - public void doFilter(ServletRequest request, ServletResponse response, - FilterChain chain) throws IOException, ServletException { - if ((request instanceof HttpServletRequest) - && (response instanceof HttpServletResponse)) { - doFilter((HttpServletRequest) request, (HttpServletResponse) response, - chain); - } - else { - chain.doFilter(request, response); - } - } - - public void doFilter(HttpServletRequest request, HttpServletResponse response, - FilterChain chain) throws IOException, ServletException { + protected void doFilterInternal(HttpServletRequest request, + HttpServletResponse response, FilterChain chain) throws ServletException, + IOException { UrlPathHelper helper = new UrlPathHelper(); String suffix = helper.getPathWithinApplication(request); StopWatch stopWatch = new StopWatch(); diff --git a/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/metrics/reader/CompositeMetricReader.java b/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/metrics/reader/CompositeMetricReader.java new file mode 100644 index 0000000000..df5340cdea --- /dev/null +++ b/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/metrics/reader/CompositeMetricReader.java @@ -0,0 +1,70 @@ +/* + * Copyright 2012-2013 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.boot.actuate.metrics.reader; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.List; + +import org.springframework.boot.actuate.metrics.Metric; + +/** + * @author Dave Syer + */ +public class CompositeMetricReader implements MetricReader { + + private List delegates = Collections.emptyList(); + + public void setDelegates(Collection delegates) { + this.delegates = new ArrayList(delegates); + } + + @Override + public Metric findOne(String metricName) { + for (MetricReader delegate : this.delegates) { + Metric value = delegate.findOne(metricName); + if (value != null) { + return value; + } + } + return null; + } + + @Override + public Iterable> findAll() { + List> values = new ArrayList>((int) count()); + for (MetricReader delegate : this.delegates) { + Iterable> all = delegate.findAll(); + for (Metric value : all) { + values.add(value); + } + } + return values; + } + + @Override + public long count() { + long count = 0; + for (MetricReader delegate : this.delegates) { + count += delegate.count(); + + } + return count; + } + +} diff --git a/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/metrics/repository/redis/RedisMetricRepository.java b/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/metrics/repository/redis/RedisMetricRepository.java index e819eaa172..300a891d4c 100644 --- a/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/metrics/repository/redis/RedisMetricRepository.java +++ b/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/metrics/repository/redis/RedisMetricRepository.java @@ -45,9 +45,9 @@ public class RedisMetricRepository implements MetricRepository { private String prefix = DEFAULT_METRICS_PREFIX; - private String keys = this.prefix + "keys"; + private String key = "keys." + DEFAULT_METRICS_PREFIX; - private final BoundZSetOperations zSetOperations; + private BoundZSetOperations zSetOperations; private final RedisOperations redisOperations; @@ -59,7 +59,7 @@ public class RedisMetricRepository implements MetricRepository { RedisTemplate longRedisTemplate = RedisUtils.createRedisTemplate( redisConnectionFactory, Long.class); this.longOperations = longRedisTemplate.opsForValue(); - this.zSetOperations = this.redisOperations.boundZSetOps(this.keys); + this.zSetOperations = this.redisOperations.boundZSetOps(this.key); } /** @@ -68,20 +68,30 @@ public class RedisMetricRepository implements MetricRepository { * @param prefix the prefix to set for all metrics keys */ public void setPrefix(String prefix) { + if (!prefix.endsWith(".")) { + prefix = prefix + "."; + } this.prefix = prefix; - this.keys = this.prefix + "keys"; + } + + /** + * The redis key to use to store the index of other keys. The redis store will hold a + * zset under this key. Defaults to "keys.spring.metrics". REad operations, especially + * {@link #findAll()} and {@link #count()}, will be much more efficient if the key is + * unique to the {@link #setPrefix(String) prefix} of this repository. + * + * @param key the key to set + */ + public void setKey(String key) { + this.key = key; + this.zSetOperations = this.redisOperations.boundZSetOps(this.key); } @Override public Metric findOne(String metricName) { String redisKey = keyFor(metricName); String raw = this.redisOperations.opsForValue().get(redisKey); - if (raw != null) { - return deserialize(redisKey, raw); - } - else { - return null; - } + return deserialize(redisKey, raw); } @Override @@ -94,7 +104,10 @@ public class RedisMetricRepository implements MetricRepository { List> result = new ArrayList>(keys.size()); List values = this.redisOperations.opsForValue().multiGet(keys); for (String v : values) { - result.add(deserialize(keysIt.next(), v)); + Metric value = deserialize(keysIt.next(), v); + if (value != null) { + result.add(value); + } } return result; @@ -131,6 +144,9 @@ public class RedisMetricRepository implements MetricRepository { } private Metric deserialize(String redisKey, String v) { + if (redisKey == null || v == null || !redisKey.startsWith(this.prefix)) { + return null; + } String[] vals = v.split("@"); Double value = Double.valueOf(vals[0]); Date timestamp = vals.length > 1 ? new Date(Long.valueOf(vals[1])) : new Date(); @@ -146,8 +162,6 @@ public class RedisMetricRepository implements MetricRepository { } private String nameFor(String redisKey) { - Assert.state(redisKey != null && redisKey.startsWith(this.prefix), - "Invalid key does not start with prefix: " + redisKey); return redisKey.substring(this.prefix.length()); } diff --git a/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/metrics/repository/redis/RedisMetricRepositoryTests.java b/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/metrics/repository/redis/RedisMetricRepositoryTests.java index 9f05be460c..d3ac2e25f0 100644 --- a/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/metrics/repository/redis/RedisMetricRepositoryTests.java +++ b/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/metrics/repository/redis/RedisMetricRepositoryTests.java @@ -37,20 +37,23 @@ public class RedisMetricRepositoryTests { @Rule public RedisServer redis = RedisServer.running(); private RedisMetricRepository repository; + private String prefix; @Before public void init() { this.repository = new RedisMetricRepository(this.redis.getResource()); + this.prefix = "spring.test." + System.currentTimeMillis(); + this.repository.setPrefix(this.prefix); } @After public void clear() { assertNotNull(new StringRedisTemplate(this.redis.getResource()).opsForValue() - .get("spring.metrics.foo")); + .get(this.prefix + ".foo")); this.repository.reset("foo"); this.repository.reset("bar"); assertNull(new StringRedisTemplate(this.redis.getResource()).opsForValue().get( - "spring.metrics.foo")); + this.prefix + ".foo")); } @Test diff --git a/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/redis/RedisAutoConfiguration.java b/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/redis/RedisAutoConfiguration.java index 247e71f57a..541d3ad4ec 100644 --- a/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/redis/RedisAutoConfiguration.java +++ b/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/redis/RedisAutoConfiguration.java @@ -25,7 +25,6 @@ import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingClass; import org.springframework.boot.context.properties.ConfigurationProperties; -import org.springframework.boot.context.properties.EnableConfigurationProperties; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.data.redis.connection.PoolConfig; @@ -112,8 +111,13 @@ public class RedisAutoConfiguration { } } + @Bean(name = "org.springframework.autoconfigure.redis.RedisProperties") + @ConditionalOnMissingBean + public RedisProperties redisProperties() { + return new RedisProperties(); + } + @Configuration - @EnableConfigurationProperties(RedisProperties.class) protected static class RedisConfiguration { @Autowired