安卓 App 内 logcat 日志查看

为方便安卓APP在开发、测试阶段的调试,需要在手机端实时查看到logcat打印情况,因此可在APP中加入一个全局悬浮窗效果的日志查看功能,作为测试阶段的调试工具。

1. logcat 介绍

1.1 简介

Android日志系统提供了记录和查看系统调试信息的功能,日志从各种软件和系统的缓冲区中记录下来的,缓冲区可通过logcat命令查看使用。

1.2 开发者选项

要使用logcat需先打开开发者选项,有一个选项叫“日志记录器缓冲区大小”,默认是256K,日志循环写入环形缓冲区,在通常情况下,写满时最旧的日志会被删除以给新输出的日志留出内存空间。

1.3 logcat缓冲区

Android log 输出量巨大,特别是通信系统的log,因此,Android把log输出到不同的缓冲区中,目前定义的有四个log缓冲区:

1
2
3
4
Radio : 输出通信系统的log,如:WiFi,蓝牙,3/4/5G,GPS,NFC等
System: 输出系统组件的log,权限比较高,如:调用相机,调用录制,调用麦克风等
Events: 输出event模块的log,event翻译过来就是事件,如:通过手指点击屏幕的某一处地方,称为点击事件;通过按电源或者音量加减等
Main: 所有java层的log,(不属于上面3层的log)

如果测试APP需要输出Main的日志,命令:adb logcat -b radio,adb logcat 默认抓取Main。

1.4 日志组成部分

  1. 写下日志的时间,如上中 “05-22 12:27:15.370”

  2. 优先级,在Android 中,日志的优先级从低到高分成以下几种:

1
2
3
4
5
V ---Verbose(啰嗦,最低级别,开发调试中的一些详细信息,仅在开发中使用,不可在发布产品上打开)
D ---Debug(调试,用于调试的信息,可以在发布产品中关闭,比较常见)
I ---Info(信息,一般提示性的消息)
W ---Warning(警告)
E ---Error(错误,已经出现可影响运行的错误,比如应用crash时输出的日志)
  1. 标签(tag),标明日志发起者和方便日志的过滤筛选,如上中“libTGL”

  2. PID(进程ID),如上中 “1438”

  3. 正文,本日志的主体内容

2. logcat 命令

logcat有许多命令可供使用,在终端输入不同命令可对logcat实现不同操作。

2.1 实时输出指定标签内容

1
2
logcat -s TAG
仅输出标签为TAG的日志我们想输出 "CameraHal" 标签的信息, 就可以使用logcat -s CameraHal;

2.2 输出日志信息到文件

1
2
3
4
5
6
7
logcat -f /data/log.txt
日志保存到log.txt中

logcat -r 1 -n 4 -f /data/log.txt
将日志文件保存到log.txt中,当日志文件大于1K字节时,日志保存在log.txt1中,直到下标到达n-1时,重新覆盖log.txt中的信息,循环覆盖
-r 每<kbytes> 时 输出日志,默认值为16,需要和-f 选 项一起使用
-n 设置日志的最大数目<count> ., 默认值是4,需要和 -r 选 项一起使用

2.3 过滤固定字符串

1
2
3
4
5
logcat | grep -i <str> 
显示包含,并忽略大小写的logcat

logcat | grep <str>
显示包含的logcat

2.4 清空日志缓存信息

1
2
logcat -c
将之前的日志信息清空, 重新开始输出日志信息

2.5 过滤格式输出

1
2
3
4
5
6
7
8
9
10
11
标签:日志等级
V : Verbose (明细)
D : Debug (调试)
I : Info (信息)
W : Warn (警告)
E : Error (错误)
F : Fatal (严重错误)
S : Silent(Super all output) (最高的优先级, 可能不会记载东西)

logcat *:E
显示 Error 以上级别的日志

2.6 将缓存日志输出

1
2
logcat -d
输出命令, 之后推出命令, 不会进行阻塞

2.7 指定 logcat 的日志输出格式

日志消息包含一个元数据字段,除了标签和优先级,还可以修改输出显示一个特定的元数据字段格式的消息。为此,使用logcat -v 选项来指定一个支持的输出格式。支持的格式有:

格式 说明
brief 显示优先级/标记和过程的PID发出的消息(默认格式)
process 只显示PID
tag 只显示优先级/标记
raw 显示原始的日志消息,没有其他元数据字段
time 调用显示日期、时间、优先级/标签和过程的PID发出消息
threadtime 调用显示日期、时间、优先级、标签遗迹PID TID线程发出的消息
long 显示所有元数据字段与空白行和单独的消息
1
2
3
adb logcat –v thread

只能指定一个输出格式-v

3.应用内日志打印

3.1 获取日志

设置打印格式:logcat -v tag

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
/**
* 打印log,子线程
*/
private suspend fun printLogCat() {
printLog = true
withContext(Dispatchers.IO) {
try {
//1.获取日志数据
val cmdLog = "logcat -v tag"
val process = Runtime.getRuntime().exec(cmdLog)
val bufferedReader = BufferedReader(InputStreamReader(process.inputStream))
var line: String?
while (printLog) {
if (bufferedReader.readLine().also { line = it } != null) {
line?.let {
if (it.contains(logcatGrep)) {
withContext(Dispatchers.Main) {
//2.处理日志数据
buildLogLine(it)
}
}
}
}
}
} catch (e: IOException) {
e.printStackTrace()
}
}
}

3.2 处理日志数据

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
/**
* 处理日志数据给log分等级添加颜色、时间戳
* 存到list中,在recycleview中显示
*/
private fun buildLogLine(line: String?) {
line?.let {
var color = getColor(com.york.common.R.color.green)
if (it.startsWith("D")) {
color = getColor(com.york.common.R.color.blue_light)
} else if (it.startsWith("W")) {
//如果是上一条警告,本条忽略。只打印第一条
if (logList.size > 1 && logList[logList.lastIndex].content.startsWith("W")) {
return
}
color = getColor(com.york.common.R.color.yellow)
} else if (it.startsWith("E")) {
color = getColor(com.york.common.R.color.red_notification)
}
val logLine = LogLine(System.currentTimeMillis(), line, color)
//限制一下,100行
if (logList.size > 100) {
logList.removeAt(0)
}
logList.add(logLine)
logcatAdapter.setList(logList)
//自动滚动到底部
if (scrollToBottom) {
floatingLogcatBinding?.rcvLogcat?.scrollToPosition(logList.size - 1)
}
}
}