Created
January 25, 2016 00:17
-
-
Save groves/b431a79a692bdd7e3adc to your computer and use it in GitHub Desktop.
GraphiteReporter with configurable reported values
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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); | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
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.