I want to be able to log all JMX data accessible via jconsole. Is there a way to do this programmatically? I'm building a form of logging of the system, and I want to create intervaled data viewable with some tool similar to jconsole.
How would I go about doing this?
java.lang.management.ManagementFactory gives you access to JMX data.
i.g.
List<MemoryPoolMXBean> memPoolBeans = ManagementFactory.getMemoryPoolMXBeans();
for (MemoryPoolMXBean mpb : memPoolBeans) {
System.out.println("Memory Pool: " + mpb.getName());
}
Some samples are available at SO query: [java]+managementfactory
A good read: https://www.ibm.com/developerworks/library/j-jtp09196/index.html
For full implementation connecting to a remote VM:
Map<String,String[]> env = new HashMap<String, String[]>();
env.put( JMXConnector.CREDENTIALS, new String[]{"user","pass"} );
JMXServiceURL address = new JMXServiceURL("service:rmi:///jndi/rmi://host:port/jmxrmi");
JMXConnector connector = JMXConnectorFactory.connect(address,env);
MBeanServerConnection mbs = connector.getMBeanServerConnection();
//get all mbeans
Set<ObjectInstance> beans = mbs.queryMBeans(null,null);
for( ObjectInstance instance : beans )
{
MBeanInfo info = mbs.getMBeanInfo( instance.getObjectName() );
}
From the info, you can query object names and attributes as desired.
I used this command line JMX client as a starting point when I wanted to do the same thing.
Related
I am using Jmeter to do load test using Ant plugin. Currently capturing server's JVM metrics during the test using JFR GUI mode, for each node. It is cumbersome to start for each node and sometimes its getting missed to start. Is there a way to start doing them together via code?
Client details
Hotspot JDK 1.8_131,
Mac OS,
Jmeter 4.0
Server details
Hotspot 1.8.0_152,
OEL ,
Tomcat
In internet I am getting the options to start JFR programmatically in server node, which will not work for me.
If you have terminal access, you could create a script and start a recording using the command line tool jcmd.
$ jcmd <class/jarfile> VM.unlock_commercial_features
$ jcmd <class/jarfile> JFR.start
or
$ jcmd (to list available pids)
$ jcmd <pid> VM.unlock_commercial_features
$ jcmd <pid> JFR.start
For more details, see
Example 2-1 Dynamic Interaction Using jcmd
If you can't access the server over SSH, or don't want to do it in the shell, you could use JMX and send the same conmands as diagnostic commands.
In JMC, in the Management/JMX Console you can inspect the MBeans on the server. There you will find the "com.sun.management / DiagnosticCommand" MXBean and the operations which you can write code against. The object name is com.sun.management:type=DiagnosticCommand and you need to call it with jfrStart.
You can set up the connection like this.
String host ="myHost";
int port = 1234;
HashMap map = new HashMap();
String[] credentials = new String[2];
credentials[0] = user;
credentials[1] = password;
map.put("jmx.remote.credentials", credentials);
String s = "/jndi/rmi://" + host + ":" + port + "/jmxrmi";
JMXServiceURL url = new JMXServiceURL("rmi", "", 0, s);
JMXConnector c= JMXConnectorFactory.newJMXConnector(url, map);
c.connect();
MBeanServerConnection conn = c.getMBeanServerConnection();
If you don't care about security, you can set map to null. Then you create an object name and invoke the operation you like to execute, for example:
ObjectName on = new ObjectName("com.sun.management:type=DiagnosticCommand");
Object[] args = new Object[] {
new String[] {
"dumponexit=true",
"filename=/recordings/rec.jfr",
"duration=600s"
}
};
String[] sig = new String[] {"[Ljava.lang.String;"};
conn.invoke(on, "jfrStart", args, sig);
In JDK 9 and later there is a FlightRecorderMXBean that can be used for remote access (including recording download), but if you are stuck on JDK 8, I would do the above. There is a MBean in Oracle JDK 8 as well, but it is unsupported, undocumented and black magic is needed to make it work.
I have been using Cassandra and its C++ driver to write APIs to insert and fetch data for some time now. However now that I've created my cluster, I want to develop monitoring tools for my cluster.
I want to build an application(preferably in C++ and I don't want to use a 3rd party application), which will store Cluster management specific attributes like memory utilization of each node in the cluster, latency of each operation, space occupied by each table on each node etc. I read about 'Metrics in Cassandra(https://cassandra.apache.org/doc/latest/operating/metrics.html) but I don't know how exactly to use them in building my application as I've not worked on Java before(excuse me for that!).
Can such application be built using C++? If it's a lot of work in C++, then it will be highly beneficial if you can share some Java code where these Cassandra Metrics have been used to monitor a Cassandra Cluster.
OS: RHEL
Cassandra version: 3.11.2
Cassandra 3.x uses the drop wizard api as you alluded to. If you can add the Jolokia jars to your deployment server(s) this will allow you to access the java jmx data using a simple http request. Jolokia exposes all the mbeans from java over a rest api.
It seems, there are no any c++ libs for JMX, but in Java it is pretty easy to get JMX metrics, all of you need is standard jdk. The following code demonstrates how to connect to cassandra node and get 'down' node count.
import java.util.HashMap;
import java.util.Map;
import javax.management.MBeanServerConnection;
import javax.management.ObjectName;
import javax.management.remote.JMXConnector;
import javax.management.remote.JMXConnectorFactory;
import javax.management.remote.JMXServiceURL;
public class Main {
public static void main( String[] args ) throws Exception {
String node = args[0];
String port = args[1];
String username = args[2];
String password = args[3];
JMXServiceURL url = new JMXServiceURL( "service:jmx:rmi:///jndi/rmi://" + node + ":" + port + "/jmxrmi" );
String[] credentials = {username, password};
Map<String, String[]> environment = new HashMap<>();
environment.put( JMXConnector.CREDENTIALS, credentials );
JMXConnector jmxConnector = JMXConnectorFactory.connect( url, environment );
MBeanServerConnection mbsc = jmxConnector.getMBeanServerConnection();//Get metrics bean
ObjectName oName = new ObjectName( "org.apache.cassandra.net:type=FailureDetector" );//create JMX object name
int downNodes = (int) mbsc.getAttribute( oName, "DownEndpointCount" ); //get number of unavailable nodes
System.out.println("Down node count: " + downNodes);
}
}
More details about jmx you can find in Oracle documentation
To obtain JMX object names and attribute names you can use jconsole tool, which is shipped together with jdk:
I created an mbean. Below is my configuration. I could see my bean configuration after the server startup on JMX console. I tried to stop/start the mbean from the jmx console and it is working.
But when I am writing the code to invoke the mbean via program running in the same JVM, I could not see my bean
mbean details : xyz.abc.test:service=myTestService,name=myTestServiceName
MBeanServer server = ManagementFactory.getPlatformMBeanServer();
try {
Set<ObjectName> names = new TreeSet<ObjectName>(server.queryNames(null, null));
for (ObjectName name : names) {
System.out.println("\tObjectName = " + name);
}
The above code is not printing my bean to invoke. Even client program is not working.
url = new JMXServiceURL("service:jmx:rmi:///jndi/rmi://localhost:8080/jmxrmi");
JMXConnector jmxc = JMXConnectorFactory.connect(url, null);
MBeanServerConnection mbsc = jmxc.getMBeanServerConnection();
I have MBeans in JBoss AS5.1 that were being accessed using RMIAdaptor from client within JBossAS5.1.
we do something like:
InitialContext ic = new InitialContext(props);
org.jboss.jmx.adaptor.rmi.RMIAdaptor rmiAdaptor = (org.jboss.jmx.adaptor.rmi.RMIAdaptor) ic.lookup("jmx/invoker/RMIAdaptor");
ObjectName mBean = new ObjectName(SOME_MBEAN_NAME) ;
rmiAdaptor.invoke(mBean, "performSomeOperation", new Object[]{"xyz"}, new String[] {"java.lang.String"});
We have migrated a portion of client code using RMIAdaptor to JBoss EAP6.4. Now, we are not able to access/invoke MBeans deployed on JBoss AS5.1.
Attempted to user remoting-jmx without success:
service:jmx:remoting-jmx://<host>:<port>
Please reply with your solution or pointers to find the solution.
When I run jconsole it shows me a list of Java processes:
I could then connect to one of these and see its MBeans. How does it discover the JMX processes? How can I do this in a program?
This article shows how to do it using Attach API from JDK tools.jar
Replying since I had this question too and got an answer.
There is a JPS program in JDK which shows java processes.
I am not 100% sure (don't want to dive deep into jconsole code) but 99% sure jconsole uses the same mechanism as jps:
http://grepcode.com/file/repository.grepcode.com/java/root/jdk/openjdk/8u40-b25/sun/tools/jps/Jps.java?av=f
HostIdentifier hostId = arguments.hostId();
MonitoredHost monitoredHost = MonitoredHost.getMonitoredHost(hostId);
// get the set active JVMs on the specified host.
Set<Integer> jvms = monitoredHost.activeVms();
These class are part of tools.jar, you need to include it in the project's classpath.
If we go deeper (I do not expose all the intermediate steps) - finally we'll know that active VMs list is populated from hsperfdata files in temporary directories:
http://grepcode.com/file/repository.grepcode.com/java/root/jdk/openjdk/8u40-b25/sun/jvmstat/perfdata/monitor/protocol/local/PerfDataFile.java
Here is a link to know more:
java hsperfdata directory
At last, here is a code snippet allowing you to get the java processes ids:
sun.jvmstat.monitor.MonitoredHost host = sun.jvmstat.monitor.MonitoredHost.getMonitoredHost(new sun.jvmstat.monitor.HostIdentifier((String) null));
System.out.println(host.activeVms());
P.S.
Then you can use Attach API (as kostya mentioned) to discover the rest of needed things.
After you register the MBean in your application, like this:
MBeanServer mbs = ManagementFactory.getPlatformMBeanServer();
//register the MBean
ObjectMbean mBean = new ObjectMbean();
ObjectName name = new ObjectName("com.gd.eventfiltering.jmx:type=SystemConfig");
mbs.registerMBean(mBean, name);
Then you can call your MBean like this:
JMXServiceURL url = new JMXServiceURL(JMX_PATH);
JMXConnector jmxc = JMXConnectorFactory.connect(url, null);
MBeanServerConnection mbsc = jmxc.getMBeanServerConnection();
ObjectName mbeanName = new ObjectName(JMX_OBJECT);
IObjectMbean mbeanProxy = JMX.newMBeanProxy(mbsc, mbeanName,IObjectMbean.class, false);
//call the method
List<EventType> filters = mbeanProxy.methodFromYourMBean();
jmxc.close();