diff --git a/collector/src/main/java/io/prometheus/jmx/JmxCollector.java b/collector/src/main/java/io/prometheus/jmx/JmxCollector.java index 1c7281bd..dd63bd92 100644 --- a/collector/src/main/java/io/prometheus/jmx/JmxCollector.java +++ b/collector/src/main/java/io/prometheus/jmx/JmxCollector.java @@ -71,7 +71,16 @@ static class Rule { String type = "UNKNOWN"; ArrayList labelNames; ArrayList labelValues; - ArrayList attributesAsLabels; + } + + public static class MetricCustomizer { + MBeanFilter mbeanFilter; + List attributesAsLabels; + } + + public static class MBeanFilter { + String domain; + Map properties; } private static class Config { @@ -87,7 +96,7 @@ private static class Config { ObjectNameAttributeFilter objectNameAttributeFilter; List rules = new ArrayList<>(); long lastUpdate = 0L; - + List metricCustomizers = new ArrayList<>(); MatchedRulesCache rulesCache; } @@ -297,6 +306,29 @@ private Config loadConfig(Map yamlConfig) throws MalformedObject } } + if (yamlConfig.containsKey("metricCustomizers")) { + List> metricCustomizersYaml = + (List>) yamlConfig.get("metricCustomizers"); + for (Map metricCustomizerYaml : metricCustomizersYaml) { + Map mbeanFilterYaml = + (Map) metricCustomizerYaml.get("mbeanFilter"); + MBeanFilter mbeanFilter = new MBeanFilter(); + mbeanFilter.domain = (String) mbeanFilterYaml.get("domain"); + mbeanFilter.properties = (Map) mbeanFilterYaml.get("properties"); + + List attributesAsLabels = + (List) metricCustomizerYaml.get("attributesAsLabels"); + if (attributesAsLabels == null) { + attributesAsLabels = new ArrayList<>(); + } + + MetricCustomizer metricCustomizer = new MetricCustomizer(); + metricCustomizer.mbeanFilter = mbeanFilter; + metricCustomizer.attributesAsLabels = attributesAsLabels; + cfg.metricCustomizers.add(metricCustomizer); + } + } + if (yamlConfig.containsKey("rules")) { List> configRules = (List>) yamlConfig.get("rules"); @@ -349,13 +381,6 @@ private Config loadConfig(Map yamlConfig) throws MalformedObject } } - if (yamlRule.containsKey("attributesAsLabels")) { - List attributes = (List) yamlRule.get("attributesAsLabels"); - rule.attributesAsLabels = new ArrayList<>(); - if (attributes != null) { - rule.attributesAsLabels.addAll(attributes); - } - } // Validation. if ((rule.labelNames != null || rule.help != null) && rule.name == null) { throw new IllegalArgumentException( @@ -750,7 +775,7 @@ public MetricSnapshots collect() { config.includeObjectNames, config.excludeObjectNames, config.objectNameAttributeFilter, - config.rules, + config.metricCustomizers, receiver, jmxMBeanPropertyCache); diff --git a/collector/src/main/java/io/prometheus/jmx/JmxScraper.java b/collector/src/main/java/io/prometheus/jmx/JmxScraper.java index 6b84890a..67a4e46d 100644 --- a/collector/src/main/java/io/prometheus/jmx/JmxScraper.java +++ b/collector/src/main/java/io/prometheus/jmx/JmxScraper.java @@ -31,7 +31,6 @@ import java.util.Optional; import java.util.Set; import java.util.TreeSet; -import java.util.regex.Matcher; import java.util.stream.Collectors; import javax.management.Attribute; import javax.management.AttributeList; @@ -74,7 +73,7 @@ void recordBean( private final String password; private final boolean ssl; private final List includeObjectNames, excludeObjectNames; - private final List rules; + private final List metricCustomizers; private final ObjectNameAttributeFilter objectNameAttributeFilter; private final JmxMBeanPropertyCache jmxMBeanPropertyCache; @@ -86,7 +85,7 @@ public JmxScraper( List includeObjectNames, List excludeObjectNames, ObjectNameAttributeFilter objectNameAttributeFilter, - List rules, + List metricCustomizers, MBeanReceiver receiver, JmxMBeanPropertyCache jmxMBeanPropertyCache) { this.jmxUrl = jmxUrl; @@ -96,7 +95,7 @@ public JmxScraper( this.ssl = ssl; this.includeObjectNames = includeObjectNames; this.excludeObjectNames = excludeObjectNames; - this.rules = rules; + this.metricCustomizers = metricCustomizers; this.objectNameAttributeFilter = objectNameAttributeFilter; this.jmxMBeanPropertyCache = jmxMBeanPropertyCache; } @@ -225,7 +224,12 @@ private void scrapeBean(MBeanServerConnection beanConn, ObjectName mBeanName) { final String mBeanNameString = mBeanName.toString(); final String mBeanDomain = mBeanName.getDomain(); - Map attributeMap = attributes.asList().stream().collect(Collectors.toMap(Attribute::getName, Attribute::getValue)); + JmxCollector.MetricCustomizer metricCustomizer = getMetricCustomizer(mBeanName); + Map attributesAsLabelsWithValues = new HashMap<>(); + if (metricCustomizer != null) { + attributesAsLabelsWithValues = + getAttributesAsLabelsWithValues(metricCustomizer, attributes); + } for (Object object : attributes) { // The contents of an AttributeList should all be Attribute instances, but we'll verify @@ -246,8 +250,6 @@ private void scrapeBean(MBeanServerConnection beanConn, ObjectName mBeanName) { continue; } - Map attributesAsLabelsWithValues = getAttributesAsLabelsWithValues(mBeanName, attribute, attributeMap); - MBeanAttributeInfo mBeanAttributeInfo = name2MBeanAttributeInfo.get(attribute.getName()); LOGGER.log(FINE, "%s_%s process", mBeanName, mBeanAttributeInfo.getName()); @@ -276,43 +278,33 @@ private void scrapeBean(MBeanServerConnection beanConn, ObjectName mBeanName) { } } - private Map getAttributesAsLabelsWithValues(ObjectName mBeanName, Attribute attribute, Map attributeMap) { - JmxCollector.Rule matchedRule = null; - for (JmxCollector.Rule rule : rules) { - if (rule.pattern != null) { - Object matchBeanValue = rule.cache ? "" : attribute.getValue(); - List attrKeys = new LinkedList<>(); - if (attribute.getValue() instanceof TabularData || attribute.getValue() instanceof CompositeData) { - attrKeys.add(attribute.getName()); - } - String beanName = mBeanName.getDomain() - + angleBrackets(jmxMBeanPropertyCache.getKeyPropertyList(mBeanName).toString()) - + angleBrackets(attrKeys.toString()); - String matchName = beanName + attribute.getName() + ": " + matchBeanValue; - Matcher matcher = rule.pattern.matcher(matchName); - if (matcher.matches() && rule.attributesAsLabels != null) { - matchedRule = rule; - } - } else if (rule.name == null) { - matchedRule = rule; + private Map getAttributesAsLabelsWithValues(JmxCollector.MetricCustomizer metricCustomizer, AttributeList attributes) { + Map attributeMap = attributes.asList().stream() + .collect(Collectors.toMap(Attribute::getName, Attribute::getValue)); + Map attributesAsLabelsWithValues = new HashMap<>(); + for (String attributeAsLabel : metricCustomizer.attributesAsLabels) { + Object attrValue = attributeMap.get(attributeAsLabel); + if (attrValue != null) { + attributesAsLabelsWithValues.put(attributeAsLabel, attrValue.toString()); } } - Map attributesAsLabelsWithValues = new HashMap<>(); - if (matchedRule != null) { - for (String attributeAsLabel : matchedRule.attributesAsLabels) { - Object attrValue = attributeMap.get(attributeAsLabel); - if (attrValue != null) { - attributesAsLabelsWithValues.put( - attributeAsLabel, - attrValue.toString()); + return attributesAsLabelsWithValues; + } + + private JmxCollector.MetricCustomizer getMetricCustomizer(ObjectName mBeanName) { + if (!metricCustomizers.isEmpty()) { + for (JmxCollector.MetricCustomizer metricCustomizer : metricCustomizers) { + if (filterMbeanByDomainAndProperties(mBeanName, metricCustomizer)) { + return metricCustomizer; } } } - return attributesAsLabelsWithValues; + return null; } - private String angleBrackets(String s) { - return "<" + s.substring(1, s.length() - 1) + ">"; + private boolean filterMbeanByDomainAndProperties(ObjectName mBeanName, JmxCollector.MetricCustomizer metricCustomizer) { + return metricCustomizer.mbeanFilter.domain.equals(mBeanName.getDomain()) && + mBeanName.getKeyPropertyList().entrySet().containsAll(metricCustomizer.mbeanFilter.properties.entrySet()); } private void processAttributesOneByOne( diff --git a/docs/README.md b/docs/README.md index 67eb2ab4..27c087e3 100644 --- a/docs/README.md +++ b/docs/README.md @@ -109,6 +109,14 @@ excludeObjectNameAttributes: "java.lang:type=Runtime": - "ClassPath" - "SystemProperties" +metricCustomizers: + - mbeanFilter: + domain: org.apache.cassandra.metrics + properties: + type: + attributesAsLabels: + - string1 + - string2 rules: - pattern: 'org.apache.cassandra.metrics<>Value: (\d+)' name: cassandra_$1_$2 @@ -119,9 +127,6 @@ rules: cache: false type: GAUGE attrNameSnakeCase: false - attributesAsLabels: - - string1 - - string2 ``` Name | Description @@ -148,7 +153,10 @@ labels | A map of label name to label value pairs. Capture groups fro help | Help text for the metric. Capture groups from `pattern` can be used. `name` must be set to use this. Defaults to the mBean attribute description, domain, and name of the attribute. cache | Whether to cache bean name expressions to rule computation (match and mismatch). Not recommended for rules matching on bean value, as only the value from the first scrape will be cached and re-used. This can increase performance when collecting a lot of mbeans. Defaults to `false`. type | The type of the metric, can be `GAUGE`, `COUNTER` or `UNTYPED`. `name` must be set to use this. Defaults to `UNTYPED`. -attributesAsLabels | A list of attributes from an mBean which will be added as labels for all the metrics of that mBean. Defaults to none. +metricCustomizers | A list of objects that contain `mbeanFilter` and `attributesAsLabels`. For those mbeans that match the filter, the items in the `attributesAsLabels` list will be added as attributes to the existing metrics. +domain | Domain of an mbean. +properties | Properties of an mbean. +attributesAsLabels | List of elements to be added as attributes to existing metrics. Metric names and label names are sanitized. All characters other than `[a-zA-Z0-9:_]` are replaced with underscores, and adjacent underscores are collapsed. There's no limitations on label values or the help text.