package com.etsy.statsd.profiler.reporter;
import com.etsy.statsd.profiler.Arguments;
import com.etsy.statsd.profiler.util.TagUtil;
import com.google.common.base.Preconditions;
import com.google.common.collect.ImmutableMap;
import org.influxdb.InfluxDB;
import org.influxdb.InfluxDBFactory;
import org.influxdb.dto.BatchPoints;
import org.influxdb.dto.Point;
import java.util.Map;
import java.util.concurrent.TimeUnit;
/**
* Reporter that sends data to InfluxDB
*
* @author Andrew Johnson
*/
public class InfluxDBReporter extends Reporter<InfluxDB> {
public static final String VALUE_COLUMN = "value";
public static final String USERNAME_ARG = "username";
public static final String PASSWORD_ARG = "password";
public static final String DATABASE_ARG = "database";
public static final String TAG_MAPPING_ARG = "tagMapping";
private String username;
private String password;
private String database;
private String tagMapping;
private final Map<String, String> tags;
public InfluxDBReporter(Arguments arguments) {
super(arguments);
String prefix = arguments.metricsPrefix;
// If we have a tag mapping it must match the number of components of the prefix
Preconditions.checkArgument(tagMapping == null || tagMapping.split("\\.").length == prefix.split("\\.").length);
tags = TagUtil.getTags(tagMapping, prefix, true);
}
/**
* Record a gauge value in InfluxDB
*
* @param key The key for the gauge
* @param value The value of the gauge
*/
@Override
public void recordGaugeValue(String key, long value) {
Map<String, Long> gauges = ImmutableMap.of(key, value);
recordGaugeValues(gauges);
}
/**
* @see #recordGaugeValue(String, long)
*/
@Override
public void recordGaugeValue(String key, double value) {
Map<String, ? extends Number> gauges = ImmutableMap.of(key, value);
recordGaugeValues(gauges);
}
/**
* Record multiple gauge values in InfluxDB
*
* @param gauges A map of gauge names to values
*/
@Override
public void recordGaugeValues(Map<String, ? extends Number> gauges) {
long time = System.currentTimeMillis();
BatchPoints batchPoints = BatchPoints.database(database).build();
for (Map.Entry<String, ? extends Number> gauge: gauges.entrySet()) {
batchPoints.point(constructPoint(time, gauge.getKey(), gauge.getValue()));
}
client.write(batchPoints);
}
/**
* InfluxDB has a rich query language and does not need the bounds metrics emitted by CPUTracingProfiler
* As such we can disable emitting these metrics
*
* @return false
*/
@Override
public boolean emitBounds() {
return false;
}
/**
*
* @param server The server to which to report data
* @param port The port on which the server is running
* @param prefix The prefix for metrics
* @return An InfluxDB client
*/
@Override
protected InfluxDB createClient(String server, int port, String prefix) {
return InfluxDBFactory.connect(String.format("http://%s:%d", server, port), username, password);
}
/**
* Handle remaining arguments
*
* @param arguments The arguments given to the profiler agent
*/
@Override
protected void handleArguments(Arguments arguments) {
username = arguments.remainingArgs.get(USERNAME_ARG);
password = arguments.remainingArgs.get(PASSWORD_ARG);
database = arguments.remainingArgs.get(DATABASE_ARG);
tagMapping = arguments.remainingArgs.get(TAG_MAPPING_ARG);
Preconditions.checkNotNull(username);
Preconditions.checkNotNull(password);
Preconditions.checkNotNull(database);
}
private Point constructPoint(long time, String key, Number value) {
Point.Builder builder = Point.measurement(key)
.time(time, TimeUnit.MILLISECONDS)
.field(VALUE_COLUMN, value);
for (Map.Entry<String, String> entry : tags.entrySet()) {
builder = builder.tag(entry.getKey(), entry.getValue());
}
return builder.build();
}
}