package zendo.playground.sd; import java.lang.management.ManagementFactory; import java.text.MessageFormat; import java.util.ArrayList; import java.util.List; import javax.management.MBeanServerConnection; import javax.management.ObjectName; import javax.management.openmbean.CompositeData; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; public class SysInfoCalculator { private static final Log log = LogFactory.getLog(SysInfoCalculator.class); private MBeanServerConnection clientConnector = ManagementFactory.getPlatformMBeanServer(); private int sequenceNumber = 0; private final int STOREDVALUES = 3; private long loadIndex = -1; private MeasuredValues<Double> systemLoadContainer = new MeasuredValues<Double>("systemLoad"); private MeasuredValues<Integer> numThreadsContainer = new MeasuredValues<Integer>("numThreads"); private MeasuredValues<Long> usedSwapSpaceSizeContainer = new MeasuredValues<Long>("freeSwapSpaceSize"); private MeasuredValues<Long> usedheapMemoryContainer = new MeasuredValues<Long>("heapMemoryUsed"); private MeasuredValues<Double> cpuUsageContainer = new MeasuredValues<Double>("cpuUsage"); // Levelborders private double numThreadsPercentAlarm = 95; private double numThreadsPercentWarning = 90; private double usedSwapSpaceSizePercentAlarm = 85; private double usedSwapSpaceSizePercentWarning = 70; private double usedHeapSizePercentAlarm = 90; private double usedHeapSizePercentWarning = 80; private double cpuUsagePercentAlarm = 90; private double cpuUsagePercentWarning = 80; private double systemLoadWarning = 1.0; private double systemLoadAlarm = 1.5; private AlarmLevelType currentAlarmLevel = AlarmLevelType.UNKNOWN; private long levelLastUpdated = System.currentTimeMillis(); private long repeatInterval = 1000 * 60 * 2; private long levelTimeout = repeatInterval * 3; public AlarmLevelType getAlarmLevel() { // level timed out? if (System.currentTimeMillis() > levelLastUpdated + levelTimeout) { currentAlarmLevel = AlarmLevelType.NORMAL; levelLastUpdated = System.currentTimeMillis(); } return currentAlarmLevel; } /** * Periodically execution of the load checks */ public void checkerThread() { long index = -1; long dummy = 0; int numChecks = 0; dummy = checkSwapSpace(); if (dummy != -1) { index += dummy; numChecks++; } dummy = checkHeapMemory(); if (dummy != -1) { index += dummy; numChecks++; } dummy = checkThreadPool(); if (dummy != -1) { index += dummy; numChecks++; } dummy = checkSystemLoad(); if (dummy != -1) { index += dummy; numChecks++; } dummy = checkCpuUsagePercentage(); if (dummy != -1) { index += dummy; numChecks++; } // average index of the checks if (numChecks > 0) { index = (index / numChecks); } // if level is on ALARM increase the index as marker this.loadIndex = (currentAlarmLevel == AlarmLevelType.ALARM) ? index + 1000 : index; if (log.isDebugEnabled()) { log.debug(MessageFormat.format("Current level : {0}, Loadindex : {1}", currentAlarmLevel.name(), loadIndex)); } } /** * Check of the availability of swap space. Verifying against warning and alarm border * (percentage use of swap space) * * @return the load index */ public long checkSwapSpace() { long index = -1; try { long freeSwapSpaceSize = (Long) clientConnector.getAttribute(new ObjectName("java.lang:type=OperatingSystem"), "FreeSwapSpaceSize"); long totalSwapSpaceSize = (Long) clientConnector.getAttribute(new ObjectName("java.lang:type=OperatingSystem"), "TotalSwapSpaceSize"); if (freeSwapSpaceSize > 0 || totalSwapSpaceSize > 0) { log.debug(MessageFormat.format("total swap space : {0}; free swap space : {1}", totalSwapSpaceSize, freeSwapSpaceSize)); } else if (log.isDebugEnabled()) { log.warn(MessageFormat.format("Could not get all necessary attributes : total swap space : {0}; free swap space : {1}", totalSwapSpaceSize, freeSwapSpaceSize)); } double avg = usedSwapSpaceSizeContainer.getAverage(totalSwapSpaceSize - freeSwapSpaceSize); index = setLevel("usedSwapSpaceSize", avg, totalSwapSpaceSize * usedSwapSpaceSizePercentWarning / 100.0, totalSwapSpaceSize * usedSwapSpaceSizePercentAlarm / 100.0); if (log.isDebugEnabled()) { log.debug(MessageFormat.format("avg swap space (percentage): {0}", avg * 100)); } } catch (Exception e) { log.info("Error checking the swap space on tomcat side!", e); } return index; } /** * @return */ public long checkHeapMemory() { long index = -1; try { CompositeData heap = (CompositeData) clientConnector.getAttribute(new ObjectName("java.lang:type=Memory"), "HeapMemoryUsage"); Long maxHeapSize = (Long) heap.get("max"); Long usedHeapSize = (Long) heap.get("used"); if (maxHeapSize > 0 && usedHeapSize > 0) { Double avg = usedheapMemoryContainer.getAverage(usedHeapSize); index = setLevel("usedHeapSize", avg, maxHeapSize * usedHeapSizePercentWarning / 100.0, maxHeapSize * usedHeapSizePercentAlarm / 100.0); if (log.isDebugEnabled()) { log.debug(MessageFormat.format("avg used heap (percentage): {}", avg * 100)); } } } catch (Exception e) { log.info("Error checking the heap usage!", e); } return index; } /** * Check of the usage of the thread pool. Verifying against warning and alarm border * (percentage use of available threads) * * @return the calculated load index where lower is better */ public long checkThreadPool() { long index = -1; try { Integer maxThreads = (Integer) clientConnector.getAttribute(new ObjectName("Catalina:type=Executor,name=tomcatThreadPool"), "maxThreads"); Integer numThreads = (Integer) clientConnector.getAttribute(new ObjectName("java.lang:type=Threading"), "ThreadCount"); if (log.isDebugEnabled()) { log.debug(MessageFormat.format("max threads : {}", maxThreads)); } Double avg = numThreadsContainer.getAverage(numThreads); index = setLevel("numThreads", avg, maxThreads * numThreadsPercentWarning / 100.0, maxThreads * numThreadsPercentAlarm / 100.0); if (log.isDebugEnabled()) { log.debug(MessageFormat.format("num threads : {}; average : {}", numThreads, avg)); } } catch (Exception e) { log.info("Error checking the tread pool on tomcat side!", e); } return index; } /** * Check of the system load depending on number of processors. Verfifying against warning and * alarm border. REMEMBER : this value is not provided on every operating system * * @return load index where lower is better */ public long checkSystemLoad() { long index = -1; try { Double systemLoadAverage = (Double) clientConnector.getAttribute(new ObjectName("java.lang:type=OperatingSystem"), "SystemLoadAverage"); int availableProcessors = (Integer) clientConnector.getAttribute(new ObjectName("java.lang:type=OperatingSystem"), "AvailableProcessors"); if (log.isDebugEnabled()) { log.debug(MessageFormat.format("System load average : {}; available Processors : {}", systemLoadAverage, availableProcessors)); } if (systemLoadAverage != null && systemLoadAverage >= 0) { double avg = systemLoadContainer.getAverage(systemLoadAverage); index = setLevel("systemLoad", avg, availableProcessors * systemLoadWarning, availableProcessors * systemLoadAlarm); } } catch (Exception e) { log.info("Error checking the system load!", e); } return index; } /** * Check of the cpu usage. Verfifying against warning and alarm border. * * @return a load index where lower is better */ public long checkCpuUsagePercentage() { long index = -1; long timestampBefore = 0; long timestampAfter = 0; double sumPercentage = 0.0; try { int availableProcessors = (Integer) clientConnector.getAttribute(new ObjectName("java.lang:type=OperatingSystem"), "AvailableProcessors"); for (int i = 0; i < STOREDVALUES; i++) { timestampBefore = System.nanoTime(); long processCpuTimeBefore = (Long) clientConnector.getAttribute(new ObjectName("java.lang:type=OperatingSystem"), "ProcessCpuTime"); if (log.isDebugEnabled()) { log.debug(MessageFormat.format("----> timestamp before : {}; process cpu time before : {}", timestampBefore, processCpuTimeBefore)); } try { Thread.sleep(500); } catch (Exception e) { } long processCpuTimeAfter = (Long) clientConnector.getAttribute(new ObjectName("java.lang:type=OperatingSystem"), "ProcessCpuTime"); timestampAfter = System.nanoTime(); if (log.isDebugEnabled()) { log.debug(MessageFormat.format("----> timestamp after : {}; process cpu time after : {}", timestampAfter, processCpuTimeAfter)); } sumPercentage += ((processCpuTimeAfter - processCpuTimeBefore) * 100.0) / (timestampAfter - timestampBefore); if (log.isDebugEnabled()) { log.debug(MessageFormat.format("====> sum percentage : {}", sumPercentage)); } } if (log.isDebugEnabled()) { log.debug(MessageFormat.format("CPU usage percentage : {}", sumPercentage / STOREDVALUES / availableProcessors)); } double avg = cpuUsageContainer.getAverage(sumPercentage / STOREDVALUES / availableProcessors); index = setLevel("cpuUsagePercentage", avg, cpuUsagePercentWarning, cpuUsagePercentAlarm); } catch (Exception e) { log.info("Error checking the cpu usage!", e); } return index; } /** * Set warn level. If new current calculated level is less or equal then the valid current * level, the current value won't be changed. * * @param type type of check * @param avg the average of the last STOREDVALUES detected values * @param warnLevelBorder the warning border * @param alarmLevelBorder the alarm border * @return the load level calculated from the given data */ public long setLevel(String type, double avg, double warnLevelBorder, double alarmLevelBorder) { AlarmLevelType level = AlarmLevelType.NORMAL; // get AlarmLevel if (avg >= alarmLevelBorder) { level = AlarmLevelType.ALARM; log.info("ALARM-" + type); } else if (avg >= warnLevelBorder) { level = AlarmLevelType.WARNING; log.info("WARNING-" + type); } if (System.currentTimeMillis() <= levelLastUpdated + levelTimeout) { if (level.getValue() >= currentAlarmLevel.getValue()) { updateAlarmLevel(level); } } else { updateAlarmLevel(level); } Double index = avg * 100 / warnLevelBorder; if (log.isDebugEnabled()) { log.debug(MessageFormat.format("Calculated level : {}, simple index : {}", level.name(), index)); } return level.getValue() * index.longValue(); } private void updateAlarmLevel(AlarmLevelType level) { currentAlarmLevel = level; levelLastUpdated = System.currentTimeMillis(); } /** * container for the last x items of the measured values */ private class MeasuredValues<T extends Number> { @SuppressWarnings("unused") private String valueName; private List<T> values = new ArrayList<T>(); private int indexOldestValue = 0; public MeasuredValues(String valueName) { this.setValueName(valueName); } public double getAverage(T newValue) { double result = 0.0D; // special case starting phase without if (values.size() < STOREDVALUES) { values.add(newValue); } else { values.set(indexOldestValue, newValue); indexOldestValue = (indexOldestValue + 1) % STOREDVALUES; } for (T value : values) { result += value.doubleValue(); } return (values.size() > 0) ? result / values.size() : 0.0; } public void setValueName(String valueName) { this.valueName = valueName; } } public void setClientConnector(MBeanServerConnection clientConnector) { this.clientConnector = clientConnector; } public void setNumThreadsPercentAlarm(double numThreadsAlarm) { this.numThreadsPercentAlarm = numThreadsAlarm; } public void setNumThreadsPercentWarning(double numThreadsWarning) { this.numThreadsPercentWarning = numThreadsWarning; } public void setUsedSwapSpaceSizePercentAlarm(double usedSwapSpaceSizePercentAlarm) { this.usedSwapSpaceSizePercentAlarm = usedSwapSpaceSizePercentAlarm; } public void setUsedSwapSpaceSizePercentWarning(double usedSwapSpaceSizePercentWarning) { this.usedSwapSpaceSizePercentWarning = usedSwapSpaceSizePercentWarning; } public void setCpuUsagePercentAlarm(double cpuUsageAlarm) { this.cpuUsagePercentAlarm = cpuUsageAlarm; } public void setCpuUsagePercentWarning(double cpuUsageWarning) { this.cpuUsagePercentWarning = cpuUsageWarning; } public void setSystemLoadWarning(double systemLoadWarning) { this.systemLoadWarning = systemLoadWarning; } public void setSystemLoadAlarm(double systemLoadAlarm) { this.systemLoadAlarm = systemLoadAlarm; } public void setUsedHeapSizePercentAlarm(double usedHeapSizePercentAlarm) { this.usedHeapSizePercentAlarm = usedHeapSizePercentAlarm; } public void setUsedHeapSizePercentWarning(double usedHeapSizePercentWarning) { this.usedHeapSizePercentWarning = usedHeapSizePercentWarning; } /* * (non-Javadoc) * @see com.netviewer.nvserver.presence.business.LoadIndicator#getLoadIndex() */ public long getLoadIndex() { return this.loadIndex; } /* * (non-Javadoc) * @see com.netviewer.nvserver.presence.business.load.LoadIndicator#getAlarmLevelName() */ public String getAlarmLevelName() { return this.currentAlarmLevel.name(); } public void setRepeatInterval(long repeatInterval) { this.repeatInterval = repeatInterval; this.levelTimeout = 3 * repeatInterval; } public List<Double> getSystemLoadValues() { return systemLoadContainer.values; } public List<Integer> getNumThreadsValues() { return numThreadsContainer.values; } public List<Long> getUsedSwapSpaceSizeValues() { return usedSwapSpaceSizeContainer.values; } public List<Long> getUsedheapMemoryValues() { return usedheapMemoryContainer.values; } public List<Double> getCpuUsageValues() { return cpuUsageContainer.values; } public double getNumThreadsPercentAlarm() { return numThreadsPercentAlarm; } public double getNumThreadsPercentWarning() { return numThreadsPercentWarning; } public double getUsedSwapSpaceSizePercentAlarm() { return usedSwapSpaceSizePercentAlarm; } public double getUsedSwapSpaceSizePercentWarning() { return usedSwapSpaceSizePercentWarning; } public double getUsedHeapSizePercentAlarm() { return usedHeapSizePercentAlarm; } public double getUsedHeapSizePercentWarning() { return usedHeapSizePercentWarning; } public double getCpuUsagePercentAlarm() { return cpuUsagePercentAlarm; } public double getCpuUsagePercentWarning() { return cpuUsagePercentWarning; } public double getSystemLoadWarning() { return systemLoadWarning; } public double getSystemLoadAlarm() { return systemLoadAlarm; } public static void main(String[] args) { SysInfoCalculator calc = new SysInfoCalculator(); System.out.println( calc.checkCpuUsagePercentage() ); } }