aboutsummaryrefslogtreecommitdiff
path: root/spark-common/src/main/java/me/lucko/spark/common/command
diff options
context:
space:
mode:
authorLuck <git@lucko.me>2021-03-23 00:52:44 +0000
committerLuck <git@lucko.me>2021-03-23 00:52:44 +0000
commit0fd7d30f2a9027c2bd9df3215759c6d91d110acc (patch)
treebdf02fa8b7c6dfc7dc6db5eaaabeb1e17189d420 /spark-common/src/main/java/me/lucko/spark/common/command
parent9766754d28fcbca1ccbeefc11ef7a88a4e3d7946 (diff)
downloadspark-0fd7d30f2a9027c2bd9df3215759c6d91d110acc.tar.gz
spark-0fd7d30f2a9027c2bd9df3215759c6d91d110acc.tar.bz2
spark-0fd7d30f2a9027c2bd9df3215759c6d91d110acc.zip
Refactor and tidy up, more consistent code style
Diffstat (limited to 'spark-common/src/main/java/me/lucko/spark/common/command')
-rw-r--r--spark-common/src/main/java/me/lucko/spark/common/command/Arguments.java14
-rw-r--r--spark-common/src/main/java/me/lucko/spark/common/command/Command.java1
-rw-r--r--spark-common/src/main/java/me/lucko/spark/common/command/CommandResponseHandler.java10
-rw-r--r--spark-common/src/main/java/me/lucko/spark/common/command/modules/ActivityLogModule.java16
-rw-r--r--spark-common/src/main/java/me/lucko/spark/common/command/modules/GcMonitoringModule.java36
-rw-r--r--spark-common/src/main/java/me/lucko/spark/common/command/modules/HealthModule.java510
-rw-r--r--spark-common/src/main/java/me/lucko/spark/common/command/modules/HeapAnalysisModule.java292
-rw-r--r--spark-common/src/main/java/me/lucko/spark/common/command/modules/SamplerModule.java365
-rw-r--r--spark-common/src/main/java/me/lucko/spark/common/command/modules/TickMonitoringModule.java21
-rw-r--r--spark-common/src/main/java/me/lucko/spark/common/command/sender/CommandSender.java2
10 files changed, 673 insertions, 594 deletions
diff --git a/spark-common/src/main/java/me/lucko/spark/common/command/Arguments.java b/spark-common/src/main/java/me/lucko/spark/common/command/Arguments.java
index 3cd0365..17c49e2 100644
--- a/spark-common/src/main/java/me/lucko/spark/common/command/Arguments.java
+++ b/spark-common/src/main/java/me/lucko/spark/common/command/Arguments.java
@@ -30,6 +30,9 @@ import java.util.Set;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
+/**
+ * Utility for parsing command-flag like arguments from raw space split strings.
+ */
public class Arguments {
private static final Pattern FLAG_REGEX = Pattern.compile("^--(.+)$");
@@ -110,19 +113,8 @@ public class Arguments {
}
public static final class ParseException extends IllegalArgumentException {
- public ParseException() {
- }
-
public ParseException(String s) {
super(s);
}
-
- public ParseException(String message, Throwable cause) {
- super(message, cause);
- }
-
- public ParseException(Throwable cause) {
- super(cause);
- }
}
}
diff --git a/spark-common/src/main/java/me/lucko/spark/common/command/Command.java b/spark-common/src/main/java/me/lucko/spark/common/command/Command.java
index e1a5146..dad15e6 100644
--- a/spark-common/src/main/java/me/lucko/spark/common/command/Command.java
+++ b/spark-common/src/main/java/me/lucko/spark/common/command/Command.java
@@ -21,6 +21,7 @@
package me.lucko.spark.common.command;
import com.google.common.collect.ImmutableList;
+
import me.lucko.spark.common.SparkPlatform;
import me.lucko.spark.common.command.sender.CommandSender;
diff --git a/spark-common/src/main/java/me/lucko/spark/common/command/CommandResponseHandler.java b/spark-common/src/main/java/me/lucko/spark/common/command/CommandResponseHandler.java
index 874939e..1acb3dc 100644
--- a/spark-common/src/main/java/me/lucko/spark/common/command/CommandResponseHandler.java
+++ b/spark-common/src/main/java/me/lucko/spark/common/command/CommandResponseHandler.java
@@ -22,6 +22,7 @@ package me.lucko.spark.common.command;
import me.lucko.spark.common.SparkPlatform;
import me.lucko.spark.common.command.sender.CommandSender;
+
import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.TextComponent;
@@ -29,9 +30,11 @@ import java.util.Set;
import java.util.function.Consumer;
import java.util.stream.Collectors;
-import static net.kyori.adventure.text.Component.*;
-import static net.kyori.adventure.text.format.NamedTextColor.*;
-import static net.kyori.adventure.text.format.TextDecoration.*;
+import static net.kyori.adventure.text.Component.text;
+import static net.kyori.adventure.text.format.NamedTextColor.DARK_GRAY;
+import static net.kyori.adventure.text.format.NamedTextColor.GRAY;
+import static net.kyori.adventure.text.format.NamedTextColor.YELLOW;
+import static net.kyori.adventure.text.format.TextDecoration.BOLD;
public class CommandResponseHandler {
@@ -94,5 +97,4 @@ public class CommandResponseHandler {
return PREFIX.append(message);
}
-
}
diff --git a/spark-common/src/main/java/me/lucko/spark/common/command/modules/ActivityLogModule.java b/spark-common/src/main/java/me/lucko/spark/common/command/modules/ActivityLogModule.java
index ae4613a..2bdb5d6 100644
--- a/spark-common/src/main/java/me/lucko/spark/common/command/modules/ActivityLogModule.java
+++ b/spark-common/src/main/java/me/lucko/spark/common/command/modules/ActivityLogModule.java
@@ -20,10 +20,11 @@
package me.lucko.spark.common.command.modules;
-import me.lucko.spark.common.activitylog.ActivityLog.Activity;
+import me.lucko.spark.common.activitylog.Activity;
import me.lucko.spark.common.command.Command;
import me.lucko.spark.common.command.CommandModule;
import me.lucko.spark.common.command.tabcomplete.TabCompleter;
+
import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.TextComponent;
import net.kyori.adventure.text.event.ClickEvent;
@@ -36,10 +37,15 @@ import java.util.Collection;
import java.util.List;
import java.util.function.Consumer;
-import static me.lucko.spark.common.command.CommandResponseHandler.*;
-import static net.kyori.adventure.text.Component.*;
-import static net.kyori.adventure.text.format.NamedTextColor.*;
-import static net.kyori.adventure.text.format.TextDecoration.*;
+import static me.lucko.spark.common.command.CommandResponseHandler.applyPrefix;
+import static net.kyori.adventure.text.Component.space;
+import static net.kyori.adventure.text.Component.text;
+import static net.kyori.adventure.text.format.NamedTextColor.DARK_GRAY;
+import static net.kyori.adventure.text.format.NamedTextColor.GOLD;
+import static net.kyori.adventure.text.format.NamedTextColor.GRAY;
+import static net.kyori.adventure.text.format.NamedTextColor.WHITE;
+import static net.kyori.adventure.text.format.NamedTextColor.YELLOW;
+import static net.kyori.adventure.text.format.TextDecoration.BOLD;
public class ActivityLogModule implements CommandModule, RowRenderer<Activity> {
diff --git a/spark-common/src/main/java/me/lucko/spark/common/command/modules/GcMonitoringModule.java b/spark-common/src/main/java/me/lucko/spark/common/command/modules/GcMonitoringModule.java
index d66a181..8e2d199 100644
--- a/spark-common/src/main/java/me/lucko/spark/common/command/modules/GcMonitoringModule.java
+++ b/spark-common/src/main/java/me/lucko/spark/common/command/modules/GcMonitoringModule.java
@@ -21,6 +21,7 @@
package me.lucko.spark.common.command.modules;
import com.sun.management.GarbageCollectionNotificationInfo;
+
import me.lucko.spark.common.SparkPlatform;
import me.lucko.spark.common.command.Command;
import me.lucko.spark.common.command.CommandModule;
@@ -28,6 +29,7 @@ import me.lucko.spark.common.command.CommandResponseHandler;
import me.lucko.spark.common.monitor.memory.GarbageCollectionMonitor;
import me.lucko.spark.common.monitor.memory.GarbageCollectorStatistics;
import me.lucko.spark.common.util.FormatUtil;
+
import net.kyori.adventure.text.Component;
import java.lang.management.MemoryUsage;
@@ -37,12 +39,18 @@ import java.util.List;
import java.util.Map;
import java.util.function.Consumer;
-import static net.kyori.adventure.text.Component.*;
-import static net.kyori.adventure.text.format.NamedTextColor.*;
-import static net.kyori.adventure.text.format.TextDecoration.*;
+import static net.kyori.adventure.text.Component.empty;
+import static net.kyori.adventure.text.Component.space;
+import static net.kyori.adventure.text.Component.text;
+import static net.kyori.adventure.text.format.NamedTextColor.DARK_GRAY;
+import static net.kyori.adventure.text.format.NamedTextColor.GOLD;
+import static net.kyori.adventure.text.format.NamedTextColor.GRAY;
+import static net.kyori.adventure.text.format.NamedTextColor.RED;
+import static net.kyori.adventure.text.format.NamedTextColor.WHITE;
+import static net.kyori.adventure.text.format.TextDecoration.BOLD;
public class GcMonitoringModule implements CommandModule {
- private static final DecimalFormat df = new DecimalFormat("#.##");
+ private static final DecimalFormat DF = new DecimalFormat("#.##");
/** The gc monitoring instance currently running, if any */
private ReportingGcMonitor activeGcMonitor = null;
@@ -106,7 +114,7 @@ public class GcMonitoringModule implements CommandModule {
);
report.add(text()
.content(" ")
- .append(text(df.format(averageCollectionTime), GOLD))
+ .append(text(DF.format(averageCollectionTime), GOLD))
.append(text(" ms avg", GRAY))
.append(text(", ", DARK_GRAY))
.append(text(collectionCount, WHITE))
@@ -147,11 +155,10 @@ public class GcMonitoringModule implements CommandModule {
private static String formatTime(long millis) {
if (millis <= 0) {
- return "0ms";
+ return "0s";
}
long second = millis / 1000;
- //millis = millis % 1000;
long minute = second / 60;
second = second % 60;
@@ -162,9 +169,6 @@ public class GcMonitoringModule implements CommandModule {
if (second != 0) {
sb.append(second).append("s ");
}
- //if (millis != 0) {
- // sb.append(millis).append("ms");
- //}
return sb.toString().trim();
}
@@ -185,15 +189,7 @@ public class GcMonitoringModule implements CommandModule {
@Override
public void onGc(GarbageCollectionNotificationInfo data) {
- String gcType;
- if (data.getGcAction().equals("end of minor GC")) {
- gcType = "Young Gen";
- } else if (data.getGcAction().equals("end of major GC")) {
- gcType = "Old Gen";
- } else {
- gcType = data.getGcAction();
- }
-
+ String gcType = GarbageCollectionMonitor.getGcType(data);
String gcCause = data.getGcCause() != null ? " (cause = " + data.getGcCause() + ")" : "";
Map<String, MemoryUsage> beforeUsages = data.getGcInfo().getMemoryUsageBeforeGc();
@@ -207,7 +203,7 @@ public class GcMonitoringModule implements CommandModule {
.append(text(gcType + " "))
.append(text("GC", RED))
.append(text(" lasting "))
- .append(text(df.format(data.getGcInfo().getDuration()), GOLD))
+ .append(text(DF.format(data.getGcInfo().getDuration()), GOLD))
.append(text(" ms." + gcCause))
.build()
));
diff --git a/spark-common/src/main/java/me/lucko/spark/common/command/modules/HealthModule.java b/spark-common/src/main/java/me/lucko/spark/common/command/modules/HealthModule.java
index 409eb38..c8f25c7 100644
--- a/spark-common/src/main/java/me/lucko/spark/common/command/modules/HealthModule.java
+++ b/spark-common/src/main/java/me/lucko/spark/common/command/modules/HealthModule.java
@@ -21,13 +21,19 @@
package me.lucko.spark.common.command.modules;
import com.google.common.base.Strings;
+
+import me.lucko.spark.common.SparkPlatform;
+import me.lucko.spark.common.command.Arguments;
import me.lucko.spark.common.command.Command;
import me.lucko.spark.common.command.CommandModule;
+import me.lucko.spark.common.command.CommandResponseHandler;
+import me.lucko.spark.common.command.sender.CommandSender;
import me.lucko.spark.common.command.tabcomplete.TabCompleter;
import me.lucko.spark.common.monitor.cpu.CpuMonitor;
import me.lucko.spark.common.monitor.tick.TickStatistics;
import me.lucko.spark.common.util.FormatUtil;
import me.lucko.spark.common.util.RollingAverage;
+
import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.TextComponent;
import net.kyori.adventure.text.format.TextColor;
@@ -45,9 +51,17 @@ import java.util.LinkedList;
import java.util.List;
import java.util.function.Consumer;
-import static net.kyori.adventure.text.Component.*;
-import static net.kyori.adventure.text.format.NamedTextColor.*;
-import static net.kyori.adventure.text.format.TextDecoration.*;
+import static net.kyori.adventure.text.Component.empty;
+import static net.kyori.adventure.text.Component.space;
+import static net.kyori.adventure.text.Component.text;
+import static net.kyori.adventure.text.format.NamedTextColor.DARK_GRAY;
+import static net.kyori.adventure.text.format.NamedTextColor.GOLD;
+import static net.kyori.adventure.text.format.NamedTextColor.GRAY;
+import static net.kyori.adventure.text.format.NamedTextColor.GREEN;
+import static net.kyori.adventure.text.format.NamedTextColor.RED;
+import static net.kyori.adventure.text.format.NamedTextColor.WHITE;
+import static net.kyori.adventure.text.format.NamedTextColor.YELLOW;
+import static net.kyori.adventure.text.format.TextDecoration.BOLD;
public class HealthModule implements CommandModule {
@@ -57,51 +71,7 @@ public class HealthModule implements CommandModule {
public void registerCommands(Consumer<Command> consumer) {
consumer.accept(Command.builder()
.aliases("tps", "cpu")
- .executor((platform, sender, resp, arguments) -> {
- TickStatistics tickStatistics = platform.getTickStatistics();
- if (tickStatistics != null) {
- resp.replyPrefixed(text("TPS from last 5s, 10s, 1m, 5m, 15m:"));
- resp.replyPrefixed(text()
- .content(" ")
- .append(formatTps(tickStatistics.tps5Sec())).append(text(", "))
- .append(formatTps(tickStatistics.tps10Sec())).append(text(", "))
- .append(formatTps(tickStatistics.tps1Min())).append(text(", "))
- .append(formatTps(tickStatistics.tps5Min())).append(text(", "))
- .append(formatTps(tickStatistics.tps15Min()))
- .build()
- );
- resp.replyPrefixed(empty());
-
- if (tickStatistics.isDurationSupported()) {
- resp.replyPrefixed(text("Tick durations (min/med/95%ile/max ms) from last 10s, 1m:"));
- resp.replyPrefixed(text()
- .content(" ")
- .append(formatTickDurations(tickStatistics.duration10Sec())).append(text("; "))
- .append(formatTickDurations(tickStatistics.duration1Min()))
- .build()
- );
- resp.replyPrefixed(empty());
- }
- }
-
- resp.replyPrefixed(text("CPU usage from last 10s, 1m, 15m:"));
- resp.replyPrefixed(text()
- .content(" ")
- .append(formatCpuUsage(CpuMonitor.systemLoad10SecAvg())).append(text(", "))
- .append(formatCpuUsage(CpuMonitor.systemLoad1MinAvg())).append(text(", "))
- .append(formatCpuUsage(CpuMonitor.systemLoad15MinAvg()))
- .append(text(" (system)", DARK_GRAY))
- .build()
- );
- resp.replyPrefixed(text()
- .content(" ")
- .append(formatCpuUsage(CpuMonitor.processLoad10SecAvg())).append(text(", "))
- .append(formatCpuUsage(CpuMonitor.processLoad1MinAvg())).append(text(", "))
- .append(formatCpuUsage(CpuMonitor.processLoad15MinAvg()))
- .append(text(" (process)", DARK_GRAY))
- .build()
- );
- })
+ .executor(HealthModule::tps)
.tabCompleter(Command.TabCompleter.empty())
.build()
);
@@ -109,197 +79,265 @@ public class HealthModule implements CommandModule {
consumer.accept(Command.builder()
.aliases("healthreport", "health", "ht")
.argumentUsage("memory", null)
- .executor((platform, sender, resp, arguments) -> {
- resp.replyPrefixed(text("Generating server health report..."));
- platform.getPlugin().executeAsync(() -> {
- List<Component> report = new LinkedList<>();
- report.add(empty());
-
- TickStatistics tickStatistics = platform.getTickStatistics();
- if (tickStatistics != null) {
- report.add(text()
- .append(text(">", DARK_GRAY, BOLD))
- .append(space())
- .append(text("TPS from last 5s, 10s, 1m, 5m, 15m:", GOLD))
- .build()
- );
- report.add(text()
- .content(" ")
- .append(formatTps(tickStatistics.tps5Sec())).append(text(", "))
- .append(formatTps(tickStatistics.tps10Sec())).append(text(", "))
- .append(formatTps(tickStatistics.tps1Min())).append(text(", "))
- .append(formatTps(tickStatistics.tps5Min())).append(text(", "))
- .append(formatTps(tickStatistics.tps15Min()))
- .build()
- );
- report.add(empty());
-
- if (tickStatistics.isDurationSupported()) {
- report.add(text()
- .append(text(">", DARK_GRAY, BOLD))
- .append(space())
- .append(text("Tick durations (min/med/95%ile/max ms) from last 10s, 1m:", GOLD))
- .build()
- );
- report.add(text()
- .content(" ")
- .append(formatTickDurations(tickStatistics.duration10Sec())).append(text("; "))
- .append(formatTickDurations(tickStatistics.duration1Min()))
- .build()
- );
- report.add(empty());
- }
- }
-
- report.add(text()
- .append(text(">", DARK_GRAY, BOLD))
- .append(space())
- .append(text("CPU usage from last 10s, 1m, 15m:", GOLD))
- .build()
- );
- report.add(text()
- .content(" ")
- .append(formatCpuUsage(CpuMonitor.systemLoad10SecAvg())).append(text(", "))
- .append(formatCpuUsage(CpuMonitor.systemLoad1MinAvg())).append(text(", "))
- .append(formatCpuUsage(CpuMonitor.systemLoad15MinAvg()))
- .append(text(" (system)", DARK_GRAY))
- .build()
- );
- report.add(text()
- .content(" ")
- .append(formatCpuUsage(CpuMonitor.processLoad10SecAvg())).append(text(", "))
- .append(formatCpuUsage(CpuMonitor.processLoad1MinAvg())).append(text(", "))
- .append(formatCpuUsage(CpuMonitor.processLoad15MinAvg()))
- .append(text(" (process)", DARK_GRAY))
- .build()
- );
- report.add(empty());
-
- MemoryMXBean memoryMXBean = ManagementFactory.getMemoryMXBean();
- MemoryUsage heapUsage = memoryMXBean.getHeapMemoryUsage();
- report.add(text()
- .append(text(">", DARK_GRAY, BOLD))
- .append(space())
- .append(text("Memory usage:", GOLD))
- .build()
- );
- report.add(text()
- .content(" ")
- .append(text(FormatUtil.formatBytes(heapUsage.getUsed()), WHITE))
- .append(space())
- .append(text("/", GRAY))
- .append(space())
- .append(text(FormatUtil.formatBytes(heapUsage.getMax()), WHITE))
- .append(text(" "))
- .append(text("(", GRAY))
- .append(text(FormatUtil.percent(heapUsage.getUsed(), heapUsage.getMax()), GREEN))
- .append(text(")", GRAY))
- .build()
- );
- report.add(text().content(" ").append(generateMemoryUsageDiagram(heapUsage, 40)).build());
- report.add(empty());
-
- if (arguments.boolFlag("memory")) {
- MemoryUsage nonHeapUsage = memoryMXBean.getNonHeapMemoryUsage();
- report.add(text()
- .append(text(">", DARK_GRAY, BOLD))
- .append(space())
- .append(text("Non-heap memory usage:", GOLD))
- .build()
- );
- report.add(text()
- .content(" ")
- .append(text(FormatUtil.formatBytes(nonHeapUsage.getUsed()), WHITE))
- .build()
- );
- report.add(empty());
-
- List<MemoryPoolMXBean> memoryPoolMXBeans = ManagementFactory.getMemoryPoolMXBeans();
- for (MemoryPoolMXBean memoryPool : memoryPoolMXBeans) {
- if (memoryPool.getType() != MemoryType.HEAP) {
- continue;
- }
-
- MemoryUsage usage = memoryPool.getUsage();
- MemoryUsage collectionUsage = memoryPool.getCollectionUsage();
-
- if (usage.getMax() == -1) {
- usage = new MemoryUsage(usage.getInit(), usage.getUsed(), usage.getCommitted(), usage.getCommitted());
- }
-
- report.add(text()
- .append(text(">", DARK_GRAY, BOLD))
- .append(space())
- .append(text(memoryPool.getName() + " pool usage:", GOLD))
- .build()
- );
- report.add(text()
- .content(" ")
- .append(text(FormatUtil.formatBytes(usage.getUsed()), WHITE))
- .append(space())
- .append(text("/", GRAY))
- .append(space())
- .append(text(FormatUtil.formatBytes(usage.getMax()), WHITE))
- .append(text(" "))
- .append(text("(", GRAY))
- .append(text(FormatUtil.percent(usage.getUsed(), usage.getMax()), GREEN))
- .append(text(")", GRAY))
- .build()
- );
- report.add(text().content(" ").append(generateMemoryPoolDiagram(usage, collectionUsage, 40)).build());
-
- if (collectionUsage != null) {
- report.add(text()
- .content(" ")
- .append(text("-", RED))
- .append(space())
- .append(text("Usage at last GC:", GRAY))
- .append(space())
- .append(text(FormatUtil.formatBytes(collectionUsage.getUsed()), WHITE))
- .build()
- );
- }
- report.add(empty());
- }
- }
-
- try {
- FileStore fileStore = Files.getFileStore(Paths.get("."));
- long totalSpace = fileStore.getTotalSpace();
- long usedSpace = totalSpace - fileStore.getUsableSpace();
- report.add(text()
- .append(text(">", DARK_GRAY, BOLD))
- .append(space())
- .append(text("Disk usage:", GOLD))
- .build()
- );
- report.add(text()
- .content(" ")
- .append(text(FormatUtil.formatBytes(usedSpace), WHITE))
- .append(space())
- .append(text("/", GRAY))
- .append(space())
- .append(text(FormatUtil.formatBytes(totalSpace), WHITE))
- .append(text(" "))
- .append(text("(", GRAY))
- .append(text(FormatUtil.percent(usedSpace, totalSpace), GREEN))
- .append(text(")", GRAY))
- .build()
- );
- report.add(text().content(" ").append(generateDiskUsageDiagram(usedSpace, totalSpace, 40)).build());
- report.add(empty());
- } catch (IOException e) {
- e.printStackTrace();
- }
-
- report.forEach(resp::reply);
- });
- })
+ .executor(HealthModule::healthReport)
.tabCompleter((platform, sender, arguments) -> TabCompleter.completeForOpts(arguments, "--memory"))
.build()
);
}
+ private static void tps(SparkPlatform platform, CommandSender sender, CommandResponseHandler resp, Arguments arguments) {
+ TickStatistics tickStatistics = platform.getTickStatistics();
+ if (tickStatistics != null) {
+ resp.replyPrefixed(text("TPS from last 5s, 10s, 1m, 5m, 15m:"));
+ resp.replyPrefixed(text()
+ .content(" ")
+ .append(formatTps(tickStatistics.tps5Sec())).append(text(", "))
+ .append(formatTps(tickStatistics.tps10Sec())).append(text(", "))
+ .append(formatTps(tickStatistics.tps1Min())).append(text(", "))
+ .append(formatTps(tickStatistics.tps5Min())).append(text(", "))
+ .append(formatTps(tickStatistics.tps15Min()))
+ .build()
+ );
+ resp.replyPrefixed(empty());
+
+ if (tickStatistics.isDurationSupported()) {
+ resp.replyPrefixed(text("Tick durations (min/med/95%ile/max ms) from last 10s, 1m:"));
+ resp.replyPrefixed(text()
+ .content(" ")
+ .append(formatTickDurations(tickStatistics.duration10Sec())).append(text("; "))
+ .append(formatTickDurations(tickStatistics.duration1Min()))
+ .build()
+ );
+ resp.replyPrefixed(empty());
+ }
+ }
+
+ resp.replyPrefixed(text("CPU usage from last 10s, 1m, 15m:"));
+ resp.replyPrefixed(text()
+ .content(" ")
+ .append(formatCpuUsage(CpuMonitor.systemLoad10SecAvg())).append(text(", "))
+ .append(formatCpuUsage(CpuMonitor.systemLoad1MinAvg())).append(text(", "))
+ .append(formatCpuUsage(CpuMonitor.systemLoad15MinAvg()))
+ .append(text(" (system)", DARK_GRAY))
+ .build()
+ );
+ resp.replyPrefixed(text()
+ .content(" ")
+ .append(formatCpuUsage(CpuMonitor.processLoad10SecAvg())).append(text(", "))
+ .append(formatCpuUsage(CpuMonitor.processLoad1MinAvg())).append(text(", "))
+ .append(formatCpuUsage(CpuMonitor.processLoad15MinAvg()))
+ .append(text(" (process)", DARK_GRAY))
+ .build()
+ );
+ }
+
+ private static void healthReport(SparkPlatform platform, CommandSender sender, CommandResponseHandler resp, Arguments arguments) {
+ resp.replyPrefixed(text("Generating server health report..."));
+ platform.getPlugin().executeAsync(() -> {
+ List<Component> report = new LinkedList<>();
+ report.add(empty());
+
+ TickStatistics tickStatistics = platform.getTickStatistics();
+ if (tickStatistics != null) {
+ addTickStats(report, tickStatistics);
+ }
+
+ addCpuStats(report);
+
+ MemoryMXBean memoryMXBean = ManagementFactory.getMemoryMXBean();
+ addBasicMemoryStats(report, memoryMXBean);
+
+ if (arguments.boolFlag("memory")) {
+ addDetailedMemoryStats(report, memoryMXBean);
+ }
+
+ try {
+ addDiskStats(report);
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+
+ report.forEach(resp::reply);
+ });
+ }
+
+ private static void addTickStats(List<Component> report, TickStatistics tickStatistics) {
+ report.add(text()
+ .append(text(">", DARK_GRAY, BOLD))
+ .append(space())
+ .append(text("TPS from last 5s, 10s, 1m, 5m, 15m:", GOLD))
+ .build()
+ );
+ report.add(text()
+ .content(" ")
+ .append(formatTps(tickStatistics.tps5Sec())).append(text(", "))
+ .append(formatTps(tickStatistics.tps10Sec())).append(text(", "))
+ .append(formatTps(tickStatistics.tps1Min())).append(text(", "))
+ .append(formatTps(tickStatistics.tps5Min())).append(text(", "))
+ .append(formatTps(tickStatistics.tps15Min()))
+ .build()
+ );
+ report.add(empty());
+
+ if (tickStatistics.isDurationSupported()) {
+ report.add(text()
+ .append(text(">", DARK_GRAY, BOLD))
+ .append(space())
+ .append(text("Tick durations (min/med/95%ile/max ms) from last 10s, 1m:", GOLD))
+ .build()
+ );
+ report.add(text()
+ .content(" ")
+ .append(formatTickDurations(tickStatistics.duration10Sec())).append(text("; "))
+ .append(formatTickDurations(tickStatistics.duration1Min()))
+ .build()
+ );
+ report.add(empty());
+ }
+ }
+
+ private static void addCpuStats(List<Component> report) {
+ report.add(text()
+ .append(text(">", DARK_GRAY, BOLD))
+ .append(space())
+ .append(text("CPU usage from last 10s, 1m, 15m:", GOLD))
+ .build()
+ );
+ report.add(text()
+ .content(" ")
+ .append(formatCpuUsage(CpuMonitor.systemLoad10SecAvg())).append(text(", "))
+ .append(formatCpuUsage(CpuMonitor.systemLoad1MinAvg())).append(text(", "))
+ .append(formatCpuUsage(CpuMonitor.systemLoad15MinAvg()))
+ .append(text(" (system)", DARK_GRAY))
+ .build()
+ );
+ report.add(text()
+ .content(" ")
+ .append(formatCpuUsage(CpuMonitor.processLoad10SecAvg())).append(text(", "))
+ .append(formatCpuUsage(CpuMonitor.processLoad1MinAvg())).append(text(", "))
+ .append(formatCpuUsage(CpuMonitor.processLoad15MinAvg()))
+ .append(text(" (process)", DARK_GRAY))
+ .build()
+ );
+ report.add(empty());
+ }
+
+ private static void addBasicMemoryStats(List<Component> report, MemoryMXBean memoryMXBean) {
+ MemoryUsage heapUsage = memoryMXBean.getHeapMemoryUsage();
+ report.add(text()
+ .append(text(">", DARK_GRAY, BOLD))
+ .append(space())
+ .append(text("Memory usage:", GOLD))
+ .build()
+ );
+ report.add(text()
+ .content(" ")
+ .append(text(FormatUtil.formatBytes(heapUsage.getUsed()), WHITE))
+ .append(space())
+ .append(text("/", GRAY))
+ .append(space())
+ .append(text(FormatUtil.formatBytes(heapUsage.getMax()), WHITE))
+ .append(text(" "))
+ .append(text("(", GRAY))
+ .append(text(FormatUtil.percent(heapUsage.getUsed(), heapUsage.getMax()), GREEN))
+ .append(text(")", GRAY))
+ .build()
+ );
+ report.add(text().content(" ").append(generateMemoryUsageDiagram(heapUsage, 40)).build());
+ report.add(empty());
+ }
+
+ private static void addDetailedMemoryStats(List<Component> report, MemoryMXBean memoryMXBean) {
+ MemoryUsage nonHeapUsage = memoryMXBean.getNonHeapMemoryUsage();
+ report.add(text()
+ .append(text(">", DARK_GRAY, BOLD))
+ .append(space())
+ .append(text("Non-heap memory usage:", GOLD))
+ .build()
+ );
+ report.add(text()
+ .content(" ")
+ .append(text(FormatUtil.formatBytes(nonHeapUsage.getUsed()), WHITE))
+ .build()
+ );
+ report.add(empty());
+
+ List<MemoryPoolMXBean> memoryPoolMXBeans = ManagementFactory.getMemoryPoolMXBeans();
+ for (MemoryPoolMXBean memoryPool : memoryPoolMXBeans) {
+ if (memoryPool.getType() != MemoryType.HEAP) {
+ continue;
+ }
+
+ MemoryUsage usage = memoryPool.getUsage();
+ MemoryUsage collectionUsage = memoryPool.getCollectionUsage();
+