/* * 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.ddmlib.logcat; import com.android.annotations.NonNull; import com.android.ddmlib.Log.LogLevel; import java.util.ArrayList; import java.util.List; import java.util.regex.Matcher; import java.util.regex.Pattern; import java.util.regex.PatternSyntaxException; /** * A Filter for logcat messages. A filter can be constructed to match * different fields of a logcat message. It can then be queried to see if * a message matches the filter's settings. */ public final class LogCatFilter { private static final String PID_KEYWORD = "pid:"; //$NON-NLS-1$ private static final String APP_KEYWORD = "app:"; //$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 final String mText; private final String mPid; private final String mAppName; private final LogLevel mLogLevel; private boolean mCheckPid; private boolean mCheckAppName; private boolean mCheckTag; private boolean mCheckText; private Pattern mAppNamePattern; private Pattern mTagPattern; private Pattern mTextPattern; /** * Construct a filter with the provided restrictions for the logcat message. All the text * fields accept Java regexes as input, but ignore invalid regexes. * @param name name for the filter * @param tag value for the logcat message's tag field. * @param text value for the logcat message's text field. * @param pid value for the logcat message's pid field. * @param appName value for the logcat message's app name field. * @param logLevel value for the logcat message's log level. Only messages of * higher priority will be accepted by the filter. */ public LogCatFilter(@NonNull String name, @NonNull String tag, @NonNull String text, @NonNull String pid, @NonNull String appName, @NonNull LogLevel logLevel) { mName = name.trim(); mTag = tag.trim(); mText = text.trim(); mPid = pid.trim(); mAppName = appName.trim(); mLogLevel = logLevel; mCheckPid = !mPid.isEmpty(); if (!mAppName.isEmpty()) { try { mAppNamePattern = Pattern.compile(mAppName, getPatternCompileFlags(mAppName)); mCheckAppName = true; } catch (PatternSyntaxException e) { mCheckAppName = false; } } if (!mTag.isEmpty()) { try { mTagPattern = Pattern.compile(mTag, getPatternCompileFlags(mTag)); mCheckTag = true; } catch (PatternSyntaxException e) { mCheckTag = false; } } if (!mText.isEmpty()) { try { mTextPattern = Pattern.compile(mText, getPatternCompileFlags(mText)); mCheckText = true; } catch (PatternSyntaxException e) { mCheckText = false; } } } /** * Obtain the flags to pass to {@link Pattern#compile(String, int)}. This method * tries to figure out whether case sensitive matching should be used. It is based on * the following heuristic: if the regex has an upper case character, then the match * will be case sensitive. Otherwise it will be case insensitive. */ private int getPatternCompileFlags(String regex) { for (char c : regex.toCharArray()) { if (Character.isUpperCase(c)) { return 0; } } return Pattern.CASE_INSENSITIVE; } /** * Construct a list of {@link LogCatFilter} 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<LogCatFilter> fromString(String query, LogLevel minLevel) { List<LogCatFilter> filterSettings = new ArrayList<LogCatFilter>(); for (String s : query.trim().split(" ")) { String tag = ""; String text = ""; String pid = ""; String app = ""; if (s.startsWith(PID_KEYWORD)) { pid = s.substring(PID_KEYWORD.length()); } else if (s.startsWith(APP_KEYWORD)) { app = s.substring(APP_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 LogCatFilter("livefilter-" + s, tag, text, pid, app, minLevel)); } return filterSettings; } @NonNull public String getName() { return mName; } @NonNull public String getTag() { return mTag; } @NonNull public String getText() { return mText; } @NonNull public String getPid() { return mPid; } @NonNull public String getAppName() { return mAppName; } @NonNull public LogLevel getLogLevel() { return mLogLevel; } /** * Check whether a given message will make it through this filter. * @param m message to check * @return true if the message matches the filter's conditions. */ public boolean matches(LogCatMessage m) { /* filter out messages of a lower priority */ if (m.getLogLevel().getPriority() < mLogLevel.getPriority()) { return false; } /* if pid filter is enabled, filter out messages whose pid does not match * the filter's pid */ if (mCheckPid && !m.getPid().equals(mPid)) { return false; } /* if app name filter is enabled, filter out messages not matching the app name */ if (mCheckAppName) { Matcher matcher = mAppNamePattern.matcher(m.getAppName()); if (!matcher.find()) { return false; } } /* if tag filter is enabled, filter out messages not matching the tag */ if (mCheckTag) { Matcher matcher = mTagPattern.matcher(m.getTag()); if (!matcher.find()) { return false; } } if (mCheckText) { Matcher matcher = mTextPattern.matcher(m.getMessage()); if (!matcher.find()) { return false; } } return true; } }