Skip to content

Instantly share code, notes, and snippets.

@groves
Created January 25, 2016 00:17
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save groves/b431a79a692bdd7e3adc to your computer and use it in GitHub Desktop.
Save groves/b431a79a692bdd7e3adc to your computer and use it in GitHub Desktop.
GraphiteReporter with configurable reported values
import com.codahale.metrics.Clock;
import com.codahale.metrics.Counter;
import com.codahale.metrics.Gauge;
import com.codahale.metrics.Histogram;
import com.codahale.metrics.Meter;
import com.codahale.metrics.Metered;
import com.codahale.metrics.MetricFilter;
import com.codahale.metrics.MetricRegistry;
import com.codahale.metrics.ScheduledReporter;
import com.codahale.metrics.Snapshot;
import com.codahale.metrics.Timer;
import com.codahale.metrics.graphite.Graphite;
import com.codahale.metrics.graphite.GraphiteSender;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.SortedMap;
import java.util.concurrent.TimeUnit;
/**
* <b>IMPORTANT: </b> This is a copy of GraphiteReporter from io.dropwizard.metrics:metrics-graphite:3.1.2 that has been
* modified as lightly as possible to make the set of values it submits to Graphite configurable. If editing this file,
* be careful to make the changes as non-invasive as possible to make merging in new versions easier.
*
* A reporter which publishes metric values to a Graphite server.
*
* @see <a href="http://graphite.wikidot.com/">Graphite - Scalable Realtime Graphing</a>
*/
class GraphiteReporter extends ScheduledReporter {
private static class Quantile {
final String name;
public Quantile(String name, double quantile) {
this.name = name;
this.quantile = quantile;
}
final double quantile;
}
/**
* Returns a new {@link Builder} for {@link GraphiteReporter}.
*
* @param registry the registry to report
* @return a {@link Builder} instance for a {@link GraphiteReporter}
*/
public static Builder forRegistry(MetricRegistry registry) {
return new Builder(registry);
}
/**
* A builder for {@link GraphiteReporter} instances. Defaults to not using a prefix, using the
* default clock, converting rates to events/second, converting durations to milliseconds, and
* not filtering metrics.
*/
public static class Builder {
private final MetricRegistry registry;
private Clock clock;
private String prefix;
private TimeUnit rateUnit;
private TimeUnit durationUnit;
private MetricFilter filter;
private boolean reportMean;
private boolean reportMin;
private boolean reportMax;
private boolean reportStdDev;
private boolean reportCount;
private List<Quantile> quantilesToReport = new ArrayList<>();
private Builder(MetricRegistry registry) {
this.registry = registry;
this.clock = Clock.defaultClock();
this.prefix = null;
this.rateUnit = TimeUnit.SECONDS;
this.durationUnit = TimeUnit.MILLISECONDS;
this.filter = MetricFilter.ALL;
}
/**
* Use the given {@link Clock} instance for the time.
*
* @param clock a {@link Clock} instance
* @return {@code this}
*/
public Builder withClock(Clock clock) {
this.clock = clock;
return this;
}
/**
* Prefix all metric names with the given string.
*
* @param prefix the prefix for all metric names
* @return {@code this}
*/
public Builder prefixedWith(String prefix) {
this.prefix = prefix;
return this;
}
/**
* Convert rates to the given time unit.
*
* @param rateUnit a unit of time
* @return {@code this}
*/
public Builder convertRatesTo(TimeUnit rateUnit) {
this.rateUnit = rateUnit;
return this;
}
/**
* Convert durations to the given time unit.
*
* @param durationUnit a unit of time
* @return {@code this}
*/
public Builder convertDurationsTo(TimeUnit durationUnit) {
this.durationUnit = durationUnit;
return this;
}
/**
* Only report metrics which match the given filter.
*
* @param filter a {@link MetricFilter}
* @return {@code this}
*/
public Builder filter(MetricFilter filter) {
this.filter = filter;
return this;
}
public Builder reportQuantile(String name, double value) {
quantilesToReport.add(new Quantile(name, value));
return this;
}
public Builder reportMax() {
reportMax = true;
return this;
}
public Builder reportMean() {
reportMean = true;
return this;
}
public Builder reportMin() {
reportMin = true;
return this;
}
public Builder reportStdDev() {
reportStdDev = true;
return this;
}
public Builder reportCount() {
reportCount = true;
return this;
}
/**
* Builds a {@link GraphiteReporter} with the given properties, sending metrics using the
* given {@link GraphiteSender}.
*
* Present for binary compatibility
*
* @param graphite a {@link Graphite}
* @return a {@link GraphiteReporter}
*/
public GraphiteReporter build(Graphite graphite) {
return build((GraphiteSender) graphite);
}
/**
* Builds a {@link GraphiteReporter} with the given properties, sending metrics using the
* given {@link GraphiteSender}.
*
* @param graphite a {@link GraphiteSender}
* @return a {@link GraphiteReporter}
*/
public GraphiteReporter build(GraphiteSender graphite) {
return new GraphiteReporter(registry,
graphite,
clock,
prefix,
rateUnit,
durationUnit,
filter,
reportMean,
reportMin,
reportMax,
reportStdDev,
reportCount,
quantilesToReport);
}
}
private static final Logger LOGGER = LoggerFactory.getLogger(GraphiteReporter.class);
private final GraphiteSender graphite;
private final Clock clock;
private final String prefix;
private final boolean reportMean;
private final boolean reportMin;
private final boolean reportMax;
private final boolean reportStdDev;
private final boolean reportCount;
private final List<Quantile> quantilesToReport;
private GraphiteReporter(MetricRegistry registry,
GraphiteSender graphite,
Clock clock,
String prefix,
TimeUnit rateUnit,
TimeUnit durationUnit,
MetricFilter filter,
boolean reportMean,
boolean reportMin,
boolean reportMax,
boolean reportStdDev,
boolean reportCount,
List<Quantile> quantilesToReport) {
super(registry, "graphite-reporter", filter, rateUnit, durationUnit);
this.graphite = graphite;
this.clock = clock;
this.prefix = prefix;
this.reportMean = reportMean;
this.reportMin = reportMin;
this.reportMax = reportMax;
this.reportStdDev = reportStdDev;
this.reportCount = reportCount;
this.quantilesToReport = quantilesToReport;
}
@Override
public void report(SortedMap<String, Gauge> gauges,
SortedMap<String, Counter> counters,
SortedMap<String, Histogram> histograms,
SortedMap<String, Meter> meters,
SortedMap<String, Timer> timers) {
final long timestamp = clock.getTime() / 1000;
// oh it'd be lovely to use Java 7 here
try {
if (!graphite.isConnected()) {
graphite.connect();
}
for (Map.Entry<String, Gauge> entry : gauges.entrySet()) {
reportGauge(entry.getKey(), entry.getValue(), timestamp);
}
for (Map.Entry<String, Counter> entry : counters.entrySet()) {
reportCounter(entry.getKey(), entry.getValue(), timestamp);
}
for (Map.Entry<String, Histogram> entry : histograms.entrySet()) {
reportHistogram(entry.getKey(), entry.getValue(), timestamp);
}
for (Map.Entry<String, Meter> entry : meters.entrySet()) {
reportMetered(entry.getKey(), entry.getValue(), timestamp);
}
for (Map.Entry<String, Timer> entry : timers.entrySet()) {
reportTimer(entry.getKey(), entry.getValue(), timestamp);
}
graphite.flush();
} catch (IOException e) {
LOGGER.warn("Unable to report to Graphite", graphite, e);
try {
graphite.close();
} catch (IOException e1) {
LOGGER.warn("Error closing Graphite", graphite, e1);
}
}
}
@Override
public void stop() {
try {
super.stop();
} finally {
try {
graphite.close();
} catch (IOException e) {
LOGGER.debug("Error disconnecting from Graphite", graphite, e);
}
}
}
private void reportTimer(String name, Timer timer, long timestamp) throws IOException {
reportSnapshot(name, timer.getSnapshot(), timestamp);
}
private void reportMetered(String name, Metered meter, long timestamp) throws IOException {
graphite.send(prefix(name, "count"), format(meter.getCount()), timestamp);
graphite.send(prefix(name, "m1_rate"),
format(convertRate(meter.getOneMinuteRate())),
timestamp);
graphite.send(prefix(name, "m5_rate"),
format(convertRate(meter.getFiveMinuteRate())),
timestamp);
graphite.send(prefix(name, "m15_rate"),
format(convertRate(meter.getFifteenMinuteRate())),
timestamp);
graphite.send(prefix(name, "mean_rate"),
format(convertRate(meter.getMeanRate())),
timestamp);
}
private void reportHistogram(String name, Histogram histogram, long timestamp) throws IOException {
reportSnapshot(name, histogram.getSnapshot(), timestamp);
}
private void reportSnapshot(String name, Snapshot snapshot, long timestamp) throws IOException {
if (reportCount) {
graphite.send(prefix(name, "count"), format(snapshot.size()), timestamp);
}
if (reportMax) {
graphite.send(prefix(name, "max"), format(snapshot.getMax()), timestamp);
}
if (reportMean) {
graphite.send(prefix(name, "mean"), format(snapshot.getMean()), timestamp);
}
if (reportMin) {
graphite.send(prefix(name, "min"), format(snapshot.getMin()), timestamp);
}
if (reportStdDev) {
graphite.send(prefix(name, "stddev"), format(snapshot.getStdDev()), timestamp);
}
for (Quantile quantile : quantilesToReport) {
graphite.send(prefix(name, quantile.name), format(snapshot.getValue(quantile.quantile)), timestamp);
}
}
private void reportCounter(String name, Counter counter, long timestamp) throws IOException {
graphite.send(prefix(name, "count"), format(counter.getCount()), timestamp);
}
private void reportGauge(String name, Gauge gauge, long timestamp) throws IOException {
final String value = format(gauge.getValue());
if (value != null) {
graphite.send(prefix(name), value, timestamp);
}
}
private String format(Object o) {
if (o instanceof Float) {
return format(((Float) o).doubleValue());
} else if (o instanceof Double) {
return format(((Double) o).doubleValue());
} else if (o instanceof Byte) {
return format(((Byte) o).longValue());
} else if (o instanceof Short) {
return format(((Short) o).longValue());
} else if (o instanceof Integer) {
return format(((Integer) o).longValue());
} else if (o instanceof Long) {
return format(((Long) o).longValue());
}
return null;
}
private String prefix(String... components) {
return MetricRegistry.name(prefix, components);
}
private String format(long n) {
return Long.toString(n);
}
private String format(double v) {
// the Carbon plaintext format is pretty underspecified, but it seems like it just wants
// US-formatted digits
return String.format(Locale.US, "%2.2f", v);
}
}
@groves
Copy link
Author

groves commented Jan 25, 2016

Note that this doesn't include the Meter values for Timers, either. I don't find them very useful in Graphite when I have the 1 minute rate from the histogram.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment