Skip to content

Commit 80c0390

Browse files
committed
导出崩溃日志时附带崩溃报告文件
1 parent f9dd7a1 commit 80c0390

2 files changed

Lines changed: 130 additions & 7 deletions

File tree

HMCL/src/main/java/org/jackhuang/hmcl/game/LogExporter.java

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@ public final class LogExporter {
4343
private LogExporter() {
4444
}
4545

46-
public static CompletableFuture<Void> exportLogs(Path zipFile, DefaultGameRepository gameRepository, String versionId, String logs, String launchScript) {
46+
public static CompletableFuture<Void> exportLogs(Path zipFile, DefaultGameRepository gameRepository, String versionId, String logs, String launchScript, Path crashReportFile) {
4747
Path runDirectory = gameRepository.getRunDirectory(versionId).toPath();
4848
Path baseDirectory = gameRepository.getBaseDirectory().toPath();
4949
List<String> versions = new ArrayList<>();
@@ -65,6 +65,14 @@ public static CompletableFuture<Void> exportLogs(Path zipFile, DefaultGameReposi
6565

6666
return CompletableFuture.runAsync(() -> {
6767
try (Zipper zipper = new Zipper(zipFile)) {
68+
if (Files.exists(crashReportFile)) {
69+
try {
70+
zipper.putFile(crashReportFile, "hmcl-game-crash-report.log");
71+
} catch (IOException e) {
72+
LOG.warning("Failed to add crash report file to zip", e);
73+
}
74+
}
75+
6876
processLogs(runDirectory.resolve("liteconfig"), "*.log", "liteconfig", zipper);
6977
processLogs(runDirectory.resolve("logs"), "*.log", "logs", zipper);
7078
processLogs(runDirectory, "*.log", "runDirectory", zipper);

HMCL/src/main/java/org/jackhuang/hmcl/ui/GameCrashWindow.java

Lines changed: 121 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -42,11 +42,16 @@
4242
import org.jackhuang.hmcl.task.Task;
4343
import org.jackhuang.hmcl.ui.construct.TwoLineListItem;
4444
import org.jackhuang.hmcl.util.*;
45-
import org.jackhuang.hmcl.util.logging.Logger;
4645
import org.jackhuang.hmcl.util.io.FileUtils;
46+
import org.jackhuang.hmcl.util.io.JarUtils;
47+
import org.jackhuang.hmcl.util.logging.Logger;
4748
import org.jackhuang.hmcl.util.platform.*;
49+
import org.jackhuang.hmcl.util.platform.hardware.CentralProcessor;
50+
import org.jackhuang.hmcl.util.platform.hardware.GraphicsCard;
4851

4952
import java.io.IOException;
53+
import java.lang.management.ManagementFactory;
54+
import java.lang.management.MemoryPoolMXBean;
5055
import java.nio.file.Files;
5156
import java.nio.file.Path;
5257
import java.nio.file.Paths;
@@ -59,9 +64,9 @@
5964
import java.util.stream.Collectors;
6065

6166
import static org.jackhuang.hmcl.util.DataSizeUnit.MEGABYTES;
62-
import static org.jackhuang.hmcl.util.logging.Logger.LOG;
6367
import static org.jackhuang.hmcl.util.Pair.pair;
6468
import static org.jackhuang.hmcl.util.i18n.I18n.i18n;
69+
import static org.jackhuang.hmcl.util.logging.Logger.LOG;
6570

6671
public class GameCrashWindow extends Stage {
6772
private final Version version;
@@ -263,11 +268,121 @@ private void showLogWindow() {
263268

264269
private void exportGameCrashInfo() {
265270
Path logFile = Paths.get("minecraft-exported-crash-info-" + LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH-mm-ss")) + ".zip").toAbsolutePath();
266-
267-
CompletableFuture.supplyAsync(() ->
268-
logs.stream().map(Log::getLog).collect(Collectors.joining("\n")))
271+
Path crashReportFile = repository.getRunDirectory(version.getId()).toPath().resolve("hmcl-game-crash-report.log");
272+
273+
CompletableFuture.supplyAsync(() -> {
274+
try {
275+
StringBuilder report = new StringBuilder();
276+
report.append("=== HMCL Game Crash Report ===\n");
277+
report.append("HMCL Version: ").append(Metadata.VERSION).append("\n");
278+
report.append("Current Directory: ").append(Metadata.CURRENT_DIRECTORY).append("\n");
279+
report.append("HMCL Global Directory: ").append(Metadata.HMCL_GLOBAL_DIRECTORY).append("\n");
280+
report.append("HMCL Current Directory: ").append(Metadata.HMCL_CURRENT_DIRECTORY).append("\n");
281+
report.append("HMCL Jar Path: ").append(Lang.requireNonNullElse(JarUtils.thisJarPath(), "Not Found")).append("\n");
282+
report.append("HMCL Log File: ").append(Lang.requireNonNullElse(LOG.getLogFile(), "In Memory")).append("\n\n");
283+
284+
report.append("== System Information ==\n");
285+
report.append("Operating System: ").append(OperatingSystem.OS_RELEASE_PRETTY_NAME == null
286+
? OperatingSystem.SYSTEM_NAME + ' ' + OperatingSystem.SYSTEM_VERSION
287+
: OperatingSystem.OS_RELEASE_PRETTY_NAME + " (" + OperatingSystem.SYSTEM_NAME + ' ' + OperatingSystem.SYSTEM_VERSION + ')').append("\n");
288+
289+
if (OperatingSystem.CURRENT_OS == OperatingSystem.WINDOWS) {
290+
report.append("Processor Identifier: ").append(System.getenv("PROCESSOR_IDENTIFIER")).append("\n");
291+
}
292+
293+
report.append("System Architecture: ").append(Architecture.SYSTEM_ARCH.getDisplayName()).append("\n");
294+
report.append("Native Encoding: ").append(OperatingSystem.NATIVE_CHARSET).append("\n");
295+
report.append("JNU Encoding: ").append(System.getProperty("sun.jnu.encoding")).append("\n");
296+
297+
if (OperatingSystem.CURRENT_OS == OperatingSystem.WINDOWS) {
298+
report.append("Code Page: ").append(OperatingSystem.CODE_PAGE).append("\n");
299+
}
300+
301+
if (OperatingSystem.CURRENT_OS.isLinuxOrBSD()) {
302+
report.append("XDG Session Type: ").append(System.getenv("XDG_SESSION_TYPE")).append("\n");
303+
report.append("XDG Current Desktop: ").append(System.getenv("XDG_CURRENT_DESKTOP")).append("\n");
304+
}
305+
306+
report.append("Total Memory: ").append(total_memory).append("\n\n");
307+
308+
report.append("== Hardware Information ==\n");
309+
310+
CentralProcessor cpu = SystemInfo.getCentralProcessor();
311+
if (cpu != null) {
312+
report.append("CPU: ").append(cpu.toString().replace("\n", " ")).append("\n");
313+
}
314+
315+
List<GraphicsCard> graphicsCards = SystemInfo.getGraphicsCards();
316+
if (graphicsCards != null) {
317+
if (graphicsCards.isEmpty()) {
318+
report.append("GPU: Not Found\n");
319+
} else if (graphicsCards.size() == 1) {
320+
report.append("GPU: ").append(graphicsCards.get(0).toString().replace("\n", " ")).append("\n");
321+
} else {
322+
int index = 1;
323+
for (GraphicsCard graphicsCard : graphicsCards) {
324+
report.append("GPU ").append(index++).append(": ")
325+
.append(graphicsCard.toString().replace("\n", " ")).append("\n");
326+
}
327+
}
328+
}
329+
330+
long totalMemorySize = SystemInfo.getTotalMemorySize();
331+
long usedMemorySize = SystemInfo.getUsedMemorySize();
332+
report.append("Memory: ")
333+
.append(DataSizeUnit.format(usedMemorySize))
334+
.append(" / ")
335+
.append(DataSizeUnit.format(totalMemorySize));
336+
if (totalMemorySize > 0 && usedMemorySize > 0) {
337+
report.append(" (").append((int) (((double) usedMemorySize / totalMemorySize) * 100)).append("%)\n");
338+
} else {
339+
report.append("\n");
340+
}
341+
342+
report.append("\n");
343+
344+
report.append("== Java Information ==\n");
345+
report.append("Java Architecture: ").append(Architecture.CURRENT_ARCH.getDisplayName()).append("\n");
346+
report.append("Java Version: ").append(System.getProperty("java.version")).append(", ").append(System.getProperty("java.vendor")).append("\n");
347+
report.append("Java VM Version: ").append(System.getProperty("java.vm.name")).append(" (").append(System.getProperty("java.vm.info")).append("), ").append(System.getProperty("java.vm.vendor")).append("\n");
348+
report.append("Java Home: ").append(System.getProperty("java.home")).append("\n");
349+
report.append("Game Java Version: ").append(java).append("\n");
350+
report.append("Game Java Path: ").append(launchOptions.getJava().getBinary().toAbsolutePath()).append("\n");
351+
report.append("JVM Max Memory: ").append(MEGABYTES.formatBytes(Runtime.getRuntime().maxMemory())).append("\n");
352+
report.append("Allocated Memory: ").append(memory).append("\n");
353+
354+
report.append("== Game Information ==\n");
355+
report.append("Game Version: ").append(version.getId()).append("\n");
356+
report.append("Game Directory: ").append(launchOptions.getGameDir().getAbsolutePath()).append("\n");
357+
358+
report.append("\n== Mod Loader Information ==\n");
359+
for (LibraryAnalyzer.LibraryType type : LibraryAnalyzer.LibraryType.values()) {
360+
if (!type.getPatchId().isEmpty()) {
361+
analyzer.getVersion(type).ifPresent(ver -> {
362+
report.append(type.getPatchId()).append(": ").append(ver).append("\n");
363+
});
364+
}
365+
}
366+
367+
report.append("\n== Crash Analysis ==\n");
368+
for (Node node : reasonTextFlow.getChildren()) {
369+
if (node instanceof Text) {
370+
report.append(((Text) node).getText());
371+
}
372+
}
373+
374+
// Structure of game mod directory
375+
report.append(FileUtils.printFileStructure(repository.getModManager(version.getId()).getModsDirectory(), 10));
376+
377+
Files.write(crashReportFile, report.toString().getBytes());
378+
} catch (IOException e) {
379+
LOG.warning("Failed to write crash report file", e);
380+
}
381+
return logs.stream().map(Log::getLog).collect(Collectors.joining("\n"));
382+
})
269383
.thenComposeAsync(logs ->
270-
LogExporter.exportLogs(logFile, repository, launchOptions.getVersionName(), logs, new CommandBuilder().addAll(managedProcess.getCommands()).toString()))
384+
LogExporter.exportLogs(logFile, repository, launchOptions.getVersionName(), logs, new CommandBuilder().addAll(managedProcess.getCommands()).toString(), crashReportFile)
385+
)
271386
.handleAsync((result, exception) -> {
272387
Alert alert;
273388

0 commit comments

Comments
 (0)