/*
* jPOS Project [http://jpos.org]
* Copyright (C) 2000-2017 jPOS Software SRL
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* 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 Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.jpos.q2.qbean;
import org.jpos.core.Configuration;
import org.jpos.core.ConfigurationException;
import org.jpos.iso.ISOUtil;
import org.jpos.q2.Q2;
import org.jpos.q2.QBeanSupport;
import org.jpos.util.Loggeable;
import org.jpos.util.Logger;
import org.jpos.util.NameRegistrar;
import javax.management.MBeanServerConnection;
import java.io.*;
import java.lang.management.ManagementFactory;
import java.lang.management.OperatingSystemMXBean;
import java.lang.management.RuntimeMXBean;
import java.lang.management.ThreadMXBean;
import java.net.InetAddress;
import java.nio.charset.Charset;
import java.time.Instant;
import java.time.ZoneId;
import java.time.format.TextStyle;
import java.time.zone.ZoneOffsetTransition;
import java.time.zone.ZoneOffsetTransitionRule;
import java.util.List;
import java.util.Locale;
/**
* Periodically dumps Thread and memory usage
*
* @author apr@cs.com.uy
* @version $Id$
* @see Logger
*/
public class SystemMonitor extends QBeanSupport
implements Runnable, SystemMonitorMBean, Loggeable
{
private long sleepTime = 60 * 60 * 1000;
private long delay = 0;
private boolean detailRequired = false;
private Thread me = null;
private static final int MB = 1024*1024;
private String[] scripts;
private String frozenDump;
public void startService() {
try {
log.info("Starting SystemMonitor");
me = new Thread(this,"SystemMonitor");
me.start();
} catch (Exception e) {
log.warn("error starting service", e);
}
}
public void stopService() {
log.info("Stopping SystemMonitor");
if (me != null)
me.interrupt();
}
public synchronized void setSleepTime(long sleepTime) {
this.sleepTime = sleepTime;
setModified(true);
if (me != null)
me.interrupt();
}
public synchronized long getSleepTime() {
return sleepTime;
}
public synchronized void setDetailRequired(boolean detail) {
this.detailRequired = detail;
setModified(true);
if (me != null)
me.interrupt();
}
public synchronized boolean getDetailRequired() {
return detailRequired;
}
void dumpThreads(ThreadGroup g, PrintStream p, String indent) {
Thread[] list = new Thread[g.activeCount() + 5];
int nthreads = g.enumerate(list);
for (int i = 0; i < nthreads; i++)
p.println(indent + list[i]);
}
public void showThreadGroup(ThreadGroup g, PrintStream p, String indent) {
if (g.getParent() != null)
showThreadGroup(g.getParent(), p, indent + " ");
else
dumpThreads(g, p, indent + " ");
}
public void run() {
while (running()) {
log.info(this);
frozenDump = null;
try {
long expected = System.currentTimeMillis() + sleepTime;
Thread.sleep(sleepTime);
delay = System.currentTimeMillis() - expected;
} catch (InterruptedException ignored) {
}
}
log.info(this);
}
public void dump (PrintStream p, String indent) {
if (frozenDump == null)
frozenDump = generateFrozenDump(indent);
p.print(frozenDump);
}
@Override
public void setConfiguration(Configuration cfg) throws ConfigurationException {
super.setConfiguration(cfg);
scripts = cfg.getAll("script");
}
private SecurityManager getSecurityManager() {
return System.getSecurityManager();
}
private boolean hasSecurityManager() {
return getSecurityManager() != null;
}
private Runtime getRuntimeInstance() {
return Runtime.getRuntime();
}
private long getServerUptimeAsMillisecond() {
return getServer().getUptime();
}
private String getInstanceIdAsString() {
return getServer().getInstanceId().toString();
}
private String getRevision() {
return Q2.getRevision();
}
private String getLocalHost () {
try {
return InetAddress.getLocalHost().toString();
} catch (Exception e) {
return e.getMessage();
}
}
private String generateFrozenDump(String indent) {
RuntimeMXBean runtimeMXBean = ManagementFactory.getRuntimeMXBean();
ThreadMXBean mxBean = ManagementFactory.getThreadMXBean();
ByteArrayOutputStream baos = new ByteArrayOutputStream();
PrintStream p = new PrintStream(baos);
String newIndent = indent + " ";
Runtime r = getRuntimeInstance();
ZoneId zi = ZoneId.systemDefault();
Instant instant = Instant.now();
File cwd = new File(".");
String freeSpace = ISOUtil.readableFileSize(cwd.getFreeSpace());
String usableSpace = ISOUtil.readableFileSize(cwd.getUsableSpace());
p.printf ("%s OS: %s (%s)%n", indent, System.getProperty("os.name"), System.getProperty("os.version"));
p.printf ("%s process name: %s%n", indent, runtimeMXBean.getName());
p.printf ("%s host: %s%n", indent, getLocalHost());
p.printf ("%s cwd: %s%n", indent, System.getProperty("user.dir"));
p.printf ("%s free space: %s%n", indent, freeSpace);
if (!freeSpace.equals(usableSpace))
p.printf ("%s usable space: %s%n", indent, usableSpace);
p.printf ("%s version: %s (%s)%n", indent, Q2.getVersion(), getRevision());
p.printf ("%s instance: %s%n", indent, getInstanceIdAsString());
p.printf ("%s uptime: %s (%f)%n", indent, ISOUtil.millisToString(getServerUptimeAsMillisecond()), loadAverage());
p.printf ("%s processors: %d%n", indent, r.availableProcessors());
p.printf ("%s drift : %d%n", indent, delay);
p.printf ("%smemory(t/u/f): %d/%d/%d%n", indent,
r.totalMemory()/MB, (r.totalMemory() - r.freeMemory())/MB, r.freeMemory()/MB);
p.printf("%s encoding: %s%n", indent, Charset.defaultCharset());
p.printf("%s timezone: %s (%s) %s%n", indent, zi,
zi.getDisplayName(TextStyle.FULL, Locale.getDefault()),
zi.getRules().getOffset(instant).toString());
p.printf("%swatch service: %s%n", indent, getServer().getWatchServiceClassname());
List<ZoneOffsetTransitionRule> l = zi.getRules().getTransitionRules();
for (ZoneOffsetTransitionRule tr : l) {
p.printf("%s rule: %s%n", indent, tr.toString());
}
ZoneOffsetTransition tran = zi.getRules().nextTransition(instant);
if (tran != null) {
Instant in = tran.getInstant();
p.printf("%s transition: %s (%s)%n", indent, in, in.atZone(zi));
}
p.printf("%s clock: %d %s%n", indent, System.currentTimeMillis() / 1000L, instant);
if (hasSecurityManager())
p.printf("%s sec-manager: %s%n", indent, getSecurityManager());
p.printf("%s thread count: %d%n", indent, mxBean.getThreadCount());
p.printf("%s peak threads: %d%n", indent, mxBean.getPeakThreadCount());
p.printf("%s user threads: %d%n", indent, Thread.activeCount());
showThreadGroup(Thread.currentThread().getThreadGroup(), p, newIndent);
NameRegistrar.getInstance().dump(p, indent, detailRequired);
for (String s : scripts) {
p.printf("%s%s:%n", indent, s);
exec(s, p, newIndent);
}
return baos.toString();
}
private void exec (String script, PrintStream ps, String indent) {
try {
Process p = Runtime.getRuntime().exec(script);
BufferedReader in = new BufferedReader(new InputStreamReader(p.getInputStream()) );
String line;
while ((line = in.readLine()) != null) {
ps.printf("%s%s%n", indent, line);
}
} catch (Exception e) {
e.printStackTrace(ps);
}
}
private double loadAverage () {
MBeanServerConnection mbsc = ManagementFactory.getPlatformMBeanServer();
try {
OperatingSystemMXBean osMBean = ManagementFactory.newPlatformMXBeanProxy(
mbsc, ManagementFactory.OPERATING_SYSTEM_MXBEAN_NAME, OperatingSystemMXBean.class);
return osMBean.getSystemLoadAverage();
} catch (Throwable ignored) { }
return -1;
}
}