@@ -1,6 +1,24 @@
|
||||
/*
|
||||
* Hello Minecraft! Launcher
|
||||
* Copyright (C) 2025 huangyuhui <huanghongxun2008@126.com> 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 <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package org.jackhuang.hmcl.util.logging;
|
||||
|
||||
import org.jackhuang.hmcl.util.Pair;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.tukaani.xz.LZMA2Options;
|
||||
import org.tukaani.xz.XZOutputStream;
|
||||
|
||||
@@ -127,47 +145,10 @@ public final class Logger {
|
||||
String caller = CLASS_NAME + ".onExit";
|
||||
|
||||
if (logRetention > 0 && logFile != null) {
|
||||
List<Pair<Path, int[]>> list = new ArrayList<>();
|
||||
Pattern fileNamePattern = Pattern.compile("(?<year>\\d{4})-(?<month>\\d{2})-(?<day>\\d{2})T(?<hour>\\d{2})-(?<minute>\\d{2})-(?<second>\\d{2})(\\.(?<n>\\d+))?\\.log(\\.(gz|xz))?");
|
||||
Path dir = logFile.getParent();
|
||||
try (DirectoryStream<Path> stream = Files.newDirectoryStream(dir)) {
|
||||
for (Path path : stream) {
|
||||
Matcher matcher = fileNamePattern.matcher(path.getFileName().toString());
|
||||
if (matcher.matches() && Files.isRegularFile(path)) {
|
||||
int year = Integer.parseInt(matcher.group("year"));
|
||||
int month = Integer.parseInt(matcher.group("month"));
|
||||
int day = Integer.parseInt(matcher.group("day"));
|
||||
int hour = Integer.parseInt(matcher.group("hour"));
|
||||
int minute = Integer.parseInt(matcher.group("minute"));
|
||||
int second = Integer.parseInt(matcher.group("second"));
|
||||
int n = Optional.ofNullable(matcher.group("n")).map(Integer::parseInt).orElse(0);
|
||||
|
||||
list.add(Pair.pair(path, new int[]{year, month, day, hour, minute, second, n}));
|
||||
}
|
||||
}
|
||||
} catch (IOException e) {
|
||||
log(Level.WARNING, caller, "Failed to list log files in " + dir, e);
|
||||
}
|
||||
|
||||
var list = findRecentLogFiles(Integer.MAX_VALUE);
|
||||
if (list.size() > logRetention) {
|
||||
list.sort((a, b) -> {
|
||||
int[] v1 = a.getValue();
|
||||
int[] v2 = b.getValue();
|
||||
|
||||
assert v1.length == v2.length;
|
||||
|
||||
for (int i = 0; i < v1.length; i++) {
|
||||
int c = Integer.compare(v1[i], v2[i]);
|
||||
if (c != 0)
|
||||
return c;
|
||||
}
|
||||
|
||||
return 0;
|
||||
});
|
||||
|
||||
for (int i = 0, end = list.size() - logRetention; i < end; i++) {
|
||||
Path file = list.get(i).getKey();
|
||||
|
||||
Path file = list.get(i);
|
||||
try {
|
||||
if (!Files.isSameFile(file, logFile)) {
|
||||
log(Level.INFO, caller, "Delete old log file " + file, null);
|
||||
@@ -276,6 +257,40 @@ public final class Logger {
|
||||
return logFile;
|
||||
}
|
||||
|
||||
public @NotNull List<Path> findRecentLogFiles(int n) {
|
||||
if (n <= 0 || logFile == null)
|
||||
return List.of();
|
||||
|
||||
var currentLogFile = LogFile.ofFile(logFile);
|
||||
|
||||
Path logDir = logFile.getParent();
|
||||
if (logDir == null || !Files.isDirectory(logDir))
|
||||
return List.of();
|
||||
|
||||
var logFiles = new ArrayList<LogFile>();
|
||||
try (DirectoryStream<Path> stream = Files.newDirectoryStream(logDir)) {
|
||||
for (Path path : stream) {
|
||||
LogFile item = LogFile.ofFile(path);
|
||||
if (item != null && (currentLogFile == null || item.compareTo(currentLogFile) < 0)) {
|
||||
logFiles.add(item);
|
||||
}
|
||||
}
|
||||
} catch (IOException e) {
|
||||
log(Level.WARNING, CLASS_NAME + ".findRecentLogFiles", "Failed to list log files in " + logDir, e);
|
||||
return List.of();
|
||||
}
|
||||
logFiles.sort(Comparator.naturalOrder());
|
||||
|
||||
final int resultLength = Math.min(n, logFiles.size());
|
||||
final int offset = logFiles.size() - resultLength;
|
||||
|
||||
var result = new Path[resultLength];
|
||||
for (int i = 0; i < resultLength; i++) {
|
||||
result[i] = logFiles.get(i + offset).file;
|
||||
}
|
||||
return List.of(result);
|
||||
}
|
||||
|
||||
public void exportLogs(OutputStream output) throws IOException {
|
||||
Objects.requireNonNull(output);
|
||||
LogEvent.ExportLog event = new LogEvent.ExportLog(output);
|
||||
@@ -352,4 +367,69 @@ public final class Logger {
|
||||
public void trace(String msg, Throwable exception) {
|
||||
log(Level.TRACE, CallerFinder.getCaller(), msg, exception);
|
||||
}
|
||||
|
||||
private static final class LogFile implements Comparable<LogFile> {
|
||||
private static final Pattern FILE_NAME_PATTERN = Pattern.compile("(?<year>\\d{4})-(?<month>\\d{2})-(?<day>\\d{2})T(?<hour>\\d{2})-(?<minute>\\d{2})-(?<second>\\d{2})(\\.(?<n>\\d+))?\\.log(\\.(gz|xz))?");
|
||||
|
||||
private static @Nullable LogFile ofFile(Path file) {
|
||||
if (!Files.isRegularFile(file))
|
||||
return null;
|
||||
|
||||
Matcher matcher = FILE_NAME_PATTERN.matcher(file.getFileName().toString());
|
||||
if (!matcher.matches())
|
||||
return null;
|
||||
|
||||
int year = Integer.parseInt(matcher.group("year"));
|
||||
int month = Integer.parseInt(matcher.group("month"));
|
||||
int day = Integer.parseInt(matcher.group("day"));
|
||||
int hour = Integer.parseInt(matcher.group("hour"));
|
||||
int minute = Integer.parseInt(matcher.group("minute"));
|
||||
int second = Integer.parseInt(matcher.group("second"));
|
||||
int n = Optional.ofNullable(matcher.group("n")).map(Integer::parseInt).orElse(0);
|
||||
|
||||
return new LogFile(file, year, month, day, hour, minute, second, n);
|
||||
}
|
||||
|
||||
private final Path file;
|
||||
private final int year;
|
||||
private final int month;
|
||||
private final int day;
|
||||
private final int hour;
|
||||
private final int minute;
|
||||
private final int second;
|
||||
private final int n;
|
||||
|
||||
private LogFile(Path file, int year, int month, int day, int hour, int minute, int second, int n) {
|
||||
this.file = file;
|
||||
this.year = year;
|
||||
this.month = month;
|
||||
this.day = day;
|
||||
this.hour = hour;
|
||||
this.minute = minute;
|
||||
this.second = second;
|
||||
this.n = n;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int compareTo(@NotNull Logger.LogFile that) {
|
||||
if (this.year != that.year) return Integer.compare(this.year, that.year);
|
||||
if (this.month != that.month) return Integer.compare(this.month, that.month);
|
||||
if (this.day != that.day) return Integer.compare(this.day, that.day);
|
||||
if (this.hour != that.hour) return Integer.compare(this.hour, that.hour);
|
||||
if (this.minute != that.minute) return Integer.compare(this.minute, that.minute);
|
||||
if (this.second != that.second) return Integer.compare(this.second, that.second);
|
||||
if (this.n != that.n) return Integer.compare(this.n, that.n);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(year, month, day, hour, minute, second, n);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
return obj instanceof LogFile && compareTo((LogFile) obj) == 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user