Skip to content

Commit

Permalink
Replace pattern matching with string comparison
Browse files Browse the repository at this point in the history
  • Loading branch information
karina-calma committed Nov 20, 2024
1 parent 3071423 commit 3cef693
Show file tree
Hide file tree
Showing 3 changed files with 76 additions and 51 deletions.
45 changes: 35 additions & 10 deletions collector/src/main/java/io/prometheus/jmx/JmxCollector.java
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,16 @@ static class Rule {
String type = "UNKNOWN";
ArrayList<String> labelNames;
ArrayList<String> labelValues;
ArrayList<String> attributesAsLabels;
}

public static class MetricCustomizer {
MBeanFilter mbeanFilter;
List<String> attributesAsLabels;
}

public static class MBeanFilter {
String domain;
Map<String, String> properties;
}

private static class Config {
Expand All @@ -87,7 +96,7 @@ private static class Config {
ObjectNameAttributeFilter objectNameAttributeFilter;
List<Rule> rules = new ArrayList<>();
long lastUpdate = 0L;

List<MetricCustomizer> metricCustomizers = new ArrayList<>();
MatchedRulesCache rulesCache;
}

Expand Down Expand Up @@ -297,6 +306,29 @@ private Config loadConfig(Map<String, Object> yamlConfig) throws MalformedObject
}
}

if (yamlConfig.containsKey("metricCustomizers")) {
List<Map<String, Object>> metricCustomizersYaml =
(List<Map<String, Object>>) yamlConfig.get("metricCustomizers");
for (Map<String, Object> metricCustomizerYaml : metricCustomizersYaml) {
Map<String, Object> mbeanFilterYaml =
(Map<String, Object>) metricCustomizerYaml.get("mbeanFilter");
MBeanFilter mbeanFilter = new MBeanFilter();
mbeanFilter.domain = (String) mbeanFilterYaml.get("domain");
mbeanFilter.properties = (Map<String, String>) mbeanFilterYaml.get("properties");

List<String> attributesAsLabels =
(List<String>) 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<Map<String, Object>> configRules =
(List<Map<String, Object>>) yamlConfig.get("rules");
Expand Down Expand Up @@ -349,13 +381,6 @@ private Config loadConfig(Map<String, Object> yamlConfig) throws MalformedObject
}
}

if (yamlRule.containsKey("attributesAsLabels")) {
List<String> attributes = (List<String>) 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(
Expand Down Expand Up @@ -750,7 +775,7 @@ public MetricSnapshots collect() {
config.includeObjectNames,
config.excludeObjectNames,
config.objectNameAttributeFilter,
config.rules,
config.metricCustomizers,
receiver,
jmxMBeanPropertyCache);

Expand Down
66 changes: 29 additions & 37 deletions collector/src/main/java/io/prometheus/jmx/JmxScraper.java
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -74,7 +73,7 @@ void recordBean(
private final String password;
private final boolean ssl;
private final List<ObjectName> includeObjectNames, excludeObjectNames;
private final List<JmxCollector.Rule> rules;
private final List<JmxCollector.MetricCustomizer> metricCustomizers;
private final ObjectNameAttributeFilter objectNameAttributeFilter;
private final JmxMBeanPropertyCache jmxMBeanPropertyCache;

Expand All @@ -86,7 +85,7 @@ public JmxScraper(
List<ObjectName> includeObjectNames,
List<ObjectName> excludeObjectNames,
ObjectNameAttributeFilter objectNameAttributeFilter,
List<JmxCollector.Rule> rules,
List<JmxCollector.MetricCustomizer> metricCustomizers,
MBeanReceiver receiver,
JmxMBeanPropertyCache jmxMBeanPropertyCache) {
this.jmxUrl = jmxUrl;
Expand All @@ -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;
}
Expand Down Expand Up @@ -225,7 +224,12 @@ private void scrapeBean(MBeanServerConnection beanConn, ObjectName mBeanName) {

final String mBeanNameString = mBeanName.toString();
final String mBeanDomain = mBeanName.getDomain();
Map<String, Object> attributeMap = attributes.asList().stream().collect(Collectors.toMap(Attribute::getName, Attribute::getValue));
JmxCollector.MetricCustomizer metricCustomizer = getMetricCustomizer(mBeanName);
Map<String, String> 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
Expand All @@ -246,8 +250,6 @@ private void scrapeBean(MBeanServerConnection beanConn, ObjectName mBeanName) {
continue;
}

Map<String, String> attributesAsLabelsWithValues = getAttributesAsLabelsWithValues(mBeanName, attribute, attributeMap);

MBeanAttributeInfo mBeanAttributeInfo =
name2MBeanAttributeInfo.get(attribute.getName());
LOGGER.log(FINE, "%s_%s process", mBeanName, mBeanAttributeInfo.getName());
Expand Down Expand Up @@ -276,43 +278,33 @@ private void scrapeBean(MBeanServerConnection beanConn, ObjectName mBeanName) {
}
}

private Map<String, String> getAttributesAsLabelsWithValues(ObjectName mBeanName, Attribute attribute, Map<String, Object> attributeMap) {
JmxCollector.Rule matchedRule = null;
for (JmxCollector.Rule rule : rules) {
if (rule.pattern != null) {
Object matchBeanValue = rule.cache ? "<cache>" : attribute.getValue();
List<String> 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<String, String> getAttributesAsLabelsWithValues(JmxCollector.MetricCustomizer metricCustomizer, AttributeList attributes) {
Map<String, Object> attributeMap = attributes.asList().stream()
.collect(Collectors.toMap(Attribute::getName, Attribute::getValue));
Map<String, String> attributesAsLabelsWithValues = new HashMap<>();
for (String attributeAsLabel : metricCustomizer.attributesAsLabels) {
Object attrValue = attributeMap.get(attributeAsLabel);
if (attrValue != null) {
attributesAsLabelsWithValues.put(attributeAsLabel, attrValue.toString());
}
}
Map<String, String> 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(
Expand Down
16 changes: 12 additions & 4 deletions docs/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,14 @@ excludeObjectNameAttributes:
"java.lang:type=Runtime":
- "ClassPath"
- "SystemProperties"
metricCustomizers:
- mbeanFilter:
domain: org.apache.cassandra.metrics
properties:
type: <type>
attributesAsLabels:
- string1
- string2
rules:
- pattern: 'org.apache.cassandra.metrics<type=(\w+), name=(\w+)><>Value: (\d+)'
name: cassandra_$1_$2
Expand All @@ -119,9 +127,6 @@ rules:
cache: false
type: GAUGE
attrNameSnakeCase: false
attributesAsLabels:
- string1
- string2
```
Name | Description
Expand All @@ -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.
Expand Down

0 comments on commit 3cef693

Please sign in to comment.