1
0
mirror of https://github.com/GeyserMC/Geyser.git synced 2025-12-19 14:59:27 +00:00

Fix missing console logs with Geyser-Standalone gui present (#5376)

* Fix missing console logs with gui present

* Properly redirect stdout & stderr & also redirect JUL
This commit is contained in:
Alex
2025-02-26 09:52:02 +01:00
committed by GitHub
parent 5f0611ff0f
commit 97cc876311
6 changed files with 76 additions and 53 deletions

View File

@@ -40,4 +40,6 @@ tasks.named<JavaExec>("run") {
dir.mkdirs() dir.mkdirs()
jvmArgs("-Dio.netty.leakDetection.level=PARANOID") jvmArgs("-Dio.netty.leakDetection.level=PARANOID")
workingDir = dir workingDir = dir
standardInput = System.`in`
} }

View File

@@ -32,12 +32,9 @@ import com.fasterxml.jackson.databind.introspect.AnnotatedField;
import com.fasterxml.jackson.databind.introspect.BeanPropertyDefinition; import com.fasterxml.jackson.databind.introspect.BeanPropertyDefinition;
import io.netty.util.ResourceLeakDetector; import io.netty.util.ResourceLeakDetector;
import lombok.Getter; import lombok.Getter;
import net.minecrell.terminalconsole.TerminalConsoleAppender;
import org.apache.logging.log4j.Level; import org.apache.logging.log4j.Level;
import org.apache.logging.log4j.LogManager; 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.Logger;
import org.apache.logging.log4j.core.appender.ConsoleAppender;
import org.checkerframework.checker.nullness.qual.NonNull; import org.checkerframework.checker.nullness.qual.NonNull;
import org.geysermc.geyser.GeyserBootstrap; import org.geysermc.geyser.GeyserBootstrap;
import org.geysermc.geyser.GeyserImpl; import org.geysermc.geyser.GeyserImpl;
@@ -92,6 +89,9 @@ public class GeyserStandaloneBootstrap implements GeyserBootstrap {
ResourceLeakDetector.setLevel(ResourceLeakDetector.Level.DISABLED); // Can eat performance 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(); GeyserStandaloneBootstrap bootstrap = new GeyserStandaloneBootstrap();
// Set defaults // Set defaults
boolean useGuiOpts = bootstrap.useGui; boolean useGuiOpts = bootstrap.useGui;
@@ -175,17 +175,10 @@ public class GeyserStandaloneBootstrap implements GeyserBootstrap {
@Override @Override
public void onGeyserInitialize() { public void onGeyserInitialize() {
log4jLogger = (Logger) LogManager.getRootLogger(); 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) { if (useGui && gui == null) {
gui = new GeyserStandaloneGUI(geyserLogger); gui = new GeyserStandaloneGUI(geyserLogger);
gui.redirectSystemStreams(); gui.addGuiAppender();
gui.startUpdateThread(); gui.startUpdateThread();
} }
@@ -198,7 +191,7 @@ public class GeyserStandaloneBootstrap implements GeyserBootstrap {
public void onGeyserEnable() { public void onGeyserEnable() {
try { try {
File configFile = FileUtils.fileOrCopiedFromResource(new File(configFilename), "config.yml", 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); geyserConfig = FileUtils.loadConfig(configFile, GeyserStandaloneConfiguration.class);
handleArgsConfigOptions(); handleArgsConfigOptions();
@@ -246,9 +239,7 @@ public class GeyserStandaloneBootstrap implements GeyserBootstrap {
geyserPingPassthrough = GeyserLegacyPingPassthrough.init(geyser); geyserPingPassthrough = GeyserLegacyPingPassthrough.init(geyser);
if (!useGui) { geyserLogger.start();
geyserLogger.start(); // Throws an error otherwise
}
} }
/** /**
@@ -261,7 +252,8 @@ public class GeyserStandaloneBootstrap implements GeyserBootstrap {
Class<?> graphicsEnv = Class.forName("java.awt.GraphicsEnvironment"); Class<?> graphicsEnv = Class.forName("java.awt.GraphicsEnvironment");
Method isHeadless = graphicsEnv.getDeclaredMethod("isHeadless"); Method isHeadless = graphicsEnv.getDeclaredMethod("isHeadless");
return (boolean) isHeadless.invoke(null); return (boolean) isHeadless.invoke(null);
} catch (Exception ignore) { } } catch (Exception ignore) {
}
return true; return true;
} }
@@ -347,12 +339,12 @@ public class GeyserStandaloneBootstrap implements GeyserBootstrap {
// Get the ignored properties // Get the ignored properties
Set<String> ignoredProperties = OBJECT_MAPPER.getSerializationConfig().getAnnotationIntrospector() Set<String> 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 // Filter properties removing the ignored ones
return properties.stream() return properties.stream()
.filter(property -> !ignoredProperties.contains(property.getName())) .filter(property -> !ignoredProperties.contains(property.getName()))
.collect(Collectors.toList()); .collect(Collectors.toList());
} }
/** /**

View File

@@ -28,7 +28,10 @@ package org.geysermc.geyser.platform.standalone;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import net.minecrell.terminalconsole.SimpleTerminalConsole; import net.minecrell.terminalconsole.SimpleTerminalConsole;
import org.apache.logging.log4j.Level; 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.core.config.Configurator;
import org.apache.logging.log4j.io.IoBuilder;
import org.geysermc.geyser.GeyserImpl; import org.geysermc.geyser.GeyserImpl;
import org.geysermc.geyser.GeyserLogger; import org.geysermc.geyser.GeyserLogger;
import org.geysermc.geyser.command.GeyserCommandSource; import org.geysermc.geyser.command.GeyserCommandSource;
@@ -39,6 +42,15 @@ import org.jline.reader.LineReaderBuilder;
@Slf4j @Slf4j
public class GeyserStandaloneLogger extends SimpleTerminalConsole implements GeyserLogger, GeyserCommandSource { 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 @Override
protected LineReader buildReader(LineReaderBuilder builder) { protected LineReader buildReader(LineReaderBuilder builder) {

View File

@@ -25,7 +25,14 @@
package org.geysermc.geyser.platform.standalone.gui; 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.GeyserImpl;
import org.geysermc.geyser.GeyserLogger; import org.geysermc.geyser.GeyserLogger;
import org.geysermc.geyser.command.CommandRegistry; import org.geysermc.geyser.command.CommandRegistry;
@@ -36,11 +43,16 @@ import javax.swing.*;
import javax.swing.table.DefaultTableModel; import javax.swing.table.DefaultTableModel;
import javax.swing.text.Document; import javax.swing.text.Document;
import java.awt.*; 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.File;
import java.io.IOException; import java.io.IOException;
import java.io.OutputStream;
import java.io.PrintStream;
import java.net.URL; import java.net.URL;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
@@ -83,7 +95,8 @@ public class GeyserStandaloneGUI {
// Remove Java UI look // Remove Java UI look
try { try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName()); UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (Exception ignored) { } } catch (Exception ignored) {
}
// Show a confirm dialog on close // Show a confirm dialog on close
frame.addWindowListener(new WindowAdapter() { frame.addWindowListener(new WindowAdapter() {
@@ -181,7 +194,8 @@ public class GeyserStandaloneGUI {
openButton.addActionListener(e -> { openButton.addActionListener(e -> {
try { try {
Desktop.getDesktop().open(new File("./")); Desktop.getDesktop().open(new File("./"));
} catch (IOException ignored) { } } catch (IOException ignored) {
}
}); });
fileMenu.add(openButton); fileMenu.add(openButton);
@@ -245,29 +259,8 @@ public class GeyserStandaloneGUI {
/** /**
* Redirect the default io streams to the text pane * Redirect the default io streams to the text pane
*/ */
public void redirectSystemStreams() { public void addGuiAppender() {
// Setup a new output stream to forward it to the text pane new GUIAppender().start();
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));
} }
/** /**
@@ -354,4 +347,30 @@ public class GeyserStandaloneGUI {
commandInput.setText(""); // clear the input commandInput.setText(""); // clear the input
} }
} }
private class GUIAppender extends AbstractAppender {
private static final List<PatternFormatter> 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());
}
}
} }

View File

@@ -4,9 +4,6 @@
<TerminalConsole name="TerminalConsole"> <TerminalConsole name="TerminalConsole">
<PatternLayout pattern="[%d{HH:mm:ss} %style{%highlight{%level}{FATAL=red, ERROR=red, WARN=yellow bright, INFO=cyan bright, DEBUG=green, TRACE=white}}] %minecraftFormatting{%msg}%n"/> <PatternLayout pattern="[%d{HH:mm:ss} %style{%highlight{%level}{FATAL=red, ERROR=red, WARN=yellow bright, INFO=cyan bright, DEBUG=green, TRACE=white}}] %minecraftFormatting{%msg}%n"/>
</TerminalConsole> </TerminalConsole>
<Console name="Console" target="SYSTEM_OUT" follow="true">
<PatternLayout pattern="[%d{HH:mm:ss} %style{%highlight{%level}{FATAL=red, ERROR=red, WARN=yellow bright, INFO=cyan bright, DEBUG=green, TRACE=white}}] %minecraftFormatting{%msg}%n"/>
</Console>
<RollingRandomAccessFile name="File" fileName="logs/latest.log" filePattern="logs/%d{yyyy-MM-dd}-%i.log.gz"> <RollingRandomAccessFile name="File" fileName="logs/latest.log" filePattern="logs/%d{yyyy-MM-dd}-%i.log.gz">
<PatternLayout pattern="[%d{HH:mm:ss.SSS} %t/%level] %minecraftFormatting{%msg}{strip}%n"/> <PatternLayout pattern="[%d{HH:mm:ss.SSS} %t/%level] %minecraftFormatting{%msg}{strip}%n"/>
<Policies> <Policies>
@@ -18,7 +15,6 @@
<Loggers> <Loggers>
<Root level="INFO" > <Root level="INFO" >
<AppenderRef ref="TerminalConsole"/> <AppenderRef ref="TerminalConsole"/>
<AppenderRef ref="Console"/>
<AppenderRef ref="File"/> <AppenderRef ref="File"/>
<filters> <filters>
<MarkerFilter marker="packet_logging" onMatch="DENY" onMismatch="ACCEPT" /> <MarkerFilter marker="packet_logging" onMatch="DENY" onMismatch="ACCEPT" />

View File

@@ -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-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-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-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 = { group = "org.jline", name = "jline-terminal", version.ref = "jline" }
jline-terminal-jna = { group = "org.jline", name = "jline-terminal-jna", 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" ] 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" ] 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" ] 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" ] jline = [ "jline-terminal", "jline-terminal-jna", "jline-reader" ]
protocol = [ "protocol-common", "protocol-codec", "protocol-connection" ] protocol = [ "protocol-common", "protocol-codec", "protocol-connection" ]