Mark said:
I have one of these, but it's not in a working state right now. They're
not hard to whip up though.
I made a few changes and got something self-contained that seems to work
OK. I'd cannot guarantee everything is 100% tested though. The first
class is LogWindowHandler and you use it just like a regular log
handler. Just add it to an existing logger and you are done.
LogWindowHandler creates a new JFrame for each logger, so you may wish
to just add one to some top level logger. LogWindowHandler should be
100% thread safe.
The second class is just a JFrame that supports the LogWindowHandler.
It's pretty simple and LogWindowHandler already deals correctly with the
Swing invocation issues (thread safety) so you don't need to worry about
those.
/*
* To change this template, choose Tools | Templates
* and open the template in the editor.
*/
package local.logwindow;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.lang.reflect.InvocationTargetException;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
import java.util.logging.Handler;
import java.util.logging.Level;
import java.util.logging.Logger;
import java.util.logging.LogRecord;
import javax.swing.JMenu;
import javax.swing.JMenuBar;
import javax.swing.JMenuItem;
import javax.swing.SwingUtilities;
/** A handler that sends its output to a Swing window.
*
* @author Brenden
*/
public class LogWindowHandler extends java.util.logging.Handler
{
private volatile LogWindow debugWindow;
private String name;
private Map<String, Level> levelsMap =
new HashMap<String, Level>();
public LogWindowHandler( String title )
{
init( title );
}
private void init( final String title )
{
name = title;
levelsMap.put( "Info", Level.INFO );
levelsMap.put( "Finest", Level.FINEST );
try {
SwingUtilities.invokeAndWait( new Runnable()
{
@Override
public void run()
{
initLogWindow( title );
}
} );
}
catch( InterruptedException ex ) {
Logger.getLogger( LogWindowHandler.class.getName() ).
log( Level.SEVERE, null, ex );
}
catch( InvocationTargetException ex ) {
Logger.getLogger( LogWindowHandler.class.getName() ).
log( Level.SEVERE, null, ex );
}
}
private void initLogWindow( String title )
{
// MUST BE CALLED ON THE EDT!!
debugWindow = LogWindow.newInstance( title );
JMenu levels = new JMenu( "Level" );
JMenuBar mb = debugWindow.getJMenuBar();
if( mb == null ) {
mb = new JMenuBar();
debugWindow.setJMenuBar( mb );
}
mb.add( levels );
LevelListener lev = new LevelListener();
Set<String> keys = levelsMap.keySet();
for( String key : keys ) {
JMenuItem jmi = new JMenuItem();
jmi.setText( key );
jmi.addActionListener( lev );
levels.add( jmi );
}
debugWindow.setVisible( true );
}
private class LevelListener implements ActionListener
{
@Override
public void actionPerformed( ActionEvent e )
{
// Object source = e.getSource();
// System.err.println( "Source: " + source );
// String actionCommand = e.getActionCommand();
// System.err.println( "ActionCommand: "+actionCommand );
Level level = levelsMap.get( e.getActionCommand() );
setLevel( level );
}
}
@Override
public void publish( LogRecord record )
{
StringBuilder sb = new StringBuilder( 160 );
sb.append( record.getLevel() + ":" );
sb.append( record.getSourceClassName() + ":" );
sb.append( record.getSourceMethodName() + ":" );
sb.append( "<" + record.getMessage() + ">" );
sb.append( "\n" );
debugWindow.addMessage( sb.toString() );
}
@Override
public void flush()
{
// nothing to do.
}
@Override
public void close()
{
debugWindow.setVisible( false );
debugWindow.dispose();
debugWindow = null;
}
public static void main( String[] args )
{
Logger testLog = Logger.getLogger( "test.log" );
Handler h = new LogWindowHandler( "test.log" );
testLog.addHandler( h );
testLog.setLevel( Level.ALL );
testLog.fine( "fine" );
testLog.finer( "finer" );
testLog.finest( "finest" );
testLog.severe( "severe" );
testLog.warning( "warning" );
testLog.config( "config" );
testLog.info( "info" );
}
}
------8< ---- cut here ---- 8< ------------
/*
* To change this template, choose Tools | Templates
* and open the template in the editor.
*/
package local.logwindow;
import javax.swing.JFrame;
import javax.swing.JScrollPane;
import javax.swing.JTextArea;
/** A visible Swing window for logging messages from a
* java.util.logging.Handler.
*
* This class is not thread safe. Please review the appropriate
* Swing documentation, and see the individual method documentation
* of this class for more information.
*
* @author Brenden
*/
public class LogWindow extends JFrame// implements ItemListener
{
private final String windowName;
private final JTextArea messages;
/**
* Creates a new Log Window.
*
* NOT THREAD SAFE.
* This method creates a Swing GUI object and must be called on
* the Event
* Dispatch Thread.
*
* @param title The title of the JFrame window.
* @return A new LogWindow.
*/
public static LogWindow newInstance( String title )
{
LogWindow temp = new LogWindow( title );
// LogWindowManager.getInstance().addLogWindow( temp );
return temp;
}
private LogWindow( String title )
{
super( title );
windowName = title;
messages = new JTextArea( 10, 20 );
init( title );
}
private void init( String title )
{
getContentPane().add( new JScrollPane( messages ) );
setDefaultCloseOperation( JFrame.DISPOSE_ON_CLOSE );
pack();
}
/** Adds a message to the LogWindow.
*
* This method is thread safe and may be called by any thread.
*
* @param message String message to be added to the LogWindow's
* display area.
*/
public void addMessage( final String message )
{
messages.append( message );
}
/** Returns this LogWindow's name (window title).
*
* This method is thread safe and may be called by any thread.
*
* @return
*/
public String getWindowName()
{
return windowName;
}
public static void main( String... args )
{
LogWindow w = LogWindow.newInstance( "Test" );
w.setVisible( true ); // NOT THREAD SAFE, test only
w.addMessage( "Test 1" );
}
}