in hertzbeat-collector/hertzbeat-collector-collector/src/main/java/org/apache/hertzbeat/collector/dispatch/MetricsCollect.java [209:391]
public void calculateFields(Metrics metrics, CollectRep.MetricsData.Builder collectData) {
collectData.setPriority(metrics.getPriority());
List<CollectRep.Field> fieldList = new LinkedList<>();
for (Metrics.Field field : metrics.getFields()) {
CollectRep.Field.Builder fieldBuilder = CollectRep.Field.newBuilder();
fieldBuilder.setName(field.getField()).setType(field.getType()).setLabel(field.isLabel());
if (field.getUnit() != null) {
fieldBuilder.setUnit(field.getUnit());
}
fieldList.add(fieldBuilder.build());
}
collectData.addAllFields(fieldList);
List<CollectRep.ValueRow> aliasRowList = collectData.getValuesList();
if (aliasRowList == null || aliasRowList.isEmpty()) {
return;
}
collectData.clearValues();
// Preprocess calculates first
if (metrics.getCalculates() == null) {
metrics.setCalculates(Collections.emptyList());
}
// eg: database_pages=Database pages unconventional mapping
Map<String, String> fieldAliasMap = new HashMap<>(8);
Map<String, JexlExpression> fieldExpressionMap = metrics.getCalculates()
.stream()
.map(cal -> transformCal(cal, fieldAliasMap))
.filter(Objects::nonNull)
.collect(Collectors.toMap(arr -> (String) arr[0], arr -> (JexlExpression) arr[1], (oldValue, newValue) -> newValue));
if (metrics.getUnits() == null) {
metrics.setUnits(Collections.emptyList());
}
Map<String, Pair<String, String>> fieldUnitMap = metrics.getUnits()
.stream()
.map(this::transformUnit)
.filter(Objects::nonNull)
.collect(Collectors.toMap(arr -> (String) arr[0], arr -> (Pair<String, String>) arr[1], (oldValue, newValue) -> newValue));
List<Metrics.Field> fields = metrics.getFields();
List<String> aliasFields = Optional.ofNullable(metrics.getAliasFields()).orElseGet(Collections::emptyList);
Map<String, String> aliasFieldValueMap = new HashMap<>(8);
Map<String, Object> fieldValueMap = new HashMap<>(8);
Map<String, Object> stringTypefieldValueMap = new HashMap<>(8);
Map<String, String> aliasFieldUnitMap = new HashMap<>(8);
CollectRep.ValueRow.Builder realValueRowBuilder = CollectRep.ValueRow.newBuilder();
for (CollectRep.ValueRow aliasRow : aliasRowList) {
for (int aliasIndex = 0; aliasIndex < aliasFields.size(); aliasIndex++) {
String aliasFieldValue = aliasRow.getColumns(aliasIndex);
String aliasField = aliasFields.get(aliasIndex);
if (!CommonConstants.NULL_VALUE.equals(aliasFieldValue)) {
aliasFieldValueMap.put(aliasField, aliasFieldValue);
// whether the alias field is a number
CollectUtil.DoubleAndUnit doubleAndUnit = CollectUtil
.extractDoubleAndUnitFromStr(aliasFieldValue);
if (doubleAndUnit != null && doubleAndUnit.getValue() != null) {
fieldValueMap.put(aliasField, doubleAndUnit.getValue());
if (doubleAndUnit.getUnit() != null) {
aliasFieldUnitMap.put(aliasField, doubleAndUnit.getUnit());
}
} else {
fieldValueMap.put(aliasField, aliasFieldValue);
}
stringTypefieldValueMap.put(aliasField, aliasFieldValue);
} else {
fieldValueMap.put(aliasField, null);
stringTypefieldValueMap.put(aliasField, null);
}
}
for (Metrics.Field field : fields) {
String realField = field.getField();
JexlExpression expression = fieldExpressionMap.get(realField);
String value = null;
String aliasFieldUnit = null;
if (expression != null) {
try {
Map<String, Object> context;
if (CommonConstants.TYPE_STRING == field.getType()) {
context = stringTypefieldValueMap;
} else {
for (Map.Entry<String, String> unitEntry : aliasFieldUnitMap.entrySet()) {
if (expression.getSourceText().contains(unitEntry.getKey())) {
aliasFieldUnit = unitEntry.getValue();
break;
}
}
context = fieldValueMap;
}
// Also executed when valueList is empty, covering pure string assignment expressions
Object objValue = JexlExpressionRunner.evaluate(expression, context);
if (objValue != null) {
value = String.valueOf(objValue);
}
} catch (Exception e) {
log.warn("[calculates execute warning, use original value.] {}", e.getMessage());
value = Optional.ofNullable(fieldValueMap.get(expression.getSourceText()))
.map(String::valueOf)
.orElse(null);
}
} else {
// does not exist then map the alias value
String aliasField = fieldAliasMap.get(realField);
if (aliasField != null) {
value = aliasFieldValueMap.get(aliasField);
} else {
value = aliasFieldValueMap.get(realField);
}
if (value != null) {
final byte fieldType = field.getType();
if (fieldType == CommonConstants.TYPE_NUMBER) {
CollectUtil.DoubleAndUnit doubleAndUnit = CollectUtil
.extractDoubleAndUnitFromStr(value);
final Double tempValue = doubleAndUnit == null ? null : doubleAndUnit.getValue();
value = tempValue == null ? null : String.valueOf(tempValue);
aliasFieldUnit = doubleAndUnit == null ? null : doubleAndUnit.getUnit();
} else if (fieldType == CommonConstants.TYPE_TIME) {
final int tempValue;
value = (tempValue = CommonUtil.parseTimeStrToSecond(value)) == -1 ? null : String.valueOf(tempValue);
}
}
}
Pair<String, String> unitPair = fieldUnitMap.get(realField);
if (aliasFieldUnit != null) {
if (unitPair != null) {
unitPair.setLeft(aliasFieldUnit);
} else if (field.getUnit() != null && !aliasFieldUnit.equalsIgnoreCase(field.getUnit())) {
unitPair = Pair.of(aliasFieldUnit, field.getUnit());
}
}
if (value != null && unitPair != null) {
for (UnitConvert unitConvert : unitConvertList) {
if (unitConvert.checkUnit(unitPair.getLeft()) && unitConvert.checkUnit(unitPair.getRight())) {
value = unitConvert.convert(value, unitPair.getLeft(), unitPair.getRight());
}
}
}
// Handle metrics values that may have units such as 34%, 34Mb, and limit values to 4 decimal places
if (CommonConstants.TYPE_NUMBER == field.getType()) {
value = CommonUtil.parseDoubleStr(value, field.getUnit());
}
if (value == null) {
value = CommonConstants.NULL_VALUE;
}
realValueRowBuilder.addColumn(value);
}
aliasFieldValueMap.clear();
fieldValueMap.clear();
aliasFieldUnitMap.clear();
stringTypefieldValueMap.clear();
CollectRep.ValueRow realValueRow = realValueRowBuilder.build();
realValueRowBuilder.clear();
// apply filter calculation to the real value row
if (!CollectionUtils.isEmpty(metrics.getFilters())) {
Map<String, Object> contextMap = new HashMap<>(8);
for (int i = 0; i < fields.size(); i++) {
Metrics.Field field = fields.get(i);
String value = realValueRow.getColumns(i);
contextMap.put(field.getField(), value);
}
boolean isMatch = false;
for (String filterExpr : metrics.getFilters()) {
try {
JexlExpression expression = JexlExpressionRunner.compile(filterExpr);
if ((Boolean) JexlExpressionRunner.evaluate(expression, contextMap)) {
isMatch = true;
break;
}
} catch (Exception e) {
log.warn("[metrics data row filters execute warning] {}.", e.getMessage());
}
}
if (!isMatch) {
// ignore this data row
continue;
}
}
collectData.addValueRow(realValueRow);
}
}