From 4e960d489a8975fb7d98ae2a8b89f37c5438a49e Mon Sep 17 00:00:00 2001 From: Julio Lopez Date: Mon, 6 Oct 2014 18:03:08 -0700 Subject: [PATCH] Allow specifying a default reservoir for a registry. Motivation: A usage pattern for the metrics library is to create a MetricRegistry and use the counter(), histogram(), meter() and timer() methods to create metrics for the application. When these methods are called MetricRegistry potentially creates new metrics with their default parameters. Creating metrics with non-default parameters, in particular a non-default reservoir, currently requires explicitly creating the metric and then registering it and remembering it for future use. This prevents leveraging functionality already provided by MetricsRegistry. Proposed approach: Allow specifying a ReservoirBuilder at MetricsRegistry creation time. ReservoirBuilder creates new reservoirs with custom parameters. This allows the creation of histograms and timers with custom default reservoirs. Alternate approaches: A potentially more comprehensive approach would be to allow passing a custom "MetricsBuilder" to the MetricsRegistry constructor. This approach is definitely worth exploring. Closes #679 --- .../ExponentiallyDecayingReservoir.java | 4 +- .../com/codahale/metrics/MetricRegistry.java | 103 ++++++++++-------- .../codahale/metrics/ReservoirBuilder.java | 75 +++++++++++++ 3 files changed, 132 insertions(+), 50 deletions(-) create mode 100644 metrics-core/src/main/java/com/codahale/metrics/ReservoirBuilder.java diff --git a/metrics-core/src/main/java/com/codahale/metrics/ExponentiallyDecayingReservoir.java b/metrics-core/src/main/java/com/codahale/metrics/ExponentiallyDecayingReservoir.java index 7ff16ded5c..c8d1cca5e3 100644 --- a/metrics-core/src/main/java/com/codahale/metrics/ExponentiallyDecayingReservoir.java +++ b/metrics-core/src/main/java/com/codahale/metrics/ExponentiallyDecayingReservoir.java @@ -21,8 +21,8 @@ * Proceedings of the 2009 IEEE International Conference on Data Engineering (2009) */ public class ExponentiallyDecayingReservoir implements Reservoir { - private static final int DEFAULT_SIZE = 1028; - private static final double DEFAULT_ALPHA = 0.015; + static final int DEFAULT_SIZE = 1028; + static final double DEFAULT_ALPHA = 0.015; private static final long RESCALE_THRESHOLD = TimeUnit.HOURS.toNanos(1); private final ConcurrentSkipListMap values; diff --git a/metrics-core/src/main/java/com/codahale/metrics/MetricRegistry.java b/metrics-core/src/main/java/com/codahale/metrics/MetricRegistry.java index 1892492a68..d8199f1c18 100644 --- a/metrics-core/src/main/java/com/codahale/metrics/MetricRegistry.java +++ b/metrics-core/src/main/java/com/codahale/metrics/MetricRegistry.java @@ -50,15 +50,22 @@ private static void append(StringBuilder builder, String part) { private final ConcurrentMap metrics; private final List listeners; + private final ReservoirBuilder reservoirBuilder; /** * Creates a new {@link MetricRegistry}. */ - public MetricRegistry() { + public MetricRegistry(ReservoirBuilder reservoirBuilder) { + this.reservoirBuilder = reservoirBuilder; this.metrics = buildMap(); this.listeners = new CopyOnWriteArrayList(); } + public MetricRegistry() { + this(new ReservoirBuilder.ExponentiallyDecayingReservoirBuilder(ExponentiallyDecayingReservoir.DEFAULT_SIZE, + ExponentiallyDecayingReservoir.DEFAULT_ALPHA)); + } + /** * Creates a new {@link ConcurrentMap} implementation for use inside the registry. Override this * to create a {@link MetricRegistry} with space- or time-bounded metric lifecycles, for @@ -111,7 +118,7 @@ public void registerAll(MetricSet metrics) throws IllegalArgumentException { * @return a new {@link Counter} */ public Counter counter(String name) { - return getOrAdd(name, MetricBuilder.COUNTERS); + return getOrAdd(name, countersBuilder); } /** @@ -121,7 +128,7 @@ public Counter counter(String name) { * @return a new {@link Histogram} */ public Histogram histogram(String name) { - return getOrAdd(name, MetricBuilder.HISTOGRAMS); + return getOrAdd(name, histogramsBuilder); } /** @@ -131,7 +138,7 @@ public Histogram histogram(String name) { * @return a new {@link Meter} */ public Meter meter(String name) { - return getOrAdd(name, MetricBuilder.METERS); + return getOrAdd(name, metersBuilder); } /** @@ -141,7 +148,7 @@ public Meter meter(String name) { * @return a new {@link Timer} */ public Timer timer(String name) { - return getOrAdd(name, MetricBuilder.TIMERS); + return getOrAdd(name, timersBuilder); } /** @@ -396,56 +403,56 @@ public Map getMetrics() { * A quick and easy way of capturing the notion of default metrics. */ private interface MetricBuilder { - MetricBuilder COUNTERS = new MetricBuilder() { - @Override - public Counter newMetric() { - return new Counter(); - } + T newMetric(); - @Override - public boolean isInstance(Metric metric) { - return Counter.class.isInstance(metric); - } - }; + boolean isInstance(Metric metric); + } - MetricBuilder HISTOGRAMS = new MetricBuilder() { - @Override - public Histogram newMetric() { - return new Histogram(new ExponentiallyDecayingReservoir()); - } + final MetricBuilder countersBuilder = new MetricBuilder() { + @Override + public Counter newMetric() { + return new Counter(); + } - @Override - public boolean isInstance(Metric metric) { - return Histogram.class.isInstance(metric); - } - }; + @Override + public boolean isInstance(Metric metric) { + return Counter.class.isInstance(metric); + } + }; - MetricBuilder METERS = new MetricBuilder() { - @Override - public Meter newMetric() { - return new Meter(); - } + final MetricBuilder histogramsBuilder = new MetricBuilder() { + @Override + public Histogram newMetric() { + return new Histogram(reservoirBuilder.newReservoir()); + } - @Override - public boolean isInstance(Metric metric) { - return Meter.class.isInstance(metric); - } - }; + @Override + public boolean isInstance(Metric metric) { + return Histogram.class.isInstance(metric); + } + }; - MetricBuilder TIMERS = new MetricBuilder() { - @Override - public Timer newMetric() { - return new Timer(); - } + final MetricBuilder metersBuilder = new MetricBuilder() { + @Override + public Meter newMetric() { + return new Meter(); + } - @Override - public boolean isInstance(Metric metric) { - return Timer.class.isInstance(metric); - } - }; + @Override + public boolean isInstance(Metric metric) { + return Meter.class.isInstance(metric); + } + }; - T newMetric(); + final MetricBuilder timersBuilder = new MetricBuilder() { + @Override + public Timer newMetric() { + return new Timer(reservoirBuilder.newReservoir()); + } - boolean isInstance(Metric metric); - } + @Override + public boolean isInstance(Metric metric) { + return Timer.class.isInstance(metric); + } + }; } diff --git a/metrics-core/src/main/java/com/codahale/metrics/ReservoirBuilder.java b/metrics-core/src/main/java/com/codahale/metrics/ReservoirBuilder.java new file mode 100644 index 0000000000..1ce0d7a2a9 --- /dev/null +++ b/metrics-core/src/main/java/com/codahale/metrics/ReservoirBuilder.java @@ -0,0 +1,75 @@ +package com.codahale.metrics; + +import java.util.concurrent.TimeUnit; + +public abstract class ReservoirBuilder { + public static final class ExponentiallyDecayingReservoirBuilder extends ReservoirBuilder { + private final double alpha; + private final int size; + private final Clock clock; + + public ExponentiallyDecayingReservoirBuilder(final int size, final double alpha, final Clock clock) { + this.size = size; + this.alpha = alpha; + this.clock = clock; + } + + public ExponentiallyDecayingReservoirBuilder(final int size, final double alpha) { + this(size, alpha, Clock.defaultClock()); + } + + @Override + public Reservoir newReservoir() { + return new ExponentiallyDecayingReservoir(size, alpha, clock); + } + } + + public static final class SlidingWindowReservoirBuilder extends ReservoirBuilder { + private final int size; + + public SlidingWindowReservoirBuilder(final int size) { + this.size = size; + } + + @Override + public final Reservoir newReservoir() { + return new SlidingWindowReservoir(size); + } + } + + public static final class SlidingTimeWindowReservoirBuilder extends ReservoirBuilder { + private final long window; + private final TimeUnit windowUnit; + private final Clock clock; + + public SlidingTimeWindowReservoirBuilder(final int window, final TimeUnit windowUnit, final Clock clock) { + this.window = window; + this.windowUnit = windowUnit; + this.clock = clock; + } + + public SlidingTimeWindowReservoirBuilder(final int window, final TimeUnit windowUnit) { + this(window, windowUnit, Clock.defaultClock()); + } + + @Override + public final Reservoir newReservoir() { + return new SlidingTimeWindowReservoir(window, windowUnit, clock); + } + } + + public static final class UniformReservoirBuilder extends ReservoirBuilder { + private final int size; + + public UniformReservoirBuilder(final int size) { + this.size = size; + } + + @Override + public final Reservoir newReservoir() { + return new UniformReservoir(size); + } + } + + public abstract Reservoir newReservoir(); +}