日志文件基础:存储位置、命名规则与结构

做回归失败日志分析,说白了第一步就是——你得知道日志文件在哪、叫什么、里面长什么样。不然分析半天,连日志都没找对,那都是白忙。

我刚开始带项目时,就犯过这个错误:测了一次 UI 回归,明明失败了,跑过去找日志,结果翻了半天 build 目录,愣是没找到。后来才发现,是同事改了输出路径,我完全不知道。所以这一章,咱们就把日志文件的「户口本」查清楚。

存储位置——去哪儿找日志

Android 回归测试(尤其是用 Compose 写的 UI 测试)产生的日志,分两大类:一类是 构建与测试报告,另一类是 运行时 Logcat。我这边按最常用的 Gradle 项目来梳理。

日志类型默认存储位置(相对项目根目录)说明
Gradle 测试报告(XML)build/test-results/testDebugUnitTest/每个测试类一个 XML 文件,JUnit 格式
Gradle 测试报告(HTML)build/reports/tests/testDebugUnitTest/可视化版本,浏览器可直接打开
Compose UI 测试截图build/reports/screenshots/失败时自动截取的 UI 快照(需配置)
Logcat 日志导出自定义,常用 app/logs/ 或 /sdcard/通过 adb logcat -d > file.log 保存

重点记住: 回归失败后,第一个翻的地方就是 build/test-results/testDebugUnitTest/。里面全是 .xml 文件,每个文件就是一条测试用例的详细结果。

我自己常用的习惯是,在 CI 脚本里会把 build/reports/ 整个目录压缩打包,作为构建产物。这样即使本地被清除,也能追溯到历史上的失败日志。

命名规则——从文件名就能读出信息

Gradle 生成的测试报告 XML 文件,命名套路非常固定:

TEST-全限定类名.xml
# 例如
TEST-com.example.app.ui.LoginScreenTest.xml

如果你用的是 JUnit5,有时会带方法名后缀,但多数场景就是这个格式。Logcat 日志的话,我习惯按 日期_进程标签.log 来命名,比如 2025-07-16_com.example.app_dump.log。

一个小技巧: 当你有一堆 TEST-*.xml 文件时,直接在终端用 grep -l 'failure\|error' *.xml 就能秒筛出哪些测试失败了,省得一个一个打开看。

我曾经遇到过团队里有人把日志命名写死了,结果 CI 跑多个分支时后一次直接覆盖前一次,历史失败记录全没了。所以后来我要求所有日志文件必须带时间戳或构建号。

结构——日志文件里到底写了什么

我们重点看 XML 测试报告,它是最结构化的失败信息源。一个典型的 TEST-*.xml 大致长这样:

<testsuite name="com.example.app.LoginScreenTest" tests="1" failures="1" errors="0">
  <testcase name="testLoginButtonDisplayed" classname="com.example.app.LoginScreenTest" time="2.345">
    <failure message="Expected visible but not found: '登录'" type="AssertionError">
      java.lang.AssertionError: Expected visible but not found: '登录'
          at com.example.app.LoginScreenTest.testLoginButtonDisplayed(LoginScreenTest.kt:42)
          ...
    </failure>
  </testcase>
</testsuite>

看到什么了?testsuite 是整个测试类的统计,testcase 是单个用例,failure 里面直接贴了异常堆栈。对 Compose 测试来说,最常见的失败类型是 AssertionError 或 SemanticsMatcher 相关异常。

Logcat 日志结构更简单,一行一条:

07-16 14:32:18.152 12345 12345 E TestRunner: java.lang.AssertionError: ...

注意看时间戳、进程 ID、标签(这里是 TestRunner)和级别(E 表示 Error)。

注意: 有些 Compose 测试框架会把日志打到 ComposeTestRule 标签下,别光盯着 TestRunner 找。我当初第一次排查 Compose UI 测试日志时,找了半天没找到堆栈,后来发现藏在 Compose 标签里。

一张图看懂日志体系

下面这张图帮大家理清日志文件的分类与层级关系。我通常把它打印出来贴在工位旁边。

日志文件分类与结构 回归失败日志 构建产物日志 运行时日志 TEST-*.xml 测试报告 HTML UI 截图(可选) adb logcat Compose 标签日志 说明 • 蓝色(顶层):日志来源根节点 • 紫色/青色(二级):按产生时机分类 • 红色/橙色(叶子):具体文件或工具

你看,构建产物日志是「死」的——它记录的是测试执行完的结果,不可以丢失。运行时日志是「活」的——它是在设备或模拟器上实时输出的,你得自己抓。但两种日志缺一不可,实际排查时往往是交叉验证。

我个人的经验: 遇到 Compose 的语义匹配失败(比如 hasText 没找到),先去翻 XML 里的失败消息,里面有对比信息;如果 XML 信息不够,再拿 Logcat 围绕 Compose 标签做过滤。这样配合效率最高。

结构化内容小结

  • 存储位置: build/test-results/ 是主战场,Logcat 要主动导出。
  • 命名规则: TEST-类名.xml 是标配,自定义日志记得加时间戳。
  • 结构: XML 里 `` 包 ``,内部带 `` 堆栈;Logcat 一行一条,解析时注意标签。

搞懂这些基础,后面的章节我们才能精准地「读」出失败原因。不然,连门都摸不着。