From 1d23cfdd3de37b9ab12a23d6cf6c89ec2418b7e4 Mon Sep 17 00:00:00 2001 From: Howard Hellyer Date: Tue, 31 Oct 2017 10:04:24 +0000 Subject: [PATCH 1/2] Add a new tab to seperate summary data for the whole run from data the rolling graphs for the last 15 minutes. --- dashboard/src/main/webapp/index.html | 154 +++++++++++++----- .../dataproviders/DataProviderManager.java | 81 ++++++--- .../dataproviders/GCDataProvider.java | 46 +++++- .../dataproviders/GCDataProviderTest.java | 6 +- 4 files changed, 215 insertions(+), 72 deletions(-) diff --git a/dashboard/src/main/webapp/index.html b/dashboard/src/main/webapp/index.html index 20c2a05..70233f5 100644 --- a/dashboard/src/main/webapp/index.html +++ b/dashboard/src/main/webapp/index.html @@ -34,21 +34,47 @@
-
-
-
-
-
+
+ + + +
+
+
+
+
+
+
+
+ +
+
+
+ +
+
+
+
+
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
- + - + diff --git a/javaagent/src/main/java/com/ibm/javametrics/dataproviders/DataProviderManager.java b/javaagent/src/main/java/com/ibm/javametrics/dataproviders/DataProviderManager.java index 7c0dc95..4cacb03 100644 --- a/javaagent/src/main/java/com/ibm/javametrics/dataproviders/DataProviderManager.java +++ b/javaagent/src/main/java/com/ibm/javametrics/dataproviders/DataProviderManager.java @@ -29,7 +29,7 @@ public class DataProviderManager { private static final String GC_TOPIC = "gc"; private static final String CPU_TOPIC = "cpu"; private static final String MEMORYPOOLS_TOPIC = "memoryPools"; - private static final String ENV_TOPIC = "env"; + private static final String ENV_TOPIC = "env"; private ScheduledExecutorService exec; @@ -37,6 +37,16 @@ private static String escapeStringForJSON(String str) { return str.replace("\\", "\\\\").replace("\"", "\\\""); } + // So long as we get the CPU samples at a constant interval + // we can average them out. (Note: the total itself is + // not meaningful.) + private double totalSystemCPULoad = 0.0; + private double totalProcessCPULoad = 0.0; + private double cpuLoadSamples = 0.0; + + private long usedHeapAfterGCMax = 0; + private long usedNativeMax = 0; + /** * Create a JavametricsMBeanConnector */ @@ -75,32 +85,48 @@ private void emitEnvironmentData() { private void emitGCData() { long timeStamp = System.currentTimeMillis(); - double gcTime = GCDataProvider.getGCCollectionTime(); - if (gcTime >= 0) { // Don't send -1 'no data' values + double gcFraction = GCDataProvider.getLatestGCPercentage(); + double gcFractionSummary = GCDataProvider.getTotalGCPercentage(); + + if (gcFraction >= 0) { // Don't send -1 'no data' values StringBuilder message = new StringBuilder(); message.append("{\"time\":\""); message.append(timeStamp); message.append("\", \"gcTime\": \""); - message.append(gcTime); + message.append(gcFraction); + message.append("\", \"gcTimeSummary\": \""); + message.append(gcFractionSummary); message.append("\"}"); Javametrics.getInstance().sendJSON(GC_TOPIC, message.toString()); } } private void emitCPUUsage() { - long timeStamp = System.currentTimeMillis(); - double process = CPUDataProvider.getProcessCpuLoad(); - double system = CPUDataProvider.getSystemCpuLoad(); - if (system >= 0 && process >= 0) { - StringBuilder message = new StringBuilder(); - message.append("{\"time\":\""); - message.append(timeStamp); - message.append("\", \"system\": \""); - message.append(system); - message.append("\", \"process\": \""); - message.append(process); - message.append("\"}"); - Javametrics.getInstance().sendJSON(CPU_TOPIC, message.toString()); + try{ + long timeStamp = System.currentTimeMillis(); + double process = CPUDataProvider.getProcessCpuLoad(); + double system = CPUDataProvider.getSystemCpuLoad(); + cpuLoadSamples++; + if (system >= 0 && process >= 0) { + totalProcessCPULoad += process; + totalSystemCPULoad += system; + + StringBuilder message = new StringBuilder(); + message.append("{\"time\":\""); + message.append(timeStamp); + message.append("\", \"system\": \""); + message.append(system); + message.append("\", \"process\": \""); + message.append(process); + message.append("\", \"processMean\": \""); + message.append(totalProcessCPULoad/cpuLoadSamples); + message.append("\", \"systemMean\": \""); + message.append(totalSystemCPULoad/cpuLoadSamples); + message.append("\"}"); + Javametrics.getInstance().sendJSON(CPU_TOPIC, message.toString()); + } + } catch (Exception e) { + e.printStackTrace(); } } @@ -110,17 +136,32 @@ private void emitMemoryPoolUsage() { long usedNative = MemoryPoolDataProvider.getNativeMemory(); long usedHeap = MemoryPoolDataProvider.getHeapMemory(); if (usedHeapAfterGC >= 0) { // check that some data is available + String usedHeapAfterGCStr = Long.toString(usedHeapAfterGC, 10); + usedHeapAfterGCMax = Math.max(usedHeapAfterGCMax, usedHeapAfterGC); + + String usedNativeStr = Long.toString(usedNative, 10); + usedNativeMax = Math.max(usedNativeMax, usedNative); + + String usedHeapStr = Long.toString(usedHeap, 10); + StringBuilder message = new StringBuilder(); message.append("{\"time\":\""); message.append(timeStamp); message.append("\", \"usedHeapAfterGC\": \""); - message.append(usedHeapAfterGC); + message.append(usedHeapAfterGCStr); message.append("\", \"usedHeap\": \""); - message.append(usedHeap); + message.append(usedHeapStr); message.append("\", \"usedNative\": \""); - message.append(usedNative); + message.append(usedNativeStr); + + // Used heap max is not actually that interesting, it ought to get to 100% just before a GC. + message.append("\", \"usedHeapAfterGCMax\": \""); + message.append(usedHeapAfterGCMax); + message.append("\", \"usedNativeMax\": \""); + message.append(usedNativeMax); message.append("\"}"); Javametrics.getInstance().sendJSON(MEMORYPOOLS_TOPIC, message.toString()); } } + } diff --git a/javaagent/src/main/java/com/ibm/javametrics/dataproviders/GCDataProvider.java b/javaagent/src/main/java/com/ibm/javametrics/dataproviders/GCDataProvider.java index 0bcb1a7..5940b52 100644 --- a/javaagent/src/main/java/com/ibm/javametrics/dataproviders/GCDataProvider.java +++ b/javaagent/src/main/java/com/ibm/javametrics/dataproviders/GCDataProvider.java @@ -32,20 +32,17 @@ public class GCDataProvider { /** * Returns the time spent in GC as a proportion of the time elapsed since * this method was last called. If no data is available -1 is returned. - * + * + * (Will always return -1 on first call.) + * * @return */ - public static double getGCCollectionTime() { + public static double getLatestGCPercentage() { long now = System.currentTimeMillis(); - List sunBeans = ManagementFactory.getGarbageCollectorMXBeans(); - long totalCollectionTime = 0; - if (sunBeans.size() == 0) { + long totalCollectionTime = getTotalCollectionTime(); + if( totalCollectionTime == -1) { return -1; } - for (Iterator iterator = sunBeans.iterator(); iterator.hasNext();) { - GarbageCollectorMXBean garbageCollectorMXBean = iterator.next(); - totalCollectionTime += garbageCollectorMXBean.getCollectionTime(); - } if (previousRequestTimeStamp == 0) { previousRequestTimeStamp = now; previousCollectionTime = totalCollectionTime; @@ -63,4 +60,35 @@ public static double getGCCollectionTime() { } } + /** + * Returns the time spent in GC as a proportion of the time elapsed since + * the JVM was started. If no data is available returns -1. + * + * @return the percentage of uptime spent in gc or -1.0 + */ + public static double getTotalGCPercentage() { + long totalCollectionTime = getTotalCollectionTime(); + if(totalCollectionTime == -1) { + return -1.0; + } + long uptime = ManagementFactory.getRuntimeMXBean().getUptime(); + return ((double) totalCollectionTime / (double) uptime); + } + + private static long getTotalCollectionTime() { + List sunBeans = ManagementFactory.getGarbageCollectorMXBeans(); + long totalCollectionTime = 0; + if (sunBeans.size() == 0) { + return -1; + } + for (Iterator iterator = sunBeans.iterator(); iterator.hasNext();) { + GarbageCollectorMXBean garbageCollectorMXBean = iterator.next(); + long collectionTime = garbageCollectorMXBean.getCollectionTime(); + if( collectionTime != -1) { + totalCollectionTime += collectionTime; + } + } + return totalCollectionTime; + } + } diff --git a/javaagent/src/test/java/com/ibm/javametrics/dataproviders/GCDataProviderTest.java b/javaagent/src/test/java/com/ibm/javametrics/dataproviders/GCDataProviderTest.java index a4a57d2..711b24e 100644 --- a/javaagent/src/test/java/com/ibm/javametrics/dataproviders/GCDataProviderTest.java +++ b/javaagent/src/test/java/com/ibm/javametrics/dataproviders/GCDataProviderTest.java @@ -26,11 +26,11 @@ public class GCDataProviderTest { /** - * Test method for {@link com.ibm.javametrics.dataproviders.GCDataProvider#getGCCollectionTime()}. + * Test method for {@link com.ibm.javametrics.dataproviders.GCDataProvider#getLatestGCPercentage()}. */ @Test public void testGetGCCollectionTime() { - double gctime = GCDataProvider.getGCCollectionTime(); + double gctime = GCDataProvider.getLatestGCPercentage(); int timeout = 3000; long startTime = System.currentTimeMillis(); // may get -1 returned before MXBeans are initialized, allow time for a @@ -42,7 +42,7 @@ public void testGetGCCollectionTime() { // Do nothing e.printStackTrace(); } - gctime = GCDataProvider.getGCCollectionTime(); + gctime = GCDataProvider.getLatestGCPercentage(); } assertTrue("GC time should be greater than or equal to 0, was " + gctime, gctime >= 0.0d); assertTrue("GC time should be less than 1 (i.e. less than 100%), was " + gctime, gctime <= 1d); From be230e183c15db92c63fbf53656957fe0ec77376 Mon Sep 17 00:00:00 2001 From: Howard Hellyer Date: Tue, 31 Oct 2017 16:01:51 +0000 Subject: [PATCH 2/2] Update submodule reference to pick up graphmetrics summary changes. --- dashboard/src/main/webapp/graphmetrics | 2 +- dashboard/src/main/webapp/index.html | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/dashboard/src/main/webapp/graphmetrics b/dashboard/src/main/webapp/graphmetrics index be182af..e5c1243 160000 --- a/dashboard/src/main/webapp/graphmetrics +++ b/dashboard/src/main/webapp/graphmetrics @@ -1 +1 @@ -Subproject commit be182af33dc47014c67a02e141a07bfc3f89d664 +Subproject commit e5c1243acf7bb02696e37971ae7fdb715df53761 diff --git a/dashboard/src/main/webapp/index.html b/dashboard/src/main/webapp/index.html index 70233f5..ead7140 100644 --- a/dashboard/src/main/webapp/index.html +++ b/dashboard/src/main/webapp/index.html @@ -147,7 +147,7 @@ let envTable = new TextTable('#envDiv', '#summary', localizedStrings.envTitle); let summaryTable = new TextTable('#summaryDiv', '#summary', "Summary"); // TODO move this to localizedStrings. let httpTop5 = new Top5('#top5Div', '#summary', localizedStrings.httpTop5Title); - httpTop5.settop5Options({host: hostname, filteredPath: dashboardRoot}) + httpTop5.settop5Options({host: hostname, filteredPath: dashboardRoot}); client.onmessage = function(message) { received = JSON.parse(message.data);