You are viewing a plain text version of this content. The canonical link for it is here.
Posted to log4j-dev@logging.apache.org by Ceki Gülcü <ce...@qos.ch> on 2002/03/23 10:44:30 UTC
Re: cvs commit:
jakarta-log4j/src/java/org/apache/log4j/chainsaw XMLFileHandler.java
MyTableModel.java Main.java LoggingReceiver.java LoadXMLAction.java
ExitAction.java EventDetails.java DetailPanel.java ControlPanel.java
Welcome to chainsaw!
At 07:51 23.03.2002 +0000, you wrote:
>oburn 02/03/22 23:51:26
>
> Added: src/java/org/apache/log4j/chainsaw XMLFileHandler.java
> MyTableModel.java Main.java LoggingReceiver.java
> LoadXMLAction.java ExitAction.java
> EventDetails.java DetailPanel.java
> ControlPanel.java
> Log:
> First version of Chainsaw based on version 1.1. The changes made were:
> - Change the package name
> - Change the license to Apache
> - Remove the startup sound
> - Remove the test generator class
> - Change the email address for the author.
>
> Revision Changes Path
> 1.1
> jakarta-log4j/src/java/org/apache/log4j/chainsaw/XMLFileHandler.java
>
> Index: XMLFileHandler.java
> ===================================================================
> /*
> * Copyright (C) The Apache Software Foundation. All rights reserved.
> *
> * This software is published under the terms of the Apache Software
> * License version 1.1, a copy of which has been included with this
> * distribution in the LICENSE.txt file. */
> package org.apache.log4j.chainsaw;
>
> import java.util.StringTokenizer;
> import org.apache.log4j.Priority;
> import org.xml.sax.Attributes;
> import org.xml.sax.SAXException;
> import org.xml.sax.helpers.DefaultHandler;
>
> /**
> * A content handler for document containing Log4J events logged using the
> * XMLLayout class. It will create events and add them to a supplied model.
> *
> * @author <a href="mailto:oliver@puppycrawl.com">Oliver Burn</a>
> * @version 1.0
> */
> class XMLFileHandler
> extends DefaultHandler
> {
> /** represents the event tag **/
> private static final String TAG_EVENT = "log4j:event";
> /** represents the message tag **/
> private static final String TAG_MESSAGE = "log4j:message";
> /** represents the ndc tag **/
> private static final String TAG_NDC = "log4j:NDC";
> /** represents the throwable tag **/
> private static final String TAG_THROWABLE = "log4j:throwable";
> /** represents the location info tag **/
> private static final String TAG_LOCATION_INFO = "log4j:locationInfo";
>
> /** where to put the events **/
> private final MyTableModel mModel;
> /** the number of events in the document **/
> private int mNumEvents;
> /** the current element being parsed **/
> private String mCurrentElement;
>
> /** the time of the event **/
> private long mTimeStamp;
> /** the priority of the event **/
> private Priority mPriority;
> /** the category of the event **/
> private String mCategoryName;
> /** the NDC for the event **/
> private String mNDC;
> /** the thread for the event **/
> private String mThreadName;
> /** the msg for the event **/
> private String mMessage;
> /** the throwable details the event **/
> private String[] mThrowableStrRep;
> /** the location details for the event **/
> private String mLocationDetails;
>
>
> /**
> * Creates a new <code>XMLFileHandler</code> instance.
> *
> * @param aModel where to add the events
> */
> XMLFileHandler(MyTableModel aModel) {
> mModel = aModel;
> }
>
> /** @see DefaultHandler **/
> public void startDocument()
> throws SAXException
> {
> mNumEvents = 0;
> }
>
> /** @see DefaultHandler **/
> public void characters(char[] aChars, int aStart, int aLength) {
> if (mCurrentElement == TAG_NDC) {
> mNDC = new String(aChars, aStart, aLength);
> } else if (mCurrentElement == TAG_MESSAGE) {
> mMessage = new String(aChars, aStart, aLength);
> } else if (mCurrentElement == TAG_THROWABLE) {
> final StringTokenizer st =
> new StringTokenizer(new String(aChars, aStart,
> aLength), "\t");
> mThrowableStrRep = new String[st.countTokens()];
> if (mThrowableStrRep.length > 0) {
> mThrowableStrRep[0] = st.nextToken();
> for (int i = 1; i < mThrowableStrRep.length; i++) {
> mThrowableStrRep[i] = "\t" + st.nextToken();
> }
> }
> }
> }
>
> /** @see DefaultHandler **/
> public void endElement(String aNamespaceURI,
> String aLocalName,
> String aQName)
> {
> if (TAG_EVENT.equals(aQName)) {
> addEvent();
> resetData();
> } else if (mCurrentElement != TAG_EVENT) {
> mCurrentElement = TAG_EVENT; // hack - but only thing I
> care about
> }
> }
>
> /** @see DefaultHandler **/
> public void startElement(String aNamespaceURI,
> String aLocalName,
> String aQName,
> Attributes aAtts)
> {
> if (TAG_EVENT.equals(aQName)) {
> mThreadName = aAtts.getValue("thread");
> mTimeStamp = Long.parseLong(aAtts.getValue("timestamp"));
> mCategoryName = aAtts.getValue("category");
> mPriority = Priority.toPriority(aAtts.getValue("priority"));
> } else if (TAG_LOCATION_INFO.equals(aQName)) {
> mLocationDetails = aAtts.getValue("class") + "."
> + aAtts.getValue("method")
> + "(" + aAtts.getValue("file") + ":" +
> aAtts.getValue("line")
> + ")";
> } else if (TAG_NDC.equals(aQName)) {
> mCurrentElement = TAG_NDC;
> } else if (TAG_MESSAGE.equals(aQName)) {
> mCurrentElement = TAG_MESSAGE;
> } else if (TAG_THROWABLE.equals(aQName)) {
> mCurrentElement = TAG_THROWABLE;
> }
> }
>
> /** @return the number of events in the document **/
> int getNumEvents() {
> return mNumEvents;
> }
>
>
>////////////////////////////////////////////////////////////////////////////
> // Private methods
>
>////////////////////////////////////////////////////////////////////////////
>
> /** Add an event to the model **/
> private void addEvent() {
> mModel.addEvent(new EventDetails(mTimeStamp,
> mPriority,
> mCategoryName,
> mNDC,
> mThreadName,
> mMessage,
> mThrowableStrRep,
> mLocationDetails));
> mNumEvents++;
> }
>
> /** Reset the data for an event **/
> private void resetData() {
> mTimeStamp = 0;
> mPriority = null;
> mCategoryName = null;
> mNDC = null;
> mThreadName = null;
> mMessage = null;
> mThrowableStrRep = null;
> mLocationDetails = null;
> }
> }
>
>
>
> 1.1
> jakarta-log4j/src/java/org/apache/log4j/chainsaw/MyTableModel.java
>
> Index: MyTableModel.java
> ===================================================================
> /*
> * Copyright (C) The Apache Software Foundation. All rights reserved.
> *
> * This software is published under the terms of the Apache Software
> * License version 1.1, a copy of which has been included with this
> * distribution in the LICENSE.txt file. */
> package org.apache.log4j.chainsaw;
>
> import java.text.DateFormat;
> import java.util.ArrayList;
> import java.util.Comparator;
> import java.util.Date;
> import java.util.Iterator;
> import java.util.List;
> import java.util.SortedSet;
> import java.util.TreeSet;
> import javax.swing.table.AbstractTableModel;
> import org.apache.log4j.Priority;
> import org.apache.log4j.Category;
>
> /**
> * Represents a list of <code>EventDetails</code> objects that are
> sorted on
> * logging time. Methods are provided to filter the events that are
> visible.
> *
> * @author <a href="mailto:oliver@puppycrawl.com">Oliver Burn</a>
> */
> class MyTableModel
> extends AbstractTableModel
> {
>
> /** used to log messages **/
> private static final Category LOG =
> Category.getInstance(MyTableModel.class);
>
> /** use the compare logging events **/
> private static final Comparator MY_COMP = new Comparator()
> {
> /** @see Comparator **/
> public int compare(Object aObj1, Object aObj2) {
> if ((aObj1 == null) && (aObj2 == null)) {
> return 0; // treat as equal
> } else if (aObj1 == null) {
> return -1; // null less than everything
> } else if (aObj2 == null) {
> return 1; // think about it. :->
> }
>
> // will assume only have LoggingEvent
> final EventDetails le1 = (EventDetails) aObj1;
> final EventDetails le2 = (EventDetails) aObj2;
>
> if (le1.getTimeStamp() < le2.getTimeStamp()) {
> return 1;
> }
> // assume not two events are logged at exactly the same time
> return -1;
> }
> };
>
> /**
> * Helper that actually processes incoming events.
> * @author <a href="mailto:oliver@puppycrawl.com">Oliver Burn</a>
> */
> private class Processor
> implements Runnable
> {
> /** loops getting the events **/
> public void run() {
> while (true) {
> try {
> Thread.sleep(1000);
> } catch (InterruptedException e) {
> // ignore
> }
>
> synchronized (mLock) {
> if (mPaused) {
> continue;
> }
>
> boolean toHead = true; // were events added to head
> boolean needUpdate = false;
> final Iterator it = mPendingEvents.iterator();
> while (it.hasNext()) {
> final EventDetails event = (EventDetails)
> it.next();
> mAllEvents.add(event);
> toHead = toHead && (event == mAllEvents.first());
> needUpdate = needUpdate || matchFilter(event);
> }
> mPendingEvents.clear();
>
> if (needUpdate) {
> updateFilteredEvents(toHead);
> }
> }
> }
>
> }
> }
>
>
> /** names of the columns in the table **/
> private static final String[] COL_NAMES = {
> "Time", "Priority", "Trace", "Category", "NDC", "Message"};
>
> /** definition of an empty list **/
> private static final EventDetails[] EMPTY_LIST = new
> EventDetails[] {};
>
> /** used to format dates **/
> private static final DateFormat DATE_FORMATTER =
> DateFormat.getDateTimeInstance(DateFormat.SHORT,
> DateFormat.MEDIUM);
>
> /** the lock to control access **/
> private final Object mLock = new Object();
> /** set of all logged events - not filtered **/
> private final SortedSet mAllEvents = new TreeSet(MY_COMP);
> /** events that are visible after filtering **/
> private EventDetails[] mFilteredEvents = EMPTY_LIST;
> /** list of events that are buffered for processing **/
> private final List mPendingEvents = new ArrayList();
> /** indicates whether event collection is paused to the UI **/
> private boolean mPaused = false;
>
> /** filter for the thread **/
> private String mThreadFilter = "";
> /** filter for the message **/
> private String mMessageFilter = "";
> /** filter for the NDC **/
> private String mNDCFilter = "";
> /** filter for the category **/
> private String mCategoryFilter = "";
> /** filter for the priority **/
> private Priority mPriorityFilter = Priority.DEBUG;
>
>
> /**
> * Creates a new <code>MyTableModel</code> instance.
> *
> */
> MyTableModel() {
> final Thread t = new Thread(new Processor());
> t.setDaemon(true);
> t.start();
> }
>
>
>
>////////////////////////////////////////////////////////////////////////////
> // Table Methods
>
>////////////////////////////////////////////////////////////////////////////
>
> /** @see TableModel **/
> public int getRowCount() {
> synchronized (mLock) {
> return mFilteredEvents.length;
> }
> }
>
> /** @see TableModel **/
> public int getColumnCount() {
> // does not need to be synchronized
> return COL_NAMES.length;
> }
>
> /** @see TableModel **/
> public String getColumnName(int aCol) {
> // does not need to be synchronized
> return COL_NAMES[aCol];
> }
>
> /** @see TableModel **/
> public Class getColumnClass(int aCol) {
> // does not need to be synchronized
> return (aCol == 2) ? Boolean.class : Object.class;
> }
>
> /** @see TableModel **/
> public Object getValueAt(int aRow, int aCol) {
> synchronized (mLock) {
> final EventDetails event = mFilteredEvents[aRow];
>
> if (aCol == 0) {
> return DATE_FORMATTER.format(new
> Date(event.getTimeStamp()));
> } else if (aCol == 1) {
> return event.getPriority();
> } else if (aCol == 2) {
> return (event.getThrowableStrRep() == null)
> ? Boolean.FALSE : Boolean.TRUE;
> } else if (aCol == 3) {
> return event.getCategoryName();
> } else if (aCol == 4) {
> return event.getNDC();
> }
> return event.getMessage();
> }
> }
>
>
>////////////////////////////////////////////////////////////////////////////
> // Public Methods
>
>////////////////////////////////////////////////////////////////////////////
>
> /**
> * Sets the priority to filter events on. Only events of equal or
> higher
> * property are now displayed.
> *
> * @param aPriority the priority to filter on
> */
> public void setPriorityFilter(Priority aPriority) {
> synchronized (mLock) {
> mPriorityFilter = aPriority;
> updateFilteredEvents(false);
> }
> }
>
> /**
> * Set the filter for the thread field.
> *
> * @param aStr the string to match
> */
> public void setThreadFilter(String aStr) {
> synchronized (mLock) {
> mThreadFilter = aStr.trim();
> updateFilteredEvents(false);
> }
> }
>
> /**
> * Set the filter for the message field.
> *
> * @param aStr the string to match
> */
> public void setMessageFilter(String aStr) {
> synchronized (mLock) {
> mMessageFilter = aStr.trim();
> updateFilteredEvents(false);
> }
> }
>
> /**
> * Set the filter for the NDC field.
> *
> * @param aStr the string to match
> */
> public void setNDCFilter(String aStr) {
> synchronized (mLock) {
> mNDCFilter = aStr.trim();
> updateFilteredEvents(false);
> }
> }
>
> /**
> * Set the filter for the category field.
> *
> * @param aStr the string to match
> */
> public void setCategoryFilter(String aStr) {
> synchronized (mLock) {
> mCategoryFilter = aStr.trim();
> updateFilteredEvents(false);
> }
> }
>
> /**
> * Add an event to the list.
> *
> * @param aEvent a <code>EventDetails</code> value
> */
> public void addEvent(EventDetails aEvent) {
> synchronized (mLock) {
> mPendingEvents.add(aEvent);
> }
> }
>
> /**
> * Clear the list of all events.
> */
> public void clear() {
> synchronized (mLock) {
> mAllEvents.clear();
> mFilteredEvents = new EventDetails[0];
> mPendingEvents.clear();
> fireTableDataChanged();
> }
> }
>
> /** Toggle whether collecting events **/
> public void toggle() {
> synchronized (mLock) {
> mPaused = !mPaused;
> }
> }
>
> /** @return whether currently paused collecting events **/
> public boolean isPaused() {
> synchronized (mLock) {
> return mPaused;
> }
> }
>
> /**
> * Get the throwable information at a specified row in the filtered
> events.
> *
> * @param aRow the row index of the event
> * @return the throwable information
> */
> public EventDetails getEventDetails(int aRow) {
> synchronized (mLock) {
> return mFilteredEvents[aRow];
> }
> }
>
>
>////////////////////////////////////////////////////////////////////////////
> // Private methods
>
>////////////////////////////////////////////////////////////////////////////
>
> /**
> * Update the filtered events data structure.
> * @param aInsertedToFront indicates whether events were added to
> front of
> * the events. If true, then the current first event must
> still exist
> * in the list after the filter is applied.
> */
> private void updateFilteredEvents(boolean aInsertedToFront) {
> final List filtered = new ArrayList();
> final Iterator it = mAllEvents.iterator();
> while (it.hasNext()) {
> final EventDetails event = (EventDetails) it.next();
> if (matchFilter(event)) {
> filtered.add(event);
> }
> }
>
> final EventDetails lastFirst = (mFilteredEvents.length == 0)
> ? null
> : mFilteredEvents[0];
> mFilteredEvents = (EventDetails[]) filtered.toArray(EMPTY_LIST);
>
> if (aInsertedToFront && (lastFirst != null)) {
> final int index = filtered.indexOf(lastFirst);
> if (index < 1) {
> LOG.warn("In strange state");
> fireTableDataChanged();
> } else {
> fireTableRowsInserted(0, index - 1);
> }
> } else {
> fireTableDataChanged();
> }
> }
>
> /**
> * Returns whether an event matches the filters.
> *
> * @param aEvent the event to check for a match
> * @return whether the event matches
> */
> private boolean matchFilter(EventDetails aEvent) {
> if (aEvent.getPriority().isGreaterOrEqual(mPriorityFilter) &&
> (aEvent.getThreadName().indexOf(mThreadFilter) >= 0) &&
> (aEvent.getCategoryName().indexOf(mCategoryFilter) >= 0) &&
> ((mNDCFilter.length() == 0) ||
> ((aEvent.getNDC() != null) &&
> (aEvent.getNDC().indexOf(mNDCFilter) >= 0))))
> {
> final String rm = aEvent.getMessage();
> if (rm == null) {
> // only match if we have not filtering in place
> return (mMessageFilter.length() == 0);
> } else {
> return (rm.indexOf(mMessageFilter) >= 0);
> }
> }
>
> return false; // by default not match
> }
> }
>
>
>
> 1.1
> jakarta-log4j/src/java/org/apache/log4j/chainsaw/Main.java
>
> Index: Main.java
> ===================================================================
> /*
> * Copyright (C) The Apache Software Foundation. All rights reserved.
> *
> * This software is published under the terms of the Apache Software
> * License version 1.1, a copy of which has been included with this
> * distribution in the LICENSE.txt file. */
> package org.apache.log4j.chainsaw;
>
> import java.awt.BorderLayout;
> import java.awt.Dimension;
> import java.awt.event.WindowAdapter;
> import java.awt.event.WindowEvent;
> import java.io.IOException;
> import java.util.Properties;
> import javax.swing.BorderFactory;
> import javax.swing.JFrame;
> import javax.swing.JMenu;
> import javax.swing.JMenuBar;
> import javax.swing.JMenuItem;
> import javax.swing.JOptionPane;
> import javax.swing.JPanel;
> import javax.swing.JScrollPane;
> import javax.swing.JSplitPane;
> import javax.swing.JTable;
> import javax.swing.ListSelectionModel;
> import org.apache.log4j.Category;
> import org.apache.log4j.PropertyConfigurator;
>
> /**
> * The main application.
> *
> * @author <a href="mailto:oliver@puppycrawl.com">Oliver Burn</a>
> */
> public class Main
> extends JFrame
> {
> /** the default port number to listen on **/
> private static final int DEFAULT_PORT = 4445;
>
> /** name of property for port name **/
> public static final String PORT_PROP_NAME = "chainsaw.port";
>
> /** use to log messages **/
> private static final Category LOG = Category.getInstance(Main.class);
>
>
> /**
> * Creates a new <code>Main</code> instance.
> */
> private Main() {
> super("CHAINSAW - Log4J Log Viewer");
> // create the all important model
> final MyTableModel model = new MyTableModel();
>
> //Create the menu bar.
> final JMenuBar menuBar = new JMenuBar();
> setJMenuBar(menuBar);
> final JMenu menu = new JMenu("File");
> menuBar.add(menu);
>
> try {
> final LoadXMLAction lxa = new LoadXMLAction(this, model);
> final JMenuItem loadMenuItem = new JMenuItem("Load file...");
> menu.add(loadMenuItem);
> loadMenuItem.addActionListener(lxa);
> } catch (Exception e) {
> LOG.info("Unable to create the action to load XML files", e);
> JOptionPane.showMessageDialog(
> this,
> "Unable to create a XML parser - unable to load XML
> events.",
> "CHAINSAW",
> JOptionPane.ERROR_MESSAGE);
> }
>
> final JMenuItem exitMenuItem = new JMenuItem("Exit");
> menu.add(exitMenuItem);
> exitMenuItem.addActionListener(ExitAction.INSTANCE);
>
> // Add control panel
> final ControlPanel cp = new ControlPanel(model);
> getContentPane().add(cp, BorderLayout.NORTH);
>
> // Create the table
> final JTable table = new JTable(model);
> table.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
> final JScrollPane scrollPane = new JScrollPane(table);
> scrollPane.setBorder(BorderFactory.createTitledBorder("Events: "));
> scrollPane.setPreferredSize(new Dimension(900, 300));
>
> // Create the details
> final JPanel details = new DetailPanel(table, model);
> details.setPreferredSize(new Dimension(900, 300));
>
> // Add the table and stack trace into a splitter
> final JSplitPane jsp =
> new JSplitPane(JSplitPane.VERTICAL_SPLIT, scrollPane, details);
> getContentPane().add(jsp, BorderLayout.CENTER);
>
> addWindowListener(new WindowAdapter() {
> public void windowClosing(WindowEvent aEvent) {
> ExitAction.INSTANCE.actionPerformed(null);
> }
> });
>
> pack();
> setVisible(true);
>
> setupReceiver(model);
> }
>
> /**
> * Setup recieving messages.
> *
> * @param aModel a <code>MyTableModel</code> value
> */
> private void setupReceiver(MyTableModel aModel) {
> int port = DEFAULT_PORT;
> final String strRep = System.getProperty(PORT_PROP_NAME);
> if (strRep != null) {
> try {
> port = Integer.parseInt(strRep);
> } catch (NumberFormatException nfe) {
> LOG.fatal("Unable to parse " + PORT_PROP_NAME +
> " property with value " + strRep + ".");
> JOptionPane.showMessageDialog(
> this,
> "Unable to parse port number from '" + strRep +
> "', quitting.",
> "CHAINSAW",
> JOptionPane.ERROR_MESSAGE);
> System.exit(1);
> }
> }
>
> try {
> final LoggingReceiver lr = new LoggingReceiver(aModel, port);
> lr.start();
> } catch (IOException e) {
> LOG.fatal("Unable to connect to socket server, quiting", e);
> JOptionPane.showMessageDialog(
> this,
> "Unable to create socket on port " + port + ", quitting.",
> "CHAINSAW",
> JOptionPane.ERROR_MESSAGE);
> System.exit(1);
> }
> }
>
>
>
>////////////////////////////////////////////////////////////////////////////
> // static methods
>
>////////////////////////////////////////////////////////////////////////////
>
>
> /** initialise log4j **/
> private static void initLog4J() {
> final Properties props = new Properties();
> props.setProperty("log4j.rootCategory", "DEBUG, A1");
> props.setProperty("log4j.appender.A1",
> "org.apache.log4j.ConsoleAppender");
> props.setProperty("log4j.appender.A1.layout",
> "org.apache.log4j.TTCCLayout");
> PropertyConfigurator.configure(props);
> }
>
> /**
> * The main method.
> *
> * @param aArgs ignored
> */
> public static void main(String[] aArgs) {
> initLog4J();
> new Main();
> }
> }
>
>
>
> 1.1
> jakarta-log4j/src/java/org/apache/log4j/chainsaw/LoggingReceiver.java
>
> Index: LoggingReceiver.java
> ===================================================================
> /*
> * Copyright (C) The Apache Software Foundation. All rights reserved.
> *
> * This software is published under the terms of the Apache Software
> * License version 1.1, a copy of which has been included with this
> * distribution in the LICENSE.txt file. */
> package org.apache.log4j.chainsaw;
>
> import java.io.EOFException;
> import java.io.IOException;
> import java.io.ObjectInputStream;
> import java.net.ServerSocket;
> import java.net.Socket;
> import java.net.SocketException;
> import org.apache.log4j.Category;
> import org.apache.log4j.spi.LoggingEvent;
>
> /**
> * A daemon thread the processes connections from a
> * <code>org.apache.log4j.net.SocketAppender.html</code>.
> *
> * @author <a href="mailto:oliver@puppycrawl.com">Oliver Burn</a>
> */
> class LoggingReceiver
> extends Thread
> {
> /** used to log messages **/
> private static final Category LOG =
> Category.getInstance(LoggingReceiver.class);
>
> /**
> * Helper that actually processes a client connection. It receives
> events
> * and adds them to the supplied model.
> *
> * @author <a href="mailto:oliver@puppycrawl.com">Oliver Burn</a>
> */
> private class Slurper
> implements Runnable
> {
> /** socket connection to read events from **/
> private final Socket mClient;
>
> /**
> * Creates a new <code>Slurper</code> instance.
> *
> * @param aClient socket to receive events from
> */
> Slurper(Socket aClient) {
> mClient = aClient;
> }
>
> /** loops getting the events **/
> public void run() {
> LOG.debug("Starting to get data");
> try {
> final ObjectInputStream ois =
> new ObjectInputStream(mClient.getInputStream());
> while (true) {
> final LoggingEvent event = (LoggingEvent)
> ois.readObject();
> mModel.addEvent(new EventDetails(event));
> }
> } catch (EOFException e) {
> LOG.info("Reached EOF, closing connection");
> } catch (SocketException e) {
> LOG.info("Caught SocketException, closing connection");
> } catch (IOException e) {
> LOG.warn("Got IOException, closing connection", e);
> } catch (ClassNotFoundException e) {
> LOG.warn("Got ClassNotFoundException, closing
> connection", e);
> }
>
> try {
> mClient.close();
> } catch (IOException e) {
> LOG.warn("Error closing connection", e);
> }
> }
> }
>
> /** where to put the events **/
> private final MyTableModel mModel;
>
> /** server for listening for connections **/
> private final ServerSocket mSvrSock;
>
> /**
> * Creates a new <code>LoggingReceiver</code> instance.
> *
> * @param aModel model to place put received into
> * @param aPort port to listen on
> * @throws IOException if an error occurs
> */
> LoggingReceiver(MyTableModel aModel, int aPort)
> throws IOException
> {
> setDaemon(true);
> mModel = aModel;
> mSvrSock = new ServerSocket(aPort);
> }
>
> /** Listens for client connections **/
> public void run() {
> LOG.info("Thread started");
> try {
> while (true) {
> LOG.debug("Waiting for a connection");
> final Socket client = mSvrSock.accept();
> LOG.debug("Got a connection from " +
> client.getInetAddress().getHostName());
> final Thread t = new Thread(new Slurper(client));
> t.setDaemon(true);
> t.start();
> }
> } catch (IOException e) {
> LOG.error("Error in accepting connections, stopping.", e);
> }
> }
> }
>
>
>
> 1.1
> jakarta-log4j/src/java/org/apache/log4j/chainsaw/LoadXMLAction.java
>
> Index: LoadXMLAction.java
> ===================================================================
> /*
> * Copyright (C) The Apache Software Foundation. All rights reserved.
> *
> * This software is published under the terms of the Apache Software
> * License version 1.1, a copy of which has been included with this
> * distribution in the LICENSE.txt file. */
> package org.apache.log4j.chainsaw;
>
> import java.awt.event.ActionEvent;
> import java.io.File;
> import java.io.IOException;
> import java.io.StringReader;
> import javax.swing.AbstractAction;
> import javax.swing.JFileChooser;
> import javax.swing.JFrame;
> import javax.swing.JOptionPane;
> import javax.xml.parsers.ParserConfigurationException;
> import javax.xml.parsers.SAXParserFactory;
> import org.apache.log4j.Category;
> import org.xml.sax.InputSource;
> import org.xml.sax.SAXException;
> import org.xml.sax.XMLReader;
>
> /**
> * Encapsulates the action to load an XML file.
> *
> * @author <a href="mailto:oliver@puppycrawl.com">Oliver Burn</a>
> * @version 1.0
> */
> class LoadXMLAction
> extends AbstractAction
> {
> /** use to log messages **/
> private static final Category LOG =
> Category.getInstance(LoadXMLAction.class);
>
> /** the parent frame **/
> private final JFrame mParent;
>
> /**
> * the file chooser - configured to allow only the selection of a
> * single file.
> */
> private final JFileChooser mChooser = new JFileChooser();
> {
> mChooser.setMultiSelectionEnabled(false);
> mChooser.setFileSelectionMode(JFileChooser.FILES_ONLY);
> }
>
> /** parser to read XML files **/
> private final XMLReader mParser;
> /** the content handler **/
> private final XMLFileHandler mHandler;
>
>
> /**
> * Creates a new <code>LoadXMLAction</code> instance.
> *
> * @param aParent the parent frame
> * @param aModel the model to add events to
> * @exception SAXException if an error occurs
> * @throws ParserConfigurationException if an error occurs
> */
> LoadXMLAction(JFrame aParent, MyTableModel aModel)
> throws SAXException, ParserConfigurationException
> {
> mParent = aParent;
> mHandler = new XMLFileHandler(aModel);
> mParser =
> SAXParserFactory.newInstance().newSAXParser().getXMLReader();
> mParser.setContentHandler(mHandler);
> }
>
> /**
> * Prompts the user for a file to load events from.
> * @param aIgnore an <code>ActionEvent</code> value
> */
> public void actionPerformed(ActionEvent aIgnore) {
> LOG.info("load file called");
> if (mChooser.showOpenDialog(mParent) ==
> JFileChooser.APPROVE_OPTION) {
> LOG.info("Need to load a file");
> final File chosen = mChooser.getSelectedFile();
> LOG.info("loading the contents of " +
> chosen.getAbsolutePath());
> try {
> final int num = loadFile(chosen.getAbsolutePath());
> JOptionPane.showMessageDialog(
> mParent,
> "Loaded " + num + " events.",
> "CHAINSAW",
> JOptionPane.INFORMATION_MESSAGE);
> } catch (Exception e) {
> LOG.warn("caught an exception loading the file", e);
> JOptionPane.showMessageDialog(
> mParent,
> "Error parsing file - " + e.getMessage(),
> "CHAINSAW",
> JOptionPane.ERROR_MESSAGE);
> }
> }
> }
>
> /**
> * Loads the contents of file into the model
> *
> * @param aFile the file to extract events from
> * @return the number of events loaded
> * @throws SAXException if an error occurs
> * @throws IOException if an error occurs
> */
> private int loadFile(String aFile)
> throws SAXException, IOException
> {
> synchronized (mParser) {
> // Create a dummy document to parse the file
> final StringBuffer buf = new StringBuffer();
> buf.append("<?xml version=\"1.0\" standalone=\"yes\"?>\n");
> buf.append("<!DOCTYPE log4j:eventSet ");
> buf.append("[<!ENTITY data SYSTEM \"file:///");
> buf.append(aFile);
> buf.append("\">]>\n");
> buf.append("<log4j:eventSet xmlns:log4j=\"Claira\">\n");
> buf.append("&data;\n");
> buf.append("</log4j:eventSet>\n");
>
> final InputSource is =
> new InputSource(new StringReader(buf.toString()));
> mParser.parse(is);
> return mHandler.getNumEvents();
> }
> }
> }
>
>
>
> 1.1
> jakarta-log4j/src/java/org/apache/log4j/chainsaw/ExitAction.java
>
> Index: ExitAction.java
> ===================================================================
> /*
> * Copyright (C) The Apache Software Foundation. All rights reserved.
> *
> * This software is published under the terms of the Apache Software
> * License version 1.1, a copy of which has been included with this
> * distribution in the LICENSE.txt file. */
> package org.apache.log4j.chainsaw;
>
> import java.awt.event.ActionEvent;
> import javax.swing.AbstractAction;
> import org.apache.log4j.Category;
>
> /**
> * Encapsulates the action to exit.
> *
> * @author <a href="mailto:oliver@puppycrawl.com">Oliver Burn</a>
> * @version 1.0
> */
> class ExitAction
> extends AbstractAction
> {
> /** use to log messages **/
> private static final Category LOG =
> Category.getInstance(ExitAction.class);
> /** The instance to share **/
> public static final ExitAction INSTANCE = new ExitAction();
>
> /** Stop people creating instances **/
> private ExitAction() {}
>
> /**
> * Will shutdown the application.
> * @param aIgnore ignored
> */
> public void actionPerformed(ActionEvent aIgnore) {
> LOG.info("shutting down");
> System.exit(0);
> }
> }
>
>
>
> 1.1
> jakarta-log4j/src/java/org/apache/log4j/chainsaw/EventDetails.java
>
> Index: EventDetails.java
> ===================================================================
> /*
> * Copyright (C) The Apache Software Foundation. All rights reserved.
> *
> * This software is published under the terms of the Apache Software
> * License version 1.1, a copy of which has been included with this
> * distribution in the LICENSE.txt file. */
> package org.apache.log4j.chainsaw;
>
> import org.apache.log4j.Priority;
> import org.apache.log4j.spi.LoggingEvent;
>
> /**
> * Represents the details of a logging event. It is intended to
> overcome the
> * problem that a LoggingEvent cannot be constructed with purely fake data.
> *
> * @author <a href="mailto:oliver@puppycrawl.com">Oliver Burn</a>
> * @version 1.0
> */
> class EventDetails {
>
> /** the time of the event **/
> private final long mTimeStamp;
> /** the priority of the event **/
> private final Priority mPriority;
> /** the category of the event **/
> private final String mCategoryName;
> /** the NDC for the event **/
> private final String mNDC;
> /** the thread for the event **/
> private final String mThreadName;
> /** the msg for the event **/
> private final String mMessage;
> /** the throwable details the event **/
> private final String[] mThrowableStrRep;
> /** the location details for the event **/
> private final String mLocationDetails;
>
> /**
> * Creates a new <code>EventDetails</code> instance.
> * @param aTimeStamp a <code>long</code> value
> * @param aPriority a <code>Priority</code> value
> * @param aCategoryName a <code>String</code> value
> * @param aNDC a <code>String</code> value
> * @param aThreadName a <code>String</code> value
> * @param aMessage a <code>String</code> value
> * @param aThrowableStrRep a <code>String[]</code> value
> * @param aLocationDetails a <code>String</code> value
> */
> EventDetails(long aTimeStamp,
> Priority aPriority,
> String aCategoryName,
> String aNDC,
> String aThreadName,
> String aMessage,
> String[] aThrowableStrRep,
> String aLocationDetails)
> {
> mTimeStamp = aTimeStamp;
> mPriority = aPriority;
> mCategoryName = aCategoryName;
> mNDC = aNDC;
> mThreadName = aThreadName;
> mMessage = aMessage;
> mThrowableStrRep = aThrowableStrRep;
> mLocationDetails = aLocationDetails;
> }
>
> /**
> * Creates a new <code>EventDetails</code> instance.
> *
> * @param aEvent a <code>LoggingEvent</code> value
> */
> EventDetails(LoggingEvent aEvent) {
>
> this(aEvent.timeStamp,
> aEvent.level,
> aEvent.categoryName,
> aEvent.getNDC(),
> aEvent.getThreadName(),
> aEvent.getRenderedMessage(),
> aEvent.getThrowableStrRep(),
> (aEvent.getLocationInformation() == null)
> ? null : aEvent.getLocationInformation().fullInfo);
> }
>
> /** @see #mTimeStamp **/
> long getTimeStamp() {
> return mTimeStamp;
> }
>
> /** @see #mPriority **/
> Priority getPriority() {
> return mPriority;
> }
>
> /** @see #mCategoryName **/
> String getCategoryName() {
> return mCategoryName;
> }
>
> /** @see #mNDC **/
> String getNDC() {
> return mNDC;
> }
>
> /** @see #mThreadName **/
> String getThreadName() {
> return mThreadName;
> }
>
> /** @see #mMessage **/
> String getMessage() {
> return mMessage;
> }
>
> /** @see #mLocationDetails **/
> String getLocationDetails(){
> return mLocationDetails;
> }
>
> /** @see #mThrowableStrRep **/
> String[] getThrowableStrRep() {
> return mThrowableStrRep;
> }
> }
>
>
>
> 1.1
> jakarta-log4j/src/java/org/apache/log4j/chainsaw/DetailPanel.java
>
> Index: DetailPanel.java
> ===================================================================
> /*
> * Copyright (C) The Apache Software Foundation. All rights reserved.
> *
> * This software is published under the terms of the Apache Software
> * License version 1.1, a copy of which has been included with this
> * distribution in the LICENSE.txt file. */
> package org.apache.log4j.chainsaw;
>
> import java.awt.BorderLayout;
> import java.text.MessageFormat;
> import java.util.Date;
> import javax.swing.BorderFactory;
> import javax.swing.JEditorPane;
> import javax.swing.JPanel;
> import javax.swing.JScrollPane;
> import javax.swing.JTable;
> import javax.swing.ListSelectionModel;
> import javax.swing.event.ListSelectionEvent;
> import javax.swing.event.ListSelectionListener;
> import org.apache.log4j.Category;
>
> /**
> * A panel for showing a stack trace.
> *
> * @author <a href="mailto:oliver@puppycrawl.com">Oliver Burn</a>
> */
> class DetailPanel
> extends JPanel
> implements ListSelectionListener
> {
> /** used to log events **/
> private static final Category LOG =
> Category.getInstance(DetailPanel.class);
>
> /** used to format the logging event **/
> private static final MessageFormat FORMATTER = new MessageFormat(
> "<b>Time:</b> <code>{0,time,medium}</code>" +
> " <b>Priority:</b> <code>{1}</code>" +
> " <b>Thread:</b> <code>{2}</code>" +
> " <b>NDC:</b> <code>{3}</code>" +
> "<br><b>Category:</b> <code>{4}</code>" +
> "<br><b>Location:</b> <code>{5}</code>" +
> "<br><b>Message:</b>" +
> "<pre>{6}</pre>" +
> "<b>Throwable:</b>" +
> "<pre>{7}</pre>");
>
> /** the model for the data to render **/
> private final MyTableModel mModel;
> /** pane for rendering detail **/
> private final JEditorPane mDetails;
>
> /**
> * Creates a new <code>DetailPanel</code> instance.
> *
> * @param aTable the table to listen for selections on
> * @param aModel the model backing the table
> */
> DetailPanel(JTable aTable, final MyTableModel aModel) {
> mModel = aModel;
> setLayout(new BorderLayout());
> setBorder(BorderFactory.createTitledBorder("Details: "));
>
> mDetails = new JEditorPane();
> mDetails.setEditable(false);
> mDetails.setContentType("text/html");
> add(new JScrollPane(mDetails), BorderLayout.CENTER);
>
> final ListSelectionModel rowSM = aTable.getSelectionModel();
> rowSM.addListSelectionListener(this);
> }
>
> /** @see ListSelectionListener **/
> public void valueChanged(ListSelectionEvent aEvent) {
> //Ignore extra messages.
> if (aEvent.getValueIsAdjusting()) {
> return;
> }
>
> final ListSelectionModel lsm = (ListSelectionModel)
> aEvent.getSource();
> if (lsm.isSelectionEmpty()) {
> mDetails.setText("Nothing selected");
> } else {
> final int selectedRow = lsm.getMinSelectionIndex();
> final EventDetails e = mModel.getEventDetails(selectedRow);
> final Object[] args =
> {
> new Date(e.getTimeStamp()),
> e.getPriority(),
> escape(e.getThreadName()),
> escape(e.getNDC()),
> escape(e.getCategoryName()),
> escape(e.getLocationDetails()),
> escape(e.getMessage()),
> escape(getThrowableStrRep(e))
> };
> mDetails.setText(FORMATTER.format(args));
> mDetails.setCaretPosition(0);
> }
> }
>
>
>////////////////////////////////////////////////////////////////////////////
> // Private methods
>
>////////////////////////////////////////////////////////////////////////////
>
> /**
> * Returns a string representation of a throwable.
> *
> * @param aEvent contains the throwable information
> * @return a <code>String</code> value
> */
> private static String getThrowableStrRep(EventDetails aEvent) {
> final String[] strs = aEvent.getThrowableStrRep();
> if (strs == null) {
> return null;
> }
>
> final StringBuffer sb = new StringBuffer();
> for (int i = 0; i < strs.length; i++) {
> sb.append(strs[i]).append("\n");
> }
>
> return sb.toString();
> }
>
> /**
> * Escape <, > & and " as their entities. It is very
> * dumb about & handling.
> * @param aStr the String to escape.
> * @return the escaped String
> */
> private String escape(String aStr) {
> if (aStr == null) {
> return null;
> }
>
> final StringBuffer buf = new StringBuffer();
> for (int i = 0; i < aStr.length(); i++) {
> char c = aStr.charAt(i);
> switch (c) {
> case '<':
> buf.append("<");
> break;
> case '>':
> buf.append(">");
> break;
> case '\"':
> buf.append(""");
> break;
> case '&':
> buf.append("&");
> break;
> default:
> buf.append(c);
> break;
> }
> }
> return buf.toString();
> }
> }
>
>
>
> 1.1
> jakarta-log4j/src/java/org/apache/log4j/chainsaw/ControlPanel.java
>
> Index: ControlPanel.java
> ===================================================================
> /*
> * Copyright (C) The Apache Software Foundation. All rights reserved.
> *
> * This software is published under the terms of the Apache Software
> * License version 1.1, a copy of which has been included with this
> * distribution in the LICENSE.txt file. */
> package org.apache.log4j.chainsaw;
>
> import java.awt.GridBagConstraints;
> import java.awt.GridBagLayout;
> import java.awt.event.ActionEvent;
> import java.awt.event.ActionListener;
> import javax.swing.BorderFactory;
> import javax.swing.JButton;
> import javax.swing.JComboBox;
> import javax.swing.JLabel;
> import javax.swing.JPanel;
> import javax.swing.JTextField;
> import javax.swing.event.DocumentEvent;
> import javax.swing.event.DocumentListener;
> import org.apache.log4j.Category;
> import org.apache.log4j.Priority;
>
> /**
> * Represents the controls for filtering, pausing, exiting, etc.
> *
> * @author <a href="mailto:oliver@puppycrawl.com">Oliver Burn</a>
> */
> class ControlPanel
> extends JPanel
> {
> /** use the log messages **/
> private static final Category LOG =
> Category.getInstance(ControlPanel.class);
>
> /**
> * Creates a new <code>ControlPanel</code> instance.
> *
> * @param aModel the model to control
> */
> ControlPanel(final MyTableModel aModel) {
> setBorder(BorderFactory.createTitledBorder("Controls: "));
> final GridBagLayout gridbag = new GridBagLayout();
> final GridBagConstraints c = new GridBagConstraints();
> setLayout(gridbag);
>
> // Pad everything
> c.ipadx = 5;
> c.ipady = 5;
>
> // Add the 1st column of labels
> c.gridx = 0;
> c.anchor = GridBagConstraints.EAST;
>
> c.gridy = 0;
> JLabel label = new JLabel("Filter Level:");
> gridbag.setConstraints(label, c);
> add(label);
>
> c.gridy++;
> label = new JLabel("Filter Thread:");
> gridbag.setConstraints(label, c);
> add(label);
>
> c.gridy++;
> label = new JLabel("Filter Category:");
> gridbag.setConstraints(label, c);
> add(label);
>
> c.gridy++;
> label = new JLabel("Filter NDC:");
> gridbag.setConstraints(label, c);
> add(label);
>
> c.gridy++;
> label = new JLabel("Filter Message:");
> gridbag.setConstraints(label, c);
> add(label);
>
> // Add the 2nd column of filters
> c.weightx = 1;
> //c.weighty = 1;
> c.gridx = 1;
> c.anchor = GridBagConstraints.WEST;
>
> c.gridy = 0;
> final Priority[] allPriorities =
> Priority.getAllPossiblePriorities();
> final JComboBox priorities = new JComboBox(allPriorities);
> final Priority lowest = allPriorities[allPriorities.length - 1];
> priorities.setSelectedItem(lowest);
> aModel.setPriorityFilter(lowest);
> gridbag.setConstraints(priorities, c);
> add(priorities);
> priorities.setEditable(false);
> priorities.addActionListener(new ActionListener() {
> public void actionPerformed(ActionEvent aEvent) {
> aModel.setPriorityFilter(
> (Priority) priorities.getSelectedItem());
> }
> });
>
>
> c.fill = GridBagConstraints.HORIZONTAL;
> c.gridy++;
> final JTextField threadField = new JTextField("");
> threadField.getDocument().addDocumentListener(new
> DocumentListener () {
> public void insertUpdate(DocumentEvent aEvent) {
> aModel.setThreadFilter(threadField.getText());
> }
> public void removeUpdate(DocumentEvent aEvente) {
> aModel.setThreadFilter(threadField.getText());
> }
> public void changedUpdate(DocumentEvent aEvent) {
> aModel.setThreadFilter(threadField.getText());
> }
> });
> gridbag.setConstraints(threadField, c);
> add(threadField);
>
> c.gridy++;
> final JTextField catField = new JTextField("");
> catField.getDocument().addDocumentListener(new DocumentListener
> () {
> public void insertUpdate(DocumentEvent aEvent) {
> aModel.setCategoryFilter(catField.getText());
> }
> public void removeUpdate(DocumentEvent aEvent) {
> aModel.setCategoryFilter(catField.getText());
> }
> public void changedUpdate(DocumentEvent aEvent) {
> aModel.setCategoryFilter(catField.getText());
> }
> });
> gridbag.setConstraints(catField, c);
> add(catField);
>
> c.gridy++;
> final JTextField ndcField = new JTextField("");
> ndcField.getDocument().addDocumentListener(new DocumentListener
> () {
> public void insertUpdate(DocumentEvent aEvent) {
> aModel.setNDCFilter(ndcField.getText());
> }
> public void removeUpdate(DocumentEvent aEvent) {
> aModel.setNDCFilter(ndcField.getText());
> }
> public void changedUpdate(DocumentEvent aEvent) {
> aModel.setNDCFilter(ndcField.getText());
> }
> });
> gridbag.setConstraints(ndcField, c);
> add(ndcField);
>
> c.gridy++;
> final JTextField msgField = new JTextField("");
> msgField.getDocument().addDocumentListener(new DocumentListener
> () {
> public void insertUpdate(DocumentEvent aEvent) {
> aModel.setMessageFilter(msgField.getText());
> }
> public void removeUpdate(DocumentEvent aEvent) {
> aModel.setMessageFilter(msgField.getText());
> }
> public void changedUpdate(DocumentEvent aEvent) {
> aModel.setMessageFilter(msgField.getText());
> }
> });
>
>
> gridbag.setConstraints(msgField, c);
> add(msgField);
>
> // Add the 3rd column of buttons
> c.weightx = 0;
> c.fill = GridBagConstraints.HORIZONTAL;
> c.anchor = GridBagConstraints.EAST;
> c.gridx = 2;
>
> c.gridy = 0;
> final JButton exitButton = new JButton("Exit");
> exitButton.setMnemonic('x');
> exitButton.addActionListener(ExitAction.INSTANCE);
> gridbag.setConstraints(exitButton, c);
> add(exitButton);
>
> c.gridy++;
> final JButton clearButton = new JButton("Clear");
> clearButton.setMnemonic('c');
> clearButton.addActionListener(new ActionListener() {
> public void actionPerformed(ActionEvent aEvent) {
> aModel.clear();
> }
> });
> gridbag.setConstraints(clearButton, c);
> add(clearButton);
>
> c.gridy++;
> final JButton toggleButton = new JButton("Pause");
> toggleButton.setMnemonic('p');
> toggleButton.addActionListener(new ActionListener() {
> public void actionPerformed(ActionEvent aEvent) {
> aModel.toggle();
> toggleButton.setText(
> aModel.isPaused() ? "Resume" : "Pause");
> }
> });
> gridbag.setConstraints(toggleButton, c);
> add(toggleButton);
> }
> }
>
>
>
>
>--
>To unsubscribe, e-mail: <ma...@jakarta.apache.org>
>For additional commands, e-mail: <ma...@jakarta.apache.org>
--
Ceki
My link of the month: http://java.sun.com/aboutJava/standardization/
--
To unsubscribe, e-mail: <ma...@jakarta.apache.org>
For additional commands, e-mail: <ma...@jakarta.apache.org>