RK3288 Android 6.0 logcat 过滤带冒号的 tag

logcat 过滤

logcat 过滤不打印的日志时,可以使用 <tag>:S 的参数,比如排除 ActivityManager 相关日志。使用 以下指令

logcat ActivityManager:S

但是我遇到一种特殊情况,应用app的tag中包含了冒号,比如 APP:SDK--, 其它类似的tag有很多,这种情况使用以下指令是无法过滤掉的。

logcat APP:SDK--:S

为了分析原因,需要查看 logcat 源码。

system/core/logcat/logcat.cpp
system/core/liblog/logprint.c

logcat.cpp

logcat.cpp 中解析参数时,针对 s 使用 android_log_addFilterRule 解析。这里需要说明的是, logcat -slogcat *:S 是等效的。logcat 在解析 <tag>:S 时也是调用 android_log_addFilterRule 函数。

   for (;;) {
         int ret;

         ret = getopt(argc, argv, ":cdDLt:T:gG:sQf:r:n:v:b:BSpP:k");

         if (ret < 0) {
             break;
         }
                      
         switch(ret) {
             case 's':
                 // default to all silent
                 android_log_addFilterRule(g_logformat, "*:s");
             break;

logprint.c

android_log_addFilterRule 定义于 logprint.c 文件。

int android_log_addFilterRule(AndroidLogFormat *p_format,
        const char *filterExpression)
{
    size_t tagNameLength;
    android_LogPriority pri = ANDROID_LOG_DEFAULT;

    tagNameLength = strcspn(filterExpression, ":");

    if (tagNameLength == 0) {
        goto error;
    }

    if(filterExpression[tagNameLength] == ':') {
        pri = filterCharToPri(filterExpression[tagNameLength+1]);

        if (pri == ANDROID_LOG_UNKNOWN) {
            goto error;
        }
    }

    if(0 == strncmp("*", filterExpression, tagNameLength)) {
        /*
         * This filter expression refers to the global filter
         * The default level for this is DEBUG if the priority
         * is unspecified
         */
        if (pri == ANDROID_LOG_DEFAULT) {
            pri = ANDROID_LOG_DEBUG;
        }

        p_format->global_pri = pri;
    } else {
        /*
         * for filter expressions that don't refer to the global
         * filter, the default is verbose if the priority is unspecified
         */
        if (pri == ANDROID_LOG_DEFAULT) {
            pri = ANDROID_LOG_VERBOSE;
        }

        char *tagName;

/*
 * Presently HAVE_STRNDUP is never defined, so the second case is always taken
 * Darwin doesn't have strnup, everything else does
 */
#ifdef HAVE_STRNDUP
        tagName = strndup(filterExpression, tagNameLength);
#else
        /* a few extra bytes copied... */
        tagName = strdup(filterExpression);
        tagName[tagNameLength] = '\0';
#endif /*HAVE_STRNDUP*/

        FilterInfo *p_fi = filterinfo_new(tagName, pri);
        free(tagName);

        p_fi->p_next = p_format->filters;
        p_format->filters = p_fi;
    }

    return 0;
error:
    return -1;
}

分析以上代码发现,在处理 <tag>:S 或者 *:S 时,是通过分隔符 : 解析为前后两部分,当tag中出现冒号时是无法正确处理的。

为此,在保证原有功能不变的基础上,可以添加一些定制化的参数,新增新的api函数,用以支持过滤特定tag。

定制化logcat

logcat已经支持的参数列表为 :cdDLt:T:gG:sQf:r:n:v:b:BSpP:k, 在这里添加一个没用到的参数,比如 C.

--- a/core/logcat/logcat.cpp
+++ b/core/logcat/logcat.cpp
@@ -255,6 +255,7 @@ static void show_help(const char *cmd)
     fprintf(stderr, "options include:\n"
                     "  -s              Set default filter to silent.\n"
                     "                  Like specifying filterspec '*:S'\n"
+                    "  -C              Set customize filter to slient.\n"
                     "  -f <filename>   Log to file. Default is stdout\n"
                     "  -r <kbytes>     Rotate log every kbytes. Requires -f\n"
                     "  -n <count>      Sets max number of rotated logs to <count>, default 4\n"
@@ -492,7 +493,7 @@ int main(int argc, char **argv)
     for (;;) {
         int ret;
 
-        ret = getopt(argc, argv, ":cdDLt:T:gG:sQf:r:n:v:b:BSpP:");
+        ret = getopt(argc, argv, ":cdDLt:T:gG:sQf:r:n:v:b:BSpP:C");
 
         if (ret < 0) {
             break;
@@ -504,6 +505,10 @@ int main(int argc, char **argv)
                 android_log_addFilterRule(g_logformat, "*:s");
             break;
 
+            case 'C':
+                android_log_addFilterRule_custom(g_logformat);
+            break;
+
             case 'c':
                 clearLog = 1;
                 mode |= ANDROID_LOG_WRONLY;

然后在 logprint.c 中添加定制化的代码。

+static int android_log_addFilterRule_customize(AndroidLogFormat *p_format,
+        const char *filterExpression)
+{
+    size_t tagNameLength;
+    android_LogPriority pri = ANDROID_LOG_SILENT;
+
+    tagNameLength = strlen(filterExpression);
+
+    char *tagName;
+
+#ifdef HAVE_STRNDUP
+    tagName = strndup(filterExpression, tagNameLength);
+#else
+    tagName = strdup(filterExpression);
+    tagName[tagNameLength] = '\0';
+#endif /*HAVE_STRNDUP*/
+
+    FilterInfo *p_fi = filterinfo_new(filterExpression, pri);
+    free(tagName);
+
+    p_fi->p_next = p_format->filters;
+    p_format->filters = p_fi;
+    return 0;
+}
+
+/**
+ * returns 0 on success and -1 on invalid expression
+ *
+ * Assumes single threaded execution
+ */
+
+int android_log_addFilterRule_custom(AndroidLogFormat *p_format)
+{
+    android_log_addFilterRule_customize(p_format, "APP:SDK--");
+    android_log_addFilterRule_customize(p_format, "APP:API--");
+    android_log_addFilterRule_customize(p_format, "APP:RUNTIME--");
+    return 0;
+}

这样一来就可以通过 logcat -C 去过滤特定标签信息了。

以上方法还可以进一步优化,比如添加打印等级相关的参数,针对当前业务已经够用,有兴趣的可以按需修改。