Skip to content

Commit ef9229a

Browse files
committed
Add ability to restrict what objectnames are queried.
1 parent 00cbfbd commit ef9229a

File tree

4 files changed

+85
-27
lines changed

4 files changed

+85
-27
lines changed

README.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,9 @@ The configuration is in JSON. An example with all possible options:
2525
"hostPort": "127.0.0.1:1234",
2626
"lowercaseOutputName": false,
2727
"lowercaseOutputLabelNames": false,
28+
"whitelistObjectNames": ["org.apache.cassandra.metrics:*"],
29+
"blacklistObjectNames": ["org.apache.cassandra.metrics:type=ColumnFamily,*"],
30+
"objectNames": ["org.apache.cassandra.metrics:*"],
2831
"rules": [
2932
{"pattern": "^org.apache.cassandra.metrics<type=(\w+), name=(\w+)><>Value:",
3033
"name": "cassandra_$1_$2",
@@ -40,6 +43,8 @@ Name | Description
4043
hostPort | The host and port to connect to via remote JMX. If not specified, will talk to the local JVM.
4144
lowercaseOutputName | Lowercase the output metric name. Applies to default format and `name`. Defaults to false.
4245
lowercaseOutputLabelNames | Lowercase the output metric label names. Applies to default format and `labels`. Defaults to false.
46+
whitelistObjectNames | A list of [ObjectNames](http://docs.oracle.com/javase/6/docs/api/javax/management/ObjectName.html) to query. Defaults to all mBeans.
47+
blacklistObjectNames | A list of [ObjectNames](http://docs.oracle.com/javase/6/docs/api/javax/management/ObjectName.html) to not query. Takes precedence over `whitelistObjectNames`. Defaults to none.
4348
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.
4449
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.
4550
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.

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

Lines changed: 23 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,8 @@
1717
import java.util.logging.Logger;
1818
import java.util.regex.Matcher;
1919
import java.util.regex.Pattern;
20+
import javax.management.MalformedObjectNameException;
21+
import javax.management.ObjectName;
2022

2123

2224
import org.json.simple.JSONArray;
@@ -41,16 +43,18 @@ private static class Rule {
4143
String hostPort;
4244
boolean lowercaseOutputName;
4345
boolean lowercaseOutputLabelNames;
46+
List<ObjectName> whitelistObjectNames = new ArrayList<ObjectName>();
47+
List<ObjectName> blacklistObjectNames = new ArrayList<ObjectName>();
4448
ArrayList<Rule> rules = new ArrayList<Rule>();
4549

46-
public JmxCollector(Reader in) throws IOException, ParseException {
50+
public JmxCollector(Reader in) throws IOException, ParseException, MalformedObjectNameException {
4751
this((JSONObject)new JSONParser().parse(in));
4852
}
49-
public JmxCollector(String jsonConfig) throws ParseException {
53+
public JmxCollector(String jsonConfig) throws ParseException, MalformedObjectNameException {
5054
this((JSONObject)new JSONParser().parse(jsonConfig));
5155
}
5256

53-
private JmxCollector(JSONObject config) throws ParseException {
57+
private JmxCollector(JSONObject config) throws ParseException, MalformedObjectNameException {
5458
if (config.containsKey("hostPort")) {
5559
hostPort = (String)config.get("hostPort");
5660
} else {
@@ -65,6 +69,21 @@ private JmxCollector(JSONObject config) throws ParseException {
6569
lowercaseOutputLabelNames = (Boolean)config.get("lowercaseOutputLabelNames");
6670
}
6771

72+
if (config.containsKey("whitelistObjectNames")) {
73+
JSONArray names = (JSONArray) config.get("whitelistObjectNames");
74+
for (Object name : names) {
75+
whitelistObjectNames.add(new ObjectName((String)name));
76+
}
77+
} else {
78+
whitelistObjectNames.add(null);
79+
}
80+
if (config.containsKey("blacklistObjectNames")) {
81+
JSONArray names = (JSONArray) config.get("blacklistObjectNames");
82+
for (Object name : names) {
83+
blacklistObjectNames.add(new ObjectName((String)name));
84+
}
85+
}
86+
6887
if (config.containsKey("rules")) {
6988
JSONArray configRules = (JSONArray) config.get("rules");
7089
for (Object ruleObject : configRules) {
@@ -260,7 +279,7 @@ public void recordBean(
260279

261280
public List<MetricFamilySamples> collect() {
262281
Receiver receiver = new Receiver();
263-
JmxScraper scraper = new JmxScraper(hostPort, receiver);
282+
JmxScraper scraper = new JmxScraper(hostPort, whitelistObjectNames, blacklistObjectNames, receiver);
264283
long start = System.nanoTime();
265284
double error = 0;
266285
try {

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

Lines changed: 16 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -38,10 +38,13 @@ void recordBean(
3838

3939
private MBeanReceiver receiver;
4040
private String hostPort;
41+
private List<ObjectName> whitelistObjectNames, blacklistObjectNames;
4142

42-
public JmxScraper(String hostPort, MBeanReceiver receiver) {
43+
public JmxScraper(String hostPort, List<ObjectName> whitelistObjectNames, List<ObjectName> blacklistObjectNames, MBeanReceiver receiver) {
4344
this.hostPort = hostPort;
4445
this.receiver = receiver;
46+
this.whitelistObjectNames = whitelistObjectNames;
47+
this.blacklistObjectNames = blacklistObjectNames;
4548
}
4649

4750
/**
@@ -60,9 +63,15 @@ public void doScrape() throws Exception {
6063
beanConn = jmxc.getMBeanServerConnection();
6164
}
6265
try {
66+
6367
// Query MBean names
64-
Set<ObjectName> mBeanNames =
65-
new TreeSet<ObjectName>(beanConn.queryNames(null, null));
68+
Set<ObjectName> mBeanNames = new TreeSet();
69+
for (ObjectName name : whitelistObjectNames) {
70+
mBeanNames.addAll(beanConn.queryNames(name, null));
71+
}
72+
for (ObjectName name : blacklistObjectNames) {
73+
mBeanNames.removeAll(beanConn.queryNames(name, null));
74+
}
6675

6776
for (ObjectName name : mBeanNames) {
6877
scrapeBean(beanConn, name);
@@ -259,10 +268,12 @@ public void recordBean(
259268
* Convenience function to run standalone.
260269
*/
261270
public static void main(String[] args) throws Exception {
271+
List<ObjectName> objectNames = new LinkedList<ObjectName>();
272+
objectNames.add(null);
262273
if (args.length > 0) {
263-
new JmxScraper(args[0], new StdoutWriter()).doScrape();
274+
new JmxScraper(args[0], objectNames, new LinkedList<ObjectName>(), new StdoutWriter()).doScrape();
264275
} else {
265-
new JmxScraper("", new StdoutWriter()).doScrape();
276+
new JmxScraper("", objectNames, new LinkedList<ObjectName>(), new StdoutWriter()).doScrape();
266277
}
267278
}
268279
}

collector/src/test/java/io/prometheus/jmx/JmxCollectorTest.java

Lines changed: 41 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,6 @@
1111
import java.lang.management.ManagementFactory;
1212
import javax.management.MBeanServer;
1313
import javax.management.ObjectName;
14-
import org.json.simple.parser.ParseException;
1514
import org.junit.Test;
1615
import org.junit.Before;
1716
import org.junit.BeforeClass;
@@ -39,78 +38,78 @@ public void setUp() throws Exception {
3938
}
4039

4140
@Test(expected=IllegalArgumentException.class)
42-
public void testRulesMustHaveNameWithHelp() throws ParseException {
41+
public void testRulesMustHaveNameWithHelp() throws Exception {
4342
JmxCollector jc = new JmxCollector("{`rules`: [{`help`: `foo`}] }".replace('`', '"'));
4443
}
4544

4645
@Test(expected=IllegalArgumentException.class)
47-
public void testRulesMustHaveNameWithLabels() throws ParseException {
46+
public void testRulesMustHaveNameWithLabels() throws Exception {
4847
JmxCollector jc = new JmxCollector("{`rules`: [{`labels`: {}}] }".replace('`', '"'));
4948
}
5049

5150
@Test(expected=IllegalArgumentException.class)
52-
public void testRulesMustHavePatternWithName() throws ParseException {
51+
public void testRulesMustHavePatternWithName() throws Exception {
5352
JmxCollector jc = new JmxCollector("{`rules`: [{`name`: `foo`}] }".replace('`', '"'));
5453
}
5554

5655
@Test
57-
public void testNameIsReplacedOnMatch() throws ParseException {
56+
public void testNameIsReplacedOnMatch() throws Exception {
5857
JmxCollector jc = new JmxCollector(
5958
"{`rules`: [{`pattern`: `^hadoop<service=DataNode, name=DataNodeActivity-ams-hdd001-50010><>replaceBlockOpMinTime:`, `name`: `foo`}]}".replace('`', '"')).register(registry);
6059
assertEquals(200, registry.getSampleValue("foo", new String[]{}, new String[]{}), .001);
6160
}
6261

6362
@Test
64-
public void testSnakeCaseAttrName() throws ParseException {
63+
public void testSnakeCaseAttrName() throws Exception {
6564
JmxCollector jc = new JmxCollector(
6665
"{`rules`: [{`pattern`: `^hadoop<service=DataNode, name=DataNodeActivity-ams-hdd001-50010><>replace_block_op_min_time:`, `name`: `foo`, `attrNameSnakeCase`: true}]}".replace('`', '"')).register(registry);
6766
assertEquals(200, registry.getSampleValue("foo", new String[]{}, new String[]{}), .001);
6867
}
6968

7069
@Test
71-
public void testLabelsAreSet() throws ParseException {
70+
public void testLabelsAreSet() throws Exception {
7271
JmxCollector jc = new JmxCollector(
7372
"{`rules`: [{`pattern`: `^hadoop<service=DataNode, name=DataNodeActivity-ams-hdd001-50010><>replaceBlockOpMinTime:`, `name`: `foo`, `labels`: {`l`: `v`}}]}".replace('`', '"')).register(registry);
7473
assertEquals(200, registry.getSampleValue("foo", new String[]{"l"}, new String[]{"v"}), .001);
7574
}
7675

7776
@Test
78-
public void testEmptyLabelsAreIgnored() throws ParseException {
77+
public void testEmptyLabelsAreIgnored() throws Exception {
7978
JmxCollector jc = new JmxCollector(
8079
"{`rules`: [{`pattern`: `^hadoop<service=DataNode, name=DataNodeActivity-ams-hdd001-50010><>replaceBlockOpMinTime:`, `name`: `foo`, `labels`: {``: `v`, `l`: ``}}]}".replace('`', '"')).register(registry);
8180
assertEquals(200, registry.getSampleValue("foo", new String[]{}, new String[]{}), .001);
8281
}
8382

8483
@Test
85-
public void testLowercaseOutputName() throws ParseException {
84+
public void testLowercaseOutputName() throws Exception {
8685
JmxCollector jc = new JmxCollector(
8786
"{`lowercaseOutputName`: true, `rules`: [{`pattern`: `^hadoop<service=DataNode, name=DataNodeActivity-ams-hdd001-50010><>replaceBlockOpMinTime:`, `name`: `Foo`}]}".replace('`', '"')).register(registry);
8887
assertEquals(200, registry.getSampleValue("foo", new String[]{}, new String[]{}), .001);
8988
}
9089

9190
@Test
92-
public void testLowercaseOutputLabelNames() throws ParseException {
91+
public void testLowercaseOutputLabelNames() throws Exception {
9392
JmxCollector jc = new JmxCollector(
9493
"{`lowercaseOutputLabelNames`: true, `rules`: [{`pattern`: `^hadoop<service=DataNode, name=DataNodeActivity-ams-hdd001-50010><>replaceBlockOpMinTime:`, `name`: `Foo` , `labels`: {`ABC`: `DEF`}}]}".replace('`', '"')).register(registry);
9594
assertEquals(200, registry.getSampleValue("Foo", new String[]{"abc"}, new String[]{"DEF"}), .001);
9695
}
9796

9897
@Test
99-
public void testNameAndLabelsFromPattern() throws ParseException {
98+
public void testNameAndLabelsFromPattern() throws Exception {
10099
JmxCollector jc = new JmxCollector(
101100
"{`rules`: [{`pattern`: `^hadoop<(service)=(DataNode), name=DataNodeActivity-ams-hdd001-50010><>(replaceBlockOpMinTime):`, `name`: `hadoop_$3`, `labels`: {`$1`: `$2`}}]}".replace('`', '"')).register(registry);
102101
assertEquals(200, registry.getSampleValue("hadoop_replaceBlockOpMinTime", new String[]{"service"}, new String[]{"DataNode"}), .001);
103102
}
104103

105104
@Test
106-
public void testNameAndLabelSanatized() throws ParseException {
105+
public void testNameAndLabelSanatized() throws Exception {
107106
JmxCollector jc = new JmxCollector(
108107
"{`rules`: [{`pattern`: `^(hadoop<service=DataNode, )name=DataNodeActivity-ams-hdd001-50010><>replaceBlockOpMinTime:`, `name`: `$1`, `labels`: {`$1`: `$1`}}]}".replace('`', '"')).register(registry);
109108
assertEquals(200, registry.getSampleValue("hadoop_service_DataNode_", new String[]{"hadoop_service_DataNode_"}, new String[]{"hadoop<service=DataNode, "}), .001);
110109
}
111110

112111
@Test
113-
public void testHelpFromPattern() throws ParseException {
112+
public void testHelpFromPattern() throws Exception {
114113
JmxCollector jc = new JmxCollector(
115114
"{`rules`: [{`pattern`: `^(hadoop)<service=DataNode, name=DataNodeActivity-ams-hdd001-50010><>replaceBlockOpMinTime:`, `name`: `foo`, `help`: `bar $1`}]}".replace('`', '"')).register(registry);
116115
for(Collector.MetricFamilySamples mfs : jc.collect()) {
@@ -122,22 +121,22 @@ public void testHelpFromPattern() throws ParseException {
122121
}
123122

124123
@Test
125-
public void stopsOnFirstMatchingRule() throws ParseException {
124+
public void stopsOnFirstMatchingRule() throws Exception {
126125
JmxCollector jc = new JmxCollector(
127126
"{`rules`: [{`pattern`: `.*`, `name`: `foo`}, {`pattern`: `.*`, `name`: `bar`}] }".replace('`', '"')).register(registry);
128127
assertNotNull(registry.getSampleValue("foo", new String[]{}, new String[]{}));
129128
assertNull(registry.getSampleValue("bar", new String[]{}, new String[]{}));
130129
}
131130

132131
@Test
133-
public void stopsOnEmptyName() throws ParseException {
132+
public void stopsOnEmptyName() throws Exception {
134133
JmxCollector jc = new JmxCollector(
135134
"{`rules`: [{`pattern`: `.*`, `name`: ``}, {`pattern`: `.*`, `name`: `foo`}] }".replace('`', '"')).register(registry);
136135
assertNull(registry.getSampleValue("foo", new String[]{}, new String[]{}));
137136
}
138137

139138
@Test
140-
public void defaultExportTest() throws ParseException {
139+
public void defaultExportTest() throws Exception {
141140
JmxCollector jc = new JmxCollector("{}").register(registry);
142141

143142
// Test JVM bean.
@@ -153,13 +152,37 @@ public void defaultExportTest() throws ParseException {
153152
}
154153

155154
@Test
156-
public void testDefaultExportLowercaseOutputName() throws ParseException {
155+
public void testWhitelist() throws Exception {
156+
JmxCollector jc = new JmxCollector("{`whitelistObjectNames`: [`java.lang:*`, `java.lang:*`, `org.apache.cassandra.concurrent:*`]}".replace('`', '"')).register(registry);
157+
158+
// Test what should and shouldn't be present.
159+
assertNotNull(registry.getSampleValue("java_lang_OperatingSystem_ProcessCpuTime", new String[]{}, new String[]{}));
160+
assertNotNull(registry.getSampleValue("org_apache_cassandra_concurrent_CONSISTENCY_MANAGER_ActiveCount", new String[]{}, new String[]{}));
161+
162+
assertNull(registry.getSampleValue("org_apache_cassandra_metrics_Compaction_Value", new String[]{"name"}, new String[]{"CompletedTasks"}));
163+
assertNull(registry.getSampleValue("hadoop_DataNode_replaceBlockOpMinTime", new String[]{"name"}, new String[]{"DataNodeActivity-ams-hdd001-50010"}));
164+
}
165+
166+
@Test
167+
public void testBlacklist() throws Exception {
168+
JmxCollector jc = new JmxCollector("{`whitelistObjectNames`: [`java.lang:*`, `org.apache.cassandra.concurrent:*`], `blacklistObjectNames`: [`org.apache.cassandra.concurrent:*`]}".replace('`', '"')).register(registry);
169+
170+
// Test what should and shouldn't be present.
171+
assertNotNull(registry.getSampleValue("java_lang_OperatingSystem_ProcessCpuTime", new String[]{}, new String[]{}));
172+
173+
assertNull(registry.getSampleValue("org_apache_cassandra_concurrent_CONSISTENCY_MANAGER_ActiveCount", new String[]{}, new String[]{}));
174+
assertNull(registry.getSampleValue("org_apache_cassandra_metrics_Compaction_Value", new String[]{"name"}, new String[]{"CompletedTasks"}));
175+
assertNull(registry.getSampleValue("hadoop_DataNode_replaceBlockOpMinTime", new String[]{"name"}, new String[]{"DataNodeActivity-ams-hdd001-50010"}));
176+
}
177+
178+
@Test
179+
public void testDefaultExportLowercaseOutputName() throws Exception {
157180
JmxCollector jc = new JmxCollector("{`lowercaseOutputName`: true}".replace('`', '"')).register(registry);
158181
assertNotNull(registry.getSampleValue("java_lang_operatingsystem_processcputime", new String[]{}, new String[]{}));
159182
}
160183

161184
@Test
162-
public void testServletRequestPattern() throws ParseException {
185+
public void testServletRequestPattern() throws Exception {
163186
JmxCollector jc = new JmxCollector(
164187
"{`rules`: [{`pattern`: `Catalina<j2eeType=Servlet, WebModule=//([-a-zA-Z0-9+&@#/%?=~_|!:.,;]*[-a-zA-Z0-9+&@#/%=~_|]), name=([-a-zA-Z0-9+/$%~_-|!.]*), J2EEApplication=none, J2EEServer=none><>RequestCount:`,`name`: `tomcat_request_servlet_count`,`labels`: {`module`:`$1`,`servlet`:`$2` },`help`: `Tomcat servlet request count`,`type`: `COUNTER`,`attrNameSnakeCase`: false}]}".replace('`', '"')).register(registry);
165188
assertEquals(1.0, registry.getSampleValue("tomcat_request_servlet_count", new String[]{"module", "servlet"}, new String[]{"localhost/host-manager", "HTMLHostManager"}), .001);

0 commit comments

Comments
 (0)