Commit 31d6a0f1 authored by Phillip Webb's avatar Phillip Webb

Polish updated metrics code

parent d2f11c46
......@@ -30,6 +30,7 @@ import org.springframework.beans.factory.annotation.Qualifier;
* others that might be installed by the user for other purposes).
*
* @author Dave Syer
* @since 1.3.0
*/
@Qualifier
@Target({ ElementType.FIELD, ElementType.METHOD, ElementType.PARAMETER, ElementType.TYPE,
......
......@@ -30,6 +30,7 @@ import org.springframework.beans.factory.annotation.Qualifier;
* the {@link ExportMetricReader} readers.
*
* @author Dave Syer
* @since 1.3.0
*/
@Qualifier
@Target({ ElementType.FIELD, ElementType.METHOD, ElementType.PARAMETER, ElementType.TYPE,
......
......@@ -29,6 +29,7 @@ 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.writer.MetricWriter;
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;
......@@ -37,9 +38,13 @@ 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
* @since 1.3.0
*/
@Configuration
@EnableScheduling
......@@ -47,22 +52,39 @@ import org.springframework.scheduling.config.ScheduledTaskRegistrar;
@EnableConfigurationProperties
public class MetricExportAutoConfiguration {
@Autowired(required = false)
@ExportMetricWriter
private Map<String, MetricWriter> writers = Collections.emptyMap();
@Autowired
private MetricExportProperties metrics;
private MetricExportProperties properties;
@Autowired(required = false)
private MetricsEndpointMetricReader endpointReader;
@Autowired(required = false)
@ExportMetricReader
private List<MetricReader> readers;
@Autowired(required = false)
private MetricsEndpointMetricReader endpointReader;
@ExportMetricWriter
private Map<String, MetricWriter> writers = Collections.emptyMap();
@Bean
@ConditionalOnMissingBean(name = "metricWritersMetricExporter")
public SchedulingConfigurer metricWritersMetricExporter() {
Map<String, MetricWriter> writers = new HashMap<String, MetricWriter>();
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) {
writers.putAll(this.writers);
return new MetricExporters(reader, writers, this.properties);
}
return new NoOpSchedulingConfigurer();
}
@Configuration
protected static class MetricExportPropertiesConfiguration {
@Value("spring.metrics.${random.value:0000}.${spring.application.name:application}")
private String prefix = "spring.metrics";
......@@ -70,36 +92,18 @@ public class MetricExportAutoConfiguration {
@ConditionalOnMissingBean
public MetricExportProperties metricExportProperties() {
MetricExportProperties export = new MetricExportProperties();
export.getRedis().setPrefix(prefix);
export.getRedis().setPrefix(this.prefix);
return export;
}
}
@Bean
@ConditionalOnMissingBean(name = "metricWritersMetricExporter")
public SchedulingConfigurer metricWritersMetricExporter() {
Map<String, MetricWriter> writers = new HashMap<String, MetricWriter>();
}
MetricReader reader = this.endpointReader;
if (reader == null && this.readers != null && !this.readers.isEmpty()) {
reader = new CompositeMetricReader(
this.readers.toArray(new MetricReader[this.readers.size()]));
}
private static class NoOpSchedulingConfigurer implements SchedulingConfigurer {
if (reader != null) {
writers.putAll(this.writers);
MetricExporters exporters = new MetricExporters(reader, writers, this.metrics);
return exporters;
@Override
public void configureTasks(ScheduledTaskRegistrar taskRegistrar) {
}
return new SchedulingConfigurer() {
@Override
public void configureTasks(ScheduledTaskRegistrar taskRegistrar) {
}
};
}
}
......@@ -32,6 +32,7 @@ import org.springframework.messaging.MessageChannel;
* {@link MessageChannel}.
*
* @author Dave Syer
* @since 1.3.0
*/
@Configuration
@ConditionalOnClass(MessageChannel.class)
......@@ -46,4 +47,4 @@ public class MetricsChannelAutoConfiguration {
return new MessageChannelMetricWriter(channel);
}
}
\ No newline at end of file
}
......@@ -35,6 +35,7 @@ import com.codahale.metrics.MetricRegistry;
* {@link EnableAutoConfiguration Auto-configuration} for Dropwizard-based metrics.
*
* @author Dave Syer
* @since 1.3.0
*/
@Configuration
@ConditionalOnClass(MetricRegistry.class)
......@@ -60,4 +61,4 @@ public class MetricsDropwizardAutoConfiguration {
return new MetricReaderPublicMetrics(reader);
}
}
\ No newline at end of file
}
......@@ -80,7 +80,8 @@ public class PublicMetricsAutoConfiguration {
@Bean
public MetricReaderPublicMetrics metricReaderPublicMetrics() {
return new MetricReaderPublicMetrics(new CompositeMetricReader(this.metricReaders.toArray(new MetricReader[0])));
return new MetricReaderPublicMetrics(new CompositeMetricReader(
this.metricReaders.toArray(new MetricReader[0])));
}
@Bean
......
......@@ -77,7 +77,7 @@ public class MetricsEndpoint extends AbstractEndpoint<Map<String, Object>> {
result.put(metric.getName(), metric.getValue());
}
}
catch (Exception e) {
catch (Exception ex) {
// Could not evaluate metrics
}
}
......
......@@ -13,6 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.boot.actuate.endpoint;
import java.util.ArrayList;
......@@ -25,12 +26,12 @@ import org.springframework.boot.actuate.metrics.Metric;
import org.springframework.boot.actuate.metrics.reader.MetricReader;
/**
* A {@link MetricReader} that pulls all current values out of the {@link MetricsEndpoint}
* . No timestamp information is available, so there is no way to check if the values are
* {@link MetricReader} that pulls all current values out of the {@link MetricsEndpoint}.
* No timestamp information is available, so there is no way to check if the values are
* recent, and they all come out with the default (current time).
*
* @author Dave Syer
*
* @author Dave Syer
* @since 1.3.0
*/
public class MetricsEndpointMetricReader implements MetricReader {
......@@ -43,7 +44,7 @@ public class MetricsEndpointMetricReader implements MetricReader {
@Override
public Metric<?> findOne(String metricName) {
Metric<Number> metric = null;
Object value = endpoint.invoke().get(metricName);
Object value = this.endpoint.invoke().get(metricName);
if (value != null) {
metric = new Metric<Number>(metricName, (Number) value);
}
......@@ -53,7 +54,7 @@ public class MetricsEndpointMetricReader implements MetricReader {
@Override
public Iterable<Metric<?>> findAll() {
List<Metric<?>> metrics = new ArrayList<Metric<?>>();
Map<String, Object> values = endpoint.invoke();
Map<String, Object> values = this.endpoint.invoke();
Date timestamp = new Date();
for (Entry<String, Object> entry : values.entrySet()) {
String name = entry.getKey();
......@@ -65,7 +66,7 @@ public class MetricsEndpointMetricReader implements MetricReader {
@Override
public long count() {
return endpoint.invoke().size();
return this.endpoint.invoke().size();
}
}
/*
* Copyright 2014-2015 the original author or authors.
* 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.
......@@ -13,6 +13,7 @@
* 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;
......@@ -35,7 +36,7 @@ import org.springframework.util.StringUtils;
* value.
*
* @author Dave Syer
*
* @since 1.3.0
*/
public class AggregateMetricReader implements MetricReader {
......@@ -52,7 +53,6 @@ public class AggregateMetricReader implements MetricReader {
/**
* The number of period-separated keys to remove from the start of the input metric
* names before aggregating.
*
* @param truncate length of source metric prefixes
*/
public void setTruncateKeyLength(int truncate) {
......@@ -62,7 +62,6 @@ public class AggregateMetricReader implements MetricReader {
/**
* Prefix to apply to all output metrics. A period will be appended if no present in
* the provided value.
*
* @param prefix the prefix to use default "aggregator.")
*/
public void setPrefix(String prefix) {
......
/*
* Copyright 2012-2014 the original author or authors.
* 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.
......@@ -25,6 +25,7 @@ import org.springframework.lang.UsesJava8;
* Fast implementation of {@link CounterService} using {@link CounterBuffers}.
*
* @author Dave Syer
* @since 1.3.0
*/
@UsesJava8
public class BufferCounterService implements CounterService {
......
/*
* Copyright 2012-2014 the original author or authors.
* 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.
......@@ -25,6 +25,7 @@ import org.springframework.lang.UsesJava8;
* Fast implementation of {@link GaugeService} using {@link GaugeBuffers}.
*
* @author Dave Syer
* @since 1.3.0
*/
@UsesJava8
public class BufferGaugeService implements GaugeService {
......
......@@ -33,6 +33,7 @@ import org.springframework.lang.UsesJava8;
* {@link GaugeBuffers}.
*
* @author Dave Syer
* @since 1.3.0
*/
@UsesJava8
public class BufferMetricReader implements MetricReader, PrefixMetricReader {
......
......@@ -28,6 +28,7 @@ import org.springframework.lang.UsesJava8;
* Fast writes to in-memory metrics store using {@link LongBuffer}.
*
* @author Dave Syer
* @since 1.3.0
*/
@UsesJava8
public class CounterBuffers {
......@@ -113,4 +114,5 @@ public class CounterBuffers {
}
consumer.accept(adder);
}
}
......@@ -20,6 +20,7 @@ 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 DoubleBuffer {
......@@ -47,4 +48,5 @@ public class DoubleBuffer {
public long getTimestamp() {
return this.timestamp;
}
}
\ No newline at end of file
}
......@@ -28,6 +28,7 @@ import org.springframework.lang.UsesJava8;
* Fast writes to in-memory metrics store using {@link DoubleBuffer}.
*
* @author Dave Syer
* @since 1.3.0
*/
@UsesJava8
public class GaugeBuffers {
......@@ -94,4 +95,5 @@ public class GaugeBuffers {
}
consumer.accept(value);
}
}
......@@ -24,6 +24,7 @@ import org.springframework.lang.UsesJava8;
* Mutable buffer containing a long adder (Java 8) and a timestamp.
*
* @author Dave Syer
* @since 1.3.0
*/
@UsesJava8
public class LongBuffer {
......@@ -56,4 +57,5 @@ public class LongBuffer {
public void add(long delta) {
this.adder.add(delta);
}
}
\ No newline at end of file
}
/*
* Copyright 2012-2014 the original author or authors.
* 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.
......@@ -33,26 +33,27 @@ import org.springframework.util.StringUtils;
* export).
*
* @author Dave Syer
* @since 1.3.0
*/
public abstract class AbstractMetricExporter implements Exporter {
private static final Log logger = LogFactory.getLog(AbstractMetricExporter.class);
private volatile AtomicBoolean processing = new AtomicBoolean(false);
private final String prefix;
private Date earliestTimestamp = new Date();
private boolean ignoreTimestamps = false;
private final String prefix;
private boolean sendLatest = true;
private Date latestTimestamp = new Date(0L);
private volatile AtomicBoolean processing = new AtomicBoolean(false);
private boolean sendLatest = true;
private Date latestTimestamp = new Date(0L);
public AbstractMetricExporter(String prefix) {
this.prefix = !StringUtils.hasText(prefix) ? "" : (prefix.endsWith(".") ? prefix
: prefix + ".");
this.prefix = (!StringUtils.hasText(prefix) ? "" : (prefix.endsWith(".") ? prefix
: prefix + "."));
}
/**
......@@ -73,7 +74,6 @@ public abstract class AbstractMetricExporter implements Exporter {
/**
* Send only the data that changed since the last export.
*
* @param sendLatest the flag to set
*/
public void setSendLatest(boolean sendLatest) {
......@@ -82,47 +82,63 @@ public abstract class AbstractMetricExporter implements Exporter {
@Override
public void export() {
if (!this.processing.compareAndSet(false, true)) {
// skip a tick
return;
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);
}
}
long latestTimestamp = 0;
try {
latestTimestamp = System.currentTimeMillis();
for (String group : groups()) {
Collection<Metric<?>> values = new ArrayList<Metric<?>>();
for (Metric<?> metric : next(group)) {
Metric<?> value = new Metric<Number>(this.prefix + metric.getName(),
metric.getValue(), metric.getTimestamp());
Date timestamp = metric.getTimestamp();
if (!this.ignoreTimestamps && this.earliestTimestamp.after(timestamp)) {
continue;
}
if (!this.ignoreTimestamps && this.sendLatest
&& this.latestTimestamp.after(timestamp)) {
continue;
}
values.add(value);
}
if (!values.isEmpty()) {
write(group, values);
}
private void exportGroups() {
for (String group : groups()) {
Collection<Metric<?>> values = new ArrayList<Metric<?>>();
for (Metric<?> metric : next(group)) {
Date timestamp = metric.getTimestamp();
if (canExportTimestamp(timestamp)) {
values.add(getPrefixedMetric(metric));
}
}
if (!values.isEmpty()) {
write(group, values);
}
}
catch (Exception e) {
logger.warn("Could not write to MetricWriter: " + e.getClass() + ": "
+ e.getMessage());
}
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;
}
finally {
try {
this.latestTimestamp = new Date(latestTimestamp);
flush();
}
catch (Exception e) {
logger.warn("Could not flush MetricWriter: " + e.getClass() + ": "
+ e.getMessage());
}
this.processing.set(false);
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());
}
}
......
/*
* Copyright 2012-2013 the original author or authors.
* 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.
......@@ -25,6 +25,7 @@ package org.springframework.boot.actuate.metrics.export;
* it using a {@code @Scheduled} annotation in a Spring ApplicationContext.
*
* @author Dave Syer
* @since 1.3.0
*/
public interface Exporter {
......
......@@ -16,23 +16,33 @@
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 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.MetricWriter;
import org.springframework.boot.actuate.metrics.writer.WriterUtils;
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}.
*
* @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 MetricWriter writer;
......@@ -41,41 +51,53 @@ public class MetricCopyExporter extends AbstractMetricExporter {
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, MetricWriter 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, MetricWriter writer, String prefix) {
super(prefix);
this.reader = reader;
this.writer = writer;
}
/**
* 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;
}
}
public MetricCopyExporter(MetricReader reader, MetricWriter writer, String prefix) {
super(prefix);
this.reader = reader;
this.writer = writer;
}
@Override
protected Iterable<Metric<?>> next(String group) {
if ((this.includes == null || this.includes.length == 0)
&& (this.excludes == null || this.excludes.length == 0)) {
if (ObjectUtils.isEmpty(this.includes) && ObjectUtils.isEmpty(this.excludes)) {
return this.reader.findAll();
}
return new Iterable<Metric<?>>() {
@Override
public Iterator<Metric<?>> iterator() {
return new PatternMatchingIterator(MetricCopyExporter.this.reader
.findAll().iterator());
}
};
return new PatternMatchingIterable(MetricCopyExporter.this.reader);
}
@Override
......@@ -87,12 +109,52 @@ public class MetricCopyExporter extends AbstractMetricExporter {
@Override
public void flush() {
WriterUtils.flush(this.writer);
flush(this.writer);
}
private void flush(MetricWriter 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;
public 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;
public PatternMatchingIterator(Iterator<Metric<?>> iterator) {
......@@ -109,33 +171,24 @@ public class MetricCopyExporter extends AbstractMetricExporter {
}
private Metric<?> findNext() {
Metric<?> metric = null;
boolean matched = false;
while (this.iterator.hasNext() && !matched) {
metric = this.iterator.next();
if (MetricCopyExporter.this.includes == null
|| MetricCopyExporter.this.includes.length == 0) {
matched = true;
}
else {
for (String pattern : MetricCopyExporter.this.includes) {
if (PatternMatchUtils.simpleMatch(pattern, metric.getName())) {
matched = true;
break;
}
}
}
if (MetricCopyExporter.this.excludes != null) {
for (String pattern : MetricCopyExporter.this.excludes) {
if (PatternMatchUtils.simpleMatch(pattern, metric.getName())) {
matched = false;
break;
}
}
while (this.iterator.hasNext()) {
Metric<?> metric = this.iterator.next();
if (isMatch(metric)) {
return metric;
}
}
return matched ? metric : null;
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
......@@ -144,5 +197,6 @@ public class MetricCopyExporter extends AbstractMetricExporter {
this.buffer = null;
return metric;
}
};
}
......@@ -27,55 +27,29 @@ import org.springframework.util.PatternMatchUtils;
import org.springframework.util.StringUtils;
/**
* Configuration properties for metrics export.
*
* @author Dave Syer
* @since 1.3.0
*/
@ConfigurationProperties("spring.metrics.export")
public class MetricExportProperties extends Trigger {
public class MetricExportProperties extends TriggerProperties {
/**
* Flag to disable all metric exports (assuming any MetricWriters are available).
*/
private boolean enabled = true;
private Map<String, SpecificTrigger> triggers = new LinkedHashMap<String, SpecificTrigger>();
private Map<String, SpecificTriggerProperties> triggers = new LinkedHashMap<String, SpecificTriggerProperties>();
private Redis redis = new Redis();
/**
* Default values for trigger configuration for all writers. Can also be set by
* including a writer with <code>name="*"</code>.
*
* @return the default trigger configuration
*/
public Trigger getDefault() {
return this;
}
/**
* 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, SpecificTrigger> getTriggers() {
return this.triggers;
}
public Redis getRedis() {
return redis;
}
public void setRedis(Redis redis) {
this.redis = redis;
}
@PostConstruct
public void setUpDefaults() {
Trigger defaults = this;
for (Entry<String, SpecificTrigger> entry : this.triggers.entrySet()) {
TriggerProperties defaults = this;
for (Entry<String, SpecificTriggerProperties> entry : this.triggers.entrySet()) {
String key = entry.getKey();
SpecificTrigger value = entry.getValue();
SpecificTriggerProperties value = entry.getValue();
if (value.getNames() == null || value.getNames().length == 0) {
value.setNames(new String[] { key });
}
......@@ -86,7 +60,7 @@ public class MetricExportProperties extends Trigger {
if (defaults.getDelayMillis() == null) {
defaults.setDelayMillis(5000);
}
for (Trigger value : this.triggers.values()) {
for (TriggerProperties value : this.triggers.values()) {
if (value.isSendLatest() == null) {
value.setSendLatest(defaults.isSendLatest());
}
......@@ -96,47 +70,56 @@ public class MetricExportProperties extends Trigger {
}
}
@Override
public boolean isEnabled() {
return this.enabled;
}
@Override
public void setEnabled(boolean enabled) {
this.enabled = enabled;
}
/**
* 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 Redis getRedis() {
return this.redis;
}
public void setRedis(Redis redis) {
this.redis = redis;
}
/**
* Default values for trigger configuration for all writers. Can also be set by
* including a writer with {@code name="*"}.
* @return the default trigger configuration
*/
public TriggerProperties getDefault() {
return this;
}
/**
* Find a matching trigger configuration.
* @param name the bean name to match
* @return a matching configuration if there is one
*/
public Trigger findTrigger(String name) {
for (SpecificTrigger value : this.triggers.values()) {
public TriggerProperties findTrigger(String name) {
for (SpecificTriggerProperties value : this.triggers.values()) {
if (PatternMatchUtils.simpleMatch(value.getNames(), name)) {
return value;
}
}
return this;
}
/**
* Trigger for specific bean names.
*/
public static class SpecificTrigger extends Trigger {
/**
* 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;
}
}
public static class Redis {
......@@ -155,9 +138,9 @@ public class MetricExportProperties extends Trigger {
* system sharing a redis repository.
*/
private String key = "keys.spring.metrics";
public String getPrefix() {
return prefix;
return this.prefix;
}
public void setPrefix(String prefix) {
......@@ -165,7 +148,7 @@ public class MetricExportProperties extends Trigger {
}
public String getKey() {
return key;
return this.key;
}
public void setKey(String key) {
......@@ -187,74 +170,3 @@ public class MetricExportProperties extends Trigger {
}
}
class Trigger {
/**
* 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;
}
}
......@@ -27,58 +27,68 @@ 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 {
private MetricExportProperties export;
private final MetricReader reader;
private Map<String, MetricWriter> writers;
private final Map<String, MetricWriter> writers;
private Map<String, Exporter> exporters = new HashMap<String, Exporter>();
private final MetricExportProperties properties;
private MetricReader reader;
private final Map<String, Exporter> exporters = new HashMap<String, Exporter>();
public MetricExporters(MetricReader reader, Map<String, MetricWriter> writers,
MetricExportProperties export) {
MetricExportProperties properties) {
this.reader = reader;
this.export = export;
this.writers = writers;
}
public Map<String, Exporter> getExporters() {
return this.exporters;
this.properties = properties;
}
@Override
public void configureTasks(ScheduledTaskRegistrar taskRegistrar) {
for (Entry<String, MetricWriter> entry : this.writers.entrySet()) {
String name = entry.getKey();
Trigger trigger = this.export.findTrigger(name);
MetricWriter writer = entry.getValue();
TriggerProperties trigger = this.properties.findTrigger(name);
if (trigger != null) {
MetricCopyExporter exporter = getExporter(writer, trigger);
this.exporters.put(name, exporter);
ExportRunner runner = new ExportRunner(exporter);
IntervalTask task = new IntervalTask(runner, trigger.getDelayMillis(),
trigger.getDelayMillis());
taskRegistrar.addFixedDelayTask(task);
}
}
}
MetricWriter writer = entry.getValue();
final MetricCopyExporter exporter = new MetricCopyExporter(this.reader,
writer);
if (trigger.getIncludes() != null || trigger.getExcludes() != null) {
exporter.setIncludes(trigger.getIncludes());
exporter.setExcludes(trigger.getExcludes());
}
exporter.setSendLatest(trigger.isSendLatest());
private MetricCopyExporter getExporter(MetricWriter writer, TriggerProperties trigger) {
MetricCopyExporter exporter = new MetricCopyExporter(this.reader, writer);
exporter.setIncludes(trigger.getIncludes());
exporter.setExcludes(trigger.getExcludes());
exporter.setSendLatest(trigger.isSendLatest());
return exporter;
}
this.exporters.put(name, exporter);
public Map<String, Exporter> getExporters() {
return this.exporters;
}
taskRegistrar.addFixedDelayTask(new IntervalTask(new Runnable() {
@Override
public void run() {
exporter.export();
}
}, trigger.getDelayMillis(), trigger.getDelayMillis()));
private static class ExportRunner implements Runnable {
}
private final MetricCopyExporter exporter;
public ExportRunner(MetricCopyExporter exporter) {
this.exporter = exporter;
}
@Override
public void run() {
this.exporter.export();
}
}
......
/*
* Copyright 2012-2014 the original author or authors.
* 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.
......@@ -30,6 +30,7 @@ import org.springframework.boot.actuate.metrics.writer.PrefixMetricWriter;
* 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 {
......@@ -64,6 +65,7 @@ public class PrefixMetricGroupExporter extends AbstractMetricExporter {
}
/**
* The groups to export.
* @param groups the groups to set
*/
public void setGroups(Set<String> groups) {
......
......@@ -30,14 +30,14 @@ 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</code> is stored as
* <code>[foo.min, foo.max. foo.val, foo.count, foo.avg, foo.alpha]</code>. If 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 {
......@@ -54,6 +54,7 @@ public class RichGaugeExporter extends AbstractMetricExporter {
private static final String ALPHA = ".alpha";
private final RichGaugeReader reader;
private final PrefixMetricWriter writer;
public RichGaugeExporter(RichGaugeReader reader, PrefixMetricWriter writer) {
......
......@@ -14,46 +14,27 @@
* limitations under the License.
*/
package org.springframework.boot.actuate.metrics.writer;
import java.io.Flushable;
import java.lang.reflect.Method;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.boot.actuate.metrics.export.MetricCopyExporter;
import org.springframework.util.ClassUtils;
import org.springframework.util.ReflectionUtils;
package org.springframework.boot.actuate.metrics.export;
/**
* Trigger for specific names or patterns.
*
* @author Dave Syer
* @since 1.3.0
*/
public class WriterUtils {
public class SpecificTriggerProperties extends TriggerProperties {
private static final Log logger = LogFactory.getLog(MetricCopyExporter.class);
/**
* Names (or patterns) for bean names that this configuration applies to.
*/
private String[] names;
public String[] getNames() {
return this.names;
}
public static void flush(MetricWriter writer) {
if (writer instanceof CompositeMetricWriter) {
for (MetricWriter element : (CompositeMetricWriter) writer) {
flush(element);
}
}
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 e) {
logger.warn("Could not flush MetricWriter: " + e.getClass() + ": "
+ e.getMessage());
}
public void setNames(String[] names) {
this.names = names;
}
}
/*
* 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;
}
}
/*
* Copyright 2015 the original author or authors.
* 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.
......@@ -13,6 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.boot.actuate.metrics.integration;
import java.util.ArrayList;
......@@ -26,14 +27,15 @@ import org.springframework.integration.support.management.Statistics;
import org.springframework.lang.UsesJava7;
/**
* A {@link MetricReader} for Spring Integration metrics (as provided by spring-integration-jmx).
*
* @author Dave Syer
* A {@link MetricReader} for Spring Integration metrics (as provided by
* spring-integration-jmx).
*
* @author Dave Syer
* @since 1.3.0
*/
@UsesJava7
public class SpringIntegrationMetricReader implements MetricReader {
private final IntegrationMBeanExporter exporter;
public SpringIntegrationMetricReader(IntegrationMBeanExporter exporter) {
......@@ -47,23 +49,34 @@ public class SpringIntegrationMetricReader implements MetricReader {
@Override
public Iterable<Metric<?>> findAll() {
IntegrationMBeanExporter exporter = this.exporter;
List<Metric<?>> metrics = new ArrayList<Metric<?>>();
for (String name : exporter.getChannelNames()) {
metrics.addAll(getStatistics("integration.channel." + name + ".errorRate", exporter.getChannelErrorRate(name)));
metrics.addAll(getStatistics("integration.channel." + name + ".sendRate", exporter.getChannelSendRate(name)));
metrics.add(new Metric<Long>("integration.channel." + name + ".receiveCount", exporter.getChannelReceiveCountLong(name)));
String prefix = "integration.channel." + name;
metrics.addAll(getStatistics(prefix + ".errorRate",
exporter.getChannelErrorRate(name)));
metrics.addAll(getStatistics(prefix + ".sendRate",
exporter.getChannelSendRate(name)));
metrics.add(new Metric<Long>(prefix + ".receiveCount", exporter
.getChannelReceiveCountLong(name)));
}
for (String name : exporter.getHandlerNames()) {
metrics.addAll(getStatistics("integration.handler." + name + ".duration", exporter.getHandlerDuration(name)));
metrics.addAll(getStatistics("integration.handler." + name + ".duration",
exporter.getHandlerDuration(name)));
}
metrics.add(new Metric<Long>("integration.activeHandlerCount", exporter.getActiveHandlerCountLong()));
metrics.add(new Metric<Integer>("integration.handlerCount", exporter.getHandlerCount()));
metrics.add(new Metric<Integer>("integration.channelCount", exporter.getChannelCount()));
metrics.add(new Metric<Integer>("integration.queuedMessageCount", exporter.getQueuedMessageCount()));
return metrics;
metrics.add(new Metric<Long>("integration.activeHandlerCount", exporter
.getActiveHandlerCountLong()));
metrics.add(new Metric<Integer>("integration.handlerCount", exporter
.getHandlerCount()));
metrics.add(new Metric<Integer>("integration.channelCount", exporter
.getChannelCount()));
metrics.add(new Metric<Integer>("integration.queuedMessageCount", exporter
.getQueuedMessageCount()));
return metrics;
}
private Collection<? extends Metric<?>> getStatistics(String name, Statistics statistic) {
private Collection<? extends Metric<?>> getStatistics(String name,
Statistics statistic) {
List<Metric<?>> metrics = new ArrayList<Metric<?>>();
metrics.add(new Metric<Double>(name + ".mean", statistic.getMean()));
metrics.add(new Metric<Double>(name + ".max", statistic.getMax()));
......@@ -75,7 +88,9 @@ public class SpringIntegrationMetricReader implements MetricReader {
@Override
public long count() {
return exporter.getChannelCount()*11 + exporter.getHandlerCount()*5 + 4;
int totalChannelCount = this.exporter.getChannelCount() * 11;
int totalHandlerCount = this.exporter.getHandlerCount() * 5;
return totalChannelCount + totalHandlerCount + 4;
}
}
......@@ -33,6 +33,7 @@ import org.springframework.util.StringUtils;
* domain is copied from the input key and the type in the input key is discarded.
*
* @author Dave Syer
* @since 1.3.0
*/
public class DefaultMetricNamingStrategy implements ObjectNamingStrategy {
......
......@@ -21,6 +21,7 @@ import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import javax.management.MalformedObjectNameException;
import javax.management.ObjectName;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
......@@ -41,6 +42,7 @@ import org.springframework.jmx.export.naming.ObjectNamingStrategy;
* and <code>value</code> keys by splitting up the metric name on periods.
*
* @author Dave Syer
* @since 1.3.0
*/
@ManagedResource(description = "MetricWriter for pushing metrics to JMX MBeans.")
public class JmxMetricWriter implements MetricWriter {
......@@ -97,10 +99,9 @@ public class JmxMetricWriter implements MetricWriter {
try {
// We can unregister the MBean, but if this writer is on the end of an
// Exporter the chances are it will be re-registered almost immediately.
this.exporter.unregisterManagedResource(this.namingStrategy
.getObjectName(value, getKey(name)));
this.exporter.unregisterManagedResource(getName(name, value));
}
catch (MalformedObjectNameException e) {
catch (MalformedObjectNameException ex) {
logger.warn("Could not unregister MBean for " + name);
}
}
......@@ -111,18 +112,19 @@ public class JmxMetricWriter implements MetricWriter {
this.values.putIfAbsent(name, new MetricValue());
MetricValue value = this.values.get(name);
try {
this.exporter.registerManagedResource(value,
this.namingStrategy.getObjectName(value, getKey(name)));
this.exporter.registerManagedResource(value, getName(name, value));
}
catch (Exception e) {
catch (Exception ex) {
// Could not register mbean, maybe just a race condition
}
}
return this.values.get(name);
}
private String getKey(String name) {
return String.format(this.domain + ":type=MetricValue,name=%s", name);
private ObjectName getName(String name, MetricValue value)
throws MalformedObjectNameException {
String key = String.format(this.domain + ":type=MetricValue,name=%s", name);
return this.namingStrategy.getObjectName(value, key);
}
@ManagedResource
......
......@@ -34,6 +34,7 @@ import org.springframework.util.ObjectUtils;
* (overwriting the default).
*
* @author Dave Syer
* @since 1.3.0
*/
public class DefaultOpenTsdbNamingStrategy implements OpenTsdbNamingStrategy {
......
......@@ -19,7 +19,10 @@ package org.springframework.boot.actuate.metrics.opentsdb;
import java.util.Map;
/**
* OpenTSDB Data.
*
* @author Dave Syer
* @since 1.3.0
*/
public class OpenTsdbData {
......
......@@ -43,6 +43,7 @@ import org.springframework.web.client.RestTemplate;
* <code>@Scheduled</code>} task to flush periodically.
*
* @author Dave Syer
* @since 1.3.0
*/
public class OpenTsdbMetricWriter implements MetricWriter {
......@@ -141,4 +142,4 @@ public class OpenTsdbMetricWriter implements MetricWriter {
set(new Metric<Long>(metricName, 0L));
}
}
\ No newline at end of file
}
......@@ -20,7 +20,10 @@ import java.util.LinkedHashMap;
import java.util.Map;
/**
* OpenTSDB Name.
*
* @author Dave Syer
* @since 1.3.0
*/
public class OpenTsdbName {
......
......@@ -17,10 +17,18 @@
package org.springframework.boot.actuate.metrics.opentsdb;
/**
* Strategy used to convert a metric name into an {@link OpenTsdbName}.
*
* @author Dave Syer
* @since 1.3.0
*/
public interface OpenTsdbNamingStrategy {
/**
* Convert the metric name into a {@link OpenTsdbName}
* @param metricName the name of the metric
* @return an Open TSDB name
*/
OpenTsdbName getName(String metricName);
}
......@@ -37,16 +37,16 @@ import com.timgroup.statsd.StatsDClientErrorHandler;
* a gauge.
*
* @author Dave Syer
* @since 1.3.0
*/
public class StatsdMetricWriter implements MetricWriter, Closeable {
private static Log logger = LogFactory.getLog(StatsdMetricWriter.class);
private static final Log logger = LogFactory.getLog(StatsdMetricWriter.class);
private final NonBlockingStatsDClient client;
/**
* Create a new writer with the given parameters.
*
* Create a new writer instance with the given parameters.
* @param host the hostname for the statsd server
* @param port the port for the statsd server
*/
......@@ -56,7 +56,6 @@ public class StatsdMetricWriter implements MetricWriter, Closeable {
/**
* Create a new writer with the given parameters.
*
* @param prefix the prefix to apply to all metric names (can be null)
* @param host the hostname for the statsd server
* @param port the port for the statsd server
......@@ -101,11 +100,13 @@ public class StatsdMetricWriter implements MetricWriter, Closeable {
private static final class LoggingStatsdErrorHandler implements
StatsDClientErrorHandler {
@Override
public void handle(Exception e) {
logger.debug("Failed to write metric. Exception: " + e.getClass()
+ ", message: " + e.getMessage());
}
}
}
......@@ -65,4 +65,5 @@ public class DefaultCounterService implements CounterService {
this.names.put(metricName, name);
return name;
}
}
......@@ -57,4 +57,5 @@ public class DefaultGaugeService implements GaugeService {
this.names.put(metricName, name);
return name;
}
}
......@@ -16,8 +16,6 @@
package org.springframework.boot.actuate.autoconfigure;
import static org.junit.Assert.assertNotNull;
import org.junit.After;
import org.junit.Test;
import org.mockito.Matchers;
......@@ -38,8 +36,10 @@ import org.springframework.messaging.MessageHandler;
import org.springframework.messaging.MessagingException;
import org.springframework.messaging.SubscribableChannel;
import static org.junit.Assert.assertNotNull;
/**
* Tests for {@link MetricRepositoryAutoConfiguration}.
* Tests for {@link MetricExportAutoConfiguration}.
*
* @author Phillip Webb
* @author Dave Syer
......@@ -88,15 +88,15 @@ public class MetricExportAutoConfigurationTests {
@Test
public void exportMetricsEndpoint() {
this.context = new AnnotationConfigApplicationContext(WriterConfig.class,
MetricEndpointConfiguration.class,
MetricExportAutoConfiguration.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);
MetricsEndpointMetricReader reader = this.context.getBean("endpointReader",
MetricsEndpointMetricReader.class);
Mockito.verify(reader, Mockito.atLeastOnce()).findAll();
}
......
......@@ -16,12 +16,6 @@
package org.springframework.boot.actuate.autoconfigure;
import static org.hamcrest.Matchers.equalTo;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertThat;
import static org.mockito.Mockito.mock;
import org.junit.After;
import org.junit.Test;
import org.springframework.boot.actuate.metrics.CounterService;
......@@ -39,6 +33,12 @@ import org.springframework.context.annotation.Configuration;
import com.codahale.metrics.Gauge;
import com.codahale.metrics.MetricRegistry;
import static org.hamcrest.Matchers.equalTo;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertThat;
import static org.mockito.Mockito.mock;
/**
* Tests for {@link MetricRepositoryAutoConfiguration}.
*
......
......@@ -27,6 +27,8 @@ import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNull;
/**
* Tests for {@link AggregateMetricReader}.
*
* @author Dave Syer
*/
public class AggregateMetricReaderTests {
......
......@@ -61,4 +61,5 @@ public class CounterBuffersTests {
public void findNonExistent() {
assertNull(this.buffers.find("foo"));
}
}
......@@ -44,6 +44,7 @@ import static org.junit.Assert.assertEquals;
/**
* Speed tests for {@link CounterService}.
*
* @author Dave Syer
*/
@RunWith(Theories.class)
......@@ -162,4 +163,5 @@ public class CounterServiceSpeedTests {
}
watch.stop();
}
}
......@@ -126,4 +126,5 @@ public class DefaultGaugeServiceSpeedTests {
System.err.println("Read(" + count + ")=" + watch.getLastTaskTimeMillis() + "ms");
assertTrue(0 < total.longValue());
}
}
......@@ -20,7 +20,6 @@ import java.util.ArrayList;
import java.util.List;
import org.junit.Test;
import org.springframework.boot.actuate.metrics.dropwizard.DropwizardMetricServices;
import com.codahale.metrics.Gauge;
import com.codahale.metrics.MetricRegistry;
......@@ -114,13 +113,15 @@ public class DropwizardMetricServicesTests {
}
public static class WriterThread extends Thread {
private int index;
private boolean failed;
private DropwizardMetricServices writer;
public WriterThread(ThreadGroup group, int index, DropwizardMetricServices writer) {
super(group, "Writer-" + index);
this.index = index;
this.writer = writer;
}
......@@ -143,5 +144,7 @@ public class DropwizardMetricServicesTests {
}
}
}
}
}
......@@ -25,12 +25,16 @@ import org.springframework.boot.actuate.metrics.repository.InMemoryMetricReposit
import static org.junit.Assert.assertEquals;
/**
* Tests for {@link MetricCopyExporter}.
*
* @author Dave Syer
*/
public class MetricCopyExporterTests {
private final InMemoryMetricRepository writer = new InMemoryMetricRepository();
private final InMemoryMetricRepository reader = new InMemoryMetricRepository();
private final MetricCopyExporter exporter = new MetricCopyExporter(this.reader,
this.writer);
......
......@@ -29,14 +29,20 @@ import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
/**
* Tests for {@link MetricExporters}.
*
* @author Dave Syer
*/
public class MetricExportersTests {
private MetricExporters exporters;
private MetricExportProperties export = new MetricExportProperties();
private Map<String, MetricWriter> writers = new LinkedHashMap<String, MetricWriter>();
private MetricReader reader = Mockito.mock(MetricReader.class);
private MetricWriter writer = Mockito.mock(MetricWriter.class);
@Test
......
/*
* Copyright 2012-2014 the original author or authors.
* 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.
......
/*
* Copyright 2012-2013 the original author or authors.
* 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.
......@@ -25,12 +25,16 @@ import org.springframework.boot.actuate.metrics.rich.InMemoryRichGaugeRepository
import static org.junit.Assert.assertEquals;
/**
* Tests for {@link RichGaugeExporter}.
*
* @author Dave Syer
*/
public class RichGaugeExporterTests {
private final InMemoryRichGaugeRepository reader = new InMemoryRichGaugeRepository();
private final InMemoryMetricRepository writer = new InMemoryMetricRepository();
private final RichGaugeExporter exporter = new RichGaugeExporter(this.reader,
this.writer);
......
package org.springframework.boot.actuate.metrics.integration;
/*
* 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.
*/
import static org.junit.Assert.assertTrue;
package org.springframework.boot.actuate.metrics.integration;
import org.junit.Test;
import org.junit.runner.RunWith;
......@@ -17,22 +31,29 @@ import org.springframework.integration.monitor.IntegrationMBeanExporter;
import org.springframework.test.annotation.DirtiesContext;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import static org.junit.Assert.assertTrue;
/**
* Tests for {@link SpringIntegrationMetricReader}.
*
* @author Dave Syer
*/
@RunWith(SpringJUnit4ClassRunner.class)
@SpringApplicationConfiguration(classes=TestConfiguration.class)
@SpringApplicationConfiguration(classes = TestConfiguration.class)
@IntegrationTest("spring.jmx.enabled=true")
@DirtiesContext
public class SpringIntegrationMetricReaderTests {
@Autowired
private SpringIntegrationMetricReader reader;
@Test
public void test() {
assertTrue(reader.count()>0);
assertTrue(this.reader.count() > 0);
}
@Configuration
@Import({JmxAutoConfiguration.class, IntegrationAutoConfiguration.class})
@Import({ JmxAutoConfiguration.class, IntegrationAutoConfiguration.class })
protected static class TestConfiguration {
@Bean
public SpringIntegrationMetricReader reader(IntegrationMBeanExporter exporter) {
......
......@@ -23,6 +23,8 @@ import org.junit.Test;
import static org.junit.Assert.assertEquals;
/**
* Tests for {@link DefaultMetricNamingStrategy}.
*
* @author Dave Syer
*/
public class DefaultMetricNamingStrategyTests {
......
......@@ -21,7 +21,6 @@ import java.util.Map;
import org.junit.Before;
import org.junit.Test;
import org.mockito.Matchers;
import org.mockito.Mockito;
import org.springframework.boot.actuate.metrics.Metric;
import org.springframework.http.HttpStatus;
......@@ -29,9 +28,13 @@ import org.springframework.http.ResponseEntity;
import org.springframework.web.client.RestOperations;
import static org.mockito.BDDMockito.given;
import static org.mockito.Matchers.any;
import static org.mockito.Matchers.anyString;
import static org.mockito.Mockito.verify;
/**
* Tests for {@link OpenTsdbMetricWriter}.
*
* @author Dave Syer
*/
public class OpenTsdbMetricWriterTests {
......@@ -48,25 +51,19 @@ public class OpenTsdbMetricWriterTests {
@Test
public void postSuccessfullyOnFlush() {
this.writer.set(new Metric<Double>("foo", 2.4));
given(
this.restTemplate.postForEntity(Matchers.anyString(),
Matchers.any(Object.class), anyMap()))
given(this.restTemplate.postForEntity(anyString(), any(Object.class), anyMap()))
.willReturn(emptyResponse());
this.writer.flush();
verify(this.restTemplate).postForEntity(Matchers.anyString(),
Matchers.any(Object.class), anyMap());
verify(this.restTemplate).postForEntity(anyString(), any(Object.class), anyMap());
}
@Test
public void flushAutomaticlly() {
given(
this.restTemplate.postForEntity(Matchers.anyString(),
Matchers.any(Object.class), anyMap()))
given(this.restTemplate.postForEntity(anyString(), any(Object.class), anyMap()))
.willReturn(emptyResponse());
this.writer.setBufferSize(0);
this.writer.set(new Metric<Double>("foo", 2.4));
verify(this.restTemplate).postForEntity(Matchers.anyString(),
Matchers.any(Object.class), anyMap());
verify(this.restTemplate).postForEntity(anyString(), any(Object.class), anyMap());
}
@SuppressWarnings("rawtypes")
......@@ -76,7 +73,7 @@ public class OpenTsdbMetricWriterTests {
@SuppressWarnings({ "rawtypes", "unchecked" })
private Class<Map> anyMap() {
return Matchers.any(Class.class);
return any(Class.class);
}
}
......@@ -99,6 +99,7 @@ public class StatsdMetricWriterTests {
private static final class DummyStatsDServer {
private final List<String> messagesReceived = new ArrayList<String>();
private final DatagramSocket server;
public DummyStatsDServer(int port) {
......@@ -141,5 +142,7 @@ public class StatsdMetricWriterTests {
public List<String> messagesReceived() {
return new ArrayList<String>(this.messagesReceived);
}
}
}
/*
* Copyright 2012-2013 the original author or authors.
* 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.
......
/*
* Copyright 2012-2013 the original author or authors.
* 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.
......
/*
* Copyright 2012-2013 the original author or authors.
* 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.
......
/*
* Copyright 2012-2014 the original author or authors.
* 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.
......
Spring Boot sample with Redis export for metrics.
= Spring Boot sample with Redis export for metrics.
Start redis, e.g. with [Docker Compose]()
......
......@@ -27,9 +27,6 @@ import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.connection.RedisConnectionFactory;
/**
* @author Dave Syer
*/
@Configuration
public class AggregateMetricsConfiguration {
......@@ -45,8 +42,8 @@ public class AggregateMetricsConfiguration {
}
private MetricReader globalMetricsForAggregation() {
return new RedisMetricRepository(this.connectionFactory,
this.export.getRedis().getAggregatePrefix(), this.export.getRedis().getKey());
return new RedisMetricRepository(this.connectionFactory, this.export.getRedis()
.getAggregatePrefix(), this.export.getRedis().getKey());
}
private MetricReader aggregatesMetricReader() {
......
......@@ -42,8 +42,8 @@ public class SampleRedisExportApplication {
@ExportMetricWriter
public RedisMetricRepository redisMetricWriter(
RedisConnectionFactory connectionFactory) {
return new RedisMetricRepository(connectionFactory, this.export.getRedis().getPrefix(),
this.export.getRedis().getKey());
return new RedisMetricRepository(connectionFactory, this.export.getRedis()
.getPrefix(), this.export.getRedis().getKey());
}
@Bean
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment