/** * Copyright (C) 2004, 2005, 2006 Aelitis SAS, All rights Reserved * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details ( see the LICENSE file ). * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * * AELITIS, SAS au capital de 46,603.30 euros, * 8 Allee Lenotre, La Grille Royale, 78600 Le Mesnil le Roi, France. */ package org.gudy.azureus2.ui.swt.views; import java.io.PrintStream; import java.text.FieldPosition; import java.text.SimpleDateFormat; import java.util.ArrayList; import java.util.LinkedList; import java.util.regex.Pattern; import java.util.regex.PatternSyntaxException; import org.eclipse.swt.SWT; import org.eclipse.swt.custom.StyledText; import org.eclipse.swt.events.ModifyEvent; import org.eclipse.swt.events.ModifyListener; import org.eclipse.swt.events.SelectionAdapter; import org.eclipse.swt.events.SelectionEvent; import org.eclipse.swt.graphics.Color; import org.eclipse.swt.graphics.GC; import org.eclipse.swt.layout.GridData; import org.eclipse.swt.layout.GridLayout; import org.eclipse.swt.layout.RowLayout; import org.eclipse.swt.widgets.*; import org.gudy.azureus2.core3.config.ParameterListener; import org.gudy.azureus2.core3.internat.MessageText; import org.gudy.azureus2.core3.logging.*; import org.gudy.azureus2.core3.logging.impl.FileLogging; import org.gudy.azureus2.core3.util.AERunnable; import org.gudy.azureus2.ui.swt.Messages; import org.gudy.azureus2.ui.swt.Utils; import org.gudy.azureus2.ui.swt.mainwindow.Colors; import org.gudy.azureus2.ui.swt.plugins.UISWTView; import org.gudy.azureus2.ui.swt.plugins.UISWTViewEvent; import org.gudy.azureus2.ui.swt.pluginsimpl.UISWTViewCoreEventListener; /** * @author TuxPaper * * @since 2.3.0.5 */ public class LoggerView implements ILogEventListener, ParameterListener, UISWTViewCoreEventListener { public static final String VIEW_ID = "LoggerView"; //private final static LogIDs LOGID = LogIDs.GUI; private static final int COLOR_INFO = 0; private static final int COLOR_WARN = 1; private static final int COLOR_ERR = 2; private static Color[] colors = null; private static final int PREFERRED_LINES = 256; private static final int MAX_LINES = 1024 + PREFERRED_LINES; private static final SimpleDateFormat dateFormatter; private static final FieldPosition formatPos; public static final String MSGID_PREFIX = "ConsoleView"; private Display display; private Composite panel; private StyledText consoleText = null; private Button buttonAutoScroll = null; private Object[] filter = null; // LinkedList is better for removing entries when full private LinkedList<LogEvent> buffer = new LinkedList<LogEvent>(); private boolean bPaused = false; private boolean bRealtime = false; private boolean bEnabled = false; private boolean bAutoScroll = true; private Pattern inclusionFilter; private Pattern exclusionFilter; // List of components we don't log. // Array represents LogTypes (info, warning, error) private ArrayList[] ignoredComponents = new ArrayList[3]; private boolean stopOnNull = false; private UISWTView swtView; static { dateFormatter = new SimpleDateFormat("[HH:mm:ss.SSS] "); formatPos = new FieldPosition(0); } public LoggerView() { this(false); setEnabled(true); } public LoggerView(boolean stopOnNull) { for (int i = 0; i < ignoredComponents.length; i++) { ignoredComponents[i] = new ArrayList(); } this.stopOnNull = stopOnNull; } public LoggerView(java.util.List<? extends LogEvent> initialList) { this(); if (initialList != null) buffer.addAll(initialList); setEnabled(true); } private void initialize(Composite composite) { display = composite.getDisplay(); Colors.getInstance().addColorsChangedListener(this); parameterChanged("Color"); panel = new Composite(composite, SWT.NULL); GridLayout layout = new GridLayout(); layout.marginHeight = 0; layout.marginWidth = 0; layout.verticalSpacing = 2; layout.numColumns = 2; panel.setLayout(layout); GridData gd; consoleText = new StyledText(panel, SWT.READ_ONLY | SWT.V_SCROLL | SWT.H_SCROLL); gd = new GridData(GridData.FILL_BOTH); gd.horizontalSpan = 2; consoleText.setLayoutData(gd); // XXX This doesn't work well, but it's better than nothing consoleText.addListener(SWT.Resize, new Listener() { public void handleEvent(Event event) { GC gc = new GC(consoleText); int charWidth = gc.getFontMetrics().getAverageCharWidth(); gc.dispose(); int areaWidth = consoleText.getBounds().width; consoleText.setTabs(areaWidth / 6 / charWidth); } }); ScrollBar sb = consoleText.getVerticalBar(); sb.addSelectionListener(new SelectionAdapter() { public void widgetSelected(SelectionEvent e) { bAutoScroll = false; if (buttonAutoScroll != null && !buttonAutoScroll.isDisposed()) buttonAutoScroll.setSelection(false); } }); Composite cLeft = new Composite(panel, SWT.NULL); layout = new GridLayout(); layout.marginHeight = 0; layout.marginWidth = 0; layout.verticalSpacing = 1; cLeft.setLayout(layout); gd = new GridData(SWT.TOP, SWT.LEAD, false, false); cLeft.setLayoutData(gd); Button buttonPause = new Button(cLeft, SWT.CHECK); Messages.setLanguageText(buttonPause, "LoggerView.pause"); gd = new GridData(); buttonPause.setLayoutData(gd); buttonPause.addSelectionListener(new SelectionAdapter() { public void widgetSelected(SelectionEvent e) { if (e.widget == null || !(e.widget instanceof Button)) return; Button btn = (Button) e.widget; bPaused = btn.getSelection(); if (!bPaused && buffer != null) { refresh(); } } }); Button buttonRealtime = new Button(cLeft, SWT.CHECK); Messages.setLanguageText(buttonRealtime, "LoggerView.realtime"); gd = new GridData(); buttonRealtime.setLayoutData(gd); buttonRealtime.addSelectionListener(new SelectionAdapter() { public void widgetSelected(SelectionEvent e) { if (e.widget == null || !(e.widget instanceof Button)) return; Button btn = (Button) e.widget; bRealtime = btn.getSelection(); } }); buttonAutoScroll = new Button(cLeft, SWT.CHECK); Messages.setLanguageText(buttonAutoScroll, "LoggerView.autoscroll"); gd = new GridData(); buttonAutoScroll.setLayoutData(gd); buttonAutoScroll.addSelectionListener(new SelectionAdapter() { public void widgetSelected(SelectionEvent e) { if (e.widget == null || !(e.widget instanceof Button)) return; Button btn = (Button) e.widget; bAutoScroll = btn.getSelection(); } }); buttonAutoScroll.setSelection(true); Button buttonClear = new Button(cLeft, SWT.PUSH); Messages.setLanguageText(buttonClear, "LoggerView.clear"); gd = new GridData(); buttonClear.setLayoutData(gd); buttonClear.addSelectionListener(new SelectionAdapter() { public void widgetSelected(SelectionEvent e) { consoleText.setText(""); } }); /** FileLogging filter, consisting of a List of types (info, warning, error) * and a checkbox Table of component IDs. */ final String sFilterPrefix = "ConfigView.section.logging.filter"; Group gLogIDs = new Group(panel, SWT.NULL); Messages.setLanguageText(gLogIDs, "LoggerView.filter"); layout = new GridLayout(); layout.marginHeight = 0; layout.numColumns = 2; gLogIDs.setLayout(layout); gd = new GridData(); gLogIDs.setLayoutData(gd); Label label = new Label(gLogIDs, SWT.NONE); Messages.setLanguageText(label, "ConfigView.section.logging.level"); label.setLayoutData(new GridData()); final Label labelCatFilter = new Label(gLogIDs, SWT.NONE); labelCatFilter.setLayoutData(new GridData(GridData.HORIZONTAL_ALIGN_FILL)); final List listLogTypes = new List(gLogIDs, SWT.BORDER | SWT.SINGLE | SWT.V_SCROLL); gd = new GridData(SWT.NULL, SWT.BEGINNING, false, false); listLogTypes.setLayoutData(gd); final int[] logTypes = { LogEvent.LT_INFORMATION, LogEvent.LT_WARNING, LogEvent.LT_ERROR }; for (int i = 0; i < logTypes.length; i++) listLogTypes.add(MessageText.getString("ConfigView.section.logging.log" + i + "type")); listLogTypes.select(0); final LogIDs[] logIDs = FileLogging.configurableLOGIDs; //Arrays.sort(logIDs); Composite cChecksAndButtons = new Composite(gLogIDs, SWT.NULL); layout = new GridLayout(2, false); layout.marginHeight = 0; layout.marginWidth = 0; cChecksAndButtons.setLayout(layout); cChecksAndButtons.setLayoutData(new GridData()); final Composite cChecks = new Composite(cChecksAndButtons, SWT.NULL); RowLayout rowLayout = new RowLayout(SWT.VERTICAL); rowLayout.wrap = true; rowLayout.marginLeft = 0; rowLayout.marginRight = 0; rowLayout.marginTop = 0; rowLayout.marginBottom = 0; cChecks.setLayout(rowLayout); SelectionAdapter buttonClickListener = new SelectionAdapter() { public void widgetSelected(SelectionEvent e) { int index = listLogTypes.getSelectionIndex(); if (index < 0 || index >= logTypes.length) return; Button item = (Button) e.widget; if (item.getSelection()) ignoredComponents[index].remove(item.getData("LOGID")); else ignoredComponents[index].add(item.getData("LOGID")); } }; for (int i = 0; i < logIDs.length; i++) { Button btn = new Button(cChecks, SWT.CHECK); btn.setText(MessageText.getString(sFilterPrefix + "." + logIDs[i], logIDs[i].toString())); btn.setData("LOGID", logIDs[i]); btn.addSelectionListener(buttonClickListener); if (i == 0) { gd = new GridData(SWT.FILL, SWT.FILL, false, false, 1, 2); gd.heightHint = (btn.computeSize(SWT.DEFAULT, SWT.DEFAULT).y + 2) * 3; cChecks.setLayoutData(gd); } } // Update table when list selection changes listLogTypes.addSelectionListener(new SelectionAdapter() { public void widgetSelected(SelectionEvent e) { int index = listLogTypes.getSelectionIndex(); if (index < 0 || index >= logTypes.length) return; labelCatFilter.setText(MessageText.getString( "ConfigView.section.logging.showLogsFor", listLogTypes .getSelection())); Control[] items = cChecks.getChildren(); for (int i = 0; i < items.length; i++) { if (items[i] instanceof Button) { LogIDs ID = (LogIDs) items[i].getData("LOGID"); if (ID != null) { boolean checked = !ignoredComponents[index].contains(ID); ((Button) items[i]).setSelection(checked); } } } } }); listLogTypes.notifyListeners(SWT.Selection, null); Button btn; btn = new Button(cChecksAndButtons, SWT.PUSH); gd = new GridData(); btn.setLayoutData(gd); Messages.setLanguageText(btn, "LoggerView.filter.checkAll"); btn.addSelectionListener(new SelectionAdapter() { public void widgetSelected(SelectionEvent e) { int index = listLogTypes.getSelectionIndex(); Control[] items = cChecks.getChildren(); for (int i = 0; i < items.length; i++) { if (items[i] instanceof Button) { LogIDs ID = (LogIDs) items[i].getData("LOGID"); if (ID != null && ignoredComponents[index].contains(ID)) { ((Button) items[i]).setSelection(true); ignoredComponents[index].remove(ID); } } } } }); btn = new Button(cChecksAndButtons, SWT.PUSH); gd = new GridData(); btn.setLayoutData(gd); Messages.setLanguageText(btn, "LoggerView.filter.uncheckAll"); btn.addSelectionListener(new SelectionAdapter() { public void widgetSelected(SelectionEvent e) { int index = listLogTypes.getSelectionIndex(); Control[] items = cChecks.getChildren(); for (int i = 0; i < items.length; i++) { if (items[i] instanceof Button) { LogIDs ID = (LogIDs) items[i].getData("LOGID"); if (ID != null && !ignoredComponents[index].contains(ID)) { ((Button) items[i]).setSelection(false); ignoredComponents[index].add(ID); } } } } }); Composite cBottom = new Composite(panel, SWT.NONE); gd = new GridData(SWT.FILL, SWT.FILL, true, false); gd.horizontalSpan = 2; cBottom.setLayoutData(gd); cBottom.setLayout(new GridLayout(2, false)); label = new Label(cBottom, SWT.NONE); label.setLayoutData(new GridData()); Messages.setLanguageText(label, "LoggerView.includeOnly"); final Text inclText = new Text(cBottom, SWT.BORDER); gd = new GridData(); gd.widthHint = 200; inclText.setLayoutData(gd); inclText.addModifyListener(new ModifyListener() { public void modifyText(ModifyEvent e) { String newExpression = inclText.getText(); if (newExpression.length() == 0) inclusionFilter = null; else { try { inclusionFilter = Pattern.compile(newExpression, Pattern.CASE_INSENSITIVE); inclText.setBackground(null); } catch (PatternSyntaxException e1) { inclText.setBackground(Colors.colorErrorBG); } } } }); label = new Label(cBottom, SWT.NONE); label.setLayoutData(new GridData()); Messages.setLanguageText(label, "LoggerView.excludeAll"); final Text exclText = new Text(cBottom, SWT.BORDER); gd = new GridData(); gd.widthHint = 200; exclText.setLayoutData(gd); exclText.addModifyListener(new ModifyListener() { public void modifyText(ModifyEvent e) { String newExpression = exclText.getText(); if (newExpression.length() == 0) exclusionFilter = null; else { try { exclusionFilter = Pattern.compile(newExpression, Pattern.CASE_INSENSITIVE); exclText.setBackground(null); } catch (PatternSyntaxException e1) { exclText.setBackground(Colors.colorErrorBG); } } } }); if (!Logger.isEnabled()) { consoleText.setText(MessageText.getString("LoggerView.loggingDisabled") + "\n"); } } private Composite getComposite() { return panel; } private void refresh() { if (bPaused) return; synchronized (buffer) { if (consoleText == null || consoleText.isDisposed()) return; for (int i = 0; i < buffer.size(); i++) { try { LogEvent event = buffer.get(i); int nbLinesBefore = consoleText.getLineCount(); if (nbLinesBefore > MAX_LINES) { consoleText.replaceTextRange(0, consoleText.getOffsetAtLine(PREFERRED_LINES), ""); nbLinesBefore = consoleText.getLineCount(); } final StringBuffer buf = new StringBuffer(); buf.append('\n'); dateFormatter.format(event.timeStamp, buf, formatPos); buf.append("{").append(event.logID).append("} "); buf.append(event.text); if (event.relatedTo != null) { buf.append("; \t| "); for (int j = 0; j < event.relatedTo.length; j++) { Object obj = event.relatedTo[j]; if (j > 0) buf.append("; "); if (obj instanceof LogRelation) { buf.append(((LogRelation) obj).getRelationText()); } else if (obj != null) { buf.append(obj.getClass().getName()).append(": '").append( obj.toString()).append("'"); } } } String toAppend = buf.toString(); if((inclusionFilter != null && !inclusionFilter.matcher(toAppend).find()) || (exclusionFilter != null && exclusionFilter.matcher(toAppend).find())) continue; consoleText.append(toAppend); int nbLinesNow = consoleText.getLineCount(); int colorIdx = -1; if (event.entryType == LogEvent.LT_INFORMATION) colorIdx = COLOR_INFO; else if (event.entryType == LogEvent.LT_WARNING) colorIdx = COLOR_WARN; else if (event.entryType == LogEvent.LT_ERROR) colorIdx = COLOR_ERR; if (colors != null && colorIdx >= 0) consoleText.setLineBackground(nbLinesBefore, nbLinesNow - nbLinesBefore, colors[colorIdx]); } catch (Exception e) { // don't send it to log, we might be feeding ourselves PrintStream ps = Logger.getOldStdErr(); if (ps != null) { ps.println("Error writing event to console:"); e.printStackTrace(ps); } } } buffer.clear(); if (bAutoScroll) consoleText.setTopIndex(consoleText.getLineCount()); } } private void delete() { Logger.removeListener(this); if (panel != null && !panel.isDisposed()) panel.dispose(); Colors.getInstance().removeColorsChangedListener(this); } private String getFullTitle() { return MessageText.getString("ConsoleView.title.full"); } // @see org.gudy.azureus2.core3.logging.ILogEventListener#log(org.gudy.azureus2.core3.logging.LogEvent) public synchronized void log(final LogEvent event) { if (display == null || display.isDisposed()) return; if (ignoredComponents[logTypeToIndex(event.entryType)].contains(event.logID)) return; // Always display STDERR messages, as they may relate to the filter boolean bMatch = (event.logID == LogIDs.STDERR || filter == null); if (!bMatch && event.relatedTo != null) { for (int i = 0; !bMatch && i < event.relatedTo.length; i++) { Object obj = event.relatedTo[i]; if (obj == null) continue; for (int j = 0; !bMatch && j < filter.length; j++) { if (obj instanceof LogRelation) { //System.err.println(obj.getClass().getSimpleName() + " is Logrelation"); Object newObj = ((LogRelation) obj).queryForClass(filter[j] .getClass()); if (newObj != null) obj = newObj; } //System.err.println(obj.getClass().getName() + " matches " + filter[j].getClass().getSimpleName() + "?"); if (obj == filter[j]) bMatch = true; } // for filter } // for relatedTo } if (bMatch) { synchronized (buffer) { if (buffer.size() >= 200) buffer.removeFirst(); buffer.add(event); } if (bRealtime && !bPaused) { Utils.execSWTThread(new AERunnable() { public void runSupport() { refresh(); } }); } } } public void setFilter(Object[] _filter) { synchronized( this ){ filter = _filter; } clearConsole(); } private void clearConsole() { if (display == null || display.isDisposed()) return; Utils.execSWTThread(new AERunnable() { public void runSupport() { if (consoleText != null && !consoleText.isDisposed()) { consoleText.setText(""); } } }); } public void setEnabled(boolean on) { if (bEnabled == on) return; bEnabled = on; if (on) Logger.addListener(this); else Logger.removeListener(this); } /* (non-Javadoc) * @see org.gudy.azureus2.plugins.PluginView#getPluginViewName() */ public String getPluginViewName() { return "Console"; } // TODO: Support multiple selection private void dataSourceChanged(Object newDataSource) { if (newDataSource == null) { if (stopOnNull) { setEnabled(false); return; } setFilter(null); } else if (newDataSource instanceof Object[]) { setFilter((Object[]) newDataSource); } else if (newDataSource instanceof Boolean) { stopOnNull = ((Boolean) newDataSource); return; } else { setFilter(new Object[] { newDataSource }); } setEnabled(true); } private int logTypeToIndex(int entryType) { switch (entryType) { case LogEvent.LT_INFORMATION: return 0; case LogEvent.LT_WARNING: return 1; case LogEvent.LT_ERROR: return 2; } return 0; } /* private int indexToLogType(int index) { switch (index) { case 0: return LogEvent.LT_INFORMATION; case 1: return LogEvent.LT_WARNING; case 2: return LogEvent.LT_ERROR; } return LogEvent.LT_INFORMATION; } */ public void parameterChanged(String parameterName) { if (parameterName.startsWith("Color")) { Utils.execSWTThread(new AERunnable() { public void runSupport() { if (display == null || display.isDisposed()) return; if (colors == null) colors = new Color[3]; final Color[] newColors = { Colors.blues[Colors.BLUES_MIDLIGHT], Colors.colorWarning, Colors.red_ConsoleView }; boolean bColorChanged = false; for (int i = 0; i < newColors.length; i++) { if (colors[i] == null || colors[i].isDisposed()) { colors[i] = newColors[i]; bColorChanged = true; } } if (bColorChanged && consoleText != null) { // remove color String text = consoleText.getText(); consoleText.setText(text); } } }); } } public boolean eventOccurred(UISWTViewEvent event) { switch (event.getType()) { case UISWTViewEvent.TYPE_CREATE: swtView = (UISWTView)event.getData(); swtView.setTitle(getFullTitle()); break; case UISWTViewEvent.TYPE_DESTROY: delete(); break; case UISWTViewEvent.TYPE_INITIALIZE: initialize((Composite)event.getData()); break; case UISWTViewEvent.TYPE_LANGUAGEUPDATE: Messages.updateLanguageForControl(getComposite()); swtView.setTitle(getFullTitle()); break; case UISWTViewEvent.TYPE_DATASOURCE_CHANGED: dataSourceChanged(event.getData()); break; case UISWTViewEvent.TYPE_FOCUSGAINED: break; case UISWTViewEvent.TYPE_REFRESH: refresh(); break; } return true; } }