Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
d927655
commit 5273578
Showing
3 changed files
with
266 additions
and
0 deletions.
There are no files selected for viewing
37 changes: 37 additions & 0 deletions
37
cmdlets/src/main/java/org/corfudb/metrics/MBeanDumperApp.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,37 @@ | ||
package org.corfudb.metrics; | ||
|
||
import javax.management.AttributeList; | ||
import javax.management.ObjectName; | ||
import java.util.Map; | ||
|
||
/** | ||
* An app to dump the MBean object names and their corresponding attributes into disk. | ||
* it uses JVM flags: corfu.jmx.service.url, dump.domains, dump.file to correspondingly | ||
* determine JMX server's service URL, the domains for which to retrieve the MBeans | ||
* and their attributes, and the file to export the results. | ||
* | ||
* Created by Sam Behnam on 3/30/18. | ||
*/ | ||
public class MBeanDumperApp { | ||
|
||
public static final String JMX_SERVICE_URL = "corfu.jmx.service.url"; | ||
public static final String DUMP_DOMAINS = "dump.domains"; | ||
public static final String DUMP_FILE = "dump.file"; | ||
|
||
public static void main(String[] args) throws Exception { | ||
final String serviceURL = System.getProperty(JMX_SERVICE_URL); | ||
final String domainsArg = System.getProperty(DUMP_DOMAINS); | ||
final String dumpFile = System.getProperty(DUMP_FILE); | ||
|
||
// null or empty domainsArgument translates to zero length domains array, | ||
// otherwise it will be turned into an array using comma as delimiter | ||
final String[] domains = (domainsArg == null || domainsArg.length() == 0) ? | ||
new String[0] : | ||
domainsArg.split(","); | ||
|
||
final Map<ObjectName, AttributeList> mBeanAttributes = | ||
MBeanUtils.getMBeanAttributes(serviceURL, domains); | ||
MBeanUtils.dumpMBeanAttributes(mBeanAttributes, dumpFile); | ||
} | ||
|
||
} |
156 changes: 156 additions & 0 deletions
156
cmdlets/src/main/java/org/corfudb/metrics/MBeanUtils.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,156 @@ | ||
package org.corfudb.metrics; | ||
|
||
import lombok.NonNull; | ||
import lombok.extern.slf4j.Slf4j; | ||
|
||
import javax.management.AttributeList; | ||
import javax.management.InstanceNotFoundException; | ||
import javax.management.IntrospectionException; | ||
import javax.management.MBeanAttributeInfo; | ||
import javax.management.MBeanInfo; | ||
import javax.management.MBeanServerConnection; | ||
import javax.management.MalformedObjectNameException; | ||
import javax.management.ObjectName; | ||
import javax.management.ReflectionException; | ||
import javax.management.remote.JMXConnector; | ||
import javax.management.remote.JMXConnectorFactory; | ||
import javax.management.remote.JMXServiceURL; | ||
import java.io.File; | ||
import java.io.FileNotFoundException; | ||
import java.io.IOException; | ||
import java.io.PrintStream; | ||
import java.util.Arrays; | ||
import java.util.HashMap; | ||
import java.util.HashSet; | ||
import java.util.Map; | ||
import java.util.Set; | ||
|
||
/** | ||
* A utility class to access a JMX server. Provides methods to retrieve attributes | ||
* as well as to export them to files. | ||
* | ||
* Created by Sam Behnam on 4/4/18. | ||
*/ | ||
|
||
@Slf4j | ||
class MBeanUtils { | ||
private MBeanUtils() { | ||
// prevent instantiation of this class | ||
} | ||
|
||
/** | ||
* Fetches the MBean objects and their attributes from a JMX server at the provided | ||
* service URL. It filters the results based on the provided domains. If the attributes | ||
* for an MBean object can not be fetched it will emit a warning to the default logger | ||
* and adds an empty entry for that object. | ||
* | ||
* @param serviceURL A well-formed JMX server's service URL. for example: | ||
* "service:jmx:rmi:///jndi/rmi://SERVER-IP:PORT/jmxrmi" | ||
* | ||
* @param domains An array of domains for which attributes will be fetched from the | ||
* JMX server. An empty array results in returning attributes for all | ||
* domains. | ||
* | ||
* @return A map of MBean object names and their corresponding attributes. | ||
* | ||
* @throws IOException | ||
* @throws MalformedObjectNameException | ||
* @throws IntrospectionException | ||
*/ | ||
static Map<ObjectName, AttributeList> getMBeanAttributes(@NonNull String serviceURL, | ||
@NonNull String[] domains) | ||
throws IOException, | ||
MalformedObjectNameException, | ||
IntrospectionException { | ||
|
||
final JMXServiceURL url = new JMXServiceURL(serviceURL); | ||
final Map<ObjectName, AttributeList> jmxDump; | ||
|
||
try (JMXConnector connectorClient = JMXConnectorFactory.connect(url, null)) { | ||
final MBeanServerConnection mBeanServerConnection = connectorClient.getMBeanServerConnection(); | ||
final Set<ObjectName> objectNames = getObjectNamesForDomains(mBeanServerConnection, domains); | ||
jmxDump = new HashMap<>(objectNames.size()); | ||
|
||
for (ObjectName objectName : objectNames) { | ||
AttributeList attributeList = new AttributeList(); | ||
try { | ||
final MBeanInfo beanInfo = mBeanServerConnection.getMBeanInfo(objectName); | ||
final MBeanAttributeInfo[] beanInfoAttributes = beanInfo.getAttributes(); | ||
String[] attributeNames = new String[beanInfoAttributes.length]; | ||
for (int i = 0; i < beanInfoAttributes.length; i++) { | ||
attributeNames[i] = beanInfoAttributes[i].getName(); | ||
} | ||
Arrays.sort(attributeNames); | ||
|
||
attributeList = mBeanServerConnection.getAttributes(objectName, attributeNames); | ||
} catch (InstanceNotFoundException | | ||
ReflectionException | | ||
IOException e) { | ||
log.warn("Unable to fetch attributes for {}", objectName.toString()); | ||
} | ||
jmxDump.put(objectName, attributeList); | ||
} | ||
} | ||
|
||
return jmxDump; | ||
} | ||
|
||
/** | ||
* Fetches the MBean object names from a JMX server. It filters the results | ||
* by retrieving attributed for the provided domains | ||
* | ||
* @param mBeanServerConnection an open connection to a server. | ||
* @param domains An array of domains for which attributes will be fetched from the | ||
* JMX server. An empty array result in returning object names for all | ||
* the domains. | ||
* @return A set of MBean ObjectNames | ||
* | ||
* @throws IOException | ||
* @throws MalformedObjectNameException | ||
*/ | ||
private static Set<ObjectName> getObjectNamesForDomains(@NonNull MBeanServerConnection mBeanServerConnection, | ||
@NonNull String[] domains) | ||
throws IOException, MalformedObjectNameException { | ||
|
||
// In case there are no domains, ObjectNames of all MBeans will be returned | ||
if (domains.length == 0) { | ||
return mBeanServerConnection.queryNames(null, null); | ||
} | ||
|
||
// In case domains are provided, all the ObjectNames within the provided domain will be returned | ||
final Set<ObjectName> objectNames = new HashSet<>(); | ||
for (String domain : domains) { | ||
final ObjectName objectNameFilter = new ObjectName(domain + ":*"); | ||
objectNames.addAll(mBeanServerConnection.queryNames(objectNameFilter, null)); | ||
} | ||
return objectNames; | ||
} | ||
|
||
/** | ||
* A convenience method for exporting a map of MBean object names and their attributes to | ||
* a file. | ||
* | ||
* @param jmxDump A map of MBean object names along with their attributes. | ||
* @param dumpFile A file name which along with operation timestamp will be | ||
* used as the target dump file name. | ||
* | ||
* @throws FileNotFoundException | ||
*/ | ||
static void dumpMBeanAttributes(@NonNull Map<ObjectName, AttributeList> jmxDump, | ||
@NonNull String dumpFile) | ||
throws FileNotFoundException { | ||
|
||
final File file = new File(dumpFile + System.currentTimeMillis()); | ||
|
||
try (PrintStream printStream = new PrintStream(file)) { | ||
for (Map.Entry<ObjectName, AttributeList> dumpEntry : jmxDump.entrySet()) { | ||
final String mBeanAttributes = new StringBuilder() | ||
.append(dumpEntry.getKey()) | ||
.append(":") | ||
.append(dumpEntry.getValue()) | ||
.toString(); | ||
printStream.println(mBeanAttributes); | ||
} | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,73 @@ | ||
#!/usr/bin/env bash | ||
|
||
if [ "$JAVA_HOME" != "" ]; then | ||
JAVA="$JAVA_HOME/bin/java" | ||
else | ||
JAVA=java | ||
fi | ||
|
||
CORFUDBBINDIR="${CORFUDBBINDIR:-/usr/bin}" | ||
CORFUDB_PREFIX="${CORFUDBBINDIR}/.." | ||
|
||
SOURCE="${BASH_SOURCE[0]}" | ||
while [ -h "$SOURCE" ]; do # resolve $SOURCE until the file is no longer a symlink | ||
DIR="$( cd -P "$( dirname "$SOURCE" )" && pwd )" | ||
SOURCE="$(readlink "$SOURCE")" | ||
[[ $SOURCE != /* ]] && SOURCE="$DIR/$SOURCE" # if $SOURCE was a relative symlink, we need to resolve it relative to the path where the symlink file was located | ||
done | ||
DIR="$( cd -P "$( dirname "$SOURCE" )" && pwd )" | ||
|
||
if ls "${DIR}"/../cmdlets/target/*.jar > /dev/null 2>&1; then | ||
CLASSPATH=("${DIR}"/../cmdlets/target/cmdlets-*-shaded.jar) | ||
else | ||
CLASSPATH=("${CORFUDB_PREFIX}"/share/corfu/lib/*.jar) | ||
fi | ||
|
||
# Windows (cygwin) support | ||
case "`uname`" in | ||
CYGWIN*) cygwin=true ;; | ||
*) cygwin=false ;; | ||
esac | ||
|
||
if $cygwin | ||
then | ||
CLASSPATH=`cygpath -wp "$CLASSPATH"` | ||
fi | ||
|
||
|
||
|
||
usage() { echo "Usage: $0 [-d (uses default flags)] [-j <corfu.jmx.service.url>] [-m <dump.domains>] [-f <dump.file>] | ||
default setting: (-Dcorfu.jmx.service.url=service:jmx:rmi:///jndi/rmi://localhost:6666/jmxrmi -Ddump.domains=corfu.metrics -Ddump.file=/tmp/metrics-dump) | ||
corfu.jmx.service.url: the jmx service url. It should be in the form of (service:jmx:rmi:///jndi/rmi://ip-address:port/jmxrmi) | ||
dump.domains: domains to be dumped. It should be in the form of comma separated string. If empty, all domains will be dumped. | ||
dump.file: path to file for the dump." 1>&2; exit 1; } | ||
|
||
default="" | ||
jmxserver="" | ||
metric="" | ||
dump="" | ||
|
||
while getopts ":j:m:f:d" opt; do | ||
case $opt in | ||
d) default="-Dcorfu.jmx.service.url=service:jmx:rmi:///jndi/rmi://localhost:6666/jmxrmi -Ddump.domains=corfu.metrics -Ddump.file=/tmp/metrics-dump" | ||
echo "Using default flags: $default" | ||
;; | ||
j) jmxserver="-Dcorfu.jmx.service.url=$OPTARG" | ||
;; | ||
m) metric="-Ddump.domains=$OPTARG" | ||
;; | ||
f) dump="-Ddump.file=$OPTARG" | ||
;; | ||
*) usage | ||
;; | ||
esac | ||
done | ||
|
||
# default heap for dumper | ||
DUMPER_HEAP="${DUMPER_HEAP:-256}" | ||
export JVMFLAGS="-Xmx${DUMPER_HEAP}m $default $jmxserver $metric $dump" | ||
|
||
echo $JVMFLAGS | ||
|
||
RUN_AS=`basename $0` | ||
"$JAVA" -cp "$CLASSPATH" $JVMFLAGS org.corfudb.metrics.MBeanDumperApp $* |