aboutsummaryrefslogtreecommitdiffhomepage
path: root/patches/server/0585-Add-System.out-err-catcher.patch
diff options
context:
space:
mode:
Diffstat (limited to 'patches/server/0585-Add-System.out-err-catcher.patch')
-rw-r--r--patches/server/0585-Add-System.out-err-catcher.patch118
1 files changed, 118 insertions, 0 deletions
diff --git a/patches/server/0585-Add-System.out-err-catcher.patch b/patches/server/0585-Add-System.out-err-catcher.patch
new file mode 100644
index 0000000000..5b85a7f2b8
--- /dev/null
+++ b/patches/server/0585-Add-System.out-err-catcher.patch
@@ -0,0 +1,118 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: underscore11code <[email protected]>
+Date: Fri, 23 Jul 2021 23:01:42 -0700
+Subject: [PATCH] Add System.out/err catcher
+
+
+diff --git a/src/main/java/io/papermc/paper/logging/SysoutCatcher.java b/src/main/java/io/papermc/paper/logging/SysoutCatcher.java
+new file mode 100644
+index 0000000000000000000000000000000000000000..a8e813ca89b033f061e695288b3383bdcf128531
+--- /dev/null
++++ b/src/main/java/io/papermc/paper/logging/SysoutCatcher.java
+@@ -0,0 +1,94 @@
++package io.papermc.paper.logging;
++
++import org.bukkit.Bukkit;
++import org.bukkit.plugin.java.JavaPlugin;
++import org.jetbrains.annotations.NotNull;
++import org.jetbrains.annotations.Nullable;
++
++import java.io.OutputStream;
++import java.io.PrintStream;
++import java.util.Objects;
++import java.util.concurrent.ConcurrentHashMap;
++import java.util.concurrent.ConcurrentMap;
++import java.util.concurrent.TimeUnit;
++import java.util.logging.Level;
++
++public final class SysoutCatcher {
++ private static final boolean SUPPRESS_NAGS = Boolean.getBoolean("io.papermc.paper.suppress.sout.nags");
++ // Nanoseconds between nag at most; if interval is caught first, this is reset.
++ // <= 0 for disabling.
++ private static final long NAG_TIMEOUT = TimeUnit.MILLISECONDS.toNanos(
++ Long.getLong("io.papermc.paper.sout.nags.timeout", TimeUnit.MINUTES.toMillis(5L)));
++ // Count since last nag; if timeout is first, this is reset.
++ // <= 0 for disabling.
++ private static final long NAG_INTERVAL = Long.getLong("io.papermc.paper.sout.nags.interval", 200L);
++
++ // We don't particularly care about how correct this is at any given moment; let's do it on a best attempt basis.
++ // The records are also pretty small, so let's just go for a size of 64 to start...
++ //
++ // Content: Plugin name => nag object
++ // Why plugin name?: This doesn't store a reference to the plugin; keeps the reload ability.
++ // Why not clean on reload?: Effort.
++ private final ConcurrentMap<String, PluginNag> nagRecords = new ConcurrentHashMap<>(64);
++
++ public SysoutCatcher() {
++ System.setOut(new WrappedOutStream(System.out, Level.INFO, "[STDOUT] "));
++ System.setErr(new WrappedOutStream(System.err, Level.SEVERE, "[STDERR] "));
++ }
++
++ private final class WrappedOutStream extends PrintStream {
++ private static final StackWalker STACK_WALKER = StackWalker.getInstance(StackWalker.Option.RETAIN_CLASS_REFERENCE);
++ private final Level level;
++ private final String prefix;
++
++ public WrappedOutStream(@NotNull final OutputStream out, final Level level, final String prefix) {
++ super(out);
++ this.level = level;
++ this.prefix = prefix;
++ }
++
++ @Override
++ public void println(@Nullable final String line) {
++ final Class<?> clazz = STACK_WALKER.getCallerClass();
++ try {
++ final JavaPlugin plugin = JavaPlugin.getProvidingPlugin(clazz);
++
++ // Instead of just printing the message, send it to the plugin's logger
++ plugin.getLogger().log(this.level, this.prefix + line);
++
++ if (SysoutCatcher.SUPPRESS_NAGS) {
++ return;
++ }
++ if (SysoutCatcher.NAG_INTERVAL > 0 || SysoutCatcher.NAG_TIMEOUT > 0) {
++ final PluginNag nagRecord = SysoutCatcher.this.nagRecords.computeIfAbsent(plugin.getName(), k -> new PluginNag());
++ final boolean hasTimePassed = SysoutCatcher.NAG_TIMEOUT > 0
++ && (nagRecord.lastNagTimestamp == Long.MIN_VALUE
++ || nagRecord.lastNagTimestamp + SysoutCatcher.NAG_TIMEOUT <= System.nanoTime());
++ final boolean hasMessagesPassed = SysoutCatcher.NAG_INTERVAL > 0
++ && (nagRecord.messagesSinceNag == Long.MIN_VALUE
++ || ++nagRecord.messagesSinceNag >= SysoutCatcher.NAG_INTERVAL);
++ if (!hasMessagesPassed && !hasTimePassed) {
++ return;
++ }
++ nagRecord.lastNagTimestamp = System.nanoTime();
++ nagRecord.messagesSinceNag = 0;
++ }
++ Bukkit.getLogger().warning(
++ String.format("Nag author(s): '%s' of '%s' about their usage of System.out/err.print. "
++ + "Please use your plugin's logger instead (JavaPlugin#getLogger).",
++ plugin.getPluginMeta().getAuthors(),
++ plugin.getPluginMeta().getDisplayName())
++ );
++ } catch (final IllegalArgumentException | IllegalStateException e) {
++ // If anything happens, the calling class doesn't exist, there is no JavaPlugin that "owns" the calling class, etc
++ // Just print out normally, with some added information
++ Bukkit.getLogger().log(this.level, String.format("%s[%s] %s", this.prefix, clazz.getName(), line));
++ }
++ }
++ }
++
++ private static class PluginNag {
++ private long lastNagTimestamp = Long.MIN_VALUE;
++ private long messagesSinceNag = Long.MIN_VALUE;
++ }
++}
+diff --git a/src/main/java/org/bukkit/craftbukkit/CraftServer.java b/src/main/java/org/bukkit/craftbukkit/CraftServer.java
+index 7c80d3290ee443ec79cf682be29d5ac1cbf3e381..7741acecf51040ba1faec4b22c0c0bfa8a2e4393 100644
+--- a/src/main/java/org/bukkit/craftbukkit/CraftServer.java
++++ b/src/main/java/org/bukkit/craftbukkit/CraftServer.java
+@@ -307,6 +307,7 @@ public final class CraftServer implements Server {
+ public int reloadCount;
+ private final io.papermc.paper.datapack.PaperDatapackManager datapackManager; // Paper
+ public static Exception excessiveVelEx; // Paper - Velocity warnings
++ private final io.papermc.paper.logging.SysoutCatcher sysoutCatcher = new io.papermc.paper.logging.SysoutCatcher(); // Paper
+
+ static {
+ ConfigurationSerialization.registerClass(CraftOfflinePlayer.class);