Skip to content

Instantly share code, notes, and snippets.

@jmason
Last active November 30, 2023 19:27
Show Gist options
  • Star 8 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save jmason/7024259 to your computer and use it in GitHub Desktop.
Save jmason/7024259 to your computer and use it in GitHub Desktop.
TimerClearingGraphiteReporter.java
import com.yammer.metrics.Metrics;
import com.yammer.metrics.core.*;
import com.yammer.metrics.reporting.GraphiteReporter;
import com.yammer.metrics.reporting.SocketProvider;
import com.yammer.metrics.stats.Snapshot;
import java.io.IOException;
import java.util.concurrent.TimeUnit;
/**
* A Metrics Reporter which sends out application metrics to a Graphite server periodically.
* After extracting metrics, each Timer's histogram and each Histogram are cleared, so that percentiles
* and max/min readings do not persist for long time periods (which they do by default in Yammer Metrics).
*
* Note: this works with Metrics 2.x; it hasn't yet been ported to 3.x. (Shouldn't be hard though,
* the GraphiteReporter hasn't changed very much! In fact 3.x makes some of this code cleaner...)
*/
public class TimerClearingGraphiteReporter extends GraphiteReporter {
/**
* Enables and starts the graphite reporter.
*
* @param period the period between successive outputs
* @param unit the time unit of {@code period}
* @param host the host name of graphite server (carbon-cache agent)
* @param port the port number on which the graphite server is listening
* @param prefix the string which is prepended to all metric names
*/
public static void enableClearingReporter(long period, TimeUnit unit,
String host, int port, String prefix) throws IOException {
final TimerClearingGraphiteReporter reporter = new TimerClearingGraphiteReporter(Metrics.defaultRegistry(),
prefix, MetricPredicate.ALL, new DefaultSocketProvider(host, port), Clock.defaultClock());
reporter.start(period, unit);
}
private TimerClearingGraphiteReporter(MetricsRegistry metricsRegistry, String prefix, MetricPredicate predicate,
SocketProvider socketProvider, Clock clock) throws IOException {
super(metricsRegistry, prefix, predicate, socketProvider, clock);
}
@Override
public void processTimer(MetricName name, Timer timer, Long epoch) throws IOException {
this.processMeter(name, timer, epoch);
// capture the values to report, in an unlocked fashion. This is still
// vulnerable to a race, since the values are not captured atomically with the
// object locked, but then, the underlying Histogram itself isn't atomic (eg. mean() works
// with sum and count separately without locking), so this is probably good enough.
// At least we're not making any system calls between capturing and clearing this time!
SummarizableSnapshot summarizableSnapshot = snapshotSummarizable(timer);
Snapshot sampleSnapshot = timer.getSnapshot();
timer.clear();
sendSnapshots(name, epoch, summarizableSnapshot, sampleSnapshot);
}
@Override
public void processHistogram(MetricName name, Histogram histogram, Long epoch) throws IOException {
SummarizableSnapshot summarizableSnapshot = snapshotSummarizable(histogram);
Snapshot sampleSnapshot = histogram.getSnapshot();
histogram.clear();
sendSnapshots(name, epoch, summarizableSnapshot, sampleSnapshot);
}
private SummarizableSnapshot snapshotSummarizable(Summarizable metric) throws IOException {
return new SummarizableSnapshot(metric.min(), metric.max(), metric.mean(), metric.stdDev());
}
private void sendSnapshots(MetricName name, Long epoch, SummarizableSnapshot summarizableSnapshot, Snapshot sampleSnapshot) throws IOException {
final String sanitizedName = this.sanitizeName(name);
sendFloat(epoch, sanitizedName, "min", summarizableSnapshot.min);
sendFloat(epoch, sanitizedName, "max", summarizableSnapshot.max);
sendFloat(epoch, sanitizedName, "mean", summarizableSnapshot.mean);
sendFloat(epoch, sanitizedName, "stddev", summarizableSnapshot.stdDev);
sendFloat(epoch, sanitizedName, "median", sampleSnapshot.getMedian());
sendFloat(epoch, sanitizedName, "75percentile", sampleSnapshot.get75thPercentile());
sendFloat(epoch, sanitizedName, "95percentile", sampleSnapshot.get95thPercentile());
sendFloat(epoch, sanitizedName, "98percentile", sampleSnapshot.get98thPercentile());
sendFloat(epoch, sanitizedName, "99percentile", sampleSnapshot.get99thPercentile());
sendFloat(epoch, sanitizedName, "999percentile", sampleSnapshot.get999thPercentile());
}
private class SummarizableSnapshot {
private final double min, max, mean, stdDev;
public SummarizableSnapshot(double min, double max, double mean, double stdDev) {
this.min = min;
this.max = max;
this.mean = mean;
this.stdDev = stdDev;
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment