package de.is24.util.monitoring.statsd;
import de.is24.util.monitoring.AbstractMonitorPlugin;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.net.SocketException;
import java.net.UnknownHostException;
/**
* InApplicationMonitor plugin. Forwards monitoring to a Statsd server.
*
* Enable via InApplicationMonitor.getInstance().registerPlugin(new StatsdPlugin("myStatsdHost", 1234));
*/
public class StatsdPlugin extends AbstractMonitorPlugin {
private static final Logger LOG = LoggerFactory.getLogger(StatsdPlugin.class);
private final StatsdClient delegate;
private final String uniqueName;
private double sampleRate;
private double highVolumeSampleRate;
/**
* Create a Statsd plugin with a specified host and port.
* caller needs to call register on the plugin to register it into the InApplicationMonitor
*
* @param host the host of the Statsd server.
* @param port the port of the Statsd server.
* @param appName a short application identifier to fulfill IS24 / Graphite naming scheme requirements through statsd
* @throws UnknownHostException if there is no such host as specified.
* @throws SocketException if the socket to the host could not be opened.
*/
public StatsdPlugin(String host, int port, String appName) throws UnknownHostException, SocketException {
this(host, port, appName, 1.0);
}
/**
* Create a Statsd plugin with a specified host and port.
* caller needs to call register on the plugin to register it into the InApplicationMonitor
*
* @param host statsd host name
* @param port udp port statsd is listening on
* @param appName a short application identifier to fulfill IS24 / Graphite naming scheme requirements through statsd
* @param sampleRate a default sample rate to use for all metrics handled
* @throws UnknownHostException
* @throws SocketException
*/
public StatsdPlugin(String host, int port, String appName, double sampleRate) throws UnknownHostException,
SocketException {
this(new StatsdClient(host, port, appName), getUniqeName(host, port, sampleRate), sampleRate);
}
public StatsdPlugin(String host, int port, StatsdMessageFormatter statsdMessageFormatter)
throws SocketException, UnknownHostException {
this(new StatsdClient(host, port, statsdMessageFormatter), getUniqeName(host, port, 1.0), 1.0);
}
public StatsdPlugin(String host, int port, double sampleRate, StatsdMessageFormatter statsdMessageFormatter)
throws SocketException, UnknownHostException {
this(new StatsdClient(host, port, statsdMessageFormatter), getUniqeName(host, port, sampleRate), sampleRate);
}
StatsdPlugin(StatsdClient client, String uniqeName, double sampleRate) {
this.delegate = client;
this.uniqueName = uniqeName;
if (sampleRate < 0) {
throw new IllegalArgumentException("negative sample rate not permitted");
}
this.sampleRate = sampleRate;
initHighVolumeSampleRate();
LOG.info("StatsdPlugin {} initialized", uniqueName);
}
private static String getUniqeName(final String host, final int port, final double sampleRate) {
return "StatsdPlugin_" + host + "_" + port + "_" + sampleRate;
}
@Override
public void afterRemovalNotification() {
LOG.info("StatsdPlugin {} notified of removal", uniqueName);
delegate.close();
}
private void initHighVolumeSampleRate() {
this.highVolumeSampleRate = sampleRate * 0.1;
}
@Override
public String getUniqueName() {
return uniqueName;
}
/*
Colons are used to separate values in calls to statsd,
thus they should not be part of the key
*/
private String sanitizeKey(String key) {
return key.replaceAll(":", "_");
}
@Override
public void incrementCounter(String key, int increment) {
delegate.increment(sanitizeKey(key), increment, sampleRate);
}
@Override
public void incrementHighRateCounter(String key, int increment) {
delegate.increment(sanitizeKey(key), increment, highVolumeSampleRate);
}
@Override
public void initializeCounter(String name) {
// we do not initialize counters as this makes no sense for statsd / graphite
}
@Override
public void addTimerMeasurement(String key, long timing) {
delegate.timing(sanitizeKey(key), (int) timing, sampleRate);
}
@Override
public void addSingleEventTimerMeasurement(String name, long timing) {
// we do not write rare events to statsd, as this fills the harddrive of underlying
// graphite service. with almost empty files.
}
@Override
public void initializeTimerMeasurement(String name) {
// we do not initialize Timers as this makes no sense for statsd / graphite
}
@Override
public void addHighRateTimerMeasurement(String key, long timing) {
delegate.timing(sanitizeKey(key), (int) timing, highVolumeSampleRate);
}
}