Implement super sexy console to close #315

This commit is contained in:
md_5 2013-06-11 18:55:15 +10:00
parent 3c1a5aabfd
commit f0f1e71c93
10 changed files with 263 additions and 115 deletions

View File

@ -28,7 +28,7 @@
<dependency>
<groupId>com.ning</groupId>
<artifactId>async-http-client</artifactId>
<version>1.7.14</version>
<version>1.7.17</version>
<scope>compile</scope>
</dependency>
<dependency>

View File

@ -31,6 +31,12 @@
<version>${netty.version}</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>jline</groupId>
<artifactId>jline</artifactId>
<version>2.11</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>net.md-5</groupId>
<artifactId>bungeecord-protocol</artifactId>

View File

@ -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<ProxiedPlayer> getPlayers()

View File

@ -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();
}
}
}

View File

@ -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

View File

@ -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 ) );
}
}

View File

@ -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<ChatColor, String> 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 )
{
}
}
}

View File

@ -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();
}
}

View File

@ -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<LogRecord> 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 );
}
}
}

View File

@ -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 );
}
}
}