org.apache.log4j.chainsaw.MyTableModel Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of reload4j Show documentation
Show all versions of reload4j Show documentation
Reload4j revives EOLed log4j 1.x
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
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.Logger;
/**
* Represents a list of EventDetails
objects that are sorted on
* logging time. Methods are provided to filter the events that are visible.
*
* @author Oliver Burn
*/
class MyTableModel extends AbstractTableModel {
/** used to log messages **/
private static final Logger LOG = Logger.getLogger(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 Oliver Burn
*/
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 MyTableModel
instance.
*
*/
MyTableModel() {
final Thread t = new Thread(new Processor());
t.setDaemon(true);
t.start();
}
////////////////////////////////////////////////////////////////////////////
// Table Methods
////////////////////////////////////////////////////////////////////////////
/** @see javax.swing.table.TableModel **/
public int getRowCount() {
synchronized (mLock) {
return mFilteredEvents.length;
}
}
/** @see javax.swing.table.TableModel **/
public int getColumnCount() {
// does not need to be synchronized
return COL_NAMES.length;
}
/** @see javax.swing.table.TableModel **/
public String getColumnName(int aCol) {
// does not need to be synchronized
return COL_NAMES[aCol];
}
/** @see javax.swing.table.TableModel **/
public Class getColumnClass(int aCol) {
// does not need to be synchronized
return (aCol == 2) ? Boolean.class : Object.class;
}
/** @see javax.swing.table.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 EventDetails
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 long start = System.currentTimeMillis();
final List filtered = new ArrayList();
final int size = mAllEvents.size();
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();
}
final long end = System.currentTimeMillis();
LOG.debug("Total time [ms]: " + (end - start) + " in update, size: " + size);
}
/**
* 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
}
}