package kr.ac.kaist.resl.lilliput.utility; import java.io.IOException; import java.net.InetAddress; import java.net.InetSocketAddress; import java.net.UnknownHostException; import java.nio.ByteBuffer; import java.nio.channels.DatagramChannel; import java.util.Locale; /** * @author Jaewook Byun, Ph.D Student, Department of Computer Science, KAIST * @email bjw0829@kaist.ac.kr * @see This code is derived from sample code of a Web Page, although I won't disrespect someone's work, the Web Page is closed */ public class StatsdHelper { private final InetSocketAddress _address; private final DatagramChannel _channel; /** * * @param host Hostname of Statsd e.g. localhost, XXX.XXX.XX.XX * @param port Port of Statsd e.g. 8125 * @throws UnknownHostException * @throws IOException */ public StatsdHelper(String host, int port) throws UnknownHostException, IOException { this(InetAddress.getByName(host), port); } /** * * @param host InetAddress of Statsd , Use StatsdHelper(String host, int port) * @param port Port of Statsd e.g. 8125 * @throws IOException */ public StatsdHelper(InetAddress host, int port) throws IOException { _address = new InetSocketAddress(host, port); _channel = DatagramChannel.open(); } /** * * @param key Bucket Name * @param value Value * @return */ public boolean timing(String key, int value) { return timing(key, value, 1.0); } /** * * @param key Bucket Name * @param value Value * @param sampleRate Tells StatsD that this counter is being sent sampled every {sampleRate} of the time. * @return */ public boolean timing(String key, int value, double sampleRate) { return send(sampleRate, String.format(Locale.ENGLISH, "%s:%d|ms", key, value)); } /** * @see Part of implementation - Counting * @param key Bucket name, it would decrease one point * @return */ public boolean decrement(String key) { return increment(key, -1, 1.0); } /** * @see Part of implementation - Counting * @param key Bucket name, it would decrease magnitude * @param magnitude * @return */ public boolean decrement(String key, int magnitude) { return decrement(key, magnitude, 1.0); } /** * @see Part of implementation - Counting * @param key Bucket name, it would decrease magnitude AT {sampleRate} rate * @param magnitude * @param sampleRate * @return */ public boolean decrement(String key, int magnitude, double sampleRate) { magnitude = magnitude < 0 ? magnitude : -magnitude; return increment(key, magnitude, sampleRate); } /** * @see Part of implementation - Counting * @param keys names of bucket would decrease one point, respectively * @return */ public boolean decrement(String... keys) { return increment(-1, 1.0, keys); } /** * @see Part of implementation - Counting * @param magnitude * @param keys names of bucket would decrease {magnitude} point, respectively * @return */ public boolean decrement(int magnitude, String... keys) { magnitude = magnitude < 0 ? magnitude : -magnitude; return increment(magnitude, 1.0, keys); } /** * @see Part of implementation - Counting * @param magnitude * @param sampleRate * @param keys names of bucket would decrease {magnitude} point at {sampleRate} rate, respectively * @return */ public boolean decrement(int magnitude, double sampleRate, String... keys) { magnitude = magnitude < 0 ? magnitude : -magnitude; return increment(magnitude, sampleRate, keys); } /** * @see Part of implementation - Counting * @param key * @return name of bucket would increase one point */ public boolean increment(String key) { return increment(key, 1, 1.0); } /** * @see Part of implementation - Counting * @param key name of bucket would increase {magnitude} point * @return */ public boolean increment(String key, int magnitude) { return increment(key, magnitude, 1.0); } /** * @see Part of implementation - Counting * @param key name of bucket would increase {magnitude} point at {sampleRate} * @param magnitude * @param sampleRate * @return */ public boolean increment(String key, int magnitude, double sampleRate) { String stat = String.format(Locale.ENGLISH, "%s:%s|c", key, magnitude); return send(sampleRate, stat); } /** * @see Part of implementation - Counting * @param key names of bucket would increase {magnitude} point at {sampleRate} * @param magnitude * @param sampleRate * @return */ public boolean increment(int magnitude, double sampleRate, String... keys) { String[] stats = new String[keys.length]; for (int i = 0; i < keys.length; i++) { stats[i] = String.format(Locale.ENGLISH, "%s:%s|c", keys[i], magnitude); } return send(sampleRate, stats); } /** * @see Gauge * @param key Bucket name * @param magnitude If the gauge is not updated at the next flush, it will send the previous value. * @return */ public boolean gauge(String key, double magnitude) { return gauge(key, magnitude, 1.0); } public boolean gauge(String key, double magnitude, double sampleRate) { final String stat = String.format(Locale.ENGLISH, "%s:%s|g", key, magnitude); return send(sampleRate, stat); } private boolean send(double sampleRate, String... stats) { boolean retval = false; // didn't send anything if (sampleRate < 1.0) { for (String stat : stats) { stat = String.format(Locale.ENGLISH, "%s|@%f", stat, sampleRate); if (doSend(stat)) { retval = true; } } } else { for (String stat : stats) { if (doSend(stat)) { retval = true; } } } return retval; } /** * * @param stat message to Statsd * @return true (no error), false (error) */ private boolean doSend(final String stat) { try { // Prepare Message final byte[] data = stat.getBytes("utf-8"); final ByteBuffer buff = ByteBuffer.wrap(data); // Send final int nbSentBytes = _channel.send(buff, _address); if (data.length == nbSentBytes) { return true; } else { System.out.println(String.format( "Could not send entirely stat %s to host %s:%d. Only sent %d bytes out of %d bytes", stat, _address.getHostName(), _address.getPort(), nbSentBytes, data.length)); return false; } } catch (IOException e) { System.out.println(String.format("Could not send stat %s to host %s:%d", stat, _address.getHostName(), _address.getPort())); return false; } } }