资讯动态

Android logcat查看Log.i日志:*:I过滤语法与实战指南

发布时间:2026/9/9 18:23:51 来源:尧图企业网站定制
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 只看某个 tagadb 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 threadtimeadb logcat -v threadtime Network:I *:Sthreadtime格式会带上日期时间、进程 PID、线程 TID比如03-20 15:39:50.689 1234 5678 I Network: request urlhttps://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 *:S4.-v不是版本它控制的是输出格式4.1-v与*:I的区别这一节必须单独拎出来说因为我在各种技术群里见过太多人把-v和版本联系到一起或者以为-v I是过滤 Info 的写法。其实-v是--format的短参数后面的值控制的是每条日志长什么样而不是显示哪些级别。-v支持的常见格式如下格式输出示例特点briefI/Network( 1234): message默认格式之一紧凑带优先级和 tagtime03-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 *:S中Network:I和*:S都是过滤规则而-v threadtime是格式设置二者各管各的。理清这三类之后再看到adb logcat -v I报错就不奇怪了-v后面需要一个格式名你给了个优先级字母解析器自然不认。想过滤 Info就把I放到尾部的*:I里别放在-v后面。5. 用 grep 抓 Log.i 的替代方案与真实区别5.1| grep I/到底行不行除了 logcat 原生的*:I很多老司机还喜欢用管道加 grepadb logcat | grep I/这种写法能不能抓到 Log.i能但有个前提grep 匹配的是 logcat 输出的文本行而文本格式受-v影响。默认的 brief 格式下Info 日志长这样I/Network( 1234): request urlhttps://api.example.com/login所以grep I/能匹配到。但如果你用了-v threadtimeInfo 日志变成03-20 15:39:50.689 1234 5678 I Network: request urlhttps://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如果返回值是W、E、S等高位级别那就说明系统在源头做了限制。可以临时改低试试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 不显示的时候往往比命令本身更关键。

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价