/* * NOTE: This copyright does *not* cover user programs that use HQ * program services by normal system calls through the application * program interfaces provided as part of the Hyperic Plug-in Development * Kit or the Hyperic Client Development Kit - this is merely considered * normal use of the program, and does *not* fall under the heading of * "derived work". * * Copyright (C) [2004-2011], VMWare, Inc. * This file is part of HQ. * * HQ is free software; you can redistribute it and/or modify * it under the terms version 2 of the GNU General Public License as * published by the Free Software Foundation. This program is distributed * in the hope that it will be useful, but WITHOUT ANY WARRANTY; without * even the implied warranty of MERCHANTABILITY or FITNESS FOR A * PARTICULAR PURPOSE. See the GNU General Public License for more * details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 * USA. */ package org.hyperic.hq.stats; import java.util.Map; import java.util.TreeMap; import java.util.concurrent.ConcurrentLinkedQueue; import java.util.concurrent.ScheduledFuture; import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicLong; import java.util.concurrent.atomic.AtomicReference; import javax.management.AttributeNotFoundException; import javax.management.InstanceNotFoundException; import javax.management.MBeanException; import javax.management.MBeanServer; import javax.management.MalformedObjectNameException; import javax.management.ObjectName; import javax.management.ReflectionException; import javax.management.openmbean.CompositeDataSupport; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.hyperic.sigar.Sigar; import org.hyperic.sigar.SigarException; import org.hyperic.util.stats.StatCollector; import org.hyperic.util.stats.StatUnreachableException; import org.hyperic.util.stats.StatsObject; public abstract class AbstractStatsCollector { private final Log log = LogFactory.getLog(AbstractStatsCollector.class); // using tree due to ordering capabilities private final Map<String, StatCollector> statKeys = new TreeMap<String, StatCollector>(); private final ConcurrentLinkedQueue<StatsObject> queue = new ConcurrentLinkedQueue<StatsObject>(); private final AtomicBoolean hasStarted = new AtomicBoolean(false); private Long pid; protected final Sigar sigar = new Sigar(); protected final MBeanServer mBeanServer; public static final String JVM_TOTAL_MEMORY = "JVM_TOTAL_MEMORY", JVM_FREE_MEMORY = "JVM_FREE_MEMORY", JVM_MAX_MEMORY = "JVM_MAX_MEMORY", SIGAR_1MLOAD = "SIGAR_1MLOAD", SIGAR_CPU = "SIGAR_CPU", SIGAR_PROC_RES_MEM = "SIGAR_PROC_RES_MEM", SIGAR_TCP_INERRS = "SIGAR_TCP_INERRS", SIGAR_TCP_RETRANS = "SIGAR_TCP_RETRANS", SIGAR_PAGEOUT = "SIGAR_PAGEOUT", SIGAR_PAGEIN = "SIGAR_PAGEIN", EDEN_MEMORY_USED = "EDEN_MEMORY_USED", SURVIVOR_MEMORY_USED = "SURVIVOR_MEMORY_USED", TENURED_MEMORY_USED = "TENURED_MEMORY_USED", HEAP_MEMORY_USED = "HEAP_MEMORY_USED", JVM_MARKSWEEP_GC = "JVM_MARKSWEEP_GC", JVM_COPY_GC = "JVM_COPY_GC", STATS_COLLECTOR = "STATS_COLLECTOR"; public AbstractStatsCollector(MBeanServer mBeanServer) { this.mBeanServer = mBeanServer; registerInternalStats(); } protected abstract ScheduledFuture<?> scheduleAtFixedRate(Runnable runnable, long seconds); public Map<String, StatCollector> getStatKeys() { return statKeys; } void setStarted(boolean started) { hasStarted.set(started); } public void register(final String statId) { // can't register any stats after the collector has been initially // started due to consistent ordering in the csv output file if (hasStarted.get()) { log.warn("Cannot register " + statId + " because the collector has already been started."); return; } if (statKeys.containsKey(statId)) { return; } statKeys.put(statId, null); } public void register(final StatCollector stat) { // can't register any stats after the collector has been initially // started due to the need for consistent ordering in the csv output // file if (hasStarted.get()) { log.warn(stat.getId() + " attempted to register in stat collector although it has " + " already been started, not allowing"); return; } if (statKeys.containsKey(stat.getId())) { log.warn(stat.getId() + " attempted to register in stat collector although it has " + " already been registered, not allowing"); return; } statKeys.put(stat.getId(), stat); } public void addStat(final long value, final String id) { if (!statKeys.containsKey(id)) { return; } final long now = System.currentTimeMillis(); // may want to look at pooling these objects to avoid creation overhead final StatsObject stat = new StatsObject(value, id); queue.add(stat); final long total = System.currentTimeMillis() - now; queue.add(new StatsObject(total, STATS_COLLECTOR)); } private final void registerInternalStats() { register(new StatCollector() { public String getId() { return JVM_TOTAL_MEMORY; } public long getVal() { return Runtime.getRuntime().totalMemory(); } }); register(new StatCollector() { public String getId() { return JVM_FREE_MEMORY; } public long getVal() { return Runtime.getRuntime().freeMemory(); } }); register(new StatCollector() { public String getId() { return JVM_MAX_MEMORY; } public long getVal() { return Runtime.getRuntime().maxMemory(); } }); register(new StatCollector() { public String getId() { return SIGAR_1MLOAD; } public long getVal() throws StatUnreachableException { try { return (long) sigar.getLoadAverage()[0]; } catch (SigarException e) { throw new StatUnreachableException(e.getMessage(), e); } } }); register(new StatCollector() { public String getId() { return SIGAR_CPU; } public long getVal() throws StatUnreachableException { try { return (long) (sigar.getProcCpu(getProcPid()).getPercent() * 100); } catch (SigarException e) { throw new StatUnreachableException(e.getMessage(), e); } } }); register(new StatCollector() { public String getId() { return SIGAR_PROC_RES_MEM; } public long getVal() throws StatUnreachableException { try { return (long) sigar.getProcMem(getProcPid()).getResident(); } catch (SigarException e) { throw new StatUnreachableException(e.getMessage(), e); } } }); register(new StatCollector() { private long last = 0; public String getId() { return SIGAR_TCP_INERRS; } public long getVal() throws StatUnreachableException { try { long curr = sigar.getTcp().getInErrs(); long rtn = curr - last; last = curr; return rtn; } catch (SigarException e) { throw new StatUnreachableException(e.getMessage(), e); } } }); register(new StatCollector() { private long last = 0; private boolean isFirst = true; public String getId() { return SIGAR_TCP_RETRANS; } public long getVal() throws StatUnreachableException { try { long curr = sigar.getTcp().getRetransSegs(); long rtn = curr - last; last = curr; if (isFirst) { isFirst = false; throw new StatUnreachableException("don't return first value"); } return rtn; } catch (SigarException e) { throw new StatUnreachableException(e.getMessage(), e); } } }); register(new StatCollector() { private long last = 0; private boolean isFirst = true; public String getId() { return SIGAR_PAGEOUT; } public long getVal() throws StatUnreachableException { try { long curr = sigar.getSwap().getPageOut(); long rtn = curr - last; last = curr; if (isFirst) { isFirst = false; throw new StatUnreachableException("don't return first value"); } return rtn; } catch (SigarException e) { throw new StatUnreachableException(e.getMessage(), e); } } }); register(new StatCollector() { private long last = 0; private boolean isFirst = true; public String getId() { return SIGAR_PAGEIN; } public long getVal() throws StatUnreachableException { try { long curr = sigar.getSwap().getPageIn(); long rtn = curr - last; last = curr; if (isFirst) { isFirst = false; throw new StatUnreachableException("don't return first value"); } return rtn; } catch (SigarException e) { throw new StatUnreachableException(e.getMessage(), e); } } }); register(new MBeanCollector(EDEN_MEMORY_USED, "java.lang:type=MemoryPool,name=", new String[] { "Par Eden Space", "PS Eden Space", "Eden Space" }, "Usage", "used")); register(new MBeanCollector(SURVIVOR_MEMORY_USED, "java.lang:type=MemoryPool,name=", new String[] {"Par Survivor Space", "PS Survivor Space", "Survivor Space" }, "Usage", "used")); register(new MBeanCollector(TENURED_MEMORY_USED, "java.lang:type=MemoryPool,name=", new String[] { "CMS Old Gen", "PS Old Gen", "Tenured Gen" }, "Usage", "used")); register(new MBeanCollector(HEAP_MEMORY_USED, "java.lang:type=Memory", new String[] {""}, "HeapMemoryUsage", "used")); register(new MBeanCollector(JVM_MARKSWEEP_GC, "java.lang:type=GarbageCollector,name=", new String[] { "ConcurrentMarkSweep", "PS MarkSweep" }, "CollectionTime", true)); register(new MBeanCollector(JVM_COPY_GC, "java.lang:type=GarbageCollector,name=", new String[] { "Copy", "ParNew", "PS Scavenge" }, "CollectionTime", true)); register(STATS_COLLECTOR); } protected long getProcPid() { if (pid != null) { return pid.longValue(); } pid = new Long(sigar.getPid()); return pid.longValue(); } public StatsObject generateMarker() { final StatsObject marker = new StatsObject(-1, null); queue.add(marker); return marker; } public StatsObject pollQueue() { return queue.poll(); } protected class StatSampler implements StatCollector { private final StatCollector stat; private final String id; private final AtomicLong value = new AtomicLong(); private final AtomicReference<StatUnreachableException> exceptionRef = new AtomicReference<StatUnreachableException>(); protected StatSampler(StatCollector s) { this.id = s.getId() + "_MAX"; this.stat = s; final Runnable runnable = new Runnable() { public void run() { if (!hasStarted.get()) { return; } try { final long newVal = stat.getVal(); synchronized (value) { final long val = value.get(); if (Math.abs(newVal) > Math.abs(val)) { value.set(newVal); } exceptionRef.set(null); } } catch (StatUnreachableException e) { exceptionRef.set(e); } catch (Throwable t) { log.error(t,t); } } }; scheduleAtFixedRate(runnable, 1000); } public String getId() { return id; } public long getVal() throws StatUnreachableException { StatUnreachableException ex; if ((ex = exceptionRef.get()) != null) { throw ex; } synchronized (value) { return value.getAndSet(0); } } } protected class MBeanCollector implements StatCollector { private final String _id, _objectName, _attrName, _valProp; private final String[] _names; private StatUnreachableException _ex = null; private int failures = 0; private final boolean _isComposite, _isTrend; private long _last = 0l; protected MBeanCollector(String id, String objectName, String[] names, String attrName, boolean trend) { _id = id; _objectName = objectName; _attrName = attrName; _names = names; _valProp = null; _isComposite = false; _isTrend = trend; } protected MBeanCollector(String id, String objectName, String attrName, boolean trend) { _id = id; _objectName = objectName; _attrName = attrName; _names = new String[] { "" }; _valProp = null; _isComposite = false; _isTrend = trend; } protected MBeanCollector(String id, String objectName, String[] names, String attrName, String valProp) { _names = names; _id = id; _objectName = objectName; _attrName = attrName; _valProp = valProp; _isComposite = true; _isTrend = false; } public String getId() { return _id; } public long getVal() throws StatUnreachableException { // no need to keep generating a new exception. If it fails // 10 times, assume that the mbean server is not on. if (_ex != null && failures >= 10) { throw _ex; } Exception exception = null; for (int i = 0; i < _names.length; i++) { try { if (_isComposite) { long val = getComposite(_names[i]); long rtn = (_isTrend) ? (val - _last) : val; _last = val; return rtn; } else { long val = getValue(_names[i]); long rtn = (_isTrend) ? (val - _last) : val; _last = val; return rtn; } } catch (Exception e) { exception = e; } } failures++; final String msg = _objectName + " query failed"; if (exception == null) { _ex = new StatUnreachableException(msg); throw _ex; } _ex = new StatUnreachableException(msg, exception); throw _ex; } private final long getComposite(String name) throws StatUnreachableException, AttributeNotFoundException, InstanceNotFoundException, MBeanException, ReflectionException, MalformedObjectNameException, NullPointerException { final ObjectName objName = new ObjectName(_objectName + name); final CompositeDataSupport cds = (CompositeDataSupport) mBeanServer.getAttribute(objName, _attrName); final long val = ((Number) cds.get(_valProp)).longValue(); failures = 0; return val; } private final long getValue(String name) throws MalformedObjectNameException, NullPointerException, AttributeNotFoundException, InstanceNotFoundException, MBeanException, ReflectionException { final ObjectName _objName = new ObjectName(_objectName + name); long rtn = ((Number) mBeanServer.getAttribute(_objName, _attrName)).longValue(); failures = 0; return rtn; } } public void destory() { this.queue.clear() ; this.statKeys.clear() ; }//EOM }