{"id":689,"date":"2016-03-25T03:14:49","date_gmt":"2016-03-25T02:14:49","guid":{"rendered":"http:\/\/hirt.se\/blog\/?p=689"},"modified":"2016-03-28T07:59:10","modified_gmt":"2016-03-28T06:59:10","slug":"recording-jmx-data-into-jfr","status":"publish","type":"post","link":"https:\/\/hirt.se\/blog\/?p=689","title":{"rendered":"Recording JMX data into JFR"},"content":{"rendered":"<p>I recently saw a <a href=\"http:\/\/stackoverflow.com\/questions\/36086280\/java-mission-control-record-mbean-values\/\">question on Stack Overflow<\/a> regarding recording JMX data\u00a0into JFR. I proposed that one could use the JFR Java API to record MBean attributes, but I realized I never did a blog post on dynamic events. So this blog post will be about dynamic JFR event generation, and how to write a configurable agent which you can use to periodically record declared JMX attributes into the Flight Recorder.<\/p>\n<p>If you couldn\u2019t care less about the implementation details, you can just download the agent jar and the example configuration file at the end of this article. \ud83d\ude09<\/p>\n<p>And here is the usual disclaimer:<\/p>\n<blockquote><p><span style=\"background-color: #ffffff\">The following blog entry will describe <strong><span style=\"color: #ff0000\">UNSUPPORTED <\/span><\/strong>functionality. This means that relying on the described APIs or functionality may <span style=\"color: #ff0000\"><strong>BREAK <\/strong><\/span><span style=\"color: #000000\">your code\/plugin with any given update of the JDK and\/or Mission Control.<\/span><\/span><\/p><\/blockquote>\n<p>&nbsp;<\/p>\n<h2><span style=\"background-color: #ffffff\"><span style=\"color: #000000\">Dynamic JFR Events<\/span><\/span><\/h2>\n<p>The JFR event API can be very useful to record custom events into the Java Flight Recorder. But what do you do when you do not know beforehand what fields the event needs to contain? Or if you wish to programmatically generate event types based on some input?<\/p>\n<p>Dynamic JFR events to the rescue. You will, as usual, need a Producer:<\/p>\n<pre class=\"brush: java; title: ; notranslate\" title=\"\">Producer p = new Producer(&quot;Dynamic Event Example&quot;, &quot;This example will generate a dynamic event over and over&quot;, &quot;http:\/\/www.hirt.se\/example\/dynamic\/&quot;);<\/pre>\n<p>Next you need to create your DynamicEventToken. This token is what you will later use to create your events.<\/p>\n<pre class=\"brush: java; title: ; notranslate\" title=\"\">token = p.createDynamicInstantEvent(&quot;Dynamic Event Name&quot;, &quot;Dynamic event description&quot;, &quot;dynamic\/path&quot;, true, true,\r\n    new DynamicValue(&quot;rndstr&quot;, &quot;Random String&quot;, &quot;Contains a random string&quot;, ContentType.None, String.class),\r\n    new DynamicValue(&quot;fraction&quot;, &quot;Random Fraction&quot;, &quot;Contains a random fraction between 0 and 1&quot;, ContentType.Percentage, Float.TYPE),\r\n    new DynamicValue(&quot;timestamp&quot;, &quot;Timestamp&quot;, &quot;Contains a timestamp&quot;, ContentType.Timestamp, Long.TYPE));<\/pre>\n<p>Don\u2019t forget to register the producer! Also note that there is one DynamicValue per attribute to record. The ContentType is used to provide the data consumer (usually JMC) with a hints for helpful visualization of the data. Check out the jmxprobes.xml file for more information on the ContentTypes.<\/p>\n<p>Here is the full source for an example recording dynamic events of the same event type, but different payloads, over and over again:<\/p>\n<pre class=\"brush: java; title: ; notranslate\" title=\"\">package se.hirt.jmx2jfr;\r\n\r\nimport java.util.Random;\r\n\r\nimport com.oracle.jrockit.jfr.ContentType;\r\nimport com.oracle.jrockit.jfr.DynamicEventToken;\r\nimport com.oracle.jrockit.jfr.DynamicValue;\r\nimport com.oracle.jrockit.jfr.InstantEvent;\r\nimport com.oracle.jrockit.jfr.Producer;\r\n\r\n@SuppressWarnings(&quot;deprecation&quot;)\r\npublic class DynamicExample implements Runnable {\r\n\tprivate static final Random RND = new Random();\r\n\tprivate final DynamicEventToken token;\r\n\r\n\tpublic DynamicExample() throws Exception {\r\n\t\tProducer p = new Producer(&quot;Dynamic Event Example&quot;, &quot;This example will generate a dynamic event over and over&quot;, &quot;http:\/\/www.hirt.se\/example\/dynamic\/&quot;);\r\n\t\ttoken = p.createDynamicInstantEvent(&quot;Dynamic Event Name&quot;, &quot;Dynamic event description&quot;, &quot;dynamic\/path&quot;, \/* record thread *\/ true, \/* record stack trace *\/true,\r\n\t\t\t\tnew DynamicValue(&quot;rndstr&quot;, &quot;Random String&quot;, &quot;Contains a random string&quot;, ContentType.None, String.class), \r\n\t\t\t\tnew DynamicValue(&quot;fraction&quot;, &quot;Random Fraction&quot;, &quot;Contains a random fraction between 0 and 1&quot;, ContentType.Percentage, Float.TYPE),\r\n\t\t\t\tnew DynamicValue(&quot;timestamp&quot;, &quot;Timestamp&quot;, &quot;Contains a timestamp&quot;, ContentType.Timestamp, Long.TYPE));\r\n\t\tp.register();\r\n\t}\r\n\r\n\tpublic void run() {\r\n\t\twhile (true) {\r\n\t\t\tInstantEvent e = token.newInstantEvent();\r\n\t\t\te.setValue(&quot;rndstr&quot;, randomString());\r\n\t\t\te.setValue(&quot;fraction&quot;, RND.nextDouble());\r\n\t\t\te.setValue(&quot;timestamp&quot;, System.currentTimeMillis());\r\n\t\t\te.commit();\r\n\t\t\tsleep(1000);\r\n\t\t}\r\n\t}\r\n\t\r\n\tpublic final static void main(String&#x5B;] args) throws Exception {\t\t\r\n\t\tThread t = new Thread(new DynamicExample(), &quot;Event generation thread&quot;);\r\n\t\tt.setDaemon(true);\r\n\t\tt.start();\r\n\t\tSystem.out.println(&quot;Press enter to exit!&quot;);\r\n\t\tSystem.in.read();\r\n\t}\r\n\t\r\n\tprivate static String randomString() {\r\n\t\tStringBuilder builder = new StringBuilder();\r\n\t\tfor (int i = 0; i &lt; RND.nextInt(10) + 1; i++) {\r\n\t\t\tbuilder.append(Character.toString((char) (RND.nextInt(26) + 64)));\r\n\t\t}\r\n\t\treturn builder.toString();\r\n\t}\r\n\t\r\n\tprivate void sleep(long millis) {\r\n\t\ttry {\r\n\t\t\tThread.sleep(millis);\r\n\t\t} catch (InterruptedException e) {\r\n\t\t}\r\n\t}\r\n}\r\n<\/pre>\n<p>To run the example, especially if on a JDK prior to 8u40, remember to start with:<\/p>\n<blockquote><p><span style=\"background-color: #ffffff\">-XX:+UnlockCommercialFeatures \u2013XX:+FlightRecorder<\/span><\/p><\/blockquote>\n<p>To make a recording, start JMC and hook up to the running process. Don\u2019t forget to enable the events!<\/p>\n<p><a href=\"https:\/\/hirt.se\/blog\/wp-content\/uploads\/2016\/03\/enable_dyn_events.png\"><img loading=\"lazy\" decoding=\"async\" style=\"border-width: 0px\" title=\"enable_dyn_events\" src=\"https:\/\/hirt.se\/blog\/wp-content\/uploads\/2016\/03\/enable_dyn_events_thumb.png\" alt=\"enable_dyn_events\" width=\"404\" height=\"325\" border=\"0\" \/><\/a><\/p>\n<p>If you do a 10 second recording, you should have recorded about 10 events. Notice that properly setting the content type allows JMC to present the information in a better way. See, for example, the timestamp field:<\/p>\n<p><a href=\"https:\/\/hirt.se\/blog\/wp-content\/uploads\/2016\/03\/generatedevents.png\"><img loading=\"lazy\" decoding=\"async\" style=\"border-width: 0px\" title=\"generatedevents\" src=\"https:\/\/hirt.se\/blog\/wp-content\/uploads\/2016\/03\/generatedevents_thumb.png\" alt=\"generatedevents\" width=\"404\" height=\"387\" border=\"0\" \/><\/a><\/p>\n<p>&nbsp;<\/p>\n<h2><\/h2>\n<p>&nbsp;<\/p>\n<h2>Using the Agent<\/h2>\n<p>The agent allows you to configure and record JMX data into JFR without having to change any code. There is a small test snippet that you can run in the agent jar to try it out (download the agent and the settings file from the end of this article):<\/p>\n<blockquote><p>java -XX:+UnlockCommercialFeatures -XX:+FlightRecorder -javaagent:jmx2jfr.jar=jmxprobes.xml -cp jmx2jfr.jar se.hirt.jmx2jfr.test.HelloWorldTest<\/p><\/blockquote>\n<p>Next hook up to the HelloWorldTest process from JMC and create a 10s recording. Don\u2019t forget to enable the MBean events:<\/p>\n<p><a href=\"https:\/\/hirt.se\/blog\/wp-content\/uploads\/2016\/03\/enablembeanevents.png\"><img loading=\"lazy\" decoding=\"async\" style=\"border-width: 0px\" title=\"enablembeanevents\" src=\"https:\/\/hirt.se\/blog\/wp-content\/uploads\/2016\/03\/enablembeanevents_thumb.png\" alt=\"enablembeanevents\" width=\"404\" height=\"323\" border=\"0\" \/><\/a><\/p>\n<p>When the recording finishes, go to the Events | Log tab. Select to only see the MBean events in the Event Types view, and select an event. You will see that MBean data has been recorded from the platform MBean server.<\/p>\n<p><a href=\"https:\/\/hirt.se\/blog\/wp-content\/uploads\/2016\/03\/mbeanslog.png\"><img loading=\"lazy\" decoding=\"async\" style=\"border-width: 0px\" title=\"mbeanslog\" src=\"https:\/\/hirt.se\/blog\/wp-content\/uploads\/2016\/03\/mbeanslog_thumb.png\" alt=\"mbeanslog\" width=\"404\" height=\"323\" border=\"0\" \/><\/a><\/p>\n<p>Use the configurations file to configure what MBeans and attributes to record, and what content types to use for the different attributes. There will be one Event Type per MBean, filled with whatever attributes you have selected:<\/p>\n<pre class=\"brush: xml; title: ; notranslate\" title=\"\">&lt;jmx2jfr&gt;\r\n        &lt;!-- Time to wait before retrying getting an MBean attribute in ms --&gt;\r\n\t&lt;retrytime&gt;60000&lt;\/retrytime&gt;\r\n\t\r\n\t&lt;!-- Time to wait until starting in ms --&gt; \r\n\t&lt;delay&gt;10000&lt;\/delay&gt;\r\n\t\r\n\t&lt;!-- How often to get the attributes and emit events for them, in ms --&gt;\r\n\t&lt;period&gt;1000&lt;\/period&gt;\r\n\t\r\n\t&lt;!-- clean: Use name or type keys for event name. Leads to cleaner event \r\n\t     type names, but can result in name collisions if you have MBeans with \r\n\t     the same name in the same domain.\r\n\t     canonical: ugly event type names, but guaranteed not to collide --&gt;\r\n\t&lt;namingmode&gt;clean&lt;\/namingmode&gt;\r\n\t\r\n\t&lt;!-- objectname:    MBean name - look it up in JMC\r\n\t     attributename: Attribute name - look it up in JMC\r\n\t     contenttype:   &#x5B;None | Bytes | Timestamp | Millis | Nanos | Ticks | Address] --&gt;\r\n\t&lt;attributes&gt;\r\n\t\t&lt;attribute&gt;\r\n\t\t\t&lt;objectname&gt;java.lang:type=OperatingSystem&lt;\/objectname&gt;\r\n\t\t\t&lt;attributename&gt;FreeSwapSpaceSize&lt;\/attributename&gt;\r\n\t\t\t&lt;contenttype&gt;Bytes&lt;\/contenttype&gt;\r\n\t\t&lt;\/attribute&gt;\r\n\t\t&lt;attribute&gt;\r\n\t\t\t&lt;objectname&gt;java.lang:type=OperatingSystem&lt;\/objectname&gt;\r\n\t\t\t&lt;attributename&gt;TotalSwapSpaceSize&lt;\/attributename&gt;\r\n\t\t\t&lt;contenttype&gt;Bytes&lt;\/contenttype&gt;\r\n\t\t&lt;\/attribute&gt;\r\n...\r\n<\/pre>\n<p>This should be fairly self explanatory. Edit the file to get the MBean attributes that you are interested in.<\/p>\n<h2>Summary<\/h2>\n<p>TL;DR \u2013 agent to easily record data from the platform MBean server into the Flight Recorder. Just edit the xml settings file, add the agent to the command line and point it to your edited settings file.<\/p>\n<p>Downloads:<\/p>\n<ul>\n<li><a href=\"http:\/\/www.hirt.se\/downloads\/software\/jfr\/agents\/jmx2jfr\/jmx2jfr.jar\">jmx2jfr.jar<\/a> \u2013 the agent jar<\/li>\n<li><a href=\"http:\/\/www.hirt.se\/downloads\/software\/jfr\/agents\/jmx2jfr\/jmxprobes.xml\">jmxprobes.xml<\/a> \u2013 the settings file<\/li>\n<li><a href=\"http:\/\/www.hirt.se\/downloads\/software\/jfr\/agents\/jmx2jfr\/se.hirt.jmx2jfr.zip\">se.hirt.jmx2jfr.zip<\/a> \u2013 the full source as an eclipse project<\/li>\n<\/ul>\n","protected":false},"excerpt":{"rendered":"<p>I recently saw a question on Stack Overflow regarding recording JMX data\u00a0into JFR. I proposed that one could use the JFR Java API to record MBean attributes, but I realized I never did a blog post on dynamic events. So this blog post will be about dynamic JFR event generation, and how to write a [&hellip;]<\/p>\n","protected":false},"author":2,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[3],"tags":[],"class_list":["post-689","post","type-post","status-publish","format-standard","hentry","category-java-mission-control-fair-dj"],"_links":{"self":[{"href":"https:\/\/hirt.se\/blog\/index.php?rest_route=\/wp\/v2\/posts\/689","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/hirt.se\/blog\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/hirt.se\/blog\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/hirt.se\/blog\/index.php?rest_route=\/wp\/v2\/users\/2"}],"replies":[{"embeddable":true,"href":"https:\/\/hirt.se\/blog\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=689"}],"version-history":[{"count":17,"href":"https:\/\/hirt.se\/blog\/index.php?rest_route=\/wp\/v2\/posts\/689\/revisions"}],"predecessor-version":[{"id":706,"href":"https:\/\/hirt.se\/blog\/index.php?rest_route=\/wp\/v2\/posts\/689\/revisions\/706"}],"wp:attachment":[{"href":"https:\/\/hirt.se\/blog\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=689"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/hirt.se\/blog\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=689"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/hirt.se\/blog\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=689"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}