From ee9cf5024d34a1e57b949af1ca8afe9c77d7eef5 Mon Sep 17 00:00:00 2001 From: Maxime Mouial Date: Wed, 7 Dec 2016 18:35:24 +0100 Subject: [PATCH] Add an 'exclude_tags' list to the configuration: fix #103 This is usefull to filter out unique tags like host/client id, ... This may/should be use in combination with the 'histogram' metric_type to avoid loosing metric points. --- .../java/org/datadog/jmxfetch/Filter.java | 16 ++++++++++++ .../org/datadog/jmxfetch/JMXAttribute.java | 23 ++++++++++++++++- .../java/org/datadog/jmxfetch/TestApp.java | 25 +++++++++++++++++++ src/test/resources/jmx_exclude_tags.yml | 22 ++++++++++++++++ 4 files changed, 85 insertions(+), 1 deletion(-) create mode 100644 src/test/resources/jmx_exclude_tags.yml diff --git a/src/main/java/org/datadog/jmxfetch/Filter.java b/src/main/java/org/datadog/jmxfetch/Filter.java index bf14d9df2..f72fa16fe 100644 --- a/src/main/java/org/datadog/jmxfetch/Filter.java +++ b/src/main/java/org/datadog/jmxfetch/Filter.java @@ -10,6 +10,7 @@ class Filter { HashMap filter; Pattern domainRegex; ArrayList beanRegexes = null; + ArrayList excludeTags = null; /** * A simple class to manipulate include/exclude filter elements more easily @@ -105,6 +106,21 @@ public ArrayList getBeanRegexes() { return this.beanRegexes; } + public ArrayList getExcludeTags() { + // Return excluded tags as an ArrayList whether it's defined as a list or not + + if (this.excludeTags == null) { + if (filter.get("exclude_tags") == null){ + this.excludeTags = new ArrayList(); + } else { + final Object exclude_tags = filter.get("exclude_tags"); + this.excludeTags = toStringArrayList(exclude_tags); + } + } + + return this.excludeTags; + } + public String getDomain() { return (String) filter.get("domain"); } diff --git a/src/main/java/org/datadog/jmxfetch/JMXAttribute.java b/src/main/java/org/datadog/jmxfetch/JMXAttribute.java index d5e69e36c..990d0ad5a 100644 --- a/src/main/java/org/datadog/jmxfetch/JMXAttribute.java +++ b/src/main/java/org/datadog/jmxfetch/JMXAttribute.java @@ -7,6 +7,7 @@ import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.atomic.AtomicLong; import java.util.HashMap; +import java.util.Iterator; import java.util.LinkedHashMap; import java.util.LinkedList; import java.util.List; @@ -28,7 +29,7 @@ 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 List EXCLUDED_BEAN_PARAMS = Arrays.asList("domain", "domain_regex", "bean_name", "bean", "bean_regex", "attribute", "exclude_tags"); private static final String FIRST_CAP_PATTERN = "(.)([A-Z][a-z]+)"; private static final String ALL_CAP_PATTERN = "([a-z0-9])([A-Z])"; private static final String METRIC_REPLACEMENT = "([^a-zA-Z0-9_.]+)|(^[^a-zA-Z]+)"; @@ -73,6 +74,23 @@ public abstract class JMXAttribute { this.defaultTagsList = sanitizeParameters(beanParametersList); } + /** + * Remove tags listed in the 'exclude_tags' list from configuration. + */ + private void applyTagsBlackList() { + Filter include = this.matchingConf.getInclude(); + if (include != null) { + + for (String excludedTagName : include.getExcludeTags()) { + for (Iterator it = this.defaultTagsList.iterator(); it.hasNext();) { + if (it.next().startsWith(excludedTagName + ":")) { + it.remove(); + } + } + } + } + } + public static HashMap getBeanParametersHash(String beanParametersString) { String[] beanParameters = beanParametersString.split(","); HashMap beanParamsMap = new HashMap(beanParameters.length); @@ -349,6 +367,9 @@ public Configuration getMatchingConf() { public void setMatchingConf(Configuration matchingConf) { this.matchingConf = matchingConf; + + // Now that we have the matchingConf, we can filter out excluded tags + applyTagsBlackList(); } MBeanAttributeInfo getAttribute() { diff --git a/src/test/java/org/datadog/jmxfetch/TestApp.java b/src/test/java/org/datadog/jmxfetch/TestApp.java index a8df013da..c4ff381f8 100644 --- a/src/test/java/org/datadog/jmxfetch/TestApp.java +++ b/src/test/java/org/datadog/jmxfetch/TestApp.java @@ -366,6 +366,31 @@ public void testMetricTypes() throws Exception { assertMetric("test.counter", 0.0, commonTags, 5, "counter"); } + @Test + public void testExcludeTags() throws Exception { + // We expose a few metrics through JMX + SimpleTestJavaApp testApp = new SimpleTestJavaApp(); + registerMBean(testApp, "org.datadog.jmxfetch.test:type=SimpleTestJavaApp"); + + // We do a first collection + initApplication("jmx_exclude_tags.yml"); + run(); + LinkedList> metrics = getMetrics(); + + // We test for the presence and the value of the metrics we want to collect. + // Tags "type", "newTag" and "env" should be excluded + List commonTags = Arrays.asList( + "instance:jmx_test_instance", + "jmx_domain:org.datadog.jmxfetch.test"); + + // 15 = 13 metrics from java.lang + the 2 collected (gauge and histogram) + assertEquals(15, metrics.size()); + + // There should only left 2 tags per metric + assertMetric("test1.gauge", 1000.0, commonTags, 2, "gauge"); + assertMetric("test1.histogram", 424242, commonTags, 2, "histogram"); + } + /** * FIXME: Split this test in multiple sub-tests. */ diff --git a/src/test/resources/jmx_exclude_tags.yml b/src/test/resources/jmx_exclude_tags.yml new file mode 100644 index 000000000..1d1116500 --- /dev/null +++ b/src/test/resources/jmx_exclude_tags.yml @@ -0,0 +1,22 @@ +init_config: + +instances: + - process_name_regex: .*surefire.* + name: jmx_test_instance + tags: + env: stage + newTag: test + conf: + - include: + domain: org.datadog.jmxfetch.test + exclude_tags: + - env + - type + - newTag + attribute: + ShouldBe1000: + metric_type: gauge + alias: test1.gauge + Int424242: + metric_type: histogram + alias: test1.histogram