Commit 06a7ab0c authored by Phillip Webb's avatar Phillip Webb

Polish ReservoirFactory support

Polish Dropwizrd reservoir support including a refactor of
`ReservoirFactory` to allow reservoirs to be created based on a
metric name.

See gh-5199
See gh-7105
parent 1fc2e870
...@@ -18,7 +18,7 @@ package org.springframework.boot.actuate.autoconfigure; ...@@ -18,7 +18,7 @@ package org.springframework.boot.actuate.autoconfigure;
import com.codahale.metrics.MetricRegistry; import com.codahale.metrics.MetricRegistry;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.ObjectProvider;
import org.springframework.boot.actuate.endpoint.MetricReaderPublicMetrics; import org.springframework.boot.actuate.endpoint.MetricReaderPublicMetrics;
import org.springframework.boot.actuate.metrics.CounterService; import org.springframework.boot.actuate.metrics.CounterService;
import org.springframework.boot.actuate.metrics.GaugeService; import org.springframework.boot.actuate.metrics.GaugeService;
...@@ -43,8 +43,12 @@ import org.springframework.context.annotation.Configuration; ...@@ -43,8 +43,12 @@ import org.springframework.context.annotation.Configuration;
@AutoConfigureBefore(MetricRepositoryAutoConfiguration.class) @AutoConfigureBefore(MetricRepositoryAutoConfiguration.class)
public class MetricsDropwizardAutoConfiguration { public class MetricsDropwizardAutoConfiguration {
@Autowired(required = false) private final ReservoirFactory reservoirFactory;
private ReservoirFactory reservoirFactory;
public MetricsDropwizardAutoConfiguration(
ObjectProvider<ReservoirFactory> reservoirFactory) {
this.reservoirFactory = reservoirFactory.getIfAvailable();
}
@Bean @Bean
@ConditionalOnMissingBean @ConditionalOnMissingBean
......
...@@ -31,6 +31,8 @@ import com.codahale.metrics.Timer; ...@@ -31,6 +31,8 @@ import com.codahale.metrics.Timer;
import org.springframework.boot.actuate.metrics.CounterService; import org.springframework.boot.actuate.metrics.CounterService;
import org.springframework.boot.actuate.metrics.GaugeService; 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 * A {@link GaugeService} and {@link CounterService} that sends data to a Dropwizard
...@@ -55,7 +57,7 @@ public class DropwizardMetricServices implements CounterService, GaugeService { ...@@ -55,7 +57,7 @@ public class DropwizardMetricServices implements CounterService, GaugeService {
private final MetricRegistry registry; private final MetricRegistry registry;
private ReservoirFactory reservoirFactory; private final ReservoirFactory reservoirFactory;
private final ConcurrentMap<String, SimpleGauge> gauges = new ConcurrentHashMap<String, SimpleGauge>(); private final ConcurrentMap<String, SimpleGauge> gauges = new ConcurrentHashMap<String, SimpleGauge>();
...@@ -66,19 +68,20 @@ public class DropwizardMetricServices implements CounterService, GaugeService { ...@@ -66,19 +68,20 @@ public class DropwizardMetricServices implements CounterService, GaugeService {
* @param registry the underlying metric registry * @param registry the underlying metric registry
*/ */
public DropwizardMetricServices(MetricRegistry registry) { public DropwizardMetricServices(MetricRegistry registry) {
this.registry = registry; this(registry, null);
} }
/** /**
* Create a new {@link DropwizardMetricServices} instance. * Create a new {@link DropwizardMetricServices} instance.
* @param registry the underlying metric registry * @param registry the underlying metric registry
* @param reservoirFactory the factory that instantiates the {@link Reservoir} that * @param reservoirFactory the factory that instantiates the {@link Reservoir} that
* will be used on Timers and Histograms * will be used on Timers and Histograms
*/ */
public DropwizardMetricServices(MetricRegistry registry, public DropwizardMetricServices(MetricRegistry registry,
ReservoirFactory reservoirFactory) { ReservoirFactory reservoirFactory) {
this.registry = registry; this.registry = registry;
this.reservoirFactory = reservoirFactory; this.reservoirFactory = (reservoirFactory == null ? ReservoirFactory.NONE
: reservoirFactory);
} }
@Override @Override
...@@ -106,14 +109,10 @@ public class DropwizardMetricServices implements CounterService, GaugeService { ...@@ -106,14 +109,10 @@ public class DropwizardMetricServices implements CounterService, GaugeService {
@Override @Override
public void submit(String name, double value) { public void submit(String name, double value) {
if (name.startsWith("histogram")) { if (name.startsWith("histogram")) {
long longValue = (long) value; submitHistogram(name, value);
Histogram metric = registerHistogram(name);
metric.update(longValue);
} }
else if (name.startsWith("timer")) { else if (name.startsWith("timer")) {
long longValue = (long) value; submitTimer(name, value);
Timer metric = registerTimer(name);
metric.update(longValue, TimeUnit.MILLISECONDS);
} }
else { else {
name = wrapGaugeName(name); name = wrapGaugeName(name);
...@@ -121,40 +120,36 @@ public class DropwizardMetricServices implements CounterService, GaugeService { ...@@ -121,40 +120,36 @@ public class DropwizardMetricServices implements CounterService, GaugeService {
} }
} }
private Histogram registerHistogram(String name) { private void submitTimer(String name, double value) {
if (this.reservoirFactory == null) { long longValue = (long) value;
return this.registry.histogram(name); Timer metric = register(name, new TimerMetricRegistrar());
} metric.update(longValue, TimeUnit.MILLISECONDS);
else {
Histogram histogram = new Histogram(this.reservoirFactory.getObject());
return getOrAddMetric(name, histogram);
}
} }
private Timer registerTimer(String name) { private void submitHistogram(String name, double value) {
if (this.reservoirFactory == null) { long longValue = (long) value;
return this.registry.timer(name); Histogram metric = register(name, new HistogramMetricRegistrar());
} metric.update(longValue);
else {
Timer timer = new Timer(this.reservoirFactory.getObject());
return getOrAddMetric(name, timer);
}
} }
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
private <T extends Metric> T getOrAddMetric(String name, T newMetric) { 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); Metric metric = this.registry.getMetrics().get(name);
if (metric == null) { if (metric != null) {
return this.registry.register(name, newMetric); registrar.checkExisting(metric);
return (T) metric;
} }
else { try {
if (metric.getClass().equals(newMetric.getClass())) { return this.registry.register(name, registrar.createForReservoir(reservoir));
return (T) metric; }
} catch (IllegalArgumentException ex) {
else { Metric added = this.registry.getMetrics().get(name);
throw new IllegalArgumentException( registrar.checkExisting(metric);
name + " is already used for a different type of metric"); return (T) added;
}
} }
} }
...@@ -201,10 +196,6 @@ public class DropwizardMetricServices implements CounterService, GaugeService { ...@@ -201,10 +196,6 @@ public class DropwizardMetricServices implements CounterService, GaugeService {
this.registry.remove(name); this.registry.remove(name);
} }
void setReservoirFactory(ReservoirFactory reservoirFactory) {
this.reservoirFactory = reservoirFactory;
}
/** /**
* Simple {@link Gauge} implementation to {@literal double} value. * Simple {@link Gauge} implementation to {@literal double} value.
*/ */
...@@ -227,4 +218,62 @@ public class DropwizardMetricServices implements CounterService, GaugeService { ...@@ -227,4 +218,62 @@ public class DropwizardMetricServices implements CounterService, GaugeService {
} }
/**
* 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);
}
}
} }
...@@ -18,22 +18,34 @@ package org.springframework.boot.actuate.metrics.dropwizard; ...@@ -18,22 +18,34 @@ package org.springframework.boot.actuate.metrics.dropwizard;
import com.codahale.metrics.Reservoir; import com.codahale.metrics.Reservoir;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.ObjectFactory;
/** /**
* A {@link Reservoir} factory to instantiate the Reservoir that will be set as default * Factory interface that can be used by {@link DropwizardMetricServices} to create a
* for the {@link DropwizardMetricServices}. * custom {@link Reservoir}.
* The Reservoir instances can't be shared across {@link com.codahale.metrics.Metric}.
* *
* @author Lucas Saldanha * @author Lucas Saldanha
* @author Phillip Webb
* @since 1.5.0
*/ */
public abstract class ReservoirFactory implements ObjectFactory<Reservoir> { public interface ReservoirFactory {
/**
* Default empty {@link ReservoirFactory} implementation.
*/
ReservoirFactory NONE = new ReservoirFactory() {
@Override
public Reservoir getReservoir(String name) {
return null;
}
};
protected abstract Reservoir defaultReservoir(); /**
* 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);
@Override
public Reservoir getObject() throws BeansException {
return defaultReservoir();
}
} }
...@@ -18,7 +18,6 @@ package org.springframework.boot.actuate.autoconfigure; ...@@ -18,7 +18,6 @@ package org.springframework.boot.actuate.autoconfigure;
import com.codahale.metrics.Reservoir; import com.codahale.metrics.Reservoir;
import com.codahale.metrics.UniformReservoir; import com.codahale.metrics.UniformReservoir;
import org.junit.After; import org.junit.After;
import org.junit.Test; import org.junit.Test;
...@@ -27,7 +26,6 @@ import org.springframework.boot.actuate.metrics.dropwizard.ReservoirFactory; ...@@ -27,7 +26,6 @@ import org.springframework.boot.actuate.metrics.dropwizard.ReservoirFactory;
import org.springframework.context.annotation.AnnotationConfigApplicationContext; import org.springframework.context.annotation.AnnotationConfigApplicationContext;
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.test.util.ReflectionTestUtils; import org.springframework.test.util.ReflectionTestUtils;
import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThat;
...@@ -52,24 +50,23 @@ public class MetricsDropwizardAutoConfigurationTests { ...@@ -52,24 +50,23 @@ public class MetricsDropwizardAutoConfigurationTests {
public void dropwizardWithoutCustomReservoirConfigured() { public void dropwizardWithoutCustomReservoirConfigured() {
this.context = new AnnotationConfigApplicationContext( this.context = new AnnotationConfigApplicationContext(
MetricsDropwizardAutoConfiguration.class); MetricsDropwizardAutoConfiguration.class);
DropwizardMetricServices dropwizardMetricServices = this.context DropwizardMetricServices dropwizardMetricServices = this.context
.getBean(DropwizardMetricServices.class); .getBean(DropwizardMetricServices.class);
ReservoirFactory reservoirFactory = (ReservoirFactory) ReflectionTestUtils
assertThat(ReflectionTestUtils.getField(dropwizardMetricServices, "reservoirFactory")) .getField(dropwizardMetricServices, "reservoirFactory");
.isNull(); assertThat(reservoirFactory.getReservoir("test")).isNull();
} }
@Test @Test
public void dropwizardWithCustomReservoirConfigured() { public void dropwizardWithCustomReservoirConfigured() {
this.context = new AnnotationConfigApplicationContext( this.context = new AnnotationConfigApplicationContext(
MetricsDropwizardAutoConfiguration.class, Config.class); MetricsDropwizardAutoConfiguration.class, Config.class);
DropwizardMetricServices dropwizardMetricServices = this.context DropwizardMetricServices dropwizardMetricServices = this.context
.getBean(DropwizardMetricServices.class); .getBean(DropwizardMetricServices.class);
ReservoirFactory reservoirFactory = (ReservoirFactory) ReflectionTestUtils
assertThat(ReflectionTestUtils.getField(dropwizardMetricServices, "reservoirFactory")) .getField(dropwizardMetricServices, "reservoirFactory");
.isNotNull(); assertThat(reservoirFactory.getReservoir("test"))
.isInstanceOf(UniformReservoir.class);
} }
@Configuration @Configuration
...@@ -77,13 +74,18 @@ public class MetricsDropwizardAutoConfigurationTests { ...@@ -77,13 +74,18 @@ public class MetricsDropwizardAutoConfigurationTests {
@Bean @Bean
public ReservoirFactory reservoirFactory() { public ReservoirFactory reservoirFactory() {
return new ReservoirFactory() { return new UniformReservoirFactory();
@Override
protected Reservoir defaultReservoir() {
return new UniformReservoir();
}
};
} }
}
private static class UniformReservoirFactory implements ReservoirFactory {
@Override
public Reservoir getReservoir(String name) {
return new UniformReservoir();
}
} }
} }
...@@ -22,14 +22,18 @@ import java.util.List; ...@@ -22,14 +22,18 @@ import java.util.List;
import com.codahale.metrics.Gauge; import com.codahale.metrics.Gauge;
import com.codahale.metrics.Histogram; import com.codahale.metrics.Histogram;
import com.codahale.metrics.MetricRegistry; import com.codahale.metrics.MetricRegistry;
import com.codahale.metrics.Reservoir;
import com.codahale.metrics.Timer; import com.codahale.metrics.Timer;
import com.codahale.metrics.UniformReservoir; import com.codahale.metrics.UniformReservoir;
import org.junit.Before;
import org.junit.Test; import org.junit.Test;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import org.springframework.test.util.ReflectionTestUtils; import org.springframework.test.util.ReflectionTestUtils;
import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.BDDMockito.given;
import static org.mockito.Matchers.anyString;
/** /**
* Tests for {@link DropwizardMetricServices}. * Tests for {@link DropwizardMetricServices}.
...@@ -39,10 +43,18 @@ import static org.assertj.core.api.Assertions.assertThat; ...@@ -39,10 +43,18 @@ import static org.assertj.core.api.Assertions.assertThat;
*/ */
public class DropwizardMetricServicesTests { public class DropwizardMetricServicesTests {
private final MetricRegistry registry = new MetricRegistry(); private MetricRegistry registry = new MetricRegistry();
private final DropwizardMetricServices writer = new DropwizardMetricServices( @Mock
this.registry); private ReservoirFactory reservoirFactory;
private DropwizardMetricServices writer;
@Before
public void setup() {
MockitoAnnotations.initMocks(this);
this.writer = new DropwizardMetricServices(this.registry, this.reservoirFactory);
}
@Test @Test
public void incrementCounter() { public void incrementCounter() {
...@@ -87,20 +99,14 @@ public class DropwizardMetricServicesTests { ...@@ -87,20 +99,14 @@ public class DropwizardMetricServicesTests {
@Test @Test
public void setCustomReservoirTimer() { public void setCustomReservoirTimer() {
this.writer.setReservoirFactory(new ReservoirFactory() { given(this.reservoirFactory.getReservoir(anyString()))
@Override .willReturn(new UniformReservoir());
protected Reservoir defaultReservoir() {
return new UniformReservoir();
}
});
this.writer.submit("timer.foo", 200); this.writer.submit("timer.foo", 200);
this.writer.submit("timer.foo", 300); this.writer.submit("timer.foo", 300);
assertThat(this.registry.timer("timer.foo").getCount()).isEqualTo(2); assertThat(this.registry.timer("timer.foo").getCount()).isEqualTo(2);
Timer timer = (Timer) this.registry.getMetrics().get("timer.foo"); Timer timer = (Timer) this.registry.getMetrics().get("timer.foo");
Histogram histogram = (Histogram) ReflectionTestUtils Histogram histogram = (Histogram) ReflectionTestUtils.getField(timer,
.getField(timer, "histogram"); "histogram");
assertThat(ReflectionTestUtils.getField(histogram, "reservoir").getClass() assertThat(ReflectionTestUtils.getField(histogram, "reservoir").getClass()
.equals(UniformReservoir.class)).isTrue(); .equals(UniformReservoir.class)).isTrue();
} }
...@@ -114,13 +120,8 @@ public class DropwizardMetricServicesTests { ...@@ -114,13 +120,8 @@ public class DropwizardMetricServicesTests {
@Test @Test
public void setCustomReservoirHistogram() { public void setCustomReservoirHistogram() {
this.writer.setReservoirFactory(new ReservoirFactory() { given(this.reservoirFactory.getReservoir(anyString()))
@Override .willReturn(new UniformReservoir());
protected Reservoir defaultReservoir() {
return new UniformReservoir();
}
});
this.writer.submit("histogram.foo", 2.1); this.writer.submit("histogram.foo", 2.1);
this.writer.submit("histogram.foo", 2.3); this.writer.submit("histogram.foo", 2.3);
assertThat(this.registry.histogram("histogram.foo").getCount()).isEqualTo(2); assertThat(this.registry.histogram("histogram.foo").getCount()).isEqualTo(2);
......
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