diff --git a/src/main/java/org/datadog/jmxfetch/Configuration.java b/src/main/java/org/datadog/jmxfetch/Configuration.java index 0960c98f..99839453 100644 --- a/src/main/java/org/datadog/jmxfetch/Configuration.java +++ b/src/main/java/org/datadog/jmxfetch/Configuration.java @@ -1,5 +1,7 @@ package org.datadog.jmxfetch; +import lombok.extern.slf4j.Slf4j; + import java.util.ArrayList; import java.util.HashMap; import java.util.HashSet; @@ -9,11 +11,13 @@ import java.util.Map.Entry; import java.util.Set; +@Slf4j public class Configuration { private Map conf; private Filter include; private Filter exclude; + private List dynamicTags = null; /** * Access configuration elements more easily @@ -24,6 +28,37 @@ public Configuration(Map conf) { this.conf = conf; this.include = new Filter(conf.get("include")); this.exclude = new Filter(conf.get("exclude")); + this.parseDynamicTags(conf.get("dynamic_tags")); + } + + /** + * Parse dynamic tags from configuration. + * Expected format: + * dynamic_tags: + * - tag_name: cluster_id + * bean_name: kafka.server:type=KafkaServer,name=ClusterId + * attribute: Value + */ + private void parseDynamicTags(Object dynamicTagsConfig) { + this.dynamicTags = new ArrayList(); + + if (dynamicTagsConfig == null) { + return; + } + + if (!(dynamicTagsConfig instanceof List)) { + log.warn("Invalid dynamic_tags configuration: expected list of tag definitions"); + return; + } + + List dynamicTagsList = (List) dynamicTagsConfig; + + for (Object tagConfig : dynamicTagsList) { + DynamicTag dynamicTag = DynamicTag.parse(tagConfig); + if (dynamicTag != null) { + this.dynamicTags.add(dynamicTag); + } + } } public Map getConf() { @@ -45,6 +80,14 @@ public String toString() { private Boolean hasInclude() { return getInclude() != null && !getInclude().isEmptyFilter(); } + + /** Get list of dynamic tags defined for this configuration. */ + public List getDynamicTags() { + if (dynamicTags == null) { + return new ArrayList(); + } + return dynamicTags; + } /** * Filter a configuration list to keep the ones with `include` filters. diff --git a/src/main/java/org/datadog/jmxfetch/DynamicTag.java b/src/main/java/org/datadog/jmxfetch/DynamicTag.java new file mode 100644 index 00000000..3e40347c --- /dev/null +++ b/src/main/java/org/datadog/jmxfetch/DynamicTag.java @@ -0,0 +1,108 @@ +package org.datadog.jmxfetch; + +import lombok.extern.slf4j.Slf4j; + +import java.util.HashMap; +import java.util.Map; +import javax.management.MalformedObjectNameException; +import javax.management.ObjectName; + +@Slf4j +public class DynamicTag { + private final String tagName; + private final String beanName; + private final String attributeName; + + /** Parse dynamic tag from configuration map (list entry format). */ + public static DynamicTag parse(Object tagConfig) { + if (tagConfig == null) { + return null; + } + + if (!(tagConfig instanceof Map)) { + log.warn("Invalid dynamic tag config: expected map with 'tag_name', 'bean_name' and " + + "'attribute' keys"); + return null; + } + + Map config = (Map) tagConfig; + Object tagNameObj = config.get("tag_name"); + Object beanObj = config.get("bean_name"); + Object attrObj = config.get("attribute"); + + if (tagNameObj == null || beanObj == null || attrObj == null) { + String missing = "Invalid dynamic tag config: missing" + + (tagNameObj == null ? " tag_name" : "") + + (beanObj == null ? " bean_name" : "") + + (attrObj == null ? " attribute" : ""); + log.warn(missing); + return null; + } + + String tagName = tagNameObj.toString(); + String beanName = beanObj.toString(); + String attributeName = attrObj.toString(); + + return new DynamicTag(tagName, beanName, attributeName); + } + + private DynamicTag(String tagName, String beanName, String attributeName) { + this.tagName = tagName; + this.beanName = beanName; + this.attributeName = attributeName; + } + + public String getTagName() { + return tagName; + } + + public String getBeanName() { + return beanName; + } + + public String getAttributeName() { + return attributeName; + } + + /** Gets a unique key for the bean and attribute combination. */ + public String getBeanAttributeKey() { + return beanName + "#" + attributeName; + } + + /** Resolve the dynamic tag by fetching the attribute value from JMX. */ + public Map.Entry resolve(Connection connection) { + try { + ObjectName objectName = new ObjectName(beanName); + Object value = connection.getAttribute(objectName, attributeName); + + if (value == null) { + log.warn("Dynamic tag '{}' resolved to null for bean '{}' attribute '{}'", + tagName, beanName, attributeName); + return null; + } + + String stringValue = value.toString(); + log.info("Resolved dynamic tag '{}' to value '{}' from bean '{}' attribute '{}'", + tagName, stringValue, beanName, attributeName); + + return new HashMap.SimpleEntry<>(tagName, stringValue); + + } catch (MalformedObjectNameException e) { + log.error("Invalid bean name '{}' for dynamic tag '{}': {}", + beanName, tagName, e.getMessage()); + return null; + } catch (Exception e) { + log.warn("Failed to resolve dynamic tag '{}' from bean '{}' attribute '{}': {}", + tagName, beanName, attributeName, e.getMessage()); + log.debug("Dynamic tag resolution error details", e); + return null; + } + } + + @Override + public String toString() { + return String.format("DynamicTag{name='%s', bean='%s', attribute='%s'}", + tagName, beanName, attributeName); + } +} + diff --git a/src/main/java/org/datadog/jmxfetch/Instance.java b/src/main/java/org/datadog/jmxfetch/Instance.java index 9dd22fbe..82570c88 100644 --- a/src/main/java/org/datadog/jmxfetch/Instance.java +++ b/src/main/java/org/datadog/jmxfetch/Instance.java @@ -79,6 +79,7 @@ public Yaml initialValue() { private ObjectName instanceTelemetryBeanName; private MBeanServer mbs; private Boolean normalizeBeanParamTags; + private Map> dynamicTagsCache; /** Constructor, instantiates Instance based of a previous instance and appConfig. */ public Instance(Instance instance, AppConfig appConfig) { @@ -447,14 +448,87 @@ public void init(boolean forceNewConnection) throws IOException, FailedLoginException, SecurityException { log.info("Trying to connect to JMX Server at " + this.toString()); connection = getConnection(instanceMap, forceNewConnection); + log.info( "Trying to collect bean list for the first time for JMX Server at {}", this); this.refreshBeansList(); this.initialRefreshTime = this.lastRefreshTime; log.info("Connected to JMX Server at {} with {} beans", this, this.beans.size()); + + // Resolve configuration-level dynamic tags for all configurations + // Must be done after refreshBeansList() so the beans exist + resolveConfigurationDynamicTags(); + this.getMatchingAttributes(); log.info("Done initializing JMX Server at {}", this); } + + private void resolveConfigurationDynamicTags() { + if (configurationList == null || configurationList.isEmpty()) { + return; + } + + this.dynamicTagsCache = new HashMap<>(); + List allDynamicTags = new ArrayList<>(); + + for (Configuration config : configurationList) { + List dynamicTags = config.getDynamicTags(); + if (dynamicTags != null && !dynamicTags.isEmpty()) { + allDynamicTags.addAll(dynamicTags); + } + } + + if (allDynamicTags.isEmpty()) { + return; + } + + int successfulResolutions = 0; + for (DynamicTag dynamicTag : allDynamicTags) { + String cacheKey = dynamicTag.getBeanAttributeKey(); + if (!this.dynamicTagsCache.containsKey(cacheKey)) { + Map.Entry resolved = dynamicTag.resolve(connection); + // Cache both successful and failed resolutions (null) to avoid retrying + this.dynamicTagsCache.put(cacheKey, resolved); + } + // Count successful resolutions (cached value is not null) + if (this.dynamicTagsCache.get(cacheKey) != null) { + successfulResolutions++; + } + } + + log.info("Resolved {} unique dynamic tag(s) from {} total references for instance {}", + successfulResolutions, allDynamicTags.size(), instanceName); + } + + /** + * Get resolved dynamic tags for a specific configuration. + * This resolves the dynamic tags defined in the configuration using the cached values. + * + * @param config the configuration to get resolved tags for + * @return map of tag name to tag value + */ + private Map getResolvedDynamicTagsForConfig(Configuration config) { + Map resolvedTags = new HashMap<>(); + + if (this.dynamicTagsCache == null || this.dynamicTagsCache.isEmpty()) { + return resolvedTags; + } + + List dynamicTags = config.getDynamicTags(); + if (dynamicTags == null || dynamicTags.isEmpty()) { + return resolvedTags; + } + + for (DynamicTag dynamicTag : dynamicTags) { + String cacheKey = dynamicTag.getBeanAttributeKey(); + Map.Entry cached = this.dynamicTagsCache.get(cacheKey); + if (cached != null) { + resolvedTags.put(cached.getKey(), cached.getValue()); + } + } + + return resolvedTags; + } /** Returns a string representation for the instance. */ @Override @@ -690,6 +764,9 @@ private void getMatchingAttributes() throws IOException { for (Configuration conf : configurationList) { try { if (jmxAttribute.match(conf)) { + Map resolvedDynamicTags = + getResolvedDynamicTagsForConfig(conf); + jmxAttribute.setResolvedDynamicTags(resolvedDynamicTags); jmxAttribute.setMatchingConf(conf); metricsCount += jmxAttribute.getMetricsCount(); this.matchingAttributes.add(jmxAttribute); diff --git a/src/main/java/org/datadog/jmxfetch/JmxAttribute.java b/src/main/java/org/datadog/jmxfetch/JmxAttribute.java index 66755ea2..d4343e35 100644 --- a/src/main/java/org/datadog/jmxfetch/JmxAttribute.java +++ b/src/main/java/org/datadog/jmxfetch/JmxAttribute.java @@ -59,6 +59,7 @@ public abstract class JmxAttribute { new HashMap>(); protected String[] tags; private Configuration matchingConf; + private Map resolvedDynamicTags; private List defaultTagsList; private boolean cassandraAliasing; protected String checkName; @@ -139,6 +140,15 @@ private void addAdditionalTags() { } } } + + /** Add dynamic tags that were resolved at connection time. */ + private void addDynamicTags() { + if (this.resolvedDynamicTags != null && !this.resolvedDynamicTags.isEmpty()) { + for (Map.Entry tag : this.resolvedDynamicTags.entrySet()) { + this.defaultTagsList.add(tag.getKey() + ":" + tag.getValue()); + } + } + } private void addServiceTags() { Iterable serviceNames = this.serviceNameProvider.getServiceNames(); @@ -495,6 +505,11 @@ public Configuration getMatchingConf() { return matchingConf; } + /** Sets resolved dynamic tags for the attribute. */ + public void setResolvedDynamicTags(Map resolvedDynamicTags) { + this.resolvedDynamicTags = resolvedDynamicTags; + } + /** Sets a matching configuration for the attribute. */ public void setMatchingConf(Configuration matchingConf) { this.matchingConf = matchingConf; @@ -502,6 +517,8 @@ public void setMatchingConf(Configuration matchingConf) { // Now that we have the matchingConf we can: // - add additional tags this.addAdditionalTags(); + // - add dynamic tags that were resolved at connection time + this.addDynamicTags(); // - filter out excluded tags this.applyTagsBlackList(); // Add the service tag(s) - comes last because if the service tag is blacklisted as diff --git a/src/test/java/org/datadog/jmxfetch/DynamicTagTestApp.java b/src/test/java/org/datadog/jmxfetch/DynamicTagTestApp.java new file mode 100644 index 00000000..95b6fc1f --- /dev/null +++ b/src/test/java/org/datadog/jmxfetch/DynamicTagTestApp.java @@ -0,0 +1,45 @@ +package org.datadog.jmxfetch; + +public class DynamicTagTestApp implements DynamicTagTestAppMBean { + private final String clusterId; + private final String version; + private final int port; + private double metric; + + public DynamicTagTestApp() { + this("local-kafka-cluster", "2.8.0", 9092); + } + + public DynamicTagTestApp(String clusterId, String version, int port) { + this.clusterId = clusterId; + this.version = version; + this.port = port; + this.metric = 100.0; + } + + @Override + public String getClusterId() { + return clusterId; + } + + @Override + public String getVersion() { + return version; + } + + @Override + public int getPort() { + return port; + } + + @Override + public Double getMetric() { + return metric; + } + + public void setMetric(double metric) { + this.metric = metric; + } +} + + diff --git a/src/test/java/org/datadog/jmxfetch/DynamicTagTestAppMBean.java b/src/test/java/org/datadog/jmxfetch/DynamicTagTestAppMBean.java new file mode 100644 index 00000000..34483152 --- /dev/null +++ b/src/test/java/org/datadog/jmxfetch/DynamicTagTestAppMBean.java @@ -0,0 +1,10 @@ +package org.datadog.jmxfetch; + +public interface DynamicTagTestAppMBean { + String getClusterId(); + String getVersion(); + int getPort(); + Double getMetric(); +} + + diff --git a/src/test/java/org/datadog/jmxfetch/TestConfigDynamicTags.java b/src/test/java/org/datadog/jmxfetch/TestConfigDynamicTags.java new file mode 100644 index 00000000..fd57a6ad --- /dev/null +++ b/src/test/java/org/datadog/jmxfetch/TestConfigDynamicTags.java @@ -0,0 +1,306 @@ +package org.datadog.jmxfetch; + +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertTrue; + +import org.junit.Test; + +import java.util.ArrayList; +import java.util.List; +import java.util.Map; + +public class TestConfigDynamicTags extends TestCommon { + + @Test + public void testConfigDynamicTagsBasic() throws Exception { + registerMBean( + new DynamicTagTestApp("kafka-prod-cluster", "3.2.0", 9092), + "org.datadog.jmxfetch.test:type=DynamicTagTestApp"); + registerMBean( + new SimpleTestJavaApp(), + "org.datadog.jmxfetch.test:foo=Bar,qux=Baz"); + + initApplication("jmx_config_dynamic_tags.yaml"); + + run(); + + List> metrics = getMetrics(); + assertTrue("Should have collected metrics", metrics.size() > 0); + + boolean foundMetric = false; + for (Map metric : metrics) { + String metricName = (String) metric.get("name"); + if ("test.config.dynamic.tags.metric".equals(metricName)) { + foundMetric = true; + + Object tagsObj = metric.get("tags"); + assertNotNull("Metric should have tags", tagsObj); + + List tagList = new ArrayList<>(); + if (tagsObj instanceof String[]) { + for (String tag : (String[]) tagsObj) { + tagList.add(tag); + } + } else if (tagsObj instanceof List) { + for (Object tag : (List) tagsObj) { + tagList.add((String) tag); + } + } + + assertTrue("Should have cluster_id tag", + tagList.contains("cluster_id:kafka-prod-cluster")); + assertTrue("Should have kafka_version tag", + tagList.contains("kafka_version:3.2.0")); + assertTrue("Should have env tag", + tagList.contains("env:test")); + + break; + } + } + + assertTrue("Should have found the test metric", foundMetric); + } + + @Test + public void testConfigDynamicTagsMultiple() throws Exception { + registerMBean( + new DynamicTagTestApp("cluster-1", "version-1", 9092), + "org.datadog.jmxfetch.test:type=DynamicTagTestApp,name=Instance1"); + + registerMBean( + new DynamicTagTestApp("cluster-2", "version-2", 9093), + "org.datadog.jmxfetch.test:type=DynamicTagTestApp,name=Instance2"); + registerMBean( + new SimpleTestJavaApp(), + "org.datadog.jmxfetch.test:foo=Bar,qux=Baz"); + + initApplication("jmx_config_dynamic_tags_multi.yaml"); + + run(); + + List> metrics = getMetrics(); + assertTrue("Should have collected metrics", metrics.size() > 0); + + boolean foundInstance1 = false; + boolean foundInstance2 = false; + + for (Map metric : metrics) { + String metricName = (String) metric.get("name"); + + if ("test.instance1.metric".equals(metricName)) { + foundInstance1 = true; + + List tagList = getTagsAsList(metric); + assertTrue("Instance1 should have cluster_id tag", + tagList.contains("cluster_id:cluster-1")); + + boolean hasVersionTag = false; + for (String tag : tagList) { + if (tag.startsWith("version:")) { + hasVersionTag = true; + break; + } + } + assertTrue("Instance1 should NOT have version tag", !hasVersionTag); + } + + if ("test.instance2.metric".equals(metricName)) { + foundInstance2 = true; + + List tagList = getTagsAsList(metric); + assertTrue("Instance2 should have version tag", + tagList.contains("version:version-2")); + + boolean hasClusterTag = false; + for (String tag : tagList) { + if (tag.startsWith("cluster_id:")) { + hasClusterTag = true; + break; + } + } + assertTrue("Instance2 should NOT have cluster_id tag", !hasClusterTag); + } + } + + assertTrue("Should have found instance1 metric", foundInstance1); + assertTrue("Should have found instance2 metric", foundInstance2); + } + + @Test + public void testConfigDynamicTagsWithBeanParams() throws Exception { + registerMBean( + new DynamicTagTestApp("test-cluster", "1.0.0", 9092), + "org.datadog.jmxfetch.test:type=DynamicTagTestApp,name=TestBean"); + registerMBean( + new SimpleTestJavaApp(), + "org.datadog.jmxfetch.test:foo=Bar,qux=Baz"); + + initApplication("jmx_config_dynamic_tags_bean_params.yaml"); + + run(); + + List> metrics = getMetrics(); + assertTrue("Should have collected metrics", metrics.size() > 0); + + boolean foundMetric = false; + for (Map metric : metrics) { + String metricName = (String) metric.get("name"); + if ("test.bean.params.metric".equals(metricName)) { + foundMetric = true; + + List tagList = getTagsAsList(metric); + + assertTrue("Should have bean_type tag", + tagList.contains("bean_type:DynamicTagTestApp")); + assertTrue("Should have bean_name tag", + tagList.contains("bean_name:TestBean")); + assertTrue("Should have cluster_id tag", + tagList.contains("cluster_id:test-cluster")); + + break; + } + } + + assertTrue("Should have found the test metric", foundMetric); + } + + @Test + public void testConfigDynamicTagsCaching() throws Exception { + registerMBean( + new DynamicTagTestApp("shared-cluster", "1.0.0", 9092), + "org.datadog.jmxfetch.test:type=DynamicTagTestApp"); + registerMBean( + new SimpleTestJavaApp(), + "org.datadog.jmxfetch.test:foo=Bar,qux=Baz"); + + initApplication("jmx_config_dynamic_tags_caching.yaml"); + + run(); + + List> metrics = getMetrics(); + assertTrue("Should have collected metrics", metrics.size() > 0); + + int metricsWithClusterId = 0; + for (Map metric : metrics) { + String metricName = (String) metric.get("name"); + if (metricName != null && metricName.startsWith("test.metric")) { + List tagList = getTagsAsList(metric); + if (tagList.contains("cluster_id:shared-cluster")) { + metricsWithClusterId++; + } + } + } + + assertTrue("Found " + metricsWithClusterId + " metrics with cluster_id, expected 2", + metricsWithClusterId == 2); + } + + @Test + public void testConfigDynamicTagsMultiplePerConf() throws Exception { + registerMBean( + new DynamicTagTestApp("cluster-1", "version-1", 9999), + "org.datadog.jmxfetch.test:type=DynamicTagTestApp"); + + initApplication("jmx_config_dynamic_tags_multiple_per_conf.yaml"); + + run(); + + List> metrics = getMetrics(); + assertTrue("Should have collected metrics", metrics.size() > 0); + + boolean foundMetric = false; + for (Map metric : metrics) { + String metricName = (String) metric.get("name"); + + if ("test.metric.with.multiple.tags".equals(metricName)) { + foundMetric = true; + + List tagList = getTagsAsList(metric); + + // Verify all three dynamic tags are present + assertTrue("Should have cluster_id dynamic tag", + tagList.contains("cluster_id:cluster-1")); + assertTrue("Should have version dynamic tag", + tagList.contains("version:version-1")); + assertTrue("Should have port dynamic tag", + tagList.contains("port:9999")); + + // Verify normal tags are also present + assertTrue("Should have env tag", + tagList.contains("env:test")); + } + } + + assertTrue("Should have found metric with multiple dynamic tags", foundMetric); + } + + @Test + public void testConfigDynamicTagsInvalid() throws Exception { + registerMBean( + new DynamicTagTestApp("cluster-1", "version-1", 9999), + "org.datadog.jmxfetch.test:type=DynamicTagTestApp"); + + initApplication("jmx_config_dynamic_tags_invalid.yaml"); + + run(); + + List> metrics = getMetrics(); + assertTrue("Should have collected metrics", metrics.size() > 0); + + boolean foundMetric = false; + for (Map metric : metrics) { + String metricName = (String) metric.get("name"); + + if ("test.metric.with.invalid.tags".equals(metricName)) { + foundMetric = true; + + List tagList = getTagsAsList(metric); + + // Verify normal tags are still applied + assertTrue("Should have env tag", + tagList.contains("env:test")); + assertTrue("Should have region tag", + tagList.contains("region:us-east-1")); + + // Verify the valid dynamic tag is applied + assertTrue("Should have valid_port dynamic tag", + tagList.contains("valid_port:9999")); + + // Verify invalid dynamic tags are NOT applied + boolean hasMissingBeanTag = false; + boolean hasMissingAttrTag = false; + for (String tag : tagList) { + if (tag.startsWith("missing_bean:")) { + hasMissingBeanTag = true; + } + if (tag.startsWith("missing_attr:")) { + hasMissingAttrTag = true; + } + } + assertTrue("Should NOT have missing_bean tag (invalid config)", + !hasMissingBeanTag); + assertTrue("Should NOT have missing_attr tag (invalid config)", + !hasMissingAttrTag); + } + } + + assertTrue("Should have found metric (verifies invalid dynamic tags don't crash)", + foundMetric); + } + + private List getTagsAsList(Map metric) { + List tagList = new ArrayList<>(); + Object tagsObj = metric.get("tags"); + if (tagsObj instanceof String[]) { + for (String tag : (String[]) tagsObj) { + tagList.add(tag); + } + } else if (tagsObj instanceof List) { + for (Object tag : (List) tagsObj) { + tagList.add((String) tag); + } + } + return tagList; + } +} + diff --git a/src/test/resources/jmx_config_dynamic_tags.yaml b/src/test/resources/jmx_config_dynamic_tags.yaml new file mode 100644 index 00000000..b39c3d22 --- /dev/null +++ b/src/test/resources/jmx_config_dynamic_tags.yaml @@ -0,0 +1,23 @@ +init_config: + +instances: + - process_name_regex: .*surefire.* + name: jmx_config_dynamic_tags_test + conf: + - include: + domain: org.datadog.jmxfetch.test + type: DynamicTagTestApp + tags: + env: test + attribute: + Metric: + metric_type: gauge + alias: test.config.dynamic.tags.metric + dynamic_tags: + - tag_name: cluster_id + bean_name: org.datadog.jmxfetch.test:type=DynamicTagTestApp + attribute: ClusterId + - tag_name: kafka_version + bean_name: org.datadog.jmxfetch.test:type=DynamicTagTestApp + attribute: Version + diff --git a/src/test/resources/jmx_config_dynamic_tags_bean_params.yaml b/src/test/resources/jmx_config_dynamic_tags_bean_params.yaml new file mode 100644 index 00000000..59cc2b09 --- /dev/null +++ b/src/test/resources/jmx_config_dynamic_tags_bean_params.yaml @@ -0,0 +1,22 @@ +init_config: + +instances: + - process_name_regex: .*surefire.* + name: jmx_config_dynamic_tags_bean_params_test + conf: + - include: + domain: org.datadog.jmxfetch.test + type: DynamicTagTestApp + tags: + bean_type: $type + bean_name: $name + attribute: + Metric: + metric_type: gauge + alias: test.bean.params.metric + dynamic_tags: + - tag_name: cluster_id + bean_name: org.datadog.jmxfetch.test:type=DynamicTagTestApp,name=TestBean + attribute: ClusterId + + diff --git a/src/test/resources/jmx_config_dynamic_tags_caching.yaml b/src/test/resources/jmx_config_dynamic_tags_caching.yaml new file mode 100644 index 00000000..e0929944 --- /dev/null +++ b/src/test/resources/jmx_config_dynamic_tags_caching.yaml @@ -0,0 +1,33 @@ +init_config: + +instances: + - process_name_regex: .*surefire.* + name: jmx_config_dynamic_tags_caching_test + conf: + # Config 1: Uses cluster_id + - include: + domain: org.datadog.jmxfetch.test + type: DynamicTagTestApp + attribute: + Metric: + metric_type: gauge + alias: test.metric1 + dynamic_tags: + - tag_name: cluster_id + bean_name: org.datadog.jmxfetch.test:type=DynamicTagTestApp + attribute: ClusterId + + # Config 2: Uses same cluster_id (should be cached) + - include: + domain: org.datadog.jmxfetch.test + type: DynamicTagTestApp + attribute: + Port: + metric_type: gauge + alias: test.metric2 + dynamic_tags: + - tag_name: cluster_id + bean_name: org.datadog.jmxfetch.test:type=DynamicTagTestApp + attribute: ClusterId + + diff --git a/src/test/resources/jmx_config_dynamic_tags_invalid.yaml b/src/test/resources/jmx_config_dynamic_tags_invalid.yaml new file mode 100644 index 00000000..45f96c97 --- /dev/null +++ b/src/test/resources/jmx_config_dynamic_tags_invalid.yaml @@ -0,0 +1,34 @@ +init_config: + +instances: + - process_name_regex: .*surefire.* + name: jmx_config_dynamic_tags_invalid_test + conf: + # Invalid dynamic tag configurations (should be handled gracefully) + # Normal tags should still be applied, but invalid dynamic tags should be ignored + - include: + domain: org.datadog.jmxfetch.test + type: DynamicTagTestApp + attribute: + Metric: + metric_type: gauge + alias: test.metric.with.invalid.tags + tags: + env: test + region: us-east-1 + dynamic_tags: + # Missing tag_name - should be ignored with warning + - bean_name: org.datadog.jmxfetch.test:type=DynamicTagTestApp + attribute: ClusterId + # Missing bean_name - should be ignored with warning + - tag_name: missing_bean + attribute: Version + # Missing attribute - should be ignored with warning + - tag_name: missing_attr + bean_name: org.datadog.jmxfetch.test:type=DynamicTagTestApp + # Valid one - should work + - tag_name: valid_port + bean_name: org.datadog.jmxfetch.test:type=DynamicTagTestApp + attribute: Port + + diff --git a/src/test/resources/jmx_config_dynamic_tags_multi.yaml b/src/test/resources/jmx_config_dynamic_tags_multi.yaml new file mode 100644 index 00000000..619a9b8a --- /dev/null +++ b/src/test/resources/jmx_config_dynamic_tags_multi.yaml @@ -0,0 +1,34 @@ +init_config: + +instances: + - process_name_regex: .*surefire.* + name: jmx_config_dynamic_tags_multi_test + conf: + # Config 1: Has cluster_id dynamic tag + - include: + domain: org.datadog.jmxfetch.test + type: DynamicTagTestApp + name: Instance1 + attribute: + Metric: + metric_type: gauge + alias: test.instance1.metric + dynamic_tags: + - tag_name: cluster_id + bean_name: org.datadog.jmxfetch.test:type=DynamicTagTestApp,name=Instance1 + attribute: ClusterId + + # Config 2: Has version dynamic tag + - include: + domain: org.datadog.jmxfetch.test + type: DynamicTagTestApp + name: Instance2 + attribute: + Metric: + metric_type: gauge + alias: test.instance2.metric + dynamic_tags: + - tag_name: version + bean_name: org.datadog.jmxfetch.test:type=DynamicTagTestApp,name=Instance2 + attribute: Version + diff --git a/src/test/resources/jmx_config_dynamic_tags_multiple_per_conf.yaml b/src/test/resources/jmx_config_dynamic_tags_multiple_per_conf.yaml new file mode 100644 index 00000000..54954957 --- /dev/null +++ b/src/test/resources/jmx_config_dynamic_tags_multiple_per_conf.yaml @@ -0,0 +1,28 @@ +init_config: + +instances: + - process_name_regex: .*surefire.* + name: jmx_config_dynamic_tags_multiple_per_conf_test + conf: + # Config with multiple dynamic tags (cluster_id AND version) on the same metric + - include: + domain: org.datadog.jmxfetch.test + type: DynamicTagTestApp + attribute: + Metric: + metric_type: gauge + alias: test.metric.with.multiple.tags + tags: + env: test + dynamic_tags: + - tag_name: cluster_id + bean_name: org.datadog.jmxfetch.test:type=DynamicTagTestApp + attribute: ClusterId + - tag_name: version + bean_name: org.datadog.jmxfetch.test:type=DynamicTagTestApp + attribute: Version + - tag_name: port + bean_name: org.datadog.jmxfetch.test:type=DynamicTagTestApp + attribute: Port + +