diff --git a/src/main/java/org/datadog/jmxfetch/JMXAttribute.java b/src/main/java/org/datadog/jmxfetch/JMXAttribute.java index e20fc418c..d5e69e36c 100644 --- a/src/main/java/org/datadog/jmxfetch/JMXAttribute.java +++ b/src/main/java/org/datadog/jmxfetch/JMXAttribute.java @@ -4,13 +4,14 @@ import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; +import java.util.concurrent.atomic.AtomicInteger; +import java.util.concurrent.atomic.AtomicLong; import java.util.HashMap; import java.util.LinkedHashMap; import java.util.LinkedList; import java.util.List; import java.util.Map; -import java.util.concurrent.atomic.AtomicInteger; -import java.util.concurrent.atomic.AtomicLong; +import java.util.regex.Matcher; import java.util.regex.Pattern; import javax.management.AttributeNotFoundException; @@ -24,6 +25,8 @@ public abstract class JMXAttribute { + protected static final String ALIAS = "alias"; + protected static final String METRIC_TYPE = "metric_type"; private final static Logger LOGGER = Logger.getLogger(JMXAttribute.class.getName()); private static final List EXCLUDED_BEAN_PARAMS = Arrays.asList("domain", "domain_regex", "bean_name", "bean", "bean_regex", "attribute"); private static final String FIRST_CAP_PATTERN = "(.)([A-Z][a-z]+)"; @@ -31,6 +34,7 @@ public abstract class JMXAttribute { private static final String METRIC_REPLACEMENT = "([^a-zA-Z0-9_.]+)|(^[^a-zA-Z]+)"; private static final String DOT_UNDERSCORE = "_*\\._*"; protected static final String CASSANDRA_DOMAIN = "org.apache.cassandra.metrics"; + private MBeanAttributeInfo attribute; private Connection connection; private ObjectName beanName; @@ -351,6 +355,108 @@ MBeanAttributeInfo getAttribute() { return attribute; } + public ObjectName getBeanName() { + return beanName; + } + + /** + * Get attribute alias. + * + * In order, tries to: + * * Use `alias_match` to generate an alias with a regular expression + * * Use `alias` directly + * * Create an generic alias prefixed with user's `metric_prefix` preference or default to `jmx` + * + * Argument(s): + * * (Optional) `field` + * `Null` for `JMXSimpleAttribute`. + */ + protected String getAlias(String field) { + String alias = null; + + Filter include = getMatchingConf().getInclude(); + LinkedHashMap conf = getMatchingConf().getConf(); + + String fullAttributeName =(field!=null)?(getAttribute().getName() + "." + field):(getAttribute().getName()); + + if (include.getAttribute() instanceof LinkedHashMap) { + LinkedHashMap> attribute = (LinkedHashMap>) (include.getAttribute()); + alias = getUserAlias(attribute, fullAttributeName); + } else 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; + } + alias = convertMetricName(alias); + return alias; + } + + /** + * Metric name aliasing specific to Cassandra. + * + * * (Default) `cassandra_aliasing` == False. + * Legacy aliasing: drop `org.apache` prefix. + * * `cassandra_aliasing` == True + * Comply with CASSANDRA-4009 + * + * More information: https://issues.apache.org/jira/browse/CASSANDRA-4009 + */ + private String getCassandraAlias() { + if (renameCassandraMetrics()) { + Map beanParameters = getBeanParameters(); + String metricName = beanParameters.get("name"); + String attributeName = getAttributeName(); + if (attributeName.equals("Value")) { + return "cassandra." + metricName; + } + return "cassandra." + metricName + "." + attributeName; + } + //Deprecated Cassandra metric. Remove domain prefix. + return getDomain().replace("org.apache.", "") + "." + getAttributeName(); + } + + /** + * Retrieve user defined alias. Substitute regular expression named groups. + * + * Example: + * ``` + * bean: org.datadog.jmxfetch.test:foo=Bar,qux=Baz + * attribute: + * toto: + * alias: my.metric.$foo.$attribute + * ``` + * returns a metric name `my.metric.bar.toto` + */ + private String getUserAlias(LinkedHashMap> attribute, String fullAttributeName){ + String alias = attribute.get(fullAttributeName).get(ALIAS); + + // Bean parameters + for (Map.Entry param : beanParameters.entrySet()) { + alias = alias.replace("$" + param.getKey(), param.getValue()); + } + + // Attribute & domain + alias = alias.replace("$attribute", fullAttributeName); + alias = alias.replace("$domain", domain); + + return alias; + } + + /** + * Overload `getAlias` method. + * + * Note: used for `JMXSimpleAttribute` only, as `field` is null. + */ + protected String getAlias(){ + return getAlias(null); + } + + @SuppressWarnings("unchecked") protected String[] getTags() { if(tags != null) { diff --git a/src/main/java/org/datadog/jmxfetch/JMXComplexAttribute.java b/src/main/java/org/datadog/jmxfetch/JMXComplexAttribute.java index bafc1b44e..9d414ea67 100644 --- a/src/main/java/org/datadog/jmxfetch/JMXComplexAttribute.java +++ b/src/main/java/org/datadog/jmxfetch/JMXComplexAttribute.java @@ -19,8 +19,6 @@ @SuppressWarnings("unchecked") public class JMXComplexAttribute extends JMXAttribute { - public static final String ALIAS = "alias"; - public static final String METRIC_TYPE = "metric_type"; private HashMap> subAttributeList; public JMXComplexAttribute(MBeanAttributeInfo attribute, ObjectName beanName, String instanceName, @@ -112,20 +110,6 @@ private Object getMetricType(String subAttribute) { return metricType; } - private String getAlias(String subAttribute) { - String subAttributeName = getAttribute().getName() + "." + subAttribute; - - Filter include = getMatchingConf().getInclude(); - LinkedHashMap conf = getMatchingConf().getConf(); - if (include.getAttribute() instanceof LinkedHashMap) { - return ((LinkedHashMap>) (include.getAttribute())).get(subAttributeName).get(ALIAS); - } else if (conf.get("metric_prefix") != null) { - return conf.get("metric_prefix") + "." + getDomain() + "." + subAttributeName; - } - return "jmx." + getDomain() + "." + subAttributeName; - } - - @Override public boolean match(Configuration configuration) { if (!matchDomain(configuration) diff --git a/src/main/java/org/datadog/jmxfetch/JMXSimpleAttribute.java b/src/main/java/org/datadog/jmxfetch/JMXSimpleAttribute.java index f6d848e94..508a0c110 100644 --- a/src/main/java/org/datadog/jmxfetch/JMXSimpleAttribute.java +++ b/src/main/java/org/datadog/jmxfetch/JMXSimpleAttribute.java @@ -16,8 +16,6 @@ @SuppressWarnings("unchecked") public class JMXSimpleAttribute extends JMXAttribute { - - private String alias; private String metricType; public JMXSimpleAttribute(MBeanAttributeInfo attribute, ObjectName beanName, String instanceName, @@ -39,7 +37,6 @@ public LinkedList> getMetrics() throws AttributeNotFound return metrics; } - public boolean match(Configuration configuration) { return matchDomain(configuration) && matchBean(configuration) @@ -82,49 +79,13 @@ private boolean matchAttribute(Configuration configuration) { return false; } - private String getAlias() { - Filter include = getMatchingConf().getInclude(); - LinkedHashMap conf = getMatchingConf().getConf(); - if (alias != null) { - return alias; - } else if (include.getAttribute() instanceof LinkedHashMap) { - LinkedHashMap> attribute = (LinkedHashMap>) (include.getAttribute()); - alias = attribute.get(getAttribute().getName()).get("alias"); - } else if (conf.get("metric_prefix") != null) { - alias = conf.get("metric_prefix") + "." + getDomain() + "." + getAttributeName(); - } else if (getDomain().startsWith("org.apache.cassandra")) { - alias = getCassandraAlias(); - } - - //If still null - generate generic alias, - if (alias == null) { - alias = "jmx." + getDomain() + "." + getAttributeName(); - } - alias = convertMetricName(alias); - return alias; - } - - private String getCassandraAlias() { - if (renameCassandraMetrics()) { - Map beanParameters = getBeanParameters(); - String metricName = beanParameters.get("name"); - String attributeName = getAttributeName(); - if (attributeName.equals("Value")) { - return "cassandra." + metricName; - } - return "cassandra." + metricName + "." + attributeName; - } - //Deprecated Cassandra metric. Remove domain prefix. - return getDomain().replace("org.apache.", "") + "." + getAttributeName(); - } - private String getMetricType() { Filter include = getMatchingConf().getInclude(); if (metricType != null) { return metricType; } else if (include.getAttribute() instanceof LinkedHashMap) { LinkedHashMap> attribute = (LinkedHashMap>) (include.getAttribute()); - metricType = attribute.get(getAttributeName()).get("metric_type"); + metricType = attribute.get(getAttributeName()).get(METRIC_TYPE); if (metricType == null) { metricType = attribute.get(getAttributeName()).get("type"); } diff --git a/src/test/java/org/datadog/jmxfetch/TestApp.java b/src/test/java/org/datadog/jmxfetch/TestApp.java index f381f2c51..d75da2c37 100644 --- a/src/test/java/org/datadog/jmxfetch/TestApp.java +++ b/src/test/java/org/datadog/jmxfetch/TestApp.java @@ -46,10 +46,39 @@ public void testBeanTags() throws Exception { assertMetric("this.is.100", tags, 6); } + /** + * Generate metric aliases from a `alias_match` regular expression. + */ + @Test + public void testRegexpAliasing() throws Exception { + // Expose MBeans + registerMBean(new SimpleTestJavaApp(), "org.datadog.jmxfetch.test:foo=Bar,qux=Baz"); + initApplication("jmx_alias_match.yaml"); + + // Collect metrics + run(); + LinkedList> metrics = getMetrics(); + + // Assertions + + // 15 metrics = 13 from `java.lang` + 2 from the user configuration file + assertEquals(15, metrics.size()); + + // Metric aliases are generated from `alias_match` + List tags = Arrays.asList( + "jmx_domain:org.datadog.jmxfetch.test", + "instance:jmx_test_instance", + "foo:Bar", + "qux:Baz" + ); + + assertMetric("this.is.100.bar.baz", tags, 4); + assertMetric("org.datadog.jmxfetch.test.baz.hashmap.thisis0", tags, 4); + } /** * Check JMXFetch Cassandra metric aliasing logic, i.e. compliant with CASSANDRA-4009 - * when `cassandra4009` flag is enabled, or default. + * when `cassandra_aliasing` flag is enabled, or default. * * More information: https://issues.apache.org/jira/browse/CASSANDRA-4009 */ diff --git a/src/test/resources/jmx_alias_match.yaml b/src/test/resources/jmx_alias_match.yaml new file mode 100644 index 000000000..bdbd55609 --- /dev/null +++ b/src/test/resources/jmx_alias_match.yaml @@ -0,0 +1,15 @@ +init_config: + +instances: + - process_name_regex: .*surefire.* + name: jmx_test_instance + conf: + - include: + domain: org.datadog.jmxfetch.test + attribute: + ShouldBe100: + metric_type: gauge + alias: this.is.100.$foo.$qux + Hashmap.thisis0: + metric_type: gauge + alias: $domain.$qux.$attribute