4、日志级别详解:DEBUG、INFO、WARN、ERROR、FATAL 的含义与用途
日志这东西,看起来简单,用好了能救你一命。
我记得刚接触 Compose 那会儿,拼命加日志,打印出来全是 Log.d,整个控制台刷屏。结果呢?真出问题时,鼠标滚轮都滚冒烟了,也找不到关键信息。
今天我们就彻底聊透五种日志级别。
日志级别的核心思想:信息的轻重缓急
说白了,日志级别就是一个 「消息分类器」。
你想想看,一段日志可以回答两个问题:
- 重要吗?——这直接影响系统是否保持运行。
- 给谁看?——是给我开发时调试用,还是给运维看?
我个人习惯把日志看作「交通信号灯」,每种颜色代表不同的紧急程度:
- DEBUG:绿灯,正常行驶,但可以观察细节。
- INFO:蓝灯,正常行驶,记录关键节点。
- WARN:黄灯,可能有坑,请注意。
- ERROR:红灯,立即停车排查。
- FATAL:黑灯,系统可能要挂了。
核心原则:生产环境中,我只保留 WARN 及以上级别,DEBUG 和 INFO 只在开发阶段全开。这样能降低 I/O 开销,也能避免重要信息被淹没。
五个级别,逐个拆解
| 级别 | 数值 (Android) | 含义 | 典型用途 |
|---|---|---|---|
DEBUG |
3 | 开发调试信息,生产环境通常关闭 | 变量值、重组次数、排版参数 |
INFO |
4 | 关键节点信息,记录正常流程 | 页面加载完成、网络请求成功 |
WARN |
5 | 潜在问题,但不影响当前功能 | 资源泄露、API 降级、重组次数过多 |
ERROR |
6 | 错误事件,可能需要人工介入 | 网络异常、数据解析失败、Compose 崩溃 |
FATAL |
7 | 致命错误,应用很可能崩溃 | ANR、OOM、kernel 异常 |
1. DEBUG 级别:开发者的放大镜
DEBUG 是写给自己的。它只存在于 BuildConfig.DEBUG = true 的版本里。
在 Compose 调试中,我最常用 DEBUG 来记录 重组次数 和 参数变化:
Log.d("ComposeRecompose", "TextContent 重组了,当前值:$text")
为什么要关注这个?因为 Compose 的声明式渲染依赖重组。如果你看到某段代码重组了几百次,那多半是不可控重组——也就是性能隐患。
技巧:在 Android Studio 的 Logcat 中,可以过滤 package:mine 来只显示自己应用的日志。我习惯再配合 Log.d(TAG, ...) 中的 TAG 做二次过滤。
2. INFO 级别:系统的脉搏
INFO 记录的是「应该发生」的事情。比如用户登录成功、屏幕旋转、布局加载完成。
对我而言,DEBUG 是微观调试,INFO 是宏观监控。
Log.i("Lifecycle", "MyScreen 已进入 Composition")
在回归测试中,我会在所有关键 activity/fragment 的 onCreate 或 LaunchedEffect 中添加 INFO 日志。这样就能快速定位:到底是哪一步没有走到?
3. WARN 级别:黄牌警告
WARN 代表「虽然有危险,但系统还能继续运行」。我在项目中遇到过,重组次数过多时,WARN 日志会提示潜在的性能问题:
Log.w("Performance", "Recompose count exceeded threshold: 50 times in 1s")
这个级别经常会被人忽略。但你想想看,线上大量 WARN 堆积,可能就是在预告某个模块即将崩溃。
4. ERROR 级别:需要立即响应
ERROR 代表功能已不可用。网络连接失败、数据格式错误、权限被拒……这些都是 ERROR。
我曾经在一个 Compose 项目里,用 ERROR 日志标记所有不可恢复的错误:
Log.e("NetworkError", "Fetch user list failed: ${error.message}")
注意:ERROR 日志一定要附带堆栈信息或错误详情,不然光看一条「Something went wrong」等于没打。
5. FATAL 级别:最后的遗言
FATAL 是最高的。它意味着应用即将或已经崩溃。在 Android 原生日志中,Log.wtf() 就属于 FATAL(What a Terrible Failure)。
我不建议滥用 FATAL,它应该只留给无法恢复、无法捕获的致命错误,比如:
- 空指针但无法捕获(Compose 内部崩溃)
- 线程死锁
- 抛出
PendingAtomicThrow后无法继续
Log.wtf("Fatal", "Unhandled composition exception: ${exception}")
你知道吗?我一般把 FATAL 当作最后一道防线。如果它出现了,那说明我的前置防御(null 检查、try-catch)全都漏了。
如何选择级别?一个小决策树
刚开始的同学容易纠结「这个该用 ERROR 还是 WARN?」。我个人习惯做如下判断:
- 用户能感知到吗? —— 能,且功能不可用 -> ERROR;能但暂时不影响 -> WARN。
- 需要人工处理吗? —— 需要 -> WARN/ERROR;只是记录 -> INFO/DEBUG。
- 这是必然事件还是偶然事件? —— 必然(如网络超时)-> ERROR;偶然(如临时降级)-> WARN。
我的一条铁律:DEBUG 打在开发环境,INFO 打在测试环境,WARN/ERROR 打在线上。线上线下分开打,用 BuildConfig 控制开关。
避坑指南:日志不是越多越好
我曾经犯过一个低级错误,把 DEBUG 日志留在发布版本里。结果线上日志量爆炸,磁盘写满,导致 ANR。
从那之后,我给自己定了几条规则:
- DEBUG 和 INFO 必须包在
if (BuildConfig.DEBUG)里 - WARN 和 ERROR 可以留在线上,但必须限制频率(比如用
RateLimiter) - FATAL 必须配合崩溃采集 SDK 上报,不要只打日志
嗯,说白了日志级别就是你写给未来自己的一封信。
写得清楚,未来少踩坑;写得潦草,排起查来眼泪汪汪。