From 97cc87631157b133b29cdf9912e487170b44ac3e Mon Sep 17 00:00:00 2001 From: Alex <40795980+AlexProgrammerDE@users.noreply.github.com> Date: Wed, 26 Feb 2025 09:52:02 +0100 Subject: [PATCH] Fix missing console logs with Geyser-Standalone gui present (#5376) * Fix missing console logs with gui present * Properly redirect stdout & stderr & also redirect JUL --- bootstrap/standalone/build.gradle.kts | 2 + .../standalone/GeyserStandaloneBootstrap.java | 30 +++----- .../standalone/GeyserStandaloneLogger.java | 12 +++ .../standalone/gui/GeyserStandaloneGUI.java | 77 ++++++++++++------- .../standalone/src/main/resources/log4j2.xml | 4 - gradle/libs.versions.toml | 4 +- 6 files changed, 76 insertions(+), 53 deletions(-) diff --git a/bootstrap/standalone/build.gradle.kts b/bootstrap/standalone/build.gradle.kts index e794c266c..5a13056eb 100644 --- a/bootstrap/standalone/build.gradle.kts +++ b/bootstrap/standalone/build.gradle.kts @@ -40,4 +40,6 @@ tasks.named("run") { dir.mkdirs() jvmArgs("-Dio.netty.leakDetection.level=PARANOID") workingDir = dir + + standardInput = System.`in` } diff --git a/bootstrap/standalone/src/main/java/org/geysermc/geyser/platform/standalone/GeyserStandaloneBootstrap.java b/bootstrap/standalone/src/main/java/org/geysermc/geyser/platform/standalone/GeyserStandaloneBootstrap.java index 87fbbf0aa..5bdb206db 100644 --- a/bootstrap/standalone/src/main/java/org/geysermc/geyser/platform/standalone/GeyserStandaloneBootstrap.java +++ b/bootstrap/standalone/src/main/java/org/geysermc/geyser/platform/standalone/GeyserStandaloneBootstrap.java @@ -32,12 +32,9 @@ import com.fasterxml.jackson.databind.introspect.AnnotatedField; import com.fasterxml.jackson.databind.introspect.BeanPropertyDefinition; import io.netty.util.ResourceLeakDetector; import lombok.Getter; -import net.minecrell.terminalconsole.TerminalConsoleAppender; import org.apache.logging.log4j.Level; import org.apache.logging.log4j.LogManager; -import org.apache.logging.log4j.core.Appender; import org.apache.logging.log4j.core.Logger; -import org.apache.logging.log4j.core.appender.ConsoleAppender; import org.checkerframework.checker.nullness.qual.NonNull; import org.geysermc.geyser.GeyserBootstrap; import org.geysermc.geyser.GeyserImpl; @@ -92,6 +89,9 @@ public class GeyserStandaloneBootstrap implements GeyserBootstrap { ResourceLeakDetector.setLevel(ResourceLeakDetector.Level.DISABLED); // Can eat performance } + System.setProperty("java.util.logging.manager", "org.apache.logging.log4j.jul.LogManager"); + GeyserStandaloneLogger.setupStreams(); + GeyserStandaloneBootstrap bootstrap = new GeyserStandaloneBootstrap(); // Set defaults boolean useGuiOpts = bootstrap.useGui; @@ -175,17 +175,10 @@ public class GeyserStandaloneBootstrap implements GeyserBootstrap { @Override public void onGeyserInitialize() { log4jLogger = (Logger) LogManager.getRootLogger(); - for (Appender appender : log4jLogger.getAppenders().values()) { - // Remove the appender that is not in use - // Prevents multiple appenders/double logging and removes harmless errors - if ((useGui && appender instanceof TerminalConsoleAppender) || (!useGui && appender instanceof ConsoleAppender)) { - log4jLogger.removeAppender(appender); - } - } if (useGui && gui == null) { gui = new GeyserStandaloneGUI(geyserLogger); - gui.redirectSystemStreams(); + gui.addGuiAppender(); gui.startUpdateThread(); } @@ -198,7 +191,7 @@ public class GeyserStandaloneBootstrap implements GeyserBootstrap { public void onGeyserEnable() { try { File configFile = FileUtils.fileOrCopiedFromResource(new File(configFilename), "config.yml", - (x) -> x.replaceAll("generateduuid", UUID.randomUUID().toString()), this); + (x) -> x.replaceAll("generateduuid", UUID.randomUUID().toString()), this); geyserConfig = FileUtils.loadConfig(configFile, GeyserStandaloneConfiguration.class); handleArgsConfigOptions(); @@ -246,9 +239,7 @@ public class GeyserStandaloneBootstrap implements GeyserBootstrap { geyserPingPassthrough = GeyserLegacyPingPassthrough.init(geyser); - if (!useGui) { - geyserLogger.start(); // Throws an error otherwise - } + geyserLogger.start(); } /** @@ -261,7 +252,8 @@ public class GeyserStandaloneBootstrap implements GeyserBootstrap { Class graphicsEnv = Class.forName("java.awt.GraphicsEnvironment"); Method isHeadless = graphicsEnv.getDeclaredMethod("isHeadless"); return (boolean) isHeadless.invoke(null); - } catch (Exception ignore) { } + } catch (Exception ignore) { + } return true; } @@ -347,12 +339,12 @@ public class GeyserStandaloneBootstrap implements GeyserBootstrap { // Get the ignored properties Set ignoredProperties = OBJECT_MAPPER.getSerializationConfig().getAnnotationIntrospector() - .findPropertyIgnoralByName(OBJECT_MAPPER.getSerializationConfig() ,beanDescription.getClassInfo()).getIgnored(); + .findPropertyIgnoralByName(OBJECT_MAPPER.getSerializationConfig(), beanDescription.getClassInfo()).getIgnored(); // Filter properties removing the ignored ones return properties.stream() - .filter(property -> !ignoredProperties.contains(property.getName())) - .collect(Collectors.toList()); + .filter(property -> !ignoredProperties.contains(property.getName())) + .collect(Collectors.toList()); } /** diff --git a/bootstrap/standalone/src/main/java/org/geysermc/geyser/platform/standalone/GeyserStandaloneLogger.java b/bootstrap/standalone/src/main/java/org/geysermc/geyser/platform/standalone/GeyserStandaloneLogger.java index b614a7b23..9c744f015 100644 --- a/bootstrap/standalone/src/main/java/org/geysermc/geyser/platform/standalone/GeyserStandaloneLogger.java +++ b/bootstrap/standalone/src/main/java/org/geysermc/geyser/platform/standalone/GeyserStandaloneLogger.java @@ -28,7 +28,10 @@ package org.geysermc.geyser.platform.standalone; import lombok.extern.slf4j.Slf4j; import net.minecrell.terminalconsole.SimpleTerminalConsole; import org.apache.logging.log4j.Level; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; import org.apache.logging.log4j.core.config.Configurator; +import org.apache.logging.log4j.io.IoBuilder; import org.geysermc.geyser.GeyserImpl; import org.geysermc.geyser.GeyserLogger; import org.geysermc.geyser.command.GeyserCommandSource; @@ -39,6 +42,15 @@ import org.jline.reader.LineReaderBuilder; @Slf4j public class GeyserStandaloneLogger extends SimpleTerminalConsole implements GeyserLogger, GeyserCommandSource { + private static final Logger logger = LogManager.getLogger("GeyserConsole"); + + /** + * Sets up {@code System.out} and {@code System.err} to redirect to log4j. + */ + public static void setupStreams() { + System.setOut(IoBuilder.forLogger(logger).setLevel(Level.INFO).buildPrintStream()); + System.setErr(IoBuilder.forLogger(logger).setLevel(Level.ERROR).buildPrintStream()); + } @Override protected LineReader buildReader(LineReaderBuilder builder) { diff --git a/bootstrap/standalone/src/main/java/org/geysermc/geyser/platform/standalone/gui/GeyserStandaloneGUI.java b/bootstrap/standalone/src/main/java/org/geysermc/geyser/platform/standalone/gui/GeyserStandaloneGUI.java index 4cbd178af..b1acb585b 100644 --- a/bootstrap/standalone/src/main/java/org/geysermc/geyser/platform/standalone/gui/GeyserStandaloneGUI.java +++ b/bootstrap/standalone/src/main/java/org/geysermc/geyser/platform/standalone/gui/GeyserStandaloneGUI.java @@ -25,7 +25,14 @@ package org.geysermc.geyser.platform.standalone.gui; -import org.checkerframework.checker.nullness.qual.NonNull; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.core.LogEvent; +import org.apache.logging.log4j.core.Logger; +import org.apache.logging.log4j.core.appender.AbstractAppender; +import org.apache.logging.log4j.core.config.NullConfiguration; +import org.apache.logging.log4j.core.config.Property; +import org.apache.logging.log4j.core.layout.PatternLayout; +import org.apache.logging.log4j.core.pattern.PatternFormatter; import org.geysermc.geyser.GeyserImpl; import org.geysermc.geyser.GeyserLogger; import org.geysermc.geyser.command.CommandRegistry; @@ -36,11 +43,16 @@ import javax.swing.*; import javax.swing.table.DefaultTableModel; import javax.swing.text.Document; import java.awt.*; -import java.awt.event.*; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.awt.event.ComponentAdapter; +import java.awt.event.ComponentEvent; +import java.awt.event.InputEvent; +import java.awt.event.KeyEvent; +import java.awt.event.WindowAdapter; +import java.awt.event.WindowEvent; import java.io.File; import java.io.IOException; -import java.io.OutputStream; -import java.io.PrintStream; import java.net.URL; import java.util.ArrayList; import java.util.List; @@ -83,7 +95,8 @@ public class GeyserStandaloneGUI { // Remove Java UI look try { UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName()); - } catch (Exception ignored) { } + } catch (Exception ignored) { + } // Show a confirm dialog on close frame.addWindowListener(new WindowAdapter() { @@ -181,7 +194,8 @@ public class GeyserStandaloneGUI { openButton.addActionListener(e -> { try { Desktop.getDesktop().open(new File("./")); - } catch (IOException ignored) { } + } catch (IOException ignored) { + } }); fileMenu.add(openButton); @@ -245,29 +259,8 @@ public class GeyserStandaloneGUI { /** * Redirect the default io streams to the text pane */ - public void redirectSystemStreams() { - // Setup a new output stream to forward it to the text pane - OutputStream out = new OutputStream() { - @Override - public void write(final int b) { - appendConsole(String.valueOf((char) b)); - } - - @Override - public void write(byte @NonNull [] b, int off, int len) { - appendConsole(new String(b, off, len)); - } - - @Override - public void write(byte @NonNull[] b) { - write(b, 0, b.length); - } - }; - - // Override the system output streams - System.setOut(new PrintStream(out, true)); - System.setErr(new PrintStream(out, true)); - + public void addGuiAppender() { + new GUIAppender().start(); } /** @@ -354,4 +347,30 @@ public class GeyserStandaloneGUI { commandInput.setText(""); // clear the input } } + + private class GUIAppender extends AbstractAppender { + private static final List FORMATTERS = PatternLayout.createPatternParser(new NullConfiguration()) + .parse( + "[%d{HH:mm:ss} %style{%highlight{%level}{FATAL=red, ERROR=red, WARN=yellow bright, INFO=cyan bright, DEBUG=green, TRACE=white}}] %minecraftFormatting{%msg}%n", + true, + false, + false + ); + + protected GUIAppender() { + super("GUIAppender", null, null, false, Property.EMPTY_ARRAY); + + ((Logger) LogManager.getRootLogger()).addAppender(this); + } + + @Override + public void append(LogEvent event) { + StringBuilder formatted = new StringBuilder(); + for (PatternFormatter formatter : FORMATTERS) { + formatter.format(event, formatted); + } + + appendConsole(formatted.toString()); + } + } } diff --git a/bootstrap/standalone/src/main/resources/log4j2.xml b/bootstrap/standalone/src/main/resources/log4j2.xml index bf361a851..0304e1924 100644 --- a/bootstrap/standalone/src/main/resources/log4j2.xml +++ b/bootstrap/standalone/src/main/resources/log4j2.xml @@ -4,9 +4,6 @@ - - - @@ -18,7 +15,6 @@ - diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index ae35c6e34..f7a8d2824 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -91,6 +91,8 @@ netty-transport-native-io_uring = { group = "io.netty.incubator", name = "netty- log4j-api = { group = "org.apache.logging.log4j", name = "log4j-api", version.ref = "log4j" } log4j-core = { group = "org.apache.logging.log4j", name = "log4j-core", version.ref = "log4j" } log4j-slf4j2-impl = { group = "org.apache.logging.log4j", name = "log4j-slf4j2-impl", version.ref = "log4j" } +log4j-iostreams = { group = "org.apache.logging.log4j", name = "log4j-iostreams", version.ref = "log4j" } +log4j-jul = { group = "org.apache.logging.log4j", name = "log4j-jul", version.ref = "log4j" } jline-terminal = { group = "org.jline", name = "jline-terminal", version.ref = "jline" } jline-terminal-jna = { group = "org.jline", name = "jline-terminal-jna", version.ref = "jline" } @@ -160,6 +162,6 @@ runpaper = { id = "xyz.jpenilla.run-paper", version.ref = "runtask" } jackson = [ "jackson-annotations", "jackson-databind", "jackson-dataformat-yaml" ] fastutil = [ "fastutil-int-int-maps", "fastutil-int-long-maps", "fastutil-int-byte-maps", "fastutil-int-boolean-maps", "fastutil-object-int-maps", "fastutil-object-object-maps" ] adventure = [ "adventure-text-serializer-gson", "adventure-text-serializer-legacy", "adventure-text-serializer-plain" ] -log4j = [ "log4j-api", "log4j-core", "log4j-slf4j2-impl" ] +log4j = [ "log4j-api", "log4j-core", "log4j-slf4j2-impl", "log4j-iostreams", "log4j-jul" ] jline = [ "jline-terminal", "jline-terminal-jna", "jline-reader" ] protocol = [ "protocol-common", "protocol-codec", "protocol-connection" ]