From 51290fa75163f0f8ddcb508aa0b0aa20ffb00db1 Mon Sep 17 00:00:00 2001 From: Minecrell Date: Fri, 22 Sep 2017 12:46:47 +0200 Subject: [PATCH] Use Log4j2 for logging and TerminalConsoleAppender for console diff --git a/bootstrap/pom.xml b/bootstrap/pom.xml index 7bc2dff7..6e8dd81e 100644 --- a/bootstrap/pom.xml +++ b/bootstrap/pom.xml @@ -80,7 +80,17 @@ + + + + + + com.github.edwgiz + maven-shade-plugin.log4j2-cachefile-transformer + 2.8.1 + + diff --git a/bootstrap/src/main/java/net/md_5/bungee/BungeeCordLauncher.java b/bootstrap/src/main/java/net/md_5/bungee/BungeeCordLauncher.java index ce1dd7a3..3d09b86a 100644 --- a/bootstrap/src/main/java/net/md_5/bungee/BungeeCordLauncher.java +++ b/bootstrap/src/main/java/net/md_5/bungee/BungeeCordLauncher.java @@ -56,6 +56,9 @@ public class BungeeCordLauncher if ( !options.has( "noconsole" ) ) { + // Waterfall start - Use TerminalConsoleAppender + io.github.waterfallmc.waterfall.WaterfallConsole.readCommands(); + /* String line; while ( bungee.isRunning && ( line = bungee.getConsoleReader().readLine( ">" ) ) != null ) { @@ -64,6 +67,8 @@ public class BungeeCordLauncher bungee.getConsole().sendMessage( ChatColor.RED + "Command not found" ); } } + */ + // Waterfall end } } } diff --git a/log4j/pom.xml b/log4j/pom.xml new file mode 100644 index 00000000..78045e1d --- /dev/null +++ b/log4j/pom.xml @@ -0,0 +1,64 @@ + + 4.0.0 + + + io.github.waterfallmc + waterfall-parent + 1.12-SNAPSHOT + ../pom.xml + + + io.github.waterfallmc + waterfall-log4j + 1.12-SNAPSHOT + jar + + Waterfall-Log + Simplistic and performant Log4j2 based logger and console API designed for use with Waterfall and Minecraft related applications. + + + + + org.apache.logging.log4j + log4j-core + 2.9.1 + + + org.apache.logging.log4j + log4j-iostreams + 2.9.1 + + + org.apache.logging.log4j + log4j-jul + 2.9.1 + + + com.lmax + disruptor + 3.3.6 + runtime + + + + + net.minecrell + terminalconsoleappender + 1.0.0 + + + net.java.dev.jna + jna + 4.4.0 + runtime + + + + io.github.waterfallmc + waterfall-chat + ${project.version} + compile + + + diff --git a/log4j/src/main/java/io/github/waterfallmc/waterfall/log4j/Log4JLogHandler.java b/log4j/src/main/java/io/github/waterfallmc/waterfall/log4j/Log4JLogHandler.java new file mode 100644 index 00000000..d5a1955b --- /dev/null +++ b/log4j/src/main/java/io/github/waterfallmc/waterfall/log4j/Log4JLogHandler.java @@ -0,0 +1,55 @@ +package io.github.waterfallmc.waterfall.log4j; + +import com.google.common.base.Strings; +import org.apache.logging.log4j.Level; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; +import org.apache.logging.log4j.jul.LevelTranslator; +import org.apache.logging.log4j.message.MessageFormatMessage; + +import java.util.Map; +import java.util.MissingResourceException; +import java.util.concurrent.ConcurrentHashMap; +import java.util.logging.Handler; +import java.util.logging.LogRecord; + +/** + * A {@link Handler} that forwards all log messages to the Log4J logger. + * + *

We don't use Log4J's custom JUL LogManager currently, because it breaks + * adding custom handlers to JUL loggers. Some plugins may depend on that + * functionality...

+ */ +class Log4JLogHandler extends Handler { + + private final Map cache = new ConcurrentHashMap<>(); + + @Override + public void publish(LogRecord record) { + Logger logger = cache.computeIfAbsent(Strings.nullToEmpty(record.getLoggerName()), LogManager::getLogger); + + String message = record.getMessage(); + if (record.getResourceBundle() != null) { + try { + message = record.getResourceBundle().getString(message); + } catch (MissingResourceException ignored) { + } + } + + final Level level = LevelTranslator.toLevel(record.getLevel()); + if (record.getParameters() != null && record.getParameters().length > 0) { + logger.log(level, new MessageFormatMessage(message, record.getParameters()), record.getThrown()); + } else { + logger.log(level, message, record.getThrown()); + } + } + + @Override + public void flush() { + } + + @Override + public void close() { + } + +} diff --git a/log4j/src/main/java/io/github/waterfallmc/waterfall/log4j/WaterfallLogger.java b/log4j/src/main/java/io/github/waterfallmc/waterfall/log4j/WaterfallLogger.java new file mode 100644 index 00000000..a0f7ed01 --- /dev/null +++ b/log4j/src/main/java/io/github/waterfallmc/waterfall/log4j/WaterfallLogger.java @@ -0,0 +1,33 @@ +package io.github.waterfallmc.waterfall.log4j; + +import org.apache.logging.log4j.Level; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.io.IoBuilder; +import java.util.logging.Handler; +import java.util.logging.Logger; + +public final class WaterfallLogger { + + private WaterfallLogger() { + } + + public static Logger create() { + org.apache.logging.log4j.Logger redirect = LogManager.getRootLogger(); + System.setOut(IoBuilder.forLogger(redirect).setLevel(Level.INFO).buildPrintStream()); + System.setErr(IoBuilder.forLogger(redirect).setLevel(Level.ERROR).buildPrintStream()); + + Logger root = Logger.getLogger(""); + root.setUseParentHandlers(false); + + // Remove existing handlers + for (Handler handler : root.getHandlers()) { + root.removeHandler(handler); + } + + // Setup forward log handler + root.addHandler(new Log4JLogHandler()); + + return Logger.getLogger("BungeeCord"); + } + +} diff --git a/log4j/src/main/resources/log4j2.xml b/log4j/src/main/resources/log4j2.xml new file mode 100644 index 00000000..d4a81199 --- /dev/null +++ b/log4j/src/main/resources/log4j2.xml @@ -0,0 +1,22 @@ + + + + + + + + + + + + + + + + + + + + + + diff --git a/pom.xml b/pom.xml index ecc35560..576944c3 100644 --- a/pom.xml +++ b/pom.xml @@ -56,6 +56,7 @@ config event log + log4j module protocol proxy diff --git a/proxy/pom.xml b/proxy/pom.xml index 1a5d036d..78326e6c 100644 --- a/proxy/pom.xml +++ b/proxy/pom.xml @@ -70,7 +70,7 @@ io.github.waterfallmc - waterfall-log + waterfall-log4j ${project.version} compile diff --git a/proxy/src/main/java/Test.java b/proxy/src/main/java/Test.java index 446dfe2f..7e7841a3 100644 --- a/proxy/src/main/java/Test.java +++ b/proxy/src/main/java/Test.java @@ -22,6 +22,9 @@ public class Test bungee.getLogger().info( "Enabled Waterfall version " + bungee.getVersion() ); bungee.start(); + // Waterfall start - Use TerminalConsoleAppender + io.github.waterfallmc.waterfall.WaterfallConsole.readCommands(); + /* while ( bungee.isRunning ) { String line = bungee.getConsoleReader().readLine( ">" ); @@ -32,6 +35,7 @@ public class Test bungee.getConsole().sendMessage( ChatColor.RED + "Command not found" ); } } - } + }*/ + // Waterfall end } } diff --git a/proxy/src/main/java/io/github/waterfallmc/waterfall/WaterfallConsole.java b/proxy/src/main/java/io/github/waterfallmc/waterfall/WaterfallConsole.java new file mode 100644 index 00000000..ed33462e --- /dev/null +++ b/proxy/src/main/java/io/github/waterfallmc/waterfall/WaterfallConsole.java @@ -0,0 +1,95 @@ +package io.github.waterfallmc.waterfall; + +import com.google.common.util.concurrent.ThreadFactoryBuilder; +import net.md_5.bungee.BungeeCord; +import net.md_5.bungee.ConsoleCommandCompleter; +import net.md_5.bungee.api.ChatColor; +import net.md_5.bungee.api.ProxyServer; +import net.minecrell.terminalconsole.TerminalConsoleAppender; +import org.jline.reader.EndOfFileException; +import org.jline.reader.LineReader; +import org.jline.reader.LineReaderBuilder; +import org.jline.reader.UserInterruptException; +import org.jline.terminal.Terminal; + +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.util.concurrent.Executor; +import java.util.concurrent.Executors; + +public final class WaterfallConsole { + + private static final Executor executor = Executors.newSingleThreadExecutor( + new ThreadFactoryBuilder().setNameFormat( "Console Command Thread #%d" ).build()); + + public static void readCommands() throws IOException { + Terminal terminal = TerminalConsoleAppender.getTerminal(); + if (terminal != null) { + readCommands(terminal); + } else { + readCommands(System.in); + } + } + + private static void runCommand(String input) { + final String command = input.trim(); + if (command.isEmpty()) { + return; + } + + executor.execute(() -> { + ProxyServer proxy = ProxyServer.getInstance(); + if (!proxy.getPluginManager().dispatchCommand(proxy.getConsole(), command)) { + proxy.getConsole().sendMessage(ChatColor.RED + "Command not found"); + } + }); + } + + private static void readCommands(Terminal terminal) throws IOException { + final BungeeCord bungee = BungeeCord.getInstance(); + final LineReader reader = LineReaderBuilder.builder() + .appName(ProxyServer.getInstance().getName()) + .terminal(terminal) + .completer(new ConsoleCommandCompleter(bungee)) + .build(); + + reader.unsetOpt(LineReader.Option.INSERT_TAB); + + TerminalConsoleAppender.setReader(reader); + + try { + String line; + while (bungee.isRunning) { + try { + line = reader.readLine("> "); + } catch (EndOfFileException ignored) { + // Continue reading after EOT + continue; + } + + if (line == null) { + break; + } + + runCommand(line); + } + } catch (UserInterruptException e) { + ProxyServer.getInstance().stop(); + } finally { + TerminalConsoleAppender.setReader(null); + } + } + + private static void readCommands(InputStream in) throws IOException { + final BungeeCord bungee = BungeeCord.getInstance(); + try (BufferedReader reader = new BufferedReader(new InputStreamReader(in))) { + String line; + while (bungee.isRunning && (line = reader.readLine()) != null) { + runCommand(line); + } + } + } + +} diff --git a/proxy/src/main/java/net/md_5/bungee/BungeeCord.java b/proxy/src/main/java/net/md_5/bungee/BungeeCord.java index 5e9484b7..b5d81439 100644 --- a/proxy/src/main/java/net/md_5/bungee/BungeeCord.java +++ b/proxy/src/main/java/net/md_5/bungee/BungeeCord.java @@ -45,7 +45,6 @@ import java.util.concurrent.locks.ReentrantReadWriteLock; import java.util.logging.Handler; import java.util.logging.Level; import java.util.logging.Logger; -import jline.console.ConsoleReader; import lombok.Getter; import lombok.Setter; import lombok.Synchronized; @@ -77,8 +76,6 @@ import net.md_5.bungee.compress.CompressFactory; import net.md_5.bungee.conf.Configuration; import net.md_5.bungee.conf.YamlConfig; import net.md_5.bungee.forge.ForgeConstants; -import net.md_5.bungee.log.BungeeLogger; -import net.md_5.bungee.log.LoggingOutputStream; import net.md_5.bungee.module.ModuleManager; import net.md_5.bungee.netty.PipelineUtils; import net.md_5.bungee.protocol.DefinedPacket; @@ -88,7 +85,6 @@ import net.md_5.bungee.protocol.packet.PluginMessage; import net.md_5.bungee.query.RemoteQuery; import net.md_5.bungee.scheduler.BungeeScheduler; import net.md_5.bungee.util.CaseInsensitiveMap; -import org.fusesource.jansi.AnsiConsole; /** * Main BungeeCord proxy class. @@ -144,8 +140,12 @@ public class BungeeCord extends ProxyServer private final File pluginsFolder = new File( "plugins" ); @Getter private final BungeeScheduler scheduler = new BungeeScheduler(); + // Waterfall start - Remove ConsoleReader for JLine 3 update + /* @Getter private final ConsoleReader consoleReader; + */ + // Waterfall end @Getter private final Logger logger; public final Gson gson = new GsonBuilder() @@ -197,6 +197,8 @@ public class BungeeCord extends ProxyServer } } + // Waterfall start - Use TerminalConsoleAppender and Log4J + /* // This is a workaround for quite possibly the weirdest bug I have ever encountered in my life! // When jansi attempts to extract its natives, by default it tries to extract a specific version, // using the loading class's implementation version. Normally this works completely fine, @@ -215,6 +217,9 @@ public class BungeeCord extends ProxyServer logger = new BungeeLogger( "BungeeCord", System.getProperty("bungee.log-file", "proxy.log"), consoleReader ); System.setErr( new PrintStream( new LoggingOutputStream( logger, Level.SEVERE ), true ) ); System.setOut( new PrintStream( new LoggingOutputStream( logger, Level.INFO ), true ) ); + */ + logger = io.github.waterfallmc.waterfall.log4j.WaterfallLogger.create(); + // Waterfall end if ( !Boolean.getBoolean( "net.md_5.bungee.native.disable" ) ) { diff --git a/proxy/src/main/java/net/md_5/bungee/ConsoleCommandCompleter.java b/proxy/src/main/java/net/md_5/bungee/ConsoleCommandCompleter.java index 455b0787..9b57fc48 100644 --- a/proxy/src/main/java/net/md_5/bungee/ConsoleCommandCompleter.java +++ b/proxy/src/main/java/net/md_5/bungee/ConsoleCommandCompleter.java @@ -2,7 +2,7 @@ package net.md_5.bungee; import com.google.common.collect.Iterables; import com.google.common.util.concurrent.ThreadFactoryBuilder; -import jline.console.completer.Completer; +import org.jline.reader.Completer; // Waterfall - Update to JLine 3 import net.md_5.bungee.api.ProxyServer; import java.util.ArrayList; @@ -28,8 +28,9 @@ public class ConsoleCommandCompleter implements Completer } @Override - public int complete( String buffer, int cursor, List candidates ) + public void complete( org.jline.reader.LineReader reader, org.jline.reader.ParsedLine line, List candidates ) // Waterfall { + final String buffer = line.line(); // Waterfall Future> future = executor.submit( new Callable>() { @Override @@ -44,6 +45,11 @@ public class ConsoleCommandCompleter implements Completer try { Iterable offers = future.get(); + // Waterfall start - Update to JLine 3 + for (String completion : offers) { + candidates.add(new org.jline.reader.Candidate(completion)); + } + /* if ( offers == null ) { return cursor; @@ -59,6 +65,8 @@ public class ConsoleCommandCompleter implements Completer { return cursor - ( buffer.length() - lastSpace - 1 ); } + */ + // Waterfall end } catch ( ExecutionException ex ) { proxy.getLogger().log( Level.WARNING, "Unhandled exception when tab completing", ex ); @@ -67,6 +75,6 @@ public class ConsoleCommandCompleter implements Completer Thread.currentThread().interrupt(); } - return cursor; + //return cursor; // Waterfall } } diff --git a/proxy/src/main/java/net/md_5/bungee/module/ModuleManager.java b/proxy/src/main/java/net/md_5/bungee/module/ModuleManager.java index 901fc5a3..8bb88bd4 100644 --- a/proxy/src/main/java/net/md_5/bungee/module/ModuleManager.java +++ b/proxy/src/main/java/net/md_5/bungee/module/ModuleManager.java @@ -42,7 +42,7 @@ public class ModuleManager ModuleVersion bungeeVersion = ModuleVersion.parse( proxy.getVersion() ); if ( bungeeVersion == null ) { - System.out.println( "Couldn't detect bungee version. Custom build?" ); + proxy.getLogger().warning( "Couldn't detect bungee version. Custom build?" ); // Waterfall - Use logger return; } -- 2.14.1