Skip to content

Commit e17a767

Browse files
authored
Logging fixes around plugin disable and shutdown; logging cleanup (#13622)
This fixes a race where a late log message to the async appender could cause console reinitialization during shutdown Also fixes issues when plugin log messages with classloader scoped context tries to print after the plugin classloader is closed by flushing the async appender as a part of disabling plugins. Remove StacktraceDeobfuscator and ExtraClassInfoRewritePolicy - Stacktrace deobfuscator has not been useful for a while and will be completely useless with MC 26.1 - ThrowableProxy is deprecated with no replacement in Log4j 2.25.0 and newer (getThrownProxy is no longer called by built in throwable formatters, so our code is already dead)
1 parent 255c25e commit e17a767

22 files changed

+228
-479
lines changed

paper-server/patches/features/0001-Moonrise-optimisation-patches.patch

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -23059,7 +23059,7 @@ index cda915fcb4822689f42b25280eb99aee082ddb74..094d2d528cb74b8f1d277cd780bba7f4
2305923059
thread1 -> {
2306023060
DedicatedServer dedicatedServer1 = new DedicatedServer(
2306123061
diff --git a/net/minecraft/server/MinecraftServer.java b/net/minecraft/server/MinecraftServer.java
23062-
index 330d1f751006e6a5acfec1a3ab390b1c2d482299..af7e9692a38f8278734e2f1ab51c1e03b123ef3c 100644
23062+
index de7264a1a4f267b99a5a1b7a29ef7c841a3463a6..5d044ca38543aeb24fe431e232afc3caeeb7078b 100644
2306323063
--- a/net/minecraft/server/MinecraftServer.java
2306423064
+++ b/net/minecraft/server/MinecraftServer.java
2306523065
@@ -185,7 +185,7 @@ import net.minecraft.world.scores.ScoreboardSaveData;
@@ -23244,8 +23244,8 @@ index 330d1f751006e6a5acfec1a3ab390b1c2d482299..af7e9692a38f8278734e2f1ab51c1e03
2324423244
+ // Paper end - rewrite chunk system
2324523245
// Paper start - Improved watchdog support - move final shutdown items here
2324623246
Util.shutdownExecutors();
23247-
try {
23248-
@@ -1081,16 +1177,31 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
23247+
this.onServerExit();
23248+
@@ -1076,16 +1172,31 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
2324923249
// execute small amounts of other tasks just in case the number of tasks we are
2325023250
// draining is large - chunk system and packet processing may be latency sensitive
2325123251

@@ -23280,15 +23280,15 @@ index 330d1f751006e6a5acfec1a3ab390b1c2d482299..af7e9692a38f8278734e2f1ab51c1e03
2328023280
profiler.pop(); // moonrise:run_all_chunk
2328123281
profiler.pop(); // moonrise:run_all_tasks
2328223282

23283-
@@ -1396,6 +1507,7 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
23283+
@@ -1391,6 +1502,7 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
2328423284

2328523285
private boolean pollTaskInternal() {
2328623286
if (super.pollTask()) {
2328723287
+ this.moonrise$executeMidTickTasks(); // Paper - rewrite chunk system
2328823288
return true;
2328923289
} else {
2329023290
boolean ret = false; // Paper - force execution of all worlds, do not just bias the first
23291-
@@ -1537,6 +1649,13 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
23291+
@@ -1532,6 +1644,13 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
2329223292
// Paper - improve tick loop - moved into runAllTasksAtTickStart
2329323293
this.runAllTasksAtTickStart(); // Paper - improve tick loop
2329423294
this.tickServer(sprinting ? () -> false : this::haveTime);
@@ -23302,7 +23302,7 @@ index 330d1f751006e6a5acfec1a3ab390b1c2d482299..af7e9692a38f8278734e2f1ab51c1e03
2330223302
this.tickFrame.end();
2330323303
this.recordEndOfTick(); // Paper - improve tick loop
2330423304
profilerFiller.pop();
23305-
@@ -2562,6 +2681,12 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
23305+
@@ -2557,6 +2676,12 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
2330623306
}
2330723307
}
2330823308

@@ -29136,7 +29136,7 @@ index 3d317880a257bf431f110048d305ddd2a06880d7..25f2e74e229fa12a2d7f1ff43c6cb12a
2913629136

2913729137
public ClipContext(Vec3 from, Vec3 to, ClipContext.Block block, ClipContext.Fluid fluid, Entity entity) {
2913829138
diff --git a/net/minecraft/world/level/EntityGetter.java b/net/minecraft/world/level/EntityGetter.java
29139-
index d4c78c7f521b31ea9ce0cf7d62978b8e324b5d10..bcbe39eed2d254861689c95f7040f27b6ff2d438 100644
29139+
index b367eb61283c0cbf4ce9cda67089de2e22c95f4f..6ae3698e8e2c84fb21c0a4facbbf1568dbd45405 100644
2914029140
--- a/net/minecraft/world/level/EntityGetter.java
2914129141
+++ b/net/minecraft/world/level/EntityGetter.java
2914229142
@@ -15,7 +15,7 @@ import net.minecraft.world.phys.shapes.Shapes;

paper-server/patches/features/0020-Incremental-chunk-and-player-saving.patch

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ Subject: [PATCH] Incremental chunk and player saving
55

66

77
diff --git a/net/minecraft/server/MinecraftServer.java b/net/minecraft/server/MinecraftServer.java
8-
index 8dc6adb868ade00fdee6f3f53aff01fddf79926d..7e8715af6433ac3324ae7d79f5252a4550af6bb1 100644
8+
index a9751f4bda862ef597ba39700b83c434f7a457d9..90e23b96ed3fc5a84f52e6f94ad40bdb028fedf7 100644
99
--- a/net/minecraft/server/MinecraftServer.java
1010
+++ b/net/minecraft/server/MinecraftServer.java
1111
@@ -972,7 +972,7 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
@@ -17,7 +17,7 @@ index 8dc6adb868ade00fdee6f3f53aff01fddf79926d..7e8715af6433ac3324ae7d79f5252a45
1717
var4 = this.saveAllChunks(suppressLogs, flush, force);
1818
} finally {
1919
this.isSaving = false;
20-
@@ -1620,9 +1620,29 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
20+
@@ -1615,9 +1615,29 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
2121
}
2222

2323
this.ticksUntilAutosave--;

paper-server/patches/features/0025-Optimise-EntityScheduler-ticking.patch

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,10 +20,10 @@ index 2bc436cdf5180a7943c45fabb9fbbedae6f7db56..f312a7f5b1b2a777ab36b94ce7cbf387
2020

2121
@Override
2222
diff --git a/net/minecraft/server/MinecraftServer.java b/net/minecraft/server/MinecraftServer.java
23-
index 7e8715af6433ac3324ae7d79f5252a4550af6bb1..092ead4419f0a558f25b4baacf3343b05c872022 100644
23+
index 90e23b96ed3fc5a84f52e6f94ad40bdb028fedf7..1885c0378a329ef24ee3ac7556ceff4b328db089 100644
2424
--- a/net/minecraft/server/MinecraftServer.java
2525
+++ b/net/minecraft/server/MinecraftServer.java
26-
@@ -1753,33 +1753,22 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
26+
@@ -1748,33 +1748,22 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
2727
}
2828
}
2929

paper-server/patches/features/0028-Optimize-Hoppers.patch

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -48,10 +48,10 @@ index 0000000000000000000000000000000000000000..24a2090e068ad3c0d08705050944abdf
4848
+ }
4949
+}
5050
diff --git a/net/minecraft/server/MinecraftServer.java b/net/minecraft/server/MinecraftServer.java
51-
index 092ead4419f0a558f25b4baacf3343b05c872022..c2227d57ea2e2da537a313d4bfd2f8c7f776be64 100644
51+
index 1885c0378a329ef24ee3ac7556ceff4b328db089..14103cf9ad602ee39aefaafe7e65dffc72880e2a 100644
5252
--- a/net/minecraft/server/MinecraftServer.java
5353
+++ b/net/minecraft/server/MinecraftServer.java
54-
@@ -1808,6 +1808,7 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
54+
@@ -1803,6 +1803,7 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
5555
serverLevel.hasPhysicsEvent = org.bukkit.event.block.BlockPhysicsEvent.getHandlerList().getRegisteredListeners().length > 0; // Paper - BlockPhysicsEvent
5656
serverLevel.hasEntityMoveEvent = io.papermc.paper.event.entity.EntityMoveEvent.getHandlerList().getRegisteredListeners().length > 0; // Paper - Add EntityMoveEvent
5757
serverLevel.updateLagCompensationTick(); // Paper - lag compensation
Lines changed: 162 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,162 @@
1+
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
2+
From: Jason Penilla <11360596+jpenilla@users.noreply.github.com>
3+
Date: Mon, 9 Feb 2026 19:09:12 -0700
4+
Subject: [PATCH] Add explicit flush support to Log4j AsyncAppender
5+
6+
7+
diff --git a/org/apache/logging/log4j/core/appender/AsyncAppender.java b/org/apache/logging/log4j/core/appender/AsyncAppender.java
8+
index f84870712345cd688a90ea53ad47eb33fe658863..126a5d1bc4239ba83565600a060877c22e39e06b 100644
9+
--- a/org/apache/logging/log4j/core/appender/AsyncAppender.java
10+
+++ b/org/apache/logging/log4j/core/appender/AsyncAppender.java
11+
@@ -446,6 +446,23 @@ public final class AsyncAppender extends AbstractAppender {
12+
return dispatcher.getAppenders();
13+
}
14+
15+
+ // Paper start - add explicit flush method
16+
+ public boolean flush(final long timeout, final TimeUnit timeUnit) {
17+
+ if (!isStarted() || dispatcher == null) {
18+
+ return true;
19+
+ }
20+
+
21+
+ final long timeoutMillis = timeout <= 0L ? 0L : timeUnit.toMillis(timeout);
22+
+ try {
23+
+ return dispatcher.flush(timeoutMillis);
24+
+ } catch (final InterruptedException ignored) {
25+
+ Thread.currentThread().interrupt();
26+
+ LOGGER.warn("Interrupted while flushing AsyncAppender {}", getName());
27+
+ return false;
28+
+ }
29+
+ }
30+
+ // Paper end - add explicit flush method
31+
+
32+
/**
33+
* Returns the name of the appender that any errors are logged to or {@code null}.
34+
*
35+
diff --git a/org/apache/logging/log4j/core/appender/AsyncAppenderEventDispatcher.java b/org/apache/logging/log4j/core/appender/AsyncAppenderEventDispatcher.java
36+
index ed962f5952c0861809d2558e709e8dd683751383..a12872c42a26d469e5b7004f7fe7ae535071a633 100644
37+
--- a/org/apache/logging/log4j/core/appender/AsyncAppenderEventDispatcher.java
38+
+++ b/org/apache/logging/log4j/core/appender/AsyncAppenderEventDispatcher.java
39+
@@ -33,6 +33,25 @@ class AsyncAppenderEventDispatcher extends Log4jThread {
40+
41+
private static final LogEvent STOP_EVENT = new Log4jLogEvent();
42+
43+
+ // Paper start - add explicit flush method
44+
+ private static final class FlushEvent extends Log4jLogEvent {
45+
+
46+
+ private final java.util.concurrent.CountDownLatch latch = new java.util.concurrent.CountDownLatch(1);
47+
+
48+
+ private void done() {
49+
+ this.latch.countDown();
50+
+ }
51+
+
52+
+ private boolean await(final long timeoutMillis) throws InterruptedException {
53+
+ if (timeoutMillis <= 0L) {
54+
+ this.latch.await();
55+
+ return true;
56+
+ }
57+
+ return this.latch.await(timeoutMillis, java.util.concurrent.TimeUnit.MILLISECONDS);
58+
+ }
59+
+ }
60+
+ // Paper end - add explicit flush method
61+
+
62+
private static final AtomicLong THREAD_COUNTER = new AtomicLong(0);
63+
64+
private static final Logger LOGGER = StatusLogger.getLogger();
65+
@@ -87,6 +106,13 @@ class AsyncAppenderEventDispatcher extends Log4jThread {
66+
if (event == STOP_EVENT) {
67+
break;
68+
}
69+
+ // Paper start - add explicit flush method
70+
+ if (event instanceof FlushEvent flushEvent) {
71+
+ this.flushAppenderOutputStreams();
72+
+ flushEvent.done();
73+
+ continue;
74+
+ }
75+
+ // Paper end - add explicit flush method
76+
event.setEndOfBatch(queue.isEmpty());
77+
dispatch(event);
78+
}
79+
@@ -105,6 +131,13 @@ class AsyncAppenderEventDispatcher extends Log4jThread {
80+
if (event == STOP_EVENT) {
81+
continue;
82+
}
83+
+ // Paper start - add explicit flush method
84+
+ if (event instanceof FlushEvent flushEvent) {
85+
+ this.flushAppenderOutputStreams();
86+
+ flushEvent.done();
87+
+ continue;
88+
+ }
89+
+ // Paper end - add explicit flush method
90+
event.setEndOfBatch(queue.isEmpty());
91+
dispatch(event);
92+
eventCount++;
93+
@@ -112,6 +145,24 @@ class AsyncAppenderEventDispatcher extends Log4jThread {
94+
LOGGER.trace("{} has processed the last {} remaining event(s).", getName(), eventCount);
95+
}
96+
97+
+ // Paper start - add explicit flush method
98+
+ private void flushAppenderOutputStreams() {
99+
+ for (int appenderIndex = 0; appenderIndex < appenders.size(); appenderIndex++) {
100+
+ flushAppender(appenders.get(appenderIndex));
101+
+ }
102+
+ if (errorAppender != null) {
103+
+ flushAppender(errorAppender);
104+
+ }
105+
+ }
106+
+
107+
+ private void flushAppender(final AppenderControl appenderControl) {
108+
+ final Appender appender = appenderControl.getAppender();
109+
+ if (appender instanceof AbstractOutputStreamAppender<?> outputStreamAppender) {
110+
+ outputStreamAppender.getManager().flush();
111+
+ }
112+
+ }
113+
+ // Paper end - add explicit flush method
114+
+
115+
/**
116+
* Dispatches the given {@code event} to the registered appenders <b>in the
117+
* current thread</b>.
118+
@@ -178,4 +229,44 @@ class AsyncAppenderEventDispatcher extends Log4jThread {
119+
// Wait for the completion.
120+
join(timeoutMillis);
121+
}
122+
+
123+
+ // Paper start - add explicit flush method
124+
+ boolean flush(final long timeoutMillis) throws InterruptedException {
125+
+ if (stoppedRef.get()) {
126+
+ flushAppenderOutputStreams();
127+
+ return true;
128+
+ }
129+
+
130+
+ // If the dispatcher thread has not started yet, fail fast
131+
+ if (Thread.State.NEW.equals(getState())) {
132+
+ return false;
133+
+ }
134+
+
135+
+ final FlushEvent flushEvent = new FlushEvent();
136+
+
137+
+ final long startNanos = timeoutMillis > 0L ? System.nanoTime() : 0L;
138+
+ final boolean enqueued;
139+
+ if (timeoutMillis <= 0L) {
140+
+ queue.put(flushEvent);
141+
+ enqueued = true;
142+
+ } else {
143+
+ enqueued = queue.offer(flushEvent, timeoutMillis, java.util.concurrent.TimeUnit.MILLISECONDS);
144+
+ }
145+
+
146+
+ if (!enqueued) {
147+
+ return false;
148+
+ }
149+
+
150+
+ if (timeoutMillis <= 0L) {
151+
+ return flushEvent.await(0L);
152+
+ }
153+
+
154+
+ final long elapsedMillis = java.util.concurrent.TimeUnit.NANOSECONDS.toMillis(System.nanoTime() - startNanos);
155+
+ final long remainingMillis = timeoutMillis - elapsedMillis;
156+
+ if (remainingMillis <= 0L) {
157+
+ return false;
158+
+ }
159+
+ return flushEvent.await(remainingMillis);
160+
+ }
161+
+ // Paper end - add explicit flush method
162+
}

paper-server/patches/sources/net/minecraft/CrashReport.java.patch

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,7 @@
11
--- a/net/minecraft/CrashReport.java
22
+++ b/net/minecraft/CrashReport.java
3-
@@ -32,8 +_,10 @@
4-
private final SystemReport systemReport = new SystemReport();
5-
3+
@@ -34,6 +_,7 @@
64
public CrashReport(String title, Throwable exception) {
7-
+ io.papermc.paper.util.StacktraceDeobfuscator.INSTANCE.deobfuscateThrowable(exception); // Paper
85
this.title = title;
96
this.exception = exception;
107
+ this.systemReport.setDetail("CraftBukkit Information", new org.bukkit.craftbukkit.CraftCrashReport()); // CraftBukkit

paper-server/patches/sources/net/minecraft/CrashReportCategory.java.patch

Lines changed: 0 additions & 10 deletions
This file was deleted.

paper-server/patches/sources/net/minecraft/server/Main.java.patch

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
@DontObfuscate
77
- public static void main(String[] args) {
88
+ public static void main(final OptionSet optionSet) { // CraftBukkit - replaces main(String[] args)
9-
+ io.papermc.paper.util.LogManagerShutdownThread.hook(); // Paper - Improved watchdog support
9+
+ io.papermc.paper.log.LogManagerShutdownThread.hook(); // Paper - Improved watchdog support
1010
SharedConstants.tryDetectVersion();
1111
+ /* CraftBukkit start - Replace everything
1212
OptionParser optionParser = new OptionParser();

paper-server/patches/sources/net/minecraft/server/MinecraftServer.java.patch

Lines changed: 2 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -217,7 +217,7 @@
217217
+ }
218218
+ */
219219
+ // Paper end
220-
+ io.papermc.paper.util.LogManagerShutdownThread.unhook(); // Paper - Improved watchdog support
220+
+ io.papermc.paper.log.LogManagerShutdownThread.unhook(); // Paper - Improved watchdog support
221221
+ Runtime.getRuntime().addShutdownHook(new org.bukkit.craftbukkit.util.ServerShutdownThread(this));
222222
+ // CraftBukkit end
223223
+ this.paperConfigurations = services.paper().configurations(); // Paper - add paper configuration files
@@ -597,7 +597,7 @@
597597
}
598598

599599
LOGGER.info("Saving worlds");
600-
@@ -707,6 +_,25 @@
600+
@@ -707,6 +_,20 @@
601601
} catch (IOException var4) {
602602
LOGGER.error("Failed to unlock level {}", this.storageSource.getLevelId(), var4);
603603
}
@@ -613,11 +613,6 @@
613613
+ // Spigot end
614614
+ // Paper start - Improved watchdog support - move final shutdown items here
615615
+ Util.shutdownExecutors();
616-
+ try {
617-
+ net.minecrell.terminalconsole.TerminalConsoleAppender.close(); // Paper - Use TerminalConsoleAppender
618-
+ } catch (final Exception ignored) {
619-
+ }
620-
+ io.papermc.paper.log.CustomLogManager.forceReset(); // Paper - Reset loggers after shutdown
621616
+ this.onServerExit();
622617
+ // Paper end - Improved watchdog support - move final shutdown items here
623618
}

paper-server/patches/sources/net/minecraft/server/players/OldUsersConverter.java.patch

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,7 @@
4646
}
4747
}
4848

49-
@@ -317,6 +_,37 @@
49+
@@ -317,6 +_,35 @@
5050
private void movePlayerFile(File file3, String oldFileName, String newFileName) {
5151
File file4 = new File(worldPlayersDirectory, oldFileName + ".dat");
5252
File file5 = new File(file3, newFileName + ".dat");
@@ -57,7 +57,6 @@
5757
+ root = net.minecraft.nbt.NbtIo.readCompressed(new java.io.FileInputStream(file4), net.minecraft.nbt.NbtAccounter.unlimitedHeap());
5858
+ } catch (Exception exception) {
5959
+ // Paper start
60-
+ io.papermc.paper.util.StacktraceDeobfuscator.INSTANCE.deobfuscateThrowable(exception);
6160
+ exception.printStackTrace();
6261
+ com.destroystokyo.paper.exception.ServerInternalException.reportInternalException(exception);
6362
+ // Paper end
@@ -74,7 +73,6 @@
7473
+ net.minecraft.nbt.NbtIo.writeCompressed(root, new java.io.FileOutputStream(file1));
7574
+ } catch (Exception exception) {
7675
+ // Paper start
77-
+ io.papermc.paper.util.StacktraceDeobfuscator.INSTANCE.deobfuscateThrowable(exception);
7876
+ exception.printStackTrace();
7977
+ com.destroystokyo.paper.exception.ServerInternalException.reportInternalException(exception);
8078
+ // Paper end

0 commit comments

Comments
 (0)