Skip to content

Commit d60a828

Browse files
MaBiContikarina-calma
authored andcommitted
Support exposing atttributes as labels
Co-authored-by: Karina Calma <[email protected]> Signed-off-by: Martin Bickel <[email protected]>
1 parent f89aa7a commit d60a828

File tree

3 files changed

+93
-3
lines changed

3 files changed

+93
-3
lines changed

collector/src/main/java/io/prometheus/jmx/JmxCollector.java

Lines changed: 28 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,7 @@ static class Rule {
7171
String type = "UNKNOWN";
7272
ArrayList<String> labelNames;
7373
ArrayList<String> labelValues;
74+
ArrayList<String> attributesAsLabels;
7475
}
7576

7677
private static class Config {
@@ -348,6 +349,13 @@ private Config loadConfig(Map<String, Object> yamlConfig) throws MalformedObject
348349
}
349350
}
350351

352+
if (yamlRule.containsKey("attributesAsLabels")) {
353+
List<String> attributes = (List<String>) yamlRule.get("attributesAsLabels");
354+
rule.attributesAsLabels = new ArrayList<>();
355+
if (attributes != null) {
356+
rule.attributesAsLabels.addAll(attributes);
357+
}
358+
}
351359
// Validation.
352360
if ((rule.labelNames != null || rule.help != null) && rule.name == null) {
353361
throw new IllegalArgumentException(
@@ -469,7 +477,8 @@ private MatchedRule defaultExport(
469477
String help,
470478
Double value,
471479
double valueFactor,
472-
String type) {
480+
String type,
481+
Map<String, String> attributesAsLabelsWithValues) {
473482
StringBuilder name = new StringBuilder();
474483
name.append(domain);
475484
if (beanProperties.size() > 0) {
@@ -504,6 +513,7 @@ private MatchedRule defaultExport(
504513
labelValues.add(entry.getValue());
505514
}
506515
}
516+
addAttributesAsLabelsWithValuesToLabels(config, attributesAsLabelsWithValues, labelNames, labelValues);
507517

508518
return new MatchedRule(
509519
fullname, matchName, type, help, labelNames, labelValues, value, valueFactor);
@@ -512,6 +522,7 @@ private MatchedRule defaultExport(
512522
public void recordBean(
513523
String domain,
514524
LinkedHashMap<String, String> beanProperties,
525+
Map<String, String> attributesAsLabelsWithValues,
515526
LinkedList<String> attrKeys,
516527
String attrName,
517528
String attrType,
@@ -609,7 +620,8 @@ public void recordBean(
609620
help,
610621
value,
611622
rule.valueFactor,
612-
rule.type);
623+
rule.type,
624+
attributesAsLabelsWithValues);
613625
addToCache(rule, matchName, matchedRule);
614626
break;
615627
}
@@ -631,6 +643,7 @@ public void recordBean(
631643
// Set the labels.
632644
ArrayList<String> labelNames = new ArrayList<>();
633645
ArrayList<String> labelValues = new ArrayList<>();
646+
addAttributesAsLabelsWithValuesToLabels(config, attributesAsLabelsWithValues, labelNames, labelValues);
634647
if (rule.labelNames != null) {
635648
for (int i = 0; i < rule.labelNames.size(); i++) {
636649
final String unsafeLabelName = rule.labelNames.get(i);
@@ -705,6 +718,18 @@ public void recordBean(
705718
}
706719
}
707720

721+
private static void addAttributesAsLabelsWithValuesToLabels(Config config, Map<String, String> attributesAsLabelsWithValues, List<String> labelNames, List<String> labelValues) {
722+
attributesAsLabelsWithValues.forEach(
723+
(attributeAsLabelName, attributeValue) -> {
724+
String labelName = safeName(attributeAsLabelName);
725+
if (config.lowercaseOutputLabelNames) {
726+
labelName = labelName.toLowerCase();
727+
}
728+
labelNames.add(labelName);
729+
labelValues.add(attributeValue);
730+
});
731+
}
732+
708733
@Override
709734
public MetricSnapshots collect() {
710735
// Take a reference to the current config and collect with this one
@@ -725,6 +750,7 @@ public MetricSnapshots collect() {
725750
config.includeObjectNames,
726751
config.excludeObjectNames,
727752
config.objectNameAttributeFilter,
753+
config.rules,
728754
receiver,
729755
jmxMBeanPropertyCache);
730756

collector/src/main/java/io/prometheus/jmx/JmxScraper.java

Lines changed: 60 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,8 @@
3131
import java.util.Optional;
3232
import java.util.Set;
3333
import java.util.TreeSet;
34+
import java.util.regex.Matcher;
35+
import java.util.stream.Collectors;
3436
import javax.management.Attribute;
3537
import javax.management.AttributeList;
3638
import javax.management.JMException;
@@ -58,6 +60,7 @@ public interface MBeanReceiver {
5860
void recordBean(
5961
String domain,
6062
LinkedHashMap<String, String> beanProperties,
63+
Map<String, String> attributesAsLabelsWithValues,
6164
LinkedList<String> attrKeys,
6265
String attrName,
6366
String attrType,
@@ -71,6 +74,7 @@ void recordBean(
7174
private final String password;
7275
private final boolean ssl;
7376
private final List<ObjectName> includeObjectNames, excludeObjectNames;
77+
private final List<JmxCollector.Rule> rules;
7478
private final ObjectNameAttributeFilter objectNameAttributeFilter;
7579
private final JmxMBeanPropertyCache jmxMBeanPropertyCache;
7680

@@ -82,6 +86,7 @@ public JmxScraper(
8286
List<ObjectName> includeObjectNames,
8387
List<ObjectName> excludeObjectNames,
8488
ObjectNameAttributeFilter objectNameAttributeFilter,
89+
List<JmxCollector.Rule> rules,
8590
MBeanReceiver receiver,
8691
JmxMBeanPropertyCache jmxMBeanPropertyCache) {
8792
this.jmxUrl = jmxUrl;
@@ -91,6 +96,7 @@ public JmxScraper(
9196
this.ssl = ssl;
9297
this.includeObjectNames = includeObjectNames;
9398
this.excludeObjectNames = excludeObjectNames;
99+
this.rules = rules;
94100
this.objectNameAttributeFilter = objectNameAttributeFilter;
95101
this.jmxMBeanPropertyCache = jmxMBeanPropertyCache;
96102
}
@@ -217,6 +223,7 @@ private void scrapeBean(MBeanServerConnection beanConn, ObjectName mBeanName) {
217223

218224
final String mBeanNameString = mBeanName.toString();
219225
final String mBeanDomain = mBeanName.getDomain();
226+
Map<String, Object> attributeMap = attributes.asList().stream().collect(Collectors.toMap(Attribute::getName, Attribute::getValue));
220227

221228
for (Object object : attributes) {
222229
// The contents of an AttributeList should all be Attribute instances, but we'll verify
@@ -237,13 +244,16 @@ private void scrapeBean(MBeanServerConnection beanConn, ObjectName mBeanName) {
237244
continue;
238245
}
239246

247+
Map<String, String> attributesAsLabelsWithValues = getAttributesAsLabelsWithValues(mBeanName, attribute, attributeMap);
248+
240249
MBeanAttributeInfo mBeanAttributeInfo =
241250
name2MBeanAttributeInfo.get(attribute.getName());
242251
LOGGER.log(FINE, "%s_%s process", mBeanName, mBeanAttributeInfo.getName());
243252
processBeanValue(
244253
mBeanName,
245254
mBeanDomain,
246255
jmxMBeanPropertyCache.getKeyPropertyList(mBeanName),
256+
attributesAsLabelsWithValues,
247257
new LinkedList<>(),
248258
mBeanAttributeInfo.getName(),
249259
mBeanAttributeInfo.getType(),
@@ -264,6 +274,45 @@ private void scrapeBean(MBeanServerConnection beanConn, ObjectName mBeanName) {
264274
}
265275
}
266276

277+
private Map<String, String> getAttributesAsLabelsWithValues(ObjectName mBeanName, Attribute attribute, Map<String, Object> attributeMap) {
278+
JmxCollector.Rule matchedRule = null;
279+
for (JmxCollector.Rule rule : rules) {
280+
if (rule.pattern != null) {
281+
Object matchBeanValue = rule.cache ? "<cache>" : attribute.getValue();
282+
List<String> attrKeys = new LinkedList<>();
283+
if (attribute.getValue() instanceof TabularData || attribute.getValue() instanceof CompositeData) {
284+
attrKeys.add(attribute.getName());
285+
}
286+
String beanName = mBeanName.getDomain()
287+
+ angleBrackets(jmxMBeanPropertyCache.getKeyPropertyList(mBeanName).toString())
288+
+ angleBrackets(attrKeys.toString());
289+
String matchName = beanName + attribute.getName() + ": " + matchBeanValue;
290+
Matcher matcher = rule.pattern.matcher(matchName);
291+
if (matcher.matches() && rule.attributesAsLabels != null) {
292+
matchedRule = rule;
293+
}
294+
} else if (rule.name == null) {
295+
matchedRule = rule;
296+
}
297+
}
298+
Map<String, String> attributesAsLabelsWithValues = new HashMap<>();
299+
if (matchedRule != null) {
300+
for (String attributeAsLabel : matchedRule.attributesAsLabels) {
301+
Object attrValue = attributeMap.get(attributeAsLabel);
302+
if (attrValue != null) {
303+
attributesAsLabelsWithValues.put(
304+
attributeAsLabel,
305+
attrValue.toString());
306+
}
307+
}
308+
}
309+
return attributesAsLabelsWithValues;
310+
}
311+
312+
private String angleBrackets(String s) {
313+
return "<" + s.substring(1, s.length() - 1) + ">";
314+
}
315+
267316
private void processAttributesOneByOne(
268317
MBeanServerConnection beanConn,
269318
ObjectName mbeanName,
@@ -282,6 +331,7 @@ private void processAttributesOneByOne(
282331
mbeanName,
283332
mbeanName.getDomain(),
284333
jmxMBeanPropertyCache.getKeyPropertyList(mbeanName),
334+
new HashMap<>(),
285335
new LinkedList<>(),
286336
attr.getName(),
287337
attr.getType(),
@@ -299,6 +349,7 @@ private void processBeanValue(
299349
ObjectName objectName,
300350
String domain,
301351
LinkedHashMap<String, String> beanProperties,
352+
Map<String, String> attributesAsLabelsWithValues,
302353
LinkedList<String> attrKeys,
303354
String attrName,
304355
String attrType,
@@ -316,7 +367,7 @@ private void processBeanValue(
316367
}
317368
LOGGER.log(FINE, "%s%s%s scrape: %s", domain, beanProperties, attrName, value);
318369
this.receiver.recordBean(
319-
domain, beanProperties, attrKeys, attrName, attrType, attrDescription, value);
370+
domain, beanProperties, attributesAsLabelsWithValues, attrKeys, attrName, attrType, attrDescription, value);
320371
} else if (value instanceof CompositeData) {
321372
LOGGER.log(FINE, "%s%s%s scrape: compositedata", domain, beanProperties, attrName);
322373
CompositeData composite = (CompositeData) value;
@@ -330,6 +381,7 @@ private void processBeanValue(
330381
objectName,
331382
domain,
332383
beanProperties,
384+
attributesAsLabelsWithValues,
333385
attrKeys,
334386
key,
335387
typ,
@@ -396,6 +448,7 @@ private void processBeanValue(
396448
objectName,
397449
domain,
398450
l2s,
451+
attributesAsLabelsWithValues,
399452
attrNames,
400453
name,
401454
typ,
@@ -416,6 +469,7 @@ private void processBeanValue(
416469
objectName,
417470
domain,
418471
beanProperties,
472+
attributesAsLabelsWithValues,
419473
attrKeys,
420474
attrName,
421475
attrType,
@@ -428,6 +482,7 @@ private void processBeanValue(
428482
objectName,
429483
domain,
430484
beanProperties,
485+
attributesAsLabelsWithValues,
431486
attrKeys,
432487
attrName,
433488
attrType,
@@ -443,6 +498,7 @@ private static class StdoutWriter implements MBeanReceiver {
443498
public void recordBean(
444499
String domain,
445500
LinkedHashMap<String, String> beanProperties,
501+
Map<String, String> attributesAsLabelsWithValues,
446502
LinkedList<String> attrKeys,
447503
String attrName,
448504
String attrType,
@@ -467,6 +523,7 @@ public static void main(String[] args) throws Exception {
467523
objectNames,
468524
new LinkedList<>(),
469525
objectNameAttributeFilter,
526+
new LinkedList<>(),
470527
new StdoutWriter(),
471528
new JmxMBeanPropertyCache())
472529
.doScrape();
@@ -479,6 +536,7 @@ public static void main(String[] args) throws Exception {
479536
objectNames,
480537
new LinkedList<>(),
481538
objectNameAttributeFilter,
539+
new LinkedList<>(),
482540
new StdoutWriter(),
483541
new JmxMBeanPropertyCache())
484542
.doScrape();
@@ -491,6 +549,7 @@ public static void main(String[] args) throws Exception {
491549
objectNames,
492550
new LinkedList<>(),
493551
objectNameAttributeFilter,
552+
new LinkedList<>(),
494553
new StdoutWriter(),
495554
new JmxMBeanPropertyCache())
496555
.doScrape();

docs/README.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -119,6 +119,10 @@ rules:
119119
cache: false
120120
type: GAUGE
121121
attrNameSnakeCase: false
122+
attributesAsLabels:
123+
- string1
124+
- string2
125+
122126
```
123127
Name | Description
124128
---------|------------
@@ -144,6 +148,7 @@ labels | A map of label name to label value pairs. Capture groups fro
144148
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.
145149
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`.
146150
type | The type of the metric, can be `GAUGE`, `COUNTER` or `UNTYPED`. `name` must be set to use this. Defaults to `UNTYPED`.
151+
attributesAsLabels | A list of attributes from an mBean which will be added as labels for all the metrics of that mBean. Defaults to none.
147152

148153
Metric names and label names are sanitized. All characters other than `[a-zA-Z0-9:_]` are replaced with underscores,
149154
and adjacent underscores are collapsed. There's no limitations on label values or the help text.

0 commit comments

Comments
 (0)