25、诊断与日志:logcat 高级用法,车载日志系统(ELK 栈),故障排查实战
做车载系统开发,最怕什么?
怕车在路上跑着,某个功能突然挂了。更怕的是,日志拿不到,或者拿到了看不懂。
我在做 Automotive 项目时,遇到过好几次这样的场景:测试工程师反馈说“中控屏黑屏了”,但 logcat 里全是正常信息。后来才发现,是日志级别没调对,关键错误被淹没了。从那以后,我对日志系统就特别上心。
今天咱们就聊聊,怎么用好 logcat,怎么搭建车载日志系统,以及怎么高效排查故障。
25.1 logcat 高级用法:别只会 adb logcat
很多人用 logcat,就是敲个 adb logcat 然后盯着屏幕看。其实这远远不够。
25.1.1 按标签和优先级过滤
车载系统里,进程多、日志杂。你不加过滤,根本看不完。
# 只看 CarService 的 error 级别日志
adb logcat -s CarService:E
# 同时看多个标签
adb logcat -s CarService:E SystemUI:W
我个人习惯,先开一个窗口看全局日志,再开一个窗口专门盯关键模块。这样不容易漏掉上下文。
25.1.2 缓冲区选择
Android 有多个日志缓冲区:main、system、events、crash。默认只输出 main 和 system。
# 看 events 日志(包含 AMS、WMS 等系统事件)
adb logcat -b events
# 看 crash 日志
adb logcat -b crash
有一次,一个车载应用闪退,main 日志里啥都没有。我切换到 -b crash,立刻看到了崩溃堆栈。嗯,这个技巧很实用。
25.1.3 日志时间戳与格式化
车载日志需要精确到毫秒,方便和 CAN 总线数据对齐。
# 显示完整时间戳
adb logcat -v threadtime
# 显示 UTC 时间(方便跨时区分析)
adb logcat -v epoch
我建议用 threadtime,它同时显示线程 ID,排查多线程问题很有帮助。
25.1.4 日志持久化
车机不像手机,你不能随时插线抓日志。所以,日志必须能写到文件里。
# 将日志重定向到文件
adb logcat -f /sdcard/logcat.txt
# 限制文件大小,自动轮转
adb logcat -r 1024 -n 5 -f /sdcard/logcat_rotating.txt
这里有个坑:-r 参数的单位是 KB。我曾经设成 -r 1024,以为能存 1GB,结果只存了 1MB。后来查文档才发现,1024 是 1024 KB,也就是 1MB。所以,想要 100MB,得写 -r 102400。
25.2 车载日志系统:ELK 栈
单台车抓日志还行,但你要管理一个车队呢?
这时候就需要 ELK 栈了。ELK 是 Elasticsearch、Logstash、Kibana 的缩写。说白了,就是把日志集中起来,方便搜索和可视化。
25.2.1 整体架构
我画了一张图,帮你理解车载日志怎么从车端流向云端。
你看,车端采集日志,上传到云端 Logstash,Logstash 解析后存入 Elasticsearch,最后用 Kibana 做可视化。工程师在浏览器里就能搜到任意一台车的日志。
25.2.2 Logstash 配置示例
Logstash 的核心是配置文件。它告诉 Logstash:从哪读数据,怎么处理,往哪写。
input {
# 从 Kafka 读取车端日志
kafka {
bootstrap_servers => "kafka:9092"
topics => ["vehicle_logs"]
codec => "json"
}
}
filter {
# 解析 logcat 格式
grok {
match => { "message" => "%{TIMESTAMP_ISO8601:timestamp} %{LOGLEVEL:level} %{DATA:tag}: %{GREEDYDATA:msg}" }
}
# 添加车辆 VIN 字段
mutate {
add_field => { "vin" => "%{[@metadata][vin]}" }
}
# 过滤掉低级别日志(只保留 WARN 及以上)
if [level] not in ["WARN", "ERROR", "FATAL"] {
drop {}
}
}
output {
elasticsearch {
hosts => ["http://elasticsearch:9200"]
index => "vehicle-logs-%{+YYYY.MM.dd}"
}
}
这个配置里,我用了 grok 插件来解析 logcat 格式。你想想看,如果不解析,日志就是一行字符串,没法按级别、标签搜索。解析之后,你就可以在 Kibana 里搜 level:ERROR AND tag:CarService,瞬间定位问题。
25.2.3 Kibana 仪表盘
Kibana 不只是搜索,还能做仪表盘。我习惯建三个面板:
- 错误趋势图:按小时统计 ERROR 日志数量,异常飙升一目了然。
- Top 10 错误标签:哪个模块报错最多,排个序。
- 车辆健康度:每台车最近 24 小时是否有 FATAL 日志。
有一次,仪表盘显示某车型的“空调控制”模块错误率突然升高。我们查了代码,发现是某个传感器数据格式变更,导致解析异常。如果没有仪表盘,这种问题可能要等用户投诉才能发现。
25.3 故障排查实战
理论说完了,咱们来点实战。我分享三个真实案例。
25.3.1 案例一:中控屏黑屏
现象:车辆启动后,中控屏一直黑屏,但声音正常。
排查步骤:
- 先看 logcat,过滤
SystemUI和SurfaceFlinger。 - 发现
SurfaceFlinger报错:ERROR: BufferQueue has been abandoned。 - 进一步查,发现是某个第三方应用持有了 Surface 但没有释放。
- 杀掉该应用,屏幕恢复。
这个问题的根因是应用没有正确处理生命周期。我们在 ELK 里加了一条规则:如果 BufferQueue abandoned 出现,自动告警。
25.3.2 案例二:导航卡顿
现象:导航过程中,地图滑动不流畅,有掉帧。
排查步骤:
- 用
adb logcat -s Choreographer:I查看帧率。 - 发现掉帧时,伴随大量
GC_FOR_ALLOC日志。 - 说明内存分配频繁,触发垃圾回收。
- 用
adb shell dumpsys meminfo查看内存,发现地图应用缓存了太多瓦片。 - 优化缓存策略后,卡顿消失。
这里有个技巧:Choreographer 日志会告诉你跳过了多少帧。如果连续跳帧超过 3 帧,用户就能感知到卡顿。
25.3.3 案例三:蓝牙连接不稳定
现象:手机连接车机蓝牙后,偶尔断连。
排查步骤:
- 抓取 Bluetooth 相关日志:
adb logcat -s BluetoothAdapter:E BluetoothManager:W。 - 发现断连前有
bt_vendor: firmware crash日志。 - 这是蓝牙固件崩溃了。需要更新固件。
- 同时,我们在 ELK 里添加了蓝牙固件版本字段,方便追踪哪些车还在用旧固件。
我曾经遇到过一个问题:蓝牙日志里全是 D 级别,根本看不到错误。后来发现是日志级别被某个系统服务改写了。所以,排查时一定要确认日志级别是否正确。
25.4 避坑指南
注意:日志别乱打
车载系统对性能敏感。日志打太多,会影响 UI 响应。我建议:
- 生产环境只保留 WARN 及以上级别。
- DEBUG 日志用
isLoggable控制开关。 - 循环日志不要打,比如每 100ms 打印一次传感器数据,用统计值代替。
小技巧:日志带上时间戳
车端和云端的时间可能不同步。我习惯在日志里加上 System.currentTimeMillis(),这样不管在哪,都能对齐时间线。
核心要点总结
- logcat 要会用
-s、-b、-v参数,别裸跑。 - ELK 栈是车载日志的标配,车端采集、云端分析。
- 故障排查要结合 logcat 和系统服务(如 dumpsys)。
- 日志级别要控制好,别让生产环境变成日志洪流。
好了,这一章的内容就到这里。日志系统是车载开发的“眼睛”,用好它,能省下大量排查时间。下一章咱们聊聊性能优化,到时候见。