├── .gitignore ├── LICENSE ├── README.md ├── pom.xml └── src ├── main └── java │ └── com │ └── yammer │ └── metrics │ └── reporting │ ├── AwsHelper.java │ ├── DatadogReporter.java │ ├── HttpTransport.java │ ├── Transport.java │ └── model │ ├── DatadogCounter.java │ ├── DatadogGauge.java │ └── DatadogSeries.java └── test └── java └── com └── yammer └── metrics └── reporting ├── DatadogCounterTest.java ├── DatadogGaugeTest.java ├── DatadogReporterTest.java └── MockTransport.java /.gitignore: -------------------------------------------------------------------------------- 1 | .classpath 2 | .project 3 | .settings 4 | target 5 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Simplified BSD License 2 | 3 | Copyright (c) 2014, Vistar Media 4 | All rights reserved. 5 | 6 | Redistribution and use in source and binary forms, with or without 7 | modification, are permitted provided that the following conditions are met: 8 | 9 | * Redistributions of source code must retain the above copyright notice, 10 | this list of conditions and the following disclaimer. 11 | * Redistributions in binary form must reproduce the above copyright notice, 12 | this list of conditions and the following disclaimer in the documentation 13 | and/or other materials provided with the distribution. 14 | * Neither the name of Vistar Media nor the names of its contributors 15 | may be used to endorse or promote products derived from this software 16 | without specific prior written permission. 17 | 18 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 19 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 20 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 21 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE 22 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 23 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 24 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 25 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 26 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 27 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 28 | 29 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Metrics Datadog Reporter 2 | Simple Metrics reporter that sends The Goods to Datadog. Real person 3 | documentation pending 4 | 5 | ## Usage 6 | 7 | ~~~scala 8 | import com.yammer.metrics.reporting.DatadogReporter 9 | 10 | ... 11 | 12 | DatadogReporter.enable(15, TimeUnit.SECONDS, myDatadogKey) 13 | ~~~ 14 | 15 | ## Maven 16 | 17 | This repo is subject to change. Nuts! 18 | 19 | * Remote: https://s3.amazonaws.com/maven.vistarmedia.com/maven/snapshots 20 | * Group: com.vistarmedia 21 | * Artifact: metrics-datadog 22 | * Version: 0.0.18-SNAPSHOT 23 | -------------------------------------------------------------------------------- /pom.xml: -------------------------------------------------------------------------------- 1 | 3 | 4.0.0 4 | com.vistarmedia 5 | metrics-datadog 6 | 0.0.18-SNAPSHOT 7 | jar 8 | Datadog Metrics Support 9 | 10 | 2.0.0 11 | 2.0.1 12 | 13 | 14 | 15 | 16 | org.kuali.maven 17 | kuali-wagon-s3 18 | 1.0.22 19 | 20 | 21 | 22 | 23 | org.apache.maven.plugins 24 | maven-compiler-plugin 25 | 2.3.1 26 | 27 | 1.5 28 | 1.5 29 | 30 | 31 | 32 | 33 | 34 | 35 | com.yammer.metrics 36 | metrics-core 37 | ${metrics.version} 38 | jar 39 | compile 40 | 41 | 42 | com.ning 43 | async-http-client 44 | 1.7.4 45 | 46 | 47 | com.fasterxml.jackson.core 48 | jackson-databind 49 | ${jackson.version} 50 | 51 | 52 | org.mockito 53 | mockito-all 54 | 1.9.0 55 | test 56 | 57 | 58 | junit 59 | junit 60 | 4.10 61 | test 62 | 63 | 64 | 65 | 66 | 67 | false 68 | maven.vistarmedia.com 69 | Vistar Snapshots 70 | s3://maven.vistarmedia.com/maven/snapshots 71 | default 72 | 73 | 74 | 75 | -------------------------------------------------------------------------------- /src/main/java/com/yammer/metrics/reporting/AwsHelper.java: -------------------------------------------------------------------------------- 1 | package com.yammer.metrics.reporting; 2 | 3 | import java.io.IOException; 4 | import java.util.concurrent.Future; 5 | 6 | import com.ning.http.client.*; 7 | 8 | public class AwsHelper { 9 | 10 | public static final String url = "http://169.254.169.254/latest/meta-data/instance-id"; 11 | 12 | public static String getEc2InstanceId() throws IOException { 13 | AsyncHttpClient client = new AsyncHttpClient(); 14 | try { 15 | Future f = client.prepareGet(url).execute(); 16 | Response resp = f.get(); 17 | 18 | return resp.getResponseBody(); 19 | } catch (Throwable t) { 20 | throw new IOException(t); 21 | } 22 | } 23 | } -------------------------------------------------------------------------------- /src/main/java/com/yammer/metrics/reporting/DatadogReporter.java: -------------------------------------------------------------------------------- 1 | package com.yammer.metrics.reporting; 2 | 3 | import java.io.IOException; 4 | import java.util.Locale; 5 | import java.util.Map.Entry; 6 | import java.util.concurrent.TimeUnit; 7 | import java.util.SortedMap; 8 | 9 | import org.slf4j.Logger; 10 | import org.slf4j.LoggerFactory; 11 | 12 | import com.fasterxml.jackson.core.JsonFactory; 13 | import com.fasterxml.jackson.core.JsonGenerator; 14 | import com.fasterxml.jackson.databind.ObjectMapper; 15 | import com.yammer.metrics.Metrics; 16 | import com.yammer.metrics.core.Clock; 17 | import com.yammer.metrics.core.Counter; 18 | import com.yammer.metrics.core.Gauge; 19 | import com.yammer.metrics.core.Histogram; 20 | import com.yammer.metrics.core.Metered; 21 | import com.yammer.metrics.core.Metric; 22 | import com.yammer.metrics.core.MetricName; 23 | import com.yammer.metrics.core.MetricPredicate; 24 | import com.yammer.metrics.core.MetricProcessor; 25 | import com.yammer.metrics.core.MetricsRegistry; 26 | import com.yammer.metrics.core.Sampling; 27 | import com.yammer.metrics.core.Summarizable; 28 | import com.yammer.metrics.core.Timer; 29 | import com.yammer.metrics.core.VirtualMachineMetrics; 30 | import com.yammer.metrics.reporting.Transport.Request; 31 | import com.yammer.metrics.reporting.model.DatadogCounter; 32 | import com.yammer.metrics.reporting.model.DatadogGauge; 33 | import com.yammer.metrics.stats.Snapshot; 34 | 35 | public class DatadogReporter extends AbstractPollingReporter implements 36 | MetricProcessor { 37 | 38 | public boolean printVmMetrics = true; 39 | protected final Locale locale = Locale.US; 40 | protected final Clock clock; 41 | private final String host; 42 | protected final MetricPredicate predicate; 43 | protected final Transport transport; 44 | private static final Logger LOG = LoggerFactory 45 | .getLogger(DatadogReporter.class); 46 | private final VirtualMachineMetrics vm; 47 | 48 | private static final JsonFactory jsonFactory = new JsonFactory(); 49 | private static final ObjectMapper mapper = new ObjectMapper(jsonFactory); 50 | private JsonGenerator jsonOut; 51 | 52 | public static void enable(long period, TimeUnit unit, String apiKey) { 53 | enable(period, unit, apiKey, null); 54 | } 55 | 56 | public static void enable(long period, TimeUnit unit, String apiKey, 57 | String host) { 58 | DatadogReporter dd = new DatadogReporter(Metrics.defaultRegistry(), apiKey, 59 | host); 60 | dd.start(period, unit); 61 | } 62 | 63 | public static void enableForEc2Instance(long period, TimeUnit unit, 64 | String apiKey) throws IOException { 65 | String hostName = AwsHelper.getEc2InstanceId(); 66 | DatadogReporter dd = new DatadogReporter(Metrics.defaultRegistry(), apiKey, 67 | hostName); 68 | dd.start(period, unit); 69 | } 70 | 71 | public DatadogReporter(MetricsRegistry registry, String apiKey) { 72 | this(registry, apiKey, null); 73 | } 74 | 75 | public DatadogReporter(MetricsRegistry registry, String apiKey, String host) { 76 | this(registry, MetricPredicate.ALL, VirtualMachineMetrics.getInstance(), 77 | new HttpTransport("app.datadoghq.com", apiKey), Clock.defaultClock(), 78 | host); 79 | } 80 | 81 | public DatadogReporter(MetricsRegistry metricsRegistry, 82 | MetricPredicate predicate, VirtualMachineMetrics vm, Transport transport, 83 | Clock clock, String host) { 84 | super(metricsRegistry, "datadog-reporter"); 85 | this.vm = vm; 86 | this.transport = transport; 87 | this.predicate = predicate; 88 | this.clock = clock; 89 | this.host = host; 90 | } 91 | 92 | @Override 93 | public void run() { 94 | Request request = null; 95 | try { 96 | request = transport.prepare(); 97 | jsonOut = jsonFactory.createJsonGenerator(request.getBodyWriter()); 98 | jsonOut.writeStartObject(); 99 | jsonOut.writeFieldName("series"); 100 | jsonOut.writeStartArray(); 101 | } catch (IOException ioe) { 102 | LOG.error("Could not prepare request", ioe); 103 | return; 104 | } 105 | 106 | final long epoch = clock.time() / 1000; 107 | if (this.printVmMetrics) { 108 | pushVmMetrics(epoch); 109 | } 110 | pushRegularMetrics(epoch); 111 | 112 | try { 113 | jsonOut.writeEndArray(); 114 | jsonOut.writeEndObject(); 115 | jsonOut.flush(); 116 | request.send(); 117 | } catch (Exception e) { 118 | LOG.error("Error sending metrics", e); 119 | } 120 | } 121 | 122 | public void processCounter(MetricName name, Counter counter, Long epoch) 123 | throws Exception { 124 | pushCounter(name, counter.count(), epoch); 125 | } 126 | 127 | public void processGauge(MetricName name, Gauge gauge, Long epoch) 128 | throws Exception { 129 | pushGauge(name, (Number) gauge.value(), epoch); 130 | } 131 | 132 | public void processHistogram(MetricName name, Histogram histogram, Long epoch) 133 | throws Exception { 134 | pushSummarizable(name, histogram, epoch); 135 | pushSampling(name, histogram, epoch); 136 | } 137 | 138 | public void processMeter(MetricName name, Metered meter, Long epoch) 139 | throws Exception { 140 | pushCounter(name, meter.count(), epoch); 141 | pushGauge(name, meter.meanRate(), epoch, "mean"); 142 | pushGauge(name, meter.oneMinuteRate(), epoch, "1MinuteRate"); 143 | pushGauge(name, meter.fiveMinuteRate(), epoch, "5MinuteRate"); 144 | pushGauge(name, meter.fifteenMinuteRate(), epoch, "15MinuteRate"); 145 | } 146 | 147 | public void processTimer(MetricName name, Timer timer, Long epoch) 148 | throws Exception { 149 | processMeter(name, timer, epoch); 150 | pushSummarizable(name, timer, epoch); 151 | pushSampling(name, timer, epoch); 152 | } 153 | 154 | private void pushSummarizable(MetricName name, Summarizable summarizable, 155 | Long epoch) { 156 | pushGauge(name, summarizable.min(), epoch, "min"); 157 | pushGauge(name, summarizable.max(), epoch, "max"); 158 | pushGauge(name, summarizable.mean(), epoch, "mean"); 159 | pushGauge(name, summarizable.stdDev(), epoch, "stddev"); 160 | } 161 | 162 | private void pushSampling(MetricName name, Sampling sampling, Long epoch) { 163 | final Snapshot snapshot = sampling.getSnapshot(); 164 | pushGauge(name, snapshot.getMedian(), epoch, "median"); 165 | pushGauge(name, snapshot.get75thPercentile(), epoch, "75percentile"); 166 | pushGauge(name, snapshot.get95thPercentile(), epoch, "95percentile"); 167 | pushGauge(name, snapshot.get98thPercentile(), epoch, "98percentile"); 168 | pushGauge(name, snapshot.get99thPercentile(), epoch, "99percentile"); 169 | pushGauge(name, snapshot.get999thPercentile(), epoch, "999percentile"); 170 | } 171 | 172 | protected void pushRegularMetrics(long epoch) { 173 | for (Entry> entry : getMetricsRegistry() 174 | .groupedMetrics(predicate).entrySet()) { 175 | for (Entry subEntry : entry.getValue().entrySet()) { 176 | final Metric metric = subEntry.getValue(); 177 | if (metric != null) { 178 | try { 179 | metric.processWith(this, subEntry.getKey(), epoch); 180 | } catch (Exception e) { 181 | LOG.error("Error pushing metric", e); 182 | } 183 | } 184 | } 185 | } 186 | } 187 | 188 | protected void pushVmMetrics(long epoch) { 189 | sendGauge("jvm.memory.heap_usage", vm.heapUsage(), epoch); 190 | sendGauge("jvm.memory.non_heap_usage", vm.nonHeapUsage(), epoch); 191 | for (Entry pool : vm.memoryPoolUsage().entrySet()) { 192 | String gaugeName = String.format("jvm.memory.memory_pool_usage[pool:%s]", 193 | pool.getKey()); 194 | 195 | sendGauge(gaugeName, pool.getValue(), epoch); 196 | } 197 | 198 | pushGauge("jvm.daemon_thread_count", vm.daemonThreadCount(), epoch); 199 | pushGauge("jvm.thread_count", vm.threadCount(), epoch); 200 | pushCounter("jvm.uptime", vm.uptime(), epoch); 201 | sendGauge("jvm.fd_usage", vm.fileDescriptorUsage(), epoch); 202 | 203 | for (Entry entry : vm.threadStatePercentages() 204 | .entrySet()) { 205 | String gaugeName = String.format("jvm.thread-states[state:%s]", 206 | entry.getKey()); 207 | sendGauge(gaugeName, entry.getValue(), epoch); 208 | } 209 | 210 | for (Entry entry : vm 211 | .garbageCollectors().entrySet()) { 212 | pushGauge("jvm.gc.time", entry.getValue().getTime(TimeUnit.MILLISECONDS), epoch); 213 | pushCounter("jvm.gc.runs", entry.getValue().getRuns(), epoch); 214 | } 215 | } 216 | 217 | private void pushCounter(MetricName metricName, Long count, Long epoch, 218 | String... path) { 219 | pushCounter(sanitizeName(metricName, path), count, epoch); 220 | 221 | } 222 | 223 | private void pushCounter(String name, Long count, Long epoch) { 224 | DatadogCounter counter = new DatadogCounter(name, count, epoch, host); 225 | try { 226 | mapper.writeValue(jsonOut, counter); 227 | } catch (Exception e) { 228 | LOG.error("Error writing counter", e); 229 | } 230 | } 231 | 232 | private void pushGauge(MetricName metricName, Number count, Long epoch, 233 | String... path) { 234 | sendGauge(sanitizeName(metricName, path), count, epoch); 235 | } 236 | 237 | private void pushGauge(String name, long count, long epoch) { 238 | sendGauge(name, new Long(count), epoch); 239 | } 240 | 241 | private void sendGauge(String name, Number count, Long epoch) { 242 | DatadogGauge gauge = new DatadogGauge(name, count, epoch, host); 243 | try { 244 | mapper.writeValue(jsonOut, gauge); 245 | } catch (Exception e) { 246 | LOG.error("Error writing gauge", e); 247 | } 248 | } 249 | 250 | protected String sanitizeName(MetricName name, String... path) { 251 | final StringBuilder sb = new StringBuilder(name.getGroup()); 252 | sb.append('.'); 253 | sb.append(name.getType()).append('.'); 254 | 255 | if (name.hasScope()) { 256 | sb.append(name.getScope()).append('.'); 257 | } 258 | 259 | String[] metricParts = name.getName().split("\\["); 260 | sb.append(metricParts[0]); 261 | 262 | for (String part : path) { 263 | sb.append('.').append(part); 264 | } 265 | 266 | for (int i = 1; i < metricParts.length; i++) { 267 | sb.append('[').append(metricParts[i]); 268 | } 269 | return sb.toString(); 270 | } 271 | 272 | } 273 | -------------------------------------------------------------------------------- /src/main/java/com/yammer/metrics/reporting/HttpTransport.java: -------------------------------------------------------------------------------- 1 | package com.yammer.metrics.reporting; 2 | 3 | import java.io.ByteArrayOutputStream; 4 | import java.io.IOException; 5 | import java.io.OutputStream; 6 | 7 | import org.slf4j.Logger; 8 | import org.slf4j.LoggerFactory; 9 | 10 | import com.ning.http.client.AsyncHandler; 11 | import com.ning.http.client.AsyncHttpClient; 12 | import com.ning.http.client.AsyncHttpClient.BoundRequestBuilder; 13 | import com.ning.http.client.HttpResponseBodyPart; 14 | import com.ning.http.client.HttpResponseHeaders; 15 | import com.ning.http.client.HttpResponseStatus; 16 | 17 | public class HttpTransport implements Transport { 18 | private final String apiKey; 19 | private final AsyncHttpClient client; 20 | private final String seriesUrl; 21 | private static final Logger LOG = LoggerFactory 22 | .getLogger(HttpTransport.class); 23 | 24 | public HttpTransport(String host, String apiKey) { 25 | this.apiKey = apiKey; 26 | this.client = new AsyncHttpClient(); 27 | this.seriesUrl = String.format("https://%s/api/v1/series?api_key=%s", host, 28 | apiKey); 29 | } 30 | 31 | public static class HttpRequest implements Transport.Request { 32 | private final BoundRequestBuilder requestBuilder; 33 | private final ByteArrayOutputStream out; 34 | 35 | public HttpRequest(HttpTransport transport, String apiKey, 36 | BoundRequestBuilder requestBuilder) throws IOException { 37 | this.requestBuilder = requestBuilder; 38 | this.requestBuilder.addHeader("Content-Type", "application/json"); 39 | this.out = new ByteArrayOutputStream(); 40 | } 41 | 42 | public OutputStream getBodyWriter() { 43 | return out; 44 | } 45 | 46 | public void send() throws Exception { 47 | out.flush(); 48 | out.close(); 49 | requestBuilder.setBody(out.toByteArray()) 50 | .execute(new AsyncHandler() { 51 | 52 | public STATE onBodyPartReceived(HttpResponseBodyPart bp) 53 | throws Exception { 54 | return STATE.CONTINUE; 55 | } 56 | 57 | public Void onCompleted() throws Exception { 58 | return null; 59 | } 60 | 61 | public STATE onHeadersReceived(HttpResponseHeaders headers) 62 | throws Exception { 63 | return STATE.CONTINUE; 64 | } 65 | 66 | public STATE onStatusReceived(HttpResponseStatus arg0) 67 | throws Exception { 68 | return STATE.CONTINUE; 69 | } 70 | 71 | public void onThrowable(Throwable t) { 72 | LOG.error("Error Writing Datadog metrics", t); 73 | } 74 | 75 | }).get(); 76 | } 77 | } 78 | 79 | public HttpRequest prepare() throws IOException { 80 | BoundRequestBuilder builder = client.preparePost(seriesUrl); 81 | return new HttpRequest(this, apiKey, builder); 82 | } 83 | } 84 | -------------------------------------------------------------------------------- /src/main/java/com/yammer/metrics/reporting/Transport.java: -------------------------------------------------------------------------------- 1 | package com.yammer.metrics.reporting; 2 | 3 | import java.io.IOException; 4 | import java.io.OutputStream; 5 | 6 | public interface Transport { 7 | public Request prepare() throws IOException ; 8 | 9 | public interface Request { 10 | OutputStream getBodyWriter(); 11 | void send() throws Exception; 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /src/main/java/com/yammer/metrics/reporting/model/DatadogCounter.java: -------------------------------------------------------------------------------- 1 | package com.yammer.metrics.reporting.model; 2 | 3 | public class DatadogCounter extends DatadogSeries { 4 | 5 | public DatadogCounter(String name, Long count, Long epoch, String host) { 6 | super(name, count, epoch, host); 7 | } 8 | 9 | public String getType() { 10 | return "counter"; 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /src/main/java/com/yammer/metrics/reporting/model/DatadogGauge.java: -------------------------------------------------------------------------------- 1 | package com.yammer.metrics.reporting.model; 2 | 3 | 4 | public class DatadogGauge extends DatadogSeries { 5 | public DatadogGauge(String name, Number count, Long epoch, String host) { 6 | super(name, count, epoch, host); 7 | } 8 | 9 | public String getType() { 10 | return "gauge"; 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /src/main/java/com/yammer/metrics/reporting/model/DatadogSeries.java: -------------------------------------------------------------------------------- 1 | package com.yammer.metrics.reporting.model; 2 | 3 | import java.util.ArrayList; 4 | import java.util.List; 5 | import java.util.regex.Pattern; 6 | import java.util.regex.Matcher; 7 | 8 | import com.fasterxml.jackson.annotation.JsonInclude; 9 | import com.fasterxml.jackson.annotation.JsonInclude.Include; 10 | 11 | public abstract class DatadogSeries { 12 | abstract protected String getType(); 13 | 14 | private String name; 15 | private T count; 16 | private Long epoch; 17 | private String host; 18 | private List tags; 19 | 20 | // Expect the tags in the pattern 21 | // namespace.metricName[tag1:value1,tag2:value2,etc....] 22 | private final Pattern tagPattern = Pattern 23 | .compile("([\\w\\.]+)\\[([\\w\\W]+)\\]"); 24 | 25 | public DatadogSeries(String name, T count, Long epoch, String host) { 26 | Matcher matcher = tagPattern.matcher(name); 27 | this.tags = new ArrayList(); 28 | 29 | if (matcher.find() && matcher.groupCount() == 2) { 30 | this.name = matcher.group(1); 31 | for(String t : matcher.group(2).split("\\,")) { 32 | this.tags.add(t.replaceAll("[^a-zA-Z0-9\\:]", "")); 33 | } 34 | } else { 35 | this.name = name; 36 | } 37 | 38 | this.count = count; 39 | this.epoch = epoch; 40 | this.host = host; 41 | } 42 | 43 | @JsonInclude(Include.NON_NULL) 44 | public String getHost() { 45 | return host; 46 | } 47 | 48 | public String getMetric() { 49 | return name; 50 | } 51 | 52 | public List getTags() { 53 | return tags; 54 | } 55 | 56 | public List> getPoints() { 57 | List point = new ArrayList(); 58 | point.add(epoch); 59 | point.add(count); 60 | 61 | List> points = new ArrayList>(); 62 | points.add(point); 63 | return points; 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /src/test/java/com/yammer/metrics/reporting/DatadogCounterTest.java: -------------------------------------------------------------------------------- 1 | package com.yammer.metrics.reporting; 2 | 3 | import static org.junit.Assert.*; 4 | 5 | import java.util.List; 6 | 7 | import org.junit.Test; 8 | 9 | import com.yammer.metrics.reporting.model.DatadogCounter; 10 | 11 | public class DatadogCounterTest { 12 | 13 | @Test 14 | public void testSplitNameAndTags() { 15 | DatadogCounter counter = new DatadogCounter( 16 | "test[tag1:value1,tag2:value2,tag3:value3]", 1L, 1234L, "Test Host"); 17 | List tags = counter.getTags(); 18 | 19 | assertEquals(3, tags.size()); 20 | assertEquals("tag1:value1", tags.get(0)); 21 | assertEquals("tag2:value2", tags.get(1)); 22 | assertEquals("tag3:value3", tags.get(2)); 23 | } 24 | 25 | @Test 26 | public void testStripInvalidCharsFromTags() { 27 | DatadogCounter counter = new DatadogCounter( 28 | "test[tag1:va lue1,tag2:va .%lue2,ta %# g3:value3]", 1L, 1234L, "Test Host"); 29 | List tags = counter.getTags(); 30 | 31 | assertEquals(3, tags.size()); 32 | assertEquals("tag1:value1", tags.get(0)); 33 | assertEquals("tag2:value2", tags.get(1)); 34 | assertEquals("tag3:value3", tags.get(2)); 35 | } 36 | 37 | } 38 | -------------------------------------------------------------------------------- /src/test/java/com/yammer/metrics/reporting/DatadogGaugeTest.java: -------------------------------------------------------------------------------- 1 | package com.yammer.metrics.reporting; 2 | 3 | import static org.junit.Assert.*; 4 | 5 | import java.util.List; 6 | 7 | import org.junit.Test; 8 | 9 | import com.yammer.metrics.reporting.model.DatadogGauge; 10 | 11 | public class DatadogGaugeTest { 12 | 13 | @Test 14 | public void testSplitNameAndTags() { 15 | DatadogGauge gauge = new DatadogGauge( 16 | "test[tag1:value1,tag2:value2,tag3:value3]", 1L, 1234L, "Test Host"); 17 | List tags = gauge.getTags(); 18 | 19 | assertEquals(3, tags.size()); 20 | assertEquals("tag1:value1", tags.get(0)); 21 | assertEquals("tag2:value2", tags.get(1)); 22 | assertEquals("tag3:value3", tags.get(2)); 23 | } 24 | 25 | @Test 26 | public void testStripInvalidCharsFromTags() { 27 | DatadogGauge gauge = new DatadogGauge( 28 | "test[tag1:va lue1,tag2:va .%lue2,ta %# g3:value3]", 1L, 1234L, 29 | "Test Host"); 30 | List tags = gauge.getTags(); 31 | 32 | assertEquals(3, tags.size()); 33 | assertEquals("tag1:value1", tags.get(0)); 34 | assertEquals("tag2:value2", tags.get(1)); 35 | assertEquals("tag3:value3", tags.get(2)); 36 | } 37 | 38 | } 39 | -------------------------------------------------------------------------------- /src/test/java/com/yammer/metrics/reporting/DatadogReporterTest.java: -------------------------------------------------------------------------------- 1 | package com.yammer.metrics.reporting; 2 | 3 | import static org.junit.Assert.*; 4 | 5 | import java.io.IOException; 6 | import java.io.UnsupportedEncodingException; 7 | import java.util.HashMap; 8 | import java.util.List; 9 | import java.util.Map; 10 | import java.util.concurrent.TimeUnit; 11 | 12 | import org.junit.Before; 13 | import org.junit.Test; 14 | 15 | import com.fasterxml.jackson.core.JsonParseException; 16 | import com.fasterxml.jackson.databind.JsonMappingException; 17 | import com.fasterxml.jackson.databind.ObjectMapper; 18 | import com.yammer.metrics.core.Clock; 19 | import com.yammer.metrics.core.Counter; 20 | import com.yammer.metrics.core.Gauge; 21 | import com.yammer.metrics.core.Meter; 22 | import com.yammer.metrics.core.MetricPredicate; 23 | import com.yammer.metrics.core.MetricsRegistry; 24 | import com.yammer.metrics.core.VirtualMachineMetrics; 25 | 26 | public class DatadogReporterTest { 27 | 28 | MetricsRegistry metricsRegistry; 29 | MockTransport transport; 30 | VirtualMachineMetrics vm; 31 | Clock clock; 32 | DatadogReporter ddNoHost; 33 | DatadogReporter dd; 34 | static final MetricPredicate ALL = MetricPredicate.ALL; 35 | 36 | @Before 37 | public void setUp() { 38 | metricsRegistry = new MetricsRegistry(); 39 | transport = new MockTransport(); 40 | clock = Clock.defaultClock(); 41 | vm = VirtualMachineMetrics.getInstance(); 42 | ddNoHost = new DatadogReporter(metricsRegistry, MetricPredicate.ALL, 43 | VirtualMachineMetrics.getInstance(), transport, Clock.defaultClock(), 44 | null); 45 | 46 | dd = new DatadogReporter(metricsRegistry, MetricPredicate.ALL, 47 | VirtualMachineMetrics.getInstance(), transport, Clock.defaultClock(), 48 | "hostname"); 49 | } 50 | 51 | @SuppressWarnings("unchecked") 52 | @Test 53 | public void testBasicSend() throws JsonParseException, JsonMappingException, 54 | IOException { 55 | dd.printVmMetrics = false; 56 | 57 | Counter counter = metricsRegistry.newCounter(DatadogReporterTest.class, 58 | "my.counter"); 59 | counter.inc(); 60 | 61 | metricsRegistry.newGauge(DatadogReporterTest.class, "my.invocations", 62 | new Gauge() { 63 | private long numInovcations = 123; 64 | 65 | @Override 66 | public Long value() { 67 | return numInovcations++; 68 | } 69 | 70 | }); 71 | 72 | assertEquals(0, transport.numRequests); 73 | dd.run(); 74 | assertEquals(1, transport.numRequests); 75 | 76 | String body = new String(transport.lastRequest.getPostBody(), "UTF-8"); 77 | Map request = new ObjectMapper().readValue(body, 78 | HashMap.class); 79 | 80 | assertEquals(1, request.keySet().size()); 81 | List series = (List) request.get("series"); 82 | 83 | assertEquals(2, series.size()); 84 | Map counterEntry = (Map) series.get(0); 85 | Map gaugeEntry = (Map) series.get(1); 86 | 87 | assertEquals("com.yammer.metrics.reporting.DatadogReporterTest.my.counter", 88 | counterEntry.get("metric")); 89 | assertEquals("counter", counterEntry.get("type")); 90 | List> points = (List>) counterEntry.get("points"); 91 | assertEquals(1, points.get(0).get(1)); 92 | 93 | assertEquals( 94 | "com.yammer.metrics.reporting.DatadogReporterTest.my.invocations", 95 | gaugeEntry.get("metric")); 96 | assertEquals("gauge", gaugeEntry.get("type")); 97 | points = (List>) gaugeEntry.get("points"); 98 | assertEquals(123, points.get(0).get(1)); 99 | } 100 | 101 | @Test 102 | public void testSupplyHostname() throws UnsupportedEncodingException { 103 | Counter counter = metricsRegistry.newCounter(DatadogReporterTest.class, 104 | "my.counter"); 105 | counter.inc(); 106 | 107 | assertEquals(0, transport.numRequests); 108 | ddNoHost.run(); 109 | assertEquals(1, transport.numRequests); 110 | String noHostBody = new String(transport.lastRequest.getPostBody(), "UTF-8"); 111 | 112 | dd.run(); 113 | assertEquals(2, transport.numRequests); 114 | String hostBody = new String(transport.lastRequest.getPostBody(), "UTF-8"); 115 | 116 | assertFalse(noHostBody.indexOf("\"host\":\"hostname\"") > -1); 117 | assertTrue(hostBody.indexOf("\"host\":\"hostname\"") > -1); 118 | } 119 | 120 | @SuppressWarnings("unchecked") 121 | @Test 122 | public void testTaggedMeter() throws Throwable { 123 | Meter s = metricsRegistry.newMeter(String.class, 124 | "meter[with,tags]", "ticks", TimeUnit.SECONDS); 125 | s.mark(); 126 | 127 | ddNoHost.printVmMetrics = false; 128 | ddNoHost.run(); 129 | String body = new String(transport.lastRequest.getPostBody(), "UTF-8"); 130 | 131 | Map request = new ObjectMapper().readValue(body, 132 | HashMap.class); 133 | List series = (List) request.get("series"); 134 | 135 | for(Object o : series) { 136 | HashMap rec = (HashMap) o; 137 | List tags = (List) rec.get("tags"); 138 | String name = rec.get("metric").toString(); 139 | 140 | assertTrue(name.startsWith("java.lang.String.meter")); 141 | assertEquals("with", tags.get(0)); 142 | assertEquals("tags", tags.get(1)); 143 | } 144 | } 145 | } 146 | 147 | -------------------------------------------------------------------------------- /src/test/java/com/yammer/metrics/reporting/MockTransport.java: -------------------------------------------------------------------------------- 1 | package com.yammer.metrics.reporting; 2 | 3 | import java.io.ByteArrayOutputStream; 4 | import java.io.IOException; 5 | import java.io.OutputStream; 6 | 7 | public class MockTransport implements Transport { 8 | public MockRequest lastRequest; 9 | public int numRequests = 0; 10 | public OutputStream out; 11 | 12 | public MockTransport(OutputStream out) { 13 | this.out = out; 14 | } 15 | 16 | public MockTransport() { 17 | 18 | } 19 | 20 | public static class MockRequest implements Request { 21 | private final OutputStream out; 22 | 23 | MockRequest(OutputStream out) { 24 | if (out != null) { 25 | this.out = out; 26 | } else { 27 | this.out = new ByteArrayOutputStream(); 28 | } 29 | } 30 | 31 | public OutputStream getBodyWriter() { 32 | return out; 33 | } 34 | 35 | public void send() throws Exception { 36 | 37 | } 38 | 39 | public byte[] getPostBody() { 40 | if (out instanceof ByteArrayOutputStream) { 41 | return ((ByteArrayOutputStream) out).toByteArray(); 42 | } 43 | return null; 44 | } 45 | } 46 | 47 | public Request prepare() throws IOException { 48 | MockRequest request = new MockRequest(out); 49 | lastRequest = request; 50 | numRequests++; 51 | 52 | return lastRequest; 53 | } 54 | 55 | } 56 | --------------------------------------------------------------------------------