diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/ui/main/SettingsPage.java b/HMCL/src/main/java/org/jackhuang/hmcl/ui/main/SettingsPage.java index ee8ea5593cf..126fe4db3f2 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/ui/main/SettingsPage.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/main/SettingsPage.java @@ -41,12 +41,14 @@ import org.jackhuang.hmcl.upgrade.UpdateChannel; import org.jackhuang.hmcl.upgrade.UpdateChecker; import org.jackhuang.hmcl.upgrade.UpdateHandler; +import org.jackhuang.hmcl.util.HotSpotCrashReportMatcher; import org.jackhuang.hmcl.util.Lang; import org.jackhuang.hmcl.util.StringUtils; import org.jackhuang.hmcl.util.i18n.I18n; import org.jackhuang.hmcl.util.i18n.SupportedLocale; import org.jackhuang.hmcl.util.io.FileUtils; import org.jackhuang.hmcl.util.io.IOUtils; +import org.jetbrains.annotations.Nullable; import org.tukaani.xz.XZInputStream; import java.io.IOException; @@ -56,6 +58,7 @@ import java.nio.file.Path; import java.time.LocalDateTime; import java.time.format.DateTimeFormatter; +import java.util.ArrayList; import java.util.HashSet; import java.util.List; import java.util.Set; @@ -316,9 +319,16 @@ private CompletableFuture onExportLogs() { return CompletableFuture.supplyAsync(Lang.wrap(() -> { String nameBase = "hmcl-exported-logs-" + LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH-mm-ss")); List recentLogFiles = LOG.findRecentLogFiles(5); + List launcherLogFiles = new ArrayList<>(recentLogFiles); + @Nullable Path currentLogFile = LOG.getLogFile(); + if (currentLogFile != null) + launcherLogFiles.add(currentLogFile); + + List filesToExport = new ArrayList<>(recentLogFiles); + filesToExport.addAll(HotSpotCrashReportMatcher.findMatchingReports(launcherLogFiles)); Path outputFile; - if (recentLogFiles.isEmpty()) { + if (filesToExport.isEmpty()) { outputFile = Metadata.CURRENT_DIRECTORY.resolve(nameBase + ".log"); LOG.info("Exporting latest logs to " + outputFile); @@ -336,7 +346,7 @@ private CompletableFuture onExportLogs() { Set entryNames = new HashSet<>(); - for (Path path : recentLogFiles) { + for (Path path : filesToExport) { String fileName = FileUtils.getName(path); String extension = StringUtils.substringAfterLast(fileName, '.'); diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/util/HotSpotCrashReportMatcher.java b/HMCL/src/main/java/org/jackhuang/hmcl/util/HotSpotCrashReportMatcher.java new file mode 100644 index 00000000000..a0a52106c24 --- /dev/null +++ b/HMCL/src/main/java/org/jackhuang/hmcl/util/HotSpotCrashReportMatcher.java @@ -0,0 +1,221 @@ +/* + * Hello Minecraft! Launcher + * Copyright (C) 2026 huangyuhui and 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 . + */ +package org.jackhuang.hmcl.util; + +import org.jackhuang.hmcl.util.io.IOUtils; +import org.jetbrains.annotations.NotNullByDefault; +import org.jetbrains.annotations.Nullable; +import org.jetbrains.annotations.Unmodifiable; + +import java.io.BufferedReader; +import java.io.IOException; +import java.nio.file.DirectoryStream; +import java.nio.file.Files; +import java.nio.file.InvalidPathException; +import java.nio.file.Path; +import java.time.Duration; +import java.time.LocalDateTime; +import java.time.format.DateTimeFormatter; +import java.time.format.DateTimeParseException; +import java.util.ArrayList; +import java.util.List; +import java.util.Locale; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +import static org.jackhuang.hmcl.Metadata.CURRENT_DIRECTORY; +import static org.jackhuang.hmcl.util.logging.Logger.LOG; + +/// Matches HotSpot error reports to HMCL launcher logs. +@NotNullByDefault +public final class HotSpotCrashReportMatcher { + /// Matches HotSpot error report file names and captures their process IDs. + private static final Pattern ERROR_FILE_PATTERN = Pattern.compile("hs_err_pid(?\\d+)\\.log"); + + /// Matches uncompressed HMCL log file names and captures their start times. + private static final Pattern LOG_FILE_PATTERN = Pattern.compile("(?