@zyl06
2021-05-09T14:15:16.000000Z
字数 6395
阅读 775
舆情
日志
监控
舆情治理前,闲鱼 APP 依托阿里集团的设施建设,已经有拥有以下能力:
以上得到了部分最重要的线上质量数据,也能处理少量舆情问题:
以上手段仍存在较多的问题:
技术舆情中的业务问题定位困难;
tlog
日志或在线日志;本地日志定位问题能力有限
tlog
日志有较为详细的集团二方 SDK 日志,但缺失设备基础信息、业务日志、用户行为日志等本地日志回捞困难
反馈问题和线上实际情况存在偏差
基于现状问题,重新梳理和补充的舆情日志查询体系如下图:
线上舆情问题治理体系
补充内容如下:
其他
特别的,MIUI 系统可监听广播
miui.intent.TAKE_SCREENSHOT
如前后端请求日志使用鹰眼 Id 关联
日志治理前期,大量业务日志进入了控制台日志,即便治理后将大量日志转入 tlog 日志,仍有部分日志流入控制台,如 Android Maven 引入独立模块和 Flutter 插件包模块。此外,部分未知异常问题,如视频绿屏、黑屏等,logcat 日志也提供了定位的可能性。
Android Log 模块提供了多种日志缓存类型,见 Android logging,可通过 logcat
命令获取对应类型的日志。这里,我们在用户反馈的时候,分别将 LOG_ID_MAIN
、LOG_ID_EVENTS
和 LOG_ID_CRASH
类型 logcat 读取并写入到本地文件,而后将 logcat 日志同 tlog 文件一起打包通过 AUS 上传至 OSS。
// LOG_ID_MAIN 主应用程序log,-t 设置日志上限 20000
adb logcat -d -v threadtime -t 20000
// LOG_ID_EVENTS 系统事件信息
adb logcat -d -b events -v threadtime -t 6666
// LOG_ID_CRASH 应用程序 crash 日志
adb logcat -d -b crash -v threadtime -t 6666
logcat日志文件
前面提到,由于在线命令推送成功率低且后台无命令缓存机制,所以闲鱼 App 本地日志回捞困难。为解决日志难以获取问题,舆情治理体系中设计了多种日志回捞策略,见下图所示。
本地日志回捞策略
本地日志下载查看流程
线上用户反馈 ANR 并给出截图证明,因为没有卡顿日志,难以定位问题。
线上用户反馈黑屏,无有效日志难以定位问题
页面卡死的另一种表现
在闲鱼 App 的混合工程场景,依托 Emas 平台已实现 iOS 端卡顿检测,Flutter 端卡顿检测方案查看 Flutter卡顿问题的监控与思考,这节主要讲述 Android 端线上卡顿/ANR 检测。
在线下场景,Android 端卡顿/ANR 检测手段已经很成熟,普通卡顿检测方案有 BlockCanary,ANR 检测可通过 adb bugreport
查看 traces.txt 文件得到。然而在线上环境,以上卡顿检测方案就存在一定问题。
在线上场景,为了监听识别是否发生 ANR 以及读取 ANR 内容,APP 需要监听 traces.txt 文件变化,并尝试读取 traces.txt 文件内容。监听文件的方案,在 Android 6.0 及以后存在权限问题,大部分场景无法检测到 ANR
dalvik.vm.stack-trace-file
如以下代码执行在红米手机 Android 11 上运行,mSystemTraceFilePath 为 "",mSystemTraceFile 的路径为 "/"
File mSystemTraceFile;
...
this.mSystemTraceFilePath = "/data/anr/traces.txt";
this.mSystemTraceFile = new File(this.mSystemTraceFilePath);
if (!this.mSystemTraceFile.exists()) {
String propSystemTraceFilePath = SystemPropertiesUtils.get("dalvik.vm.stack-trace-file");
...
this.mSystemTraceFile = new File(propSystemTraceFilePath);
...
}
...
BlockCanary 检测 500ms 卡顿
BlockCanary 的核心原理是,设置 UI 线程的 Looper.mLogging 字段,主线程每次处理消息均会执行 print 方法。
public void start() {
if (!mMonitorStarted) {
mMonitorStarted = true;
Looper.getMainLooper().setMessageLogging(mBlockCanaryCore.monitor);
}
}
public void setMessageLogging(@Nullable Printer printer) {
mLogging = printer;
}
public static void loop() {
...
for (;;) {
...
// This must be in a local variable, in case a UI event sets the logger
final Printer logging = me.mLogging;
if (logging != null) {
logging.println(">>>>> Dispatching to " + msg.target + " " +
msg.callback + ": " + msg.what);
}
...
}
}
Looper.java
在 print 方法中时,触发 StackSampler 工作,即取消上一次异步线程延迟任务,重新触发一次延迟任务,延迟时间为 BlockThreshold * 0.8f (假设要检测 500ms 以上的卡顿堆栈,则延迟时间为 400ms)。若 UI Looper 任务发生卡顿(>BlockThreshold),则延迟任务被执行,且在卡顿期间的获取主线程堆栈信息,之后在下一次 print 方法被执行的时候,若确认发生卡顿,则可把主线程堆栈信息当做卡顿堆栈记录上报。
@Override
public void println(String x) {
...
if (!mPrintingStarted) {
mStartTimestamp = System.currentTimeMillis();
mStartThreadTimestamp = SystemClock.currentThreadTimeMillis();
mPrintingStarted = true;
startDump();
} else {
final long endTime = System.currentTimeMillis();
mPrintingStarted = false;
if (isBlock(endTime)) {
notifyBlockEvent(endTime);
}
stopDump();
}
}
在线上环境,大部分场景下并不会发生卡顿,但卡顿检测 SDK 会一直执行。可以发现 app 每一帧时间(16.6ms),UI Looper 中的任务会执行多次,最终产生大量的无效字符串拼接操作和大量小对象碎片。特别的,在线上低端机或者 cpu 负载较高的场景下,app 性能会因此降级,影响用户体感。
logging.println(">>>>> Dispatching to " + msg.target + " " +
msg.callback + ": " + msg.what);
针对 traces.txt 读取权限问题,可通过检测主线程 5s 卡顿当做 anr。针对 BlockCanary 线上使用的性能问题,为降低延迟任务取消和触发频率,同时避免字符串对象频繁创建问题,必须放弃 Looper.mLogging 的方案。重新思考为什么可以通过设置 Looper.mLogging 检测卡顿?其满足 2 个条件:
闲鱼线上使用 Android 帧回调代替 Looper Task 方案,同时满足以上 2 个条件:
此外,为避免 BlockCanary 方案延迟任务触发和取消的频率过高的问题,仅在帧回调处记录时间戳,不再取消延迟任务,但在延迟任务执行时判断是否发生卡顿,同时记录主线程堆栈。
假设 500ms 以上为卡顿,整体方案流程图如下:
无卡顿场景
卡顿场景
仅在频繁触发的帧回调处,记录时间戳,无性能消耗
定义 5s 以上卡顿为 ANR,为检测 ANR,同样通过 500ms 卡顿检测,并做卡顿堆栈聚合,当连续发生卡顿大于 5s 且堆栈信息不变,则认为发生 ANR。发生 ANR 时,记录当前设备 CPU 负载等信息。
闲鱼首页卡片点击事件中故意制造 500ms 和 5s 卡顿查看卡顿检测结果。
// CardView61801.java
private void doOnClick(String redirectUrl, Map<String, String> trackParams) {
if (null == mCardBean) return;
try {
Thread.sleep(500);
// Thread.sleep(5000);
} catch (Exception e) {
e.printStackTrace();
}
...
}
查看检测结果
Sleep 500ms
Sleep 5s
以上是闲鱼 Android 线上卡顿检测方案,线上已经运行超过 3 个版本,通过日志可发现用户反馈卡死、无反应、闪退、黑屏等现象都有可能是 ANR 造成。整体方案有以下特点:
相比 BlockCanary Looper Task 在 0.8*卡顿阈值
时获取卡顿堆栈,本方案 500ms 卡顿检测有更高概率获取错误堆栈。但通过连续卡顿堆栈比对,可确保 5s 卡顿堆栈的准确性。而 500ms 卡顿通过线上统计提升准确性
由于闲鱼 App 反馈入口较深,因此相比上亿用户而言,每日技术舆情反馈量占比偏低,以此可知技术舆情反馈量并不能准确反应线上质量情况。为此,我们通过监控埋点构建线上关键舆情问题和基础性能大盘,同时通过监控大盘主动发现线上重点待解决问题,加速线上舆情收敛速度和质量提升,流程图如下所示。
主动发现问题流程图
主动发现问题大盘示例
业务舆情问题大盘
通过统计一段时间的舆情问题,得到关键舆情问题,以此添加监控埋点并构建线上报表。通过大盘数据得到线上问题发生量和重要归因,通过重点解决排名前几的归因,达到舆情问题快速收敛的目的。
基础问题
除了 Crash、Flutter异常、性能等线上大盘,我们构建了 5s 卡顿监控、网络请求失败、慢请求、错误 toast 等基础监控大盘。
基于大盘发现了问题,需要获取对应日志来定位问题,但问题对应的用户很可能并不会反馈舆情或反馈内容不是该问题,为此我们构建了实时日志查询以及本地日志批量回捞能力。
自建舆情追踪平台
针对关键监控问题,客户端增加对应的 SLS 实时日志,在自建舆情追踪平台获取用户的在线日志。平台支持用户名和时间查询,同时支持用户行为、用户异常、舆情日志类型的组合查询。
在线日志难以避免存在日志内容不足的问题,如基础日志、其他关键模块日志等,然而单个用户的本地日志回捞成功很低(原因见上文内容),为此构建舆情日志回捞平台,通过批量回捞的方式确保能获取到舆情问题某个用户的本地全量日志。
输入舆情 IssueName 查询用户 id
批量回捞结果查询
以上初步建立了闲鱼舆情治理体系:
此外,未来仍有很多的演进空间如下:
日志可视化
日志解析后的内存可视化举例
日志智能解析
关键日志反向回捞用户日志
日志关联聚合查询