Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Added MBean attribute exclusion filtering #870

Merged
merged 2 commits into from
Aug 8, 2023
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,12 @@ lowercaseOutputName: false
lowercaseOutputLabelNames: false
includeObjectNames: ["org.apache.cassandra.metrics:*"]
excludeObjectNames: ["org.apache.cassandra.metrics:type=ColumnFamily,*"]
excludeObjectNameAttributes:
"java.lang:type=OperatingSystem":
- "ObjectName"
"java.lang:type=Runtime":
- "ClassPath"
- "SystemProperties"
rules:
- pattern: 'org.apache.cassandra.metrics<type=(\w+), name=(\w+)><>Value: (\d+)'
name: cassandra_$1_$2
Expand All @@ -106,6 +112,8 @@ lowercaseOutputName | Lowercase the output metric name. Applies to default forma
lowercaseOutputLabelNames | Lowercase the output metric label names. Applies to default format and `labels`. Defaults to false.
includeObjectNames | A list of [ObjectNames](http://docs.oracle.com/javase/6/docs/api/javax/management/ObjectName.html) to query. Defaults to all mBeans.
excludeObjectNames | A list of [ObjectNames](http://docs.oracle.com/javase/6/docs/api/javax/management/ObjectName.html) to not query. Takes precedence over `includeObjectNames`. Defaults to none.
excludeObjectNameAttributesDynamic | Whether to use dynamic [ObjectName](http://docs.oracle.com/javase/6/docs/api/javax/management/ObjectName.html) attribute filtering. Defaults to false.
excludeObjectNameAttributes | Optional a map of [ObjectNames](http://docs.oracle.com/javase/6/docs/api/javax/management/ObjectName.html) with a list of attribute names.
rules | A list of rules to apply in order, processing stops at the first matching rule. Attributes that aren't matched aren't collected. If not specified, defaults to collecting everything in the default format.
pattern | Regex pattern to match against each bean attribute. The pattern is not anchored. Capture groups can be used in other options. Defaults to matching everything.
attrNameSnakeCase | Converts the attribute name to snake case. This is seen in the names matched by the pattern and the default format. For example, anAttrName to an\_attr\_name. Defaults to false.
Expand Down
3 changes: 3 additions & 0 deletions collector/src/main/java/io/prometheus/jmx/JmxCollector.java
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,7 @@ private static class Config {
boolean lowercaseOutputLabelNames;
List<ObjectName> includeObjectNames = new ArrayList<>();
List<ObjectName> excludeObjectNames = new ArrayList<>();
ObjectNameAttributeFilter objectNameAttributeFilter;
List<Rule> rules = new ArrayList<>();
long lastUpdate = 0L;

Expand Down Expand Up @@ -323,6 +324,7 @@ private Config loadConfig(Map<String, Object> yamlConfig) throws MalformedObject
}

cfg.rulesCache = new MatchedRulesCache(cfg.rules);
cfg.objectNameAttributeFilter = ObjectNameAttributeFilter.create(yamlConfig);

return cfg;
}
Expand Down Expand Up @@ -756,6 +758,7 @@ public List<MetricFamilySamples> collect() {
config.ssl,
config.includeObjectNames,
config.excludeObjectNames,
config.objectNameAttributeFilter,
receiver,
jmxMBeanPropertyCache);
long start = System.nanoTime();
Expand Down
33 changes: 32 additions & 1 deletion collector/src/main/java/io/prometheus/jmx/JmxScraper.java
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,7 @@ void recordBean(
private final String password;
private final boolean ssl;
private final List<ObjectName> includeObjectNames, excludeObjectNames;
private final ObjectNameAttributeFilter objectNameAttributeFilter;
private final JmxMBeanPropertyCache jmxMBeanPropertyCache;

public JmxScraper(
Expand All @@ -80,6 +81,7 @@ public JmxScraper(
boolean ssl,
List<ObjectName> includeObjectNames,
List<ObjectName> excludeObjectNames,
ObjectNameAttributeFilter objectNameAttributeFilter,
MBeanReceiver receiver,
JmxMBeanPropertyCache jmxMBeanPropertyCache) {
this.jmxUrl = jmxUrl;
Expand All @@ -89,6 +91,7 @@ public JmxScraper(
this.ssl = ssl;
this.includeObjectNames = includeObjectNames;
this.excludeObjectNames = excludeObjectNames;
this.objectNameAttributeFilter = objectNameAttributeFilter;
this.jmxMBeanPropertyCache = jmxMBeanPropertyCache;
}

Expand Down Expand Up @@ -174,9 +177,18 @@ private void scrapeBean(MBeanServerConnection beanConn, ObjectName mBeanName) {
LOGGER.log(FINE, "%s_%s not readable", mBeanName, mBeanAttributeInfo.getName());
continue;
}

if (objectNameAttributeFilter.exclude(mBeanName, mBeanAttributeInfo.getName())) {
continue;
}

name2MBeanAttributeInfo.put(mBeanAttributeInfo.getName(), mBeanAttributeInfo);
}

if (name2MBeanAttributeInfo.isEmpty()) {
return;
}

AttributeList attributes;

try {
Expand Down Expand Up @@ -221,6 +233,7 @@ private void scrapeBean(MBeanServerConnection beanConn, ObjectName mBeanName) {
name2MBeanAttributeInfo.get(attribute.getName());
LOGGER.log(FINE, "%s_%s process", mBeanName, mBeanAttributeInfo.getName());
processBeanValue(
mBeanName,
mBeanName.getDomain(),
jmxMBeanPropertyCache.getKeyPropertyList(mBeanName),
new LinkedList<>(),
Expand Down Expand Up @@ -253,6 +266,7 @@ private void processAttributesOneByOne(

LOGGER.log(FINE, "%s_%s process", mbeanName, attr.getName());
processBeanValue(
mbeanName,
mbeanName.getDomain(),
jmxMBeanPropertyCache.getKeyPropertyList(mbeanName),
new LinkedList<>(),
Expand All @@ -269,6 +283,7 @@ private void processAttributesOneByOne(
* pass of getting the values/names out in a way it can be processed elsewhere easily.
*/
private void processBeanValue(
ObjectName objectName,
String domain,
LinkedHashMap<String, String> beanProperties,
LinkedList<String> attrKeys,
Expand Down Expand Up @@ -299,7 +314,14 @@ private void processBeanValue(
String typ = type.getType(key).getTypeName();
Object valu = composite.get(key);
processBeanValue(
domain, beanProperties, attrKeys, key, typ, type.getDescription(), valu);
objectName,
domain,
beanProperties,
attrKeys,
key,
typ,
type.getDescription(),
valu);
}
} else if (value instanceof TabularData) {
// I don't pretend to have a good understanding of TabularData.
Expand Down Expand Up @@ -358,6 +380,7 @@ private void processBeanValue(
name = attrName;
}
processBeanValue(
objectName,
domain,
l2s,
attrNames,
Expand All @@ -377,6 +400,7 @@ private void processBeanValue(
Optional<?> optional = (Optional<?>) value;
if (optional.isPresent()) {
processBeanValue(
objectName,
domain,
beanProperties,
attrKeys,
Expand All @@ -388,6 +412,7 @@ private void processBeanValue(
} else if (value.getClass().isEnum()) {
LOGGER.log(FINE, "%s%s%s scrape: %s", domain, beanProperties, attrName, value);
processBeanValue(
objectName,
domain,
beanProperties,
attrKeys,
Expand All @@ -396,6 +421,7 @@ private void processBeanValue(
attrDescription,
value.toString());
} else {
objectNameAttributeFilter.add(objectName, attrName);
LOGGER.log(FINE, "%s%s scrape: %s not exported", domain, beanProperties, attrType);
}
}
Expand All @@ -415,6 +441,8 @@ public void recordBean(

/** Convenience function to run standalone. */
public static void main(String[] args) throws Exception {
ObjectNameAttributeFilter objectNameAttributeFilter =
ObjectNameAttributeFilter.create(new HashMap<>());
List<ObjectName> objectNames = new LinkedList<>();
objectNames.add(null);
if (args.length >= 3) {
Expand All @@ -425,6 +453,7 @@ public static void main(String[] args) throws Exception {
(args.length > 3 && "ssl".equalsIgnoreCase(args[3])),
objectNames,
new LinkedList<>(),
objectNameAttributeFilter,
new StdoutWriter(),
new JmxMBeanPropertyCache())
.doScrape();
Expand All @@ -436,6 +465,7 @@ public static void main(String[] args) throws Exception {
false,
objectNames,
new LinkedList<>(),
objectNameAttributeFilter,
new StdoutWriter(),
new JmxMBeanPropertyCache())
.doScrape();
Expand All @@ -447,6 +477,7 @@ public static void main(String[] args) throws Exception {
false,
objectNames,
new LinkedList<>(),
objectNameAttributeFilter,
new StdoutWriter(),
new JmxMBeanPropertyCache())
.doScrape();
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,133 @@
/*
* Copyright (C) 2015-2023 The Prometheus jmx_exporter Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package io.prometheus.jmx;

import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import javax.management.MalformedObjectNameException;
import javax.management.ObjectName;

/** Class to implement an ObjectNameAttributeFilter */
dhoard marked this conversation as resolved.
Show resolved Hide resolved
@SuppressWarnings("unchecked")
public class ObjectNameAttributeFilter {

public static final String EXCLUDE_OBJECT_NAME_ATTRIBUTES = "excludeObjectNameAttributes";

public static final String EXCLUDE_OBJECT_NAME_ATTRIBUTES_DYNAMIC =
EXCLUDE_OBJECT_NAME_ATTRIBUTES + "Dynamic";

private final Map<ObjectName, Set<String>> excludeObjectNameAttributesMap;

private boolean dynamicExclusion;

/** Constructor */
private ObjectNameAttributeFilter() {
excludeObjectNameAttributesMap = Collections.synchronizedMap(new HashMap<>());
dhoard marked this conversation as resolved.
Show resolved Hide resolved
}

/**
* Method to initialize the ObjectNameAttributeFilter
*
* @param yamlConfig yamlConfig
* @return an ObjectNameAttributeFilter
* @throws MalformedObjectNameException MalformedObjectNameException
*/
private ObjectNameAttributeFilter initialize(Map<String, Object> yamlConfig)
throws MalformedObjectNameException {
if (yamlConfig.containsKey(EXCLUDE_OBJECT_NAME_ATTRIBUTES)) {
Map<Object, Object> objectNameAttributeMap =
(Map<Object, Object>) yamlConfig.get(EXCLUDE_OBJECT_NAME_ATTRIBUTES);

for (Map.Entry<Object, Object> entry : objectNameAttributeMap.entrySet()) {
ObjectName objectName = new ObjectName((String) entry.getKey());

List<String> attributeNames = (List<String>) entry.getValue();

Set<String> attributeNameSet =
excludeObjectNameAttributesMap.computeIfAbsent(
objectName, o -> Collections.synchronizedSet(new HashSet<>()));

for (String attribueName : attributeNames) {
dhoard marked this conversation as resolved.
Show resolved Hide resolved
attributeNameSet.add(attribueName);
}

excludeObjectNameAttributesMap.put(objectName, attributeNameSet);
}
}

if (yamlConfig.containsKey(EXCLUDE_OBJECT_NAME_ATTRIBUTES_DYNAMIC)) {
dynamicExclusion = (Boolean) yamlConfig.get(EXCLUDE_OBJECT_NAME_ATTRIBUTES_DYNAMIC);
}

return this;
}

/**
* Method to add an attribute to the filter if dynamic exclusion is enabled
*
* @param objectName the ObjectName
* @param attributeName the attribute name
*/
public void add(ObjectName objectName, String attributeName) {
if (dynamicExclusion) {
Set<String> attribteNameSet =
excludeObjectNameAttributesMap.computeIfAbsent(
objectName, o -> Collections.synchronizedSet(new HashSet<>()));

attribteNameSet.add(attributeName);
}
}

/**
* Method to check if an attribute should be excluded
*
* @param objectName the ObjectName
* @param attributeName the attribute name
* @return true if it should be excluded, false otherwise
*/
public boolean exclude(ObjectName objectName, String attributeName) {
boolean result = false;

if (excludeObjectNameAttributesMap.size() > 0) {
Set<String> attributeNameSet = excludeObjectNameAttributesMap.get(objectName);
if (attributeNameSet != null) {
result = attributeNameSet.contains(attributeName);
}
}

return result;
}

/**
* Method to create an ObjectNameAttributeFilter
*
* @param yamlConfig yamlConfig
* @return an ObjectNameAttributeFilter
*/
public static ObjectNameAttributeFilter create(Map<String, Object> yamlConfig) {
try {
return new ObjectNameAttributeFilter().initialize(yamlConfig);
} catch (MalformedObjectNameException e) {
throw new RuntimeException(
"Invalid configuration format for excludeObjectNameAttributes", e);
}
}
}
Loading