package com.revolsys.swing.logging;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.sql.Timestamp;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import javax.swing.JTable;
import javax.swing.SortOrder;
import javax.swing.SwingUtilities;
import org.apache.log4j.Level;
import org.apache.log4j.Logger;
import org.apache.log4j.spi.LoggingEvent;
import org.jdesktop.swingx.plaf.basic.core.BasicTransferable;
import org.jdesktop.swingx.table.TableColumnExt;
import com.revolsys.swing.Icons;
import com.revolsys.swing.TabbedPane;
import com.revolsys.swing.dnd.ClipboardUtil;
import com.revolsys.swing.menu.BaseJPopupMenu;
import com.revolsys.swing.menu.MenuFactory;
import com.revolsys.swing.parallel.Invoke;
import com.revolsys.swing.table.AbstractTableModel;
import com.revolsys.swing.table.BaseJTable;
import com.revolsys.swing.table.TablePanel;
import com.revolsys.util.Property;
public class Log4jTableModel extends AbstractTableModel {
private static final List<String> COLUMN_NAMES = Arrays.asList("Time", "Level", "Logger Name",
"Message");
private static final long serialVersionUID = 1L;
public static void addNewTabPane(final TabbedPane tabs) {
final TablePanel panel = newPanel();
final Log4jTableModel tableModel = panel.getTableModel();
final int tabIndex = tabs.getTabCount();
tabs.addTab(null, Icons.getIcon("error"), panel);
final Log4jTabLabel tabLabel = new Log4jTabLabel(tabs, tableModel);
tabs.setTabComponentAt(tabIndex, tabLabel);
tabs.setSelectedIndex(tabIndex);
}
public static TablePanel newPanel() {
final BaseJTable table = newTable();
return new TablePanel(table);
}
public static BaseJTable newTable() {
final Log4jTableModel model = new Log4jTableModel();
final BaseJTable table = new BaseJTable(model);
table.setAutoResizeMode(JTable.AUTO_RESIZE_LAST_COLUMN);
for (int i = 0; i < model.getColumnCount(); i++) {
final TableColumnExt column = table.getColumnExt(i);
if (i == 0) {
column.setMinWidth(180);
column.setPreferredWidth(180);
column.setMaxWidth(180);
} else if (i == 1) {
column.setMinWidth(80);
column.setPreferredWidth(80);
column.setMaxWidth(80);
} else if (i == 2) {
column.setMinWidth(200);
column.setPreferredWidth(400);
} else if (i == 3) {
column.setMinWidth(200);
column.setPreferredWidth(800);
}
}
table.setSortOrder(0, SortOrder.DESCENDING);
table.addMouseListener(new MouseAdapter() {
@Override
public void mouseClicked(final MouseEvent e) {
if (SwingUtilities.isLeftMouseButton(e) && e.getClickCount() == 2) {
int row = table.rowAtPoint(e.getPoint());
row = table.convertRowIndexToModel(row);
if (row != -1) {
final LoggingEvent loggingEvent = model.getLoggingEvent(row);
LoggingEventPanel.showDialog(table, loggingEvent);
}
}
}
});
return table;
}
private final ListLog4jAppender appender = new ListLog4jAppender(this);
private List<LoggingEvent> rows = new ArrayList<>();
private boolean hasNewErrors = false;
public Log4jTableModel() {
Logger.getRootLogger().addAppender(this.appender);
final MenuFactory menu = getMenu();
menu.addMenuItem("all", "Delete all messages", "delete", this::clear);
addMenuItem("selected", "Delete selected messages", "delete", (final BaseJTable table) -> {
int count = 0;
final int[] selectedRows = table.getSelectedRowsInModel();
for (final int row : selectedRows) {
final int rowIndex = row - count;
this.rows.remove(rowIndex);
count++;
}
this.hasNewErrors = false;
fireTableDataChanged();
});
addMenuItem("message", "Delete message", "delete", this::removeLoggingEvent);
addMenuItem("message", "Copy message", "page_copy", this::copyLoggingEvent);
}
public void addLoggingEvent(final LoggingEvent event) {
if (event != null) {
Invoke.later(() -> {
if (event.getLevel().isGreaterOrEqual(Level.ERROR)) {
this.hasNewErrors = true;
}
this.rows.add(event);
while (this.rows.size() > 99) {
this.rows.remove(0);
}
fireTableDataChanged();
});
}
}
public void clear() {
Invoke.later(() -> {
this.rows.clear();
this.hasNewErrors = false;
fireTableDataChanged();
});
}
public void clearHasNewErrors() {
Invoke.later(() -> {
this.hasNewErrors = false;
final JTable table = getTable();
table.repaint();
});
}
public void copyLoggingEvent(final int index) {
final LoggingEvent event = getLoggingEvent(index);
final StringBuilder plain = new StringBuilder();
final StringBuilder html = new StringBuilder();
final Object message = event.getMessage();
if (message != null) {
plain.append(message);
html.append("<b>");
html.append(message);
html.append("</b>");
}
final String stackTrace = LoggingEventPanel.getStackTrace(event);
if (Property.hasValue(stackTrace)) {
if (plain.length() > 0) {
plain.append("\n");
}
plain.append(stackTrace);
html.append("<pre>");
html.append(stackTrace);
html.append("</pre>");
}
final BasicTransferable transferable = new BasicTransferable(plain.toString(), html.toString());
ClipboardUtil.setContents(transferable);
}
@Override
protected void finalize() throws Throwable {
super.finalize();
Logger.getRootLogger().removeAppender(this.appender);
}
@Override
public int getColumnCount() {
return COLUMN_NAMES.size();
}
@Override
public String getColumnName(final int column) {
return COLUMN_NAMES.get(column);
}
public LoggingEvent getLoggingEvent(final int rowIndex) {
try {
if (rowIndex < this.rows.size()) {
return this.rows.get(rowIndex);
}
} catch (final Throwable e) {
}
return null;
}
@Override
public BaseJPopupMenu getMenu(final int rowIndex, final int columnIndex) {
clearHasNewErrors();
return super.getMenu(rowIndex, columnIndex);
}
public int getMessageCount() {
return this.rows.size();
}
@Override
public int getRowCount() {
return this.rows.size();
}
@Override
public Object getValueAt(final int rowIndex, final int columnIndex) {
final LoggingEvent event = getLoggingEvent(rowIndex);
if (event == null) {
return null;
} else {
switch (columnIndex) {
case 0:
final long time = event.getTimeStamp();
final Timestamp timestamp = new Timestamp(time);
return timestamp;
case 1:
return event.getLevel();
case 2:
return event.getLoggerName();
case 3:
return event.getMessage();
default:
return null;
}
}
}
public boolean isHasMessages() {
return !this.rows.isEmpty();
}
public boolean isHasNewErrors() {
return this.hasNewErrors;
}
public void removeLoggingEvent(final int index) {
Invoke.later(() -> {
this.rows.remove(index);
this.hasNewErrors = false;
fireTableDataChanged();
});
}
public void setRows(final List<LoggingEvent> rows) {
Invoke.later(() -> {
this.rows = new ArrayList<>(rows);
fireTableDataChanged();
});
}
}