package de.is24.util.monitoring.tools;
import de.is24.util.monitoring.CorePlugin;
import de.is24.util.monitoring.MultiValueProvider;
import de.is24.util.monitoring.ReportVisitor;
import de.is24.util.monitoring.StateValueProvider;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.management.Attribute;
import javax.management.AttributeList;
import javax.management.JMException;
import javax.management.MBeanServer;
import javax.management.ObjectName;
import java.lang.Thread.State;
import java.lang.management.GarbageCollectorMXBean;
import java.lang.management.MemoryMXBean;
import java.lang.management.MemoryPoolMXBean;
import java.lang.management.OperatingSystemMXBean;
import java.lang.management.ThreadInfo;
import java.lang.management.ThreadMXBean;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.TreeMap;
import java.util.concurrent.TimeUnit;
/**
* A collection of Java Virtual Machine metrics.
* This code was originally taken from de.is24.util.monitoring.tools.VirtualMachineMetrics
*/
public class VirtualMachineMetrics {
private static final Logger LOGGER = LoggerFactory.getLogger(VirtualMachineMetrics.class);
private static final int MAX_STACK_TRACE_DEPTH = 100;
/**
* Per-GC statistics.
*/
public static class GarbageCollectorStats {
private final long runs, timeMS;
private GarbageCollectorStats(long runs, long timeMS) {
this.runs = runs;
this.timeMS = timeMS;
}
/**
* Returns the number of times the garbage collector has run.
*
* @return the number of times the garbage collector has run
*/
public long getRuns() {
return runs;
}
/**
* Returns the amount of time in the given unit the garbage collector has taken in total.
*
* @param unit the time unit for the return value
* @return the amount of time in the given unit the garbage collector
*/
public long getTime(TimeUnit unit) {
return unit.convert(timeMS, TimeUnit.MILLISECONDS);
}
}
/**
* The management interface for a buffer pool, for example a pool of {@link
* java.nio.ByteBuffer#allocateDirect direct} or {@link java.nio.MappedByteBuffer mapped}
* buffers.
*/
public static class BufferPoolStats {
private final long count, memoryUsed, totalCapacity;
private BufferPoolStats(long count, long memoryUsed, long totalCapacity) {
this.count = count;
this.memoryUsed = memoryUsed;
this.totalCapacity = totalCapacity;
}
/**
* Returns an estimate of the number of buffers in the pool.
*
* @return An estimate of the number of buffers in this pool
*/
public long getCount() {
return count;
}
/**
* Returns an estimate of the memory that the Java virtual machine is using for this buffer
* pool. The value returned by this method may differ from the estimate of the total {@link
* #getTotalCapacity capacity} of the buffers in this pool. This difference is explained by
* alignment, memory allocator, and other implementation specific reasons.
*
* @return An estimate of the memory that the Java virtual machine is using for this buffer
* pool in bytes, or {@code -1L} if an estimate of the memory usage is not
* available
*/
public long getMemoryUsed() {
return memoryUsed;
}
/**
* Returns an estimate of the total capacity of the buffers in this pool. A buffer's
* capacity is the number of elements it contains and the value returned by this method is
* an estimate of the total capacity of buffers in the pool in bytes.
*
* @return An estimate of the total capacity of the buffers in this pool in bytes
*/
public long getTotalCapacity() {
return totalCapacity;
}
}
public static void registerVMStates(CorePlugin corePlugin) {
final MemoryMXBean memory = VirtualMachineMBeans.getInstance().getMemory();
corePlugin.registerStateValue(new StateValueProvider() {
@Override
public long getValue() {
return memory.getHeapMemoryUsage().getInit();
}
@Override
public String getName() {
return "jvm.memory.heap.init";
}
});
corePlugin.registerStateValue(new StateValueProvider() {
@Override
public long getValue() {
return memory.getNonHeapMemoryUsage().getInit();
}
@Override
public String getName() {
return "jvm.memory.nonHeap.init";
}
});
corePlugin.registerStateValue(new StateValueProvider() {
@Override
public long getValue() {
return memory.getHeapMemoryUsage().getUsed();
}
@Override
public String getName() {
return "jvm.memory.heap.used";
}
});
corePlugin.registerStateValue(new StateValueProvider() {
@Override
public long getValue() {
return memory.getNonHeapMemoryUsage().getUsed();
}
@Override
public String getName() {
return "jvm.memory.nonHeap.used";
}
});
corePlugin.registerStateValue(new StateValueProvider() {
@Override
public long getValue() {
return memory.getHeapMemoryUsage().getCommitted();
}
@Override
public String getName() {
return "jvm.memory.heap.committed";
}
});
corePlugin.registerStateValue(new StateValueProvider() {
@Override
public long getValue() {
return memory.getNonHeapMemoryUsage().getCommitted();
}
@Override
public String getName() {
return "jvm.memory.nonHeap.committed";
}
});
for (final MemoryPoolMXBean pool : VirtualMachineMBeans.getInstance().getMemoryPools()) {
corePlugin.registerStateValue(new StateValueProvider() {
@Override
public long getValue() {
return pool.getUsage().getUsed();
}
@Override
public String getName() {
return "jvm.memory." + pool.getName().replace(" ", "_") + ".used";
}
});
corePlugin.registerStateValue(new StateValueProvider() {
@Override
public long getValue() {
return pool.getUsage().getCommitted();
}
@Override
public String getName() {
return "jvm.memory." + pool.getName().replace(" ", "_") + ".committed";
}
});
}
final ThreadMXBean threadMXBean = VirtualMachineMBeans.getInstance().getThreads();
corePlugin.registerStateValue(new StateValueProvider() {
@Override
public long getValue() {
return threadMXBean.getThreadCount();
}
@Override
public String getName() {
return "jvm.threads.count";
}
});
corePlugin.registerStateValue(new StateValueProvider() {
@Override
public long getValue() {
return threadMXBean.getDaemonThreadCount();
}
@Override
public String getName() {
return "jvm.threads.daemon";
}
});
final OperatingSystemMXBean operatingSystemMXBean = VirtualMachineMBeans.getInstance().getOs();
corePlugin.registerStateValue(new StateValueProvider() {
@Override
public long getValue() {
try {
final Method getOpenFileDescriptorCount = operatingSystemMXBean.getClass()
.getDeclaredMethod("getOpenFileDescriptorCount");
getOpenFileDescriptorCount.setAccessible(true);
return ((Long) getOpenFileDescriptorCount.invoke(operatingSystemMXBean)).longValue();
} catch (NoSuchMethodException e) {
return -1;
} catch (IllegalAccessException e) {
return -1;
} catch (InvocationTargetException e) {
return -1;
}
}
@Override
public String getName() {
return "jvm.filedescriptors.open";
}
});
corePlugin.registerStateValue(new StateValueProvider() {
@Override
public long getValue() {
try {
final Method getMaxFileDescriptorCount = operatingSystemMXBean.getClass()
.getDeclaredMethod("getMaxFileDescriptorCount");
getMaxFileDescriptorCount.setAccessible(true);
return ((Long) getMaxFileDescriptorCount.invoke(operatingSystemMXBean)).longValue();
} catch (NoSuchMethodException e) {
return -1;
} catch (IllegalAccessException e) {
return -1;
} catch (InvocationTargetException e) {
return -1;
}
}
@Override
public String getName() {
return "jvm.filedescriptors.max";
}
});
List<GarbageCollectorMXBean> garbageCollectors = VirtualMachineMBeans.getInstance().getGarbageCollectors();
LOGGER.info("found {} garbage collectors", garbageCollectors.size());
for (GarbageCollectorMXBean gc : garbageCollectors) {
final GarbageCollectorMXBean finalGC = gc;
LOGGER.info("adding garbage collector {}", gc.getName());
corePlugin.registerStateValue(new StateValueProvider() {
@Override
public long getValue() {
return finalGC.getCollectionCount();
}
@Override
public String getName() {
return "jvm.gc." + finalGC.getName() + ".count";
}
});
corePlugin.registerStateValue(new StateValueProvider() {
@Override
public long getValue() {
return finalGC.getCollectionTime();
}
@Override
public String getName() {
return "jvm.gc." + finalGC.getName() + ".time";
}
});
}
corePlugin.registerMultiValueProvider(new ThreadStateProvider());
}
public static class ThreadStateProvider implements MultiValueProvider {
@Override
public Collection<de.is24.util.monitoring.State> getValues() {
final List<de.is24.util.monitoring.State> threadStates = new ArrayList<de.is24.util.monitoring.State>();
final Map<State, Integer> conditions = new HashMap<State, Integer>();
for (State state : State.values()) {
conditions.put(state, 0);
}
ThreadMXBean threadMXBean = VirtualMachineMBeans.getInstance().getThreads();
final long[] allThreadIds = threadMXBean.getAllThreadIds();
final ThreadInfo[] allThreads = threadMXBean.getThreadInfo(allThreadIds);
int liveCount = 0;
for (ThreadInfo info : allThreads) {
if (info != null) {
final State state = info.getThreadState();
conditions.put(state, conditions.get(state) + 1);
liveCount++;
}
}
long total = 0;
for (State state : new ArrayList<State>(conditions.keySet())) {
Integer value = conditions.get(state);
total = total + value.longValue();
threadStates.add(new de.is24.util.monitoring.State("jvm.threads", state.name(), value));
}
threadStates.add(new de.is24.util.monitoring.State("jvm.threads", "total", total));
return threadStates;
}
@Override
public String getName() {
return "VMThreadStates";
}
@Override
public void accept(ReportVisitor visitor) {
visitor.reportMultiValue(this);
}
}
public Map<String, BufferPoolStats> getBufferPoolStats() {
try {
final String[] attributes = { "Count", "MemoryUsed", "TotalCapacity" };
final ObjectName direct = new ObjectName("java.nio:type=BufferPool,name=direct");
final ObjectName mapped = new ObjectName("java.nio:type=BufferPool,name=mapped");
MBeanServer mBeanServer = VirtualMachineMBeans.getInstance().getMBeanServer();
final AttributeList directAttributes = mBeanServer.getAttributes(direct, attributes);
final AttributeList mappedAttributes = mBeanServer.getAttributes(mapped, attributes);
final Map<String, BufferPoolStats> stats = new TreeMap<String, BufferPoolStats>();
final BufferPoolStats directStats = new BufferPoolStats((Long) ((Attribute) directAttributes.get(0)).getValue(),
(Long) ((Attribute) directAttributes.get(1)).getValue(),
(Long) ((Attribute) directAttributes.get(2)).getValue());
stats.put("direct", directStats);
final BufferPoolStats mappedStats = new BufferPoolStats((Long) ((Attribute) mappedAttributes.get(0)).getValue(),
(Long) ((Attribute) mappedAttributes.get(1)).getValue(),
(Long) ((Attribute) mappedAttributes.get(2)).getValue());
stats.put("mapped", mappedStats);
return Collections.unmodifiableMap(stats);
} catch (JMException e) {
return Collections.emptyMap();
}
}
}