Get only numeric values of JMX metrics (#318)

* Use only numeric JMX metrics
* Propagate all exceptions from JmxClusterUtil.getJmxMetric to upper call
This commit is contained in:
Ildar Almakaev 2021-03-31 12:34:43 +03:00 committed by GitHub
parent aedf3c6536
commit e9a0a1af91
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
3 changed files with 58 additions and 41 deletions

View file

@ -1,11 +1,10 @@
package com.provectus.kafka.ui.util;
import com.provectus.kafka.ui.model.Metric;
import java.io.IOException;
import java.math.BigDecimal;
import java.net.MalformedURLException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.Hashtable;
import java.util.List;
@ -13,16 +12,12 @@ import java.util.Map;
import java.util.Optional;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import javax.management.AttributeNotFoundException;
import javax.management.InstanceNotFoundException;
import javax.management.MBeanAttributeInfo;
import javax.management.MBeanException;
import javax.management.MBeanServerConnection;
import javax.management.MalformedObjectNameException;
import javax.management.ObjectName;
import javax.management.ReflectionException;
import javax.management.remote.JMXConnector;
import lombok.RequiredArgsConstructor;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.pool2.KeyedObjectPool;
import org.springframework.stereotype.Component;
@ -38,12 +33,19 @@ public class JmxClusterUtil {
private static final String NAME_METRIC_FIELD = "name";
private final KeyedObjectPool<String, JMXConnector> pool;
@SneakyThrows
public List<Metric> getJmxMetrics(int jmxPort, String jmxHost) {
String jmxUrl = JMX_URL + jmxHost + ":" + jmxPort + "/" + JMX_SERVICE_TYPE;
List<Metric> result = new ArrayList<>();
JMXConnector srv = null;
JMXConnector srv;
try {
srv = pool.borrowObject(jmxUrl);
} catch (Exception e) {
log.error("Cannot get JMX connector for the pool due to: ", e);
return Collections.emptyList();
}
List<Metric> result = new ArrayList<>();
try {
MBeanServerConnection msc = srv.getMBeanServerConnection();
var jmxMetrics = msc.queryNames(null, null).stream()
.filter(q -> q.getCanonicalName().startsWith(KAFKA_SERVER_PARAM))
@ -54,50 +56,28 @@ public class JmxClusterUtil {
metric.setName(params.get(NAME_METRIC_FIELD));
metric.setCanonicalName(jmxMetric.getCanonicalName());
metric.setParams(params);
metric.setValue(getJmxMetric(jmxMetric.getCanonicalName(), msc, srv, jmxUrl));
metric.setValue(getJmxMetric(jmxMetric.getCanonicalName(), msc));
result.add(metric);
}
pool.returnObject(jmxUrl, srv);
} catch (IOException ioe) {
log.error("Cannot get jmxMetricsNames, {}", jmxUrl, ioe);
closeConnectionExceptionally(jmxUrl, srv);
} catch (Exception e) {
log.error("Cannot get JmxConnection from pool, {}", jmxUrl, e);
log.error("Cannot get jmxMetricsNames, {}", jmxUrl, e);
closeConnectionExceptionally(jmxUrl, srv);
}
return result;
}
private Map<String, BigDecimal> getJmxMetric(String canonicalName, MBeanServerConnection msc,
JMXConnector srv, String jmxUrl) {
@SneakyThrows
private Map<String, BigDecimal> getJmxMetric(String canonicalName, MBeanServerConnection msc) {
Map<String, BigDecimal> resultAttr = new HashMap<>();
try {
ObjectName name = new ObjectName(canonicalName);
var attrNames = msc.getMBeanInfo(name).getAttributes();
for (MBeanAttributeInfo attrName : attrNames) {
var value = msc.getAttribute(name, attrName.getName());
if ((value instanceof Number)
&& (!(value instanceof Double) || !((Double) value).isInfinite())) {
resultAttr.put(attrName.getName(), new BigDecimal(value.toString()));
}
ObjectName name = new ObjectName(canonicalName);
var attrNames = msc.getMBeanInfo(name).getAttributes();
for (MBeanAttributeInfo attrName : attrNames) {
var value = msc.getAttribute(name, attrName.getName());
if (NumberUtil.isNumeric(value)) {
resultAttr.put(attrName.getName(), new BigDecimal(value.toString()));
}
} catch (MalformedURLException url) {
log.error("Cannot create JmxServiceUrl from {}", jmxUrl);
closeConnectionExceptionally(jmxUrl, srv);
} catch (IOException io) {
log.error("Cannot connect to KafkaJmxServer with url {}", jmxUrl);
closeConnectionExceptionally(jmxUrl, srv);
} catch (MBeanException | AttributeNotFoundException
| InstanceNotFoundException | ReflectionException e) {
log.error("Cannot find attribute", e);
closeConnectionExceptionally(jmxUrl, srv);
} catch (MalformedObjectNameException objectNameE) {
log.error("Cannot create objectName", objectNameE);
closeConnectionExceptionally(jmxUrl, srv);
} catch (Exception e) {
log.error("Error while retrieving connection {} from pool", jmxUrl);
closeConnectionExceptionally(jmxUrl, srv);
}
return resultAttr;
}

View file

@ -0,0 +1,9 @@
package com.provectus.kafka.ui.util;
import org.apache.commons.lang3.math.NumberUtils;
public class NumberUtil {
public static boolean isNumeric(Object value) {
return value != null && NumberUtils.isCreatable(value.toString());
}
}

View file

@ -0,0 +1,28 @@
package com.provectus.kafka.ui.util;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;
class NumberUtilTest {
@Test
void shouldReturnFalseWhenNonNumeric() {
Assertions.assertFalse(NumberUtil.isNumeric(Double.POSITIVE_INFINITY));
Assertions.assertFalse(NumberUtil.isNumeric(Double.NEGATIVE_INFINITY));
Assertions.assertFalse(NumberUtil.isNumeric(Double.NaN));
Assertions.assertFalse(NumberUtil.isNumeric(null));
Assertions.assertFalse(NumberUtil.isNumeric(" "));
Assertions.assertFalse(NumberUtil.isNumeric(new Object()));
Assertions.assertFalse(NumberUtil.isNumeric("1231asd"));
}
@Test
void shouldReturnTrueWhenNumeric() {
Assertions.assertTrue(NumberUtil.isNumeric("123.45"));
Assertions.assertTrue(NumberUtil.isNumeric(123.45));
Assertions.assertTrue(NumberUtil.isNumeric(123));
Assertions.assertTrue(NumberUtil.isNumeric(-123.45));
Assertions.assertTrue(NumberUtil.isNumeric(-1e-10));
Assertions.assertTrue(NumberUtil.isNumeric(1e-10));
}
}