需求
就是运行android测试的时候抓取系统日志,遇到的主要问题包括:
- adb logcat 指定起始时间( not in time format 错误 );
- 如何保存单元测试开始时间;
- gradle中执行 adb 命令;
ADB 获取 logcat
用法与问题
使用 adb logcat
命令可以抓取logcat日志 (可参考官方文档)
但是在不加控制选项的情况下,默认是抓取所有时间的日志的,我们只希望抓取单元测试开始到结束时的日志。
针对此需求,我们查阅官方文档可以看到有个 -t
的选项可以指定起始时间:
-t ‘
官方也给出了示例用法如下:
adb logcat -t '01-26 20:52:41.820'
可以看到使用了一个单引号包围,主要是为了保留空格,但是,当我们按照这种写法进行测试的时候,却报错了:
$ adb logcat -t '05-19 11:32:00.000'
logcat: -t ''05-19' not in time format.
- 我还注意到,官方页面还温馨的给了一个链接( -P 选项),但是其中的描述也是使用单引号。
- 猜想可能是文档更新不及时?于是通过 adb 自带帮助看看:
$ adb logcat --help
-t <count> Print only the most recent <count> lines (implies -d).
-t '<time>' Print the lines since specified time (implies -d).
-T <count> Print only the most recent <count> lines (does not imply -d).
-T '<time>' Print the lines since specified time (not imply -d).
count is pure numerical, time is 'MM-DD hh:mm:ss.mmm...'
'YYYY-MM-DD hh:mm:ss.mmm...' or 'sssss.mmm...' format.
还是一样,😂😂😂😂,这就很让人无语了。
解决
怎么解决的? 灵机一动🤞,换成了双引号,就pass了。不得不说,Google 也挺坑😂。
$ adb logcat -t "2022-05-19 11:41:56.000"
--------- beginning of main
05-19 11:41:56.626 1740 2360 E WifiScoringParams: Invalid frequency(-1), using 5G as default rssi array
保存单元测试时间
执行单元测试时,我们通过 connectedDebugAndroidTest
的gradle任务来进行触发。
很自然的,我们先获取到 该任务,然后在它的 doFirst 方法中保存时间,问题就在于这个时间保存到什么地方?
tasks.findByName("connectedDebugAndroidTest").doFirst {
saveTestStartTime()
}
- 最初想到的是存储在 project 的属性中,即:
project.properties.put()
但是,在我们使用在另外一个任务中去取时,无法取到; - 当然,可以存储在一个指定文件中,但是还是倾向于存储到内存中,毕竟这个文件没啥用
- 又想到使用 extensions,即
project.extensions.add()
,测试后确实可以获取到,故最后的模式如下:
tasks.findByName("connectedDebugAndroidTest").doFirst {
saveTestStartTime()
}
// 保存开始时间
private void saveTestStartTime() {
project.extensions.add("testTaskStartTime", new SimpleDateFormat("MM-dd HH:mm:ss.mmm").format(new Date()))
String startTime = project.extensions.getByName("testTaskStartTime")
logger.lifecycle("startTime=" + startTime)
}
// 获取开始时间
private String getTestStartTime() {
try {
String startTime = project.extensions.getByName("testTaskStartTime")
startTime = "\"$startTime\""
logger.lifecycle("startTime=" + startTime)
return startTime
} catch (ignored) {
return null
}
}
执行 adb 命令
获取adb路径
为了保证能在不同的机器上都能找到adb,所以我们需要动态的获取adb的路径。
很自然的想到通过 local.properties 文件获取:
sdk.dir=D\:\\ProgramFiles\\Android\\Sdk
读取 local.properties 文件,然后取出 sdk.dir 属性即可:
private String getAdbExecPath() {
String adbPath = getStringProp("sdk.dir", null)
if (adbPath == null) {
return null;
}
String osName = System.getProperty("os.name")
String ext = osName.contains("Windows") ? ".exe" : ""
File adb = new File(adbPath, "${File.separator}platform-tools${File.separator}adb$ext")
if (!adb.exists()) {
logger.lifecycle("adb不存在:" + adb.getAbsolutePath())
return null
}
return adb.getAbsolutePath()
}
String getStringProp(String name, String defaultValue) {
String value = null
File localProp = project.rootProject.file('local.properties')
if (localProp.exists()) {
Properties p = new Properties()
p.load(new FileInputStream(localProp))
value = p.get(name)
}
if (value == null) {
value = getProperties().get(name)
}
return (value == null) ? defaultValue : value;
}
执行adb命令
就是通过 Runtime.getRuntime().exec()
执行命令即可
// 添加测试完成抓取logcat日志的操作任务
project.afterEvaluate {
String adbPath = getAdbExecPath()
tasks.create("getLogcatToFile") {
group = "custom"
doLast {
String startTime = getTestStartTime()
File logFile = rootProject.file("report/html/${project.name}_${timeStamp()}.log")
Runtime runtime = Runtime.getRuntime();
String cmd
if (startTime == null) {
cmd = adbPath + " logcat -d";
} else {
cmd = adbPath + " logcat -d " + " -t $startTime"
}
logger.lifecycle("cmd: $cmd")
Process process = runtime.exec(cmd)
outputToFile(logFile, process.getInputStream())
logger.lifecycle("adb日志抓取完毕")
}
}
tasks.findByName("connectedDebugAndroidTest").finalizedBy("getLogcatToFile")
}
private static String timeStamp() {
return new SimpleDateFormat("YYYY-MM-dd_HH-mm").format(new Date())
}
完成后执行 connectedDebugAndroidTest 任务即可自动抓取日志到指定的文件中。
延申
运行gradle任务抓取所有logcat
创建一个Gradle Task,去掉adb logcat 命令中的时间戳即可。