diff options
author | Siva Velusamy <vsiva@google.com> | 2011-08-12 15:01:54 -0700 |
---|---|---|
committer | Siva Velusamy <vsiva@google.com> | 2011-08-12 15:04:17 -0700 |
commit | a58953374907a09015aab4c506e239cdee88e55b (patch) | |
tree | dd7b195335fbe5c341d0eec73e3e73cee52aa453 /ddms | |
parent | afc6435e658b46219b91193977cc501274bb8b3e (diff) | |
download | sdk-a58953374907a09015aab4c506e239cdee88e55b.zip sdk-a58953374907a09015aab4c506e239cdee88e55b.tar.gz sdk-a58953374907a09015aab4c506e239cdee88e55b.tar.bz2 |
Add support for live filter/search bar.
A search bar at the top of the logcat view provides a quick way to
search for messages matching a certain pattern. By default, words in
the query are searched in the logcat message text. Other fields such
as pid or tag can be searched by using a prefix such as pid: or tag:.
Change-Id: I3f9f83697f84c30b0371beedfba457480001d71a
Diffstat (limited to 'ddms')
6 files changed, 255 insertions, 34 deletions
diff --git a/ddms/libs/ddmuilib/src/com/android/ddmuilib/logcat/LogCatFilterSettings.java b/ddms/libs/ddmuilib/src/com/android/ddmuilib/logcat/LogCatFilterSettings.java index 7dba6d8..2b16897 100644 --- a/ddms/libs/ddmuilib/src/com/android/ddmuilib/logcat/LogCatFilterSettings.java +++ b/ddms/libs/ddmuilib/src/com/android/ddmuilib/logcat/LogCatFilterSettings.java @@ -17,22 +17,68 @@ package com.android.ddmuilib.logcat; import com.android.ddmlib.Log.LogLevel; +import java.util.ArrayList; +import java.util.List; + /** * Settings for a filter for logcat messages. */ public final class LogCatFilterSettings { + private static final String PID_KEYWORD = "pid:"; //$NON-NLS-1$ + private static final String TAG_KEYWORD = "tag:"; //$NON-NLS-1$ + private static final String TEXT_KEYWORD = "text:"; //$NON-NLS-1$ + private final String mName; private final String mTag; + private String mText; private final String mPid; private final LogLevel mLogLevel; - public LogCatFilterSettings(String name, String tag, String pid, LogLevel logLevel) { + public LogCatFilterSettings(String name, String tag, String text, String pid, + LogLevel logLevel) { mName = name; mTag = tag; + mText = text; mPid = pid; mLogLevel = logLevel; } + /** + * Construct a list of {@link LogCatFilterSettings} objects by decoding the query. + * @param query encoded search string. The query is simply a list of words (can be regexes) + * a user would type in a search bar. These words are searched for in the text field of + * each collected logcat message. To search in a different field, the word could be prefixed + * with a keyword corresponding to the field name. Currently, the following keywords are + * supported: "pid:", "tag:" and "text:". Invalid regexes are ignored. + * @param minLevel minimum log level to match + * @return list of filter settings that fully match the given query + */ + public static List<LogCatFilterSettings> fromString(String query, LogLevel minLevel) { + List<LogCatFilterSettings> filterSettings = new ArrayList<LogCatFilterSettings>(); + + for (String s : query.trim().split(" ")) { + String tag = ""; + String text = ""; + String pid = ""; + + if (s.startsWith(PID_KEYWORD)) { + pid = s.substring(PID_KEYWORD.length()); + } else if (s.startsWith(TAG_KEYWORD)) { + tag = s.substring(TAG_KEYWORD.length()); + } else { + if (s.startsWith(TEXT_KEYWORD)) { + text = s.substring(TEXT_KEYWORD.length()); + } else { + text = s; + } + } + filterSettings.add(new LogCatFilterSettings("livefilter-" + s, + tag, text, pid, minLevel)); + } + + return filterSettings; + } + public String getName() { return mName; } @@ -41,6 +87,10 @@ public final class LogCatFilterSettings { return mTag; } + public String getText() { + return mText; + } + public String getPidString() { return mPid; } diff --git a/ddms/libs/ddmuilib/src/com/android/ddmuilib/logcat/LogCatFilterSettingsDialog.java b/ddms/libs/ddmuilib/src/com/android/ddmuilib/logcat/LogCatFilterSettingsDialog.java index 52c0c0a..2487be1 100644 --- a/ddms/libs/ddmuilib/src/com/android/ddmuilib/logcat/LogCatFilterSettingsDialog.java +++ b/ddms/libs/ddmuilib/src/com/android/ddmuilib/logcat/LogCatFilterSettingsDialog.java @@ -48,11 +48,13 @@ public final class LogCatFilterSettingsDialog extends TitleAreaDialog { private String mFilterName; private String mTag; + private String mText; private String mPID; private String mLogLevel; private Text mFilterNameText; private Text mTagFilterText; + private Text mTextFilterText; private Text mPIDFilterText; private Combo mLogLevelCombo; private Button mOkButton; @@ -63,19 +65,22 @@ public final class LogCatFilterSettingsDialog extends TitleAreaDialog { */ public LogCatFilterSettingsDialog(Shell parentShell) { super(parentShell); - setDefaults("", "", "", LogLevel.VERBOSE); + setDefaults("", "", "", "", LogLevel.VERBOSE); } /** * Set the default values to show when the dialog is opened. * @param filterName name for the filter. * @param tag value for filter by tag + * @param text value for filter by text * @param pid value for filter by pid * @param level value for filter by log level */ - public void setDefaults(String filterName, String tag, String pid, LogLevel level) { + public void setDefaults(String filterName, String tag, String text, String pid, + LogLevel level) { mFilterName = filterName; mTag = tag; + mText = text; mPID = pid; mLogLevel = level.getStringValue(); } @@ -102,6 +107,11 @@ public final class LogCatFilterSettingsDialog extends TitleAreaDialog { mTagFilterText.setLayoutData(new GridData(GridData.FILL_HORIZONTAL)); mTagFilterText.setText(mTag); + createLabel(c, "by Log Message:"); + mTextFilterText = new Text(c, SWT.BORDER); + mTextFilterText.setLayoutData(new GridData(GridData.FILL_HORIZONTAL)); + mTextFilterText.setText(mText); + createLabel(c, "by PID:"); mPIDFilterText = new Text(c, SWT.BORDER); mPIDFilterText.setLayoutData(new GridData(GridData.FILL_HORIZONTAL)); @@ -122,6 +132,7 @@ public final class LogCatFilterSettingsDialog extends TitleAreaDialog { }; mFilterNameText.addModifyListener(m); mTagFilterText.addModifyListener(m); + mTextFilterText.addModifyListener(m); mPIDFilterText.addModifyListener(m); return c; @@ -188,6 +199,17 @@ public final class LogCatFilterSettingsDialog extends TitleAreaDialog { } } + /* text field must use a valid regex pattern */ + String messageText = mTextFilterText.getText().trim(); + if (!messageText.equals("")) { + try { + Pattern.compile(messageText); + } catch (PatternSyntaxException e) { + return new DialogStatus(false, + "Invalid regex used in text field: " + e.getMessage()); + } + } + return new DialogStatus(true, null); } @@ -211,6 +233,7 @@ public final class LogCatFilterSettingsDialog extends TitleAreaDialog { /* save values from the widgets before the shell is closed. */ mFilterName = mFilterNameText.getText(); mTag = mTagFilterText.getText(); + mText = mTextFilterText.getText(); mLogLevel = mLogLevelCombo.getText(); String pidText = mPIDFilterText.getText().trim(); @@ -238,6 +261,14 @@ public final class LogCatFilterSettingsDialog extends TitleAreaDialog { } /** + * Obtain the text regex to filter by. + * @return user provided tag regex, maybe empty. + */ + public String getText() { + return mText; + } + + /** * Obtain user provided PID to filter by. * @return user provided pid, maybe empty. */ diff --git a/ddms/libs/ddmuilib/src/com/android/ddmuilib/logcat/LogCatPanel.java b/ddms/libs/ddmuilib/src/com/android/ddmuilib/logcat/LogCatPanel.java index b923ebc..9d417df 100644 --- a/ddms/libs/ddmuilib/src/com/android/ddmuilib/logcat/LogCatPanel.java +++ b/ddms/libs/ddmuilib/src/com/android/ddmuilib/logcat/LogCatPanel.java @@ -26,10 +26,11 @@ import org.eclipse.jface.preference.IPreferenceStore; import org.eclipse.jface.util.IPropertyChangeListener; import org.eclipse.jface.util.PropertyChangeEvent; import org.eclipse.jface.viewers.TableViewer; -import org.eclipse.jface.viewers.ViewerFilter; import org.eclipse.jface.window.Window; import org.eclipse.swt.SWT; import org.eclipse.swt.custom.SashForm; +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.Font; @@ -120,10 +121,13 @@ public final class LogCatPanel extends SelectionDependentPanel mLogCatFilters = new ArrayList<LogCatFilterSettings>(); /* add a couple of filters by default */ + String tag = ""; + String text = ""; + String pid = ""; mLogCatFilters.add(new LogCatFilterSettings("All messages (no filters)", - "", "", LogLevel.VERBOSE)); + tag, text, pid, LogLevel.VERBOSE)); mLogCatFilters.add(new LogCatFilterSettings("Errors only", - "", "", LogLevel.ERROR)); + tag, text, pid, LogLevel.ERROR)); /* FIXME restore saved filters from prefStore */ } @@ -252,6 +256,7 @@ public final class LogCatPanel extends SelectionDependentPanel LogCatFilterSettings f = new LogCatFilterSettings(d.getFilterName().trim(), d.getTag().trim(), + d.getText().trim(), d.getPID().trim(), LogLevel.getByString(d.getLogLevel())); @@ -289,14 +294,15 @@ public final class LogCatPanel extends SelectionDependentPanel LogCatFilterSettingsDialog dialog = new LogCatFilterSettingsDialog( Display.getCurrent().getActiveShell()); - dialog.setDefaults(curFilter.getName(), curFilter.getTag(), curFilter.getPidString(), - curFilter.getLogLevel()); + dialog.setDefaults(curFilter.getName(), curFilter.getTag(), curFilter.getText(), + curFilter.getPidString(), curFilter.getLogLevel()); if (dialog.open() != Window.OK) { return; } LogCatFilterSettings f = new LogCatFilterSettings(dialog.getFilterName(), dialog.getTag(), + dialog.getText(), dialog.getPID(), LogLevel.getByString(dialog.getLogLevel())); mLogCatFilters.set(selectedIndex, f); @@ -348,11 +354,22 @@ public final class LogCatPanel extends SelectionDependentPanel mLiveFilterText = new Text(c, SWT.BORDER | SWT.SEARCH); mLiveFilterText.setLayoutData(new GridData(GridData.FILL_HORIZONTAL)); mLiveFilterText.setMessage(DEFAULT_SEARCH_MESSAGE); + mLiveFilterText.addModifyListener(new ModifyListener() { + public void modifyText(ModifyEvent arg0) { + updateAppliedFilters(); + } + }); mLiveFilterLevelCombo = new Combo(c, SWT.READ_ONLY | SWT.DROP_DOWN); mLiveFilterLevelCombo.setItems( LogCatFilterSettingsDialog.getLogLevels().toArray(new String[0])); mLiveFilterLevelCombo.select(0); + mLiveFilterLevelCombo.addSelectionListener(new SelectionAdapter() { + @Override + public void widgetSelected(SelectionEvent arg0) { + updateAppliedFilters(); + } + }); ToolBar toolBar = new ToolBar(c, SWT.FLAT); @@ -378,10 +395,6 @@ public final class LogCatPanel extends SelectionDependentPanel refreshLogCatTable(); } }); - - /* FIXME: Enable all the UI elements after adding support for user interaction with them. */ - mLiveFilterText.setEnabled(false); - mLiveFilterLevelCombo.setEnabled(false); } /** @@ -534,35 +547,52 @@ public final class LogCatPanel extends SelectionDependentPanel * Perform all necessary updates whenever a filter is selected (by user or programmatically). */ private void filterSelectionChanged() { - int idx = mFiltersTableViewer.getTable().getSelectionIndex(); - - updateFiltersToolBar(idx); - selectFilter(idx); + updateFiltersToolBar(); + updateAppliedFilters(); } - private void updateFiltersToolBar(int index) { - boolean en = true; - if (index == 0) { - en = false; - } + private int getSavedFilterIndex() { + return mFiltersTableViewer.getTable().getSelectionIndex(); + } - /* The default filter at index 0 can neither be edited or removed. */ + private void updateFiltersToolBar() { + /* The default filter at index 0 can neither be edited, nor removed. */ + boolean en = getSavedFilterIndex() != 0; mEditFilterToolItem.setEnabled(en); mDeleteFilterToolItem.setEnabled(en); } - private void selectFilter(int index) { - assert index > 0 && index < mLogCatFilters.size(); - - mViewer.setFilters(new ViewerFilter[] { - new LogCatViewerFilter(mLogCatFilters.get(index)), - }); + private void updateAppliedFilters() { + /* list of filters to apply = saved filter + live filters */ + List<LogCatViewerFilter> filters = new ArrayList<LogCatViewerFilter>(); + filters.add(getSelectedSavedFilter()); + filters.addAll(getCurrentLiveFilters()); + mViewer.setFilters(filters.toArray(new LogCatViewerFilter[filters.size()])); /* whenever filters are changed, the number of displayed logs changes * drastically. Display the latest log in such a situation. */ scrollToLatestLog(); } + private List<LogCatViewerFilter> getCurrentLiveFilters() { + List<LogCatViewerFilter> liveFilters = new ArrayList<LogCatViewerFilter>(); + + List<LogCatFilterSettings> liveFilterSettings = LogCatFilterSettings.fromString( + mLiveFilterText.getText(), /* current query */ + LogLevel.getByString(mLiveFilterLevelCombo.getText())); /* current log level */ + for (LogCatFilterSettings s : liveFilterSettings) { + liveFilters.add(new LogCatViewerFilter(s)); + } + + return liveFilters; + } + + private LogCatViewerFilter getSelectedSavedFilter() { + int index = getSavedFilterIndex(); + return new LogCatViewerFilter(mLogCatFilters.get(index)); + } + + @Override public void setFocus() { } diff --git a/ddms/libs/ddmuilib/src/com/android/ddmuilib/logcat/LogCatViewerFilter.java b/ddms/libs/ddmuilib/src/com/android/ddmuilib/logcat/LogCatViewerFilter.java index bfe0451..1b04615 100644 --- a/ddms/libs/ddmuilib/src/com/android/ddmuilib/logcat/LogCatViewerFilter.java +++ b/ddms/libs/ddmuilib/src/com/android/ddmuilib/logcat/LogCatViewerFilter.java @@ -34,7 +34,10 @@ public final class LogCatViewerFilter extends ViewerFilter { private boolean mCheckPID; private boolean mCheckTag; + private boolean mCheckText; + private Pattern mTagPattern; + private Pattern mTextPattern; /** * Construct a {@link ViewerFilter} filtering logcat messages based on @@ -47,16 +50,27 @@ public final class LogCatViewerFilter extends ViewerFilter { mCheckPID = mFilterSettings.getPidString().trim().length() != 0; mCheckTag = mFilterSettings.getTag().trim().length() != 0; + mCheckText = mFilterSettings.getText().trim().length() != 0; if (mCheckTag) { try { mTagPattern = Pattern.compile(mFilterSettings.getTag()); } catch (PatternSyntaxException e) { - Log.e("LogCatFilter", "Ignoring invalid regex."); - Log.e("LogCatFilter", e); + Log.e("LogCatFilter", "Ignoring invalid tag regex."); + Log.e("LogCatFilter", e.getMessage()); mCheckTag = false; } } + + if (mCheckText) { + try { + mTextPattern = Pattern.compile(mFilterSettings.getText()); + } catch (PatternSyntaxException e) { + Log.e("LogCatFilter", "Ignoring invalid text regex."); + Log.e("LogCatFilter", e.getMessage()); + mCheckText = false; + } + } } @Override @@ -81,7 +95,14 @@ public final class LogCatViewerFilter extends ViewerFilter { /* if tag filter is enabled, filter out messages not matching the tag */ if (mCheckTag) { Matcher matcher = mTagPattern.matcher(m.getTag()); - if (!matcher.matches()) { + if (!matcher.find()) { + return false; + } + } + + if (mCheckText) { + Matcher matcher = mTextPattern.matcher(m.getMessage()); + if (!matcher.find()) { return false; } } diff --git a/ddms/libs/ddmuilib/tests/src/com/android/ddmuilib/logcat/LogCatFilterSettingsTest.java b/ddms/libs/ddmuilib/tests/src/com/android/ddmuilib/logcat/LogCatFilterSettingsTest.java new file mode 100644 index 0000000..8bed741 --- /dev/null +++ b/ddms/libs/ddmuilib/tests/src/com/android/ddmuilib/logcat/LogCatFilterSettingsTest.java @@ -0,0 +1,74 @@ +/* + * Copyright (C) 2011 The Android Open Source Project + * + * Licensed 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 com.android.ddmuilib.logcat; + +import com.android.ddmlib.Log.LogLevel; + +import java.util.ArrayList; +import java.util.List; + +import junit.framework.TestCase; + +public class LogCatFilterSettingsTest extends TestCase { + public void testMatchingText() { + LogCatMessage msg = new LogCatMessage(LogLevel.VERBOSE, + "", "", "", //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ + "message with word1 and word2"); //$NON-NLS-1$ + assertEquals(true, search("word1 with", msg)); //$NON-NLS-1$ + assertEquals(true, search("text:w.* ", msg)); //$NON-NLS-1$ + assertEquals(false, search("absent", msg)); //$NON-NLS-1$ + } + + public void testTagKeyword() { + LogCatMessage msg = new LogCatMessage(LogLevel.VERBOSE, + "", "tag", "", //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ + "sample message"); //$NON-NLS-1$ + assertEquals(false, search("t.*", msg)); //$NON-NLS-1$ + assertEquals(true, search("tag:t.*", msg)); //$NON-NLS-1$ + } + + public void testPidKeyword() { + LogCatMessage msg = new LogCatMessage(LogLevel.VERBOSE, + "123", "", "", //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ + "sample message"); //$NON-NLS-1$ + assertEquals(false, search("123", msg)); //$NON-NLS-1$ + assertEquals(true, search("pid:123", msg)); //$NON-NLS-1$ + } + + /** + * Helper method: search if the query string matches the message. + * @param query words to search for + * @param message text to search in + * @return true if the encoded query is present in message + */ + private boolean search(String query, LogCatMessage message) { + List<LogCatFilterSettings> settings = LogCatFilterSettings.fromString(query, + LogLevel.VERBOSE); + + List<LogCatViewerFilter> filters = new ArrayList<LogCatViewerFilter>(); + for (LogCatFilterSettings s : settings) { + filters.add(new LogCatViewerFilter(s)); + } + + /* all filters have to match for the query to match */ + for (LogCatViewerFilter f : filters) { + if (!f.select(null, null, message)) { + return false; + } + } + return true; + } +} diff --git a/ddms/libs/ddmuilib/tests/src/com/android/ddmuilib/logcat/LogCatViewerFilterTest.java b/ddms/libs/ddmuilib/tests/src/com/android/ddmuilib/logcat/LogCatViewerFilterTest.java index eec102d..7bd94c4 100644 --- a/ddms/libs/ddmuilib/tests/src/com/android/ddmuilib/logcat/LogCatViewerFilterTest.java +++ b/ddms/libs/ddmuilib/tests/src/com/android/ddmuilib/logcat/LogCatViewerFilterTest.java @@ -22,7 +22,7 @@ import junit.framework.TestCase; public class LogCatViewerFilterTest extends TestCase { public void testFilterByLogLevel() { LogCatFilterSettings settings = new LogCatFilterSettings("", //$NON-NLS-1$ - "", "", LogLevel.DEBUG); //$NON-NLS-1$ //$NON-NLS-2$ + "", "", "", LogLevel.DEBUG); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ LogCatViewerFilter filter = new LogCatViewerFilter(settings); /* filter message below filter's log level */ @@ -38,7 +38,7 @@ public class LogCatViewerFilterTest extends TestCase { public void testFilterByPid() { LogCatFilterSettings settings = new LogCatFilterSettings("", //$NON-NLS-1$ - "", "123", LogLevel.VERBOSE); //$NON-NLS-1$ //$NON-NLS-2$ + "", "", "123", LogLevel.VERBOSE); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ LogCatViewerFilter filter = new LogCatViewerFilter(settings); /* show message with pid matching filter */ @@ -54,7 +54,7 @@ public class LogCatViewerFilterTest extends TestCase { public void testFilterByTagRegex() { LogCatFilterSettings settings = new LogCatFilterSettings("", //$NON-NLS-1$ - "tag.*", "", LogLevel.VERBOSE); //$NON-NLS-1$ //$NON-NLS-2$ + "tag.*", "", "", LogLevel.VERBOSE); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ LogCatViewerFilter filter = new LogCatViewerFilter(settings); /* show message with tag matching filter */ @@ -66,4 +66,19 @@ public class LogCatViewerFilterTest extends TestCase { "", "ta123", "", ""); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$ assertEquals(false, filter.select(null, null, msg)); } + + public void testFilterByTextRegex() { + LogCatFilterSettings settings = new LogCatFilterSettings("", //$NON-NLS-1$ + "", "text.*", "", LogLevel.VERBOSE); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ + LogCatViewerFilter filter = new LogCatViewerFilter(settings); + + /* show message with text matching filter */ + LogCatMessage msg = new LogCatMessage(LogLevel.VERBOSE, + "", "", "", "text123"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$ + assertEquals(true, filter.select(null, null, msg)); + + msg = new LogCatMessage(LogLevel.VERBOSE, + "", "", "", "te123"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$ + assertEquals(false, filter.select(null, null, msg)); + } } |