Skip to content

Commit 77f869d

Browse files
committed
feat: add CPU metrics
1 parent d13e185 commit 77f869d

15 files changed

Lines changed: 172 additions & 22 deletions

File tree

logs/latest.log

Whitespace-only changes.

src/client/java/org/damon233/performtrackermod/config/ClothConfigScreen.java

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -95,6 +95,14 @@ public static Screen createConfigScreen(Screen parent) {
9595
.setTooltip(Text.translatable("performtracker.config.collect_heap.tooltip"))
9696
.setSaveConsumer(ConfigAccess::setCollectHeap)
9797
.build());
98+
99+
metrics.addEntry(entryBuilder.startBooleanToggle(
100+
Text.translatable("performtracker.config.collect_cpu"),
101+
ConfigAccess.isCollectCpu())
102+
.setDefaultValue(true)
103+
.setTooltip(Text.translatable("performtracker.config.collect_cpu.tooltip"))
104+
.setSaveConsumer(ConfigAccess::setCollectCpu)
105+
.build());
98106

99107
ConfigCategory network = builder.getOrCreateCategory(Text.translatable("performtracker.config.category.network"));
100108

src/main/java/org/damon233/performtrackermod/collector/ServerMetricsCollector.java

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,10 @@
11
package org.damon233.performtrackermod.collector;
22

3+
import java.lang.management.ManagementFactory;
4+
import java.lang.management.OperatingSystemMXBean;
35
import java.util.concurrent.ConcurrentLinkedDeque;
46
import java.util.concurrent.atomic.AtomicBoolean;
7+
import java.util.concurrent.atomic.AtomicReference;
58

69
import net.fabricmc.fabric.api.event.lifecycle.v1.ServerTickEvents;
710

@@ -10,14 +13,64 @@ public class ServerMetricsCollector {
1013
private static final double NANOS_TO_SECONDS = 1_000_000_000.0;
1114
private static final long WINDOW_SECONDS = 5;
1215
private static final long WINDOW_NANOS = WINDOW_SECONDS * 1_000_000_000L;
16+
private static final double CPU_SMOOTHING_FACTOR = 0.3;
1317

1418
private final ConcurrentLinkedDeque<Long> tickTimes = new ConcurrentLinkedDeque<>();
1519
private final AtomicBoolean enabled = new AtomicBoolean(true);
20+
private final OperatingSystemMXBean osBean;
21+
private final AtomicReference<Double> smoothedCpuUsage;
1622

1723
public ServerMetricsCollector() {
24+
this.osBean = ManagementFactory.getOperatingSystemMXBean();
25+
this.smoothedCpuUsage = new AtomicReference<>(0.0);
26+
warmUpCpu();
1827
ServerTickEvents.END_SERVER_TICK.register(this::onEndServerTick);
1928
}
2029

30+
private void warmUpCpu() {
31+
for (int i = 0; i < 10; i++) {
32+
double raw = getRawCpuUsage();
33+
if (raw >= 0) {
34+
smoothedCpuUsage.set(raw);
35+
return;
36+
}
37+
try {
38+
Thread.sleep(100);
39+
} catch (InterruptedException ignored) {
40+
}
41+
}
42+
}
43+
44+
private double getRawCpuUsage() {
45+
try {
46+
if (osBean instanceof com.sun.management.OperatingSystemMXBean sunOsBean) {
47+
double load = sunOsBean.getCpuLoad();
48+
if (load >= 0) {
49+
return load * 100.0;
50+
}
51+
}
52+
long[] ticks = getSystemCpuLoadTicks();
53+
if (ticks != null && ticks.length >= 4) {
54+
long total = ticks[0] + ticks[1] + ticks[2] + ticks[3];
55+
if (total > 0) {
56+
return ((double) (total - ticks[3]) / total) * 100.0;
57+
}
58+
}
59+
} catch (Exception ignored) {
60+
}
61+
return -1;
62+
}
63+
64+
private long[] getSystemCpuLoadTicks() {
65+
try {
66+
java.lang.reflect.Method method = osBean.getClass().getMethod("getSystemCpuLoadTicks");
67+
method.setAccessible(true);
68+
return (long[]) method.invoke(osBean);
69+
} catch (Exception ignored) {
70+
return null;
71+
}
72+
}
73+
2174
private void onEndServerTick(net.minecraft.server.MinecraftServer server) {
2275
if (!enabled.get()) {
2376
return;
@@ -65,6 +118,18 @@ public double getHeapUsedMB() {
65118
public double getHeapMaxMB() {
66119
return Runtime.getRuntime().maxMemory() / (1024.0 * 1024.0);
67120
}
121+
122+
public double getCpuUsagePercent() {
123+
double rawUsage = getRawCpuUsage();
124+
if (rawUsage < 0) {
125+
return rawUsage;
126+
}
127+
128+
double current = smoothedCpuUsage.get();
129+
double smoothed = current * (1 - CPU_SMOOTHING_FACTOR) + rawUsage * CPU_SMOOTHING_FACTOR;
130+
smoothedCpuUsage.set(smoothed);
131+
return smoothed;
132+
}
68133

69134
public void reset() {
70135
tickTimes.clear();

src/main/java/org/damon233/performtrackermod/config/ConfigAccess.java

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ public class ConfigAccess {
2626
private static final boolean DEFAULT_COLLECT_TPS = true;
2727
private static final boolean DEFAULT_COLLECT_MSPT = true;
2828
private static final boolean DEFAULT_COLLECT_HEAP = true;
29+
private static final boolean DEFAULT_COLLECT_CPU = true;
2930
private static final boolean DEFAULT_BINARY_UNITS = true;
3031

3132
private static ConfigData configData;
@@ -43,6 +44,7 @@ private static class ConfigData {
4344
boolean collectTps = DEFAULT_COLLECT_TPS;
4445
boolean collectMspt = DEFAULT_COLLECT_MSPT;
4546
boolean collectHeap = DEFAULT_COLLECT_HEAP;
47+
boolean collectCpu = DEFAULT_COLLECT_CPU;
4648
boolean binaryUnits = DEFAULT_BINARY_UNITS;
4749
}
4850

@@ -157,6 +159,10 @@ public static boolean isCollectHeap() {
157159
return configData != null ? configData.collectHeap : DEFAULT_COLLECT_HEAP;
158160
}
159161

162+
public static boolean isCollectCpu() {
163+
return configData != null ? configData.collectCpu : DEFAULT_COLLECT_CPU;
164+
}
165+
160166
public static boolean isBinaryUnits() {
161167
return configData != null ? configData.binaryUnits : DEFAULT_BINARY_UNITS;
162168
}
@@ -209,7 +215,7 @@ public static String validateNetworkEndpoint(String endpoint) {
209215
return null;
210216
}
211217
String trimmed = endpoint.trim();
212-
if (!trimmed.matches("^https?://[\\w\\-]+(\\.[\\w\\-]+)*(:\\d+)?$")) {
218+
if (!trimmed.matches("^https?://[\\w\\-]+(\\.[\\w\\-]+)*(:\\d+)?(/.*)?$")) {
213219
return null;
214220
}
215221
if (!trimmed.endsWith("/")) {
@@ -250,6 +256,13 @@ public static void setCollectHeap(boolean enabled) {
250256
}
251257
}
252258

259+
public static void setCollectCpu(boolean enabled) {
260+
if (configData != null) {
261+
configData.collectCpu = enabled;
262+
save();
263+
}
264+
}
265+
253266
public static void setBinaryUnits(boolean enabled) {
254267
if (configData != null) {
255268
configData.binaryUnits = enabled;
@@ -301,6 +314,10 @@ public static boolean getDefaultCollectHeap() {
301314
return DEFAULT_COLLECT_HEAP;
302315
}
303316

317+
public static boolean getDefaultCollectCpu() {
318+
return DEFAULT_COLLECT_CPU;
319+
}
320+
304321
public static boolean getDefaultBinaryUnits() {
305322
return DEFAULT_BINARY_UNITS;
306323
}

src/main/java/org/damon233/performtrackermod/controller/TrackerController.java

Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -143,7 +143,8 @@ private int getCollectConfigHash() {
143143
return (ConfigAccess.isCollectFps() ? 1 : 0) |
144144
(ConfigAccess.isCollectTps() ? 2 : 0) |
145145
(ConfigAccess.isCollectMspt() ? 4 : 0) |
146-
(ConfigAccess.isCollectHeap() ? 8 : 0);
146+
(ConfigAccess.isCollectHeap() ? 8 : 0) |
147+
(ConfigAccess.isCollectCpu() ? 16 : 0);
147148
}
148149

149150
private String[] buildCsvHeaders() {
@@ -152,6 +153,7 @@ private String[] buildCsvHeaders() {
152153
if (ConfigAccess.isCollectTps()) sb.append("tps,");
153154
if (ConfigAccess.isCollectMspt()) sb.append("mspt,");
154155
if (ConfigAccess.isCollectHeap()) sb.append("heap_used,heap_max,");
156+
if (ConfigAccess.isCollectCpu()) sb.append("cpu,");
155157
if (sb.length() > 0) sb.setLength(sb.length() - 1);
156158
return sb.toString().split(",");
157159
}
@@ -233,7 +235,8 @@ private void outputMetrics() {
233235
ConfigAccess.isCollectFps(), metrics.fps(),
234236
ConfigAccess.isCollectTps(), metrics.tps(),
235237
ConfigAccess.isCollectMspt(), metrics.mspt(),
236-
ConfigAccess.isCollectHeap(), metrics.heapUsed(), metrics.heapMax());
238+
ConfigAccess.isCollectHeap(), metrics.heapUsed(), metrics.heapMax(),
239+
ConfigAccess.isCollectCpu(), metrics.cpuUsage());
237240
httpService.send(json);
238241
}
239242
}
@@ -264,6 +267,11 @@ private String buildChatMessage(PerformanceMetrics metrics) {
264267
.append(" / ")
265268
.append(TranslationService.colorValue(PerformanceMetrics.formatMemoryMB(metrics.heapMax())));
266269
}
270+
if (ConfigAccess.isCollectCpu()) {
271+
if (!first) sb.append(" | ");
272+
sb.append(TranslationService.colorLabel("CPU: "))
273+
.append(TranslationService.colorValue(String.format("%.1f%%", metrics.cpuUsage())));
274+
}
267275
return sb.toString();
268276
}
269277

@@ -273,6 +281,7 @@ private Object[] buildCsvRowValues(PerformanceMetrics metrics) {
273281
if (ConfigAccess.isCollectTps()) count++;
274282
if (ConfigAccess.isCollectMspt()) count++;
275283
if (ConfigAccess.isCollectHeap()) count += 2;
284+
if (ConfigAccess.isCollectCpu()) count++;
276285

277286
Object[] values = new Object[count];
278287
int i = 0;
@@ -283,6 +292,7 @@ private Object[] buildCsvRowValues(PerformanceMetrics metrics) {
283292
values[i++] = metrics.heapUsed();
284293
values[i++] = metrics.heapMax();
285294
}
295+
if (ConfigAccess.isCollectCpu()) values[i++] = metrics.cpuUsage();
286296
return values;
287297
}
288298

@@ -292,7 +302,8 @@ public PerformanceMetrics getMetrics() {
292302
double mspt = ConfigAccess.isCollectMspt() ? serverCollector.getMspt() : 0;
293303
double heapUsed = ConfigAccess.isCollectHeap() ? serverCollector.getHeapUsedMB() : 0;
294304
double heapMax = ConfigAccess.isCollectHeap() ? serverCollector.getHeapMaxMB() : 0;
295-
return new PerformanceMetrics(fps, tps, mspt, heapUsed, heapMax);
305+
double cpuUsage = ConfigAccess.isCollectCpu() ? serverCollector.getCpuUsagePercent() : -1;
306+
return new PerformanceMetrics(fps, tps, mspt, heapUsed, heapMax, cpuUsage);
296307
}
297308

298309
public boolean isRunning() {

src/main/java/org/damon233/performtrackermod/data/PerformanceMetrics.java

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,16 +7,18 @@ public record PerformanceMetrics(
77
double tps,
88
double mspt,
99
double heapUsed,
10-
double heapMax
10+
double heapMax,
11+
double cpuUsage
1112
) {
12-
public static final PerformanceMetrics EMPTY = new PerformanceMetrics(0.0, 0.0, 0.0, 0.0, 0.0);
13+
public static final PerformanceMetrics EMPTY = new PerformanceMetrics(0.0, 0.0, 0.0, 0.0, 0.0, -1.0);
1314

1415
public PerformanceMetrics {
1516
fps = Math.max(0.0, fps);
1617
tps = Math.max(0.0, tps);
1718
mspt = Math.max(0.0, mspt);
1819
heapUsed = Math.max(0.0, heapUsed);
1920
heapMax = Math.max(0.0, heapMax);
21+
if (cpuUsage < 0) cpuUsage = -1.0;
2022
}
2123

2224
public static String formatValue(double value) {

src/main/java/org/damon233/performtrackermod/network/JsonFormatter.java

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,8 @@ public static String formatMetrics(long timestamp, String sessionId,
77
boolean collectFps, double fps,
88
boolean collectTps, double tps,
99
boolean collectMspt, double mspt,
10-
boolean collectHeap, double heapUsed, double heapMax) {
10+
boolean collectHeap, double heapUsed, double heapMax,
11+
boolean collectCpu, double cpuUsage) {
1112
StringBuilder sb = new StringBuilder(128);
1213
sb.append("{\"timestamp\":").append(timestamp);
1314
sb.append(",\"sessionId\":\"").append(sessionId).append('"');
@@ -33,6 +34,11 @@ public static String formatMetrics(long timestamp, String sessionId,
3334
if (!first) sb.append(',');
3435
sb.append("\"heapUsed\":").append(heapUsed);
3536
sb.append(",\"heapMax\":").append(heapMax);
37+
first = false;
38+
}
39+
if (collectCpu) {
40+
if (!first) sb.append(',');
41+
sb.append("\"cpu\":").append(cpuUsage);
3642
}
3743
sb.append("}}");
3844

src/main/resources/assets/performtracker/lang/en_us.json

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,8 @@
3636
"performtracker.config.collect_mspt.tooltip": "Collect mean server tick time in milliseconds",
3737
"performtracker.config.collect_heap": "Collect Heap Memory",
3838
"performtracker.config.collect_heap.tooltip": "Collect JVM heap memory usage",
39+
"performtracker.config.collect_cpu": "Collect CPU Usage",
40+
"performtracker.config.collect_cpu.tooltip": "Collect system CPU usage percentage",
3941
"performtracker.config.binary_units": "Use Binary Units (MiB/GiB)",
4042
"performtracker.config.binary_units.tooltip": "Use binary (1024-based) units instead of decimal (1000-based)",
4143
"performtracker.config.info": "%s: %s (default: %s)",

src/main/resources/assets/performtracker/lang/lzh.json

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,8 @@
3636
"performtracker.config.collect_mspt.tooltip": "稽伺器每刻均耗 (毫秒)",
3737
"performtracker.config.collect_heap": "稽察堆區之存",
3838
"performtracker.config.collect_heap.tooltip": "稽 JVM 堆區之存用",
39+
"performtracker.config.collect_cpu": "稽察算牒之用",
40+
"performtracker.config.collect_cpu.tooltip": "稽伺器算牒用百分之率",
3941
"performtracker.config.binary_units": "採二冪之度 (MiB/GiB)",
4042
"performtracker.config.binary_units.tooltip": "以二冪之度 (一零二四進) 代十冪之度 (一零零零進)",
4143
"performtracker.config.info": "%s: %s (常設: %s)",

src/main/resources/assets/performtracker/lang/zh_cn.json

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,8 @@
3636
"performtracker.config.collect_mspt.tooltip": "收集服务器平均 Tick 耗时 (毫秒)",
3737
"performtracker.config.collect_heap": "收集堆内存",
3838
"performtracker.config.collect_heap.tooltip": "收集 JVM 堆内存使用情况",
39+
"performtracker.config.collect_cpu": "收集 CPU 使用率",
40+
"performtracker.config.collect_cpu.tooltip": "收集系统 CPU 使用率百分比",
3941
"performtracker.config.binary_units": "使用二进制单位 (MiB/GiB)",
4042
"performtracker.config.binary_units.tooltip": "使用二进制 (1024进制) 单位代替十进制 (1000进制) 单位",
4143
"performtracker.config.info": "%s: %s (默认: %s)",

0 commit comments

Comments
 (0)