Skip to content

Commit 2d5775a

Browse files
authored
Allow JMX Insight reuse for remote connections (#12178)
1 parent 59acff5 commit 2d5775a

File tree

10 files changed

+213
-179
lines changed

10 files changed

+213
-179
lines changed

instrumentation/jmx-metrics/javaagent/src/main/java/io/opentelemetry/instrumentation/javaagent/jmx/JmxMetricInsightInstaller.java

+1-1
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ public void afterAgent(AutoConfiguredOpenTelemetrySdk autoConfiguredSdk) {
3636
JmxMetricInsight.createService(
3737
GlobalOpenTelemetry.get(), beanDiscoveryDelay(config).toMillis());
3838
MetricConfiguration conf = buildMetricConfiguration(config);
39-
service.start(conf);
39+
service.startLocal(conf);
4040
}
4141
}
4242

instrumentation/jmx-metrics/javaagent/src/test/java/io/opentelemetry/instrumentation/javaagent/jmx/JmxMetricInsightInstallerTest.java

+1-1
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ class JmxMetricInsightInstallerTest {
3838
@Test
3939
void testToVerifyExistingRulesAreValid() throws Exception {
4040
RuleParser parser = RuleParser.get();
41-
assertThat(parser == null).isFalse();
41+
assertThat(parser).isNotNull();
4242

4343
Path path = Paths.get(PATH_TO_ALL_EXISTING_RULES);
4444
assertThat(Files.exists(path)).isTrue();

instrumentation/jmx-metrics/library/src/main/java/io/opentelemetry/instrumentation/jmx/engine/BeanAttributeExtractor.java

+21-20
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@
1616
import javax.management.InstanceNotFoundException;
1717
import javax.management.MBeanAttributeInfo;
1818
import javax.management.MBeanInfo;
19-
import javax.management.MBeanServer;
19+
import javax.management.MBeanServerConnection;
2020
import javax.management.ObjectName;
2121
import javax.management.openmbean.CompositeData;
2222
import javax.management.openmbean.TabularData;
@@ -132,18 +132,17 @@ public String getAttributeName() {
132132
* including the internals of CompositeData and TabularData, if applicable, and that the provided
133133
* values will be numerical.
134134
*
135-
* @param server the MBeanServer that reported knowledge of the ObjectName
136-
* @param objectName the ObjectName identifying the MBean
137-
* @return AttributeInfo if the attribute is properly recognized, or null
135+
* @param connection the {@link MBeanServerConnection} that reported knowledge of the ObjectName
136+
* @param objectName the {@link ObjectName} identifying the MBean
138137
*/
139138
@Nullable
140-
AttributeInfo getAttributeInfo(MBeanServer server, ObjectName objectName) {
139+
AttributeInfo getAttributeInfo(MBeanServerConnection connection, ObjectName objectName) {
141140
if (logger.isLoggable(FINE)) {
142141
logger.log(FINE, "Resolving {0} for {1}", new Object[] {getAttributeName(), objectName});
143142
}
144143

145144
try {
146-
MBeanInfo info = server.getMBeanInfo(objectName);
145+
MBeanInfo info = connection.getMBeanInfo(objectName);
147146
MBeanAttributeInfo[] allAttributes = info.getAttributes();
148147

149148
for (MBeanAttributeInfo attr : allAttributes) {
@@ -152,7 +151,7 @@ AttributeInfo getAttributeInfo(MBeanServer server, ObjectName objectName) {
152151

153152
// Verify correctness of configuration by attempting to extract the metric value.
154153
// The value will be discarded, but its type will be checked.
155-
Object sampleValue = extractAttributeValue(server, objectName, logger);
154+
Object sampleValue = extractAttributeValue(connection, objectName, logger);
156155

157156
// Only numbers can be used to generate metric values
158157
if (sampleValue instanceof Number) {
@@ -199,18 +198,20 @@ AttributeInfo getAttributeInfo(MBeanServer server, ObjectName objectName) {
199198
* Extracts the specified attribute value. In case the value is a CompositeData, drills down into
200199
* it to find the correct singleton value (usually a Number or a String).
201200
*
202-
* @param server the MBeanServer to use
203-
* @param objectName the ObjectName specifying the MBean to use, it should not be a pattern
201+
* @param connection the {@link MBeanServerConnection} to use
202+
* @param objectName the {@link ObjectName} specifying the MBean to use, it should not be a
203+
* pattern
204204
* @param logger the logger to use, may be null. Typically we want to log any issues with the
205205
* attributes during MBean discovery, but once the attribute is successfully detected and
206206
* confirmed to be eligble for metric evaluation, any further attribute extraction
207207
* malfunctions will be silent to avoid flooding the log.
208-
* @return the attribute value, if found, or null if an error occurred
208+
* @return the attribute value, if found, or {@literal null} if an error occurred
209209
*/
210210
@Nullable
211-
private Object extractAttributeValue(MBeanServer server, ObjectName objectName, Logger logger) {
211+
private Object extractAttributeValue(
212+
MBeanServerConnection connection, ObjectName objectName, Logger logger) {
212213
try {
213-
Object value = server.getAttribute(objectName, baseName);
214+
Object value = connection.getAttribute(objectName, baseName);
214215

215216
int k = 0;
216217
while (k < nameChain.length) {
@@ -247,13 +248,13 @@ private Object extractAttributeValue(MBeanServer server, ObjectName objectName,
247248
}
248249

249250
@Nullable
250-
private Object extractAttributeValue(MBeanServer server, ObjectName objectName) {
251-
return extractAttributeValue(server, objectName, null);
251+
private Object extractAttributeValue(MBeanServerConnection connection, ObjectName objectName) {
252+
return extractAttributeValue(connection, objectName, null);
252253
}
253254

254255
@Nullable
255-
Number extractNumericalAttribute(MBeanServer server, ObjectName objectName) {
256-
Object value = extractAttributeValue(server, objectName);
256+
Number extractNumericalAttribute(MBeanServerConnection connection, ObjectName objectName) {
257+
Object value = extractAttributeValue(connection, objectName);
257258
if (value instanceof Number) {
258259
return (Number) value;
259260
}
@@ -262,13 +263,13 @@ Number extractNumericalAttribute(MBeanServer server, ObjectName objectName) {
262263

263264
@Override
264265
@Nullable
265-
public String extractValue(MBeanServer server, ObjectName objectName) {
266-
return extractStringAttribute(server, objectName);
266+
public String extractValue(MBeanServerConnection connection, ObjectName objectName) {
267+
return extractStringAttribute(connection, objectName);
267268
}
268269

269270
@Nullable
270-
private String extractStringAttribute(MBeanServer server, ObjectName objectName) {
271-
Object value = extractAttributeValue(server, objectName);
271+
private String extractStringAttribute(MBeanServerConnection connection, ObjectName objectName) {
272+
Object value = extractAttributeValue(connection, objectName);
272273
if (value instanceof String) {
273274
return (String) value;
274275
}

instrumentation/jmx-metrics/library/src/main/java/io/opentelemetry/instrumentation/jmx/engine/BeanFinder.java

+39-19
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55

66
package io.opentelemetry.instrumentation.jmx.engine;
77

8+
import java.io.IOException;
89
import java.lang.management.ManagementFactory;
910
import java.util.ArrayList;
1011
import java.util.HashSet;
@@ -13,8 +14,10 @@
1314
import java.util.concurrent.Executors;
1415
import java.util.concurrent.ScheduledExecutorService;
1516
import java.util.concurrent.TimeUnit;
16-
import javax.management.MBeanServer;
17-
import javax.management.MBeanServerFactory;
17+
import java.util.function.Supplier;
18+
import java.util.logging.Level;
19+
import java.util.logging.Logger;
20+
import javax.management.MBeanServerConnection;
1821
import javax.management.ObjectName;
1922

2023
/**
@@ -23,6 +26,8 @@
2326
*/
2427
class BeanFinder {
2528

29+
private static final Logger logger = Logger.getLogger(BeanFinder.class.getName());
30+
2631
private final MetricRegistrar registrar;
2732
private MetricConfiguration conf;
2833
private final ScheduledExecutorService exec =
@@ -42,12 +47,20 @@ class BeanFinder {
4247
this.maxDelay = Math.max(60000, discoveryDelay);
4348
}
4449

45-
void discoverBeans(MetricConfiguration conf) {
50+
/**
51+
* Starts bean discovery on a list of local or remote connections
52+
*
53+
* @param conf metric configuration
54+
* @param connections supplier for instances of {@link MBeanServerConnection}, will be invoked
55+
* after delayed JMX initialization once the {@link #discoveryDelay} has expired.
56+
*/
57+
void discoverBeans(
58+
MetricConfiguration conf, Supplier<List<? extends MBeanServerConnection>> connections) {
4659
this.conf = conf;
4760

4861
exec.schedule(
4962
() -> {
50-
// Issue 9336: Corner case: PlatformMBeanServer will remain unitialized until a direct
63+
// Issue 9336: Corner case: PlatformMBeanServer will remain uninitialized until a direct
5164
// reference to it is made. This call makes sure that the PlatformMBeanServer will be in
5265
// the set of MBeanServers reported by MBeanServerFactory.
5366
// Issue 11143: This call initializes java.util.logging.LogManager. We should not call it
@@ -62,7 +75,7 @@ void discoverBeans(MetricConfiguration conf) {
6275
new Runnable() {
6376
@Override
6477
public void run() {
65-
refreshState();
78+
refreshState(connections);
6679
// Use discoveryDelay as the increment for the actual delay
6780
delay = Math.min(delay + discoveryDelay, maxDelay);
6881
exec.schedule(this, delay, TimeUnit.MILLISECONDS);
@@ -77,9 +90,11 @@ public void run() {
7790
* found for a given metric definition, submit the definition to MetricRegistrar for further
7891
* handling. Successive invocations of this method may find matches that were previously
7992
* unavailable, in such cases MetricRegistrar will extend the coverage for the new MBeans
93+
*
94+
* @param connections supplier providing {@link MBeanServerConnection} instances to query
8095
*/
81-
private void refreshState() {
82-
List<MBeanServer> servers = MBeanServerFactory.findMBeanServer(null);
96+
private void refreshState(Supplier<List<? extends MBeanServerConnection>> connections) {
97+
List<? extends MBeanServerConnection> servers = connections.get();
8398

8499
for (MetricDef metricDef : conf.getMetricDefs()) {
85100
resolveBeans(metricDef, servers);
@@ -92,22 +107,26 @@ private void refreshState() {
92107
* collection of corresponding metrics.
93108
*
94109
* @param metricDef the MetricDef used to find matching MBeans
95-
* @param servers the list of MBeanServers to query
110+
* @param connections the list of {@link MBeanServerConnection} to query
96111
*/
97-
private void resolveBeans(MetricDef metricDef, List<MBeanServer> servers) {
112+
private void resolveBeans(
113+
MetricDef metricDef, List<? extends MBeanServerConnection> connections) {
98114
BeanGroup beans = metricDef.getBeanGroup();
99115

100-
for (MBeanServer server : servers) {
116+
for (MBeanServerConnection connection : connections) {
101117
// The set of all matching ObjectNames recognized by the server
102118
Set<ObjectName> allObjectNames = new HashSet<>();
103119

104120
for (ObjectName pattern : beans.getNamePatterns()) {
105-
Set<ObjectName> objectNames = server.queryNames(pattern, beans.getQueryExp());
106-
allObjectNames.addAll(objectNames);
121+
try {
122+
allObjectNames.addAll(connection.queryNames(pattern, beans.getQueryExp()));
123+
} catch (IOException e) {
124+
logger.log(Level.WARNING, "IO error while resolving mbean", e);
125+
}
107126
}
108127

109128
if (!allObjectNames.isEmpty()) {
110-
resolveAttributes(allObjectNames, server, metricDef);
129+
resolveAttributes(allObjectNames, connection, metricDef);
111130

112131
// Assuming that only one MBeanServer has the required MBeans
113132
break;
@@ -119,19 +138,20 @@ private void resolveBeans(MetricDef metricDef, List<MBeanServer> servers) {
119138
* Go over the collection of matching MBeans and try to find all matching attributes. For every
120139
* successful match, activate metric value collection.
121140
*
122-
* @param objectNames the collection of ObjectNames identifying the MBeans
123-
* @param server the MBeanServer which recognized the collection of ObjectNames
124-
* @param metricDef the MetricDef describing the attributes to look for
141+
* @param objectNames the collection of {@link ObjectName}s identifying the MBeans
142+
* @param connection the {@link MBeanServerConnection} which recognized the collection of
143+
* ObjectNames
144+
* @param metricDef the {@link MetricDef} describing the attributes to look for
125145
*/
126146
private void resolveAttributes(
127-
Set<ObjectName> objectNames, MBeanServer server, MetricDef metricDef) {
147+
Set<ObjectName> objectNames, MBeanServerConnection connection, MetricDef metricDef) {
128148
for (MetricExtractor extractor : metricDef.getMetricExtractors()) {
129149
// For each MetricExtractor, find the subset of MBeans that have the required attribute
130150
List<ObjectName> validObjectNames = new ArrayList<>();
131151
AttributeInfo attributeInfo = null;
132152
for (ObjectName objectName : objectNames) {
133153
AttributeInfo attr =
134-
extractor.getMetricValueExtractor().getAttributeInfo(server, objectName);
154+
extractor.getMetricValueExtractor().getAttributeInfo(connection, objectName);
135155
if (attr != null) {
136156
if (attributeInfo == null) {
137157
attributeInfo = attr;
@@ -143,7 +163,7 @@ private void resolveAttributes(
143163
}
144164
if (!validObjectNames.isEmpty()) {
145165
// Ready to collect metric values
146-
registrar.enrollExtractor(server, validObjectNames, extractor, attributeInfo);
166+
registrar.enrollExtractor(connection, validObjectNames, extractor, attributeInfo);
147167
}
148168
}
149169
}

instrumentation/jmx-metrics/library/src/main/java/io/opentelemetry/instrumentation/jmx/engine/DetectionStatus.java

+6-6
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
package io.opentelemetry.instrumentation.jmx.engine;
77

88
import java.util.Collection;
9-
import javax.management.MBeanServer;
9+
import javax.management.MBeanServerConnection;
1010
import javax.management.ObjectName;
1111

1212
/**
@@ -15,16 +15,16 @@
1515
*/
1616
class DetectionStatus {
1717

18-
private final MBeanServer server;
18+
private final MBeanServerConnection connection;
1919
private final Collection<ObjectName> objectNames;
2020

21-
DetectionStatus(MBeanServer server, Collection<ObjectName> objectNames) {
22-
this.server = server;
21+
DetectionStatus(MBeanServerConnection connection, Collection<ObjectName> objectNames) {
22+
this.connection = connection;
2323
this.objectNames = objectNames;
2424
}
2525

26-
MBeanServer getServer() {
27-
return server;
26+
MBeanServerConnection getConnection() {
27+
return connection;
2828
}
2929

3030
Collection<ObjectName> getObjectNames() {

instrumentation/jmx-metrics/library/src/main/java/io/opentelemetry/instrumentation/jmx/engine/JmxMetricInsight.java

+27-2
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,11 @@
88
import static java.util.logging.Level.FINE;
99

1010
import io.opentelemetry.api.OpenTelemetry;
11+
import java.util.List;
12+
import java.util.function.Supplier;
1113
import java.util.logging.Logger;
14+
import javax.management.MBeanServerConnection;
15+
import javax.management.MBeanServerFactory;
1216

1317
/** Collecting and exporting JMX metrics. */
1418
public class JmxMetricInsight {
@@ -33,7 +37,28 @@ private JmxMetricInsight(OpenTelemetry openTelemetry, long discoveryDelay) {
3337
this.discoveryDelay = discoveryDelay;
3438
}
3539

36-
public void start(MetricConfiguration conf) {
40+
/**
41+
* Starts metric registration for local JVM
42+
*
43+
* @param conf metric configuration
44+
*/
45+
public void startLocal(MetricConfiguration conf) {
46+
start(conf, () -> MBeanServerFactory.findMBeanServer(null));
47+
}
48+
49+
/**
50+
* Starts metric registration for a remote JVM connection
51+
*
52+
* @param conf metric configuration
53+
* @param connections supplier for list of remote connections
54+
*/
55+
public void startRemote(
56+
MetricConfiguration conf, Supplier<List<? extends MBeanServerConnection>> connections) {
57+
start(conf, connections);
58+
}
59+
60+
private void start(
61+
MetricConfiguration conf, Supplier<List<? extends MBeanServerConnection>> connections) {
3762
if (conf.isEmpty()) {
3863
logger.log(
3964
FINE,
@@ -42,7 +67,7 @@ public void start(MetricConfiguration conf) {
4267
} else {
4368
MetricRegistrar registrar = new MetricRegistrar(openTelemetry, INSTRUMENTATION_SCOPE);
4469
BeanFinder finder = new BeanFinder(registrar, discoveryDelay);
45-
finder.discoverBeans(conf);
70+
finder.discoverBeans(conf, connections);
4671
}
4772
}
4873
}

instrumentation/jmx-metrics/library/src/main/java/io/opentelemetry/instrumentation/jmx/engine/MetricAttribute.java

+3-3
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55

66
package io.opentelemetry.instrumentation.jmx.engine;
77

8-
import javax.management.MBeanServer;
8+
import javax.management.MBeanServerConnection;
99
import javax.management.ObjectName;
1010

1111
/**
@@ -26,7 +26,7 @@ public String getAttributeName() {
2626
return name;
2727
}
2828

29-
String acquireAttributeValue(MBeanServer server, ObjectName objectName) {
30-
return extractor.extractValue(server, objectName);
29+
String acquireAttributeValue(MBeanServerConnection connection, ObjectName objectName) {
30+
return extractor.extractValue(connection, objectName);
3131
}
3232
}

0 commit comments

Comments
 (0)