ARTICLE DETAIL

建站实战干货

来自一线的建站与推广经验沉淀,每一条都经过真实交付验证。

掌握Android Logcat:从日志写入到高效排查问题的完整指南

2026/9/30 6:05:40 拓冰建站 浏览量
掌握Android Logcat:从日志写入到高效排查问题的完整指南 写日志这事在 Android 开发里简直太日常了日常到很多新手根本不把它当回事。可真到了联调、排查线上事故、给测试同学定位问题的时候日志写得好不好、能不能快速从 Android Studio 的 Logcat 里捞到有用的信息直接决定你是一个小时定位问题还是一整天都在抓瞎。说白了Logcat 就像飞机驾驶舱里的仪表盘平时不看它也能飞但一旦出状况它就是那个帮你找出真相、甚至保命的东西。这篇东西主要围绕在 Android Studio 里如何高效地写入日志、查看日志来聊核心是 Logcat 这个自带工具。我会从最基础的界面操作讲起一直聊到代码里怎么写规范日志、怎么用命令行抓取日志、遇到日志不显示或者丢掉这种坑怎么排查适合刚入门 Android 开发的新手也适合那些写了好一阵子代码但一直在用“土办法”看日志的同学。看完你会明白日志不该是乱打的Logcat 也不该是随便看一眼就切走的工具。1. 内容整体设计与思路拆解1.1 为什么要专门讲 Logcat 的“写入”和“查看”一句话解释 Logcat它是 Android 系统提供的一个环形日志缓冲区把系统进程和应用进程通过内核 logger 写出来的日志统一收集起来开发者可以通过 adb 命令或者 Android Studio 的 Logcat 窗口实时读取。这个“环形缓冲区”的机制有点像行车记录仪的存储卡存满了就覆盖最老的记录只保留最近一段时间的数据所以抓日志讲究时效性。在 Android Studio 里Logcat 窗口的定位不是“看报错的小黑框”而是开发者的主战场之一。为什么这么讲因为一个应用从启动、布局渲染、网络请求到崩溃所有关键事件都可以通过日志串起来。比如你在某个页面发现数据没有加载出来第一件事不该是去看代码逻辑对不对而是先看 Logcat 里网络层有没有打印出请求失败的信息、有没有异常堆栈先锁定问题的大致范围再去定位代码里的具体位置。而要做到这一步前提有两个第一代码里得有足够的日志输出第二你得会用工具把日志筛出来看明白。这两件事恰好对应标题里的“写入”和“查看”是相辅相成的两块。1.2 常规方案与备选工具的取舍写日志的方式不止一种市面上也有第三方日志框架比如最常用的 Timber、Logger还有早期很多人用的 Log4j 移植版。这些框架底层封装的依然是 android.util.Log只是在上层做了 Tag 管理、格式化、日志文件输出等增强。实际开发中我的建议是不要一上来就引入框架先把系统自带的 android.util.Log 用熟练理解了 Tag 和 Level 这两个核心概念之后再去根据项目需要选框架。因为框架解决的是“日志写起来更方便、管理更规范”但不会替你解决“日志怎么看、怎么分析”的问题。查看日志的工具同样不止 Android Studio 自带的 Logcat 面板还有 adb logcat 命令行、DDMS旧版调试工具、第三方的 Logcat 可视化工具以及像 Android Studio 里的 Profiler 可以配合抓取 CPU、内存等数据。这里面的核心原则是能直接用 Android Studio 自带功能解决的问题就不额外安装第三方工具需要脱离 IDE 在真机现场抓数据的时候用 adb 命令行更靠谱需要拿到系统级别的完整日志比如 SystemServer、Input 系统、重启原因才考虑 adb bugreport。这篇文章的重点放在最常用、最核心的 Logcat 窗口和 adb logcat 命令上这两块用好了日常开发基本就能覆盖九成场景。2. 核心细节解析与实操要点2.1 Logcat 窗口核心概念Tag、Level、Message在 Android Studio 的 Logcat 窗口里任何一条日志都有三个关键元素TAG标签、LEVEL级别、MESSAGE消息内容。很多人看日志只盯着右下角的红色报错看这是不对的。红色报错只是结果中间的级别和它们之间的关系才是关键。TAG 是日志的来源标识通常是类名或者某个业务模块名。它的作用就是给日志做分类让你能快速筛选出某一个模块的输出。比如网络请求库一般会打一个 OkHttp 或 Retrofit 的 Tag你用 Logcat 的过滤条件输入这个 Tag就能只看网络层的日志其他全部隐藏。LEVEL 是日志的优先级从低到高分别是 Verbose冗余、Debug调试、Info信息、Warn警告、Error错误、Assert断言失败。这 6 个级别的设计和日志保留策略有关。Verbose 和 Debug 级别通常只在开发阶段开启发布到线上的包应该把它们关掉否则会泄露出大量代码内部细节Info 级别的日志适合记录一个关键操作的结果比如“用户登录成功”Warn 是有些隐患但不至于报错的情况Error 则是必须关注的错误信息。MESSAGE 就是日志的具体内容是你在代码里拼接的那个字符串。三者的关系可以用生活化的方式理解TAG 像是快递包裹上的收件人标签Level 像是包裹上的“易碎品 / 普通件”标识MESSAGE 才是包裹里面的实际货物。看日志的时候不要只盯着某一行看要把同一个 Tag 下面一段时间的日志连在一起读还原当时的执行顺序。2.2 日志级别的选择标准什么时候用什么级别这一点值得单独拿出来聊因为很多新手的日志级别用得一团乱。最常见的现象是所有日志不分青红皂白都是 Log.e或者全用 Log.i导致最后日志里全是垃圾信息真正出错的地方反而被淹没了。日志级别的选择有一个很朴素的标准——这条日志是给谁看、在什么场景下起作用。Verbose最细粒度的信息比如 for 循环里每一轮处理的具体值。这个级别在连载 CPU 占用高、耗时长的任务时有奇效但通常只在开发环境开。Android 官方建议Verbose 日志不应该被编译进正式发布的应用里因为它影响性能且没有保留价值。Debug调试用的信息用于确认某个分支走没走、某个变量的值对不对。这类日志在开发时最常用可以用 BuildConfig.DEBUG 控制只让 debug 包输出。Info表达一个有意义的事件发生了比如 Activity 生命周期切换、网络请求发出去了、数据库迁移完成不对应任何错误但事后可以通过这些信息把执行路径还原出来。Warn某些情况发生了但程序还能继续跑比如缓存文件不存在自动重建、用户输入了 null 值走了默认分支这类日志值得保留因为它们往往暗示逻辑上有潜在问题。Error这个级别只留给真正的异常和错误比如网络请求失败返回错误码、JSON 解析抛异常、某个关键的初始化步骤失败。Error 日志一定要带上尽量完整的上下文信息比如请求 URL、状态码、异常堆栈别只丢一句“failed”。Assert极少用到一般配合断言使用表示“程序走到这里一定是出了问题不能继续执行”。一个容易踩的坑是有些人觉得 Warn 比 Error 级别低所以把不严重的错都打成 Warn严重的打成 Error。这样分类其实没问题但要注意Error 日志会让 Android Studio 的 Logcat 自动聚焦到它上面如果你把 Error 滥用成输出普通信息的渠道Logcat 的“红色报警”就会失去意义等真正出问题的时候反而不容易发现。我的习惯是Error 一年到头出现的次数应该很少如果发现某个类里全是 Log.e说明日志级别用错了要回头改。2.3 在代码里写入日志从 Log.v 到 Log.wtf写入日志是代码层面最简单、但也最考验纪律的事情。一个正确的日志调用要包含合适的 Tag、合理的级别、清晰的描述信息。具体到代码里写法不难核心是“知道该在哪里打、打什么内容”。先看一个基础的完整示例import android.util.Log; public class MainActivity extends AppCompatActivity { private static final String TAG MainActivity; Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); Log.d(TAG, onCreate called, savedInstanceState savedInstanceState); setContentView(R.layout.activity_main); String userName loadUserName(); if (userName null) { Log.w(TAG, userName is null, use default value); userName default_user; } Log.i(TAG, userName loaded: userName); } private String loadUserName() { try { // 模拟从本地读取用户信息 return null; } catch (Exception e) { Log.e(TAG, loadUserName failed, e); return null; } } }代码里值得注意的几个点第一Tag 的定义。我习惯用private static final String TAG MainActivity这种写法类名做 Tag。不要直接在方法里写字符串字面量不然后续要改 Tag 名字的时候得全文搜索一堆散落的字符串。如果你的类名特别长可以用业务简写比如支付模块统一用Payment但一个项目里 Tag 的命名规则要统一否则后期按 Tag 过滤日志会很痛苦。第二字符串拼接和参数占位。上面示例里用了onCreate called, savedInstanceState savedInstanceState这种字符串拼接这在 Java 里其实会有一定的性能开销但在开发阶段打印 Debug 级别日志时完全够用不必过度优化。如果你用 Kotlin 写可以用Log.d(TAG, onCreate called, savedInstanceState $savedInstanceState)更简洁。要打印对象的多个字段建议打印 JSON 字符串或toString()方法返回的内容便于阅读。第三捕获异常时打日志的姿势。Log.e(TAG, loadUserName failed, e)这里的第三个参数是 Throwable也就是异常对象。这个参数的重要性很容易被忽略——如果只写Log.e(TAG, loadUserName failed)你只能看到一句自定义描述看不到异常堆栈排查问题时等于没有日志。把 throwable 传进去Logcat 会以完整堆栈的形式把异常打印出来很多时候问题原因直接就在堆栈的某一行里。第四还有一个很少有人用的 APILog.wtf。它的全称是 What a Terrible Failure中文意思是“什么样的可怕错误”。它打印的日志级别是 Assert不仅仅把日志写进缓冲区还会在控制台震动或者弹窗提醒甚至在特定 ROM 上会导致进程崩溃。这个 API 适合用在“绝对不可能发生、一旦发生就等于代码逻辑写错了”的场景比如一个仅允许被某类调用、结果却被别的模块调用了的接口可以在里面加一句Log.wtf。日常开发不用频繁使用但要记住它比Log.e严重得多不要随手打。2.4 日志的格式化与可读性设计日志不是写给别人看作文而是写给自己和队友排查问题用的所以格式非常重要。同一个项目里如果每个人打日志的格式都不同那 Logcat 看下来就是一场灾难。一个好的日志格式应该包含时间、线程、Tag、级别、具体内容。Android Studio 的 Logcat 窗口默认会展示这些信息但代码里打日志的时候我们还需要做两件事来提高可读性。第一件事是多行日志怎么打。不要一次性Log.d(TAG, line1\nline2\nline3)这样倒是省事但 Logcat 会把多行日志的后续行标记为“continuation”并把它们【折叠】起来在 Android Studio 里默认只显示第一行你要手动展开才能看到剩余内容排查起来很容易漏掉。正确做法是把每条信息拆成单独一条日志或者用统一的“key value”格式拼成一行比如Log.i(TAG, request start, url url , method method , headers headers); Log.i(TAG, request success, code code , costMs costMs);第二件事是结构化地打印 JSON 或网络响应。如果是网络请求直接用Log.d(TAG, responseBody)把整个 JSON 打印出来在 Logcat 里会是一长串没有换行的字符串看起来极其痛苦。你可以在打印前先对 JSON 字符串做一次格式化用org.json.JSONObject的toString(2)方法输出带缩进的格式化文本。但这又涉及上面说的多行折叠问题——Android Studio 对折叠处理得还行点击即可展开所以在可读性和可折叠之间做个取舍即可。我个人建议是网络响应这种大段内容单独打一个 Tag比如NetResponse不要混在OkHttp这种通用 Tag 里这样方便在 Logcat 里单独筛出来看。3. 实操过程与核心环节实现3.1 Android Studio 里 Logcat 窗口的完整操作指南先讲怎么打开和基本布局。在 Android Studio 中底部工具栏默认有 Logcat 窗口如果没有显示用快捷键打开Windows 上是 Alt6macOS 上是 Cmd6。窗口打开之后你会看到右上角有几个关键控件从左到右依次是设备下拉框、进程下拉框、日志级别过滤器下拉框、搜索框、暂停按钮、清空按钮。刚接触的人经常搞混“设备下拉框”和“进程下拉框”这两个东西第一个管的是选择哪台真机或者模拟器第二个管的是选择这台设备上的哪一个进程尤其是多开应用的时候进程选错了日志自然对不上。日志级别过滤器下拉框是所有新手最先要学会用的东西。Android Studio 的 Logcat 窗口升级到新版之后这个下拉框自带了一个“正则表达式显示所有级别”的特性你可以选择只显示某些级别比如只显示 Warn 和 Error这样能快速把刷屏的日志过滤掉只看重点关注的内容。但在开发时注意如果过滤只显示 Error那些起了警告作用但还不报错的 Warn 日志就看不到了定位问题有时候反而会漏掉线索。我的习惯是默认选 Info通过搜索框二次过滤要用时再切级别。搜索框是 Logcat 里最高频用到的功能。它的逻辑是先通过设备选择确认想看的设备再用搜索框按关键字把日志筛出来。搜索结果默认是“包含关键字”的逻辑但点击搜索框左侧的漏斗图标可以选择“正则表达式匹配”“忽略大小写”等高级模式。比如你想把所有 Activity 的生命周期日志和不含特定 Tag 的日志都筛出来就可以用正则(MainActivity|SecondActivity).*(onCreate|onResume)来匹配。不过对于大多数场景直接用普通关键字就够。还有一个特别有用、但很多人不知道的操作在日志上单击左键可以直接在搜索框里以该日志的 Tag 为关键字进行过滤右键单击日志可以把该行复制出来或者复制它的原始日志内容。如果你要跟同事沟通一个问题把复制的日志粘贴到聊天工具里比发一张截图更有用因为对方可以直接在本地检索关键字。3.2 用 Logcat 查看崩溃日志与异常堆栈看崩溃日志是 Logcat 的高频场景也是新手最容易手足无措的场景。当应用崩溃时Logcat 里会出现类似FATAL EXCEPTION: main的一段日志后面跟着Process: com.example.app, PID: 12345以及一大串java.lang.NullPointerException之类的堆栈。这里的要点是不要看第一行报错信息就慌要把整段堆栈从头到尾看完重点找“我们自己代码的类名出现的那一行”我们习惯称之为“堆栈里离自己最近的那个引用”。为什么会这样因为一个异常堆栈可能夹杂了几十行系统框架的调用它们只是帮你定位的辅助信息真正引起异常的是堆栈上最近一个出现在你自己代码里的方法调用。举个例子如果异常是空指针堆栈底部会告诉你at com.example.MainActivity.onCreate(MainActivity.java:20)这一行直接指向了你代码里的具体位置其他什么at android.app.Activity.performCreate之类的基本可以忽略。如果堆栈里找不到自己写的类那才需要担心是不是系统层面的框架 bug 或者第三方库的调用问题。看崩溃日志时还有两个小技巧。第一崩溃之后日志会被应用的进程缓存但如果在 Android Studio 里切走了设备回来可能看不到最近的崩溃日志这时可以用 adb 命令把日志 dump 出来看而不是只依赖 IDE 界面。第二Android 的异常捕获机制可能会导致 Logcat 里有多个“FATAL EXCEPTION”比如主线程崩了还有 ANR Application Not Responding应用无响应日志要认清哪个才是导致用户看到“XX 已停止运行”的那一条。一般来说Process: 应用包名那一条就是目标异常。3.3 用 Logcat 分析网络请求与业务逻辑网络请求的排查是 Logcat 应用最广泛、最有价值的场景。开发一个需要联网的应用时日志打得够不够细直接决定你调试网络请求的速度。先看一段模拟网络请求的日志输出长什么样D/OkHttp: -- GET http://api.example.com/v1/user/info D/OkHttp: Accept: application/json D/OkHttp: User-Agent: DemoApp/1.0.0 (Android 14; samsung SM-S918B) D/OkHttp: -- END GET D/NetResponse: {code:0,message:success,data:{userName:张三,avatar:http://avatar.example.com/1.png}}这条日志包含的信息足够多你一眼就能看到请求发了什么地址、带了什么请求头、服务器返回了什么 JSON。排查问题的顺序也因此变得简单如果响应里 code 不等于 0基本是业务逻辑或参数问题去看后端接口文档如果连日志都没有那就是请求根本没发出去要检查网络权限、域名、拦截器等如果有响应但 JSON 解析报错那就是序列化层的问题。在实际项目中我用过很多方案来打印这种网络日志。最省事的是在 OkHttp 的拦截器里统一打印而不是在每个请求代码里手动加日志。它的好处是日志集中、格式统一、Tag 固定排查时只要筛选这一个 Tag 就行。不过要注意不要在生产环境中把包括请求体、响应体在内的完整日志打印出来因为请求参数里可能包含手机号、身份证、Token 等敏感信息这类数据一旦进入日志就可能被日志系统、临时文件等渠道泄露出去。线上包可以只打印请求地址和状态码把 body 打码。3.4 adb logcat 命令行抓取日志脱离 IDE 的保命技能离开 Android Studio 之后用 adb logcat 命令抓日志是每个 Android 开发者都必须掌握的技能。因为它不仅能抓应用日志还能抓系统日志、内核日志甚至能配合 dumpsys、bugreport 抓取更完整的信息。下面列几个最常用的 adb logcat 用法# 查看实时日志会持续输出CtrlC 停止 adb logcat # 根据关键字过滤只显示包含关键字 Activity 的日志 adb logcat | grep Activity # 输出到文件方便保存分析 adb logcat app.log # 清空日志缓冲区在多轮抓取时非常有用 adb logcat -c # 输出当前缓冲区已有日志后退出不阻塞 adb logcat -d # 根据 Tag 和级别过滤比如只看 MainActivity 的 Error 日志 adb logcat -s MainActivity:E # 带时间戳输出推荐方便和业务时间点对齐 adb logcat -v threadtime这里解释几个容易困惑的点。第一adb logcat和adb logcat -d的区别是前者实时刷新后者打印缓冲区当前内容后自动退出如果你想拿到崩溃前最后一刻的日志常用adb logcat -d把现存的日志 dump 出来。第二-s MainActivity:E这种写法是“静默模式 指定过滤”意思是除了 MainActivity 这个 Tag 的 Error 级别日志其他全部不显示用来精确定位单个模块的问题时非常高效。第三如果同时接入了多台设备需要在命令前指定设备序列号adb -s device_serial logcat否则 adb 会报“more than one device”错误。把日志保存到文件时尽量用-v threadtime先把线程号和时间戳加上。没有时间戳的日志在分析“异常是先发生还是后发生”时毫无价值。另外提醒一个坑直接用adb logcat app.log重定向标准输出时可能出现日志乱码的问题尤其是中文日志建议在命令前设置adb logcat -v threadtime app.log 21把错误输出也一起重定向确保文件完整性。3.5 bugreport 抓取完整日志定位系统级疑难杂症有些问题不是应用代码的问题。比如应用闪退但 Logcat 里找不到任何异常、设备重启后应用崩溃、低内存时进程被系统杀死这种时候单靠 Logcat 就有点乏力了需要用到 Android 系统自带的 bugreport 工具。它会把当前设备上包括 Logcat、系统事件缓存、CPU/内存占用、GPU 渲染信息、网络状态、正在运行的服务在内的所有诊断信息打包成一个文件。抓取 bugreport 的命令很简单# 生成 bugreport 文件并拉取到电脑 adb bugreport这个命令执行之后在较新的 adb 版本里会自动把生成的 .zip 文件保存到当前电脑目录。这个 zip 文件打开后里面有bugreport-*.txt可读文本、FS目录文件系统信息、traces目录ANR 堆栈等。想快速找到应用相关的信息可以在生成的 txt 文件里搜索应用包名应用配套设施、死锁信息、内存快照等都会以不同形式出现。比如排查“后台时被系统杀掉”的问题可以看lowmemorykiller相关的日志排查“系统重启原因”看SYSTEM_BOOT和last_kmsg后者需要 root 权限但部分设备在 bugreport 里会带上 Restart 相关的事件记录。需要记住的是bugreport 的抓取是一个重度操作会消耗较长的时间文件体积也可能到几百兆不要在没必要时经常抓。它的定位是“Logcat 不够用、需要系统全景视角”时才上的杀手锏。4. 常见问题与排查技巧实录4.1 日志不显示先检查设备、进程和级别过滤器很多开发者在 Logcat 里看不到自己打印的日志第一反应是代码写错了但实际上大部分原因是过滤条件没设对。花两分钟按下面顺序排查基本都能解决。第一步确认设备下拉框选的是当前连接的手机而不是某个很久以前挂着的模拟器在多设备环境下这个错误极其常见选了旧设备新日志当然看不到。第二步确认进程下拉框选的是当前应用包名。如果你同时调试的是双开应用或一个进程里有多个渠道包进程选错会导致该进程的日志完全被过滤掉。第三步确认级别过滤器不是选在 “Error” 之上。比如你打的是Log.d但过滤器默认只显示 Warn 及以上级别那 Debug 日志就不会出现这是新手最常见的原因。第四步确认 Android Studio 的 Logcat 窗口没有被正则表达式搜索框里的旧关键字锁死。搜索框里留着一个你上一次调试用过的关键字再往下滚日志自然什么都看不到。如果以上四项都检查过还是没日志那才轮到代码层面怀疑——是不是这个类的日志没执行到或者是这个类所在进程压根没启动。4.2 日志文件已保存但内容为空或不全用 adb logcat 重定向到文件时输出的日志不全甚至为空这种情况一般有两个原因。第一个原因是 shell 的重定向缓冲机制当你用adb logcat app.log并提前中断命令比如 CtrlC时缓冲区的数据可能还没来得及写入文件导致最终文件缺失一部分。解决办法是用stdbuf如果环境支持或者在停止前用adb logcat -d app.log这种一次性导出方式导出完成后自动退出数据完整。第二个原因是日志缓冲区被截断或者覆盖。Android 的 logcat 环形缓冲区默认大小有限不同系统上可能只有几百 KB 到几 MB。如果在日志量特别大的模块比如系统日志、网络打印里持续打日志早期内容会被覆盖。可以通过以下命令调整缓冲区大小# 查看当前缓冲区状态 adb logcat -g # 设置 main 缓冲区为 16MB重启后失效 adb logcat -G 16M开发调试时建议直接把缓冲区调大特别在要复现一个偶现 bug、等它出现的场景里缓冲区太小的后果是你等了半天 bug 终于复现了想回看日志时关键部分已经被刷掉了。4.3 日志太多刷屏如何快速过滤出关键信息日志刷屏是开发中绕不开的问题。系统的高频日志、其他应用的日志、自己的冗余日志混在一起让人很难快速定位。这里有三个亲测有效的过滤思路。第一个思路是多 Tag 过滤。新版本 Android Studio 的 Logcat 支持在搜索框里通过添加过滤标签的方式实现多 Tag 联合过滤。比如你想同时看 MainActivity 和 NetResponse 两个 Tag 的日志可以在过滤配置里分别添加两个过滤条件而不是手动输入正则表达式。这样 Logcat 只显示这两个 Tag 的日志其他全部屏蔽。第二个思路是利用日志级别配合关键字。比如你怀疑是网络问题可以搜索url或者code之类的关键字然后再通过级别过滤器把范围缩小到 Warn 和 Error往往能快速定位异常在哪。第三个思路是利用adb logcat -s在命令行直接做白名单过滤。这种方式尤其适合真机联调时用因为命令行可以配合 shell 脚本做更复杂的处理比如把同一个 Tag 的日志按时间排序、统计出现的频率等。不过需要注意-s后面可以跟多个 Tagadb logcat -s MainActivity:D OkHttp:W表示 MainActivity 的 Debug 和 OkHttp 的 Warn 日志都保留其余丢弃。4.4 关于日志性能与隐私的禁忌日志虽然好用但有几个场景下要严格约束自己。第一个是性能问题。打印日志本身是 IO 操作单条日志的成本不高但如果在一个被频繁调用比如 for 循环里、onDraw 里、RecyclerView 的 onBindViewHolder 里的方法里打印日志累积起来会导致明显的卡顿。在开发环境或许感觉不到但把 Debug 日志带到线上包等于每分每秒都在为日志付性能账单。解决办法是用BuildConfig.DEBUG包一层开关比如if (BuildConfig.DEBUG) { Log.d(TAG, bind item: position); }Kotlin 里可以用更优雅的方式if (BuildConfig.DEBUG) Log.d(TAG, bind item: $position)或者直接依赖android.util.Log.isLoggable(TAG, Log.DEBUG)在系统层面控制。这些开关可以在 debug 包打开所有日志release 包自动关闭。第二个是隐私问题。日志里绝对不能打印密码、短信验证码、支付密码、完整身份证号、完整手机号、Token、Cookie 等敏感信息。即使是在开发环境中这些信息一旦被日志系统收集或者流传出去风险都极高。如果非要打印也要做脱敏处理。最简单的脱敏是只保留前三位和后四位中间用星号代替更稳妥的做法是不要打印到日志里而是用断点来看。4.5 观察 Logcat 里 Binder、ANR 与系统日志的细节在实际联调过程中Logcat 里偶尔会出现一些平时很少见的日志内容需要知道它们大概表示什么否则容易一头雾水。第一类是 Arrow-和 Binder 相关的日志。Logcat 中经常出现形如Process: com.example.app和at com.example.MainActivity.onResume(MainActivity.java:20)这种带-箭头的内容比如Binder:12345_C之类它们表示不同进程之间通过 Binder 通信。当你看到自己应用的日志里混入系统进程的输出别急着怀疑日志怎么看错了——这大概率是应用调用了系统服务比如 ActivityManager、PackageManager系统服务在处理过程中也打印了日志。这类日志有助于判断某个系统调用是不是卡住了。第二类是 ANRApplication Not Responding日志。应用长时间无响应时系统会打出一条ANR in com.example.app的 FATAL 日志。如果只盯着 Logcat 看可能漏掉 ANR 的核心信息。配合 bugreport 或者/data/anr/traces.txt才能拿到线程栈从而判断是主线程被 IO 阻塞还是死锁或者有广播接收器执行超时。我的建议是遇到 ANR不要只依赖 Logcat立刻用 bugreport 抓取现场这是最接近事实真相的材料。第三类是 “Input dispatching timed out”输入事件分发超时日志。它表示用户触摸事件在指定时间内没有被处理完往往意味着主线程被卡住了。排查思路是找到超时日志对应的时间戳往前推几百毫秒看主线程那段日志里在执行什么任务。很多情况下会发现主线程在做文件读写、网络请求、JSON 解析这类本不该在主线程做的事日志里会直接体现出来。5. 从入门到进阶的一条建议路线讲了一堆具体的操作最后想说说学 Logcat 的整体思路给不同阶段的读者一个参考路径。刚入门的同学先不要急着去研究各种高级过滤规则和命令行的花活把最核心的三件事做好能合理选择日志级别、能规范地用一个 Tag、能在 Logcat 窗口里通过关键字和级别过滤器快速定位自己打印的日志。这三点做到日常调试就够用了。写了两三个月代码之后可以开始刻意练习一些进阶用法用 OkHttp 拦截器统一打印网络日志、为不同的业务模块规划统一的 Tag 规范、用 adb logcat 把日志导出成文件做更长时间维度的分析。这时候的重点是“从点状看日志过渡到线状看日志”也就是不再只看某一个时刻的那一行报错而是能把一段时间内多个模块的日志串联成一整个执行流程。有经验之后再学习怎么把日志、崩溃堆栈、bugreport、系统事件关联起来分析复杂问题。比如同时篡改多份材料去判断一个线上问题这类分析能力和工具本身的熟练程度关系不大更看重平时积累的“对系统运行原理的理解”。6. 写在最后日志这件事值得认真对待我个人在做项目的时候吃过不少日志不规范带来的亏也见过团队里因为日志打得差一个非常简单的历史问题查了整整一天的场景。回想原因很多时候不是问题本身有多难而是日志里根本找不到有效信息——要么没打、要么打了但被垃圾日志淹没、要么打在了根本没执行到的分支里。后来在团队里推动“写日志要像写代码一样有规范”从 Tag 命名、级别选择、关键节点的落盘方式都统一了一遍排查问题的时间肉眼可见地降了下来。最后再分享一个小习惯每写完一个功能模块花十分钟顺手把日志过一遍想象一下未来某天这个模块出了问题你希望当时的自己留下哪些线索。按着这个标准去补日志比事后靠猜省太多时间。日志这东西平时看着不起眼真到关键时刻就是排障的脐带别让它断了。