Fix #4409: 修复导出的历史日志文件为空的问题 (#4411)

This commit is contained in:
Glavo 2025-09-07 22:31:44 +08:00 committed by GitHub
parent 68dbe6945b
commit 24fd2cf521
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194

View File

@ -35,17 +35,15 @@ import org.jackhuang.hmcl.upgrade.UpdateHandler;
import org.jackhuang.hmcl.util.StringUtils; import org.jackhuang.hmcl.util.StringUtils;
import org.jackhuang.hmcl.util.i18n.Locales; import org.jackhuang.hmcl.util.i18n.Locales;
import org.jackhuang.hmcl.util.io.FileUtils; import org.jackhuang.hmcl.util.io.FileUtils;
import org.jackhuang.hmcl.util.io.IOUtils;
import org.tukaani.xz.XZInputStream; import org.tukaani.xz.XZInputStream;
import java.io.File; import java.io.File;
import java.io.IOException; import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
import java.io.OutputStream; import java.io.OutputStream;
import java.nio.channels.Channels;
import java.nio.channels.FileChannel;
import java.nio.file.Files; import java.nio.file.Files;
import java.nio.file.Path; import java.nio.file.Path;
import java.nio.file.StandardOpenOption;
import java.time.LocalDateTime; import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter; import java.time.format.DateTimeFormatter;
import java.util.List; import java.util.List;
@ -132,6 +130,42 @@ public final class SettingsPage extends SettingsView {
UpdateHandler.updateFrom(target); UpdateHandler.updateFrom(target);
} }
/// This method guarantees to close both `input` and the current zip entry.
///
/// If no exception occurs, this method returns `true`;
/// If an exception occurs while reading from `input`, this method returns `false`;
/// If an exception occurs while writing to `output`, this method will throw it as is.
private static boolean exportLogFile(ZipOutputStream output,
Path file, // For logging
String entryName,
InputStream input,
byte[] buffer) throws IOException {
//noinspection TryFinallyCanBeTryWithResources
try {
output.putNextEntry(new ZipEntry(entryName));
int read;
while (true) {
try {
read = input.read(buffer);
if (read <= 0)
return true;
} catch (Throwable ex) {
LOG.warning("Failed to decompress log file " + file, ex);
return false;
}
output.write(buffer, 0, read);
}
} finally {
try {
input.close();
} catch (Throwable ex) {
LOG.warning("Failed to close log file " + file, ex);
}
output.closeEntry();
}
}
@Override @Override
protected void onExportLogs() { protected void onExportLogs() {
thread(() -> { thread(() -> {
@ -152,45 +186,54 @@ public final class SettingsPage extends SettingsView {
LOG.info("Exporting latest logs to " + outputFile); LOG.info("Exporting latest logs to " + outputFile);
Path tempFile = Files.createTempFile("hmcl-decompress-log-", ".txt"); byte[] buffer = new byte[IOUtils.DEFAULT_BUFFER_SIZE];
try (var tempChannel = FileChannel.open(tempFile, StandardOpenOption.READ, StandardOpenOption.WRITE); try (var os = Files.newOutputStream(outputFile);
var os = Files.newOutputStream(outputFile);
var zos = new ZipOutputStream(os)) { var zos = new ZipOutputStream(os)) {
for (Path path : recentLogFiles) { for (Path path : recentLogFiles) {
String extension = FileUtils.getExtension(path); String fileName = FileUtils.getName(path);
decompress: String extension = StringUtils.substringAfterLast(fileName, '.');
if ("gz".equalsIgnoreCase(extension) || "xz".equalsIgnoreCase(extension)) {
try (InputStream fis = Files.newInputStream(path); if ("gz".equals(extension) || "xz".equals(extension)) {
InputStream uncompressed = "gz".equalsIgnoreCase(extension) // If an exception occurs while decompressing the input file, we should
? new GZIPInputStream(fis) // ensure the input file and the current zip entry are closed,
: new XZInputStream(fis)) { // then copy the compressed file content as-is into a new entry in the zip file.
uncompressed.transferTo(Channels.newOutputStream(tempChannel));
} catch (IOException e) { InputStream input = null;
LOG.warning("Failed to decompress log: " + path, e); try {
break decompress; input = Files.newInputStream(path);
input = "gz".equals(extension)
? new GZIPInputStream(input)
: new XZInputStream(input);
} catch (Throwable ex) {
LOG.warning("Failed to open log file " + path, ex);
IOUtils.closeQuietly(input, ex);
input = null;
} }
zos.putNextEntry(new ZipEntry(StringUtils.substringBeforeLast(FileUtils.getName(path), '.'))); String entryName = StringUtils.substringBeforeLast(fileName, ".");
Channels.newInputStream(tempChannel).transferTo(zos); if (input != null && exportLogFile(zos, path, entryName, input, buffer))
zos.closeEntry(); continue;
tempChannel.truncate(0); }
// Copy the log file content as-is into a new entry in the zip file.
// If an exception occurs while decompressing the input file, we should
// ensure the input file and the current zip entry are closed.
InputStream input;
try {
input = Files.newInputStream(path);
} catch (Throwable ex) {
LOG.warning("Failed to open log file " + path, ex);
continue; continue;
} }
zos.putNextEntry(new ZipEntry(FileUtils.getName(path))); exportLogFile(zos, path, fileName, input, buffer);
Files.copy(path, zos);
zos.closeEntry();
} }
zos.putNextEntry(new ZipEntry("hmcl-latest.log")); zos.putNextEntry(new ZipEntry("hmcl-latest.log"));
LOG.exportLogs(zos); LOG.exportLogs(zos);
zos.closeEntry(); zos.closeEntry();
} finally {
try {
Files.deleteIfExists(tempFile);
} catch (IOException ignored) {
}
} }
} }
} catch (IOException e) { } catch (IOException e) {