第2章 采集缓存日志与数据
2.1 现象识别:什么情况需要采集缓存数据?
缓存问题很少直接报红,更多表现为“构建变慢”“缓存未命中”“构建结果不可复现”等隐性症状。当出现以下任一现象时,应立即启动数据采集流程:
- 缓存命中率断崖下降:构建扫描中缓存命中率低于50%或持续走低。
- 本地构建与CI构建结果不一致:相同代码在本地和CI上产生不同产出。
- 任务输出被反复重算:每次构建都重新执行同一Task,即使输入未变。
- 缓存目录异常膨胀:
~/.gradle/caches占用空间超过预期值。 - 构建日志中出现“CACHE MISS”或“NO CACHE”警告。
采集时机很重要:必须在问题复现的同一构建中采集数据,不要先尝试修复再回溯日志。保持项目状态、JDK版本、Gradle版本与问题环境一致。
2.2 数据采集方法
缓存排查需要三类核心数据:详细日志(记录每个Task的缓存决策)、构建扫描(可视化分析缓存命中/未命中)、缓存统计(量化数据观察趋势)。三者互为补充,缺一不可。
2.2.1 启用 --info 与 --debug 日志级别
默认Gradle输出 --lifecycle 级别日志,不包含缓存决策细节。必须显式提升日志级别:
- --info:输出每个Task的缓存状态(HIT / MISS / NO CACHE),足以覆盖80%的缓存诊断。
- --debug:输出Task输入/输出文件哈希、缓存键计算过程,适合深入分析“为什么未命中”。
诊断策略:先用 --info 快速扫描,若发现异常再对特定Task追加 --debug,避免日志爆炸。
典型用法:
# 仅查看缓存命中结果
./gradlew assembleDebug --info
# 全量调试日志(重定向到文件便于搜索)
./gradlew assembleDebug --debug 2>&1 | tee build_debug.log
2.2.2 导出构建扫描(Build Scan)
Gradle Build Scan 提供Web仪表盘,一键查看每个Task的缓存状态、耗时、输入快照。导出步骤:
- 添加扫描插件或在构建命令后加
--scan:./gradlew assembleDebug --scan - 构建完成后,终端输出一个唯一URL,例如:
Publishing build scan... https://ge.gradle.org/s/example-scan-uuid - 打开该链接,在 “Performance” → “Cache” 面板查看命中率、未命中Task列表及原因。
- 如需保存到本地,安装 Gradle Enterprise 或使用
--build-cache-debug生成JSON报告。
构建扫描不包含完整的Task输入哈希细节,需要与 --debug 日志配合使用:扫描给出“未命中Task列表”,日志给出“具体哪些输入文件变了”。
2.2.3 收集缓存统计(Cache Statistics)
Gradle内置缓存统计API,可在构建结束时输出结构化数据。有两种方式:
| 方式 | 命令/配置 | 输出内容 |
|---|---|---|
| 运行时参数 | | 总命中数/总请求数、字节命中率、本地/远程缓存请求分布 |
| Gradle Enterprise API | POST到GE服务 | 按Task、按缓存密钥分组的详细统计 |
| 自定义插件 | 实现 BuildCacheService 监听器 | 任意自定义统计(如特定文件类型的命中率) |
推荐始终使用 --build-cache-stats 收集基础指标:
./gradlew clean assembleDebug --build-cache-stats
# 输出示例:
# Build cache statistics
# Total cache operations: 42
# Cache hits: 30 (71.4%)
# Cache misses: 12
# Data from cache: 245 MB
# Data stored to cache: 89 MB
2.3 原因定位:从采集的数据中初步诊断
根据采集到的三类数据,使用下面的判断规则快速定位问题类型:
| 数据特征 | 可能原因 | 下一步动作 |
|---|---|---|
| --info日志中大量“NO CACHE”且未配置build cache | 远程缓存未启用或网络不可达 | 检查 settings.gradle.kts 中 buildCache 配置 |
| 构建扫描中缓存命中率低,且未命中Task的输入包含时间戳/随机值 | Task输入声明不完整(如使用了 @InputFiles 但未排除无关文件) | 用 --debug 查看Task输入文件列表,移除动态文件 |
| 构建扫描中所有Task均为MISS,且本地和远程缓存状态一致 | Gradle版本升级导致缓存键格式变化 | 清理 ~/.gradle/caches/journal-* 后重试 |
--build-cache-stats 显示高命中率但构建仍然缓慢 | 缓存还原速度慢(例如远程缓存延迟高) | 检查网络延迟或切换到本地SSD缓存 |
| 多次构建中缓存统计波动巨大 | 并发构建冲突或增量编译与缓存交叉影响 | 单线程重试对比,添加 --no-parallel |
2.4 修复动作:确保数据采集的正确性
在进入更深入的原因定位前,先排除采集本身的问题:
- 确认日志文件完整:检查是否因磁盘空间不足导致日志截断,使用
wc -l build_debug.log对比预期行数。 - 构建扫描是否过期:免费版Build Scan仅保留30天,及时导出PDF或截图。
- 缓存统计是否覆盖了所有Task:
--build-cache-stats默认只统计 CacheableTask,不包含未启用缓存的Task。 - 多项目构建:确保在根项目执行命令,子项目各自独立启用缓存。
2.5 复测:验证数据采集成功
执行一个简单的“空构建”验证采集工作是否正常:
# 1. 清理上次构建
./gradlew clean
# 2. 运行一次构建,开启完整采集
./gradlew assembleDebug --info --scan --build-cache-stats
# 3. 检查输出是否包含以下三个关键信息
# - "Build cache state for task:..." (每行一个Task)
# - 构建扫描URL
# - "Build cache statistics" 区块
若缺少任意一项,按以下顺序排查:
- 缺少info日志 → 检查是否误用了
-q或--quiet覆盖日志级别。 - 缺少扫描URL → 确认Gradle版本 ≥ 4.3,且未使用
--no-scan。 - 缺少缓存统计 → 确认Gradle版本 ≥ 6.0,且未使用自定义缓存配置禁用统计。
2.6 预防:将采集配置纳入开发流程
避免每次排查都临时加参数,建议在项目中预置以下配置:
- 在
gradle.properties中设置默认日志备份:org.gradle.logging.level=info org.gradle.caching.debug=true - 在CI pipeline中自动导出构建扫描:添加
--scan -Pgradle.enterprise.url=...。 - 定期运行缓存统计脚本:每周执行
./gradlew --build-cache-stats并将结果写入监控仪表盘。 - 培训团队:要求开发者遇到构建缓慢时先采集这3种数据再提问,减少沟通成本。
本章要点回顾:缓存数据采集是排查的起点,必须在问题复现的同一构建中同时启用 --info(或 --debug)、--scan 和 --build-cache-stats。将采集命令预置到项目脚本和CI中,能大幅缩短定位周期。