v1.0.15.3 — non-blocking logger (fix install freeze), URL-encode pack file paths, news ordering by mtime

This commit is contained in:
SashegDev
2026-08-13 19:56:42 +00:00
parent 26e0280d84
commit f706e8393c
3 changed files with 68 additions and 21 deletions
@@ -9,6 +9,9 @@ import java.nio.file.Paths;
import java.nio.file.StandardOpenOption; 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.concurrent.BlockingQueue;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.ReentrantLock; import java.util.concurrent.locks.ReentrantLock;
public class LauncherLogger { public class LauncherLogger {
@@ -17,6 +20,28 @@ public class LauncherLogger {
private static boolean initialized = false; private static boolean initialized = false;
private static final ReentrantLock lock = new ReentrantLock(); private static final ReentrantLock lock = new ReentrantLock();
private static final int STDOUT_QUEUE_CAPACITY = 2048;
private static final BlockingQueue<String> stdoutQueue = new LinkedBlockingQueue<>(STDOUT_QUEUE_CAPACITY);
private static final Thread stdoutWriterThread;
static {
stdoutWriterThread = new Thread(() -> {
while (true) {
try {
String line = stdoutQueue.take();
System.out.println(line);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
return;
} catch (Exception ignored) {
// stdout may be a closed or full pipe; never let it kill the writer
}
}
}, "launcher-log-stdout");
stdoutWriterThread.setDaemon(true);
stdoutWriterThread.start();
}
public static synchronized void init() { public static synchronized void init() {
if (initialized) return; if (initialized) return;
initialized = true; initialized = true;
@@ -65,17 +90,28 @@ public class LauncherLogger {
String ts = LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss.SSS")); String ts = LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss.SSS"));
String line = "[" + ts + "] [" + level + "] " + msg; String line = "[" + ts + "] [" + level + "] " + msg;
System.out.println(line); // stdout goes through a dedicated writer thread with a bounded queue so a
// full/hung stdout pipe (e.g. javaw piped to the parent exe) can never
// block application threads.
offerStdout(line);
if (t != null) { if (t != null) {
StringWriter sw = new StringWriter(); StringWriter sw = new StringWriter();
PrintWriter pw = new PrintWriter(sw); PrintWriter pw = new PrintWriter(sw);
t.printStackTrace(pw); t.printStackTrace(pw);
pw.flush(); pw.flush();
System.err.print(sw.toString()); offerStdout(sw.toString());
} }
if (logFile != null) { if (logFile != null) {
lock.lock(); // Never block indefinitely on the file write; give up after a short wait
// so a stuck writer cannot deadlock every logging thread.
boolean acquired = false;
try {
acquired = lock.tryLock(200, TimeUnit.MILLISECONDS);
} catch (InterruptedException ignored) {
Thread.currentThread().interrupt();
}
if (acquired) {
try { try {
Files.writeString(logFile, line + "\n", StandardOpenOption.APPEND); Files.writeString(logFile, line + "\n", StandardOpenOption.APPEND);
if (t != null) { if (t != null) {
@@ -93,3 +129,14 @@ public class LauncherLogger {
} }
} }
} }
private static void offerStdout(String line) {
try {
if (!stdoutQueue.offer(line, 10, TimeUnit.MILLISECONDS)) {
// Queue full; drop rather than block the caller.
}
} catch (InterruptedException ignored) {
Thread.currentThread().interrupt();
}
}
}
+1 -1
View File
@@ -19,7 +19,7 @@
<properties> <properties>
<revision>1.0.15</revision> <revision>1.0.15</revision>
<hotfix>2</hotfix> <hotfix>3</hotfix>
<maven.compiler.source>21</maven.compiler.source> <maven.compiler.source>21</maven.compiler.source>
<maven.compiler.target>21</maven.compiler.target> <maven.compiler.target>21</maven.compiler.target>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
+6 -6
View File
@@ -4,7 +4,7 @@ import os
from contextlib import asynccontextmanager from contextlib import asynccontextmanager
from datetime import datetime from datetime import datetime
from pathlib import Path from pathlib import Path
from urllib.parse import urlparse from urllib.parse import urlparse, quote
from typing import Optional from typing import Optional
import httpx import httpx
@@ -1128,9 +1128,9 @@ async def get_pack_diff(
for path, entry in server_files.items(): for path, entry in server_files.items():
client_hash = body.get(path) client_hash = body.get(path)
if client_hash is None or client_hash != entry.hash: if client_hash is None or client_hash != entry.hash:
url = f"/pack/{pack_name}/file/{path}" url = f"/pack/{pack_name}/file/{quote(path, safe='/')}"
if preset: if preset:
url += f"?preset={preset}" url += f"?preset={quote(preset, safe='')}"
to_download.append({ to_download.append({
"path": path, "path": path,
"url": url, "url": url,
@@ -1969,8 +1969,9 @@ async def list_news():
return {"news": []} return {"news": []}
news_list = [] news_list = []
for f in sorted(NEWS_DIR.iterdir()): files = [f for f in NEWS_DIR.iterdir() if f.is_file() and f.suffix == ".txt"]
if f.is_file() and f.suffix == ".txt": files.sort(key=lambda f: f.stat().st_mtime, reverse=True)
for f in files:
try: try:
content = f.read_text(encoding="utf-8").strip().split("\n") content = f.read_text(encoding="utf-8").strip().split("\n")
if len(content) >= 4: if len(content) >= 4:
@@ -1988,7 +1989,6 @@ async def list_news():
except Exception as e: except Exception as e:
logger.warning(f"Failed to read news file {f.name}: {e}") logger.warning(f"Failed to read news file {f.name}: {e}")
news_list.reverse()
return {"news": news_list} return {"news": news_list}