
com.izettle.metrics.influxdb.utils.InfluxDbWriteObjectSerializer 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.
The newest version!
package com.izettle.metrics.influxdb.utils;
import com.izettle.metrics.influxdb.data.InfluxDbPoint;
import com.izettle.metrics.influxdb.data.InfluxDbWriteObject;
import java.text.NumberFormat;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Locale;
import java.util.Map;
import java.util.concurrent.TimeUnit;
import java.util.regex.Pattern;
public class InfluxDbWriteObjectSerializer {
private static final Pattern COMMA = Pattern.compile(",");
private static final Pattern SPACE = Pattern.compile(" ");
private static final Pattern EQUAL = Pattern.compile("=");
private static final Pattern DOUBLE_QUOTE = Pattern.compile("\"");
private static final Pattern FIELD = Pattern.compile("\\.");
private final String measurementPrefix;
public InfluxDbWriteObjectSerializer(String measurementPrefix) {
this.measurementPrefix = measurementPrefix;
}
// measurement[,tag=value,tag2=value2...] field=value[,field2=value2...] [unixnano]
/**
* calculate the lineprotocol for all Points.
*
* @return the String with newLines.
*/
public String getLineProtocolString(InfluxDbWriteObject influxDbWriteObject) {
StringBuilder stringBuilder = new StringBuilder();
for (InfluxDbPoint point : influxDbWriteObject.getPoints()) {
pointLineProtocol(point, influxDbWriteObject.getPrecision(), stringBuilder);
stringBuilder.append("\n");
}
return stringBuilder.toString();
}
/**
* calculate the line protocol for all Points - grouped with same tags and timestamp. The realMeasurement
* is what's going to be common for all measurements on the line.
*
* @return the String with newLines.
*/
public String getGroupedLineProtocolString(InfluxDbWriteObject influxDbWriteObject, String realMeasurement) {
// First develop a set of timestamps.
HashSet times = new HashSet<>();
for (InfluxDbPoint point : influxDbWriteObject.getPoints()) {
times.add(point.getTime());
}
// Write lines, one per timestamp, instead of one per point. Collect tags from one point
// as all are presumed the same.
StringBuilder stringBuilder = new StringBuilder();
for (Long time : times) {
Map fields = new HashMap<>();
Map tags = null;
for (InfluxDbPoint point : influxDbWriteObject.getPoints()) {
if (point.getTime().equals(time)) {
mergeFields(fields, point.getFields(), point.getMeasurement());
tags = point.getTags();
}
}
lineProtocol(tags, fields, realMeasurement, time, influxDbWriteObject.getPrecision(),
stringBuilder);
stringBuilder.append("\n");
}
return stringBuilder.toString();
}
/**
* Merge fields, also parsing up the field name.
*
* Rip off the first "." element from the measurement, then prepend measurement to all field keys.
* This is an ASSUMPTION of the user's naming convention, because they're using this feature. This
* is acknowledged evil based on the current design.
*
* Eg src: {temp=10,bytes=7}, measurement: confabulator.a.b.c
* dest: {confabulator.a.b.c.temp=10,confabulator.a.b.c.bytes=7}
*
* In the specific case where there is ONE field and its key is "value" (ie, the JVM gauge group),
* there is no need for its postfixed ".value" on everything, so we drop it. Another ASSUMPTION.
* TODO: (?) configurable?
*
* Eg src: {value=10}, measurement: confabulator.jvm.memory_usage.pools.Code-Cache.max
* dest: {jvm.memory_usage.pools.Code-Cache.max=10}
*/
private void mergeFields(Map dest, Map src, String measurement) {
String[] words = FIELD.split(measurement, 2);
String tail;
if (words.length == 2) {
tail = words[1];
} else {
tail = measurement;
}
for (Map.Entry field : src.entrySet()) {
if (src.entrySet().size() == 1 && field.getKey().equals("value")) {
dest.put(tail, field.getValue());
}
else {
dest.put(tail + "." + field.getKey(), field.getValue());
}
}
}
private void pointLineProtocol(InfluxDbPoint point, TimeUnit precision, StringBuilder stringBuilder) {
lineProtocol(point.getTags(), point.getFields(), point.getMeasurement(), point.getTime(),
precision, stringBuilder);
}
private void lineProtocol(Map tags, Map fields,
String measurement, Long time, TimeUnit precision, StringBuilder stringBuilder) {
stringBuilder.append(escapeMeasurement(measurementPrefix + measurement));
concatenatedTags(tags, stringBuilder);
concatenateFields(fields, stringBuilder);
formattedTime(time, precision, stringBuilder);
}
private void concatenatedTags(Map tags, StringBuilder stringBuilder) {
for (Map.Entry tag : tags.entrySet()) {
stringBuilder.append(",");
stringBuilder.append(escapeKey(tag.getKey())).append("=").append(escapeKey(tag.getValue()));
}
stringBuilder.append(" ");
}
private void concatenateFields(Map fields, StringBuilder stringBuilder) {
NumberFormat numberFormat = NumberFormat.getInstance(Locale.ENGLISH);
numberFormat.setMaximumFractionDigits(340);
numberFormat.setGroupingUsed(false);
numberFormat.setMinimumFractionDigits(1);
boolean firstField = true;
for (Map.Entry field : fields.entrySet()) {
Object value = field.getValue();
if (value instanceof Double) {
Double doubleValue = (Double) value;
if (doubleValue.isNaN() || doubleValue.isInfinite()) {
continue;
}
} else if (value instanceof Float) {
Float floatValue = (Float) value;
if (floatValue.isNaN() || floatValue.isInfinite()) {
continue;
}
}
if (!firstField) {
stringBuilder.append(",");
}
stringBuilder.append(escapeKey(field.getKey())).append("=");
firstField = false;
if (value instanceof String) {
String stringValue = (String) value;
stringBuilder.append("\"").append(escapeField(stringValue)).append("\"");
} else if (value instanceof Number) {
stringBuilder.append(numberFormat.format(value));
} else if (value instanceof Boolean) {
stringBuilder.append(value);
} else {
stringBuilder.append("\"").append(escapeField(value.toString())).append("\"");
}
}
}
private void formattedTime(Long time, TimeUnit precision, StringBuilder stringBuilder) {
if (null == time) {
time = System.currentTimeMillis();
}
stringBuilder.append(" ").append(precision.convert(time, TimeUnit.MILLISECONDS));
}
private String escapeKey(String key) {
String toBeEscaped = SPACE.matcher(key).replaceAll("\\\\ ");
toBeEscaped = COMMA.matcher(toBeEscaped).replaceAll("\\\\,");
return EQUAL.matcher(toBeEscaped).replaceAll("\\\\=");
}
private String escapeMeasurement(String key) {
String toBeEscaped = SPACE.matcher(key).replaceAll("\\\\ ");
return COMMA.matcher(toBeEscaped).replaceAll("\\\\,");
}
private String escapeField(String field) {
return DOUBLE_QUOTE.matcher(field).replaceAll("\\\"");
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy