diff --git a/src/main/java/org/datadog/jmxfetch/Connection.java b/src/main/java/org/datadog/jmxfetch/Connection.java index 7d562bd6f..90cb2688e 100644 --- a/src/main/java/org/datadog/jmxfetch/Connection.java +++ b/src/main/java/org/datadog/jmxfetch/Connection.java @@ -50,6 +50,13 @@ public MBeanAttributeInfo[] getAttributesForBean(ObjectName beanName) return mbs.getMBeanInfo(beanName).getAttributes(); } + /** Gets class name for matching bean name. */ + public String getClassNameForBean(ObjectName beanName) + throws InstanceNotFoundException, IntrospectionException, ReflectionException, + IOException { + return mbs.getMBeanInfo(beanName).getClassName(); + } + /** Queries beans on specific scope. Returns set of matching query names.. */ public Set queryNames(ObjectName name) throws IOException { String scope = (name != null) ? name.toString() : "*:*"; diff --git a/src/main/java/org/datadog/jmxfetch/Filter.java b/src/main/java/org/datadog/jmxfetch/Filter.java index a9f242c52..096084734 100644 --- a/src/main/java/org/datadog/jmxfetch/Filter.java +++ b/src/main/java/org/datadog/jmxfetch/Filter.java @@ -12,6 +12,7 @@ class Filter { Map filter; Pattern domainRegex; + Pattern classNameRegex; List beanRegexes = null; List excludeTags = null; Map additionalTags = null; @@ -148,6 +149,23 @@ public Pattern getDomainRegex() { return this.domainRegex; } + public String getClassName() { + return (String) filter.get("class"); + } + + public Pattern getClassNameRegex() { + if (this.filter.get("class_regex") == null) { + return null; + } + + if (this.classNameRegex == null) { + this.classNameRegex = Pattern.compile((String) this.filter.get("class_regex")); + } + + return this.classNameRegex; + } + + public Object getAttribute() { return filter.get("attribute"); } diff --git a/src/main/java/org/datadog/jmxfetch/Instance.java b/src/main/java/org/datadog/jmxfetch/Instance.java index fbbb2df38..ef39d5189 100644 --- a/src/main/java/org/datadog/jmxfetch/Instance.java +++ b/src/main/java/org/datadog/jmxfetch/Instance.java @@ -478,21 +478,24 @@ private void getMatchingAttributes() throws IOException { break; } } + String className; MBeanAttributeInfo[] attributeInfos; - try { + log.debug("Getting class name for bean: " + beanName); + className = connection.getClassNameForBean(beanName); + // Get all the attributes for bean_name log.debug("Getting attributes for bean: " + beanName); attributeInfos = connection.getAttributesForBean(beanName); } catch (IOException e) { // we should not continue - log.warn("Cannot get bean attributes " + e.getMessage()); + log.warn("Cannot get bean attributes or class name: " + e.getMessage()); if (e.getMessage() == connection.CLOSED_CLIENT_CAUSE) { throw e; } continue; } catch (Exception e) { - log.warn("Cannot get bean attributes " + e.getMessage()); + log.warn("Cannot get bean attributes or class name: " + e.getMessage()); continue; } @@ -523,6 +526,7 @@ private void getMatchingAttributes() throws IOException { new JmxSimpleAttribute( attributeInfo, beanName, + className, instanceName, checkName, connection, @@ -540,6 +544,7 @@ private void getMatchingAttributes() throws IOException { new JmxComplexAttribute( attributeInfo, beanName, + className, instanceName, checkName, connection, @@ -556,6 +561,7 @@ private void getMatchingAttributes() throws IOException { new JmxTabularAttribute( attributeInfo, beanName, + className, instanceName, checkName, connection, diff --git a/src/main/java/org/datadog/jmxfetch/JmxAttribute.java b/src/main/java/org/datadog/jmxfetch/JmxAttribute.java index 718d5492f..638fcaec8 100644 --- a/src/main/java/org/datadog/jmxfetch/JmxAttribute.java +++ b/src/main/java/org/datadog/jmxfetch/JmxAttribute.java @@ -34,6 +34,8 @@ public abstract class JmxAttribute { "bean_name", "bean", "bean_regex", + "class", + "class_regex", "attribute", "exclude_tags", "tags"); @@ -47,6 +49,7 @@ public abstract class JmxAttribute { private Connection connection; private ObjectName beanName; private String domain; + private String className; private String beanStringName; private Map beanParameters; private String attributeName; @@ -61,6 +64,7 @@ public abstract class JmxAttribute { JmxAttribute( MBeanAttributeInfo attribute, ObjectName beanName, + String className, String instanceName, String checkName, Connection connection, @@ -69,6 +73,7 @@ public abstract class JmxAttribute { boolean emptyDefaultHostname) { this.attribute = attribute; this.beanName = beanName; + this.className = className; this.matchingConf = null; this.connection = connection; this.attributeName = attribute.getName(); @@ -268,19 +273,39 @@ Object getJmxValue() } boolean matchDomain(Configuration conf) { - String includeDomain = conf.getInclude().getDomain(); - Pattern includeDomainRegex = conf.getInclude().getDomainRegex(); - - return (includeDomain == null || includeDomain.equals(domain)) - && (includeDomainRegex == null || includeDomainRegex.matcher(domain).matches()); + return includeMatchName(domain, + conf.getInclude().getDomain(), + conf.getInclude().getDomainRegex()); } boolean excludeMatchDomain(Configuration conf) { - String excludeDomain = conf.getExclude().getDomain(); - Pattern excludeDomainRegex = conf.getExclude().getDomainRegex(); + return excludeMatchName(domain, + conf.getExclude().getDomain(), + conf.getExclude().getDomainRegex()); + } + + + boolean matchClassName(Configuration conf) { + return includeMatchName(className, + conf.getInclude().getClassName(), + conf.getInclude().getClassNameRegex()); + } + + + boolean excludeMatchClassName(Configuration conf) { + return excludeMatchName(className, + conf.getExclude().getClassName(), + conf.getExclude().getClassNameRegex()); + } + + private boolean includeMatchName(String name, String includeName, Pattern includeNameRegex) { + return (includeName == null || includeName.equals(name)) + && (includeNameRegex == null || includeNameRegex.matcher(name).matches()); + } - return excludeDomain != null && excludeDomain.equals(domain) - || excludeDomainRegex != null && excludeDomainRegex.matcher(domain).matches(); + private boolean excludeMatchName(String name, String excludeName, Pattern excludeNameRegex) { + return (excludeName != null && excludeName.equals(name)) + || (excludeNameRegex != null && excludeNameRegex.matcher(name).matches()); } Object convertMetricValue(Object metricValue, String field) { diff --git a/src/main/java/org/datadog/jmxfetch/JmxComplexAttribute.java b/src/main/java/org/datadog/jmxfetch/JmxComplexAttribute.java index 08c09a514..4a76ede7c 100644 --- a/src/main/java/org/datadog/jmxfetch/JmxComplexAttribute.java +++ b/src/main/java/org/datadog/jmxfetch/JmxComplexAttribute.java @@ -22,6 +22,7 @@ public class JmxComplexAttribute extends JmxSubAttribute { public JmxComplexAttribute( MBeanAttributeInfo attribute, ObjectName beanName, + String className, String instanceName, String checkName, Connection connection, @@ -30,6 +31,7 @@ public JmxComplexAttribute( super( attribute, beanName, + className, instanceName, checkName, connection, @@ -88,8 +90,10 @@ private Object getValue(String subAttribute) @Override public boolean match(Configuration configuration) { if (!matchDomain(configuration) + || !matchClassName(configuration) || !matchBean(configuration) || excludeMatchDomain(configuration) + || excludeMatchClassName(configuration) || excludeMatchBean(configuration)) { return false; } diff --git a/src/main/java/org/datadog/jmxfetch/JmxSimpleAttribute.java b/src/main/java/org/datadog/jmxfetch/JmxSimpleAttribute.java index c5b5f9b7f..d5107a1f4 100644 --- a/src/main/java/org/datadog/jmxfetch/JmxSimpleAttribute.java +++ b/src/main/java/org/datadog/jmxfetch/JmxSimpleAttribute.java @@ -19,6 +19,7 @@ public class JmxSimpleAttribute extends JmxAttribute { public JmxSimpleAttribute( MBeanAttributeInfo attribute, ObjectName beanName, + String className, String instanceName, String checkName, Connection connection, @@ -28,6 +29,7 @@ public JmxSimpleAttribute( super( attribute, beanName, + className, instanceName, checkName, connection, @@ -54,9 +56,11 @@ public List getMetrics() /** Returns whether an attribute matches in a configuration spec. */ public boolean match(Configuration configuration) { return matchDomain(configuration) + && matchClassName(configuration) && matchBean(configuration) && matchAttribute(configuration) && !(excludeMatchDomain(configuration) + || excludeMatchClassName(configuration) || excludeMatchBean(configuration) || excludeMatchAttribute(configuration)); } diff --git a/src/main/java/org/datadog/jmxfetch/JmxSubAttribute.java b/src/main/java/org/datadog/jmxfetch/JmxSubAttribute.java index bb8e8ecb0..cfeb35f4b 100644 --- a/src/main/java/org/datadog/jmxfetch/JmxSubAttribute.java +++ b/src/main/java/org/datadog/jmxfetch/JmxSubAttribute.java @@ -8,10 +8,11 @@ abstract class JmxSubAttribute extends JmxAttribute { private Map cachedMetrics = new HashMap(); - public JmxSubAttribute(MBeanAttributeInfo attribute, ObjectName beanName, String instanceName, - String checkName, Connection connection, Map instanceTags, - boolean cassandraAliasing, boolean emptyDefaultHostname) { - super(attribute, beanName, instanceName, checkName, connection, instanceTags, + public JmxSubAttribute(MBeanAttributeInfo attribute, ObjectName beanName, String className, + String instanceName, String checkName, Connection connection, + Map instanceTags, boolean cassandraAliasing, + boolean emptyDefaultHostname) { + super(attribute, beanName, className, instanceName, checkName, connection, instanceTags, cassandraAliasing, emptyDefaultHostname); } diff --git a/src/main/java/org/datadog/jmxfetch/JmxTabularAttribute.java b/src/main/java/org/datadog/jmxfetch/JmxTabularAttribute.java index d7d026a36..e3246a5b5 100644 --- a/src/main/java/org/datadog/jmxfetch/JmxTabularAttribute.java +++ b/src/main/java/org/datadog/jmxfetch/JmxTabularAttribute.java @@ -31,6 +31,7 @@ public class JmxTabularAttribute extends JmxSubAttribute { public JmxTabularAttribute( MBeanAttributeInfo attribute, ObjectName beanName, + String className, String instanceName, String checkName, Connection connection, @@ -39,6 +40,7 @@ public JmxTabularAttribute( super( attribute, beanName, + className, instanceName, checkName, connection, @@ -233,8 +235,10 @@ private Object getValue(String key, String subAttribute) @Override public boolean match(Configuration configuration) { if (!matchDomain(configuration) + || !matchClassName(configuration) || !matchBean(configuration) || excludeMatchDomain(configuration) + || excludeMatchClassName(configuration) || excludeMatchBean(configuration)) { return false; } diff --git a/src/test/java/org/datadog/jmxfetch/TestApp.java b/src/test/java/org/datadog/jmxfetch/TestApp.java index 774debc19..3c7fa8381 100644 --- a/src/test/java/org/datadog/jmxfetch/TestApp.java +++ b/src/test/java/org/datadog/jmxfetch/TestApp.java @@ -256,6 +256,65 @@ public void testDomainRegex() throws Exception { assertEquals(15, metrics.size()); } + @Test + public void testClassInclude() throws Exception { + // We expose a few metrics through JMX + registerMBean(new SimpleTestJavaApp(), "org.datadog.jmxfetch.includeme:type=AType"); + initApplication("jmx_class_include.yaml"); + + // Collecting metrics + run(); + List> metrics = getMetrics(); + + // First filter 29 = 13 metrics from java.lang + 16 metrics implicitly defined + assertEquals(29, metrics.size()); + } + + @Test + public void testClassExclude() throws Exception { + class SimpleTestJavaAnotherApp extends SimpleTestJavaApp { + + } + + // We expose a few metrics through JMX + registerMBean(new SimpleTestJavaApp(), "org.datadog.jmxfetch.includeme:type=AType"); + registerMBean(new SimpleTestJavaAnotherApp(), "org.datadog.jmxfetch.includeme2:type=AnotherType"); + + // Initializing application + initApplication("jmx_class_exclude.yaml"); + + // Collecting metrics + run(); + List> metrics = getMetrics(); + + // First filter 14 = 13 metrics from java.lang + 2 metrics explicitly define- 1 implicitly + // defined in the exclude section + assertEquals(14, metrics.size()); + } + + @Test + public void testClassRegex() throws Exception { + class SimpleTestJavaAppIncludeMe extends SimpleTestJavaApp { } + class SimpleTestJavaAppIncludeMeToo extends SimpleTestJavaApp { } + class SimpleTestJavaAppIncludeMeNotMeX extends SimpleTestJavaApp { } + + // We expose a few metrics through JMX + registerMBean(new SimpleTestJavaAppIncludeMe(), "org.datadog.jmxfetch.includeme:type=AType"); + registerMBean(new SimpleTestJavaAppIncludeMeToo(), "org.datadog.jmxfetch.includeme.too:type=AType"); + registerMBean(new SimpleTestJavaAppIncludeMeNotMeX(), "org.datadog.jmxfetch.includeme.not.me:type=AType"); + + // Initializing application + initApplication("jmx_class_regex.yaml"); + + // Collecting metrics + run(); + List> metrics = getMetrics(); + + // First filter 15 = 13 metrics from java.lang + 3 metrics explicitly defined - 1 implicitly + // defined in exclude section + assertEquals(15, metrics.size()); + } + @Test public void testParameterMatch() throws Exception { // Do not match beans which do not contain types specified in the conf diff --git a/src/test/resources/jmx_class_exclude.yaml b/src/test/resources/jmx_class_exclude.yaml new file mode 100644 index 000000000..22c06256e --- /dev/null +++ b/src/test/resources/jmx_class_exclude.yaml @@ -0,0 +1,13 @@ +init_config: + +instances: + - process_name_regex: .*surefire.* + name: jmx_test_instance + conf: + - include: + attribute: + ShouldBe100: + metric_type: gauge + alias: this.is.100 + exclude: + class: org.datadog.jmxfetch.TestApp$1SimpleTestJavaAnotherApp diff --git a/src/test/resources/jmx_class_include.yaml b/src/test/resources/jmx_class_include.yaml new file mode 100644 index 000000000..560e80071 --- /dev/null +++ b/src/test/resources/jmx_class_include.yaml @@ -0,0 +1,8 @@ +init_config: + +instances: + - process_name_regex: .*surefire.* + name: jmx_test_instance + conf: + - include: + class: org.datadog.jmxfetch.SimpleTestJavaApp diff --git a/src/test/resources/jmx_class_regex.yaml b/src/test/resources/jmx_class_regex.yaml new file mode 100644 index 000000000..a74274b8d --- /dev/null +++ b/src/test/resources/jmx_class_regex.yaml @@ -0,0 +1,14 @@ +init_config: + +instances: + - process_name_regex: .*surefire.* + name: jmx_test_instance + conf: + - include: + class_regex: .*IncludeMe.* + attribute: + ShouldBe100: + metric_type: gauge + alias: this.is.100 + exclude: + class_regex: .*Me$