diff --git a/jpos/src/main/java/org/jpos/log/AuditLogEvent.java b/jpos/src/main/java/org/jpos/log/AuditLogEvent.java
index 4228194ada..501cd494d0 100644
--- a/jpos/src/main/java/org/jpos/log/AuditLogEvent.java
+++ b/jpos/src/main/java/org/jpos/log/AuditLogEvent.java
@@ -36,7 +36,8 @@
@JsonSubTypes.Type(value = Shutdown.class, name = "shutdown"),
@JsonSubTypes.Type(value = DeployActivity.class, name = "deploy-activity"),
@JsonSubTypes.Type(value = ThrowableAuditLogEvent.class, name = "throwable"),
- @JsonSubTypes.Type(value = License.class, name = "license")
+ @JsonSubTypes.Type(value = License.class, name = "license"),
+ @JsonSubTypes.Type(value = SysInfo.class, name = "sysinfo"),
})
-public sealed interface AuditLogEvent permits Deploy, DeployActivity, ThrowableAuditLogEvent, License, LogMessage, Shutdown, Start, Stop, UnDeploy { }
+public sealed interface AuditLogEvent permits Deploy, DeployActivity, License, LogMessage, Shutdown, Start, Stop, SysInfo, ThrowableAuditLogEvent, UnDeploy { }
diff --git a/jpos/src/main/java/org/jpos/log/evt/KV.java b/jpos/src/main/java/org/jpos/log/evt/KV.java
new file mode 100644
index 0000000000..a994ee4e00
--- /dev/null
+++ b/jpos/src/main/java/org/jpos/log/evt/KV.java
@@ -0,0 +1,10 @@
+package org.jpos.log.evt;
+
+import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlProperty;
+import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlRootElement;
+import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlText;
+
+@JacksonXmlRootElement(localName = "entry")
+public record KV(
+ @JacksonXmlProperty(isAttribute = true, localName = "key") String key,
+ @JacksonXmlText String value) { }
diff --git a/jpos/src/main/java/org/jpos/log/evt/LogEvt.java b/jpos/src/main/java/org/jpos/log/evt/LogEvt.java
index 02b554f627..4ba41a2f40 100644
--- a/jpos/src/main/java/org/jpos/log/evt/LogEvt.java
+++ b/jpos/src/main/java/org/jpos/log/evt/LogEvt.java
@@ -25,7 +25,6 @@
import org.jpos.log.AuditLogEvent;
import java.time.Instant;
import java.util.List;
-import java.util.UUID;
@JacksonXmlRootElement(localName = "log")
public record LogEvt(
diff --git a/jpos/src/main/java/org/jpos/log/evt/ProcessOutput.java b/jpos/src/main/java/org/jpos/log/evt/ProcessOutput.java
new file mode 100644
index 0000000000..493ccbfeb9
--- /dev/null
+++ b/jpos/src/main/java/org/jpos/log/evt/ProcessOutput.java
@@ -0,0 +1,25 @@
+package org.jpos.log.evt;
+
+import com.fasterxml.jackson.annotation.JsonInclude;
+import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlProperty;
+import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlText;
+
+/**
+ * Record representing the output of a process, encapsulating the process name,
+ * standard output, and optionally standard error.
+ *
+ *
This record makes use of Jackson annotations to control the serialization
+ * and deserialization process. The {@code name} field is serialized as an
+ * attribute with the local name "name". The {@code stdout} field is serialized
+ * as XML text. The {@code stderr} field is included in the JSON output only if
+ * it is non-null.
+ *
+ * @param name the name of the process, serialized as an XML attribute with the local name "name"
+ * @param stdout the standard output of the process, serialized as XML text
+ * @param stderr the standard error of the process, included in JSON output only if non-null
+ */
+public record ProcessOutput(
+ @JacksonXmlProperty(isAttribute = true, localName = "name") String name,
+ @JacksonXmlText String stdout,
+ @JsonInclude(JsonInclude.Include.NON_NULL) String stderr)
+{ }
diff --git a/jpos/src/main/java/org/jpos/log/evt/Start.java b/jpos/src/main/java/org/jpos/log/evt/Start.java
index 4dde77e8de..30446d992d 100644
--- a/jpos/src/main/java/org/jpos/log/evt/Start.java
+++ b/jpos/src/main/java/org/jpos/log/evt/Start.java
@@ -20,7 +20,6 @@
import org.jpos.log.AuditLogEvent;
-import java.time.Instant;
import java.util.UUID;
/**
diff --git a/jpos/src/main/java/org/jpos/log/evt/SysInfo.java b/jpos/src/main/java/org/jpos/log/evt/SysInfo.java
new file mode 100644
index 0000000000..63d3c515dc
--- /dev/null
+++ b/jpos/src/main/java/org/jpos/log/evt/SysInfo.java
@@ -0,0 +1,53 @@
+package org.jpos.log.evt;
+
+import com.fasterxml.jackson.annotation.JsonProperty;
+import com.fasterxml.jackson.annotation.JsonPropertyOrder;
+import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlProperty;
+import org.jpos.log.AuditLogEvent;
+import java.nio.charset.Charset;
+import java.time.Duration;
+import java.util.List;
+import java.util.UUID;
+
+@JsonPropertyOrder({
+ "osName", "osVersion", "javaVersion", "javaVendor", "aes", "host", "userName", "cwd", "watch-service", "environment",
+ "args", "encoding", "zone-info", "processName", "freeSpace", "usableSpace", "version", "revision", "instance",
+ "uptime", "loadAverage", "processors", "drift", "maxMemory", "totalMemory", "freeMemory", "inUseMemory",
+ "gcTotalCnt", "gcTotalTime", "threadCount", "threadPeak", "nameRegistrar"
+})
+public record SysInfo (
+ @JsonProperty("os-name") String osName,
+ @JsonProperty("os-version") String osVersion,
+ @JsonProperty("java-version") String javaVersion,
+ @JsonProperty("java-vendor") String javaVendor,
+ @JsonProperty("AES") String aes,
+ String host,
+ @JsonProperty("user-name") String userName,
+ @JsonProperty("cwd") String cwd,
+ @JsonProperty("watch-service") String watchService,
+ String environment,
+ String args,
+ Charset encoding,
+ @JsonProperty("zone-info") String zoneInfo,
+ @JsonProperty("process-name") String processName,
+ @JsonProperty("free-space") String freeSpace,
+ @JsonProperty("usable-space") String usableSpace,
+ String version,
+ String revision,
+ UUID instance,
+ Duration uptime,
+ @JsonProperty("load-average") double loadAverage,
+ int processors,
+ long drift,
+ @JsonProperty("max-memory") long maxMemory,
+ @JsonProperty("total-memory") long totalMemory,
+ @JsonProperty("free-memory") long freeMemory,
+ @JsonProperty("in-use-memory") long inUseMemory,
+ @JsonProperty("gc-total-cnt") long gcTotalCnt,
+ @JsonProperty("gc-total-time") long gcTotalTime,
+ @JsonProperty("thread-count") int threadCount,
+ @JsonProperty("thread-peak") int threadPeak,
+ @JsonProperty("name-registrar") @JacksonXmlProperty(localName = "name-registrar") List nameRegistrarEntries,
+ @JsonProperty("threads") @JacksonXmlProperty(localName = "threads") List threads,
+ @JsonProperty("scripts") @JacksonXmlProperty(localName = "scripts") List scripts
+) implements AuditLogEvent { }
diff --git a/jpos/src/main/java/org/jpos/log/render/json/AuditLogEventJsonLogRenderer.java b/jpos/src/main/java/org/jpos/log/render/json/AuditLogEventJsonLogRenderer.java
index e8dafadb79..5986d4052e 100644
--- a/jpos/src/main/java/org/jpos/log/render/json/AuditLogEventJsonLogRenderer.java
+++ b/jpos/src/main/java/org/jpos/log/render/json/AuditLogEventJsonLogRenderer.java
@@ -2,6 +2,7 @@
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
+import com.fasterxml.jackson.databind.SerializationFeature;
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
import org.jpos.log.AuditLogEvent;
import org.jpos.log.LogRenderer;
diff --git a/jpos/src/main/java/org/jpos/log/render/json/LogEventJsonLogRenderer.java b/jpos/src/main/java/org/jpos/log/render/json/LogEventJsonLogRenderer.java
index 3ecbdf328f..0f5b6c71da 100644
--- a/jpos/src/main/java/org/jpos/log/render/json/LogEventJsonLogRenderer.java
+++ b/jpos/src/main/java/org/jpos/log/render/json/LogEventJsonLogRenderer.java
@@ -6,7 +6,6 @@
import com.fasterxml.jackson.databind.SerializationFeature;
import com.fasterxml.jackson.databind.module.SimpleModule;
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
-import org.jdom2.JDOMException;
import org.jpos.log.AuditLogEvent;
import org.jpos.log.LogRenderer;
diff --git a/jpos/src/main/java/org/jpos/log/render/markdown/SysInfoMarkdownRenderer.java b/jpos/src/main/java/org/jpos/log/render/markdown/SysInfoMarkdownRenderer.java
new file mode 100644
index 0000000000..31de9165cb
--- /dev/null
+++ b/jpos/src/main/java/org/jpos/log/render/markdown/SysInfoMarkdownRenderer.java
@@ -0,0 +1,114 @@
+package org.jpos.log.render.markdown;
+
+import org.jpos.log.LogRenderer;
+import org.jpos.log.evt.KV;
+import org.jpos.log.evt.ProcessOutput;
+import org.jpos.log.evt.SysInfo;
+import org.jpos.util.Profiler;
+
+import java.io.PrintStream;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.lang.reflect.RecordComponent;
+import java.util.List;
+import java.util.Map;
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
+
+import static java.lang.StringTemplate.STR;
+
+public final class SysInfoMarkdownRenderer implements LogRenderer {
+
+ @Override
+ public void render(SysInfo sysinfo, PrintStream ps, String indent) {
+ Map entries = extractEntriesToMap(sysinfo);
+ int width = maxLength(entries);
+ final String fmt = STR."| %-\{width}s | %s |%n";
+ ps.println ("## SystemMonitor");
+ ps.print (row(fmt, "id", "value"));
+ ps.print (row(fmt, "-".repeat(width), "-----"));
+ entries.forEach((k, v) -> {
+ switch (k) {
+ case "nameRegistrarEntries":
+ case "threads":
+ case "scripts":
+ break;
+ default:
+ ps.print(
+ row(fmt, k, v.toString())
+ );
+ }
+ });
+ renderNameRegistrar(sysinfo.nameRegistrarEntries(), ps, fmt, width);
+ renderThreads(sysinfo.threads(), ps, fmt, width);
+ renderScripts(sysinfo.scripts(), ps);
+ }
+ public Class> clazz() {
+ return SysInfo.class;
+ }
+ public Type type() {
+ return Type.MARKDOWN;
+ }
+
+ private void renderNameRegistrar(List entries, PrintStream ps, String fmt, int width) {
+ ps.println ("### NameRegistrar");
+ ps.print (row(fmt, "id", "component"));
+ ps.print (row(fmt, "-".repeat(width), "---------"));
+ entries.forEach(kv -> {
+ ps.print(
+ row(fmt, kv.key(), kv.value()));
+ });
+ }
+
+ private void renderThreads(List entries, PrintStream ps, String fmt, int width) {
+ ps.println ("### Threads");
+ ps.print (row(fmt, "thread", "info"));
+ ps.print (row(fmt, "-".repeat(width), "----"));
+ entries.forEach(kv -> {
+ ps.print(
+ row(fmt, kv.key(), kv.value()));
+ });
+ }
+
+ private void renderScripts(List entries, PrintStream ps) {
+ entries.forEach (processOutput -> {
+ ps.printf ("### %s%n", processOutput.name());
+ if (!processOutput.stdout().isEmpty()) {
+ ps.println ("```");
+ ps.println (processOutput.stdout());
+ ps.println ("```");
+ }
+ if (processOutput.stderr() != null) {
+ ps.println ("```");
+ ps.println (processOutput.stderr());
+ ps.println ("```");
+ }
+ });
+ }
+
+
+ private String row (String fmt, String c1, String c2) {
+ return fmt.formatted(c1, c2);
+ }
+
+ private int maxLength(Map map) {
+ return map.keySet().stream()
+ .map(String::length)
+ .max(Integer::compareTo).orElse(0);
+ }
+
+ private Map extractEntriesToMap(SysInfo record) {
+ return Stream.of(record.getClass().getRecordComponents())
+ .collect(Collectors.toMap(
+ RecordComponent::getName,
+ component -> {
+ try {
+ Method accessor = component.getAccessor();
+ return accessor.invoke(record);
+ } catch (IllegalAccessException | InvocationTargetException e) {
+ return "%s:%s".formatted(e.getClass().getSimpleName(), e.getMessage());
+ }
+ }
+ ));
+ }
+}
diff --git a/jpos/src/main/java/org/jpos/q2/Q2.java b/jpos/src/main/java/org/jpos/q2/Q2.java
index a556c98daf..75385eaa76 100644
--- a/jpos/src/main/java/org/jpos/q2/Q2.java
+++ b/jpos/src/main/java/org/jpos/q2/Q2.java
@@ -673,8 +673,8 @@ public Log getLog () {
public MBeanServer getMBeanServer () {
return server;
}
- public long getUptime() {
- return Duration.between(startTime, Instant.now()).toMillis();
+ public Duration getUptime() {
+ return Duration.between(startTime, Instant.now());
}
public void displayVersion () {
System.out.println(getVersionString());
diff --git a/jpos/src/main/java/org/jpos/q2/cli/UPTIME.java b/jpos/src/main/java/org/jpos/q2/cli/UPTIME.java
index 878ebbbb64..adbfa8b43f 100644
--- a/jpos/src/main/java/org/jpos/q2/cli/UPTIME.java
+++ b/jpos/src/main/java/org/jpos/q2/cli/UPTIME.java
@@ -25,6 +25,6 @@
@SuppressWarnings("unused")
public class UPTIME implements CLICommand {
public void exec(CLIContext ctx, String[] args) throws Exception {
- ctx.println(ISOUtil.millisToString(ctx.getCLI().getQ2().getUptime()));
+ ctx.println(ISOUtil.millisToString(ctx.getCLI().getQ2().getUptime().toMillis()));
}
}
diff --git a/jpos/src/main/java/org/jpos/q2/qbean/SystemMonitor.java b/jpos/src/main/java/org/jpos/q2/qbean/SystemMonitor.java
index 2326d4d156..3d73a44297 100644
--- a/jpos/src/main/java/org/jpos/q2/qbean/SystemMonitor.java
+++ b/jpos/src/main/java/org/jpos/q2/qbean/SystemMonitor.java
@@ -23,6 +23,10 @@
import org.jpos.core.Environment;
import org.jpos.core.annotation.Config;
import org.jpos.iso.ISOUtil;
+import org.jpos.log.AuditLogEvent;
+import org.jpos.log.evt.KV;
+import org.jpos.log.evt.ProcessOutput;
+import org.jpos.log.evt.SysInfo;
import org.jpos.q2.Q2;
import org.jpos.q2.QBeanSupport;
import org.jpos.util.*;
@@ -38,16 +42,11 @@
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.*;
-import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
import java.util.stream.Collectors;
-import static io.micrometer.core.instrument.Metrics.globalRegistry;
-
/**
* Periodically dumps Thread and memory usage
*
@@ -56,10 +55,10 @@
* @see Logger
*/
public class SystemMonitor extends QBeanSupport
- implements Runnable, SystemMonitorMBean, Loggeable
+ implements Runnable, SystemMonitorMBean
{
private long sleepTime = 60 * 60 * 1000;
- private long delay = 0;
+ private long drift = 0;
private boolean detailRequired = false;
private Thread me = null;
private static final int MB = 1024*1024;
@@ -81,7 +80,6 @@ public void initService() { }
@Override
public void startService() {
try {
- log.info("Starting SystemMonitor");
me = Thread.ofVirtual().name("SystemMonitor").start(this);
} catch (Exception e) {
log.warn("error starting service", e);
@@ -89,7 +87,6 @@ public void startService() {
}
public void stopService() {
- log.info("Stopping SystemMonitor");
interruptMainThread();
}
public void destroyService() throws InterruptedException {
@@ -116,70 +113,39 @@ public synchronized boolean getDetailRequired() {
return detailRequired;
}
- private void dumpThreads(ThreadGroup g, PrintStream p, String indent) {
+ private List generateThreadInfo () {
+ List threads = new ArrayList<>();
Thread.getAllStackTraces().entrySet().stream()
.sorted(Comparator.comparingLong(e -> e.getKey().threadId()))
.forEach((e -> {
- Thread t = e.getKey();
- p.printf("%s%d: %s:%s%n", indent, t.threadId(), t.getThreadGroup().getName(), t.getName());
- if (dumpStackTrace) {
- int i = 0;
- for (var s : e.getValue()) {
- if (++i > stackTraceDepth)
- break;
- p.printf("%s %s%n", indent, s);
- }
- }
+ Thread t = e.getKey();
+ StackTraceElement[] stackTrace = e.getValue();
+ String currentMethodInfo = stackTrace.length > 0 ?
+ "%s.%s(%s:%d)".formatted(stackTrace[0].getClassName(), stackTrace[0].getMethodName(),
+ stackTrace[0].getFileName(), stackTrace[0].getLineNumber()) :
+ "";
+ threads.add(new KV(
+ "%s:%d".formatted(t.getThreadGroup(), t.threadId()),
+ "%s - %s".formatted(t.getName(), currentMethodInfo)
+ ));
}));
- }
-
- private void dumpMeters(PrintStream p, String indent) {
- AtomicBoolean isFirst = new AtomicBoolean(true);
- getServer().getMeterRegistry().getMeters()
- .stream()
- .sorted(Comparator.comparing(meter -> meter.getId().getName()))
- .forEach(meter -> {
- String prefix = indent + (isFirst.getAndSet(false) ? " meters: " : " ");
- p.printf("%s%s %s %s%n", prefix, meter.getId().getName(), meter.getId().getTags(), meter.measure());
- });
- }
-
- void showThreadGroup(ThreadGroup g, PrintStream p, String indent) {
- if (g.getParent() != null)
- showThreadGroup(g.getParent(), p, indent + " ");
- else
- dumpThreads(g, p, indent + " ");
+ return threads;
}
public void run() {
localHost = getLocalHost();
processName = ManagementFactory.getRuntimeMXBean().getName();
while (running()) {
- dumping.lock();
- try {
- frozenDump = generateFrozenDump ("");
- log.info(this);
- frozenDump = null;
- } finally {
- dumping.unlock();
- }
+ dumpSystemInfo();
try {
long expected = System.currentTimeMillis() + sleepTime;
Thread.sleep(sleepTime);
- delay = System.currentTimeMillis() - expected;
- } catch (InterruptedException ignored) {
- }
+ drift = System.currentTimeMillis() - expected;
+ } catch (InterruptedException ignored) { }
}
- frozenDump = generateFrozenDump ("");
- log.info(this);
- frozenDump = null;
- }
- public void dump (PrintStream p, String indent) {
- if (frozenDump == null)
- frozenDump = generateFrozenDump(indent);
- p.print(frozenDump);
- dumpMetrics();
+ dumpSystemInfo();
}
+
@Override
public void setConfiguration(Configuration cfg) throws ConfigurationException {
super.setConfiguration(cfg);
@@ -192,7 +158,7 @@ private Runtime getRuntimeInstance() {
}
private long getServerUptimeAsMillisecond() {
- return getServer().getUptime();
+ return getServer().getUptime().toMillis();
}
private String getInstanceIdAsString() {
@@ -209,75 +175,6 @@ private String getLocalHost () {
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"));
- int maxKeyLength = 0;
- try {
- maxKeyLength = Cipher.getMaxAllowedKeyLength("AES");
- } catch (NoSuchAlgorithmException ignored) { }
- p.printf("%s Java: %s (%s) AES-%s%n", indent,
- System.getProperty("java.version"),
- System.getProperty("java.vendor"),
- maxKeyLength == Integer.MAX_VALUE ? "secure" : Integer.toString(maxKeyLength)
- );
- p.printf ("%s environment: %s%n", indent, Environment.getEnvironment().getName());
- p.printf ("%s process name: %s%n", indent, processName);
- p.printf ("%s user name: %s%n", indent, System.getProperty("user.name"));
- p.printf ("%s host: %s%n", indent, localHost);
- 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 args: %s%n", indent, String.join(",", ManagementFactory.getRuntimeMXBean().getInputArguments()));
- dumpGCStats(p, indent);
- 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());
- p.printf("%s metrics dir: %s%n", indent, metricsDir);
- List 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);
- p.printf("%s thread count: %d%n", indent, mxBean.getThreadCount());
- p.printf("%s peak threads: %d%n", indent, mxBean.getPeakThreadCount());
- dumpMeters(p, indent);
-
- 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 {
ProcessBuilder pb = new ProcessBuilder (QExec.parseCommandLine(script));
@@ -318,16 +215,6 @@ private void dumpMetrics() {
}
}
- private void dumpGCStats(PrintStream p, String indent) {
- long totalCount = 0;
- long totalTime = 0;
- for(GarbageCollectorMXBean gc : ManagementFactory.getGarbageCollectorMXBeans()) {
- totalCount += Math.max(gc.getCollectionCount(), 0L);
- totalTime += Math.max(gc.getCollectionTime(), 0L);
- }
- p.printf ("%s GC Stats: %d/%d%n", indent, totalCount, totalTime);
- }
-
private void interruptMainThread() {
if (me != null) {
dumping.lock();
@@ -338,4 +225,128 @@ private void interruptMainThread() {
}
}
}
+
+ private LogEvent generateSystemInfo () {
+ LogEvent evt = new LogEvent().withTraceId(getServer().getInstanceId());
+ List events = new ArrayList<>();
+ RuntimeMXBean runtimeMXBean = ManagementFactory.getRuntimeMXBean();
+ ThreadMXBean mxBean = ManagementFactory.getThreadMXBean();
+ 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());
+ int maxKeyLength = 0;
+ try {
+ maxKeyLength = Cipher.getMaxAllowedKeyLength("AES");
+ } catch (NoSuchAlgorithmException ignored) { }
+
+ long totalMemory = r.totalMemory();
+ long freeMemory = r.freeMemory();
+ long usedMemory = totalMemory - freeMemory;
+ long maxMemory = r.maxMemory();
+ long gcTotalCnt = 0;
+ long gcTotalTime = 0;
+ for(GarbageCollectorMXBean gc : ManagementFactory.getGarbageCollectorMXBeans()) {
+ gcTotalCnt += Math.max(gc.getCollectionCount(), 0L);
+ gcTotalTime += Math.max(gc.getCollectionTime(), 0L);
+ }
+ evt.addMessage (new SysInfo(
+ System.getProperty("os.name"),
+ System.getProperty("os.version"),
+ System.getProperty("java.version"),
+ System.getProperty("java.vendor"),
+ maxKeyLength == Integer.MAX_VALUE ? "secure" : Integer.toString(maxKeyLength),
+ localHost,
+ System.getProperty("user.name"),
+ System.getProperty("user.dir"),
+ getServer().getWatchServiceClassname(),
+ Environment.getEnvironment().getName(),
+ String.join(",", runtimeMXBean.getInputArguments()),
+ Charset.defaultCharset(),
+ String.format("%s (%s) %s %s%s",
+ zi, zi.getDisplayName(TextStyle.FULL, Locale.getDefault()),
+ zi.getRules().getOffset(instant),
+ zi.getRules().getTransitionRules().toString(),
+ Optional.ofNullable(zi.getRules().nextTransition(instant))
+ .map(transition -> " " + transition)
+ .orElse("")
+ ),
+ processName,
+ freeSpace,
+ usableSpace,
+ Q2.getVersion(),
+ Q2.getRevision(),
+ getServer().getInstanceId(),
+ getServer().getUptime(),
+ loadAverage(),
+ r.availableProcessors(),
+ drift,
+ maxMemory/MB,
+ totalMemory/MB,
+ freeMemory/MB,
+ usedMemory/MB,
+ gcTotalCnt,
+ gcTotalTime,
+ mxBean.getThreadCount(),
+ mxBean.getPeakThreadCount(),
+ NameRegistrar.getAsMap()
+ .entrySet()
+ .stream()
+ .map(entry -> new KV(entry.getKey(), entry.getValue().toString()))
+ .collect(Collectors.toList()),
+ generateThreadInfo(),
+ runScripts()
+ ));
+ return evt;
+
+ }
+
+ private List runScripts() {
+ List l = new ArrayList<>();
+ for (String s : scripts) {
+ l.add(exec(s));
+ }
+ return l;
+ }
+
+ private ProcessOutput exec(String script) {
+ StringBuilder stdout = new StringBuilder();
+ StringBuilder stderr = new StringBuilder();
+ try {
+ ProcessBuilder pb = new ProcessBuilder(QExec.parseCommandLine(script));
+ Process p = pb.start();
+ // Capture standard output
+ try (BufferedReader reader = new BufferedReader(new InputStreamReader(p.getInputStream()))) {
+ stdout.append(reader.lines().collect(Collectors.joining(System.lineSeparator())));
+ }
+ // Capture error output
+ try (BufferedReader reader = new BufferedReader(new InputStreamReader(p.getErrorStream()))) {
+ String errorOutput = reader.lines().collect(Collectors.joining(System.lineSeparator()));
+ if (!errorOutput.isEmpty()) {
+ stderr.append(reader.lines().collect(Collectors.joining(System.lineSeparator())));
+ }
+ }
+ p.waitFor();
+ } catch (Exception e) {
+ stderr.append("Exception: ").append(e.getMessage());
+ e.printStackTrace(new PrintStream(new OutputStream() {
+ @Override
+ public void write(int b) {
+ stdout.append((char) b);
+ }
+ }));
+ }
+ return new ProcessOutput (script, stdout.toString(), stderr.isEmpty() ? null : stderr.toString());
+ }
+
+ private void dumpSystemInfo () {
+ dumping.lock();
+ try {
+ Logger.log(generateSystemInfo());
+ } finally {
+ dumping.unlock();
+ }
+ }
}
diff --git a/jpos/src/main/resources/META-INF/services/org.jpos.log.LogRenderer b/jpos/src/main/resources/META-INF/services/org.jpos.log.LogRenderer
index f39b80c715..13bccb16f0 100644
--- a/jpos/src/main/resources/META-INF/services/org.jpos.log.LogRenderer
+++ b/jpos/src/main/resources/META-INF/services/org.jpos.log.LogRenderer
@@ -6,6 +6,7 @@ org.jpos.log.render.xml.LoggeableXmlLogRenderer
org.jpos.log.render.xml.LogEventXmlLogRenderer
org.jpos.log.render.markdown.ProfilerMarkdownRenderer
+org.jpos.log.render.markdown.SysInfoMarkdownRenderer
org.jpos.log.render.markdown.ContextMarkdownRenderer
org.jpos.log.render.markdown.LoggeableMarkdownLogRenderer
org.jpos.log.render.markdown.LogEventMarkdownRenderer