Skip to content

Commit

Permalink
Support exposing atttributes as labels
Browse files Browse the repository at this point in the history
Co-authored-by: Karina Calma <[email protected]>
Signed-off-by: Martin Bickel <[email protected]>
  • Loading branch information
2 people authored and Martin Bickel committed Aug 28, 2024
1 parent f89aa7a commit d60a828
Show file tree
Hide file tree
Showing 3 changed files with 93 additions and 3 deletions.
30 changes: 28 additions & 2 deletions collector/src/main/java/io/prometheus/jmx/JmxCollector.java
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,7 @@ static class Rule {
String type = "UNKNOWN";
ArrayList<String> labelNames;
ArrayList<String> labelValues;
ArrayList<String> attributesAsLabels;
}

private static class Config {
Expand Down Expand Up @@ -348,6 +349,13 @@ 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 @@ -469,7 +477,8 @@ private MatchedRule defaultExport(
String help,
Double value,
double valueFactor,
String type) {
String type,
Map<String, String> attributesAsLabelsWithValues) {
StringBuilder name = new StringBuilder();
name.append(domain);
if (beanProperties.size() > 0) {
Expand Down Expand Up @@ -504,6 +513,7 @@ private MatchedRule defaultExport(
labelValues.add(entry.getValue());
}
}
addAttributesAsLabelsWithValuesToLabels(config, attributesAsLabelsWithValues, labelNames, labelValues);

return new MatchedRule(
fullname, matchName, type, help, labelNames, labelValues, value, valueFactor);
Expand All @@ -512,6 +522,7 @@ private MatchedRule defaultExport(
public void recordBean(
String domain,
LinkedHashMap<String, String> beanProperties,
Map<String, String> attributesAsLabelsWithValues,
LinkedList<String> attrKeys,
String attrName,
String attrType,
Expand Down Expand Up @@ -609,7 +620,8 @@ public void recordBean(
help,
value,
rule.valueFactor,
rule.type);
rule.type,
attributesAsLabelsWithValues);
addToCache(rule, matchName, matchedRule);
break;
}
Expand All @@ -631,6 +643,7 @@ public void recordBean(
// Set the labels.
ArrayList<String> labelNames = new ArrayList<>();
ArrayList<String> labelValues = new ArrayList<>();
addAttributesAsLabelsWithValuesToLabels(config, attributesAsLabelsWithValues, labelNames, labelValues);
if (rule.labelNames != null) {
for (int i = 0; i < rule.labelNames.size(); i++) {
final String unsafeLabelName = rule.labelNames.get(i);
Expand Down Expand Up @@ -705,6 +718,18 @@ public void recordBean(
}
}

private static void addAttributesAsLabelsWithValuesToLabels(Config config, Map<String, String> attributesAsLabelsWithValues, List<String> labelNames, List<String> labelValues) {
attributesAsLabelsWithValues.forEach(
(attributeAsLabelName, attributeValue) -> {
String labelName = safeName(attributeAsLabelName);
if (config.lowercaseOutputLabelNames) {
labelName = labelName.toLowerCase();
}
labelNames.add(labelName);
labelValues.add(attributeValue);
});
}

@Override
public MetricSnapshots collect() {
// Take a reference to the current config and collect with this one
Expand All @@ -725,6 +750,7 @@ public MetricSnapshots collect() {
config.includeObjectNames,
config.excludeObjectNames,
config.objectNameAttributeFilter,
config.rules,
receiver,
jmxMBeanPropertyCache);

Expand Down
61 changes: 60 additions & 1 deletion collector/src/main/java/io/prometheus/jmx/JmxScraper.java
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,8 @@
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;
import javax.management.JMException;
Expand Down Expand Up @@ -58,6 +60,7 @@ public interface MBeanReceiver {
void recordBean(
String domain,
LinkedHashMap<String, String> beanProperties,
Map<String, String> attributesAsLabelsWithValues,
LinkedList<String> attrKeys,
String attrName,
String attrType,
Expand All @@ -71,6 +74,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 ObjectNameAttributeFilter objectNameAttributeFilter;
private final JmxMBeanPropertyCache jmxMBeanPropertyCache;

Expand All @@ -82,6 +86,7 @@ public JmxScraper(
List<ObjectName> includeObjectNames,
List<ObjectName> excludeObjectNames,
ObjectNameAttributeFilter objectNameAttributeFilter,
List<JmxCollector.Rule> rules,
MBeanReceiver receiver,
JmxMBeanPropertyCache jmxMBeanPropertyCache) {
this.jmxUrl = jmxUrl;
Expand All @@ -91,6 +96,7 @@ public JmxScraper(
this.ssl = ssl;
this.includeObjectNames = includeObjectNames;
this.excludeObjectNames = excludeObjectNames;
this.rules = rules;
this.objectNameAttributeFilter = objectNameAttributeFilter;
this.jmxMBeanPropertyCache = jmxMBeanPropertyCache;
}
Expand Down Expand Up @@ -217,6 +223,7 @@ 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));

for (Object object : attributes) {
// The contents of an AttributeList should all be Attribute instances, but we'll verify
Expand All @@ -237,13 +244,16 @@ 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());
processBeanValue(
mBeanName,
mBeanDomain,
jmxMBeanPropertyCache.getKeyPropertyList(mBeanName),
attributesAsLabelsWithValues,
new LinkedList<>(),
mBeanAttributeInfo.getName(),
mBeanAttributeInfo.getType(),
Expand All @@ -264,6 +274,45 @@ 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;
}
}
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 String angleBrackets(String s) {
return "<" + s.substring(1, s.length() - 1) + ">";
}

private void processAttributesOneByOne(
MBeanServerConnection beanConn,
ObjectName mbeanName,
Expand All @@ -282,6 +331,7 @@ private void processAttributesOneByOne(
mbeanName,
mbeanName.getDomain(),
jmxMBeanPropertyCache.getKeyPropertyList(mbeanName),
new HashMap<>(),
new LinkedList<>(),
attr.getName(),
attr.getType(),
Expand All @@ -299,6 +349,7 @@ private void processBeanValue(
ObjectName objectName,
String domain,
LinkedHashMap<String, String> beanProperties,
Map<String, String> attributesAsLabelsWithValues,
LinkedList<String> attrKeys,
String attrName,
String attrType,
Expand All @@ -316,7 +367,7 @@ private void processBeanValue(
}
LOGGER.log(FINE, "%s%s%s scrape: %s", domain, beanProperties, attrName, value);
this.receiver.recordBean(
domain, beanProperties, attrKeys, attrName, attrType, attrDescription, value);
domain, beanProperties, attributesAsLabelsWithValues, attrKeys, attrName, attrType, attrDescription, value);
} else if (value instanceof CompositeData) {
LOGGER.log(FINE, "%s%s%s scrape: compositedata", domain, beanProperties, attrName);
CompositeData composite = (CompositeData) value;
Expand All @@ -330,6 +381,7 @@ private void processBeanValue(
objectName,
domain,
beanProperties,
attributesAsLabelsWithValues,
attrKeys,
key,
typ,
Expand Down Expand Up @@ -396,6 +448,7 @@ private void processBeanValue(
objectName,
domain,
l2s,
attributesAsLabelsWithValues,
attrNames,
name,
typ,
Expand All @@ -416,6 +469,7 @@ private void processBeanValue(
objectName,
domain,
beanProperties,
attributesAsLabelsWithValues,
attrKeys,
attrName,
attrType,
Expand All @@ -428,6 +482,7 @@ private void processBeanValue(
objectName,
domain,
beanProperties,
attributesAsLabelsWithValues,
attrKeys,
attrName,
attrType,
Expand All @@ -443,6 +498,7 @@ private static class StdoutWriter implements MBeanReceiver {
public void recordBean(
String domain,
LinkedHashMap<String, String> beanProperties,
Map<String, String> attributesAsLabelsWithValues,
LinkedList<String> attrKeys,
String attrName,
String attrType,
Expand All @@ -467,6 +523,7 @@ public static void main(String[] args) throws Exception {
objectNames,
new LinkedList<>(),
objectNameAttributeFilter,
new LinkedList<>(),
new StdoutWriter(),
new JmxMBeanPropertyCache())
.doScrape();
Expand All @@ -479,6 +536,7 @@ public static void main(String[] args) throws Exception {
objectNames,
new LinkedList<>(),
objectNameAttributeFilter,
new LinkedList<>(),
new StdoutWriter(),
new JmxMBeanPropertyCache())
.doScrape();
Expand All @@ -491,6 +549,7 @@ public static void main(String[] args) throws Exception {
objectNames,
new LinkedList<>(),
objectNameAttributeFilter,
new LinkedList<>(),
new StdoutWriter(),
new JmxMBeanPropertyCache())
.doScrape();
Expand Down
5 changes: 5 additions & 0 deletions docs/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -119,6 +119,10 @@ rules:
cache: false
type: GAUGE
attrNameSnakeCase: false
attributesAsLabels:
- string1
- string2
```
Name | Description
---------|------------
Expand All @@ -144,6 +148,7 @@ 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.

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 d60a828

Please sign in to comment.