com.izettle.metrics.influxdb.InfluxDbReporter Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of metrics-influxdb Show documentation
Show all versions of metrics-influxdb Show documentation
A reporter for Metrics which announces measurements to a InfluxDb server.
package com.izettle.metrics.influxdb;
import com.codahale.metrics.*;
import com.izettle.metrics.influxdb.data.InfluxDbPoint;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
import java.util.SortedMap;
import java.util.TreeMap;
import java.util.concurrent.TimeUnit;
import java.util.regex.Pattern;
import java.util.regex.PatternSyntaxException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public final class InfluxDbReporter extends ScheduledReporter {
public static class Builder {
private final MetricRegistry registry;
private Map tags;
private TimeUnit rateUnit;
private TimeUnit durationUnit;
private MetricFilter filter;
private boolean skipIdleMetrics;
private boolean groupGauges;
private Set includeTimerFields;
private Set includeMeterFields;
private Map measurementMappings;
private Builder(MetricRegistry registry) {
this.registry = registry;
this.tags = null;
this.rateUnit = TimeUnit.SECONDS;
this.durationUnit = TimeUnit.MILLISECONDS;
this.filter = MetricFilter.ALL;
}
/**
* Add these tags to all metrics.
*
* @param tags a map containing tags common to all metrics
* @return {@code this}
*/
public Builder withTags(Map tags) {
this.tags = Collections.unmodifiableMap(tags);
return this;
}
/**
* Convert rates to the given time unit.
*
* @param rateUnit a unit of time
* @return {@code this}
*/
public Builder convertRatesTo(TimeUnit rateUnit) {
this.rateUnit = rateUnit;
return this;
}
/**
* Convert durations to the given time unit.
*
* @param durationUnit a unit of time
* @return {@code this}
*/
public Builder convertDurationsTo(TimeUnit durationUnit) {
this.durationUnit = durationUnit;
return this;
}
/**
* Only report metrics which match the given filter.
*
* @param filter a {@link MetricFilter}
* @return {@code this}
*/
public Builder filter(MetricFilter filter) {
this.filter = filter;
return this;
}
/**
* Only report metrics that have changed.
*
* @param skipIdleMetrics true/false for skipping metrics not reported
* @return {@code this}
*/
public Builder skipIdleMetrics(boolean skipIdleMetrics) {
this.skipIdleMetrics = skipIdleMetrics;
return this;
}
/**
* Group gauges by metric name with field names as everything after the last period
*
* If there is no `.', field name will be `value'. If the metric name terminates in a `.' field name will be empty.
*
*
* @param groupGauges true/false for whether to group gauges or not
* @return {@code this}
*/
public Builder groupGauges(boolean groupGauges) {
this.groupGauges = groupGauges;
return this;
}
/**
* Only report timer fields in the set.
*
* @param fields Fields to include.
* @return {@code this}
*/
public Builder includeTimerFields(Set fields) {
this.includeTimerFields = fields;
return this;
}
/**
* Only report meter fields in the set.
*
* @param fields Fields to include.
* @return {@code this}
*/
public Builder includeMeterFields(Set fields) {
this.includeMeterFields = fields;
return this;
}
/**
* Map measurement to a defined measurement name, where the key is the measurement name
* and the value is the reqex the measurement should be mapped by.
*
* @param measurementMappings
* @return {@code this}
*/
public Builder measurementMappings(Map measurementMappings) {
Map mappingsByPattern = new HashMap();
for(Map.Entry entry: measurementMappings.entrySet()) {
try {
final Pattern pattern = Pattern.compile(entry.getValue());
mappingsByPattern.put(entry.getKey(), pattern);
} catch (PatternSyntaxException e) {
throw new RuntimeException("Could not compile regex: " + entry.getValue(), e);
}
}
this.measurementMappings = mappingsByPattern;
return this;
}
public InfluxDbReporter build(final InfluxDbSender influxDb) {
return new InfluxDbReporter(
registry, influxDb, tags, rateUnit, durationUnit, filter, skipIdleMetrics,
groupGauges, includeTimerFields, includeMeterFields, measurementMappings
);
}
}
private static final Logger LOGGER = LoggerFactory.getLogger(InfluxDbReporter.class);
private final InfluxDbSender influxDb;
private final boolean skipIdleMetrics;
private final Map previousValues;
private final boolean groupGauges;
private final Set includeTimerFields;
private final Set includeMeterFields;
private final Map measurementMappings;
private InfluxDbReporter(
final MetricRegistry registry,
final InfluxDbSender influxDb,
final Map tags,
final TimeUnit rateUnit,
final TimeUnit durationUnit,
final MetricFilter filter,
final boolean skipIdleMetrics,
final boolean groupGauges,
final Set includeTimerFields,
final Set includeMeterFields,
final Map measurementMappings
) {
super(registry, "influxDb-reporter", filter, rateUnit, durationUnit);
this.influxDb = influxDb;
this.skipIdleMetrics = skipIdleMetrics;
this.groupGauges = groupGauges;
this.includeTimerFields = includeTimerFields;
this.includeMeterFields = includeMeterFields;
influxDb.setTags(tags);
this.previousValues = new TreeMap();
this.measurementMappings = measurementMappings == null ? Collections.emptyMap() : measurementMappings;
}
public static Builder forRegistry(MetricRegistry registry) {
return new Builder(registry);
}
@Override
public void report(
final SortedMap gauges,
final SortedMap counters,
final SortedMap histograms,
final SortedMap meters,
final SortedMap timers) {
final long now = System.currentTimeMillis();
try {
influxDb.flush();
reportGauges(gauges, now);
for (Map.Entry entry : counters.entrySet()) {
reportCounter(entry.getKey(), entry.getValue(), now);
}
for (Map.Entry entry : histograms.entrySet()) {
reportHistogram(entry.getKey(), entry.getValue(), now);
}
for (Map.Entry entry : meters.entrySet()) {
reportMeter(entry.getKey(), entry.getValue(), now);
}
for (Map.Entry entry : timers.entrySet()) {
reportTimer(entry.getKey(), entry.getValue(), now);
}
if (influxDb.hasSeriesData()) {
influxDb.writeData();
}
} catch (Exception e) {
LOGGER.warn("Unable to report to InfluxDB. Discarding data.", e);
}
}
private void reportGauges(SortedMap gauges, long now) {
if (groupGauges) {
Map> groupedGauges = groupGauges(gauges);
for (Map.Entry> entry : groupedGauges.entrySet()) {
reportGaugeGroup(entry.getKey(), entry.getValue(), now);
}
} else {
for (Map.Entry entry : gauges.entrySet()) {
reportGauge(entry.getKey(), entry.getValue(), now);
}
}
}
private Map> groupGauges(SortedMap gauges) {
Map> groupedGauges = new HashMap>();
for (Map.Entry entry : gauges.entrySet()) {
final String metricName;
final String fieldName;
int lastDotIndex = entry.getKey().lastIndexOf(".");
if (lastDotIndex != -1) {
metricName = entry.getKey().substring(0, lastDotIndex);
fieldName = entry.getKey().substring(lastDotIndex + 1);
} else {
// no `.` to group by in the metric name, just report the metric as is
metricName = entry.getKey();
fieldName = "value";
}
Map fields = groupedGauges.get(metricName);
if (fields == null) {
fields = new HashMap();
}
fields.put(fieldName, entry.getValue());
groupedGauges.put(metricName, fields);
}
return groupedGauges;
}
private void reportGaugeGroup(String name, Map gaugeGroup, long now) {
Map fields = new HashMap();
for (Map.Entry entry : gaugeGroup.entrySet()) {
Object gaugeValue = sanitizeGauge(entry.getValue().getValue());
if (gaugeValue != null) {
fields.put(entry.getKey(), gaugeValue);
}
}
Map tags = new HashMap();
tags.put("metricName", name);
if (!fields.isEmpty()) {
influxDb.appendPoints(
new InfluxDbPoint(
getMeasurementName(name),
tags,
String.valueOf(now),
fields));
}
}
/**
* InfluxDB does not like "NaN" for number fields, use null instead
*
* @param value the value to sanitize
* @return value, or null if value is a number and is finite
*/
private Object sanitizeGauge(Object value) {
final Object finalValue;
if (value instanceof Double && (Double.isInfinite((Double) value) || Double.isNaN((Double) value))) {
finalValue = null;
} else if (value instanceof Float && (Float.isInfinite((Float) value) || Float.isNaN((Float) value))) {
finalValue = null;
} else {
finalValue = value;
}
return finalValue;
}
private void reportTimer(String name, Timer timer, long now) {
if (canSkipMetric(name, timer)) {
return;
}
final Snapshot snapshot = timer.getSnapshot();
Map fields = new HashMap();
fields.put("count", timer.getCount());
fields.put("min", convertDuration(snapshot.getMin()));
fields.put("max", convertDuration(snapshot.getMax()));
fields.put("mean", convertDuration(snapshot.getMean()));
fields.put("stddev", convertDuration(snapshot.getStdDev()));
fields.put("p50", convertDuration(snapshot.getMedian()));
fields.put("p75", convertDuration(snapshot.get75thPercentile()));
fields.put("p95", convertDuration(snapshot.get95thPercentile()));
fields.put("p98", convertDuration(snapshot.get98thPercentile()));
fields.put("p99", convertDuration(snapshot.get99thPercentile()));
fields.put("p999", convertDuration(snapshot.get999thPercentile()));
fields.put("m1_rate", convertRate(timer.getOneMinuteRate()));
fields.put("m5_rate", convertRate(timer.getFiveMinuteRate()));
fields.put("m15_rate", convertRate(timer.getFifteenMinuteRate()));
fields.put("mean_rate", convertRate(timer.getMeanRate()));
if (includeTimerFields != null) {
fields.keySet().retainAll(includeTimerFields);
}
Map tags = new HashMap();
tags.put("metricName", name);
influxDb.appendPoints(
new InfluxDbPoint(
getMeasurementName(name),
tags,
String.valueOf(now),
fields));
}
private void reportHistogram(String name, Histogram histogram, long now) {
if (canSkipMetric(name, histogram)) {
return;
}
final Snapshot snapshot = histogram.getSnapshot();
Map fields = new HashMap();
fields.put("count", histogram.getCount());
fields.put("min", snapshot.getMin());
fields.put("max", snapshot.getMax());
fields.put("mean", snapshot.getMean());
fields.put("stddev", snapshot.getStdDev());
fields.put("p50", snapshot.getMedian());
fields.put("p75", snapshot.get75thPercentile());
fields.put("p95", snapshot.get95thPercentile());
fields.put("p98", snapshot.get98thPercentile());
fields.put("p99", snapshot.get99thPercentile());
fields.put("p999", snapshot.get999thPercentile());
Map tags = new HashMap();
tags.put("metricName", name);
influxDb.appendPoints(
new InfluxDbPoint(
getMeasurementName(name),
tags,
String.valueOf(now),
fields));
}
private void reportCounter(String name, Counter counter, long now) {
Map fields = new HashMap();
fields.put("count", counter.getCount());
Map tags = new HashMap();
tags.put("metricName", name);
influxDb.appendPoints(
new InfluxDbPoint(
getMeasurementName(name),
tags,
String.valueOf(now),
fields));
}
private void reportGauge(String name, Gauge> gauge, long now) {
Map fields = new HashMap();
Object sanitizeGauge = sanitizeGauge(gauge.getValue());
if (sanitizeGauge != null) {
Map tags = new HashMap();
tags.put("metricName", name);
fields.put("value", sanitizeGauge);
influxDb.appendPoints(
new InfluxDbPoint(
getMeasurementName(name),
tags,
String.valueOf(now),
fields));
}
}
private void reportMeter(String name, Metered meter, long now) {
if (canSkipMetric(name, meter)) {
return;
}
Map fields = new HashMap();
fields.put("count", meter.getCount());
fields.put("m1_rate", convertRate(meter.getOneMinuteRate()));
fields.put("m5_rate", convertRate(meter.getFiveMinuteRate()));
fields.put("m15_rate", convertRate(meter.getFifteenMinuteRate()));
fields.put("mean_rate", convertRate(meter.getMeanRate()));
if (includeMeterFields != null) {
fields.keySet().retainAll(includeMeterFields);
}
Map tags = new HashMap();
tags.put("metricName", name);
influxDb.appendPoints(
new InfluxDbPoint(
getMeasurementName(name),
tags,
String.valueOf(now),
fields));
}
private boolean canSkipMetric(String name, Counting counting) {
boolean isIdle = (calculateDelta(name, counting.getCount()) == 0);
if (skipIdleMetrics && !isIdle) {
previousValues.put(name, counting.getCount());
}
return skipIdleMetrics && isIdle;
}
private long calculateDelta(String name, long count) {
Long previous = previousValues.get(name);
if (previous == null) {
return -1;
}
if (count < previous) {
LOGGER.warn("Saw a non-monotonically increasing value for metric '{}'", name);
return 0;
}
return count - previous;
}
private String getMeasurementName(final String name) {
for (Map.Entry entry : measurementMappings.entrySet()) {
final Pattern pattern = entry.getValue();
if (pattern.matcher(name).matches()) {
return entry.getKey();
}
}
return name;
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy