Android logcat查看Log.i日志:*:I过滤语法与实战指南
2026/9/9 18:23:49 网站建设 项目流程

Android 开发圈里被问过的问题里,"logcat 怎么看 Log.i 的日志"绝对排得上前几。很多人第一次跑adb logcat的时候都犯过嘀咕:明明代码里写了Log.i("TAG", "hello"),终端里却什么都没看到,于是去各个社区问"要加什么参数才能显示出来"。这个问法本身其实藏着一个对 logcat 过滤机制的误解。先把答案说清楚:默认情况下你不需要加任何东西,Log.i 日志一直都在输出,你只是被大量 Verbose 和 Debug 信息淹没了;如果你确实只想看 Info 级别及以上的日志,那么要加的是*:I这样的优先级过滤表达式,不是-v I,更不是-i。这篇文章把这条命令背后的语法、容易踩的坑,以及实际开发里的组合用法一次讲透。

1. Log.i 的过滤逻辑:先搞清楚"加什么"到底是什么意思

1.1 答案先放出来:*:I就是你要加的

直接命令行操作一下。假设代码里有这样一条日志:

Log.i("MarketDemo", "onCreate: user clicked login button"); Log.d("MarketDemo", "onCreate: debug detail info");

如果你执行的是普通adb logcat,终端会被各种进程的日志刷屏,V 和 D 级别的占大多数,Log.i 很容易被淹没在几十行里,肉眼根本注意不到。这时候想要"只看 Info 及以上的日志",标准写法是:

adb logcat *:I

*表示匹配所有 tag,冒号后面跟的是优先级I,意思是"所有标签下,Info 及以上级别的日志都显示出来"。所以你会看到I MarketDemo这一行出现在屏幕上,而D MarketDemo这一行被过滤掉了。

有人可能会觉得奇怪:为什么不直接写adb logcat I?这就是 logcat 语法的重点了——单独一个I在这里不是优先级,而是被当成标签名去匹配,你看到的会是 tag 名恰好叫 "I" 的日志,跟 Info 级别没有半点关系。想过滤级别,必须先写tag:再写优先级,*:I是整个规则里最简单的一种。

1.2 logcat 的默认行为:不是看不见,是信息太多

其实 logcat 的默认行为非常"宽容":只要没有指定过滤规则,它会把所有优先级、所有 tag 的日志全部吐出来,从 Verbose 到 Fatal 一个不漏。所以你问"要加什么才能显示 Log.i",从严格意义上说答案是"不需要加任何参数,Log.i 本来就会显示"。

那为什么实践中总觉得 Log.i "不显示"?主要有两个原因。

第一个原因是信息过载。一个正常 App 跑起来,周围的系统进程、服务进程、你依赖的第三方库,每秒都能产生几十行 V/D 日志。Log.i 在里面既不靠前也不显眼,轻轻松松就被刷没了。尤其当你用 USB 连接真机、日志量特别大的时候,终端滚屏速度非常快,想靠肉眼抓到一条自己的日志几乎不可能。

第二个原因是 tag 没配对。logcat 的输出里每一行都会带优先级/Tag,如果你在代码里写的是Log.i("MyApp", "hello"),但在adb logcat里直接搜 "hello" 是没问题的,可如果你下意识搜的是别的关键词,自然觉得"没有输出"。

所以真正的问题通常不是"Log.i 没显示",而是"怎么让 Log.i 从海量日志里跳出来"。*:I这种优先级过滤就是最直接的手段。

2. 日志优先级与 logcat 的 tag:priority 语法

2.1 Android 日志级别都有哪些

要彻底看明白这条命令,先得把 Android 的日志级别体系过一遍。Android 的android.util.Log类提供了五个静态方法,对应五个优先级:

方法优先级含义常见用途
Log.v()V / VERBOSE啰嗦级别最细粒度的调试信息,平时基本不输出
Log.d()D / DEBUG调试级别开发阶段排查问题的细节输出
Log.i()I / INFO信息级别关键节点、业务流程、事件通知
Log.w()W / WARN警告级别有潜在问题但不影响运行的提示
Log.e()E / ERROR错误级别出错、异常、崩溃前的关键日志

在 logcat 内部其实还有一个 FATAL 级别,对应 F,通常由系统处理致命错误时使用,普通 App 代码里一般不会主动打。六个级别的优先级从低到高是:V < D < I < W < E < F。

这里最容易被忽略的一点是:logcat 按优先级过滤时,遵循的是"大于等于"的规则。也就是说*:I过滤出来的不只是 Log.i,还包括 Log.w、Log.e 这些比 I 更高的级别。如果你只想看 Log.i 本身,不想看 W 和 E,单纯靠 logcat 的优先级过滤是做不到的,需要配合 grep 做精确文本匹配,这个后面会专门讲。

2.2tag:priority过滤规则的运作原理

logcat 的过滤规则语法可以概括成一句话:<tag>:<priority>,多个规则用空格分隔。比如:

adb logcat ActivityManager:I MyApp:D *:S

这条命令的意思是:输出ActivityManager标签下 Info 及以上级别,再加MyApp标签下 Debug 及以上级别,其余所有标签一律静默。多个规则之间是"并集"关系,不是"交集"关系。

这里的*:S需要单独解释一下。S代表 SILENT,也就是静默。*:S表示所有标签都静默,这通常会作为"兜底规则"跟具体 tag 的规则一起使用,用来把其他所有进程的日志全部关掉,只留下你关心的那部分。

比如你只想看MyApp这个 tag 的日志,最干净的写法是:

adb logcat MyApp:* *:S

或者简写成adb logcat -s MyApp-s参数是 logcat 提供的一个快捷方式,含义是"把所有标签设为静默,只显式指定标签"。

讲到这儿顺便多说一句:日志的过滤不是在 PC 端"事后删除"的,而是 adb 服务端把过滤条件传给设备上的logd守护进程,由 logd 在日志进入缓冲区之前就决定要不要保留。所以使用*:I这种原生过滤能显著降低 USB 传输和终端的显示压力,这也是我优先推荐原生过滤而不是| grep的原因。

3. 实战:三种最常见的"只看 Log.i"场景

3.1 全局过滤:adb logcat *:I

最简单粗暴的用法,适合不关心具体 tag、只想把日志量压下去的场景。

adb logcat *:I

执行之后,终端输出会明显干净很多,原本满屏的 V/D 都没了,剩下 I、W、E、F 四条级别。如果这个时候你的Log.i还在刷屏,那说明你的 Info 日志确实打得太多了,需要反思一下是不是有的地方应该用Log.d

实际开发里,我经常在需要跟别人远程协作、一边操作 App 一边看日志的时候用它。因为 W 和 E 一般量不大,I 覆盖了业务关键节点,信息密度刚刚好,不会漏掉重要内容,也不至于被 Debug 日志淹没。

3.2 只看某个 tag:adb logcat MyTag:I *:S

实际项目里,你关心的往往不是整个系统的 Info 日志,而是自己某个模块、某个 tag 下的日志。举个例子,代码里所有网络请求日志都打成了Log.i("Network", "..."),你想只看 Network 相关,那就跑:

adb logcat Network:I *:S

这条命令的效果是:只显示 tag 为 Network、级别为 Info 及以上的日志,其他 tag 一律不显示。

如果想同时保留多个 tag,可以这样写:

adb logcat Network:I Push:I *:S

如果觉得每次敲一长串太麻烦,用-s更短:

adb logcat -s Network:I Push:I

但注意-s的语义是"静默其他所有",所以它后面的规则里可以省略*:S。两种写法等价,我个人习惯用-s,少敲几个字符。

3.3 顺手把格式和缓冲区调好:-v threadtime-c

光有过滤还不够,日常抓 Log.i 的时候,我基本都会把输出格式和缓冲区一起安排好。

输出格式用-v threadtime

adb logcat -v threadtime Network:I *:S

threadtime格式会带上日期时间、进程 PID、线程 TID,比如:

03-20 15:39:50.689 1234 5678 I Network: request url=https://api.example.com/login

这样你能清楚看到这条 Log.i 是哪个进程、哪个线程打出来的,排查并发问题的时候特别有用。关于-v的更多细节,下一章专门讲。

缓冲区用-c先清空:

adb logcat -c adb logcat -v threadtime Network:I *:S

-c的作用是清空设备上的 logcat 环形缓冲区。为什么要先清?因为 logcat 缓冲区是有限的环形存储,里面可能残留了很多旧的日志。你连接 adb 之后如果不清空,新日志会排在旧日志后面,干扰判断。通常的操作流程是:先清空缓冲区,然后在 App 里复现一次问题,再回来过滤查看,这样输出里基本就是你这次操作产生的日志。

一条龙组合命令:

adb logcat -c && adb logcat -v threadtime Network:I *:S

4.-v不是"版本",它控制的是输出格式

4.1-v*:I的区别

这一节必须单独拎出来说,因为我在各种技术群里见过太多人把-v和"版本"联系到一起,或者以为-v I是过滤 Info 的写法。其实-v--format的短参数,后面的值控制的是"每条日志长什么样",而不是"显示哪些级别"。

-v支持的常见格式如下:

格式输出示例特点
briefI/Network( 1234): message默认格式之一,紧凑,带优先级和 tag
time03-20 15:39:50.689 I/Network( 1234): message带时间,不带线程号
threadtime03-20 15:39:50.689 1234 5678 I Network: message带时间、PID、TID,最常用
processI( 1234) message (Network)以进程为单位,紧凑
tagI/Network: message只保留优先级和 tag,最干净
rawmessage只输出消息本体,什么都不带
long[ 03-20 15:39:50.689 1234: 5678 I/Network ]加多行消息适合长日志,会分行显示

-v后面跟的只能是上述这类格式名。所以adb logcat -v I会直接解析失败,终端报:

Unknown argument for -v: I

因为 logcat 在解析参数时把I当成了一种未知的输出格式,而不是优先级。这跟*:I完全是两码事。

4.2 为什么adb logcat -v I会报错

把 logcat 的参数分类想清楚,就能避免这类错误。logcat 命令行参数大致分三类:

第一类是"动作类"参数,比如-c(清空缓冲区)、-g(查看缓冲区大小)、-d(导出当前缓冲区内容后退出)、-h(查看帮助)。这一类命令通常单独使用,不会跟过滤规则混在一起。

第二类是"格式类"参数,就是-v加各种格式名,影响输出的排版。这一类可以跟过滤规则共存,互不干扰,比如adb logcat -v threadtime *:I就是"以 threadtime 格式输出 Info 及以上日志"。

第三类是"过滤规则",也就是tag:priority这种表达式。它不跟在任何参数后面,直接作为命令的尾部参数出现。这里容易出错的是,adb logcat -v threadtime Network:I *:SNetwork:I*:S都是过滤规则,而-v threadtime是格式设置,二者各管各的。

理清这三类之后,再看到adb logcat -v I报错就不奇怪了:-v后面需要一个格式名,你给了个优先级字母,解析器自然不认。想过滤 Info,就把I放到尾部的*:I里,别放在-v后面。

5. 用 grep 抓 Log.i 的替代方案与真实区别

5.1| grep " I/"到底行不行

除了 logcat 原生的*:I,很多老司机还喜欢用管道加 grep:

adb logcat | grep " I/"

这种写法能不能抓到 Log.i?能,但有个前提:grep 匹配的是 logcat 输出的文本行,而文本格式受-v影响。默认的 brief 格式下,Info 日志长这样:

I/Network( 1234): request url=https://api.example.com/login

所以grep " I/"能匹配到。但如果你用了-v threadtime,Info 日志变成:

03-20 15:39:50.689 1234 5678 I Network: request url=https://api.example.com/login

这里 tag 前面是一个空格加I,再跟一个空格加Network,没有斜杠了。这时候你要想匹配 Info 级别,应该写:

adb logcat -v threadtime | grep " I Network"

或者更稳妥一点,用正则匹配I/I两种:

adb logcat -v threadtime | grep -E "I/(Network|MyApp)| I (Network|MyApp)"

所以说,用 grep 本身没错,但一定要先确认你用的-v格式是什么,否则匹配条件对不上,输出就是空的。

5.2 什么时候该用 logcat 原生过滤,什么时候该用 grep

这里给一张对比表,看完你就知道怎么选了:

维度原生*:I过滤| grep " I/"
过滤位置设备端 logd,入口处丢弃PC 端,日志已经传过来再筛
USB/终端压力低,不显示的不传输高,海量日志先传过来再说
是否能拦截 V/D能,直接在源头丢了不能,先输出再删
能否匹配"只显示 I 不含 W/E"不能,*:I包含 W/E能,grep 按文本精确匹配
正则/复杂匹配不支持支持
系统属性过滤影响persist.log.tag等限制同样受影响,因为源头就已经没了

从这张表能看出一个关键结论:如果你的目的是"减少日志量,让 Log.i 从噪音中露出来",优先用原生*:I;如果你的目的是"在已经确定日志量可控的前提下,做更精细的文本筛选",比如只提取某个请求 URL、某个异常关键字,再上 grep。

还有个常见组合是前后搭配:先用*:I把日志量压下来,再管道给 grep 做关键词匹配。

adb logcat -v threadtime *:I | grep "login"

这样即使 grep 支持的匹配很精确,输入给它处理的数据量也小了很多,效率更高。

6. 日志明明打了却没输出的几个隐藏坑

6.1 真机上把日志级别给改了:persist.log.tag

很多真机、尤其是某些定制 ROM 上,会存在一个隐藏的"全局日志级别开关",它就是系统属性persist.log.tag。如果这个属性被设成了W或者E,那么 logd 在入口处就会把所有Log.i甚至Log.w都丢掉,你代码里怎么写都打不出来,PC 端怎么过滤都白搭。

检查方法:

adb shell getprop persist.log.tag

如果返回值是WES等高位级别,那就说明系统在源头做了限制。可以临时改低试试:

adb shell setprop persist.log.tag V

这里V是因为 V 是最低级别,能放行所有日志。有些系统还支持按 tag 设置,比如只针对某个 tag 放行:

adb shell setprop log.tag.MyApp D

意思是 tag 为 MyApp 的日志,Debug 及以上级别都能正常输出。

这类属性一般需要 adb root 权限或者 debuggable 的 userdebug 版本才能改。遇到 Log.i 打了却不显示的情况,第一步就该查这个,别急着怀疑自己的过滤命令写错了。

6.2 日志缓冲区太小,Log.i 被刷掉

logcat 的日志缓冲区是环形结构,大小有限。如果你在开发者选项里把"日志记录器缓冲区大小"设得太小,比如 64KB,那么日志量稍微一冲,旧的 Log.i 就会直接被新的日志顶掉,等你打开终端看的时候,刚才打印的 Info 已经无影无踪了。

查看当前缓冲区状态用:

adb logcat -g

输出类似:

main: ring buffer is 256Kb (239Kb consumed), max entry is 5120, max payload is 10240 system: ring buffer is 256Kb (0Kb consumed), max entry is 5120, max payload is 10240

如果你发现consumed经常接近上限,说明日志在持续刷新,旧数据很容易被覆盖。这时候除了调大缓冲区,还可以用-b all一次性查看所有缓冲区:

adb logcat -b all -v threadtime *:I

这里-b选择缓冲区,all表示 main、system、events、crash 全部输出。绝大多数 App 用Log.i打出来的日志都在main缓冲区里,但系统组件的日志可能落在system,崩溃堆栈在crash,多缓冲区一起看能避免漏掉关键上下文。

6.3 用 --pid 精确锁定进程

有时候你过滤了 tag,也过滤了级别,输出里还是混着多进程的日志,干扰判断。这时候最干净的办法是按 PID 过滤,直接从进程维度锁死。

命令是这样:

adb logcat --pid=$(adb shell pidof -s com.example.app)

pidof -s会返回指定包名进程的 PID,然后--pid参数让 logcat 只输出这个进程的日志。如果你还想加上 Info 过滤,可以这样组合:

adb logcat --pid=$(adb shell pidof -s com.example.app) *:I

这条命令是我抓应用启动日志最常用的方案:先-c清空缓冲区,重启 App,然后执行上面这行,输出里基本就是你这个进程的所有 Info 及以上日志,干净、精准、不吵。

需要注意的是,如果应用是多进程架构,pidof -s返回的只是其中一个进程的 PID,其他进程的日志看不到。需要多进程一起看的时候,可以去掉-s拿到多个 PID,或者干脆用--pid的多次调用分别开终端。

最后还有一个小经验:Log.i的 tag 命名尽量规范,别带空格、冒号这类特殊字符。因为 logcat 的tag:priority语法是按冒号切分 tag 和优先级的,tag 里一旦出现冒号,过滤规则就可能解析错位,明明日志打了,你也看不到。我见过有人把 tag 写成"Time:Cost",然后adb logcat Time:Cost:I *:S怎么跑都不对,最后才发现是冒号把规则拆成了三段。这些细节在你排查"Log.i 不显示"的时候往往比命令本身更关键。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询