diff --git a/src/com/googlecode/utterlyidle/statsd/BufferedMessenger.java b/src/com/googlecode/utterlyidle/statsd/BufferedMessenger.java new file mode 100644 index 00000000..952833c3 --- /dev/null +++ b/src/com/googlecode/utterlyidle/statsd/BufferedMessenger.java @@ -0,0 +1,27 @@ +package com.googlecode.utterlyidle.statsd; + +import java.io.IOException; +import java.util.List; +import java.util.concurrent.CopyOnWriteArrayList; + +import static com.googlecode.totallylazy.Sequences.sequence; + +public class BufferedMessenger implements Messenger { + private final Messenger messenger; + private final List buffer = new CopyOnWriteArrayList<>(); + + public BufferedMessenger(Messenger messenger) { + this.messenger = messenger; + } + + @Override + public void message(Iterable values) throws IOException { + buffer.addAll(sequence(values)); + } + + @Override + public void close() throws IOException { + messenger.message(buffer); + buffer.clear(); + } +} diff --git a/src/com/googlecode/utterlyidle/statsd/Messenger.java b/src/com/googlecode/utterlyidle/statsd/Messenger.java new file mode 100644 index 00000000..aea25189 --- /dev/null +++ b/src/com/googlecode/utterlyidle/statsd/Messenger.java @@ -0,0 +1,23 @@ +package com.googlecode.utterlyidle.statsd; + +import java.io.Closeable; +import java.io.IOException; + +import static com.googlecode.totallylazy.Sequences.cons; +import static com.googlecode.totallylazy.Sequences.sequence; + +public interface Messenger extends Closeable { + void message(Iterable values) throws IOException; + + default void message(String head, String... tail) throws IOException { + message(cons(head, sequence(tail))); + } + + @Override + default void close() throws IOException { + } + + static Messenger messager() throws IOException { + return value -> { }; + } +} diff --git a/src/com/googlecode/utterlyidle/statsd/StatsDClient.java b/src/com/googlecode/utterlyidle/statsd/StatsDClient.java index 3e59ac02..55c87246 100644 --- a/src/com/googlecode/utterlyidle/statsd/StatsDClient.java +++ b/src/com/googlecode/utterlyidle/statsd/StatsDClient.java @@ -1,121 +1,96 @@ package com.googlecode.utterlyidle.statsd; -import java.io.Closeable; import java.io.IOException; import java.net.InetSocketAddress; import java.net.SocketAddress; -import java.nio.channels.DatagramChannel; -import static com.googlecode.totallylazy.Bytes.bytes; import static java.lang.String.format; -import static java.nio.ByteBuffer.wrap; -/* -Taken from https://github.com/b/statsd_spec + +/** + * Taken from https://github.com/b/statsd_spec */ -public interface StatsDClient extends Closeable { +public interface StatsDClient { static StatsDClient statsDClient(String host, int port) throws IOException { return statsDClient(new InetSocketAddress(host, port)); } static StatsDClient statsDClient(SocketAddress socketAddress) throws IOException { - return statsDClient(Messager.messager(socketAddress)); + return statsDClient(UdpMessenger.udpMessager(socketAddress)); } - static StatsDClient statsDClient(final Messager messager) { - return () -> messager; + static StatsDClient statsDClient(final Messenger messenger) { + return () -> messenger; } - Messager messager(); - - /* - A gauge is an instantaneous measurement of a value, like the gas gauge in a car. It differs from a counter by being - calculated at the client rather than the server. Valid gauge values are in the range [0, 2^64^) + Messenger messager(); - :|g + /** + * A gauge is an instantaneous measurement of a value, like the gas gauge in a car. It differs from a counter by being + * calculated at the client rather than the server. Valid gauge values are in the range [0, 2^64^) + *

+ * metric name:value|g */ default void gauge(String name, long value) throws IOException { assert value >= 0; messager().message(format("%s:%d|g", name, value)); } - /* - A counter is a gauge calculated at the server. Metrics sent by the client increment or decrement the value of the - gauge rather than giving its current value. Counters may also have an associated sample rate, given as a decimal of - the number of samples per event count. For example, a sample rate of 1/10 would be exported as 0.1. Valid counter - values are in the range (-2^63^, 2^63^). - - :|c[|@] - */ + /** + * A counter is a gauge calculated at the server. Metrics sent by the client increment or decrement the value of the + * gauge rather than giving its current value. Counters may also have an associated sample rate, given as a decimal of + * the number of samples per event count. For example, a sample rate of 1/10 would be exported as 0.1. Valid counter + * values are in the range (-2^63^, 2^63^). + *

+ * metric name:value|c + */ default void counter(String name, long value) throws IOException { messager().message(format("%s:%d|c", name, value)); } + /** + * A counter is a gauge calculated at the server. Metrics sent by the client increment or decrement the value of the + * gauge rather than giving its current value. Counters may also have an associated sample rate, given as a decimal of + * the number of samples per event count. For example, a sample rate of 1/10 would be exported as 0.1. Valid counter + * values are in the range (-2^63^, 2^63^). + *

+ * metric name:value|c|@sample rate + */ default void counter(String name, long value, float sampleRate) throws IOException { messager().message(format("%s:%d|c|@%f", name, value, sampleRate)); } - /* - A timer is a measure of the number of milliseconds elapsed between a start and end time, for example the time to - complete rendering of a web page for a user. Valid timer values are in the range [0, 2^64^). - - :|ms - */ + /** + * A timer is a measure of the number of milliseconds elapsed between a start and end time, for example the time to + * complete rendering of a web page for a user. Valid timer values are in the range [0, 2^64^). + *

+ * metric name:value|ms + */ default void timer(String name, long value) throws IOException { assert value >= 0; messager().message(format("%s:%d|ms", name, value)); } - /* - A histogram is a measure of the distribution of timer values over time, calculated at the server. As the data - exported for timers and histograms is the same, this is currently an alias for a timer. Valid histogram values are - in the range [0, 2^64^). - - :|h - */ + /** + * A histogram is a measure of the distribution of timer values over time, calculated at the server. As the data + * exported for timers and histograms is the same, this is currently an alias for a timer. Valid histogram values are + * in the range [0, 2^64^). + *

+ * metric name:value|h + */ default void histogram(String name, long value) throws IOException { assert value >= 0; messager().message(format("%s:%d|h", name, value)); } - /* - A meter measures the rate of events over time, calculated at the server. They may also be thought of as - increment-only counters. Valid meter values are in the range [0, 2^64^). - - :|m + /** + * A meter measures the rate of events over time, calculated at the server. They may also be thought of as + * increment-only counters. Valid meter values are in the range [0, 2^64^). + *

+ * metric name:value|m */ default void meter(String name, long value) throws IOException { assert value >= 0; messager().message(format("%s:%d|m", name, value)); } - @Override - default void close() throws IOException { - messager().close(); - } - - interface Messager extends Closeable { - void message(String value) throws IOException; - - @Override - default void close() throws IOException{} - - static Messager messager() throws IOException { - return value -> {}; - } - - static Messager messager(SocketAddress socketAddress) throws IOException { - DatagramChannel channel = DatagramChannel.open().connect(socketAddress); - return new Messager() { - @Override - public void message(String value) throws IOException { - channel.write(wrap(bytes(value + '\n'))); - } - - @Override - public void close() throws IOException { - channel.close(); - } - }; - } - } } diff --git a/src/com/googlecode/utterlyidle/statsd/UdpMessenger.java b/src/com/googlecode/utterlyidle/statsd/UdpMessenger.java new file mode 100644 index 00000000..93237585 --- /dev/null +++ b/src/com/googlecode/utterlyidle/statsd/UdpMessenger.java @@ -0,0 +1,38 @@ +package com.googlecode.utterlyidle.statsd; + +import com.googlecode.totallylazy.LazyException; + +import java.io.IOException; +import java.net.SocketAddress; +import java.nio.channels.DatagramChannel; + +import static com.googlecode.totallylazy.Bytes.bytes; +import static com.googlecode.totallylazy.Sequences.sequence; +import static java.nio.ByteBuffer.wrap; + +public class UdpMessenger implements Messenger { + private final DatagramChannel channel; + + public UdpMessenger(DatagramChannel channel) { + this.channel = channel; + } + + + public static UdpMessenger udpMessager(SocketAddress socketAddress) { + try { + return new UdpMessenger(DatagramChannel.open().connect(socketAddress)); + } catch (IOException e) { + throw LazyException.lazyException(e); + } + } + + @Override + public void message(Iterable values) throws IOException { + channel.write(wrap(bytes(sequence(values).toString("\n")))); + } + + @Override + public void close() throws IOException { + channel.close(); + } +} diff --git a/test/com/googlecode/utterlyidle/statsd/StatsDClientTest.java b/test/com/googlecode/utterlyidle/statsd/StatsDClientTest.java index 5f003609..b3f98a5e 100644 --- a/test/com/googlecode/utterlyidle/statsd/StatsDClientTest.java +++ b/test/com/googlecode/utterlyidle/statsd/StatsDClientTest.java @@ -11,10 +11,9 @@ import static com.googlecode.totallylazy.predicates.Predicates.is; import static com.googlecode.utterlyidle.statsd.StatsDClient.statsDClient; -@Ignore("Manual test currently") public class StatsDClientTest { - List messages = new ArrayList<>(); - StatsDClient client = statsDClient(messages::add); + private List messages = new ArrayList<>(); + private StatsDClient client = statsDClient(values -> messages.addAll(sequence(values))); @Test public void supportsGauge() throws Exception { @@ -22,6 +21,30 @@ public void supportsGauge() throws Exception { assertThat(messages, is(sequence("foo.bar:1|g"))); } + @Test + public void supportsCounter() throws Exception { + client.counter("foo.bar", 1); + assertThat(messages, is(sequence("foo.bar:1|c"))); + } + + @Test + public void supportsCounterWithSample() throws Exception { + client.counter("foo.bar", 1, 0.5f); + assertThat(messages, is(sequence("foo.bar:1|c|@0.500000"))); + } + + @Test + public void supportsTimer() throws Exception { + client.timer("foo.bar", 1); + assertThat(messages, is(sequence("foo.bar:1|ms"))); + } + + @Test + public void supportsHistogram() throws Exception { + client.histogram("foo.bar", 1); + assertThat(messages, is(sequence("foo.bar:1|h"))); + } + @Test public void supportsMeter() throws Exception { client.meter("foo.bar", 1);