diff --git a/src/main/java/com/bealetech/metrics/reporting/StatsdReporter.java b/src/main/java/com/bealetech/metrics/reporting/StatsdReporter.java index 6d4cdb2..ab74f5d 100644 --- a/src/main/java/com/bealetech/metrics/reporting/StatsdReporter.java +++ b/src/main/java/com/bealetech/metrics/reporting/StatsdReporter.java @@ -48,6 +48,9 @@ public static enum StatType { COUNTER, TIMER, GAUGE } private boolean prependNewline = false; private boolean printVMMetrics = true; + private boolean shouldTranslateTimersToGauges = false; // Statsd rewrites timers with more info, causing a potential explosion in + // the number of metrics being pushed from statsd to graphite if you have a + // lot of timers or histograms. See: https://github.com/etsy/statsd/blob/master/docs/metric_types.md#timing public interface UDPSocketProvider { DatagramSocket get() throws Exception; @@ -108,6 +111,14 @@ public void setPrintVMMetrics(boolean printVMMetrics) { this.printVMMetrics = printVMMetrics; } + public boolean isShouldTranslateTimersToGauges() { + return shouldTranslateTimersToGauges; + } + + public void setShouldTranslateTimersToGauges(boolean shouldTranslateTimersToGauges) { + this.shouldTranslateTimersToGauges = shouldTranslateTimersToGauges; + } + @Override public void run() { DatagramSocket socket = null; @@ -303,7 +314,7 @@ protected void sendData(String name, String value, StatType statType) { statTypeStr = "g"; break; case TIMER: - statTypeStr = "ms"; + statTypeStr = shouldTranslateTimersToGauges ? "g" : "ms"; break; } diff --git a/src/test/java/com/bealetech/metrics/reporting/StatsdReporterTest.java b/src/test/java/com/bealetech/metrics/reporting/StatsdReporterTest.java index e1906b6..fce7713 100644 --- a/src/test/java/com/bealetech/metrics/reporting/StatsdReporterTest.java +++ b/src/test/java/com/bealetech/metrics/reporting/StatsdReporterTest.java @@ -16,7 +16,6 @@ package com.bealetech.metrics.reporting; import com.yammer.metrics.core.*; -import com.yammer.metrics.reporting.AbstractPollingReporter; import com.yammer.metrics.stats.Snapshot; import org.junit.Before; import org.junit.Test; @@ -39,7 +38,7 @@ public class StatsdReporterTest { protected final Clock clock = mock(Clock.class); - protected AbstractPollingReporter reporter; + protected StatsdReporter reporter; protected TestMetricsRegistry registry; protected DatagramPacket packet; @@ -59,7 +58,7 @@ public void init() throws Exception { reporter = createReporter(registry, clock); } - protected AbstractPollingReporter createReporter(MetricsRegistry registry, Clock clock) throws Exception { + protected StatsdReporter createReporter(MetricsRegistry registry, Clock clock) throws Exception { final DatagramSocket socket = mock(DatagramSocket.class); final StatsdReporter.UDPSocketProvider provider = mock(StatsdReporter.UDPSocketProvider.class); when(provider.get()).thenReturn(socket); @@ -75,12 +74,22 @@ protected AbstractPollingReporter createReporter(MetricsRegistry registry, Clock } protected void assertReporterOutput(Callable action, String... expected) throws Exception { + assertReporterOutput(action, false, expected); + } + + protected void assertReporterOutput(Callable action, boolean shouldTranslateTimersToGauges, String... expected) throws Exception { // Invoke the callable to trigger (ie, mark()/inc()/etc) and return the metric final T metric = action.call(); try { // Add the metric to the registry, run the reporter and flush the result registry.add(new MetricName(Object.class, "metric"), metric); - reporter.run(); + boolean oldTimerTranslationConfig = reporter.isShouldTranslateTimersToGauges(); + try { + reporter.setShouldTranslateTimersToGauges(shouldTranslateTimersToGauges); + reporter.run(); + } finally { + reporter.setShouldTranslateTimersToGauges(oldTimerTranslationConfig); + } String packetData = new String(packet.getData()); final String[] lines = packetData.split("\r?\n|\r"); @@ -125,6 +134,26 @@ public String[] expectedTimerResult() { }; } + public String[] expectedTranslatedTimerResult() { + return new String[]{ + "prefix.java.lang.Object.metric.count:1|g", + "prefix.java.lang.Object.metric.meanRate:2.00|g", + "prefix.java.lang.Object.metric.1MinuteRate:1.00|g", + "prefix.java.lang.Object.metric.5MinuteRate:5.00|g", + "prefix.java.lang.Object.metric.15MinuteRate:15.00|g", + "prefix.java.lang.Object.metric.min:1.00|g", + "prefix.java.lang.Object.metric.max:3.00|g", + "prefix.java.lang.Object.metric.mean:2.00|g", + "prefix.java.lang.Object.metric.stddev:1.50|g", + "prefix.java.lang.Object.metric.median:0.50|g", + "prefix.java.lang.Object.metric.75percentile:0.75|g", + "prefix.java.lang.Object.metric.95percentile:0.95|g", + "prefix.java.lang.Object.metric.98percentile:0.98|g", + "prefix.java.lang.Object.metric.99percentile:0.99|g", + "prefix.java.lang.Object.metric.999percentile:1.00|g" + }; + } + public String[] expectedMeterResult() { return new String[]{ "prefix.java.lang.Object.metric.count:1|g", @@ -218,6 +247,19 @@ public Gauge call() throws Exception { expectedGaugeResult(value)); } + @Test + public final void timerWithTranslatedOutput() throws Exception { + assertReporterOutput( + new Callable() { + @Override + public Timer call() throws Exception { + return createTimer(); + } + }, + true, + expectedTranslatedTimerResult()); + } + static Counter createCounter(long count) throws Exception { final Counter mock = mock(Counter.class); when(mock.count()).thenReturn(count);