package org.stagemonitor.core.metrics.metrics2;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import com.codahale.metrics.Counter;
import com.codahale.metrics.ExponentiallyDecayingReservoir;
import com.codahale.metrics.Gauge;
import com.codahale.metrics.Histogram;
import com.codahale.metrics.Meter;
import com.codahale.metrics.Metric;
import com.codahale.metrics.MetricFilter;
import com.codahale.metrics.MetricRegistry;
import com.codahale.metrics.MetricSet;
import com.codahale.metrics.Timer;
/**
* A metrics registry that does not use a simple dotted metric name but a key-value pair for the metric identity
*/
public class Metric2Registry implements Metric2Set {
private final ConcurrentMap<MetricName, Metric> metrics = new ConcurrentHashMap<MetricName, Metric>();
// An internal reference to a legacy Dropwizard Metric Registry
// that we echo registration/removal of Metrics to so that it
// matches the state of our registry and supports the Dropwizard
// Metrics listener and reporting patterns.
private final MetricRegistry metricRegistry;
public Metric2Registry() {
this(new MetricRegistry());
}
public Metric2Registry(MetricRegistry metricRegistry) {
this.metricRegistry = metricRegistry;
}
/**
* 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(MetricName name, T metric) throws IllegalArgumentException {
if (metric instanceof MetricSet) {
throw new IllegalArgumentException("This metrics registry is not compatible with MetricSets. Use a Metric2Set instead.");
} else {
final Metric existing = metrics.putIfAbsent(name, metric);
if (existing != null) {
throw new IllegalArgumentException("A metric named " + name + " already exists");
}
else {
// This is a new metric - we have to register the Metric with
// the legacy Dropwizard Metric registry as
// well to support existing reports and listeners
metricRegistry.register(name.toGraphiteName(), metric);
}
}
return metric;
}
/**
* 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(Metric2Set metrics) throws IllegalArgumentException {
for (Map.Entry<MetricName, Metric> entry : metrics.getMetrics().entrySet()) {
register(entry.getKey(), entry.getValue());
}
}
/**
* Given a metric set, registers the ones not already registered.
* This method prevents IllegalArgumentException
* @param metrics a set of metrics
*/
public void registerAny(Metric2Set metrics) {
for (Map.Entry<MetricName, Metric> entry : metrics.getMetrics().entrySet()) {
registerNewMetrics(entry.getKey(), entry.getValue());
}
}
/**
* Only registers the metric if it is not already registered
* @param name the name of the metric
* @param metric the metric
*/
public void registerNewMetrics(MetricName name, Metric metric) {
final Set<MetricName> registeredNames = getNames();
if (!registeredNames.contains(name)) {
try {
register(name, metric);
} catch (IllegalArgumentException e){/* exception due to race condition*/}
}
}
/**
* 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(MetricName name) {
return getOrAdd(name, MetricBuilder.COUNTERS);
}
/**
* 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(MetricName name) {
return getOrAdd(name, MetricBuilder.HISTOGRAMS);
}
/**
* 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(MetricName name) {
return getOrAdd(name, MetricBuilder.METERS);
}
/**
* 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(MetricName name) {
return getOrAdd(name, MetricBuilder.TIMERS);
}
/**
* 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(MetricName name) {
final Metric metric = metrics.remove(name);
if (metric != null) {
// We have to unregister the Metric with the legacy Dropwizard Metric registry as
// well to support existing reports and listeners
metricRegistry.remove(name.toGraphiteName());
return true;
}
return false;
}
/**
* Returns a set of the names of all the metrics in the registry.
*
* @return the names of all the metrics
*/
public Set<MetricName> getNames() {
return Collections.unmodifiableSet(new HashSet<MetricName>(metrics.keySet()));
}
/**
* Returns a map of all the gauges in the registry and their names.
*
* @return all the gauges in the registry
*/
public Map<MetricName, Gauge> getGauges() {
return getGauges(Metric2Filter.ALL);
}
/**
* 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
*/
public Map<MetricName, Gauge> getGauges(Metric2Filter filter) {
return getMetrics(Gauge.class, filter);
}
/**
* Returns a map of all the counters in the registry and their names.
*
* @return all the counters in the registry
*/
public Map<MetricName, Counter> getCounters() {
return getCounters(Metric2Filter.ALL);
}
/**
* 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 Map<MetricName, Counter> getCounters(Metric2Filter filter) {
return getMetrics(Counter.class, filter);
}
/**
* Returns a map of all the histograms in the registry and their names.
*
* @return all the histograms in the registry
*/
public Map<MetricName, Histogram> getHistograms() {
return getHistograms(Metric2Filter.ALL);
}
/**
* 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 Map<MetricName, Histogram> getHistograms(Metric2Filter filter) {
return getMetrics(Histogram.class, filter);
}
/**
* Returns a map of all the meters in the registry and their names.
*
* @return all the meters in the registry
*/
public Map<MetricName, Meter> getMeters() {
return getMeters(Metric2Filter.ALL);
}
/**
* 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 Map<MetricName, Meter> getMeters(Metric2Filter filter) {
return getMetrics(Meter.class, filter);
}
/**
* Returns a map of all the timers in the registry and their names.
*
* @return all the timers in the registry
*/
public Map<MetricName, Timer> getTimers() {
return getTimers(Metric2Filter.ALL);
}
/**
* 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 Map<MetricName, Timer> getTimers(Metric2Filter filter) {
return getMetrics(Timer.class, filter);
}
@SuppressWarnings("unchecked")
private <T extends Metric> T getOrAdd(MetricName 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> Map<MetricName, T> getMetrics(Class<T> klass, Metric2Filter filter) {
final Map<MetricName, T> metrics = new HashMap<MetricName, T>();
for (Map.Entry<MetricName, Metric> entry : this.metrics.entrySet()) {
if (klass.isInstance(entry.getValue()) && filter.matches(entry.getKey(), entry.getValue())) {
metrics.put(entry.getKey(), (T) entry.getValue());
}
}
return Collections.unmodifiableMap(metrics);
}
@SuppressWarnings("unchecked")
protected <T extends Metric> Map<MetricName, T> getMetrics(Class<T> klass, MetricFilter filter) {
final Map<MetricName, T> metrics = new HashMap<MetricName, T>();
for (Map.Entry<MetricName, Metric> entry : this.metrics.entrySet()) {
if (klass.isInstance(entry.getValue()) && filter.matches(entry.getKey().toGraphiteName(), entry.getValue())) {
metrics.put(entry.getKey(), (T) entry.getValue());
}
}
return Collections.unmodifiableMap(metrics);
}
@Override
public Map<MetricName, Metric> getMetrics() {
return Collections.unmodifiableMap(metrics);
}
/**
* Removes all metrics which match the given filter.
*
* @param filter a filter
*/
public void removeMatching(MetricFilter filter) {
for (Map.Entry<MetricName, Metric> entry : metrics.entrySet()) {
if (filter.matches(entry.getKey().toGraphiteName(), entry.getValue())) {
remove(entry.getKey());
}
}
}
/**
* Removes all metrics which match the given filter.
*
* @param filter a filter
*/
public void removeMatching(Metric2Filter filter) {
for (Map.Entry<MetricName, Metric> entry : metrics.entrySet()) {
if (filter.matches(entry.getKey(), entry.getValue())) {
remove(entry.getKey());
}
}
}
/**
* 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);
}
/**
* Returns the wrapped legacy {@link MetricRegistry}
*
* @return the wrapped legacy {@link MetricRegistry}
*/
public MetricRegistry getMetricRegistry() {
return metricRegistry;
}
}