JMX Monitoring of a CLM Tomcat Server only with the CLM Webinterface

[Update: Source Code now with Garbage collector]
Sometimes it is necessary to tackle down CLM performance issues. To monitor the OS resources like CPU, IO, Memory etc. is not really helpful, because Java is like a black box for an OS like LINUX. With JMX you can look “behind the scene”.

You can monitor the Heap, Threads, Memory and other things from a CLM  Tomcat Server. With the help of some JMX settings you can access this information with jconsole. jconsole is include in every Java JDK (not JRE) since 1.5.

But JMX has some disadvantages:

  • Only Java
  • Problems with firewalls. Especially Productive CLM server are in a protected environment, so it is nearly impossible to get access with your jconsole.
  • “All or nothing” protection

Today we will create a Open Social Widget that will show the amount of running Threads, Heapsize, CCM Requests and Garbage Collector with the help of JMX and a Jolokia WAR file. Only deploy this WAR file to the server and nothing else. Details at the Jolokia Website.

Download the code and unzip it to <serverinstall>/tomcat/webapps/extensions. As a result there should be  <serverinstall>/tomcat/webapps/extensions/jmxgrap directory. Details about the directory structure at https://jazz.net/wiki/bin/view/Main/RMExtensionsInTheWidgetCatalog50.

For this we use:

  • Jolokia a remote JMX with JSON over Http. Just deploy the jolokia.war file to the webapps directory
  • Jolokia Javascript Library
  • D3 cubism like in this example
  • Config the 4.0.3 RTC Tomcat Server JMX described here is not necessary!

 

Open Social Widget inside RTC(Graphic with D3/Cubism):jmxgraph01

 

status.js  (please change the hostname at line 9 and 150)

$(function() {

var context = cubism.context()
    .serverDelay(0)
    .clientDelay(0)
    .step(2 * 60 * 1000)      // Distance between data points in milliseconds =&gt;2 min
    .size(297);		      // Number of data points

var jolokia = context.jolokia("https://ssejtsserver:9443/jolokia");

var memory = jolokia.metric(
    function (resp1, resp2) {
        return Number(resp1.value) / Number(resp2.value);
    },
    {type:"read", mbean:"java.lang:type=Memory", attribute:"HeapMemoryUsage", path:"used"},
    {type:"read", mbean:"java.lang:type=Memory", attribute:"HeapMemoryUsage", path:"max"},
	"Heap-Memory"
);

var gcCount = jolokia.metric(
    {type:"read", mbean:"java.lang:type=GarbageCollector,name=MarkSweepCompact", attribute:"CollectionCount"},
    {delta:1000, name:"GC MarkSweepCompact"}
);

var gcCount2 = jolokia.metric(
    {type:"read", mbean:"java.lang:type=GarbageCollector,name=Copy", attribute:"CollectionCount"},
    {delta:1000, name:"GC Copy"}
);

var jtsRequest = jolokia.metric(
    {type:        "read", mbean:"Catalina:j2eeType=Servlet,name=equinoxbridgeservlet,WebModule=//localhost/jts,J2EEApplication=none,J2EEServer=none",
        attribute:"requestCount"}, {name:"JTS", delta:1000}
);

var ccmRequest = jolokia.metric(
    {type:        "read", mbean:"Catalina:j2eeType=Servlet,name=equinoxbridgeservlet,WebModule=//localhost/ccm,J2EEApplication=none,J2EEServer=none",
        attribute:"requestCount"}, {name:"CCM", delta:1000}
);

var allRequests = jolokia.metric(
    function (resp) {
        var attrs = resp.value;
        var sum = 0;
        for (var key in attrs) {
            sum += attrs[key].requestCount;
        }
        return sum;
    },
    {type:        "read", mbean:"Catalina:j2eeType=Servlet,*",
        attribute:"requestCount"}, {name:"All", delta: 1000}
);

var thread = jolokia.metric(
    function (resp1, resp2) {
        return Number(resp1.value) / Number(resp2.value);
    },
    { type: "read", mbean: "java.lang:type=Threading", attribute: "ThreadCount"},
    { type: "read", mbean: 'Catalina:name=\"http-bio-9443\",type=ThreadPool', attribute: "maxThreads"},
	"Threads"
);

var colorsRed = ["#FDBE85", "#FEEDDE", "#FD8D3C", "#E6550D", "#A63603", "#FDBE85", "#FEEDDE", "#FD8D3C", "#E6550D", "#A63603" ],
    colorsGreen = [ "#E5F5F9", "#99D8C9", "#2CA25F", "#E5F5F9", "#99D8C9", "#2CA25F"],
    colorsBlue = [ "#ECE7F2", "#A6BDDB", "#2B8CBE", "#ECE7F2", "#A6BDDB", "#2B8CBE"];

// Created graphs

    d3.select("#memory").call(function (div) {

        div.append("div")
            .attr("class", "axis")
            .call(context.axis().orient("top"));

        div.selectAll(".horizon")
            .data([memory, allRequests])
            .enter().append("div")
            .attr("class", "horizon")
            .call(
            context.horizon()
                .colors(colorsRed)
                .format(d3.format(".4p"))
        );
        div.selectAll(".horizon-gc")
            .data([gcCount2, gcCount])
            .enter().append("div")
            .attr("class", "horizon horizon-gc")
            .call(
            context.horizon().colors(colorsRed).height(20)
        );
        div.append("div")
            .attr("class", "rule")
            .call(context.rule());

    });

	d3.select("#request").call(function (div) {
        div.append("div")
            .attr("class", "axis")
            .call(context.axis().orient("top"));

        div.selectAll(".horizon")
            .data([ jtsRequest, ccmRequest, allRequests])
            .enter()
            .append("div")
            .attr("class", "horizon")
            .call(context.horizon()
            .format(d3.format("2d"))
            .colors(function (d, i) {
                return i == 3 ? colorsBlue : colorsGreen
            }));

        div.append("div")
            .attr("class", "rule")
            .call(context.rule());

    });

	d3.select("#thread").call(function (div) {
	        div.append("div")
	            .attr("class", "axis")
	            .call(context.axis().orient("top"));

	        div.selectAll(".horizon")
	            .data([thread])
	            .enter().append("div")
	            .attr("class", "horizon")
	            .call(
	            context.horizon()
	                .colors(colorsRed)
	                .format(d3.format(".4p"))
	        );

	        div.append("div")
	            .attr("class", "rule")
	            .call(context.rule()
			);

	    });

    // On mousemove, reposition the chart values to match the rule.
    context.on("focus", function (i) {
        d3.selectAll("#charts.value").style("right", i == null ? null : context.size() - i + "px");
		d3.selectAll("#request.value").style("right", i == null ? null : context.size() - i + "px");
		d3.selectAll("#thread.value").style("right", i == null ? null : context.size() - i + "px");
    });

});

function gc() {
	var j4p = new Jolokia("https://localhost:9443/jolokia");
    j4p.request({type:"exec", mbean:"java.lang:type=Memory", operation:"gc"});
}

status.xml

<?xml version="1.0" encoding="UTF-8" ?>
<!--
 Licensed Materials - Property of IBM
 status.xml
© Copyright IBM Corporation 2013

U.S. Government Users Restricted Rights:  Use, duplication or disclosure restricted by GSA ADP Schedule Contract with IBM Corp.
-->

<Module>
<ModulePrefs title="JMX Graphs" height="750" scrolling="true">
<Optional feature="com.ibm.rdm.rm.api"></Optional>
</ModulePrefs>
<Content type="html">
<![CDATA[

    <link rel="stylesheet" href="css/style.css" type="text/css" media="screen" />

	<p>
		This extension uses <a href="http://www.jolokia.org/">Jolkia</a>. For installation instruction visit this site.
	</p>
        <button style="margin-top: 10px; margin-left: 5px;" onclick="gc();">Trigger Garbage Collection</button>
	<h4 class="title">JMX Memory</h4>
	<div id="memory" ></div>
	<h4 class="title">JMX Requests</h4>
	<div id="request" ></div>
	<h4 class="title">JMX Threads</h4>
	<div id="thread" ></div>

    <script src="//cdnjs.cloudflare.com/ajax/libs/d3/3.2.2/d3.v3.min.js" charset="utf-8"></script>
    <script type="text/javascript" src="lib/jquery-1.10.1.js"></script>
    <script type="text/javascript" src="lib/cubism.v1.min.js"></script>
    <script type="text/javascript" src="lib/jquery.flot.js"></script>
    <script type="text/javascript" src="lib/jolokia.js"></script>
    <script type="text/javascript" src="lib/jolokia-simple.js"></script>
    <script type="text/javascript" src="lib/json2.js"></script>
    <script type="text/javascript" src="lib/jolokia-cubism.js"></script>
    <script src="js/status.js"></script>

]]>
</Content>
</Module>

status.css


footer {
    font-size: small;
    margin-top: 8em;
}

aside {
    font-size: small;
    left: 180px;
    position: absolute;
    width: 180px;
}

svg {
    font: 10px sans-serif;
}

.axis path, .axis line {
    fill: none;
    stroke: #000;
    shape-rendering: crispEdges;
}

sup, sub {
    line-height: 0;
}

.axis {
    font: 10px ;
}

.axis text {
    -webkit-transition: fill-opacity 250ms linear;
}

.axis path {
    display: none;
}

.axis line {
    stroke: #000;
    shape-rendering: crispEdges;
}

.horizon {
    border-bottom: solid 1px #000;
    overflow: hidden;
    position: relative;
}

.horizon {
    border-top: solid 1px #000;
    border-bottom: solid 1px #000;
}

.horizon + .horizon {
    border-top: none;
}

.horizon canvas {

    display: block;
}

.horizon .title,
.horizon .value {
    bottom: 0;
    line-height: 30px;
    margin: 0 6px;
    position: absolute;
    text-shadow: 0 1px 0 rgba(255,255,255,.5);
    white-space: nowrap;
}

.horizon-gc .title {
    line-height: 10px;
    font-size: 6pt;
}
.horizon .title {
    left: 0;
}
.horizon .value {
    right: 0;
}

.col_title {
    font-weight: bold;
    margin-bottom: 5px;
}

.line {
    background: #000;
    opacity: .2;
    z-index: 2;
}
  1. #1 by rsjazz on 14/04/2014 - 13:15

    Reblogged this on rsjazz and commented:
    Stefan is doing some amazing work here and I think you might be interested. This is an example for server monitoring in a dashboard.

  2. #2 by Praveen Patidar on 15/04/2014 - 03:17

    Hello Ralph,

    Nice to see that the information about the JVM is tracked. Is there similar way to implement the same in WebSphere same as Tomcat. I am not quite friendly with the Server architecture however looking something helpful that can be integrated with WAS as well.

    Thanks-
    Praveen

    • #3 by rtcpractise on 15/04/2014 - 08:10

      Hi Patidar,

      I have no personal experience with JMX/Jolokia and WAS. They mentioned at the Jolokia Website (http://www.jolokia.org/agent.html) WAS 8.0.0.1. My recommendation is…use my JMX Widget only at a Test environment. All my CLM Customer have a identical CLM Testenvironment (compared with their Prod. environment). This constellation is also useful when you play around with the amount of Threads or Heapsize.
      Let me know your testresults with WAS.
      Thx
      Steve

  3. #4 by Casey on 06/06/2016 - 09:09

    Hi, thanks for sharing this wonderful information. However, does it have a conflict on other network monitoring tools like monit or other monitoring tool? Thanks

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

%d bloggers like this: