feat: 启动监控扩展追溯字段并修复补报事件语义
- StartupMonitor 上报新增时间拆解与客户端环境字段:response_latency、startability_processstart_dur、appattach_to_appforeground_dur、bundle_name、device_type、system_version、build_mode、target_name、git_commit、is_first_install - 新增 is_backfill 补报标识:以 watcher 注册时间为锚点判定系统补投递的上次启动遗留事件,entry_page 置 unknown,避免错误归因 - CrashDiagnosticsDemo 同步字段展示与补报判定逻辑,devecocli build 构建通过 - 真机验证(nova 14 Pro):冷启动/多入口拦截/热启动/5 秒退出/补报判定/全场景 3 组全部通过,耗时 126~180ms - 接入方案文档更新:第 4 节追溯字段与时间拆解、第 6 节 5 秒退出边界、第 8 节验证记录 Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
@@ -6,6 +6,7 @@
|
||||
import { common } from '@kit.AbilityKit';
|
||||
import { inspector } from '@kit.ArkUI';
|
||||
import { hiAppEvent, hilog } from '@kit.PerformanceAnalysisKit';
|
||||
import { deviceInfo } from '@kit.BasicServicesKit';
|
||||
|
||||
enum DrawReportState {
|
||||
IDLE,
|
||||
@@ -19,11 +20,27 @@ enum DrawReportState {
|
||||
*
|
||||
* 监听系统 APP_LAUNCH 事件,并确保一个进程生命周期内只提交一次
|
||||
* reportDrawnCompleted。检测结果同时输出到 hilog 和模拟页面。
|
||||
*
|
||||
* 与 future_entry_demo 的 StartupMonitor.ets 对应:本文件为无依赖演示实现
|
||||
* (仅 hilog + 页面展示,不做 ELK 上传),追溯字段已同步系统时间拆解字段
|
||||
* 与 deviceInfo 环境字段;git_commit / is_first_install / build_mode 依赖
|
||||
* biz_common 与 BuildProfile,演示工程不适用,故未同步。
|
||||
*/
|
||||
export class StartupDiagnostics {
|
||||
private static readonly WATCHER_NAME: string = 'startupDiagnosticsWatcher';
|
||||
private static readonly LOG_DOMAIN: number = 0xD002;
|
||||
private static readonly MAX_EVENT_KEYS: number = 20;
|
||||
/**
|
||||
* 补报事件判定容差(ms)。
|
||||
* 本次启动链中 icon_input_time(点击图标)与 watcher 注册时间间隔在毫秒~秒级;
|
||||
* 系统补投递的上次启动遗留事件,其 icon_input_time 至少早于注册时间 5 秒(事件送达延迟),
|
||||
* 因此容差取 2s 可稳定区分两者。
|
||||
*/
|
||||
private static readonly BACKFILL_TOLERANCE_MS: number = 2000;
|
||||
/**
|
||||
* watcher 注册时间戳(epoch ms),用于识别系统补投递的上次启动遗留事件。
|
||||
*/
|
||||
private static watcherRegisterTime: number = 0;
|
||||
private static watcherInitialized: boolean = false;
|
||||
private static drawReportState: DrawReportState = DrawReportState.IDLE;
|
||||
private static capturedEntryPage: string = '未捕获';
|
||||
@@ -59,6 +76,9 @@ export class StartupDiagnostics {
|
||||
}
|
||||
};
|
||||
|
||||
// 记录注册时间戳,用于区分本次启动事件与系统补投递的上次启动遗留事件
|
||||
StartupDiagnostics.watcherRegisterTime = Date.now();
|
||||
|
||||
try {
|
||||
hiAppEvent.addWatcher(watcher);
|
||||
StartupDiagnostics.watcherInitialized = true;
|
||||
@@ -230,15 +250,52 @@ export class StartupDiagnostics {
|
||||
StartupDiagnostics.processedEventKeys.shift();
|
||||
}
|
||||
|
||||
const bundleName: string = (params['bundle_name'] as string) ?? '';
|
||||
const bundleVersion: string = (params['bundle_version'] as string) ?? '';
|
||||
const animationFinishTime: number = (params['animation_finish_time'] as number) ?? 0;
|
||||
// 时间拆解字段(与接入方案文档第 4 节一致):
|
||||
// response_latency 需 API 22+;startability_processstart_dur / appattach_to_appforeground_dur 仅冷启动存在
|
||||
const responseLatency: number = (params['response_latency'] as number) ?? 0;
|
||||
const startabilityProcessStartDur: number = (params['startability_processstart_dur'] as number) ?? 0;
|
||||
const appattachToAppForegroundDur: number = (params['appattach_to_appforeground_dur'] as number) ?? 0;
|
||||
|
||||
// 补投递事件(上次启动提交绘制但进程在事件送达前退出)的 capturedEntryPage 是当前进程的快照,
|
||||
// 不能错误归因到本次启动的页面,因此补报事件的入口标记为未知。
|
||||
const isBackfill: boolean = StartupDiagnostics.isBackfillEvent(iconInputTime);
|
||||
|
||||
StartupDiagnostics.launchEventCount++;
|
||||
StartupDiagnostics.lastLaunchDetail =
|
||||
`extend_time=${extendTime}ms, icon_input_time=${iconInputTime}, ` +
|
||||
`start_type=${startType}, process_name=${processName}`;
|
||||
`start_type=${startType}, process_name=${processName}\n` +
|
||||
`bundle_name=${bundleName}, bundle_version=${bundleVersion}\n` +
|
||||
`animation_finish_time=${animationFinishTime}ms, response_latency=${responseLatency}ms\n` +
|
||||
`startability_processstart_dur=${startabilityProcessStartDur}ms, ` +
|
||||
`appattach_to_appforeground_dur=${appattachToAppForegroundDur}ms\n` +
|
||||
`device_type=${StartupDiagnostics.getDeviceType()}, ` +
|
||||
`system_version=${StartupDiagnostics.getSystemVersion()}\n` +
|
||||
`补报事件=${isBackfill ? '是(entry_page 不归因,按 unknown 处理)' : '否'}`;
|
||||
StartupDiagnostics.lastAction = '已捕获有效冷启动 APP_LAUNCH';
|
||||
StartupDiagnostics.logInfo(StartupDiagnostics.lastLaunchDetail);
|
||||
StartupDiagnostics.notifyStatusChanged();
|
||||
}
|
||||
|
||||
/**
|
||||
* 判断事件是否为系统补投递的上次启动遗留事件。
|
||||
* 本次启动链中 icon_input_time 与 watcher 注册时间间隔在容差内;
|
||||
* 补报事件的 icon_input_time 早于注册时间超过容差(至少 5 秒的送达延迟)。
|
||||
*/
|
||||
private static isBackfillEvent(iconInputTime: number): boolean {
|
||||
return iconInputTime < StartupDiagnostics.watcherRegisterTime - StartupDiagnostics.BACKFILL_TOLERANCE_MS;
|
||||
}
|
||||
|
||||
private static getDeviceType(): string {
|
||||
return deviceInfo.deviceType;
|
||||
}
|
||||
|
||||
private static getSystemVersion(): string {
|
||||
return deviceInfo.osFullName;
|
||||
}
|
||||
|
||||
private static getStateName(): string {
|
||||
switch (StartupDiagnostics.drawReportState) {
|
||||
case DrawReportState.IDLE:
|
||||
|
||||
Reference in New Issue
Block a user