diff options
author | Luck <git@lucko.me> | 2019-04-17 00:58:28 +0100 |
---|---|---|
committer | Luck <git@lucko.me> | 2019-04-17 00:58:28 +0100 |
commit | 394c59d375811e4b1e0f23a528ef85d8c4d0e5a0 (patch) | |
tree | 2e9486f564a2a518fc938dfc0d5b58ae067789f1 /spark-common/src/main/java/me/lucko/spark/common/command | |
parent | ecd4cec8545460a4fc4ca65b911c2503a00cd8e7 (diff) | |
download | spark-394c59d375811e4b1e0f23a528ef85d8c4d0e5a0.tar.gz spark-394c59d375811e4b1e0f23a528ef85d8c4d0e5a0.tar.bz2 spark-394c59d375811e4b1e0f23a528ef85d8c4d0e5a0.zip |
Add /spark health command to report tps / memory usage stats
Diffstat (limited to 'spark-common/src/main/java/me/lucko/spark/common/command')
5 files changed, 190 insertions, 59 deletions
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 a5a7391..0df2950 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 @@ -28,7 +28,7 @@ import java.util.function.Consumer; public class CommandResponseHandler<S> { /** The prefix used in all messages */ - private static final String PREFIX = "&8[&fspark&8] &7"; + private static final String PREFIX = "&8[&e&l⚡&8] &7"; private final SparkPlatform<S> platform; private final S sender; 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 new file mode 100644 index 0000000..8e4178a --- /dev/null +++ b/spark-common/src/main/java/me/lucko/spark/common/command/modules/HealthModule.java @@ -0,0 +1,182 @@ +/* + * This file is part of spark. + * + * Copyright (c) lucko (Luck) <luck@lucko.me> + * Copyright (c) contributors + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +package me.lucko.spark.common.command.modules; + +import com.google.common.base.Strings; +import me.lucko.spark.common.command.Command; +import me.lucko.spark.common.command.CommandModule; +import me.lucko.spark.common.command.tabcomplete.TabCompleter; +import me.lucko.spark.common.monitor.tick.TpsCalculator; + +import java.lang.management.ManagementFactory; +import java.lang.management.MemoryMXBean; +import java.lang.management.MemoryPoolMXBean; +import java.lang.management.MemoryType; +import java.lang.management.MemoryUsage; +import java.util.ArrayList; +import java.util.List; +import java.util.function.Consumer; + +public class HealthModule<S> implements CommandModule<S> { + + @Override + public void registerCommands(Consumer<Command<S>> consumer) { + consumer.accept(Command.<S>builder() + .aliases("tps") + .executor((platform, sender, resp, arguments) -> { + TpsCalculator tpsCalculator = platform.getTpsCalculator(); + if (tpsCalculator != null) { + resp.replyPrefixed("TPS from last 5s, 10s, 1m, 5m, 15m:"); + resp.replyPrefixed(" " + tpsCalculator.toFormattedString()); + } else { + resp.replyPrefixed("Not supported!"); + } + }) + .tabCompleter(Command.TabCompleter.empty()) + .build() + ); + + consumer.accept(Command.<S>builder() + .aliases("healthreport", "health") + .argumentUsage("memory", null) + .executor((platform, sender, resp, arguments) -> { + resp.replyPrefixed("&7Generating server health report..."); + platform.getPlugin().runAsync(() -> { + List<String> report = new ArrayList<>(15); + report.add(""); + + TpsCalculator tpsCalculator = platform.getTpsCalculator(); + if (tpsCalculator != null) { + report.add("&8&l>&6 TPS from last 5s, 10s, 1m, 5m, 15m:"); + report.add(" " + tpsCalculator.toFormattedString()); + report.add(""); + } + + MemoryMXBean memoryMXBean = ManagementFactory.getMemoryMXBean(); + + MemoryUsage heapUsage = memoryMXBean.getHeapMemoryUsage(); + report.add("&8&l>&6 Memory usage: "); + report.add(" &f" + formatBytes(heapUsage.getUsed()) + " &7/ &f" + formatBytes(heapUsage.getMax()) + + " &7(&a" + percent(heapUsage.getUsed(), heapUsage.getMax()) + "&7)"); + report.add(" " + generateMemoryUsageDiagram(heapUsage, 40)); + report.add(""); + + if (arguments.boolFlag("memory")) { + MemoryUsage nonHeapUsage = memoryMXBean.getNonHeapMemoryUsage(); + report.add("&8&l>&6 Non-heap memory usage: "); + report.add(" &f" + formatBytes(nonHeapUsage.getUsed())); + report.add(""); + + 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("&8&l>&6 " + memoryPool.getName() + " pool usage: "); + report.add(" &f" + formatBytes(usage.getUsed()) + " &7/ &f" + formatBytes(usage.getMax()) + + " &7(&a" + percent(usage.getUsed(), usage.getMax()) + "&7)"); + report.add(" " + generateMemoryPoolDiagram(usage, collectionUsage,40)); + + + if (collectionUsage != null) { + report.add(" &c- &7Usage at last GC: &f" + formatBytes(collectionUsage.getUsed())); + } + report.add(""); + } + } + + report.forEach(resp::reply); + }); + }) + .tabCompleter((platform, sender, arguments) -> TabCompleter.completeForOpts(arguments, "--memory")) + .build() + ); + } + + private static String percent(double value, double max) { + double percent = (value * 100d) / max; + return (int) percent + "%"; + } + + private static String generateMemoryUsageDiagram(MemoryUsage usage, int length) { + double used = usage.getUsed(); + double committed = usage.getCommitted(); + double max = usage.getMax(); + + int usedChars = (int) ((used * length) / max); + int committedChars = (int) ((committed * length) / max); + + String line = "&7" + Strings.repeat("/", usedChars); + if (committedChars > usedChars) { + line += Strings.repeat(" ", (committedChars - usedChars) - 1) + "&e|"; + } + if (length > committedChars) { + line += Strings.repeat(" ", (length - committedChars)); + } + + return "&8[" + line + "&8]"; + } + + private static String generateMemoryPoolDiagram(MemoryUsage usage, MemoryUsage collectionUsage, int length) { + double used = usage.getUsed(); + double collectionUsed = used; + if (collectionUsage != null) { + collectionUsed = collectionUsage.getUsed(); + } + double committed = usage.getCommitted(); + double max = usage.getMax(); + + int usedChars = (int) ((used * length) / max); + int collectionUsedChars = (int) ((collectionUsed * length) / max); + int committedChars = (int) ((committed * length) / max); + + String line = "&7" + Strings.repeat("/", collectionUsedChars); + if (usedChars > collectionUsedChars) { + line += "&c|&7" + Strings.repeat("/", (usedChars - collectionUsedChars) - 1); + } + if (committedChars > usedChars) { + line += Strings.repeat(" ", (committedChars - usedChars) - 1) + "&e|"; + } + if (length > committedChars) { + line += Strings.repeat(" ", (length - committedChars)); + } + + return "&8[" + line + "&8]"; + } + + private static String formatBytes(long bytes) { + if (bytes == 0) { + return "0 bytes"; + } + String[] sizes = new String[]{"bytes", "KB", "MB", "GB", "TB", "PB", "EB", "ZB", "YB"}; + int sizeIndex = (int) (Math.log(bytes) / Math.log(1024)); + return String.format("%.1f", bytes / Math.pow(1024, sizeIndex)) + " " + sizes[sizeIndex]; + } + +} diff --git a/spark-common/src/main/java/me/lucko/spark/common/command/modules/MemoryModule.java b/spark-common/src/main/java/me/lucko/spark/common/command/modules/MemoryModule.java index 2cb2e07..583b1b6 100644 --- a/spark-common/src/main/java/me/lucko/spark/common/command/modules/MemoryModule.java +++ b/spark-common/src/main/java/me/lucko/spark/common/command/modules/MemoryModule.java @@ -24,8 +24,8 @@ import me.lucko.spark.common.SparkPlatform; import me.lucko.spark.common.command.Command; import me.lucko.spark.common.command.CommandModule; import me.lucko.spark.common.command.tabcomplete.TabCompleter; -import me.lucko.spark.common.memory.HeapDump; -import me.lucko.spark.common.memory.HeapDumpSummary; +import me.lucko.spark.common.heapdump.HeapDump; +import me.lucko.spark.common.heapdump.HeapDumpSummary; import okhttp3.MediaType; import java.io.IOException; @@ -64,7 +64,7 @@ public class MemoryModule<S> implements CommandModule<S> { byte[] output = heapDump.formCompressedDataPayload(); try { String key = SparkPlatform.BYTEBIN_CLIENT.postContent(output, JSON_TYPE, false).key(); - resp.broadcastPrefixed("&bHeap dump summmary output:"); + resp.broadcastPrefixed("&6Heap dump summmary output:"); resp.broadcastLink(SparkPlatform.VIEWER_URL + key); } catch (IOException e) { resp.broadcastPrefixed("&cAn error occurred whilst uploading the data."); @@ -108,7 +108,7 @@ public class MemoryModule<S> implements CommandModule<S> { return; } - resp.broadcastPrefixed("&bHeap dump written to: " + file.toString()); + resp.broadcastPrefixed("&6Heap dump written to: " + file.toString()); }); }) .tabCompleter((platform, sender, arguments) -> TabCompleter.completeForOpts(arguments, "--run-gc-before", "--include-non-live")) diff --git a/spark-common/src/main/java/me/lucko/spark/common/command/modules/MonitoringModule.java b/spark-common/src/main/java/me/lucko/spark/common/command/modules/MonitoringModule.java deleted file mode 100644 index b543e1d..0000000 --- a/spark-common/src/main/java/me/lucko/spark/common/command/modules/MonitoringModule.java +++ /dev/null @@ -1,51 +0,0 @@ -/* - * This file is part of spark. - * - * Copyright (c) lucko (Luck) <luck@lucko.me> - * Copyright (c) contributors - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU 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 General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - */ - -package me.lucko.spark.common.command.modules; - -import me.lucko.spark.common.command.Command; -import me.lucko.spark.common.command.CommandModule; -import me.lucko.spark.common.monitor.tick.TpsCalculator; - -import java.util.function.Consumer; - -public class MonitoringModule<S> implements CommandModule<S> { - - @Override - public void registerCommands(Consumer<Command<S>> consumer) { - consumer.accept(Command.<S>builder() - .aliases("tps") - .executor((platform, sender, resp, arguments) -> { - TpsCalculator tpsCalculator = platform.getTpsCalculator(); - if (tpsCalculator == null) { - resp.replyPrefixed("TPS data is not available."); - return; - } - - String formattedTpsString = tpsCalculator.toFormattedString(); - resp.replyPrefixed("TPS from last 5s, 10s, 1m, 5m, 15m"); - resp.replyPrefixed(formattedTpsString); - }) - .tabCompleter(Command.TabCompleter.empty()) - .build() - ); - } - -} diff --git a/spark-common/src/main/java/me/lucko/spark/common/command/modules/SamplerModule.java b/spark-common/src/main/java/me/lucko/spark/common/command/modules/SamplerModule.java index a0f171c..99f8007 100644 --- a/spark-common/src/main/java/me/lucko/spark/common/command/modules/SamplerModule.java +++ b/spark-common/src/main/java/me/lucko/spark/common/command/modules/SamplerModule.java @@ -137,7 +137,7 @@ public class SamplerModule<S> implements CommandModule<S> { } sampler = this.activeSampler = builder.start(); - resp.broadcastPrefixed("&bProfiler now active!"); + resp.broadcastPrefixed("&6Profiler now active!"); if (timeoutSeconds == -1) { resp.broadcastPrefixed("&7Use '/" + platform.getPlugin().getLabel() + " stop' to stop profiling and upload the results."); } else { @@ -234,7 +234,7 @@ public class SamplerModule<S> implements CommandModule<S> { } else { this.activeSampler.cancel(); this.activeSampler = null; - resp.broadcastPrefixed("&bThe active sampling task has been cancelled."); + resp.broadcastPrefixed("&6The active sampling task has been cancelled."); } } }) @@ -247,7 +247,7 @@ public class SamplerModule<S> implements CommandModule<S> { byte[] output = sampler.formCompressedDataPayload(); try { String key = SparkPlatform.BYTEBIN_CLIENT.postContent(output, JSON_TYPE, false).key(); - resp.broadcastPrefixed("&bSampling results:"); + resp.broadcastPrefixed("&6Sampling results:"); resp.broadcastLink(SparkPlatform.VIEWER_URL + key); } catch (IOException e) { resp.broadcastPrefixed("&cAn error occurred whilst uploading the results."); |