第27章 日志与调试:日志级别、--stacktrace与--scan、Gradle Console
说实话,Gradle 构建跑不起来的时候,最怕的就是两眼一抹黑。我早期带团队的时候,有个同事跑构建报错,日志就一行「BUILD FAILED」,他盯着屏幕看了十分钟,愣是不知道哪里挂了。后来我过去加了个 --stacktrace,好家伙,错误堆栈直接定位到某个插件版本冲突。所以这一章,咱们就把 Gradle 的日志和调试工具彻底捋一遍。
27.1 日志级别:从 QUIET 到 DEBUG
Gradle 的日志系统,说白了就是给你一个「信息过滤器」。你想想看,构建过程中有成百上千条消息,如果全打印出来,你根本找不到重点。所以 Gradle 定义了六个级别,从最安静到最啰嗦:
| 级别 | 用途 | 典型场景 |
|---|---|---|
| ERROR | 只显示错误 | 构建失败时的关键信息 |
| QUIET | 重要提示 | 生命周期通知、任务结果摘要 |
| WARNING | 警告信息 | API 废弃、配置问题 |
| LIFECYCLE | 默认级别 | 正常构建进度 |
| INFO | 详细信息 | 任务执行细节、依赖解析 |
| DEBUG | 调试信息 | 类加载、脚本执行、内部状态 |
我个人习惯,平时用默认的 LIFECYCLE 就够了。但一旦遇到诡异的问题,我会直接上 --info 甚至 --debug。举个例子:
# 只看关键输出
gradle build -q
# 看详细信息(比如依赖冲突)
gradle dependencies --info
# 看所有内部细节(慎用,输出量巨大)
gradle build --debug
--debug 刷屏太快,可以配合 2>&1 | grep '关键字符串' 过滤。我在排查插件加载顺序时就这么干,效率翻倍。
27.2 --stacktrace:让错误不再「裸奔」
默认情况下,Gradle 只显示错误摘要。比如「Task 'compileJava' failed」。但为什么失败?不知道。这时候 --stacktrace 就是你的救命稻草。
它有两个变体:
--stacktrace(短格式):显示 Gradle 内部的调用栈,但会裁剪掉一些无关的框架层。--full-stacktrace(长格式):显示完整的 Java 堆栈,包括 Gradle 内部实现细节。
我一般先用短格式,如果定位不到,再上长格式。比如:
gradle test --stacktrace
输出会告诉你:哪个脚本的哪一行调用了哪个任务,然后哪个插件抛了异常。我曾经遇到一个坑:自定义任务里用了 project.file(),但路径传了个 null。没有 --stacktrace 时只显示「NullPointerException」,加了之后直接定位到第 47 行——原来是我少了个判断。
--full-stacktrace 输出非常长,建议重定向到文件查看:gradle build --full-stacktrace > build.log 2>&1。别在终端里硬看,眼睛会瞎。
27.3 --scan:构建的「CT 扫描」
如果说 --stacktrace 是放大镜,那 --scan 就是核磁共振。它会生成一个 HTML 报告,里面包含:
- 每个任务的耗时(精确到毫秒)
- 依赖解析的详细树
- 插件和脚本的执行顺序
- 缓存命中/未命中情况
- 构建环境信息(JDK 版本、Gradle 版本、操作系统)
用法极其简单:
gradle build --scan
执行完后,终端会输出一个链接(比如 https://scans.gradle.com/s/abc123)。打开它,你会看到一个交互式页面。我记得有一次项目构建从 2 分钟突然变成 10 分钟,我打开 scan 一看,发现某个自定义任务居然在每次构建时都重新下载依赖——因为它的 inputs 声明漏了一个文件。这种问题,光看日志根本看不出来。
27.4 Gradle Console:实时监控的「仪表盘」
如果你在终端里运行 gradle build,默认会看到一个动态更新的控制台界面。这就是 Gradle Console。它有几个关键区域:
- 任务树:显示所有任务及其当前状态(待执行、执行中、成功、失败)。
- 进度条:整体构建的百分比。
- 日志区域:滚动显示当前正在输出的日志行。
- 构建结果:最终的成功/失败状态,以及耗时。
我个人特别喜欢它的「任务树」视图。当构建卡住时,你能一眼看到是哪个任务在「转圈圈」。比如有一次,我发现 :app:compileDebugKotlin 一直卡在 80%,就知道是 Kotlin 编译器在吃内存。然后我加了 --max-workers=1 限制并行度,问题就解决了。
如果你想禁用这个控制台界面(比如在 CI 环境里),可以用 --no-daemon --console=plain:
gradle build --console=plain
这样输出就是纯文本,适合被其他工具捕获。
27.5 知识体系:日志与调试全景图
下面这张图,把本章的核心逻辑串起来了。你可以把它当作调试时的「导航地图」:
你看,整个调试流程其实就三步:先调日志级别缩小范围,再用堆栈跟踪定位代码,最后用构建扫描做全局分析。而 Gradle Console 就像你的驾驶舱仪表盘,实时反馈每一步的状态。
27.6 避坑指南:我踩过的三个坑
嗯,这里分享几个真实教训:
- 别在 CI 里用 --console=auto:默认的自动模式会输出 ANSI 转义字符,导致日志系统解析乱码。我吃过一次亏,排查了半天才发现是控制台颜色代码污染了日志文件。后来统一用
--console=plain。 - --scan 链接别随便公开:虽然 Gradle 官方说扫描数据是匿名的,但里面会包含项目路径、任务名称、依赖版本。如果你公司有安全要求,建议自建 Gradle Enterprise 或者用
--no-scan禁用。 - DEBUG 日志别开太久:有一次我忘了关
--debug,结果构建跑了 20 分钟还没结束——因为日志输出太多,I/O 成了瓶颈。记住:调试完立刻切回默认级别。
gradle build --info --stacktrace,如果还搞不定,再加 --scan。这套组合能覆盖 90% 的问题。剩下的 10%,嗯… 那就得看源码了。
好了,日志和调试这块就聊到这儿。工具都是死的,关键是形成自己的调试节奏。下次构建挂了,别慌,按这张图一步步来,问题总会现形的。