package com.signalfx.codahale.reporter;
import java.util.SortedMap;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.ThreadFactory;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.*;
import com.yammer.metrics.core.Counter;
import com.yammer.metrics.core.Gauge;
import com.yammer.metrics.core.Histogram;
import com.yammer.metrics.core.Meter;
import com.yammer.metrics.core.MetricPredicate;
import com.yammer.metrics.core.MetricsRegistry;
import com.yammer.metrics.core.Timer;
import com.yammer.metrics.core.MetricName;
import com.yammer.metrics.core.Metric;
/**
* The abstract base class for all scheduled reporters (i.e., reporters which process a registry's
* metrics periodically).
*
*/
public abstract class CustomScheduledReporter {
/**
* A simple named thread factory.
*/
@SuppressWarnings("NullableProblems")
private static class NamedThreadFactory implements ThreadFactory {
private final ThreadGroup group;
private final AtomicInteger threadNumber = new AtomicInteger(1);
private final String namePrefix;
private NamedThreadFactory(String name) {
final SecurityManager s = System.getSecurityManager();
this.group = (s != null) ? s.getThreadGroup() : Thread.currentThread().getThreadGroup();
this.namePrefix = "metrics-" + name + "-thread-";
}
@Override
public Thread newThread(Runnable r) {
final Thread t = new Thread(group, r, namePrefix + threadNumber.getAndIncrement(), 0);
t.setDaemon(true);
if (t.getPriority() != Thread.NORM_PRIORITY) {
t.setPriority(Thread.NORM_PRIORITY);
}
return t;
}
}
private final MetricsRegistry registry;
private final ScheduledExecutorService executor;
private final MetricPredicate filter;
/**
* Creates a new {@link CustomScheduledReporter} instance.
*
* @param registry the MetricsRegistry containing the metrics this
* reporter will report
* @param name the reporter's name
* @param filter the filter for which metrics to report
* @param rateUnit
* @param durationUnit
*/
protected CustomScheduledReporter(MetricsRegistry registry,
String name,
MetricPredicate filter,
TimeUnit rateUnit,
TimeUnit durationUnit) {
this.registry = registry;
this.filter = filter;
this.executor = Executors.newSingleThreadScheduledExecutor(new NamedThreadFactory(name));
}
/**
* get Metrics by class and predicate
*
* @param klass
* @param filter
* @return
*/
@SuppressWarnings("unchecked")
private <T extends Metric> SortedMap<MetricName, T> getMetrics(Class<T> klass, MetricPredicate filter) {
Map<MetricName, Metric> allMetrics = registry.allMetrics();
final TreeMap<MetricName, T> timers = new TreeMap<MetricName, T>();
for (Map.Entry<MetricName, Metric> entry : allMetrics.entrySet()) {
if (klass.isInstance(entry.getValue()) && filter.matches(entry.getKey(),
entry.getValue())) {
timers.put(entry.getKey(), (T) entry.getValue());
}
}
return Collections.unmodifiableSortedMap(timers);
}
/**
* get all Gauge metrics
* @param filter
* @return
*/
private SortedMap<MetricName, Gauge> getGauges(MetricPredicate filter) {
return getMetrics(Gauge.class, filter);
}
/**
* get all Counter metrics
* @param filter
* @return
*/
private SortedMap<MetricName, Counter> getCounters(MetricPredicate filter) {
return getMetrics(Counter.class, filter);
}
/**
* get all Histogram metrics
* @param filter
* @return
*/
private SortedMap<MetricName, Histogram> getHistograms(MetricPredicate filter) {
return getMetrics(Histogram.class, filter);
}
/**
* get all Meters metrics
* @param filter
* @return
*/
private SortedMap<MetricName, Meter> getMeters(MetricPredicate filter) {
return getMetrics(Meter.class, filter);
}
/**
* get all Timers metrics
* @param filter
* @return
*/
private SortedMap<MetricName, Timer> getTimers(MetricPredicate filter) {
return getMetrics(Timer.class, filter);
}
/**
* Starts the reporter polling at the given period.
*
* @param period the amount of time between polls
* @param unit the unit for {@code period}
*/
public void start(long period, TimeUnit unit) {
executor.scheduleAtFixedRate(new Runnable() {
@Override
public void run() {
report();
}
}, period, period, unit);
}
/**
* Stops the reporter and shuts down its thread of execution.
*/
public void stop() {
executor.shutdown();
try {
executor.awaitTermination(1, TimeUnit.SECONDS);
} catch (InterruptedException ignored) {
// do nothing
}
}
/**
* Report the current values of all metrics in the registry.
*/
public void report() {
report(getGauges(filter),
getCounters(filter),
getHistograms(filter),
getMeters(filter),
getTimers(filter));
}
/**
* Called periodically by the polling thread. Subclasses should report all the given metrics.
*
* @param gauges all of the gauges in the registry
* @param counters all of the counters in the registry
* @param histograms all of the histograms in the registry
* @param meters all of the meters in the registry
* @param timers all of the timers in the registry
*/
public abstract void report(SortedMap<MetricName, Gauge> gauges,
SortedMap<MetricName, Counter> counters,
SortedMap<MetricName, Histogram> histograms,
SortedMap<MetricName, Meter> meters,
SortedMap<MetricName, Timer> timers);
}