Skip to content

Instantly share code, notes, and snippets.

@amanjeetsingh150
Last active April 4, 2022 07:36
Show Gist options
  • Save amanjeetsingh150/915a0655ac3d4594cc1c36a593a7d64a to your computer and use it in GitHub Desktop.
Save amanjeetsingh150/915a0655ac3d4594cc1c36a593a7d64a to your computer and use it in GitHub Desktop.
Dumping logcat via dadb
import dadb.AdbShellResponse
import dadb.Dadb
import okio.Sink
import okio.buffer
import okio.sink
import java.io.File
import java.io.IOException
import java.text.SimpleDateFormat
import java.util.Date
import java.util.Locale
class LogsReporter(private val dadb: Dadb) {
fun captureLogs(testId: String, vararg options: String): Result<File> {
return if (checkOrCreateDirectories()) {
val logFileName = SimpleDateFormat(
"yyyy-MM-dd_HH-mm-ss_SSS'-$testId.txt'",
Locale.US
).format(Date())
val logFilePath = "/data/local/tmp/logs/$logFileName"
dadb.shell("logcat -v time -f $logFilePath -d ${options.joinToString(" ")}")
return getLogFile(logFilePath)
} else {
val message = "Failed to write logs for $testId, logs directory was not available"
Result.failure(LogsDirectoryMissing(message))
}
}
private fun checkOrCreateDirectories(): Boolean {
return if (directoryExists("/data/local/tmp/logs")) {
true
} else {
val response = shell("mkdir -p /data/local/tmp/logs && echo 1 || echo 0")
response.trim().removeSuffix("\n") == "1"
}
}
private fun getLogFile(outputFilePath: String): Result<File> {
val outputFileName = outputFilePath.substring(outputFilePath.lastIndexOf('/'))
val outputFile = File.createTempFile(outputFileName, ".txt")
return try {
val bufferedSink = outputFile.sink().buffer()
pull(bufferedSink, outputFilePath)
Result.success(outputFile)
} catch (exception: IOException) {
Result.failure(exception)
}
}
private fun directoryExists(path: String): Boolean {
return shell("[ -d $path ] && echo 1 || echo 0").contains("1")
}
private fun shell(command: String): String {
val response: AdbShellResponse = try {
dadb.shell(command)
} catch (e: IOException) {
throw e
}
if (response.exitCode != 0) {
throw ShellException(command, response.allOutput)
}
return response.output
}
private fun pull(sink: Sink, remotePath: String) {
try {
dadb.pull(sink, remotePath)
} catch (e: IOException) {
throw e
}
}
fun cleanLogs() {
if (logsExists()) {
shell("rm -r /data/local/tmp/logs/*.txt")
}
}
private fun logsExists(): Boolean {
return directoryExists("/data/local/tmp/logs") &&
filePatternExists("/data/local/tmp/logs/*.txt")
}
private fun filePatternExists(filePattern: String): Boolean {
return shell("ls $filePattern&> /dev/null && echo 1 || echo 0").contains("1")
}
}
class ShellException(command: String, output: String): Throwable("Adb shell failed for $command, output: $output")
class LogsDirectoryMissing(message: String) : Throwable(message)
fun main () {
// Call this from place where you want logs from
val dadb = Dadb.create("localhost", 5555, AdbKeyPair.readDefault())
val logsReporter = LogsReporter(dadb)
/**
* You can pass options to this API (regex, pid based etc.),
* look at the available options to logcat command here:
* https://developer.android.com/studio/command-line/logcat#options
*/
val fileResult = logsReporter.captureLogs("ui-test-id")
try {
val file = fileResult.getOrThrow()
} catch (exception: Exception) {
// log or do something with exception
} finally {
// cleanup the logs dir /data/local/tmp/logs
logsReporter.cleanLogs()
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment