package com.codahale.metrics;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.SortedMap;
import java.util.SortedSet;
import java.util.TreeMap;
import java.util.TreeSet;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.CopyOnWriteArrayList;
A registry of metric instances.
/**
* A registry of metric instances.
*/
public class MetricRegistry implements MetricSet {
Concatenates elements to form a dotted name, eliding any null values or empty strings.
Params: - name – the first element of the name
- names – the remaining elements of the name
Returns: name
and names
concatenated by periods
/**
* Concatenates elements to form a dotted name, eliding any null values or empty strings.
*
* @param name the first element of the name
* @param names the remaining elements of the name
* @return {@code name} and {@code names} concatenated by periods
*/
public static String name(String name, String... names) {
final StringBuilder builder = new StringBuilder();
append(builder, name);
if (names != null) {
for (String s : names) {
append(builder, s);
}
}
return builder.toString();
}
Concatenates a class name and elements to form a dotted name, eliding any null values or
empty strings.
Params: - klass – the first element of the name
- names – the remaining elements of the name
Returns: klass
and names
concatenated by periods
/**
* Concatenates a class name and elements to form a dotted name, eliding any null values or
* empty strings.
*
* @param klass the first element of the name
* @param names the remaining elements of the name
* @return {@code klass} and {@code names} concatenated by periods
*/
public static String name(Class<?> klass, String... names) {
return name(klass.getName(), names);
}
private static void append(StringBuilder builder, String part) {
if (part != null && !part.isEmpty()) {
if (builder.length() > 0) {
builder.append('.');
}
builder.append(part);
}
}
private final ConcurrentMap<String, Metric> metrics;
private final List<MetricRegistryListener> listeners;
Creates a new MetricRegistry
. /**
* Creates a new {@link MetricRegistry}.
*/
public MetricRegistry() {
this.metrics = buildMap();
this.listeners = new CopyOnWriteArrayList<>();
}
Creates a new ConcurrentMap
implementation for use inside the registry. Override this to create a MetricRegistry
with space- or time-bounded metric lifecycles, for example. Returns: a new ConcurrentMap
/**
* 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
* example.
*
* @return a new {@link ConcurrentMap}
*/
protected ConcurrentMap<String, Metric> buildMap() {
return new ConcurrentHashMap<>();
}
Given a Metric
, registers it under the given name. Params: - name – the name of the metric
- metric – the metric
Type parameters: - <T> – the type of the metric
Throws: - IllegalArgumentException – if the name is already registered
Returns: metric
/**
* Given a {@link Metric}, registers it under the given name.
*
* @param name the name of the metric
* @param metric the metric
* @param <T> the type of the metric
* @return {@code metric}
* @throws IllegalArgumentException if the name is already registered
*/
@SuppressWarnings("unchecked")
public <T extends Metric> T register(String name, T metric) throws IllegalArgumentException {
if (metric instanceof MetricRegistry) {
final MetricRegistry childRegistry = (MetricRegistry)metric;
final String childName = name;
childRegistry.addListener(new MetricRegistryListener() {
@Override
public void onGaugeAdded(String name, Gauge<?> gauge) {
register(name(childName, name), gauge);
}
@Override
public void onGaugeRemoved(String name) {
remove(name(childName, name));
}
@Override
public void onCounterAdded(String name, Counter counter) {
register(name(childName, name), counter);
}
@Override
public void onCounterRemoved(String name) {
remove(name(childName, name));
}
@Override
public void onHistogramAdded(String name, Histogram histogram) {
register(name(childName, name), histogram);
}
@Override
public void onHistogramRemoved(String name) {
remove(name(childName, name));
}
@Override
public void onMeterAdded(String name, Meter meter) {
register(name(childName, name), meter);
}
@Override
public void onMeterRemoved(String name) {
remove(name(childName, name));
}
@Override
public void onTimerAdded(String name, Timer timer) {
register(name(childName, name), timer);
}
@Override
public void onTimerRemoved(String name) {
remove(name(childName, name));
}
});
} else if (metric instanceof MetricSet) {
registerAll(name, (MetricSet) metric);
} else {
final Metric existing = metrics.putIfAbsent(name, metric);
if (existing == null) {
onMetricAdded(name, metric);
} else {
throw new IllegalArgumentException("A metric named " + name + " already exists");
}
}
return metric;
}
Given a metric set, registers them.
Params: - metrics – a set of metrics
Throws: - IllegalArgumentException – if any of the names are already registered
/**
* Given a metric set, registers them.
*
* @param metrics a set of metrics
* @throws IllegalArgumentException if any of the names are already registered
*/
public void registerAll(MetricSet metrics) throws IllegalArgumentException {
registerAll(null, metrics);
}
Return the Counter
registered under this name; or create and register a new Counter
if none is registered. Params: - name – the name of the metric
Returns: a new or pre-existing Counter
/**
* Return the {@link Counter} registered under this name; or create and register
* a new {@link Counter} if none is registered.
*
* @param name the name of the metric
* @return a new or pre-existing {@link Counter}
*/
public Counter counter(String name) {
return getOrAdd(name, MetricBuilder.COUNTERS);
}
Return the Counter
registered under this name; or create and register a new Counter
using the provided MetricSupplier if none is registered. Params: - name – the name of the metric
- supplier – a MetricSupplier that can be used to manufacture a counter.
Returns: a new or pre-existing Counter
/**
* Return the {@link Counter} registered under this name; or create and register
* a new {@link Counter} using the provided MetricSupplier if none is registered.
*
* @param name the name of the metric
* @param supplier a MetricSupplier that can be used to manufacture a counter.
* @return a new or pre-existing {@link Counter}
*/
public Counter counter(String name, final MetricSupplier<Counter> supplier) {
return getOrAdd(name, new MetricBuilder<Counter>() {
@Override
public Counter newMetric() {
return supplier.newMetric();
}
@Override
public boolean isInstance(Metric metric) {
return Counter.class.isInstance(metric);
}
});
}
Return the Histogram
registered under this name; or create and register a new Histogram
if none is registered. Params: - name – the name of the metric
Returns: a new or pre-existing Histogram
/**
* Return the {@link Histogram} registered under this name; or create and register
* a new {@link Histogram} if none is registered.
*
* @param name the name of the metric
* @return a new or pre-existing {@link Histogram}
*/
public Histogram histogram(String name) {
return getOrAdd(name, MetricBuilder.HISTOGRAMS);
}
Return the Histogram
registered under this name; or create and register a new Histogram
using the provided MetricSupplier if none is registered. Params: - name – the name of the metric
- supplier – a MetricSupplier that can be used to manufacture a histogram
Returns: a new or pre-existing Histogram
/**
* Return the {@link Histogram} registered under this name; or create and register
* a new {@link Histogram} using the provided MetricSupplier if none is registered.
*
* @param name the name of the metric
* @param supplier a MetricSupplier that can be used to manufacture a histogram
* @return a new or pre-existing {@link Histogram}
*/
public Histogram histogram(String name, final MetricSupplier<Histogram> supplier) {
return getOrAdd(name, new MetricBuilder<Histogram>() {
@Override
public Histogram newMetric() {
return supplier.newMetric();
}
@Override
public boolean isInstance(Metric metric) {
return Histogram.class.isInstance(metric);
}
});
}
Return the Meter
registered under this name; or create and register a new Meter
if none is registered. Params: - name – the name of the metric
Returns: a new or pre-existing Meter
/**
* Return the {@link Meter} registered under this name; or create and register
* a new {@link Meter} if none is registered.
*
* @param name the name of the metric
* @return a new or pre-existing {@link Meter}
*/
public Meter meter(String name) {
return getOrAdd(name, MetricBuilder.METERS);
}
Return the Meter
registered under this name; or create and register a new Meter
using the provided MetricSupplier if none is registered. Params: - name – the name of the metric
- supplier – a MetricSupplier that can be used to manufacture a Meter
Returns: a new or pre-existing Meter
/**
* Return the {@link Meter} registered under this name; or create and register
* a new {@link Meter} using the provided MetricSupplier if none is registered.
*
* @param name the name of the metric
* @param supplier a MetricSupplier that can be used to manufacture a Meter
* @return a new or pre-existing {@link Meter}
*/
public Meter meter(String name, final MetricSupplier<Meter> supplier) {
return getOrAdd(name, new MetricBuilder<Meter>() {
@Override
public Meter newMetric() {
return supplier.newMetric();
}
@Override
public boolean isInstance(Metric metric) {
return Meter.class.isInstance(metric);
}
});
}
Return the Timer
registered under this name; or create and register a new Timer
if none is registered. Params: - name – the name of the metric
Returns: a new or pre-existing Timer
/**
* Return the {@link Timer} registered under this name; or create and register
* a new {@link Timer} if none is registered.
*
* @param name the name of the metric
* @return a new or pre-existing {@link Timer}
*/
public Timer timer(String name) {
return getOrAdd(name, MetricBuilder.TIMERS);
}
Return the Timer
registered under this name; or create and register a new Timer
using the provided MetricSupplier if none is registered. Params: - name – the name of the metric
- supplier – a MetricSupplier that can be used to manufacture a Timer
Returns: a new or pre-existing Timer
/**
* Return the {@link Timer} registered under this name; or create and register
* a new {@link Timer} using the provided MetricSupplier if none is registered.
*
* @param name the name of the metric
* @param supplier a MetricSupplier that can be used to manufacture a Timer
* @return a new or pre-existing {@link Timer}
*/
public Timer timer(String name, final MetricSupplier<Timer> supplier) {
return getOrAdd(name, new MetricBuilder<Timer>() {
@Override
public Timer newMetric() {
return supplier.newMetric();
}
@Override
public boolean isInstance(Metric metric) {
return Timer.class.isInstance(metric);
}
});
}
Return the Gauge
registered under this name; or create and register a new Gauge
using the provided MetricSupplier if none is registered. Params: - name – the name of the metric
- supplier – a MetricSupplier that can be used to manufacture a Gauge
Returns: a new or pre-existing Gauge
/**
* Return the {@link Gauge} registered under this name; or create and register
* a new {@link Gauge} using the provided MetricSupplier if none is registered.
*
* @param name the name of the metric
* @param supplier a MetricSupplier that can be used to manufacture a Gauge
* @return a new or pre-existing {@link Gauge}
*/
@SuppressWarnings("rawtypes")
public Gauge gauge(String name, final MetricSupplier<Gauge> supplier) {
return getOrAdd(name, new MetricBuilder<Gauge>() {
@Override
public Gauge newMetric() {
return supplier.newMetric();
}
@Override
public boolean isInstance(Metric metric) {
return Gauge.class.isInstance(metric);
}
});
}
Removes the metric with the given name.
Params: - name – the name of the metric
Returns: whether or not the metric was removed
/**
* Removes the metric with the given name.
*
* @param name the name of the metric
* @return whether or not the metric was removed
*/
public boolean remove(String name) {
final Metric metric = metrics.remove(name);
if (metric != null) {
onMetricRemoved(name, metric);
return true;
}
return false;
}
Removes all metrics which match the given filter.
Params: - filter – a filter
/**
* Removes all metrics which match the given filter.
*
* @param filter a filter
*/
public void removeMatching(MetricFilter filter) {
for (Map.Entry<String, Metric> entry : metrics.entrySet()) {
if (filter.matches(entry.getKey(), entry.getValue())) {
remove(entry.getKey());
}
}
}
Adds a MetricRegistryListener
to a collection of listeners that will be notified on metric creation. Listeners will be notified in the order in which they are added.
N.B.: The listener will be notified of all existing metrics when it first registers.
Params: - listener – the listener that will be notified
/**
* Adds a {@link MetricRegistryListener} to a collection of listeners that will be notified on
* metric creation. Listeners will be notified in the order in which they are added.
* <p>
* <b>N.B.:</b> The listener will be notified of all existing metrics when it first registers.
*
* @param listener the listener that will be notified
*/
public void addListener(MetricRegistryListener listener) {
listeners.add(listener);
for (Map.Entry<String, Metric> entry : metrics.entrySet()) {
notifyListenerOfAddedMetric(listener, entry.getValue(), entry.getKey());
}
}
Removes a MetricRegistryListener
from this registry's collection of listeners. Params: - listener – the listener that will be removed
/**
* Removes a {@link MetricRegistryListener} from this registry's collection of listeners.
*
* @param listener the listener that will be removed
*/
public void removeListener(MetricRegistryListener listener) {
listeners.remove(listener);
}
Returns a set of the names of all the metrics in the registry.
Returns: the names of all the metrics
/**
* Returns a set of the names of all the metrics in the registry.
*
* @return the names of all the metrics
*/
public SortedSet<String> getNames() {
return Collections.unmodifiableSortedSet(new TreeSet<>(metrics.keySet()));
}
Returns a map of all the gauges in the registry and their names.
Returns: all the gauges in the registry
/**
* Returns a map of all the gauges in the registry and their names.
*
* @return all the gauges in the registry
*/
@SuppressWarnings("rawtypes")
public SortedMap<String, Gauge> getGauges() {
return getGauges(MetricFilter.ALL);
}
Returns a map of all the gauges in the registry and their names which match the given filter.
Params: - filter – the metric filter to match
Returns: all the gauges in the registry
/**
* Returns a map of all the gauges in the registry and their names which match the given filter.
*
* @param filter the metric filter to match
* @return all the gauges in the registry
*/
@SuppressWarnings("rawtypes")
public SortedMap<String, Gauge> getGauges(MetricFilter filter) {
return getMetrics(Gauge.class, filter);
}
Returns a map of all the counters in the registry and their names.
Returns: all the counters in the registry
/**
* Returns a map of all the counters in the registry and their names.
*
* @return all the counters in the registry
*/
public SortedMap<String, Counter> getCounters() {
return getCounters(MetricFilter.ALL);
}
Returns a map of all the counters in the registry and their names which match the given
filter.
Params: - filter – the metric filter to match
Returns: all the counters in the registry
/**
* Returns a map of all the counters in the registry and their names which match the given
* filter.
*
* @param filter the metric filter to match
* @return all the counters in the registry
*/
public SortedMap<String, Counter> getCounters(MetricFilter filter) {
return getMetrics(Counter.class, filter);
}
Returns a map of all the histograms in the registry and their names.
Returns: all the histograms in the registry
/**
* Returns a map of all the histograms in the registry and their names.
*
* @return all the histograms in the registry
*/
public SortedMap<String, Histogram> getHistograms() {
return getHistograms(MetricFilter.ALL);
}
Returns a map of all the histograms in the registry and their names which match the given
filter.
Params: - filter – the metric filter to match
Returns: all the histograms in the registry
/**
* Returns a map of all the histograms in the registry and their names which match the given
* filter.
*
* @param filter the metric filter to match
* @return all the histograms in the registry
*/
public SortedMap<String, Histogram> getHistograms(MetricFilter filter) {
return getMetrics(Histogram.class, filter);
}
Returns a map of all the meters in the registry and their names.
Returns: all the meters in the registry
/**
* Returns a map of all the meters in the registry and their names.
*
* @return all the meters in the registry
*/
public SortedMap<String, Meter> getMeters() {
return getMeters(MetricFilter.ALL);
}
Returns a map of all the meters in the registry and their names which match the given filter.
Params: - filter – the metric filter to match
Returns: all the meters in the registry
/**
* Returns a map of all the meters in the registry and their names which match the given filter.
*
* @param filter the metric filter to match
* @return all the meters in the registry
*/
public SortedMap<String, Meter> getMeters(MetricFilter filter) {
return getMetrics(Meter.class, filter);
}
Returns a map of all the timers in the registry and their names.
Returns: all the timers in the registry
/**
* Returns a map of all the timers in the registry and their names.
*
* @return all the timers in the registry
*/
public SortedMap<String, Timer> getTimers() {
return getTimers(MetricFilter.ALL);
}
Returns a map of all the timers in the registry and their names which match the given filter.
Params: - filter – the metric filter to match
Returns: all the timers in the registry
/**
* Returns a map of all the timers in the registry and their names which match the given filter.
*
* @param filter the metric filter to match
* @return all the timers in the registry
*/
public SortedMap<String, Timer> getTimers(MetricFilter filter) {
return getMetrics(Timer.class, filter);
}
@SuppressWarnings("unchecked")
private <T extends Metric> T getOrAdd(String name, MetricBuilder<T> builder) {
final Metric metric = metrics.get(name);
if (builder.isInstance(metric)) {
return (T) metric;
} else if (metric == null) {
try {
return register(name, builder.newMetric());
} catch (IllegalArgumentException e) {
final Metric added = metrics.get(name);
if (builder.isInstance(added)) {
return (T) added;
}
}
}
throw new IllegalArgumentException(name + " is already used for a different type of metric");
}
@SuppressWarnings("unchecked")
private <T extends Metric> SortedMap<String, T> getMetrics(Class<T> klass, MetricFilter filter) {
final TreeMap<String, T> timers = new TreeMap<>();
for (Map.Entry<String, Metric> entry : metrics.entrySet()) {
if (klass.isInstance(entry.getValue()) && filter.matches(entry.getKey(),
entry.getValue())) {
timers.put(entry.getKey(), (T) entry.getValue());
}
}
return Collections.unmodifiableSortedMap(timers);
}
private void onMetricAdded(String name, Metric metric) {
for (MetricRegistryListener listener : listeners) {
notifyListenerOfAddedMetric(listener, metric, name);
}
}
private void notifyListenerOfAddedMetric(MetricRegistryListener listener, Metric metric, String name) {
if (metric instanceof Gauge) {
listener.onGaugeAdded(name, (Gauge<?>) metric);
} else if (metric instanceof Counter) {
listener.onCounterAdded(name, (Counter) metric);
} else if (metric instanceof Histogram) {
listener.onHistogramAdded(name, (Histogram) metric);
} else if (metric instanceof Meter) {
listener.onMeterAdded(name, (Meter) metric);
} else if (metric instanceof Timer) {
listener.onTimerAdded(name, (Timer) metric);
} else {
throw new IllegalArgumentException("Unknown metric type: " + metric.getClass());
}
}
private void onMetricRemoved(String name, Metric metric) {
for (MetricRegistryListener listener : listeners) {
notifyListenerOfRemovedMetric(name, metric, listener);
}
}
private void notifyListenerOfRemovedMetric(String name, Metric metric, MetricRegistryListener listener) {
if (metric instanceof Gauge) {
listener.onGaugeRemoved(name);
} else if (metric instanceof Counter) {
listener.onCounterRemoved(name);
} else if (metric instanceof Histogram) {
listener.onHistogramRemoved(name);
} else if (metric instanceof Meter) {
listener.onMeterRemoved(name);
} else if (metric instanceof Timer) {
listener.onTimerRemoved(name);
} else {
throw new IllegalArgumentException("Unknown metric type: " + metric.getClass());
}
}
Given a metric set, registers them with the given prefix prepended to their names.
Params: - prefix – a name prefix
- metrics – a set of metrics
Throws: - IllegalArgumentException – if any of the names are already registered
/**
* Given a metric set, registers them with the given prefix prepended to their names.
*
* @param prefix a name prefix
* @param metrics a set of metrics
* @throws IllegalArgumentException if any of the names are already registered
*/
public void registerAll(String prefix, MetricSet metrics) throws IllegalArgumentException {
for (Map.Entry<String, Metric> entry : metrics.getMetrics().entrySet()) {
if (entry.getValue() instanceof MetricSet) {
registerAll(name(prefix, entry.getKey()), (MetricSet) entry.getValue());
} else {
register(name(prefix, entry.getKey()), entry.getValue());
}
}
}
@Override
public Map<String, Metric> getMetrics() {
return Collections.unmodifiableMap(metrics);
}
@FunctionalInterface
public interface MetricSupplier<T extends Metric> {
T newMetric();
}
A quick and easy way of capturing the notion of default metrics.
/**
* A quick and easy way of capturing the notion of default metrics.
*/
private interface MetricBuilder<T extends Metric> {
MetricBuilder<Counter> COUNTERS = new MetricBuilder<Counter>() {
@Override
public Counter newMetric() {
return new Counter();
}
@Override
public boolean isInstance(Metric metric) {
return Counter.class.isInstance(metric);
}
};
MetricBuilder<Histogram> HISTOGRAMS = new MetricBuilder<Histogram>() {
@Override
public Histogram newMetric() {
return new Histogram(new ExponentiallyDecayingReservoir());
}
@Override
public boolean isInstance(Metric metric) {
return Histogram.class.isInstance(metric);
}
};
MetricBuilder<Meter> METERS = new MetricBuilder<Meter>() {
@Override
public Meter newMetric() {
return new Meter();
}
@Override
public boolean isInstance(Metric metric) {
return Meter.class.isInstance(metric);
}
};
MetricBuilder<Timer> TIMERS = new MetricBuilder<Timer>() {
@Override
public Timer newMetric() {
return new Timer();
}
@Override
public boolean isInstance(Metric metric) {
return Timer.class.isInstance(metric);
}
};
T newMetric();
boolean isInstance(Metric metric);
}
}