Skip to content

Commit

Permalink
Cache Metric instances (#273)
Browse files Browse the repository at this point in the history
* Replace Map metric to a Metric class

During collection process, metrics are collected as
Map<String, Object>, but keys are known and fixed so we can replace
this map to a POJO which will be lighter in term of memory footprint
and allocation.

* cache Metric instances

Use Metric class as a cache and avoid recreating it for each
collection. Only the value is updated. For simple & complex attribute
Tags are also cached into the Metric instance.
For complex & tabular attribute, cache is done per sub attribute into
a new JMXSubAttribute abstract class
Transfer checkName into the attribute to be inserted into the Metric
instance at creation

* fix bad metric instantiantion

Co-authored-by: Jaime Fullaondo <truthbk@gmail.com>
  • Loading branch information
jpbempel and truthbk authored Feb 21, 2020
1 parent ce22836 commit c03bfff
Show file tree
Hide file tree
Showing 8 changed files with 103 additions and 124 deletions.
13 changes: 5 additions & 8 deletions src/main/java/org/datadog/jmxfetch/Instance.java
Original file line number Diff line number Diff line change
Expand Up @@ -425,14 +425,8 @@ public List<Metric> getMetrics() throws IOException {
JmxAttribute jmxAttr = it.next();
try {
List<Metric> jmxAttrMetrics = jmxAttr.getMetrics();
for (Metric m : jmxAttrMetrics) {
m.setCheckName(this.checkName);
metrics.add(m);
}

if (this.failingAttributes.contains(jmxAttr)) {
this.failingAttributes.remove(jmxAttr);
}
metrics.addAll(jmxAttrMetrics);
this.failingAttributes.remove(jmxAttr);
} catch (IOException e) {
throw e;
} catch (Exception e) {
Expand Down Expand Up @@ -530,6 +524,7 @@ private void getMatchingAttributes() throws IOException {
attributeInfo,
beanName,
instanceName,
checkName,
connection,
tags,
cassandraAliasing,
Expand All @@ -546,6 +541,7 @@ private void getMatchingAttributes() throws IOException {
attributeInfo,
beanName,
instanceName,
checkName,
connection,
tags,
emptyDefaultHostname);
Expand All @@ -561,6 +557,7 @@ private void getMatchingAttributes() throws IOException {
attributeInfo,
beanName,
instanceName,
checkName,
connection,
tags,
emptyDefaultHostname);
Expand Down
58 changes: 34 additions & 24 deletions src/main/java/org/datadog/jmxfetch/JmxAttribute.java
Original file line number Diff line number Diff line change
Expand Up @@ -55,15 +55,17 @@ public abstract class JmxAttribute {
protected String[] tags;
private Configuration matchingConf;
private List<String> defaultTagsList;
private Boolean cassandraAliasing;
private boolean cassandraAliasing;
protected String checkName;

JmxAttribute(
MBeanAttributeInfo attribute,
ObjectName beanName,
String instanceName,
String checkName,
Connection connection,
Map<String, String> instanceTags,
Boolean cassandraAliasing,
boolean cassandraAliasing,
boolean emptyDefaultHostname) {
this.attribute = attribute;
this.beanName = beanName;
Expand All @@ -72,6 +74,7 @@ public abstract class JmxAttribute {
this.attributeName = attribute.getName();
this.beanStringName = beanName.toString();
this.cassandraAliasing = cassandraAliasing;
this.checkName = checkName;

// A bean name is formatted like that:
// org.apache.cassandra.db:type=Caches,keyspace=system,cache=HintsColumnFamilyKeyCache
Expand Down Expand Up @@ -282,15 +285,15 @@ boolean excludeMatchDomain(Configuration conf) {

Object convertMetricValue(Object metricValue, String field) {
Object converted = metricValue;

if (!getValueConversions(field).isEmpty()) {
converted = getValueConversions(field).get(metricValue);
if (converted == null && getValueConversions(field).get("default") != null) {
converted = getValueConversions(field).get("default");
}
Map<Object, Object> valueConversions = getValueConversions(field);
if (valueConversions == null || valueConversions.isEmpty()) {
return converted;
}

return converted;
converted = valueConversions.get(metricValue);
if (converted != null) {
return converted;
}
return valueConversions.get("default");
}

double castToDouble(Object metricValue, String field) {
Expand Down Expand Up @@ -460,29 +463,24 @@ public ObjectName getBeanName() {
*/
protected String getAlias(String field) {
String alias = null;

Filter include = getMatchingConf().getInclude();
Map<String, Object> conf = getMatchingConf().getConf();

String fullAttributeName =
(field != null)
? (getAttribute().getName() + "." + field)
: (getAttribute().getName());

if (include.getAttribute() instanceof Map<?, ?>) {
Map<String, Map<String, String>> attribute =
(Map<String, Map<String, String>>) (include.getAttribute());
alias = getUserAlias(attribute, fullAttributeName);
}

if (alias == null) {
if (conf.get("metric_prefix") != null) {
alias = conf.get("metric_prefix") + "." + getDomain() + "." + fullAttributeName;
} else if (getDomain().startsWith("org.apache.cassandra")) {
alias = getCassandraAlias();
}
}

// If still null - generate generic alias
if (alias == null) {
alias = "jmx." + getDomain() + "." + fullAttributeName;
Expand All @@ -491,15 +489,6 @@ protected String getAlias(String field) {
return alias;
}

/**
* Overload `getAlias` method.
*
* <p>Note: used for `JmxSimpleAttribute` only, as `field` is null.
*/
protected String getAlias() {
return getAlias(null);
}

/**
* Metric name aliasing specific to Cassandra.
*
Expand Down Expand Up @@ -611,4 +600,25 @@ protected String getDomain() {
protected Map<String, String> getBeanParameters() {
return beanParameters;
}

protected String getMetricType(String subAttribute) {
String localMetricType = null;
String name = subAttribute != null
? getAttribute().getName() + "." + subAttribute
: attributeName;
Filter include = getMatchingConf().getInclude();
if (include.getAttribute() instanceof Map<?, ?>) {
Map<String, Map<String, String>> attribute =
(Map<String, Map<String, String>>) (include.getAttribute());
Map<String, String> attrInfo = attribute.get(name);
localMetricType = attrInfo.get(METRIC_TYPE);
if (localMetricType == null) {
localMetricType = attrInfo.get("type");
}
}
if (localMetricType == null) {
localMetricType = "gauge";
}
return localMetricType;
}
}
32 changes: 6 additions & 26 deletions src/main/java/org/datadog/jmxfetch/JmxComplexAttribute.java
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
import javax.management.openmbean.CompositeData;

@SuppressWarnings("unchecked")
public class JmxComplexAttribute extends JmxAttribute {
public class JmxComplexAttribute extends JmxSubAttribute {

private List<String> subAttributeList = new ArrayList<String>();

Expand All @@ -23,13 +23,15 @@ public JmxComplexAttribute(
MBeanAttributeInfo attribute,
ObjectName beanName,
String instanceName,
String checkName,
Connection connection,
Map<String, String> instanceTags,
boolean emptyDefaultHostname) {
super(
attribute,
beanName,
instanceName,
checkName,
connection,
instanceTags,
false,
Expand Down Expand Up @@ -57,11 +59,10 @@ public List<Metric> getMetrics() throws AttributeNotFoundException, MBeanExcepti
ReflectionException, InstanceNotFoundException, IOException {
List<Metric> metrics = new ArrayList<Metric>(subAttributeList.size());
for (String subAttribute : subAttributeList) {
String alias = convertMetricName(getAlias(subAttribute));
String metricType = getMetricType(subAttribute);
String[] tags = getTags();
Metric metric = getCachedMetric(subAttribute);
double value = castToDouble(getValue(subAttribute), subAttribute);
metrics.add(new Metric(alias, metricType, value, tags));
metric.setValue(value);
metrics.add(metric);
}
return metrics;
}
Expand All @@ -84,27 +85,6 @@ private Object getValue(String subAttribute)
throw new NumberFormatException();
}

private String getMetricType(String subAttribute) {
String subAttributeName = getAttribute().getName() + "." + subAttribute;
String metricType = null;

Filter include = getMatchingConf().getInclude();
if (include.getAttribute() instanceof Map<?, ?>) {
Map<String, Map<String, String>> attribute =
(Map<String, Map<String, String>>) (include.getAttribute());
metricType = attribute.get(subAttributeName).get(METRIC_TYPE);
if (metricType == null) {
metricType = attribute.get(subAttributeName).get("type");
}
}

if (metricType == null) {
metricType = "gauge";
}

return metricType;
}

@Override
public boolean match(Configuration configuration) {
if (!matchDomain(configuration)
Expand Down
41 changes: 12 additions & 29 deletions src/main/java/org/datadog/jmxfetch/JmxSimpleAttribute.java
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
package org.datadog.jmxfetch;

import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import javax.management.AttributeNotFoundException;
Expand All @@ -13,13 +13,14 @@

@SuppressWarnings("unchecked")
public class JmxSimpleAttribute extends JmxAttribute {
private String metricType;
private Metric cachedMetric;

/** JmxSimpleAttribute constructor. */
public JmxSimpleAttribute(
MBeanAttributeInfo attribute,
ObjectName beanName,
String instanceName,
String checkName,
Connection connection,
Map<String, String> instanceTags,
boolean cassandraAliasing,
Expand All @@ -28,6 +29,7 @@ public JmxSimpleAttribute(
attribute,
beanName,
instanceName,
checkName,
connection,
instanceTags,
cassandraAliasing,
Expand All @@ -38,14 +40,15 @@ public JmxSimpleAttribute(
public List<Metric> getMetrics()
throws AttributeNotFoundException, InstanceNotFoundException, MBeanException,
ReflectionException, IOException {
String alias = getAlias();
String metricType = getMetricType();
if (cachedMetric == null) {
String alias = getAlias(null);
String metricType = getMetricType(null);
String[] tags = getTags();
cachedMetric = new Metric(alias, metricType, tags, checkName);
}
double value = castToDouble(getValue(), null);
String[] tags = getTags();
Metric metric = new Metric(alias, metricType, value, tags);
List<Metric> metrics = new ArrayList<Metric>(1);
metrics.add(metric);
return metrics;
cachedMetric.setValue(value);
return Collections.singletonList(cachedMetric);
}

/** Returns whether an attribute matches in a configuration spec. */
Expand Down Expand Up @@ -92,26 +95,6 @@ private boolean matchAttribute(Configuration configuration) {
return false;
}

private String getMetricType() {
Filter include = getMatchingConf().getInclude();
if (metricType != null) {
return metricType;
} else if (include.getAttribute() instanceof Map<?, ?>) {
Map<String, Map<String, String>> attribute =
(Map<String, Map<String, String>>) (include.getAttribute());
metricType = attribute.get(getAttributeName()).get(METRIC_TYPE);
if (metricType == null) {
metricType = attribute.get(getAttributeName()).get("type");
}
}

if (metricType == null) { // Default to gauge
metricType = "gauge";
}

return metricType;
}

private Object getValue()
throws AttributeNotFoundException, InstanceNotFoundException, MBeanException,
ReflectionException, IOException, NumberFormatException {
Expand Down
30 changes: 30 additions & 0 deletions src/main/java/org/datadog/jmxfetch/JmxSubAttribute.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
package org.datadog.jmxfetch;

import java.util.HashMap;
import java.util.Map;
import javax.management.MBeanAttributeInfo;
import javax.management.ObjectName;

abstract class JmxSubAttribute extends JmxAttribute {
private Map<String, Metric> cachedMetrics = new HashMap<String, Metric>();

public JmxSubAttribute(MBeanAttributeInfo attribute, ObjectName beanName, String instanceName,
String checkName, Connection connection, Map<String, String> instanceTags,
boolean cassandraAliasing, boolean emptyDefaultHostname) {
super(attribute, beanName, instanceName, checkName, connection, instanceTags,
cassandraAliasing, emptyDefaultHostname);
}

public Metric getCachedMetric(String name) {
Metric metric = cachedMetrics.get(name);
if (metric != null) {
return metric;
}
String alias = getAlias(name);
String metricType = getMetricType(name);
String[] tags = getTags();
metric = new Metric(alias, metricType, tags, checkName);
cachedMetrics.put(name, metric);
return metric;
}
}
Loading

0 comments on commit c03bfff

Please sign in to comment.