From f0f1e71c93954617bfbdf79082d9368635359a1b Mon Sep 17 00:00:00 2001 From: md_5 Date: Tue, 11 Jun 2013 18:55:15 +1000 Subject: [PATCH] Implement super sexy console to close #315 --- api/pom.xml | 2 +- proxy/pom.xml | 6 ++ .../main/java/net/md_5/bungee/BungeeCord.java | 48 ++++++--- .../java/net/md_5/bungee/BungeeLogger.java | 99 ------------------- .../bungee/command/ConsoleCommandSender.java | 4 +- .../net/md_5/bungee/log/BungeeLogger.java | 48 +++++++++ .../net/md_5/bungee/log/ColouredWriter.java | 60 +++++++++++ .../net/md_5/bungee/log/ConciseFormatter.java | 35 +++++++ .../net/md_5/bungee/log/LogDispatcher.java | 48 +++++++++ .../md_5/bungee/log/LoggingOutputStream.java | 28 ++++++ 10 files changed, 263 insertions(+), 115 deletions(-) delete mode 100644 proxy/src/main/java/net/md_5/bungee/BungeeLogger.java create mode 100644 proxy/src/main/java/net/md_5/bungee/log/BungeeLogger.java create mode 100644 proxy/src/main/java/net/md_5/bungee/log/ColouredWriter.java create mode 100644 proxy/src/main/java/net/md_5/bungee/log/ConciseFormatter.java create mode 100644 proxy/src/main/java/net/md_5/bungee/log/LogDispatcher.java create mode 100644 proxy/src/main/java/net/md_5/bungee/log/LoggingOutputStream.java diff --git a/api/pom.xml b/api/pom.xml index d3f9e82b..6b4e67b9 100644 --- a/api/pom.xml +++ b/api/pom.xml @@ -28,7 +28,7 @@ com.ning async-http-client - 1.7.14 + 1.7.17 compile diff --git a/proxy/pom.xml b/proxy/pom.xml index f76f403e..d60d269e 100644 --- a/proxy/pom.xml +++ b/proxy/pom.xml @@ -31,6 +31,12 @@ ${netty.version} compile + + jline + jline + 2.11 + compile + net.md-5 bungeecord-protocol 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 7eb6b0ca..0921ed13 100644 --- a/proxy/src/main/java/net/md_5/bungee/BungeeCord.java +++ b/proxy/src/main/java/net/md_5/bungee/BungeeCord.java @@ -1,5 +1,6 @@ package net.md_5.bungee; +import net.md_5.bungee.log.BungeeLogger; import net.md_5.bungee.reconnect.SQLReconnectHandler; import net.md_5.bungee.scheduler.BungeeScheduler; import com.google.common.util.concurrent.ThreadFactoryBuilder; @@ -16,9 +17,9 @@ import io.netty.channel.MultithreadEventLoopGroup; import io.netty.channel.nio.NioEventLoopGroup; import io.netty.channel.socket.nio.NioServerSocketChannel; import net.md_5.bungee.config.Configuration; -import java.io.BufferedReader; import java.io.File; -import java.io.InputStreamReader; +import java.io.IOException; +import java.io.PrintStream; import java.net.InetSocketAddress; import java.util.Calendar; import java.util.Collection; @@ -35,9 +36,11 @@ import java.util.concurrent.locks.ReadWriteLock; import java.util.concurrent.locks.ReentrantReadWriteLock; import java.util.logging.Level; import java.util.logging.Logger; +import jline.console.ConsoleReader; import lombok.Getter; import lombok.Setter; import lombok.Synchronized; +import net.md_5.bungee.api.ChatColor; import net.md_5.bungee.api.CommandSender; import net.md_5.bungee.api.ProxyServer; import net.md_5.bungee.api.ReconnectHandler; @@ -50,6 +53,7 @@ import net.md_5.bungee.api.plugin.PluginManager; import net.md_5.bungee.api.scheduler.TaskScheduler; import net.md_5.bungee.command.*; import net.md_5.bungee.config.YamlConfig; +import net.md_5.bungee.log.LoggingOutputStream; import net.md_5.bungee.netty.PipelineUtils; import net.md_5.bungee.protocol.packet.DefinedPacket; import net.md_5.bungee.protocol.packet.Packet3Chat; @@ -116,6 +120,10 @@ public class BungeeCord extends ProxyServer new NettyAsyncHttpProvider( new AsyncHttpClientConfig.Builder().setAsyncHttpClientProviderConfig( new NettyAsyncHttpProviderConfig().addProperty( NettyAsyncHttpProviderConfig.BOSS_EXECUTOR_SERVICE, executors ) ).setExecutorService( executors ).build() ) ); + @Getter + private final ConsoleReader consoleReader; + @Getter + private final Logger logger; { @@ -139,6 +147,28 @@ public class BungeeCord extends ProxyServer return (BungeeCord) ProxyServer.getInstance(); } + public BungeeCord() throws IOException + { + consoleReader = new ConsoleReader(); + Runtime.getRuntime().addShutdownHook( new Thread( "JLine Cleanup Thread" ) + { + @Override + public void run() + { + try + { + consoleReader.getTerminal().restore(); + } catch ( Exception ex ) + { + } + } + } ); + + logger = new BungeeLogger( this ); + System.setErr( new PrintStream( new LoggingOutputStream( logger, Level.SEVERE ), true ) ); + System.setOut( new PrintStream( new LoggingOutputStream( logger, Level.INFO ), true ) ); + } + /** * Starts a new instance of BungeeCord. * @@ -163,16 +193,14 @@ public class BungeeCord extends ProxyServer bungee.getLogger().info( "Enabled BungeeCord version " + bungee.getVersion() ); bungee.start(); - BufferedReader br = new BufferedReader( new InputStreamReader( System.in ) ); while ( bungee.isRunning ) { - String line = br.readLine(); + String line = bungee.getConsoleReader().readLine( ">" ); if ( line != null ) { - boolean handled = getInstance().getPluginManager().dispatchCommand( ConsoleCommandSender.getInstance(), line ); - if ( !handled ) + if ( !bungee.getPluginManager().dispatchCommand( ConsoleCommandSender.getInstance(), line ) ) { - System.err.println( "Command not found" ); + bungee.getConsole().sendMessage( ChatColor.RED + "Command not found" ); } } } @@ -359,12 +387,6 @@ public class BungeeCord extends ProxyServer return translation; } - @Override - public Logger getLogger() - { - return BungeeLogger.instance; - } - @Override @SuppressWarnings("unchecked") public Collection getPlayers() diff --git a/proxy/src/main/java/net/md_5/bungee/BungeeLogger.java b/proxy/src/main/java/net/md_5/bungee/BungeeLogger.java deleted file mode 100644 index 3b177eb9..00000000 --- a/proxy/src/main/java/net/md_5/bungee/BungeeLogger.java +++ /dev/null @@ -1,99 +0,0 @@ -package net.md_5.bungee; - -import java.io.IOException; -import java.io.PrintWriter; -import java.io.StringWriter; -import java.text.SimpleDateFormat; -import java.util.logging.FileHandler; -import java.util.logging.Formatter; -import java.util.logging.Level; -import java.util.logging.LogRecord; -import java.util.logging.Logger; - -/** - * Logger to handle formatting and storage of the proxy's logger. - */ -public class BungeeLogger extends Logger -{ - - private static final Formatter formatter = new ConsoleLogFormatter(); - static final BungeeLogger instance = new BungeeLogger(); - - public BungeeLogger() - { - super( "BungeeCord", null ); - try - { - FileHandler handler = new FileHandler( "proxy.log", 1 << 24, 8, true ); - handler.setFormatter( formatter ); - addHandler( handler ); - } catch ( IOException ex ) - { - System.err.println( "Could not register logger!" ); - ex.printStackTrace(); - } - } - - @Override - public void log(LogRecord record) - { - super.log( record ); - String message = formatter.format( record ); - if ( record.getLevel() == Level.SEVERE || record.getLevel() == Level.WARNING ) - { - System.err.print( message ); - } else - { - System.out.print( message ); - } - } - - public static class ConsoleLogFormatter extends Formatter - { - - private SimpleDateFormat formatter = new SimpleDateFormat( "HH:mm:ss" ); - - @Override - public String format(LogRecord logrecord) - { - StringBuilder formatted = new StringBuilder(); - - formatted.append( formatter.format( logrecord.getMillis() ) ); - Level level = logrecord.getLevel(); - - if ( level == Level.FINEST ) - { - formatted.append( " [FINEST] " ); - } else if ( level == Level.FINER ) - { - formatted.append( " [FINER] " ); - } else if ( level == Level.FINE ) - { - formatted.append( " [FINE] " ); - } else if ( level == Level.INFO ) - { - formatted.append( " [INFO] " ); - } else if ( level == Level.WARNING ) - { - formatted.append( " [WARNING] " ); - } else if ( level == Level.SEVERE ) - { - formatted.append( " [SEVERE] " ); - } - - formatted.append( formatMessage( logrecord ) ); - formatted.append( '\n' ); - Throwable throwable = logrecord.getThrown(); - - if ( throwable != null ) - { - StringWriter writer = new StringWriter(); - - throwable.printStackTrace( new PrintWriter( writer ) ); - formatted.append( writer ); - } - - return formatted.toString(); - } - } -} diff --git a/proxy/src/main/java/net/md_5/bungee/command/ConsoleCommandSender.java b/proxy/src/main/java/net/md_5/bungee/command/ConsoleCommandSender.java index 10991c0c..65a2505a 100644 --- a/proxy/src/main/java/net/md_5/bungee/command/ConsoleCommandSender.java +++ b/proxy/src/main/java/net/md_5/bungee/command/ConsoleCommandSender.java @@ -3,8 +3,8 @@ package net.md_5.bungee.command; import java.util.Collection; import java.util.Collections; import lombok.Getter; -import net.md_5.bungee.api.ChatColor; import net.md_5.bungee.api.CommandSender; +import net.md_5.bungee.api.ProxyServer; /** * Command sender representing the proxy console. @@ -22,7 +22,7 @@ public class ConsoleCommandSender implements CommandSender @Override public void sendMessage(String message) { - System.out.println( ChatColor.stripColor( message ) ); + ProxyServer.getInstance().getLogger().info( message ); } @Override diff --git a/proxy/src/main/java/net/md_5/bungee/log/BungeeLogger.java b/proxy/src/main/java/net/md_5/bungee/log/BungeeLogger.java new file mode 100644 index 00000000..fa8c508c --- /dev/null +++ b/proxy/src/main/java/net/md_5/bungee/log/BungeeLogger.java @@ -0,0 +1,48 @@ +package net.md_5.bungee.log; + +import java.io.IOException; +import java.util.logging.FileHandler; +import java.util.logging.Formatter; +import java.util.logging.LogRecord; +import java.util.logging.Logger; +import net.md_5.bungee.BungeeCord; + +public class BungeeLogger extends Logger +{ + + private final BungeeCord bungee; + private final ColouredWriter writer; + private final Formatter formatter = new ConciseFormatter(); + private final LogDispatcher dispatcher = new LogDispatcher( this ); + + public BungeeLogger(BungeeCord bungee) + { + super( "BungeeCord", null ); + this.bungee = bungee; + this.writer = new ColouredWriter( bungee.getConsoleReader() ); + + try + { + FileHandler handler = new FileHandler( "proxy.log", 1 << 24, 8, true ); + handler.setFormatter( formatter ); + addHandler( handler ); + } catch ( IOException ex ) + { + System.err.println( "Could not register logger!" ); + ex.printStackTrace(); + } + dispatcher.start(); + } + + @Override + public void log(LogRecord record) + { + dispatcher.queue( record ); + } + + void doLog(LogRecord record) + { + super.log( record ); + writer.print( formatter.format( record ) ); + } +} diff --git a/proxy/src/main/java/net/md_5/bungee/log/ColouredWriter.java b/proxy/src/main/java/net/md_5/bungee/log/ColouredWriter.java new file mode 100644 index 00000000..9d2ead4d --- /dev/null +++ b/proxy/src/main/java/net/md_5/bungee/log/ColouredWriter.java @@ -0,0 +1,60 @@ +package net.md_5.bungee.log; + +import java.io.IOException; +import java.util.EnumMap; +import java.util.Map; +import jline.console.ConsoleReader; +import net.md_5.bungee.api.ChatColor; +import org.fusesource.jansi.Ansi; + +public class ColouredWriter +{ + + private final Map replacements = new EnumMap<>( ChatColor.class ); + private final ChatColor[] colors = ChatColor.values(); + private final ConsoleReader console; + + public ColouredWriter(ConsoleReader console) + { + this.console = console; + + replacements.put( ChatColor.BLACK, Ansi.ansi().a( Ansi.Attribute.RESET ).fg( Ansi.Color.BLACK ).boldOff().toString() ); + replacements.put( ChatColor.DARK_BLUE, Ansi.ansi().a( Ansi.Attribute.RESET ).fg( Ansi.Color.BLUE ).boldOff().toString() ); + replacements.put( ChatColor.DARK_GREEN, Ansi.ansi().a( Ansi.Attribute.RESET ).fg( Ansi.Color.GREEN ).boldOff().toString() ); + replacements.put( ChatColor.DARK_AQUA, Ansi.ansi().a( Ansi.Attribute.RESET ).fg( Ansi.Color.CYAN ).boldOff().toString() ); + replacements.put( ChatColor.DARK_RED, Ansi.ansi().a( Ansi.Attribute.RESET ).fg( Ansi.Color.RED ).boldOff().toString() ); + replacements.put( ChatColor.DARK_PURPLE, Ansi.ansi().a( Ansi.Attribute.RESET ).fg( Ansi.Color.MAGENTA ).boldOff().toString() ); + replacements.put( ChatColor.GOLD, Ansi.ansi().a( Ansi.Attribute.RESET ).fg( Ansi.Color.YELLOW ).boldOff().toString() ); + replacements.put( ChatColor.GRAY, Ansi.ansi().a( Ansi.Attribute.RESET ).fg( Ansi.Color.WHITE ).boldOff().toString() ); + replacements.put( ChatColor.DARK_GRAY, Ansi.ansi().a( Ansi.Attribute.RESET ).fg( Ansi.Color.BLACK ).bold().toString() ); + replacements.put( ChatColor.BLUE, Ansi.ansi().a( Ansi.Attribute.RESET ).fg( Ansi.Color.BLUE ).bold().toString() ); + replacements.put( ChatColor.GREEN, Ansi.ansi().a( Ansi.Attribute.RESET ).fg( Ansi.Color.GREEN ).bold().toString() ); + replacements.put( ChatColor.AQUA, Ansi.ansi().a( Ansi.Attribute.RESET ).fg( Ansi.Color.CYAN ).bold().toString() ); + replacements.put( ChatColor.RED, Ansi.ansi().a( Ansi.Attribute.RESET ).fg( Ansi.Color.RED ).bold().toString() ); + replacements.put( ChatColor.LIGHT_PURPLE, Ansi.ansi().a( Ansi.Attribute.RESET ).fg( Ansi.Color.MAGENTA ).bold().toString() ); + replacements.put( ChatColor.YELLOW, Ansi.ansi().a( Ansi.Attribute.RESET ).fg( Ansi.Color.YELLOW ).bold().toString() ); + replacements.put( ChatColor.WHITE, Ansi.ansi().a( Ansi.Attribute.RESET ).fg( Ansi.Color.WHITE ).bold().toString() ); + replacements.put( ChatColor.MAGIC, Ansi.ansi().a( Ansi.Attribute.BLINK_SLOW ).toString() ); + replacements.put( ChatColor.BOLD, Ansi.ansi().a( Ansi.Attribute.UNDERLINE_DOUBLE ).toString() ); + replacements.put( ChatColor.STRIKETHROUGH, Ansi.ansi().a( Ansi.Attribute.STRIKETHROUGH_ON ).toString() ); + replacements.put( ChatColor.UNDERLINE, Ansi.ansi().a( Ansi.Attribute.UNDERLINE ).toString() ); + replacements.put( ChatColor.ITALIC, Ansi.ansi().a( Ansi.Attribute.ITALIC ).toString() ); + replacements.put( ChatColor.RESET, Ansi.ansi().a( Ansi.Attribute.RESET ).toString() ); + } + + public void print(String s) + { + for ( ChatColor color : colors ) + { + s = s.replaceAll( "(?i)" + color.toString(), replacements.get( color ) ); + } + try + { + console.print( ConsoleReader.RESET_LINE + s + Ansi.ansi().reset().toString() ); + console.drawLine(); + console.flush(); + } catch ( IOException ex ) + { + } + } +} diff --git a/proxy/src/main/java/net/md_5/bungee/log/ConciseFormatter.java b/proxy/src/main/java/net/md_5/bungee/log/ConciseFormatter.java new file mode 100644 index 00000000..555e92a4 --- /dev/null +++ b/proxy/src/main/java/net/md_5/bungee/log/ConciseFormatter.java @@ -0,0 +1,35 @@ +package net.md_5.bungee.log; + +import java.io.PrintWriter; +import java.io.StringWriter; +import java.text.DateFormat; +import java.text.SimpleDateFormat; +import java.util.logging.Formatter; +import java.util.logging.LogRecord; + +public class ConciseFormatter extends Formatter +{ + + private final DateFormat date = new SimpleDateFormat( "HH:mm:ss" ); + + @Override + public String format(LogRecord record) + { + StringBuilder formatted = new StringBuilder(); + + formatted.append( date.format( record.getMillis() ) ); + formatted.append( " [" ); + formatted.append( record.getLevel().getLocalizedName() ); + formatted.append( "] " ); + formatted.append( formatMessage( record ) ); + formatted.append( '\n' ); + if ( record.getThrown() != null ) + { + StringWriter writer = new StringWriter(); + record.getThrown().printStackTrace( new PrintWriter( writer ) ); + formatted.append( writer ); + } + + return formatted.toString(); + } +} diff --git a/proxy/src/main/java/net/md_5/bungee/log/LogDispatcher.java b/proxy/src/main/java/net/md_5/bungee/log/LogDispatcher.java new file mode 100644 index 00000000..2ff69b2f --- /dev/null +++ b/proxy/src/main/java/net/md_5/bungee/log/LogDispatcher.java @@ -0,0 +1,48 @@ +package net.md_5.bungee.log; + +import java.util.concurrent.BlockingQueue; +import java.util.concurrent.LinkedBlockingQueue; +import java.util.logging.LogRecord; + +public class LogDispatcher extends Thread +{ + + private final BungeeLogger logger; + private final BlockingQueue queue = new LinkedBlockingQueue<>(); + + public LogDispatcher(BungeeLogger logger) + { + super( "BungeeCord Logger Thread - " + logger ); + this.logger = logger; + } + + @Override + public void run() + { + while ( !isInterrupted() ) + { + LogRecord record; + try + { + record = queue.take(); + } catch ( InterruptedException ex ) + { + continue; + } + + logger.doLog( record ); + } + for ( LogRecord record : queue ) + { + logger.doLog( record ); + } + } + + public void queue(LogRecord record) + { + if ( !isInterrupted() ) + { + queue.add( record ); + } + } +} diff --git a/proxy/src/main/java/net/md_5/bungee/log/LoggingOutputStream.java b/proxy/src/main/java/net/md_5/bungee/log/LoggingOutputStream.java new file mode 100644 index 00000000..f78298cd --- /dev/null +++ b/proxy/src/main/java/net/md_5/bungee/log/LoggingOutputStream.java @@ -0,0 +1,28 @@ +package net.md_5.bungee.log; + +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.util.logging.Level; +import java.util.logging.Logger; +import lombok.RequiredArgsConstructor; + +@RequiredArgsConstructor +public class LoggingOutputStream extends ByteArrayOutputStream +{ + + private static final String separator = System.getProperty( "line.separator" ); + /*========================================================================*/ + private final Logger logger; + private final Level level; + + @Override + public void flush() throws IOException + { + String contents = toString(); + super.reset(); + if ( !contents.isEmpty() && !contents.equals( separator ) ) + { + logger.logp( level, "", "", contents ); + } + } +}