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